Merge branch 'MDL-63426-master' of https://github.com/lucaboesch/moodle
authorJun Pataleta <jun@moodle.com>
Wed, 3 Oct 2018 07:25:22 +0000 (15:25 +0800)
committerJun Pataleta <jun@moodle.com>
Wed, 3 Oct 2018 07:25:22 +0000 (15:25 +0800)
36 files changed:
blocks/timeline/tests/behat/block_timeline_dates.feature
grade/edit/tree/lib.php
lang/en/deprecated.txt
lang/en/message.php
lang/en/moodle.php
lib/badgeslib.php
lib/classes/event/message_contact_blocked.php
lib/classes/event/message_contact_unblocked.php
lib/classes/event/message_user_blocked.php [new file with mode: 0644]
lib/classes/event/message_user_unblocked.php [new file with mode: 0644]
lib/db/install.xml
lib/db/messages.php
lib/db/services.php
lib/db/upgrade.php
lib/deprecatedlib.php
lib/outputrenderers.php
lib/upgrade.txt
message/amd/build/message_area_profile.min.js
message/amd/src/message_area_profile.js
message/classes/api.php
message/classes/output/messagearea/messages.php
message/classes/privacy/provider.php
message/externallib.php
message/lib.php
message/pendingcontactrequests.php [new file with mode: 0644]
message/tests/api_test.php
message/tests/behat/manage_contacts.feature
message/tests/events_test.php
message/tests/externallib_test.php
message/tests/messagelib_test.php
message/tests/privacy_provider_test.php
message/upgrade.txt
mod/assign/locallib.php
mod/assign/tests/locallib_test.php
mod/forum/lib.php
version.php

index 6aaf269..22c04ec 100644 (file)
@@ -64,7 +64,7 @@ Feature: The timeline block allows users to see upcoming activities
     And I should see "Test feedback 3 closes" in the "Timeline" "block"
     And I should not see "Test choice 2 closes" in the "Timeline" "block"
     And I should not see "Test feedback 2 closes" in the "Timeline" "block"
-    And I click on "[data-region='paging-bar'] [data-control='next']" "css_element" in the "Timeline" "block"
+    And I click on "[data-region='paging-bar'] [data-control='next'] [data-region='page-link']" "css_element" in the "Timeline" "block"
     And I should see "Test feedback 2 closes" in the "Timeline" "block"
     And I should not see "Test assign 1 is due" in the "Timeline" "block"
     And I should not see "Test feedback 1 closes" in the "Timeline" "block"
index fe7c132..86cf33e 100644 (file)
@@ -113,10 +113,8 @@ class grade_edit_tree {
 
         $object = $element['object'];
         $eid    = $element['eid'];
-        $object->name = format_string($this->gtree->get_element_header($element, true, true, true, true, true),
-            false, array("context" => context_course::instance($COURSE->id)));
+        $object->name = $this->gtree->get_element_header($element, true, true, true, true, true);
         $object->stripped_name = $this->gtree->get_element_header($element, false, false, false);
-
         $is_category_item = false;
         if ($element['type'] == 'categoryitem' || $element['type'] == 'courseitem') {
             $is_category_item = true;
index 5a3e194..873ad79 100644 (file)
@@ -135,3 +135,5 @@ keepsearching,core
 allfieldsrequired,core
 previewhtml,core
 messagedselecteduserfailed,core
+eventmessagecontactblocked,core_message
+eventmessagecontactunblocked,core_message
index 4517a30..41fb26e 100644 (file)
@@ -28,9 +28,11 @@ $string['ago'] = '{$a} ago';
 $string['allusers'] = 'All messages from all users';
 $string['backupmessageshelp'] = 'If enabled, then instant messages will be included in SITE automated backups';
 $string['blockcontact'] = 'Block contact';
+$string['blockedusers'] = 'Blocked users';
 $string['blocknoncontacts'] = 'Prevent non-contacts from messaging me';
 $string['canceledit'] = 'Cancel editing messages';
 $string['contactblocked'] = 'Contact blocked';
+$string['contactrequests'] = 'Contact requests';
 $string['contacts'] = 'Contacts';
 $string['defaultmessageoutputs'] = 'Default message outputs';
 $string['defaults'] = 'Defaults';
@@ -48,10 +50,10 @@ $string['errortranslatingdefault'] = 'Error translating default setting provided
 $string['eventnotificationviewed'] = 'Notification viewed';
 $string['eventnotificationsent'] = 'Notification sent';
 $string['eventmessagecontactadded'] = 'Message contact added';
-$string['eventmessagecontactblocked'] = 'Message contact blocked';
 $string['eventmessagecontactremoved'] = 'Message contact removed';
-$string['eventmessagecontactunblocked'] = 'Message contact unblocked';
 $string['eventmessagedeleted'] = 'Message deleted';
+$string['eventmessageuserblocked'] = 'User blocked';
+$string['eventmessageuserunblocked'] = 'User unblocked';
 $string['eventmessageviewed'] = 'Message viewed';
 $string['eventmessagesent'] = 'Message sent';
 $string['forced'] = 'Locked';
@@ -69,6 +71,8 @@ $string['managemessageoutputs'] = 'Manage message outputs';
 $string['messageoutputs'] = 'Message outputs';
 $string['messagepreferences'] = 'Message preferences';
 $string['message'] = 'Message';
+$string['messagecontactrequestsnotification'] = '{$a} wants to be added as a contact';
+$string['messagecontactrequestsnotificationsubject'] = '{$a} wants to be added as a contact';
 $string['messagepreferences'] = 'Message preferences';
 $string['messages'] = 'Messages';
 $string['messagingdatahasnotbeenmigrated'] = 'Your messages are temporarily unavailable due to upgrades in the messaging infrastructure. Please wait for them to be migrated.';
@@ -107,9 +111,13 @@ $string['privacy:metadata:messages:smallmessage'] = 'A small version of the mess
 $string['privacy:metadata:messages:subject'] = 'The subject of the message';
 $string['privacy:metadata:messages:timecreated'] = 'The time when the message was created';
 $string['privacy:metadata:message_contacts'] = 'The list of contacts';
-$string['privacy:metadata:message_contacts:blocked'] = 'Flag whether or not the user is blocked';
 $string['privacy:metadata:message_contacts:contactid'] = 'The ID of the user who is a contact';
+$string['privacy:metadata:message_contacts:timecreated'] = 'The time when the contact was created';
 $string['privacy:metadata:message_contacts:userid'] = 'The ID of the user whose contact list we are viewing';
+$string['privacy:metadata:message_contact_requests'] = 'The list of contact requests';
+$string['privacy:metadata:message_contact_requests:requesteduserid'] = 'The ID of the user who received the contact request';
+$string['privacy:metadata:message_contact_requests:timecreated'] = 'The time when the contact request was created';
+$string['privacy:metadata:message_contact_requests:userid'] = 'The ID of the user who sent the contact request';
 $string['privacy:metadata:message_conversation_members'] = 'The list of users in a conversation';
 $string['privacy:metadata:message_conversation_members:conversationid'] = 'The ID of the conversation';
 $string['privacy:metadata:message_conversation_members:timecreated'] = 'The time when the member was created';
@@ -119,6 +127,10 @@ $string['privacy:metadata:message_user_actions:action'] = 'The action that was p
 $string['privacy:metadata:message_user_actions:messageid'] = 'The ID of the message this action belongs to';
 $string['privacy:metadata:message_user_actions:timecreated'] = 'The time when the action was created';
 $string['privacy:metadata:message_user_actions:userid'] = 'The ID of the user who performed this action';
+$string['privacy:metadata:message_users_blocked'] = 'The list of blocked users';
+$string['privacy:metadata:message_users_blocked:blockeduserid'] = 'The ID of the user who was blocked';
+$string['privacy:metadata:message_users_blocked:timecreated'] = 'The time when the blocked action was created';
+$string['privacy:metadata:message_users_blocked:userid'] = 'The ID of the user who performed the blocked action';
 $string['privacy:metadata:notifications'] = 'Notifications';
 $string['privacy:metadata:notifications:component'] = 'The component responsible for sending the notification';
 $string['privacy:metadata:notifications:contexturl'] = 'The URL related to this notification';
@@ -180,4 +192,6 @@ $string['writeamessage'] = 'Write a message...';
 $string['you'] = 'You:';
 
 // Deprecated since Moodle 3.6.
+$string['eventmessagecontactblocked'] = 'Message contact blocked';
+$string['eventmessagecontactunblocked'] = 'Message contact unblocked';
 $string['messagingdisabled'] = 'Messaging is disabled on this site, emails will be sent instead';
index eb72e18..1e5b746 100644 (file)
@@ -1194,6 +1194,7 @@ $string['messageprovider:courserequested'] = 'Course creation request notificati
 $string['messageprovider:courserequestrejected'] = 'Course creation request rejection notification';
 $string['messageprovider:errors'] = 'Important errors with the site';
 $string['messageprovider:errors_help'] = 'These are important errors that an administrator should know about.';
+$string['messageprovider:messagecontactrequests'] = 'Message contact requests notification';
 $string['messageprovider:notices'] = 'Notices about minor problems';
 $string['messageprovider:notices_help'] = 'These are notices that an administrator might be interested in seeing.';
 $string['messageprovider:insights'] = 'Insights generated by prediction models';
index ddbdbfb..9c1e698 100644 (file)
@@ -1054,7 +1054,7 @@ function badges_bake($hash, $badgeid, $userid = 0, $pathhash = false) {
 
     $fs = get_file_storage();
     if (!$fs->file_exists($user_context->id, 'badges', 'userbadge', $badge->id, '/', $hash . '.png')) {
-        if ($file = $fs->get_file($badge_context->id, 'badges', 'badgeimage', $badge->id, '/', 'f1.png')) {
+        if ($file = $fs->get_file($badge_context->id, 'badges', 'badgeimage', $badge->id, '/', 'f3.png')) {
             $contents = $file->get_content();
 
             $filehandler = new PNG_MetaDataHandler($contents);
index da9fc4c..1c207a3 100644 (file)
@@ -26,6 +26,9 @@ namespace core\event;
 
 defined('MOODLE_INTERNAL') || die();
 
+debugging('core\\event\\message_contact_blocked has been deprecated. Please use
+        core\\event\\message_user_blocked instead', DEBUG_DEVELOPER);
+
 /**
  * Message contact blocked event class.
  *
@@ -99,4 +102,13 @@ class message_contact_blocked extends base {
         // Messaging contacts are not backed up, so no need to map them on restore.
         return array('db' => 'message_contacts', 'restore' => base::NOT_MAPPED);
     }
+
+    /**
+     * This event has been deprecated.
+     *
+     * @return boolean
+     */
+    public static function is_deprecated() {
+        return true;
+    }
 }
index 417a1a6..61f83e6 100644 (file)
@@ -26,6 +26,9 @@ namespace core\event;
 
 defined('MOODLE_INTERNAL') || die();
 
+debugging('core\\event\\message_contact_unblocked has been deperecated. Please use
+        core\\event\\message_user_unblocked instead', DEBUG_DEVELOPER);
+
 /**
  * Message contact unblocked event class.
  *
@@ -99,4 +102,13 @@ class message_contact_unblocked extends base {
         // Messaging contacts are not backed up, so no need to map them on restore.
         return array('db' => 'message_contacts', 'restore' => base::NOT_MAPPED);
     }
+
+    /**
+     * This event has been deprecated.
+     *
+     * @return boolean
+     */
+    public static function is_deprecated() {
+        return true;
+    }
 }
diff --git a/lib/classes/event/message_user_blocked.php b/lib/classes/event/message_user_blocked.php
new file mode 100644 (file)
index 0000000..cd6abbf
--- /dev/null
@@ -0,0 +1,83 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Message user blocked event.
+ *
+ * @package    core
+ * @copyright  2018 Mark Nelson <markn@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Message user blocked event class.
+ *
+ * @package    core
+ * @since      Moodle 3.6
+ * @copyright  2018 Mark Nelson <markn@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class message_user_blocked extends base {
+
+    /**
+     * Init method.
+     */
+    protected function init() {
+        $this->data['objecttable'] = 'message_users_blocked';
+        $this->data['crud'] = 'c';
+        $this->data['edulevel'] = self::LEVEL_OTHER;
+    }
+
+    /**
+     * Returns localised general event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventmessageusersblocked', 'message');
+    }
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "The user with id '$this->userid' blocked the user with id '$this->relateduserid'.";
+    }
+
+    /**
+     * Custom validation.
+     *
+     * @throws \coding_exception
+     */
+    protected function validate_data() {
+        parent::validate_data();
+
+        if (!isset($this->relateduserid)) {
+            throw new \coding_exception('The \'relateduserid\' must be set.');
+        }
+    }
+
+    public static function get_objectid_mapping() {
+        // Blocked users are not backed up, so no need to map them on restore.
+        return array('db' => 'message_users_blocked', 'restore' => base::NOT_MAPPED);
+    }
+}
diff --git a/lib/classes/event/message_user_unblocked.php b/lib/classes/event/message_user_unblocked.php
new file mode 100644 (file)
index 0000000..2463c80
--- /dev/null
@@ -0,0 +1,83 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Message users unblocked event.
+ *
+ * @package    core
+ * @copyright  2018 Mark Nelson <markn@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Message users unblocked event class.
+ *
+ * @package    core
+ * @since      Moodle 3.6
+ * @copyright  2018 Mark Nelson <markn@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class message_user_unblocked extends base {
+
+    /**
+     * Init method.
+     */
+    protected function init() {
+        $this->data['objecttable'] = 'message_users_blocked';
+        $this->data['crud'] = 'd';
+        $this->data['edulevel'] = self::LEVEL_OTHER;
+    }
+
+    /**
+     * Returns localised general event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventmessageusersunblocked', 'message');
+    }
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "The user with id '$this->userid' unblocked the user with id '$this->relateduserid'.";
+    }
+
+    /**
+     * Custom validation.
+     *
+     * @throws \coding_exception
+     */
+    protected function validate_data() {
+        parent::validate_data();
+
+        if (!isset($this->relateduserid)) {
+            throw new \coding_exception('The \'relateduserid\' must be set.');
+        }
+    }
+
+    public static function get_objectid_mapping() {
+        // Messaging contacts are not backed up, so no need to map them on restore.
+        return array('db' => 'message_users_blocked', 'restore' => base::NOT_MAPPED);
+    }
+}
index 2101c71..455b29c 100644 (file)
         <KEY NAME="useridto" TYPE="foreign" FIELDS="useridto" REFTABLE="user" REFFIELDS="id"/>
       </KEYS>
     </TABLE>
-    <TABLE NAME="message_contacts" COMMENT="Maintains lists of relationships between users">
+    <TABLE NAME="message_contacts" COMMENT="Maintains lists of contacts between users">
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
-        <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
-        <FIELD NAME="contactid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
-        <FIELD NAME="blocked" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
+        <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="contactid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/>
       </FIELDS>
       <KEYS>
         <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+        <KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id"/>
+        <KEY NAME="contactid" TYPE="foreign" FIELDS="contactid" REFTABLE="user" REFFIELDS="id"/>
       </KEYS>
       <INDEXES>
         <INDEX NAME="userid-contactid" UNIQUE="true" FIELDS="userid, contactid"/>
       </INDEXES>
     </TABLE>
+    <TABLE NAME="message_contact_requests" COMMENT="Maintains list of contact requests between users">
+      <FIELDS>
+        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
+        <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="requesteduserid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+      </FIELDS>
+      <KEYS>
+        <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+        <KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id"/>
+        <KEY NAME="requesteduserid" TYPE="foreign" FIELDS="requesteduserid" REFTABLE="user" REFFIELDS="id"/>
+      </KEYS>
+      <INDEXES>
+        <INDEX NAME="userid-requesteduserid" UNIQUE="true" FIELDS="userid, requesteduserid"/>
+      </INDEXES>
+    </TABLE>
+    <TABLE NAME="message_users_blocked" COMMENT="Maintains lists of blocked users">
+      <FIELDS>
+        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
+        <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="blockeduserid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/>
+      </FIELDS>
+      <KEYS>
+        <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+        <KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id"/>
+        <KEY NAME="blockeduserid" TYPE="foreign" FIELDS="blockeduserid" REFTABLE="user" REFFIELDS="id"/>
+      </KEYS>
+      <INDEXES>
+        <INDEX NAME="userid-blockeduserid" UNIQUE="true" FIELDS="userid, blockeduserid"/>
+      </INDEXES>
+    </TABLE>
     <TABLE NAME="modules" COMMENT="modules available in the site">
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
index 68ac95f..7a08841 100644 (file)
@@ -105,5 +105,13 @@ $messageproviders = array (
     // User insights.
     'insights' => array (
          'capability'  => 'moodle/analytics:listinsights'
-    )
+    ),
+
+    // Message contact requests.
+    'messagecontactrequests' => [
+        'defaults' => [
+            'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_LOGGEDIN + MESSAGE_DEFAULT_LOGGEDOFF,
+            'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_LOGGEDOFF,
+        ]
+    ],
 );
index 08195cc..2334240 100644 (file)
@@ -821,11 +821,21 @@ $functions = array(
         'description' => 'Updates existing groupings',
         'type' => 'write',
     ),
+    'core_message_block_user' => array(
+        'classname' => 'core_message_external',
+        'methodname' => 'block_user',
+        'classpath' => 'message/externallib.php',
+        'description' => 'Blocks a user',
+        'type' => 'write',
+        'ajax' => true,
+        'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
+    ),
     'core_message_block_contacts' => array(
         'classname' => 'core_message_external',
         'methodname' => 'block_contacts',
         'classpath' => 'message/externallib.php',
-        'description' => 'Block contacts',
+        'description' => '** DEPRECATED ** Please do not call this function any more.
+                          Block contacts',
         'type' => 'write',
         'ajax' => true,
         'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
@@ -834,11 +844,47 @@ $functions = array(
         'classname' => 'core_message_external',
         'methodname' => 'create_contacts',
         'classpath' => 'message/externallib.php',
-        'description' => 'Add contacts to the contact list',
+        'description' => '** DEPRECATED ** Please do not call this function any more.
+                          Add contacts to the contact list',
         'type' => 'write',
         'ajax' => true,
         'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
+    ),
+    'core_message_get_contact_requests' => array(
+        'classname' => 'core_message_external',
+        'methodname' => 'get_contact_requests',
+        'classpath' => 'message/externallib.php',
+        'description' => 'Returns contact requests for a user',
+        'type' => 'read',
+        'ajax' => true,
+        'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
+    ),
+    'core_message_create_contact_request' => array(
+        'classname' => 'core_message_external',
+        'methodname' => 'create_contact_request',
+        'classpath' => 'message/externallib.php',
+        'description' => 'Creates a contact request',
+        'type' => 'write',
         'ajax' => true,
+        'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
+    ),
+    'core_message_confirm_contact_request' => array(
+        'classname' => 'core_message_external',
+        'methodname' => 'confirm_contact_request',
+        'classpath' => 'message/externallib.php',
+        'description' => 'Confirms a contact request',
+        'type' => 'write',
+        'ajax' => true,
+        'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
+    ),
+    'core_message_decline_contact_request' => array(
+        'classname' => 'core_message_external',
+        'methodname' => 'decline_contact_request',
+        'classpath' => 'message/externallib.php',
+        'description' => 'Declines a contact request',
+        'type' => 'write',
+        'ajax' => true,
+        'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
     ),
     'core_message_delete_contacts' => array(
         'classname' => 'core_message_external',
@@ -848,7 +894,6 @@ $functions = array(
         'type' => 'write',
         'ajax' => true,
         'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
-        'ajax' => true,
     ),
     'core_message_delete_conversation' => array(
         'classname' => 'core_message_external',
@@ -1043,11 +1088,21 @@ $functions = array(
         'ajax' => true,
         'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
     ),
+    'core_message_unblock_user' => array(
+        'classname' => 'core_message_external',
+        'methodname' => 'unblock_user',
+        'classpath' => 'message/externallib.php',
+        'description' => 'Unblocks a user',
+        'type' => 'write',
+        'ajax' => true,
+        'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
+    ),
     'core_message_unblock_contacts' => array(
         'classname' => 'core_message_external',
         'methodname' => 'unblock_contacts',
         'classpath' => 'message/externallib.php',
-        'description' => 'Unblock contacts',
+        'description' => '** DEPRECATED ** Please do not call this function any more.
+                         Unblock contacts',
         'type' => 'write',
         'ajax' => true,
         'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
index 7f6a1f1..a6087d3 100644 (file)
@@ -2167,7 +2167,7 @@ function xmldb_main_upgrade($oldversion) {
         $table = new xmldb_table('message_user_actions');
 
         // Conditionally launch add index.
-        $index = new xmldb_index('userid_messageid_action', XMLDB_INDEX_UNIQUE, array('userid, messageid, action'));
+        $index = new xmldb_index('userid_messageid_action', XMLDB_INDEX_UNIQUE, array('userid', 'messageid', 'action'));
         if (!$dbman->index_exists($table, $index)) {
             $dbman->add_index($table, $index);
         }
@@ -2181,7 +2181,7 @@ function xmldb_main_upgrade($oldversion) {
         $table = new xmldb_table('messages');
 
         // Conditionally launch add index.
-        $index = new xmldb_index('conversationid_timecreated', XMLDB_INDEX_NOTUNIQUE, array('conversationidtimecreated'));
+        $index = new xmldb_index('conversationid_timecreated', XMLDB_INDEX_NOTUNIQUE, array('conversationid', 'timecreated'));
         if (!$dbman->index_exists($table, $index)) {
             $dbman->add_index($table, $index);
         }
@@ -2202,7 +2202,7 @@ function xmldb_main_upgrade($oldversion) {
 
     if ($oldversion < 2018040500.01) {
 
-        // Define field indexpriority to be added to search_index_requests. Allow null initially.
+        // Define field cohort to be added to theme. Allow null initially.
         $table = new xmldb_table('cohort');
         $field = new xmldb_field('theme', XMLDB_TYPE_CHAR, '50',
                 null, null, null, null, 'timemodified');
@@ -2324,6 +2324,7 @@ function xmldb_main_upgrade($oldversion) {
                 $DB->delete_records('blog_association', ['contextid' => $module->modcontextid]);
             }
         }
+
         // Main savepoint reached.
         upgrade_main_savepoint(true, 2018083100.01);
     }
@@ -2373,7 +2374,7 @@ function xmldb_main_upgrade($oldversion) {
 
     if ($oldversion < 2018092100.02) {
         $table = new xmldb_table('question');
-        $index = new xmldb_index('categoryidnumber', XMLDB_INDEX_UNIQUE, array('categoryidnumber'));
+        $index = new xmldb_index('categoryidnumber', XMLDB_INDEX_UNIQUE, array('category', 'idnumber'));
         if (!$dbman->index_exists($table, $index)) {
             $dbman->add_index($table, $index);
         }
@@ -2391,7 +2392,7 @@ function xmldb_main_upgrade($oldversion) {
 
     if ($oldversion < 2018092100.04) {
         $table = new xmldb_table('question_categories');
-        $index = new xmldb_index('contextididnumber', XMLDB_INDEX_UNIQUE, array('contextididnumber'));
+        $index = new xmldb_index('contextididnumber', XMLDB_INDEX_UNIQUE, array('contextid', 'idnumber'));
         if (!$dbman->index_exists($table, $index)) {
             $dbman->add_index($table, $index);
         }
@@ -2399,5 +2400,129 @@ function xmldb_main_upgrade($oldversion) {
         upgrade_main_savepoint(true, 2018092100.04);
     }
 
+    if ($oldversion < 2018092800.00) {
+        // Alter the table 'message_contacts'.
+        $table = new xmldb_table('message_contacts');
+
+        // Remove index so we can alter the fields.
+        $index = new xmldb_index('userid-contactid', XMLDB_INDEX_UNIQUE, ['userid', 'contactid']);
+        if ($dbman->index_exists($table, $index)) {
+            $dbman->drop_index($table, $index);
+        }
+
+        // Remove defaults of '0' from the 'userid' and 'contactid' fields.
+        $field = new xmldb_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'id');
+        $dbman->change_field_default($table, $field);
+
+        $field = new xmldb_field('contactid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'userid');
+        $dbman->change_field_default($table, $field);
+
+        // Add the missing FKs that will now be added to new installs.
+        $key = new xmldb_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']);
+        $dbman->add_key($table, $key);
+
+        $key = new xmldb_key('contactid', XMLDB_KEY_FOREIGN, ['contactid'], 'user', ['id']);
+        $dbman->add_key($table, $key);
+
+        // Re-add the index.
+        if (!$dbman->index_exists($table, $index)) {
+            $dbman->add_index($table, $index);
+        }
+
+        // Add the field 'timecreated'. Allow null, since existing records won't have an accurate value we can use.
+        $field = new xmldb_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'blocked');
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // Create new 'message_contact_requests' table.
+        $table = new xmldb_table('message_contact_requests');
+        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null, null);
+        $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'id');
+        $table->add_field('requesteduserid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'userid');
+        $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'requesteduserid');
+
+        $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id'], null, null);
+        $table->add_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']);
+        $table->add_key('requesteduserid', XMLDB_KEY_FOREIGN, ['requesteduserid'], 'user', ['id']);
+
+        $table->add_index('userid-requesteduserid', XMLDB_INDEX_UNIQUE, ['userid', 'requesteduserid']);
+
+        if (!$dbman->table_exists($table)) {
+            $dbman->create_table($table);
+        }
+
+        // Create new 'message_users_blocked' table.
+        $table = new xmldb_table('message_users_blocked');
+        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null, null);
+        $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'id');
+        $table->add_field('blockeduserid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'userid');
+        // Allow NULLs in the 'timecreated' field because we will be moving existing data here that has no timestamp.
+        $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'blockeduserid');
+
+        $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id'], null, null);
+        $table->add_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']);
+        $table->add_key('blockeduserid', XMLDB_KEY_FOREIGN, ['blockeduserid'], 'user', ['id']);
+
+        $table->add_index('userid-blockeduserid', XMLDB_INDEX_UNIQUE, ['userid', 'blockeduserid']);
+
+        if (!$dbman->table_exists($table)) {
+            $dbman->create_table($table);
+        }
+
+        upgrade_main_savepoint(true, 2018092800.00);
+    }
+
+    if ($oldversion < 2018092800.01) {
+        // Move all the 'blocked' contacts to the new table 'message_users_blocked'.
+        $updatesql = "INSERT INTO {message_users_blocked} (userid, blockeduserid, timecreated)
+                           SELECT userid, contactid, null as timecreated
+                             FROM {message_contacts}
+                            WHERE blocked = :blocked";
+        $DB->execute($updatesql, ['blocked' => 1]);
+
+        // Removed the 'blocked' column from 'message_contacts'.
+        $table = new xmldb_table('message_contacts');
+        $field = new xmldb_field('blocked');
+        $dbman->drop_field($table, $field);
+
+        upgrade_main_savepoint(true, 2018092800.01);
+    }
+
+    if ($oldversion < 2018092800.02) {
+        // Delete any contacts that are not mutual (meaning they both haven't added each other).
+        $sql = "SELECT c1.id
+                  FROM {message_contacts} c1
+             LEFT JOIN {message_contacts} c2
+                    ON c1.userid = c2.contactid
+                   AND c1.contactid = c2.userid
+                 WHERE c2.id IS NULL";
+        if ($contacts = $DB->get_records_sql($sql)) {
+            list($insql, $inparams) = $DB->get_in_or_equal(array_keys($contacts));
+            $DB->delete_records_select('message_contacts', "id $insql", $inparams);
+        }
+
+        upgrade_main_savepoint(true, 2018092800.02);
+    }
+
+    if ($oldversion < 2018092800.03) {
+        // Remove any duplicate rows - from now on adding contacts just requires 1 row.
+        // The person who made the contact request (userid) and the person who approved
+        // it (contactid). Upgrade the table so that the first person to add the contact
+        // was the one who made the request.
+        $sql = "SELECT c1.id
+                  FROM {message_contacts} c1
+            INNER JOIN {message_contacts} c2
+                    ON c1.userid = c2.contactid
+                   AND c1.contactid = c2.userid
+                 WHERE c1.id > c2.id";
+        if ($contacts = $DB->get_records_sql($sql)) {
+            list($insql, $inparams) = $DB->get_in_or_equal(array_keys($contacts));
+            $DB->delete_records_select('message_contacts', "id $insql", $inparams);
+        }
+
+        upgrade_main_savepoint(true, 2018092800.03);
+    }
+
     return true;
 }
index ca248e7..44e8b8b 100644 (file)
@@ -3455,3 +3455,159 @@ function get_roles_with_assignment_on_context(context $context) {
 
     return get_roles_used_in_context($context, false);
 }
+
+/**
+ * Add the selected user as a contact for the current user
+ *
+ * @deprecated since Moodle 3.6
+ * @param int $contactid the ID of the user to add as a contact
+ * @param int $blocked 1 if you wish to block the contact
+ * @param int $userid the user ID of the user we want to add the contact for, defaults to current user if not specified.
+ * @return bool/int false if the $contactid isnt a valid user id. True if no changes made.
+ *                  Otherwise returns the result of update_record() or insert_record()
+ */
+function message_add_contact($contactid, $blocked = 0, $userid = 0) {
+    debugging('message_add_contact() is deprecated. Please use \core_message\api::create_contact_request() instead. ' .
+        'If you wish to block or unblock a user please use \core_message\api::is_blocked() and ' .
+        '\core_message\api::block_user() or \core_message\api::unblock_user() respectively.', DEBUG_DEVELOPER);
+
+    global $USER, $DB;
+
+    if (!$DB->record_exists('user', array('id' => $contactid))) {
+        return false;
+    }
+
+    if (empty($userid)) {
+        $userid = $USER->id;
+    }
+
+    // Check if a record already exists as we may be changing blocking status.
+    if (\core_message\api::is_contact($userid, $contactid)) {
+        $isblocked = \core_message\api::is_blocked($userid, $contactid);
+        // Check if blocking status has been changed.
+        if ($isblocked != $blocked) {
+            if ($blocked == 1) {
+                if (!$isblocked) {
+                    \core_message\api::block_user($userid, $contactid);
+                }
+            } else {
+                \core_message\api::unblock_user($userid, $contactid);
+            }
+
+            return true;
+        } else {
+            // No change to blocking status.
+            return true;
+        }
+    } else {
+        if ($blocked == 1) {
+            if (!\core_message\api::is_blocked($userid, $contactid)) {
+                \core_message\api::block_user($userid, $contactid);
+            }
+        } else {
+            \core_message\api::unblock_user($userid, $contactid);
+            if (!\core_message\api::does_contact_request_exist($userid, $contactid)) {
+                \core_message\api::create_contact_request($userid, $contactid);
+            }
+        }
+
+        return true;
+    }
+}
+
+/**
+ * Remove a contact.
+ *
+ * @deprecated since Moodle 3.6
+ * @param int $contactid the user ID of the contact to remove
+ * @param int $userid the user ID of the user we want to remove the contacts for, defaults to current user if not specified.
+ * @return bool returns the result of delete_records()
+ */
+function message_remove_contact($contactid, $userid = 0) {
+    debugging('message_remove_contact() is deprecated. Please use \core_message\api::remove_contact() instead.',
+        DEBUG_DEVELOPER);
+
+    global $USER;
+
+    if (empty($userid)) {
+        $userid = $USER->id;
+    }
+
+    \core_message\api::remove_contact($userid, $contactid);
+
+    return true;
+}
+
+/**
+ * Unblock a contact.
+ *
+ * @deprecated since Moodle 3.6
+ * @param int $contactid the user ID of the contact to unblock
+ * @param int $userid the user ID of the user we want to unblock the contact for, defaults to current user
+ *  if not specified.
+ * @return bool returns the result of delete_records()
+ */
+function message_unblock_contact($contactid, $userid = 0) {
+    debugging('message_unblock_contact() is deprecated. Please use \core_message\api::unblock_user() instead.',
+        DEBUG_DEVELOPER);
+
+    global $DB, $USER;
+
+    if (!$DB->record_exists('user', array('id' => $contactid))) {
+        return false;
+    }
+
+    if (empty($userid)) {
+        $userid = $USER->id;
+    }
+
+    \core_message\api::unblock_user($userid, $contactid);
+
+    return true;
+}
+
+/**
+ * Block a user.
+ *
+ * @deprecated since Moodle 3.6
+ * @param int $contactid the user ID of the user to block
+ * @param int $userid the user ID of the user we want to unblock the contact for, defaults to current user
+ *  if not specified.
+ * @return bool
+ */
+function message_block_contact($contactid, $userid = 0) {
+    debugging('message_block_contact() is deprecated. Please use \core_message\api::is_blocked() and ' .
+        '\core_message\api::block_user() instead.', DEBUG_DEVELOPER);
+
+    global $DB, $USER;
+
+    if (!$DB->record_exists('user', array('id' => $contactid))) {
+        return false;
+    }
+
+    if (empty($userid)) {
+        $userid = $USER->id;
+    }
+
+    if (!\core_message\api::is_blocked($userid, $contactid)) {
+        \core_message\api::block_user($userid, $contactid);
+    }
+
+    return true;
+}
+
+/**
+ * Load a user's contact record
+ *
+ * @deprecated since Moodle 3.6
+ * @param int $contactid the user ID of the user whose contact record you want
+ * @return array message contacts
+ */
+function message_get_contact($contactid) {
+    debugging('message_get_contact() is deprecated. Please use \core_message\api::get_contact() instead.',
+        DEBUG_DEVELOPER);
+
+    global $USER;
+
+    return \core_message\api::get_contact($USER->id, $contactid);
+}
index 454adf3..465e78f 100644 (file)
@@ -4182,7 +4182,7 @@ EOD;
 
                 // Check to see if we should be displaying a message button.
                 if (!empty($CFG->messaging) && $USER->id != $user->id && has_capability('moodle/site:sendmessage', $context)) {
-                    $iscontact = !empty(message_get_contact($user->id));
+                    $iscontact = \core_message\api::is_contact($USER->id, $user->id);
                     $contacttitle = $iscontact ? 'removefromyourcontacts' : 'addtoyourcontacts';
                     $contacturlaction = $iscontact ? 'removecontact' : 'addcontact';
                     $contactimage = $iscontact ? 'removecontact' : 'addcontact';
index 6d6ecec..288ed76 100644 (file)
@@ -130,6 +130,11 @@ any group. Besides, groups_get_members_ids_sql, get_enrolled_sql and get_enrolle
 the groupid field.
 * Added $CFG->conversionattemptlimit setting to config.php allowing a maximum number of retries before giving up conversion
   of a given document by the assignfeedback_editpdf\task\convert_submissions task. Default value: 3.
+* The following events have been deprecated and should not be used any more:
+  - message_contact_blocked
+  - message_contact_unblocked
+  The reason for this is because you can now block/unblock users without them necessarily being a contact. These events
+  have been replaced with message_user_blocked and message_user_unblocked respectively.
 
 === 3.5 ===
 
index 1e38b6b..c99cadb 100644 (file)
Binary files a/message/amd/build/message_area_profile.min.js and b/message/amd/build/message_area_profile.min.js differ
index c4cedfb..eb73dcb 100644 (file)
@@ -153,7 +153,7 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/str'
          * @private
          */
         Profile.prototype._blockContact = function() {
-            var action = this._performAction('core_message_block_contacts', 'unblockcontact', 'profile-block-contact',
+            var action = this._performAction('core_message_block_user', 'unblockcontact', 'profile-block-contact',
                 'profile-unblock-contact', '');
             return action.then(function() {
                 this.messageArea.trigger(Events.CONTACTBLOCKED, this._getUserId());
@@ -167,7 +167,7 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/str'
          * @private
          */
         Profile.prototype._unblockContact = function() {
-            var action = this._performAction('core_message_unblock_contacts', 'blockcontact', 'profile-unblock-contact',
+            var action = this._performAction('core_message_unblock_user', 'blockcontact', 'profile-unblock-contact',
                 'profile-block-contact', 'danger');
             return action.then(function() {
                 this.messageArea.trigger(Events.CONTACTUNBLOCKED, this._getUserId());
@@ -181,7 +181,7 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/str'
          * @private
          */
         Profile.prototype._addContact = function() {
-            var action = this._performAction('core_message_create_contacts', 'removecontact', 'profile-add-contact',
+            var action = this._performAction('core_message_create_contact_request', 'removecontact', 'profile-add-contact',
                 'profile-remove-contact', 'danger');
             return action.then(function() {
                 this.messageArea.trigger(Events.CONTACTADDED, this._getUserId());
@@ -214,14 +214,40 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/str'
          * @private
          */
         Profile.prototype._performAction = function(service, string, oldaction, newaction, newclass) {
+            // This is a temporary hack as we are getting rid of this UI.
+            var userargs = '';
+            switch (service) {
+                case 'core_message_block_user':
+                    userargs = {
+                        userid: this.messageArea.getCurrentUserId(),
+                        blockeduserid: this._getUserId()
+                    };
+                    break;
+                case 'core_message_unblock_user':
+                    userargs = {
+                        userid: this.messageArea.getCurrentUserId(),
+                        unblockeduserid: this._getUserId()
+                    };
+                    break;
+                case 'core_message_create_contact_request':
+                    userargs = {
+                        userid: this.messageArea.getCurrentUserId(),
+                        requesteduserid: this._getUserId()
+                    };
+                    break;
+                default:
+                    userargs = {
+                        userid: this.messageArea.getCurrentUserId(),
+                        userids: [
+                            this._getUserId()
+                        ]
+                    };
+            }
+
+
             var promises = Ajax.call([{
                 methodname: service,
-                args: {
-                    userid: this.messageArea.getCurrentUserId(),
-                    userids: [
-                        this._getUserId()
-                    ]
-                }
+                args: userargs
             }]);
 
             return promises[0].then(function() {
index 51a067a..8bc93a7 100644 (file)
@@ -63,8 +63,8 @@ class api {
         $ufields2 = \user_picture::fields('u2', array('lastaccess'), 'userto_id', 'userto_');
 
         $sql = "SELECT m.id, m.useridfrom, mcm.userid as useridto, m.subject, m.fullmessage, m.fullmessagehtml, m.fullmessageformat,
-                       m.smallmessage, m.timecreated, 0 as isread, $ufields, mcont.blocked as userfrom_blocked, $ufields2,
-                       mcont2.blocked as userto_blocked
+                       m.smallmessage, m.timecreated, 0 as isread, $ufields, mub.id as userfrom_blocked, $ufields2,
+                       mub2.id as userto_blocked
                   FROM {messages} m
             INNER JOIN {user} u
                     ON u.id = m.useridfrom
@@ -74,10 +74,10 @@ class api {
                     ON mcm.conversationid = m.conversationid
             INNER JOIN {user} u2
                     ON u2.id = mcm.userid
-             LEFT JOIN {message_contacts} mcont
-                    ON (mcont.contactid = u.id AND mcont.userid = ?)
-             LEFT JOIN {message_contacts} mcont2
-                    ON (mcont2.contactid = u2.id AND mcont2.userid = ?)
+             LEFT JOIN {message_users_blocked} mub
+                    ON (mub.blockeduserid = u.id AND mub.userid = ?)
+             LEFT JOIN {message_users_blocked} mub2
+                    ON (mub2.blockeduserid = u2.id AND mub2.userid = ?)
              LEFT JOIN {message_user_actions} mua
                     ON (mua.messageid = m.id AND mua.userid = ? AND mua.action = ?)
                  WHERE (m.useridfrom = ? OR mcm.userid = ?)
@@ -101,7 +101,7 @@ class api {
                     $message->isread = true;
                 }
                 $blockedcol = $prefix . 'blocked';
-                $message->blocked = $message->$blockedcol;
+                $message->blocked = $message->$blockedcol ? 1 : 0;
 
                 $message->messageid = $message->id;
                 $conversations[] = helper::create_contact($message, $prefix);
@@ -126,12 +126,12 @@ class api {
 
         // Get all the users in the course.
         list($esql, $params) = get_enrolled_sql(\context_course::instance($courseid), '', 0, true);
-        $sql = "SELECT u.*, mc.blocked
+        $sql = "SELECT u.*, mub.id as isblocked
                   FROM {user} u
                   JOIN ($esql) je
                     ON je.id = u.id
-             LEFT JOIN {message_contacts} mc
-                    ON (mc.contactid = u.id AND mc.userid = :userid)
+             LEFT JOIN {message_users_blocked} mub
+                    ON (mub.blockeduserid = u.id AND mub.userid = :userid)
                  WHERE u.deleted = 0";
         // Add more conditions.
         $fullname = $DB->sql_fullname();
@@ -144,6 +144,7 @@ class api {
         $contacts = array();
         if ($users = $DB->get_records_sql($sql, $params, $limitfrom, $limitnum)) {
             foreach ($users as $user) {
+                $user->blocked = $user->isblocked ? 1 : 0;
                 $contacts[] = helper::create_contact($user);
             }
         }
@@ -172,19 +173,22 @@ class api {
 
         // Ok, let's search for contacts first.
         $contacts = array();
-        $sql = "SELECT $ufields, mc.blocked
+        $sql = "SELECT $ufields, mub.id as isuserblocked
                   FROM {user} u
                   JOIN {message_contacts} mc
                     ON u.id = mc.contactid
+             LEFT JOIN {message_users_blocked} mub
+                    ON (mub.userid = :userid2 AND mub.blockeduserid = u.id)
                  WHERE mc.userid = :userid
                    AND u.deleted = 0
                    AND u.confirmed = 1
                    AND " . $DB->sql_like($fullname, ':search', false) . "
                    AND u.id $exclude
               ORDER BY " . $DB->sql_fullname();
-        if ($users = $DB->get_records_sql($sql, array('userid' => $userid, 'search' => '%' . $search . '%') + $excludeparams,
-            0, $limitnum)) {
+        if ($users = $DB->get_records_sql($sql, array('userid' => $userid, 'userid2' => $userid,
+                'search' => '%' . $search . '%') + $excludeparams, 0, $limitnum)) {
             foreach ($users as $user) {
+                $user->blocked = $user->isuserblocked ? 1 : 0;
                 $contacts[] = helper::create_contact($user);
             }
         }
@@ -225,7 +229,7 @@ class api {
                                      WHERE userid = :userid)
               ORDER BY " . $DB->sql_fullname();
         if ($users = $DB->get_records_sql($sql,  array('userid' => $userid, 'search' => '%' . $search . '%') + $excludeparams,
-            0, $limitnum)) {
+                0, $limitnum)) {
             foreach ($users as $user) {
                 $noncontacts[] = helper::create_contact($user);
             }
@@ -324,7 +328,7 @@ class api {
             return [];
         }
 
-        $contactssql = "SELECT contactid, blocked
+        $contactssql = "SELECT contactid
                           FROM {message_contacts}
                          WHERE userid = ?
                            AND contactid $useridsql";
@@ -401,21 +405,41 @@ class api {
     public static function get_contacts($userid, $limitfrom = 0, $limitnum = 0) {
         global $DB;
 
-        $arrcontacts = array();
-        $sql = "SELECT u.*, mc.blocked
+        $contactids = [];
+        $sql = "SELECT mc.*
                   FROM {message_contacts} mc
-                  JOIN {user} u
-                    ON mc.contactid = u.id
-                 WHERE mc.userid = :userid
-                   AND u.deleted = 0
-              ORDER BY " . $DB->sql_fullname();
-        if ($contacts = $DB->get_records_sql($sql, array('userid' => $userid), $limitfrom, $limitnum)) {
+                 WHERE mc.userid = ? OR mc.contactid = ?
+              ORDER BY timecreated DESC";
+        if ($contacts = $DB->get_records_sql($sql, [$userid, $userid], $limitfrom, $limitnum)) {
             foreach ($contacts as $contact) {
-                $arrcontacts[] = helper::create_contact($contact);
+                if ($userid == $contact->userid) {
+                    $contactids[] = $contact->contactid;
+                } else {
+                    $contactids[] = $contact->userid;
+                }
+            }
+        }
+
+        if (!empty($contactids)) {
+            list($insql, $inparams) = $DB->get_in_or_equal($contactids);
+
+            $sql = "SELECT u.*, mub.id as isblocked
+                      FROM {user} u
+                 LEFT JOIN {message_users_blocked} mub
+                        ON u.id = mub.blockeduserid
+                     WHERE u.id $insql";
+            if ($contacts = $DB->get_records_sql($sql, $inparams)) {
+                $arrcontacts = [];
+                foreach ($contacts as $contact) {
+                    $contact->blocked = $contact->isblocked ? 1 : 0;
+                    $arrcontacts[] = helper::create_contact($contact);
+                }
+
+                return $arrcontacts;
             }
         }
 
-        return $arrcontacts;
+        return [];
     }
 
     /**
@@ -434,21 +458,24 @@ class api {
         $unreadcountssql = "SELECT $userfields, count(m.id) as messagecount
                               FROM {message_contacts} mc
                         INNER JOIN {user} u
-                                ON u.id = mc.contactid
+                                ON (u.id = mc.contactid OR u.id = mc.userid)
                          LEFT JOIN {messages} m
-                                ON m.useridfrom = mc.contactid
+                                ON ((m.useridfrom = mc.contactid OR m.useridfrom = mc.userid) AND m.useridfrom != ?)
                          LEFT JOIN {message_conversation_members} mcm
                                 ON mcm.conversationid = m.conversationid AND mcm.userid = ? AND mcm.userid != m.useridfrom
                          LEFT JOIN {message_user_actions} mua
                                 ON (mua.messageid = m.id AND mua.userid = ? AND mua.action = ?)
+                         LEFT JOIN {message_users_blocked} mub
+                                ON (mub.userid = ? AND mub.blockeduserid = u.id)
                              WHERE mua.id is NULL
-                               AND mc.userid = ?
-                               AND mc.blocked = 0
+                               AND mub.id is NULL
+                               AND (mc.userid = ? OR mc.contactid = ?)
+                               AND u.id != ?
                                AND u.deleted = 0
                           GROUP BY $userfields";
 
-        return $DB->get_records_sql($unreadcountssql, [$userid, $userid, self::MESSAGE_ACTION_READ,
-            $userid, $userid], $limitfrom, $limitnum);
+        return $DB->get_records_sql($unreadcountssql, [$userid, $userid, $userid, self::MESSAGE_ACTION_READ,
+            $userid, $userid, $userid, $userid], $limitfrom, $limitnum);
     }
 
     /**
@@ -474,14 +501,17 @@ class api {
                                 ON (mua.messageid = m.id AND mua.userid = ? AND mua.action = ?)
                          LEFT JOIN {message_contacts} mc
                                 ON (mc.userid = ? AND mc.contactid = u.id)
+                         LEFT JOIN {message_users_blocked} mub
+                                ON (mub.userid = ? AND mub.blockeduserid = u.id)
                              WHERE mcm.userid = ?
                                AND mcm.userid != m.useridfrom
                                AND mua.id is NULL
+                               AND mub.id is NULL
                                AND mc.id is NULL
                                AND u.deleted = 0
                           GROUP BY $userfields";
 
-        return $DB->get_records_sql($unreadcountssql, [$userid, self::MESSAGE_ACTION_READ, $userid, $userid],
+        return $DB->get_records_sql($unreadcountssql, [$userid, self::MESSAGE_ACTION_READ, $userid, $userid, $userid],
             $limitfrom, $limitnum);
     }
 
@@ -550,7 +580,7 @@ class api {
      * @return \stdClass
      */
     public static function get_profile($userid, $otheruserid) {
-        global $CFG, $DB, $PAGE;
+        global $CFG, $PAGE;
 
         require_once($CFG->dirroot . '/user/lib.php');
 
@@ -587,15 +617,8 @@ class api {
             }
         }
 
-        // Check if the contact has been blocked.
-        $contact = $DB->get_record('message_contacts', array('userid' => $userid, 'contactid' => $otheruserid));
-        if ($contact) {
-            $data->isblocked = (bool) $contact->blocked;
-            $data->iscontact = true;
-        } else {
-            $data->isblocked = false;
-            $data->iscontact = false;
-        }
+        $data->isblocked = self::is_blocked($userid, $otheruserid);
+        $data->iscontact = self::is_contact($userid, $otheruserid);
 
         return $data;
     }
@@ -854,9 +877,9 @@ class api {
             $user = $USER;
         }
 
-        $sql = "SELECT count(mc.id)
-                  FROM {message_contacts} mc
-                 WHERE mc.userid = :userid AND mc.blocked = 1";
+        $sql = "SELECT count(mub.id)
+                  FROM {message_users_blocked} mub
+                 WHERE mub.userid = :userid";
         return $DB->count_records_sql($sql, array('userid' => $user->id));
     }
 
@@ -890,8 +913,14 @@ class api {
         if ($sender !== null && isset($sender->id)) {
             $senderid = $sender->id;
         }
+
+        $systemcontext = \context_system::instance();
+        if (has_capability('moodle/site:readallmessages', $systemcontext, $senderid)) {
+            return true;
+        }
+
         // The recipient has specifically blocked this sender.
-        if (self::is_user_blocked($recipient->id, $senderid)) {
+        if (self::is_blocked($recipient->id, $senderid)) {
             return false;
         }
 
@@ -908,7 +937,7 @@ class api {
      * @return bool true if $sender is blocked, false otherwise.
      */
     public static function is_user_non_contact_blocked($recipient, $sender = null) {
-        global $USER, $DB;
+        global $USER;
 
         if (is_null($sender)) {
             // The message is from the logged in user, unless otherwise specified.
@@ -918,8 +947,7 @@ class api {
         $blockednoncontacts = get_user_preferences('message_blocknoncontacts', '', $recipient->id);
         if (!empty($blockednoncontacts)) {
             // Confirm the sender is a contact of the recipient.
-            $exists = $DB->record_exists('message_contacts', array('userid' => $recipient->id, 'contactid' => $sender->id));
-            if ($exists) {
+            if (self::is_contact($sender->id, $recipient->id)) {
                 // All good, the recipient is a contact of the sender.
                 return false;
             } else {
@@ -937,12 +965,16 @@ class api {
      * Note: This function will always return false if the sender has the
      * readallmessages capability at the system context level.
      *
+     * @deprecated since 3.6
      * @param int $recipientid User ID of the recipient.
      * @param int $senderid User ID of the sender.
      * @return bool true if $sender is blocked, false otherwise.
      */
     public static function is_user_blocked($recipientid, $senderid = null) {
-        global $USER, $DB;
+        debugging('\core_message\api::is_user_blocked is deprecated and should not be used.',
+            DEBUG_DEVELOPER);
+
+        global $USER;
 
         if (is_null($senderid)) {
             // The message is from the logged in user, unless otherwise specified.
@@ -954,7 +986,7 @@ class api {
             return false;
         }
 
-        if ($DB->get_field('message_contacts', 'blocked', ['userid' => $recipientid, 'contactid' => $senderid])) {
+        if (self::is_blocked($recipientid, $senderid)) {
             return true;
         }
 
@@ -1079,12 +1111,11 @@ class api {
 
         $userfields = \user_picture::fields('u', array('lastaccess'));
         $blockeduserssql = "SELECT $userfields
-                              FROM {message_contacts} mc
+                              FROM {message_users_blocked} mub
                         INNER JOIN {user} u
-                                ON u.id = mc.contactid
+                                ON u.id = mub.blockeduserid
                              WHERE u.deleted = 0
-                               AND mc.userid = ?
-                               AND mc.blocked = 1
+                               AND mub.userid = ?
                           GROUP BY $userfields
                           ORDER BY u.firstname ASC";
         return $DB->get_records_sql($blockeduserssql, [$userid]);
@@ -1287,4 +1318,298 @@ class api {
 
         return $conversation->id;
     }
+
+    /**
+     * Checks if a user can create a contact request.
+     *
+     * @param int $userid The id of the user who is creating the contact request
+     * @param int $requesteduserid The id of the user being requested
+     * @return bool
+     */
+    public static function can_create_contact(int $userid, int $requesteduserid) : bool {
+        global $CFG;
+
+        // If we can't message at all, then we can't create a contact.
+        if (empty($CFG->messaging)) {
+            return false;
+        }
+
+        // If we can message anyone on the site then we can create a contact.
+        if ($CFG->messagingallusers) {
+            return true;
+        }
+
+        // We need to check if they are in the same course.
+        return enrol_sharing_course($userid, $requesteduserid);
+    }
+
+    /**
+     * Handles creating a contact request.
+     *
+     * @param int $userid The id of the user who is creating the contact request
+     * @param int $requesteduserid The id of the user being requested
+     */
+    public static function create_contact_request(int $userid, int $requesteduserid) {
+        global $DB;
+
+        $request = new \stdClass();
+        $request->userid = $userid;
+        $request->requesteduserid = $requesteduserid;
+        $request->timecreated = time();
+
+        $DB->insert_record('message_contact_requests', $request);
+
+        // Send a notification.
+        $userfrom = \core_user::get_user($userid);
+        $userfromfullname = fullname($userfrom);
+        $userto = \core_user::get_user($requesteduserid);
+        $url = new \moodle_url('/message/pendingcontactrequests.php');
+
+        $subject = get_string('messagecontactrequestsnotificationsubject', 'core_message', $userfromfullname);
+        $fullmessage = get_string('messagecontactrequestsnotification', 'core_message', $userfromfullname);
+
+        $message = new \core\message\message();
+        $message->courseid = SITEID;
+        $message->component = 'moodle';
+        $message->name = 'messagecontactrequests';
+        $message->notification = 1;
+        $message->userfrom = $userfrom;
+        $message->userto = $userto;
+        $message->subject = $subject;
+        $message->fullmessage = text_to_html($fullmessage);
+        $message->fullmessageformat = FORMAT_HTML;
+        $message->fullmessagehtml = $fullmessage;
+        $message->smallmessage = '';
+        $message->contexturl = $url->out(false);
+
+        message_send($message);
+    }
+
+
+    /**
+     * Handles confirming a contact request.
+     *
+     * @param int $userid The id of the user who created the contact request
+     * @param int $requesteduserid The id of the user confirming the request
+     */
+    public static function confirm_contact_request(int $userid, int $requesteduserid) {
+        global $DB;
+
+        if ($request = $DB->get_record('message_contact_requests', ['userid' => $userid,
+                'requesteduserid' => $requesteduserid])) {
+            self::add_contact($userid, $requesteduserid);
+
+            $DB->delete_records('message_contact_requests', ['id' => $request->id]);
+        }
+    }
+
+    /**
+     * Handles declining a contact request.
+     *
+     * @param int $userid The id of the user who created the contact request
+     * @param int $requesteduserid The id of the user declining the request
+     */
+    public static function decline_contact_request(int $userid, int $requesteduserid) {
+        global $DB;
+
+        if ($request = $DB->get_record('message_contact_requests', ['userid' => $userid,
+                'requesteduserid' => $requesteduserid])) {
+            $DB->delete_records('message_contact_requests', ['id' => $request->id]);
+        }
+    }
+
+    /**
+     * Handles returning the contact requests for a user.
+     *
+     * This also includes the user data necessary to display information
+     * about the user.
+     *
+     * It will not include blocked users.
+     *
+     * @param int $userid
+     * @return array The list of contact requests
+     */
+    public static function get_contact_requests(int $userid) : array {
+        global $DB;
+
+        // Used to search for contacts.
+        $ufields = \user_picture::fields('u');
+
+        $sql = "SELECT $ufields, mcr.id as contactrequestid
+                  FROM {user} u
+                  JOIN {message_contact_requests} mcr
+                    ON u.id = mcr.userid
+             LEFT JOIN {message_users_blocked} mub
+                    ON (mub.userid = ? AND mub.blockeduserid = u.id)
+                 WHERE mcr.requesteduserid = ?
+                   AND u.deleted = 0
+                   AND mub.id is NULL
+              ORDER BY mcr.timecreated DESC";
+
+        return $DB->get_records_sql($sql, [$userid, $userid]);
+    }
+
+    /**
+     * Handles adding a contact.
+     *
+     * @param int $userid The id of the user who requested to be a contact
+     * @param int $contactid The id of the contact
+     */
+    public static function add_contact(int $userid, int $contactid) {
+        global $DB;
+
+        $messagecontact = new \stdClass();
+        $messagecontact->userid = $userid;
+        $messagecontact->contactid = $contactid;
+        $messagecontact->timecreated = time();
+        $messagecontact->id = $DB->insert_record('message_contacts', $messagecontact);
+
+        $eventparams = [
+            'objectid' => $messagecontact->id,
+            'userid' => $userid,
+            'relateduserid' => $contactid,
+            'context' => \context_user::instance($userid)
+        ];
+        $event = \core\event\message_contact_added::create($eventparams);
+        $event->add_record_snapshot('message_contacts', $messagecontact);
+        $event->trigger();
+    }
+
+    /**
+     * Handles removing a contact.
+     *
+     * @param int $userid The id of the user who is removing a user as a contact
+     * @param int $contactid The id of the user to be removed as a contact
+     */
+    public static function remove_contact(int $userid, int $contactid) {
+        global $DB;
+
+        if ($contact = self::get_contact($userid, $contactid)) {
+            $DB->delete_records('message_contacts', ['id' => $contact->id]);
+
+            $event = \core\event\message_contact_removed::create(array(
+                'objectid' => $contact->id,
+                'userid' => $userid,
+                'relateduserid' => $contactid,
+                'context' => \context_user::instance($userid)
+            ));
+            $event->add_record_snapshot('message_contacts', $contact);
+            $event->trigger();
+        }
+    }
+
+    /**
+     * Handles blocking a user.
+     *
+     * @param int $userid The id of the user who is blocking
+     * @param int $usertoblockid The id of the user being blocked
+     */
+    public static function block_user(int $userid, int $usertoblockid) {
+        global $DB;
+
+        $blocked = new \stdClass();
+        $blocked->userid = $userid;
+        $blocked->blockeduserid = $usertoblockid;
+        $blocked->timecreated = time();
+        $blocked->id = $DB->insert_record('message_users_blocked', $blocked);
+
+        // Trigger event for blocking a contact.
+        $event = \core\event\message_user_blocked::create(array(
+            'objectid' => $blocked->id,
+            'userid' => $userid,
+            'relateduserid' => $usertoblockid,
+            'context' => \context_user::instance($userid)
+        ));
+        $event->add_record_snapshot('message_users_blocked', $blocked);
+        $event->trigger();
+    }
+
+    /**
+     * Handles unblocking a user.
+     *
+     * @param int $userid The id of the user who is unblocking
+     * @param int $usertounblockid The id of the user being unblocked
+     */
+    public static function unblock_user(int $userid, int $usertounblockid) {
+        global $DB;
+
+        if ($blockeduser = $DB->get_record('message_users_blocked',
+                ['userid' => $userid, 'blockeduserid' => $usertounblockid])) {
+            $DB->delete_records('message_users_blocked', ['id' => $blockeduser->id]);
+
+            // Trigger event for unblocking a contact.
+            $event = \core\event\message_user_unblocked::create(array(
+                'objectid' => $blockeduser->id,
+                'userid' => $userid,
+                'relateduserid' => $usertounblockid,
+                'context' => \context_user::instance($userid)
+            ));
+            $event->add_record_snapshot('message_users_blocked', $blockeduser);
+            $event->trigger();
+        }
+    }
+
+    /**
+     * Checks if users are already contacts.
+     *
+     * @param int $userid The id of one of the users
+     * @param int $contactid The id of the other user
+     * @return bool Returns true if they are a contact, false otherwise
+     */
+    public static function is_contact(int $userid, int $contactid) : bool {
+        global $DB;
+
+        $sql = "SELECT id
+                  FROM {message_contacts} mc
+                 WHERE (mc.userid = ? AND mc.contactid = ?)
+                    OR (mc.userid = ? AND mc.contactid = ?)";
+        return $DB->record_exists_sql($sql, [$userid, $contactid, $contactid, $userid]);
+    }
+
+    /**
+     * Returns the row in the database table message_contacts that represents the contact between two people.
+     *
+     * @param int $userid The id of one of the users
+     * @param int $contactid The id of the other user
+     * @return mixed A fieldset object containing the record, false otherwise
+     */
+    public static function get_contact(int $userid, int $contactid) {
+        global $DB;
+
+        $sql = "SELECT mc.*
+                  FROM {message_contacts} mc
+                 WHERE (mc.userid = ? AND mc.contactid = ?)
+                    OR (mc.userid = ? AND mc.contactid = ?)";
+        return $DB->get_record_sql($sql, [$userid, $contactid, $contactid, $userid]);
+    }
+
+    /**
+     * Checks if a user is already blocked.
+     *
+     * @param int $userid
+     * @param int $blockeduserid
+     * @return bool Returns true if they are a blocked, false otherwise
+     */
+    public static function is_blocked(int $userid, int $blockeduserid) : bool {
+        global $DB;
+
+        return $DB->record_exists('message_users_blocked', ['userid' => $userid, 'blockeduserid' => $blockeduserid]);
+    }
+
+    /**
+     * Checks if a contact request already exists between users.
+     *
+     * @param int $userid The id of the user who is creating the contact request
+     * @param int $requesteduserid The id of the user being requested
+     * @return bool Returns true if a contact request exists, false otherwise
+     */
+    public static function does_contact_request_exist(int $userid, int $requesteduserid) : bool {
+        global $DB;
+
+        $sql = "SELECT id
+                  FROM {message_contact_requests} mcr
+                 WHERE (mcr.userid = ? AND mcr.requesteduserid = ?)
+                    OR (mcr.userid = ? AND mcr.requesteduserid = ?)";
+        return $DB->record_exists_sql($sql, [$userid, $requesteduserid, $requesteduserid, $userid]);
+    }
 }
index a9b53d3..7d98e7b 100644 (file)
@@ -101,7 +101,8 @@ class messages implements templatable, renderable {
             $data->messages[] = $message->export_for_template($output);
         }
 
-        $data->isblocked = api::is_user_blocked($this->currentuserid, $this->otheruserid);
+        $blockeduserid = $this->otheruserid ?? $USER->id;
+        $data->isblocked = api::is_blocked($this->currentuserid, $blockeduserid);
 
         return $data;
     }
index 8551353..b9049e5 100644 (file)
@@ -90,11 +90,31 @@ class provider implements
             [
                 'userid' => 'privacy:metadata:message_contacts:userid',
                 'contactid' => 'privacy:metadata:message_contacts:contactid',
-                'blocked' => 'privacy:metadata:message_contacts:blocked',
+                'timecreated' => 'privacy:metadata:message_contacts:timecreated',
             ],
             'privacy:metadata:message_contacts'
         );
 
+        $items->add_database_table(
+            'message_contact_requests',
+            [
+                'userid' => 'privacy:metadata:message_contact_requests:userid',
+                'requesteduserid' => 'privacy:metadata:message_contact_requests:requesteduserid',
+                'timecreated' => 'privacy:metadata:message_contact_requests:timecreated',
+            ],
+            'privacy:metadata:message_contact_requests'
+        );
+
+        $items->add_database_table(
+            'message_users_blocked',
+            [
+                'userid' => 'privacy:metadata:message_users_blocked:userid',
+                'blockeduserid' => 'privacy:metadata:message_users_blocked:blockeduserid',
+                'timecreated' => 'privacy:metadata:message_users_blocked:timecreated',
+            ],
+            'privacy:metadata:message_users_blocked'
+        );
+
         $items->add_database_table(
             'notifications',
             [
@@ -186,6 +206,12 @@ class provider implements
         // Export the contacts.
         self::export_user_data_contacts($userid);
 
+        // Export the contact requests.
+        self::export_user_data_contact_requests($userid);
+
+        // Export the blocked users.
+        self::export_user_data_blocked_users($userid);
+
         // Export the notifications.
         self::export_user_data_notifications($userid);
 
@@ -209,6 +235,8 @@ class provider implements
         $DB->delete_records('message_user_actions');
         $DB->delete_records('message_conversation_members');
         $DB->delete_records('message_contacts');
+        $DB->delete_records('message_contact_requests');
+        $DB->delete_records('message_users_blocked');
         $DB->delete_records('notifications');
     }
 
@@ -239,6 +267,8 @@ class provider implements
         $DB->delete_records('message_user_actions', ['userid' => $userid]);
         $DB->delete_records('message_conversation_members', ['userid' => $userid]);
         $DB->delete_records_select('message_contacts', 'userid = ? OR contactid = ?', [$userid, $userid]);
+        $DB->delete_records_select('message_contact_requests', 'userid = ? OR requesteduserid = ?', [$userid, $userid]);
+        $DB->delete_records_select('message_users_blocked', 'userid = ? OR blockeduserid = ?', [$userid, $userid]);
         $DB->delete_records_select('notifications', 'useridfrom = ? OR useridto = ?', [$userid, $userid]);
     }
 
@@ -253,18 +283,70 @@ class provider implements
         $context = \context_system::instance();
 
         // Get the user's contacts.
-        if ($contacts = $DB->get_records('message_contacts', ['userid' => $userid], 'id ASC')) {
+        if ($contacts = $DB->get_records_select('message_contacts', 'userid = ? OR contactid = ?', [$userid, $userid], 'id ASC')) {
             $contactdata = [];
             foreach ($contacts as $contact) {
                 $contactdata[] = (object) [
-                    'contact' => transform::user($contact->contactid),
-                    'blocked' => transform::yesno($contact->blocked)
+                    'contact' => transform::user($contact->contactid)
                 ];
             }
             writer::with_context($context)->export_data([get_string('contacts', 'core_message')], (object) $contactdata);
         }
     }
 
+    /**
+     * Export the messaging contact requests data.
+     *
+     * @param int $userid
+     */
+    protected static function export_user_data_contact_requests(int $userid) {
+        global $DB;
+
+        $context = \context_system::instance();
+
+        if ($contactrequests = $DB->get_records_select('message_contact_requests', 'userid = ? OR requesteduserid = ?',
+                [$userid, $userid], 'id ASC')) {
+            $contactrequestsdata = [];
+            foreach ($contactrequests as $contactrequest) {
+                if ($userid == $contactrequest->requesteduserid) {
+                    $maderequest = false;
+                    $contactid = $contactrequest->userid;
+                } else {
+                    $maderequest = true;
+                    $contactid = $contactrequest->requesteduserid;
+                }
+
+                $contactrequestsdata[] = (object) [
+                    'contactrequest' => transform::user($contactid),
+                    'maderequest' => transform::yesno($maderequest)
+                ];
+            }
+            writer::with_context($context)->export_data([get_string('contactrequests', 'core_message')],
+                (object) $contactrequestsdata);
+        }
+    }
+
+    /**
+     * Export the messaging blocked users data.
+     *
+     * @param int $userid
+     */
+    protected static function export_user_data_blocked_users(int $userid) {
+        global $DB;
+
+        $context = \context_system::instance();
+
+        if ($blockedusers = $DB->get_records('message_users_blocked', ['userid' => $userid], 'id ASC')) {
+            $blockedusersdata = [];
+            foreach ($blockedusers as $blockeduser) {
+                $blockedusersdata[] = (object) [
+                    'blockeduser' => transform::user($blockeduser->blockeduserid)
+                ];
+            }
+            writer::with_context($context)->export_data([get_string('blockedusers', 'core_message')], (object) $blockedusersdata);
+        }
+    }
+
     /**
      * Export the messaging data.
      *
index 5151adb..f678c2f 100644 (file)
@@ -90,23 +90,30 @@ class core_message_external extends external_api {
         foreach($params['messages'] as $message) {
             $receivers[] = $message['touserid'];
         }
-        list($sqluserids, $sqlparams) = $DB->get_in_or_equal($receivers, SQL_PARAMS_NAMED, 'userid_');
+        list($sqluserids, $sqlparams) = $DB->get_in_or_equal($receivers);
         $tousers = $DB->get_records_select("user", "id " . $sqluserids . " AND deleted = 0", $sqlparams);
         $blocklist   = array();
         $contactlist = array();
-        $sqlparams['contactid'] = $USER->id;
+        $contactsqlparams = array_merge($sqlparams, [$USER->id], [$USER->id], $sqlparams);
         $rs = $DB->get_recordset_sql("SELECT *
                                         FROM {message_contacts}
-                                       WHERE userid $sqluserids
-                                             AND contactid = :contactid", $sqlparams);
+                                       WHERE (userid $sqluserids AND contactid = ?)
+                                          OR (userid = ? AND contactid $sqluserids)", $contactsqlparams);
         foreach ($rs as $record) {
-            if ($record->blocked) {
-                // $record->userid is blocking current user
-                $blocklist[$record->userid] = true;
-            } else {
-                // $record->userid have current user as contact
-                $contactlist[$record->userid] = true;
+            $useridtouse = $record->userid;
+            if ($record->userid == $USER->id) {
+                $useridtouse = $record->contactid;
             }
+            $contactlist[$useridtouse] = true;
+        }
+        $rs->close();
+        $blocksqlparams = array_merge($sqlparams, [$USER->id]);
+        $rs = $DB->get_recordset_sql("SELECT *
+                                        FROM {message_users_blocked}
+                                       WHERE userid $sqluserids
+                                         AND blockeduserid = ?", $blocksqlparams);
+        foreach ($rs as $record) {
+            $blocklist[$record->userid] = true;
         }
         $rs->close();
 
@@ -191,6 +198,7 @@ class core_message_external extends external_api {
     /**
      * Create contacts parameters description.
      *
+     * @deprecated since Moodle 3.6
      * @return external_function_parameters
      * @since Moodle 2.5
      */
@@ -210,6 +218,7 @@ class core_message_external extends external_api {
     /**
      * Create contacts.
      *
+     * @deprecated since Moodle 3.6
      * @param array $userids array of user IDs.
      * @param int $userid The id of the user we are creating the contacts for
      * @return external_description
@@ -256,6 +265,7 @@ class core_message_external extends external_api {
     /**
      * Create contacts return description.
      *
+     * @deprecated since Moodle 3.6
      * @return external_description
      * @since Moodle 2.5
      */
@@ -263,6 +273,15 @@ class core_message_external extends external_api {
         return new external_warnings();
     }
 
+    /**
+     * Marking the method as deprecated.
+     *
+     * @return bool
+     */
+    public static function create_contacts_is_deprecated() {
+        return true;
+    }
+
     /**
      * Delete contacts parameters description.
      *
@@ -315,7 +334,7 @@ class core_message_external extends external_api {
         $params = self::validate_parameters(self::delete_contacts_parameters(), $params);
 
         foreach ($params['userids'] as $id) {
-            message_remove_contact($id, $userid);
+            \core_message\api::remove_contact($userid, $id);
         }
 
         return null;
@@ -331,9 +350,121 @@ class core_message_external extends external_api {
         return null;
     }
 
+    /**
+     * Block user parameters description.
+     *
+     * @return external_function_parameters
+     */
+    public static function block_user_parameters() {
+        return new external_function_parameters(
+            [
+                'userid' => new external_value(PARAM_INT, 'The id of the user who is blocking'),
+                'blockeduserid' => new external_value(PARAM_INT, 'The id of the user being blocked'),
+            ]
+        );
+    }
+
+    /**
+     * Blocks a user.
+     *
+     * @param int $userid The id of the user who is blocking
+     * @param int $blockeduserid The id of the user being blocked
+     * @return external_description
+     */
+    public static function block_user(int $userid, int $blockeduserid) {
+        global $CFG, $USER;
+
+        // Check if messaging is enabled.
+        if (empty($CFG->messaging)) {
+            throw new moodle_exception('disabled', 'message');
+        }
+
+        // Validate context.
+        $context = context_system::instance();
+        self::validate_context($context);
+
+        $capability = 'moodle/site:manageallmessaging';
+        if (($USER->id != $userid) && !has_capability($capability, $context)) {
+            throw new required_capability_exception($context, $capability, 'nopermissions', '');
+        }
+
+        $params = ['userid' => $userid, 'blockeduserid' => $blockeduserid];
+        $params = self::validate_parameters(self::block_user_parameters(), $params);
+
+        if (!\core_message\api::is_blocked($params['userid'], $params['blockeduserid'])) {
+            \core_message\api::block_user($params['userid'], $params['blockeduserid']);
+        }
+
+        return [];
+    }
+
+    /**
+     * Block user return description.
+     *
+     * @return external_description
+     */
+    public static function block_user_returns() {
+        return new external_warnings();
+    }
+
+    /**
+     * Unblock user parameters description.
+     *
+     * @return external_function_parameters
+     */
+    public static function unblock_user_parameters() {
+        return new external_function_parameters(
+            [
+                'userid' => new external_value(PARAM_INT, 'The id of the user who is unblocking'),
+                'unblockeduserid' => new external_value(PARAM_INT, 'The id of the user being unblocked'),
+            ]
+        );
+    }
+
+    /**
+     * Unblock user.
+     *
+     * @param int $userid The id of the user who is unblocking
+     * @param int $unblockeduserid The id of the user being unblocked
+     */
+    public static function unblock_user(int $userid, int $unblockeduserid) {
+        global $CFG, $USER;
+
+        // Check if messaging is enabled.
+        if (empty($CFG->messaging)) {
+            throw new moodle_exception('disabled', 'message');
+        }
+
+        // Validate context.
+        $context = context_system::instance();
+        self::validate_context($context);
+
+        $capability = 'moodle/site:manageallmessaging';
+        if (($USER->id != $userid) && !has_capability($capability, $context)) {
+            throw new required_capability_exception($context, $capability, 'nopermissions', '');
+        }
+
+        $params = ['userid' => $userid, 'unblockeduserid' => $unblockeduserid];
+        $params = self::validate_parameters(self::unblock_user_parameters(), $params);
+
+        \core_message\api::unblock_user($params['userid'], $params['unblockeduserid']);
+
+        return [];
+    }
+
+    /**
+     * Unblock user return description.
+     *
+     * @return external_description
+     */
+    public static function unblock_user_returns() {
+        return new external_warnings();
+    }
+
     /**
      * Block contacts parameters description.
      *
+     * @deprecated since Moodle 3.6
      * @return external_function_parameters
      * @since Moodle 2.5
      */
@@ -353,6 +484,7 @@ class core_message_external extends external_api {
     /**
      * Block contacts.
      *
+     * @deprecated since Moodle 3.6
      * @param array $userids array of user IDs.
      * @param int $userid The id of the user we are blocking the contacts for
      * @return external_description
@@ -399,6 +531,7 @@ class core_message_external extends external_api {
     /**
      * Block contacts return description.
      *
+     * @deprecated since Moodle 3.6
      * @return external_description
      * @since Moodle 2.5
      */
@@ -406,9 +539,19 @@ class core_message_external extends external_api {
         return new external_warnings();
     }
 
+    /**
+     * Marking the method as deprecated.
+     *
+     * @return bool
+     */
+    public static function block_contacts_is_deprecated() {
+        return true;
+    }
+
     /**
      * Unblock contacts parameters description.
      *
+     * @deprecated since Moodle 3.6
      * @return external_function_parameters
      * @since Moodle 2.5
      */
@@ -428,6 +571,7 @@ class core_message_external extends external_api {
     /**
      * Unblock contacts.
      *
+     * @deprecated since Moodle 3.6
      * @param array $userids array of user IDs.
      * @param int $userid The id of the user we are unblocking the contacts for
      * @return null
@@ -467,6 +611,7 @@ class core_message_external extends external_api {
     /**
      * Unblock contacts return description.
      *
+     * @deprecated since Moodle 3.6
      * @return external_description
      * @since Moodle 2.5
      */
@@ -474,6 +619,265 @@ class core_message_external extends external_api {
         return null;
     }
 
+    /**
+     * Marking the method as deprecated.
+     *
+     * @return bool
+     */
+    public static function unblock_contacts_is_deprecated() {
+        return true;
+    }
+
+    /**
+     * Returns contact requests parameters description.
+     *
+     * @return external_function_parameters
+     */
+    public static function get_contact_requests_parameters() {
+        return new external_function_parameters(
+            [
+                'userid' => new external_value(PARAM_INT, 'The id of the user we want the requests for')
+            ]
+        );
+    }
+
+    /**
+     * Handles returning the contact requests for a user.
+     *
+     * This also includes the user data necessary to display information
+     * about the user.
+     *
+     * It will not include blocked users.
+     *
+     * @param int $userid The id of the user we want to get the contact requests for
+     */
+    public static function get_contact_requests(int $userid) {
+        global $CFG, $USER;
+
+        // Check if messaging is enabled.
+        if (empty($CFG->messaging)) {
+            throw new moodle_exception('disabled', 'message');
+        }
+
+        // Validate context.
+        $context = context_system::instance();
+        self::validate_context($context);
+
+        $capability = 'moodle/site:manageallmessaging';
+        if (($USER->id != $userid) && !has_capability($capability, $context)) {
+            throw new required_capability_exception($context, $capability, 'nopermissions', '');
+        }
+
+        $params = ['userid' => $userid];
+        $params = self::validate_parameters(self::get_contact_requests_parameters(), $params);
+
+        return \core_message\api::get_contact_requests($params['userid']);
+    }
+
+    /**
+     * Returns the contact requests return description.
+     *
+     * @return external_description
+     */
+    public static function get_contact_requests_returns() {
+        return new external_multiple_structure(
+            new external_single_structure(
+                [
+                    'id' => new external_value(core_user::get_property_type('id'), 'ID of the user'),
+                    'contactrequestid' => new external_value(PARAM_INT, 'The ID of the contact request'),
+                    'picture' => new external_value(core_user::get_property_type('picture'), 'The picture'),
+                    'firstname' => new external_value(core_user::get_property_type('firstname'),
+                        'The first name(s) of the user'),
+                    'lastname' => new external_value(core_user::get_property_type('lastname'),
+                        'The family name of the user'),
+                    'firstnamephonetic' => new external_value(core_user::get_property_type('firstnamephonetic'),
+                        'The phonetic first name of the user'),
+                    'lastnamephonetic' => new external_value(core_user::get_property_type('lastnamephonetic'),
+                        'The phonetic last name of the user'),
+                    'middlename' => new external_value(core_user::get_property_type('middlename'),
+                        'The middle name of the user'),
+                    'alternatename' => new external_value(core_user::get_property_type('alternatename'),
+                        'The alternate name of the user'),
+                    'email' => new external_value(core_user::get_property_type('email'), 'An email address')
+                ]
+            )
+        );
+    }
+
+    /**
+     * Creates a contact request parameters description.
+     *
+     * @return external_function_parameters
+     */
+    public static function create_contact_request_parameters() {
+        return new external_function_parameters(
+            [
+                'userid' => new external_value(PARAM_INT, 'The id of the user making the request'),
+                'requesteduserid' => new external_value(PARAM_INT, 'The id of the user being requested')
+            ]
+        );
+    }
+
+    /**
+     * Creates a contact request.
+     *
+     * @param int $userid The id of the user who is creating the contact request
+     * @param int $requesteduserid The id of the user being requested
+     */
+    public static function create_contact_request(int $userid, int $requesteduserid) {
+        global $CFG, $USER;
+
+        // Check if messaging is enabled.
+        if (empty($CFG->messaging)) {
+            throw new moodle_exception('disabled', 'message');
+        }
+
+        // Validate context.
+        $context = context_system::instance();
+        self::validate_context($context);
+
+        $capability = 'moodle/site:manageallmessaging';
+        if (($USER->id != $userid) && !has_capability($capability, $context)) {
+            throw new required_capability_exception($context, $capability, 'nopermissions', '');
+        }
+
+        $params = ['userid' => $userid, 'requesteduserid' => $requesteduserid];
+        $params = self::validate_parameters(self::create_contact_request_parameters(), $params);
+
+        if (!\core_message\api::can_create_contact($params['userid'], $params['requesteduserid'])) {
+            $warning[] = [
+                'item' => 'user',
+                'itemid' => $params['requesteduserid'],
+                'warningcode' => 'cannotcreatecontactrequest',
+                'message' => 'You are unable to create a contact request for this user'
+            ];
+            return $warning;
+        }
+
+        if (!\core_message\api::does_contact_request_exist($params['userid'], $params['requesteduserid'])) {
+            \core_message\api::create_contact_request($params['userid'], $params['requesteduserid']);
+        }
+
+        return [];
+    }
+
+    /**
+     * Creates a contact request return description.
+     *
+     * @return external_description
+     */
+    public static function create_contact_request_returns() {
+        return new external_warnings();
+    }
+
+    /**
+     * Confirm a contact request parameters description.
+     *
+     * @return external_function_parameters
+     */
+    public static function confirm_contact_request_parameters() {
+        return new external_function_parameters(
+            [
+                'userid' => new external_value(PARAM_INT, 'The id of the user making the request'),
+                'requesteduserid' => new external_value(PARAM_INT, 'The id of the user being requested')
+            ]
+        );
+    }
+
+    /**
+     * Confirm a contact request.
+     *
+     * @param int $userid The id of the user who is creating the contact request
+     * @param int $requesteduserid The id of the user being requested
+     */
+    public static function confirm_contact_request(int $userid, int $requesteduserid) {
+        global $CFG, $USER;
+
+        // Check if messaging is enabled.
+        if (empty($CFG->messaging)) {
+            throw new moodle_exception('disabled', 'message');
+        }
+
+        // Validate context.
+        $context = context_system::instance();
+        self::validate_context($context);
+
+        $capability = 'moodle/site:manageallmessaging';
+        if (($USER->id != $requesteduserid) && !has_capability($capability, $context)) {
+            throw new required_capability_exception($context, $capability, 'nopermissions', '');
+        }
+
+        $params = ['userid' => $userid, 'requesteduserid' => $requesteduserid];
+        $params = self::validate_parameters(self::confirm_contact_request_parameters(), $params);
+
+        \core_message\api::confirm_contact_request($params['userid'], $params['requesteduserid']);
+
+        return [];
+    }
+
+    /**
+     * Confirm a contact request return description.
+     *
+     * @return external_description
+     */
+    public static function confirm_contact_request_returns() {
+        return new external_warnings();
+    }
+
+    /**
+     * Declines a contact request parameters description.
+     *
+     * @return external_function_parameters
+     */
+    public static function decline_contact_request_parameters() {
+        return new external_function_parameters(
+            [
+                'userid' => new external_value(PARAM_INT, 'The id of the user making the request'),
+                'requesteduserid' => new external_value(PARAM_INT, 'The id of the user being requested')
+            ]
+        );
+    }
+
+    /**
+     * Declines a contact request.
+     *
+     * @param int $userid The id of the user who is creating the contact request
+     * @param int $requesteduserid The id of the user being requested
+     */
+    public static function decline_contact_request(int $userid, int $requesteduserid) {
+        global $CFG, $USER;
+
+        // Check if messaging is enabled.
+        if (empty($CFG->messaging)) {
+            throw new moodle_exception('disabled', 'message');
+        }
+
+        // Validate context.
+        $context = context_system::instance();
+        self::validate_context($context);
+
+        $capability = 'moodle/site:manageallmessaging';
+        if (($USER->id != $requesteduserid) && !has_capability($capability, $context)) {
+            throw new required_capability_exception($context, $capability, 'nopermissions', '');
+        }
+
+        $params = ['userid' => $userid, 'requesteduserid' => $requesteduserid];
+        $params = self::validate_parameters(self::decline_contact_request_parameters(), $params);
+
+        \core_message\api::decline_contact_request($params['userid'], $params['requesteduserid']);
+
+        return [];
+    }
+
+    /**
+     * Declines a contact request return description.
+     *
+     * @return external_description
+     */
+    public static function decline_contact_request_returns() {
+        return new external_warnings();
+    }
+
     /**
      * Return the structure of a message area contact.
      *
index a3f99fc..1fbe61c 100644 (file)
@@ -151,156 +151,6 @@ function message_format_message_text($message, $forcetexttohtml = false) {
     return format_text($messagetext, $format, $options);
 }
 
-/**
- * Add the selected user as a contact for the current user
- *
- * @param int $contactid the ID of the user to add as a contact
- * @param int $blocked 1 if you wish to block the contact
- * @param int $userid the user ID of the user we want to add the contact for, defaults to current user if not specified.
- * @return bool/int false if the $contactid isnt a valid user id. True if no changes made.
- *                  Otherwise returns the result of update_record() or insert_record()
- */
-function message_add_contact($contactid, $blocked = 0, $userid = 0) {
-    global $USER, $DB;
-
-    if (!$DB->record_exists('user', array('id' => $contactid))) { // invalid userid
-        return false;
-    }
-
-    if (empty($userid)) {
-        $userid = $USER->id;
-    }
-
-    // Check if a record already exists as we may be changing blocking status.
-    if (($contact = $DB->get_record('message_contacts', array('userid' => $userid, 'contactid' => $contactid))) !== false) {
-        // Check if blocking status has been changed.
-        if ($contact->blocked != $blocked) {
-            $contact->blocked = $blocked;
-            $DB->update_record('message_contacts', $contact);
-
-            if ($blocked == 1) {
-                // Trigger event for blocking a contact.
-                $event = \core\event\message_contact_blocked::create(array(
-                    'objectid' => $contact->id,
-                    'userid' => $contact->userid,
-                    'relateduserid' => $contact->contactid,
-                    'context'  => context_user::instance($contact->userid)
-                ));
-                $event->add_record_snapshot('message_contacts', $contact);
-                $event->trigger();
-            } else {
-                // Trigger event for unblocking a contact.
-                $event = \core\event\message_contact_unblocked::create(array(
-                    'objectid' => $contact->id,
-                    'userid' => $contact->userid,
-                    'relateduserid' => $contact->contactid,
-                    'context'  => context_user::instance($contact->userid)
-                ));
-                $event->add_record_snapshot('message_contacts', $contact);
-                $event->trigger();
-            }
-
-            return true;
-        } else {
-            // No change to blocking status.
-            return true;
-        }
-
-    } else {
-        // New contact record.
-        $contact = new stdClass();
-        $contact->userid = $userid;
-        $contact->contactid = $contactid;
-        $contact->blocked = $blocked;
-        $contact->id = $DB->insert_record('message_contacts', $contact);
-
-        $eventparams = array(
-            'objectid' => $contact->id,
-            'userid' => $contact->userid,
-            'relateduserid' => $contact->contactid,
-            'context'  => context_user::instance($contact->userid)
-        );
-
-        if ($blocked) {
-            $event = \core\event\message_contact_blocked::create($eventparams);
-        } else {
-            $event = \core\event\message_contact_added::create($eventparams);
-        }
-        // Trigger event.
-        $event->trigger();
-
-        return true;
-    }
-}
-
-/**
- * remove a contact
- *
- * @param int $contactid the user ID of the contact to remove
- * @param int $userid the user ID of the user we want to remove the contacts for, defaults to current user if not specified.
- * @return bool returns the result of delete_records()
- */
-function message_remove_contact($contactid, $userid = 0) {
-    global $USER, $DB;
-
-    if (empty($userid)) {
-        $userid = $USER->id;
-    }
-
-    if ($contact = $DB->get_record('message_contacts', array('userid' => $userid, 'contactid' => $contactid))) {
-        $DB->delete_records('message_contacts', array('id' => $contact->id));
-
-        // Trigger event for removing a contact.
-        $event = \core\event\message_contact_removed::create(array(
-            'objectid' => $contact->id,
-            'userid' => $contact->userid,
-            'relateduserid' => $contact->contactid,
-            'context'  => context_user::instance($contact->userid)
-        ));
-        $event->add_record_snapshot('message_contacts', $contact);
-        $event->trigger();
-
-        return true;
-    }
-
-    return false;
-}
-
-/**
- * Unblock a contact. Note that this reverts the previously blocked user back to a non-contact.
- *
- * @param int $contactid the user ID of the contact to unblock
- * @param int $userid the user ID of the user we want to unblock the contact for, defaults to current user
- *  if not specified.
- * @return bool returns the result of delete_records()
- */
-function message_unblock_contact($contactid, $userid = 0) {
-    return message_add_contact($contactid, 0, $userid);
-}
-
-/**
- * Block a user.
- *
- * @param int $contactid the user ID of the user to block
- * @param int $userid the user ID of the user we want to unblock the contact for, defaults to current user
- *  if not specified.
- * @return bool
- */
-function message_block_contact($contactid, $userid = 0) {
-    return message_add_contact($contactid, 1, $userid);
-}
-
-/**
- * Load a user's contact record
- *
- * @param int $contactid the user ID of the user whose contact record you want
- * @return array message contacts
- */
-function message_get_contact($contactid) {
-    global $USER, $DB;
-    return $DB->get_record('message_contacts', array('userid' => $USER->id, 'contactid' => $contactid));
-}
-
 /**
  * Search through course users.
  *
@@ -337,6 +187,7 @@ function message_search_users($courseids, $searchtext, $sort='', $exceptions='')
 
     $params = array(
         'userid' => $USER->id,
+        'userid2' => $USER->id,
         'query' => "%$searchtext%"
     );
 
@@ -357,10 +208,12 @@ function message_search_users($courseids, $searchtext, $sort='', $exceptions='')
 
     if (in_array(SITEID, $courseids)) {
         // Search on site level.
-        return $DB->get_records_sql("SELECT $ufields, mc.id as contactlistid, mc.blocked
+        return $DB->get_records_sql("SELECT $ufields, mc.id as contactlistid, mub.id as userblockedid
                                        FROM {user} u
                                        LEFT JOIN {message_contacts} mc
                                             ON mc.contactid = u.id AND mc.userid = :userid
+                                       LEFT JOIN {message_users_blocked} mub
+                                            ON mub.userid = :userid2 AND mub.blockeduserid = u.id
                                       WHERE u.deleted = '0' AND u.confirmed = '1'
                                             AND (".$DB->sql_like($fullname, ':query', false).")
                                             $except
@@ -379,11 +232,13 @@ function message_search_users($courseids, $searchtext, $sort='', $exceptions='')
 
         // Everyone who has a role assignment in this course or higher.
         // TODO: add enabled enrolment join here (skodak)
-        $users = $DB->get_records_sql("SELECT DISTINCT $ufields, mc.id as contactlistid, mc.blocked
+        $users = $DB->get_records_sql("SELECT DISTINCT $ufields, mc.id as contactlistid, mub.id as userblockedid
                                          FROM {user} u
                                          JOIN {role_assignments} ra ON ra.userid = u.id
                                          LEFT JOIN {message_contacts} mc
                                               ON mc.contactid = u.id AND mc.userid = :userid
+                                         LEFT JOIN {message_users_blocked} mub
+                                              ON mub.userid = :userid2 AND mub.blockeduserid = u.id
                                         WHERE u.deleted = '0' AND u.confirmed = '1'
                                               AND (".$DB->sql_like($fullname, ':query', false).")
                                               AND ra.contextid $contextwhere
diff --git a/message/pendingcontactrequests.php b/message/pendingcontactrequests.php
new file mode 100644 (file)
index 0000000..cab15e9
--- /dev/null
@@ -0,0 +1,93 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * A page displaying the user's contact requests.
+ *
+ * This is a temporary (well, should be) page until the new UI is introduced for 3.6.
+ *
+ * @package    core_message
+ * @copyright  2018 Mark Nelson <markn@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once('../config.php');
+require_once($CFG->dirroot . '/message/externallib.php');
+
+require_login(null, false);
+
+if (isguestuser()) {
+    redirect($CFG->wwwroot);
+}
+
+if (empty($CFG->messaging)) {
+    print_error('disabled', 'message');
+}
+
+$id = optional_param('id', '', PARAM_INT); // The id of the request.
+$action = optional_param('action', '', PARAM_ALPHA);
+
+// Confirm the request is able to be approved/disapproved.
+if ($id) {
+    $request = $DB->get_record('message_contact_requests', ['id' => $id, 'requesteduserid' => $USER->id], '*', MUST_EXIST);
+}
+
+// Use external functions as these are what we will be using in the new UI.
+if ($id && $action && confirm_sesskey()) {
+    if ($action == 'approve') {
+        core_message_external::confirm_contact_request($request->userid, $USER->id);
+    } else if ($action == 'decline') {
+        core_message_external::decline_contact_request($request->userid, $USER->id);
+    }
+
+    redirect(new moodle_url('/message/pendingcontactrequests.php'));
+}
+
+$table = new html_table();
+
+$headers = [];
+$headers[] = '';
+$headers[] = '';
+
+$table->head = $headers;
+
+// Use external functions as these are what we will be using in the new UI.
+if ($contactrequests = core_message_external::get_contact_requests($USER->id)) {
+    foreach ($contactrequests as $contactrequest) {
+        $approvelink = new moodle_url('/message/pendingcontactrequests.php', ['id' => $contactrequest->contactrequestid,
+            'action' => 'approve', 'sesskey' => sesskey()]);
+        $declinelink = new moodle_url('/message/pendingcontactrequests.php', ['id' => $contactrequest->contactrequestid,
+            'action' => 'decline', 'sesskey' => sesskey()]);
+
+        $cells = array();
+        $cells[] = fullname($contactrequest);
+        $cells[] = html_writer::link($approvelink, get_string('approve')) . " | " .
+            html_writer::link($declinelink, get_string('cancel'));
+        $table->data[] = new html_table_row($cells);
+    }
+}
+
+$url = new moodle_url('/message/pendingcontactrequests.php');
+$PAGE->set_url($url);
+
+$PAGE->set_context(context_user::instance($USER->id));
+$PAGE->set_pagelayout('standard');
+$PAGE->set_title('Pending contact requests');
+$PAGE->set_heading('Pending contact requests');
+
+echo $OUTPUT->header();
+echo html_writer::table($table);
+echo $OUTPUT->footer();
index 42f8192..0278d67 100644 (file)
@@ -102,20 +102,20 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
      * Test count_blocked_users.
      */
     public function test_count_blocked_users() {
+        global $USER;
+
         // Set this user as the admin.
         $this->setAdminUser();
 
-        // Create users to add to the admin's contact list.
+        // Create user to add to the admin's block list.
         $user1 = $this->getDataGenerator()->create_user();
-        $user2 = $this->getDataGenerator()->create_user();
 
         $this->assertEquals(0, \core_message\api::count_blocked_users());
 
-        // Add 1 blocked and 1 normal contact to admin's contact list.
-        message_add_contact($user1->id);
-        message_add_contact($user2->id, 1);
+        // Add 1 blocked user to admin's blocked user list.
+        \core_message\api::block_user($USER->id, $user1->id);
 
-        $this->assertEquals(0, \core_message\api::count_blocked_users($user2));
+        $this->assertEquals(0, \core_message\api::count_blocked_users($user1));
         $this->assertEquals(1, \core_message\api::count_blocked_users());
     }
 
@@ -140,7 +140,7 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
         $user2 = self::getDataGenerator()->create_user($user2);
 
         // Block the second user.
-        message_block_contact($user2->id, $user1->id);
+        \core_message\api::block_user($user1->id, $user2->id);
 
         $user3 = new stdClass();
         $user3->firstname = 'User';
@@ -247,9 +247,9 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
         $this->getDataGenerator()->enrol_user($user1->id, $course5->id, $role->id);
 
         // Add some users as contacts.
-        message_add_contact($user2->id, 0, $user1->id);
-        message_add_contact($user3->id, 0, $user1->id);
-        message_add_contact($user4->id, 0, $user1->id);
+        \core_message\api::add_contact($user1->id, $user2->id);
+        \core_message\api::add_contact($user1->id, $user3->id);
+        \core_message\api::add_contact($user1->id, $user4->id);
 
         // Remove the viewparticipants capability from one of the courses.
         $course5context = context_course::instance($course5->id);
@@ -280,25 +280,31 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
         // Create some users.
         $user1 = self::getDataGenerator()->create_user();
         $user2 = self::getDataGenerator()->create_user();
+        $user3 = self::getDataGenerator()->create_user();
 
         // The person doing the search.
         $this->setUser($user1);
 
         // Send some messages back and forth.
         $time = 1;
-        $this->send_fake_message($user1, $user2, 'Yo!', 0, $time);
-        $this->send_fake_message($user2, $user1, 'Sup mang?', 0, $time + 1);
-        $this->send_fake_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 2);
-        $this->send_fake_message($user2, $user1, 'Word.', 0, $time + 3);
+        $this->send_fake_message($user3, $user1, 'Don\'t block me.', 0, $time);
+        $this->send_fake_message($user1, $user2, 'Yo!', 0, $time + 1);
+        $this->send_fake_message($user2, $user1, 'Sup mang?', 0, $time + 2);
+        $this->send_fake_message($user1, $user2, 'Writing PHPUnit tests!', 0, $time + 3);
+        $this->send_fake_message($user2, $user1, 'Word.', 0, $time + 4);
+
+        // Block user 3.
+        \core_message\api::block_user($user1->id, $user3->id);
 
         // Perform a search.
         $messages = \core_message\api::search_messages($user1->id, 'o');
 
         // Confirm the data is correct.
-        $this->assertEquals(2, count($messages));
+        $this->assertEquals(3, count($messages));
 
         $message1 = $messages[0];
         $message2 = $messages[1];
+        $message3 = $messages[2];
 
         $this->assertEquals($user2->id, $message1->userid);
         $this->assertEquals($user2->id, $message1->useridfrom);
@@ -321,6 +327,17 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
         $this->assertTrue($message2->isread);
         $this->assertFalse($message2->isblocked);
         $this->assertNull($message2->unreadcount);
+
+        $this->assertEquals($user3->id, $message3->userid);
+        $this->assertEquals($user3->id, $message3->useridfrom);
+        $this->assertEquals(fullname($user3), $message3->fullname);
+        $this->assertTrue($message3->ismessaging);
+        $this->assertEquals('Don\'t block me.', $message3->lastmessage);
+        $this->assertNotEmpty($message3->messageid);
+        $this->assertNull($message3->isonline);
+        $this->assertFalse($message3->isread);
+        $this->assertTrue($message3->isblocked);
+        $this->assertNull($message3->unreadcount);
     }
 
     /**
@@ -939,15 +956,16 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
         $user5 = self::getDataGenerator()->create_user($user5);
 
         // Add some users as contacts.
-        message_add_contact($user2->id, 0, $user1->id);
-        message_add_contact($user3->id, 0, $user1->id);
-        message_add_contact($user4->id, 0, $user1->id);
+        \core_message\api::add_contact($user1->id, $user2->id);
+        \core_message\api::add_contact($user1->id, $user3->id);
+        \core_message\api::add_contact($user1->id, $user4->id);
 
         // Retrieve the contacts.
         $contacts = \core_message\api::get_contacts($user1->id);
 
         // Confirm the data is correct.
         $this->assertEquals(3, count($contacts));
+        usort($contacts, ['static', 'sort_contacts']);
 
         $contact1 = $contacts[0];
         $contact2 = $contacts[1];
@@ -1315,7 +1333,7 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
         $this->setUser($user1);
 
         // Block the second user.
-        message_block_contact($user2->id);
+        \core_message\api::block_user($user1->id, $user2->id);
 
         // Check that the second user can no longer send the first user a message.
         $this->assertFalse(\core_message\api::can_post_message($user1, $user2));
@@ -1343,10 +1361,17 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
         $this->assertTrue(\core_message\api::is_user_non_contact_blocked($user2));
 
         // Add the first user as a contact for the second user.
-        message_add_contact($user1->id, 0, $user2->id);
+        \core_message\api::add_contact($user2->id, $user1->id);
 
         // Check that the return result is now false.
         $this->assertFalse(\core_message\api::is_user_non_contact_blocked($user2));
+
+        // Set the first user's preference to not receive messages from non-contacts.
+        set_user_preference('message_blocknoncontacts', 1, $user1->id);
+        $this->setUser($user2);
+        // Confirm it is still false. We want to ensure a contact request works both ways
+        // as it is now an agreement between users.
+        $this->assertFalse(\core_message\api::is_user_non_contact_blocked($user1));
     }
 
     /**
@@ -1363,16 +1388,19 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
 
         // User shouldn't be blocked.
         $this->assertFalse(\core_message\api::is_user_blocked($user1->id, $user2->id));
+        $this->assertDebuggingCalled();
 
         // Block the user.
-        message_block_contact($user2->id);
+        \core_message\api::block_user($user1->id, $user2->id);
 
         // User should be blocked.
         $this->assertTrue(\core_message\api::is_user_blocked($user1->id, $user2->id));
+        $this->assertDebuggingCalled();
 
         // Unblock the user.
-        message_unblock_contact($user2->id);
+        \core_message\api::unblock_user($user1->id, $user2->id);
         $this->assertFalse(\core_message\api::is_user_blocked($user1->id, $user2->id));
+        $this->assertDebuggingCalled();
     }
 
     /**
@@ -1386,13 +1414,14 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
         $this->setUser($user1);
 
         // Block the admin user.
-        message_block_contact(2);
+        \core_message\api::block_user($user1->id, 2);
 
         // Now change to the admin user.
         $this->setAdminUser();
 
         // As the admin you should still be able to send messages to the user.
         $this->assertFalse(\core_message\api::is_user_blocked($user1->id));
+        $this->assertDebuggingCalled();
     }
 
     /*
@@ -1623,13 +1652,12 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
         $user2 = $this->getDataGenerator()->create_user();
 
         // Add users to the admin's contact list.
-        message_add_contact($user1->id);
-        message_add_contact($user2->id, 1);
+        \core_message\api::block_user($USER->id, $user2->id);
 
         $this->assertCount(1, \core_message\api::get_blocked_users($USER->id));
 
         // Block other user.
-        message_block_contact($user1->id);
+        \core_message\api::block_user($USER->id, $user1->id);
         $this->assertCount(2, \core_message\api::get_blocked_users($USER->id));
 
         // Test deleting users.
@@ -1649,9 +1677,8 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
         $user4 = self::getDataGenerator()->create_user();
 
         // Add the users to each of their contacts.
-        message_add_contact($user1->id, 0, $user2->id);
-        message_add_contact($user2->id, 0, $user1->id);
-        message_add_contact($user3->id, 0, $user2->id);
+        \core_message\api::add_contact($user1->id, $user2->id);
+        \core_message\api::add_contact($user2->id, $user3->id);
 
         $this->send_fake_message($user1, $user2);
         $this->send_fake_message($user1, $user2);
@@ -1669,13 +1696,13 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
 
         // Get the contacts and the unread message count.
         $messages = \core_message\api::get_contacts_with_unread_message_count($user2->id);
-
         // Confirm the size is correct.
         $this->assertCount(2, $messages);
         ksort($messages);
 
         $messageinfo1 = array_shift($messages);
         $messageinfo2 = array_shift($messages);
+
         $this->assertEquals($user1->id, $messageinfo1->id);
         $this->assertEquals(4, $messageinfo1->messagecount);
         $this->assertEquals($user3->id, $messageinfo2->id);
@@ -1742,7 +1769,7 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
         $user2 = self::getDataGenerator()->create_user();
 
         // Add the users to each of their contacts.
-        message_add_contact($user1->id, 0, $user2->id);
+        \core_message\api::add_contact($user2->id, $user1->id);
 
         // Check we get the correct message count.
         $messages = \core_message\api::get_contacts_with_unread_message_count($user2->id);
@@ -1768,8 +1795,8 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
         $user4 = self::getDataGenerator()->create_user();
 
         // Add a user to the contact list of the users we are testing this function with.
-        message_add_contact($user4->id, 0, $user1->id);
-        message_add_contact($user4->id, 0, $user2->id);
+        \core_message\api::add_contact($user1->id, $user4->id);
+        \core_message\api::add_contact($user2->id, $user4->id);
 
         $this->send_fake_message($user1, $user2);
         $this->send_fake_message($user1, $user2);
@@ -1939,4 +1966,274 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
         $this->assertEquals($conversationid,
             \core_message\api::get_conversation_between_users([$user1->id, $user2->id]));
     }
+
+    /**
+     * Test can create a contact request.
+     */
+    public function test_can_create_contact_request() {
+        global $CFG;
+
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        // Disable messaging.
+        $CFG->messaging = 0;
+        $this->assertFalse(\core_message\api::can_create_contact($user1->id, $user2->id));
+
+        // Re-enable messaging.
+        $CFG->messaging = 1;
+
+        // Allow users to message anyone site-wide.
+        $CFG->messagingallusers = 1;
+        $this->assertTrue(\core_message\api::can_create_contact($user1->id, $user2->id));
+
+        // Disallow users from messaging anyone site-wide.
+        $CFG->messagingallusers = 0;
+        $this->assertFalse(\core_message\api::can_create_contact($user1->id, $user2->id));
+
+        // Put the users in the same course so a contact request should be possible.
+        $course = self::getDataGenerator()->create_course();
+        $this->getDataGenerator()->enrol_user($user1->id, $course->id);
+        $this->getDataGenerator()->enrol_user($user2->id, $course->id);
+        $this->assertTrue(\core_message\api::can_create_contact($user1->id, $user2->id));
+    }
+
+    /**
+     * Test creating a contact request.
+     */
+    public function test_create_contact_request() {
+        global $DB;
+
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        \core_message\api::create_contact_request($user1->id, $user2->id);
+
+        $request = $DB->get_records('message_contact_requests');
+
+        $this->assertCount(1, $request);
+
+        $request = reset($request);
+
+        $this->assertEquals($user1->id, $request->userid);
+        $this->assertEquals($user2->id, $request->requesteduserid);
+    }
+
+    /**
+     * Test confirming a contact request.
+     */
+    public function test_confirm_contact_request() {
+        global $DB;
+
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        \core_message\api::create_contact_request($user1->id, $user2->id);
+
+        \core_message\api::confirm_contact_request($user1->id, $user2->id);
+
+        $this->assertEquals(0, $DB->count_records('message_contact_requests'));
+
+        $contact = $DB->get_records('message_contacts');
+
+        $this->assertCount(1, $contact);
+
+        $contact = reset($contact);
+
+        $this->assertEquals($user1->id, $contact->userid);
+        $this->assertEquals($user2->id, $contact->contactid);
+    }
+
+    /**
+     * Test declining a contact request.
+     */
+    public function test_decline_contact_request() {
+        global $DB;
+
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        \core_message\api::create_contact_request($user1->id, $user2->id);
+
+        \core_message\api::decline_contact_request($user1->id, $user2->id);
+
+        $this->assertEquals(0, $DB->count_records('message_contact_requests'));
+        $this->assertEquals(0, $DB->count_records('message_contacts'));
+    }
+
+    /**
+     * Test retrieving contact requests.
+     */
+    public function test_get_contact_requests() {
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+        $user3 = self::getDataGenerator()->create_user();
+
+        // Block one user, their request should not show up.
+        \core_message\api::block_user($user1->id, $user3->id);
+
+        \core_message\api::create_contact_request($user2->id, $user1->id);
+        \core_message\api::create_contact_request($user3->id, $user1->id);
+
+        $requests = \core_message\api::get_contact_requests($user1->id);
+
+        $this->assertCount(1, $requests);
+
+        $request = reset($requests);
+
+        $this->assertEquals($user2->id, $request->id);
+        $this->assertEquals($user2->picture, $request->picture);
+        $this->assertEquals($user2->firstname, $request->firstname);
+        $this->assertEquals($user2->lastname, $request->lastname);
+        $this->assertEquals($user2->firstnamephonetic, $request->firstnamephonetic);
+        $this->assertEquals($user2->lastnamephonetic, $request->lastnamephonetic);
+        $this->assertEquals($user2->middlename, $request->middlename);
+        $this->assertEquals($user2->alternatename, $request->alternatename);
+        $this->assertEquals($user2->email, $request->email);
+    }
+
+    /**
+     * Test adding contacts.
+     */
+    public function test_add_contact() {
+        global $DB;
+
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        \core_message\api::add_contact($user1->id, $user2->id);
+
+        $contact = $DB->get_records('message_contacts');
+
+        $this->assertCount(1, $contact);
+
+        $contact = reset($contact);
+
+        $this->assertEquals($user1->id, $contact->userid);
+        $this->assertEquals($user2->id, $contact->contactid);
+    }
+
+    /**
+     * Test removing contacts.
+     */
+    public function test_remove_contact() {
+        global $DB;
+
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        \core_message\api::add_contact($user1->id, $user2->id);
+        \core_message\api::remove_contact($user1->id, $user2->id);
+
+        $this->assertEquals(0, $DB->count_records('message_contacts'));
+    }
+
+    /**
+     * Test blocking users.
+     */
+    public function test_block_user() {
+        global $DB;
+
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        \core_message\api::block_user($user1->id, $user2->id);
+
+        $blockedusers = $DB->get_records('message_users_blocked');
+
+        $this->assertCount(1, $blockedusers);
+
+        $blockeduser = reset($blockedusers);
+
+        $this->assertEquals($user1->id, $blockeduser->userid);
+        $this->assertEquals($user2->id, $blockeduser->blockeduserid);
+    }
+
+    /**
+     * Test unblocking users.
+     */
+    public function test_unblock_user() {
+        global $DB;
+
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        \core_message\api::block_user($user1->id, $user2->id);
+        \core_message\api::unblock_user($user1->id, $user2->id);
+
+        $this->assertEquals(0, $DB->count_records('message_users_blocked'));
+    }
+
+    /**
+     * Test is contact check.
+     */
+    public function test_is_contact() {
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+        $user3 = self::getDataGenerator()->create_user();
+
+        \core_message\api::add_contact($user1->id, $user2->id);
+
+        $this->assertTrue(\core_message\api::is_contact($user1->id, $user2->id));
+        $this->assertTrue(\core_message\api::is_contact($user2->id, $user1->id));
+        $this->assertFalse(\core_message\api::is_contact($user2->id, $user3->id));
+    }
+
+    /**
+     * Test get contact.
+     */
+    public function test_get_contact() {
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        \core_message\api::add_contact($user1->id, $user2->id);
+
+        $contact = \core_message\api::get_contact($user1->id, $user2->id);
+
+        $this->assertEquals($user1->id, $contact->userid);
+        $this->assertEquals($user2->id, $contact->contactid);
+    }
+
+    /**
+     * Test is blocked checked.
+     */
+    public function test_is_blocked() {
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        $this->assertFalse(\core_message\api::is_blocked($user1->id, $user2->id));
+        $this->assertFalse(\core_message\api::is_blocked($user2->id, $user1->id));
+
+        \core_message\api::block_user($user1->id, $user2->id);
+
+        $this->assertTrue(\core_message\api::is_blocked($user1->id, $user2->id));
+        $this->assertFalse(\core_message\api::is_blocked($user2->id, $user1->id));
+    }
+
+    /**
+     * Test the contact request exist check.
+     */
+    public function test_does_contact_request_exist() {
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        $this->assertFalse(\core_message\api::does_contact_request_exist($user1->id, $user2->id));
+        $this->assertFalse(\core_message\api::does_contact_request_exist($user2->id, $user1->id));
+
+        \core_message\api::create_contact_request($user1->id, $user2->id);
+
+        $this->assertTrue(\core_message\api::does_contact_request_exist($user1->id, $user2->id));
+        $this->assertTrue(\core_message\api::does_contact_request_exist($user2->id, $user1->id));
+    }
+
+    /**
+     * Comparison function for sorting contacts.
+     *
+     * @param stdClass $a
+     * @param stdClass $b
+     * @return bool
+     */
+    protected static function sort_contacts($a, $b) {
+        return $a->userid > $b->userid;
+    }
 }
index 062ed01..135e154 100644 (file)
@@ -6,16 +6,34 @@ Feature: Manage contacts
 
   Background:
     Given the following "users" exist:
-      | username | firstname | lastname | email            |
+      | username | firstname | lastname | email                |
       | user1    | User      | 1        | user1@example.com    |
       | user2    | User      | 2        | user2@example.com    |
       | user3    | User      | 3        | user3@example.com    |
+    And I log in as "admin"
+    And I set the following administration settings values:
+      | messagingallusers | 1 |
+    And I log out
     And I log in as "user1"
     And I view the "User 2" contact in the message area
     And I click on "Add contact" "link"
     And I view the "User 3" contact in the message area
     And I click on "Add contact" "link"
     And I log out
+    # Approve the contact request for user2
+    And I log in as "user2"
+    And I open the notification popover
+    And I click on "View full notification" "link" in the "#nav-notification-popover-container" "css_element"
+    And I click on "Go to" "link" in the "[data-region=footer]" "css_element"
+    And I click on "Approve" "link" in the "User 1" "table_row"
+    And I log out
+    # Approve the contact request for user3
+    And I log in as "user3"
+    And I open the notification popover
+    And I click on "View full notification" "link" in the "#nav-notification-popover-container" "css_element"
+    And I click on "Go to" "link" in the "[data-region=footer]" "css_element"
+    And I click on "Approve" "link" in the "User 1" "table_row"
+    And I log out
 
   Scenario: Add contact shows in contacts tab
     When I log in as "user1"
index 3f63ed0..25660ce 100644 (file)
@@ -52,6 +52,8 @@ class core_message_events_testcase extends core_message_messagelib_testcase {
      * Test the message contact added event.
      */
     public function test_message_contact_added() {
+        global $USER;
+
         // Set this user as the admin.
         $this->setAdminUser();
 
@@ -60,7 +62,7 @@ class core_message_events_testcase extends core_message_messagelib_testcase {
 
         // Trigger and capture the event when adding a contact.
         $sink = $this->redirectEvents();
-        message_add_contact($user->id);
+        \core_message\api::add_contact($USER->id, $user->id);
         $events = $sink->get_events();
         $event = reset($events);
 
@@ -78,6 +80,8 @@ class core_message_events_testcase extends core_message_messagelib_testcase {
      * Test the message contact removed event.
      */
     public function test_message_contact_removed() {
+        global $USER;
+
         // Set this user as the admin.
         $this->setAdminUser();
 
@@ -85,11 +89,11 @@ class core_message_events_testcase extends core_message_messagelib_testcase {
         $user = $this->getDataGenerator()->create_user();
 
         // Add the user to the admin's contact list.
-        message_add_contact($user->id);
+        \core_message\api::add_contact($USER->id, $user->id);
 
         // Trigger and capture the event when adding a contact.
         $sink = $this->redirectEvents();
-        message_remove_contact($user->id);
+        \core_message\api::remove_contact($USER->id, $user->id);
         $events = $sink->get_events();
         $event = reset($events);
 
@@ -104,61 +108,37 @@ class core_message_events_testcase extends core_message_messagelib_testcase {
     }
 
     /**
-     * Test the message contact blocked event.
+     * Test the message user blocked event.
      */
-    public function test_message_contact_blocked() {
+    public function test_message_user_blocked() {
+        global $USER;
+
         // Set this user as the admin.
         $this->setAdminUser();
 
         // Create a user to add to the admin's contact list.
         $user = $this->getDataGenerator()->create_user();
-        $user2 = $this->getDataGenerator()->create_user();
 
         // Add the user to the admin's contact list.
-        message_add_contact($user->id);
+        \core_message\api::add_contact($USER->id, $user->id);
 
         // Trigger and capture the event when blocking a contact.
         $sink = $this->redirectEvents();
-        message_block_contact($user->id);
+        \core_message\api::block_user($USER->id, $user->id);
         $events = $sink->get_events();
         $event = reset($events);
 
         // Check that the event data is valid.
-        $this->assertInstanceOf('\core\event\message_contact_blocked', $event);
+        $this->assertInstanceOf('\core\event\message_user_blocked', $event);
         $this->assertEquals(context_user::instance(2), $event->get_context());
-        $expected = array(SITEID, 'message', 'block contact', 'index.php?user1=' . $user->id . '&amp;user2=2', $user->id);
-        $this->assertEventLegacyLogData($expected, $event);
-        $url = new moodle_url('/message/index.php', array('user1' => $event->userid, 'user2' => $event->relateduserid));
-        $this->assertEquals($url, $event->get_url());
-
-        // Make sure that the contact blocked event is not triggered again.
-        $sink->clear();
-        message_block_contact($user->id);
-        $events = $sink->get_events();
-        $event = reset($events);
-        $this->assertEmpty($event);
-        // Make sure that we still have 1 blocked user.
-        $this->assertEquals(1, \core_message\api::count_blocked_users());
-
-        // Now blocking a user that is not a contact.
-        $sink->clear();
-        message_block_contact($user2->id);
-        $events = $sink->get_events();
-        $event = reset($events);
-
-        // Check that the event data is valid.
-        $this->assertInstanceOf('\core\event\message_contact_blocked', $event);
-        $this->assertEquals(context_user::instance(2), $event->get_context());
-        $expected = array(SITEID, 'message', 'block contact', 'index.php?user1=' . $user2->id . '&amp;user2=2', $user2->id);
-        $this->assertEventLegacyLogData($expected, $event);
-        $url = new moodle_url('/message/index.php', array('user1' => $event->userid, 'user2' => $event->relateduserid));
-        $this->assertEquals($url, $event->get_url());
     }
 
     /**
-     * Test the message contact unblocked event.
+     * Test the message user unblocked event.
      */
-    public function test_message_contact_unblocked() {
+    public function test_message_user_unblocked() {
+        global $USER;
+
         // Set this user as the admin.
         $this->setAdminUser();
 
@@ -166,39 +146,25 @@ class core_message_events_testcase extends core_message_messagelib_testcase {
         $user = $this->getDataGenerator()->create_user();
 
         // Add the user to the admin's contact list.
-        message_add_contact($user->id);
+        \core_message\api::add_contact($USER->id, $user->id);
 
         // Block the user.
-        message_block_contact($user->id);
+        \core_message\api::block_user($USER->id, $user->id);
         // Make sure that we have 1 blocked user.
         $this->assertEquals(1, \core_message\api::count_blocked_users());
 
         // Trigger and capture the event when unblocking a contact.
         $sink = $this->redirectEvents();
-        message_unblock_contact($user->id);
+        \core_message\api::unblock_user($USER->id, $user->id);
         $events = $sink->get_events();
         $event = reset($events);
 
         // Check that the event data is valid.
-        $this->assertInstanceOf('\core\event\message_contact_unblocked', $event);
+        $this->assertInstanceOf('\core\event\message_user_unblocked', $event);
         $this->assertEquals(context_user::instance(2), $event->get_context());
-        $expected = array(SITEID, 'message', 'unblock contact', 'index.php?user1=' . $user->id . '&amp;user2=2', $user->id);
-        $this->assertEventLegacyLogData($expected, $event);
-        $url = new moodle_url('/message/index.php', array('user1' => $event->userid, 'user2' => $event->relateduserid));
-        $this->assertEquals($url, $event->get_url());
 
         // Make sure that we have no blocked users.
         $this->assertEmpty(\core_message\api::count_blocked_users());
-
-        // Make sure that the contact unblocked event is not triggered again.
-        $sink->clear();
-        message_unblock_contact($user->id);
-        $events = $sink->get_events();
-        $event = reset($events);
-        $this->assertEmpty($event);
-
-        // Make sure that we still have no blocked users.
-        $this->assertEmpty(\core_message\api::count_blocked_users());
     }
 
     /**
index f78cc46..784ad14 100644 (file)
@@ -91,42 +91,154 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
     }
 
     /**
-     * Test send_instant_messages
+     * Test send_instant_messages.
      */
     public function test_send_instant_messages() {
+        global $DB, $USER;
 
-        global $DB, $USER, $CFG;
+        $this->resetAfterTest();
 
-        $this->resetAfterTest(true);
         // Transactions used in tests, tell phpunit use alternative reset method.
         $this->preventResetByRollback();
 
-        // Turn off all message processors (so nothing is really sent)
-        require_once($CFG->dirroot . '/message/lib.php');
-        $messageprocessors = get_message_processors();
-        foreach($messageprocessors as $messageprocessor) {
-            $messageprocessor->enabled = 0;
-            $DB->update_record('message_processors', $messageprocessor);
-        }
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
 
-        // Set the required capabilities by the external function
-        $contextid = context_system::instance()->id;
-        $roleid = $this->assignUserCapability('moodle/site:sendmessage', $contextid);
+        $this->setUser($user1);
+
+        // Create test message data.
+        $message1 = array();
+        $message1['touserid'] = $user2->id;
+        $message1['text'] = 'the message.';
+        $message1['clientmsgid'] = 4;
+        $messages = array($message1);
+
+        $sentmessages = core_message_external::send_instant_messages($messages);
+        $sentmessages = external_api::clean_returnvalue(core_message_external::send_instant_messages_returns(), $sentmessages);
+
+        $sentmessage = reset($sentmessages);
+
+        $sql = "SELECT m.*, mcm.userid as useridto
+                 FROM {messages} m
+           INNER JOIN {message_conversations} mc
+                   ON m.conversationid = mc.id
+           INNER JOIN {message_conversation_members} mcm
+                   ON mcm.conversationid = mc.id
+                WHERE mcm.userid != ?
+                  AND m.id = ?";
+        $themessage = $DB->get_record_sql($sql, [$USER->id, $sentmessage['msgid']]);
+
+        // Confirm that the message was inserted correctly.
+        $this->assertEquals($themessage->useridfrom, $user1->id);
+        $this->assertEquals($themessage->useridto, $message1['touserid']);
+        $this->assertEquals($themessage->smallmessage, $message1['text']);
+        $this->assertEquals($sentmessage['clientmsgid'], $message1['clientmsgid']);
+    }
+
+    /**
+     * Test send_instant_messages to a user who has blocked you.
+     */
+    public function test_send_instant_messages_blocked_user() {
+        global $DB;
+
+        $this->resetAfterTest();
+
+        // Transactions used in tests, tell phpunit use alternative reset method.
+        $this->preventResetByRollback();
 
         $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        $this->setUser($user1);
+
+        \core_message\api::block_user($user2->id, $user1->id);
 
         // Create test message data.
         $message1 = array();
-        $message1['touserid'] = $user1->id;
+        $message1['touserid'] = $user2->id;
         $message1['text'] = 'the message.';
         $message1['clientmsgid'] = 4;
         $messages = array($message1);
 
         $sentmessages = core_message_external::send_instant_messages($messages);
+        $sentmessages = external_api::clean_returnvalue(core_message_external::send_instant_messages_returns(), $sentmessages);
 
-        // We need to execute the return values cleaning process to simulate the web service server.
+        $sentmessage = reset($sentmessages);
+
+        $this->assertEquals(get_string('userisblockingyou', 'message'), $sentmessage['errormessage']);
+
+        $this->assertEquals(0, $DB->count_records('messages'));
+    }
+
+    /**
+     * Test send_instant_messages when sending a message to a non-contact who has blocked non-contacts.
+     */
+    public function test_send_instant_messages_block_non_contacts() {
+        global $DB;
+
+        $this->resetAfterTest(true);
+
+        // Transactions used in tests, tell phpunit use alternative reset method.
+        $this->preventResetByRollback();
+
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        $this->setUser($user1);
+
+        // Set the user preference so user 2 does not accept messages from non-contacts.
+        set_user_preference('message_blocknoncontacts', 1, $user2);
+
+        // Create test message data.
+        $message1 = array();
+        $message1['touserid'] = $user2->id;
+        $message1['text'] = 'the message.';
+        $message1['clientmsgid'] = 4;
+        $messages = array($message1);
+
+        $sentmessages = core_message_external::send_instant_messages($messages);
+        $sentmessages = external_api::clean_returnvalue(core_message_external::send_instant_messages_returns(), $sentmessages);
+
+        $sentmessage = reset($sentmessages);
+
+        $this->assertEquals(get_string('userisblockingyounoncontact', 'message', fullname($user2)), $sentmessage['errormessage']);
+
+        $this->assertEquals(0, $DB->count_records('messages'));
+    }
+
+    /**
+     * Test send_instant_messages when sending a message to a contact who has blocked non-contacts.
+     */
+    public function test_send_instant_messages_block_non_contacts_but_am_contact() {
+        global $DB, $USER;
+
+        $this->resetAfterTest(true);
+
+        // Transactions used in tests, tell phpunit use alternative reset method.
+        $this->preventResetByRollback();
+
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        $this->setUser($user1);
+
+        // Set the user preference so user 2 does not accept messages from non-contacts.
+        set_user_preference('message_blocknoncontacts', 1, $user2);
+
+        \core_message\api::add_contact($user1->id, $user2->id);
+
+        // Create test message data.
+        $message1 = array();
+        $message1['touserid'] = $user2->id;
+        $message1['text'] = 'the message.';
+        $message1['clientmsgid'] = 4;
+        $messages = array($message1);
+
+        $sentmessages = core_message_external::send_instant_messages($messages);
         $sentmessages = external_api::clean_returnvalue(core_message_external::send_instant_messages_returns(), $sentmessages);
 
+        $sentmessage = reset($sentmessages);
+
         $sql = "SELECT m.*, mcm.userid as useridto
                  FROM {messages} m
            INNER JOIN {message_conversations} mc
@@ -135,13 +247,75 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
                    ON mcm.conversationid = mc.id
                 WHERE mcm.userid != ?
                   AND m.id = ?";
-        $themessage = $DB->get_record_sql($sql, [$USER->id, $sentmessages[0]['msgid']]);
+        $themessage = $DB->get_record_sql($sql, [$USER->id, $sentmessage['msgid']]);
 
         // Confirm that the message was inserted correctly.
-        $this->assertEquals($themessage->useridfrom, $USER->id);
+        $this->assertEquals($themessage->useridfrom, $user1->id);
         $this->assertEquals($themessage->useridto, $message1['touserid']);
         $this->assertEquals($themessage->smallmessage, $message1['text']);
-        $this->assertEquals($sentmessages[0]['clientmsgid'], $message1['clientmsgid']);
+        $this->assertEquals($sentmessage['clientmsgid'], $message1['clientmsgid']);
+    }
+
+    /**
+     * Test send_instant_messages with no capabilities
+     */
+    public function test_send_instant_messages_no_capability() {
+        global $DB;
+
+        $this->resetAfterTest(true);
+
+        // Transactions used in tests, tell phpunit use alternative reset method.
+        $this->preventResetByRollback();
+
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        $this->setUser($user1);
+
+        // Unset the required capabilities by the external function.
+        $contextid = context_system::instance()->id;
+        $userrole = $DB->get_record('role', array('shortname' => 'user'));
+        $this->unassignUserCapability('moodle/site:sendmessage', $contextid, $userrole->id);
+
+        // Create test message data.
+        $message1 = array();
+        $message1['touserid'] = $user2->id;
+        $message1['text'] = 'the message.';
+        $message1['clientmsgid'] = 4;
+        $messages = array($message1);
+
+        $this->expectException('required_capability_exception');
+        core_message_external::send_instant_messages($messages);
+    }
+
+    /**
+     * Test send_instant_messages when messaging is disabled.
+     */
+    public function test_send_instant_messages_messaging_disabled() {
+        global $CFG;
+
+        $this->resetAfterTest(true);
+
+        // Transactions used in tests, tell phpunit use alternative reset method.
+        $this->preventResetByRollback();
+
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        $this->setUser($user1);
+
+        // Disable messaging.
+        $CFG->messaging = 0;
+
+        // Create test message data.
+        $message1 = array();
+        $message1['touserid'] = $user2->id;
+        $message1['text'] = 'the message.';
+        $message1['clientmsgid'] = 4;
+        $messages = array($message1);
+
+        $this->expectException('moodle_exception');
+        core_message_external::send_instant_messages($messages);
     }
 
     /**
@@ -159,21 +333,25 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
 
         // Adding a contact.
         $return = core_message_external::create_contacts(array($user2->id));
+        $this->assertDebuggingCalled();
         $return = external_api::clean_returnvalue(core_message_external::create_contacts_returns(), $return);
         $this->assertEquals(array(), $return);
 
         // Adding a contact who is already a contact.
         $return = core_message_external::create_contacts(array($user2->id));
+        $this->assertDebuggingCalled();
         $return = external_api::clean_returnvalue(core_message_external::create_contacts_returns(), $return);
         $this->assertEquals(array(), $return);
 
         // Adding multiple contacts.
         $return = core_message_external::create_contacts(array($user3->id, $user4->id));
+        $this->assertDebuggingCalledCount(2);
         $return = external_api::clean_returnvalue(core_message_external::create_contacts_returns(), $return);
         $this->assertEquals(array(), $return);
 
         // Adding a non-existing user.
         $return = core_message_external::create_contacts(array(99999));
+        $this->assertDebuggingCalled();
         $return = external_api::clean_returnvalue(core_message_external::create_contacts_returns(), $return);
         $this->assertCount(1, $return);
         $return = array_pop($return);
@@ -182,6 +360,7 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
 
         // Adding contacts with valid and invalid parameters.
         $return = core_message_external::create_contacts(array($user5->id, 99999));
+        $this->assertDebuggingCalledCount(2);
         $return = external_api::clean_returnvalue(core_message_external::create_contacts_returns(), $return);
         $this->assertCount(1, $return);
         $return = array_pop($return);
@@ -207,8 +386,11 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
         $user5 = self::getDataGenerator()->create_user();
         $user6 = self::getDataGenerator()->create_user();
         $this->setUser($user1);
-        $this->assertEquals(array(), core_message_external::create_contacts(
-            array($user3->id, $user4->id, $user5->id, $user6->id)));
+
+        \core_message\api::add_contact($user1->id, $user3->id);
+        \core_message\api::add_contact($user1->id, $user4->id);
+        \core_message\api::add_contact($user1->id, $user5->id);
+        \core_message\api::add_contact($user1->id, $user6->id);
 
         // Removing a non-contact.
         $return = core_message_external::delete_contacts(array($user2->id));
@@ -248,25 +430,32 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
         $user4 = self::getDataGenerator()->create_user();
         $user5 = self::getDataGenerator()->create_user();
         $this->setUser($user1);
-        $this->assertEquals(array(), core_message_external::create_contacts(array($user3->id, $user4->id, $user5->id)));
+
+        \core_message\api::add_contact($user1->id, $user3->id);
+        \core_message\api::add_contact($user1->id, $user4->id);
+        \core_message\api::add_contact($user1->id, $user5->id);
 
         // Blocking a contact.
         $return = core_message_external::block_contacts(array($user2->id));
+        $this->assertDebuggingCalled();
         $return = external_api::clean_returnvalue(core_message_external::block_contacts_returns(), $return);
         $this->assertEquals(array(), $return);
 
         // Blocking a contact who is already a contact.
         $return = core_message_external::block_contacts(array($user2->id));
+        $this->assertDebuggingCalled();
         $return = external_api::clean_returnvalue(core_message_external::block_contacts_returns(), $return);
         $this->assertEquals(array(), $return);
 
         // Blocking multiple contacts.
         $return = core_message_external::block_contacts(array($user3->id, $user4->id));
+        $this->assertDebuggingCalledCount(2);
         $return = external_api::clean_returnvalue(core_message_external::block_contacts_returns(), $return);
         $this->assertEquals(array(), $return);
 
         // Blocking a non-existing user.
         $return = core_message_external::block_contacts(array(99999));
+        $this->assertDebuggingCalled();
         $return = external_api::clean_returnvalue(core_message_external::block_contacts_returns(), $return);
         $this->assertCount(1, $return);
         $return = array_pop($return);
@@ -275,6 +464,7 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
 
         // Blocking contacts with valid and invalid parameters.
         $return = core_message_external::block_contacts(array($user5->id, 99999));
+        $this->assertDebuggingCalledCount(2);
         $return = external_api::clean_returnvalue(core_message_external::block_contacts_returns(), $return);
         $this->assertCount(1, $return);
         $return = array_pop($return);
@@ -300,33 +490,494 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
         $user5 = self::getDataGenerator()->create_user();
         $user6 = self::getDataGenerator()->create_user();
         $this->setUser($user1);
-        $this->assertEquals(array(), core_message_external::create_contacts(
-            array($user3->id, $user4->id, $user5->id, $user6->id)));
+
+        \core_message\api::add_contact($user1->id, $user3->id);
+        \core_message\api::add_contact($user1->id, $user4->id);
+        \core_message\api::add_contact($user1->id, $user5->id);
+        \core_message\api::add_contact($user1->id, $user6->id);
 
         // Removing a non-contact.
         $return = core_message_external::unblock_contacts(array($user2->id));
+        $this->assertDebuggingCalled();
         $this->assertNull($return);
 
         // Removing one contact.
         $return = core_message_external::unblock_contacts(array($user3->id));
+        $this->assertDebuggingCalled();
         $this->assertNull($return);
 
         // Removing multiple contacts.
         $return = core_message_external::unblock_contacts(array($user4->id, $user5->id));
+        $this->assertDebuggingCalledCount(2);
         $this->assertNull($return);
 
         // Removing contact from unexisting user.
         $return = core_message_external::unblock_contacts(array(99999));
+        $this->assertDebuggingCalled();
         $this->assertNull($return);
 
         // Removing mixed valid and invalid data.
         $return = core_message_external::unblock_contacts(array($user6->id, 99999));
+        $this->assertDebuggingCalledCount(2);
         $this->assertNull($return);
 
         // Try to unblock a contact of another user contact list, should throw an exception.
         // All assertions must be added before this point.
         $this->expectException('required_capability_exception');
         core_message_external::unblock_contacts(array($user2->id), $user3->id);
+        $this->assertDebuggingCalled();
+    }
+
+    /**
+     * Test getting contact requests.
+     */
+    public function test_get_contact_requests() {
+        $this->resetAfterTest();
+
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+        $user3 = self::getDataGenerator()->create_user();
+
+        $this->setUser($user1);
+
+        // Block one user, their request should not show up.
+        \core_message\api::block_user($user1->id, $user3->id);
+
+        \core_message\api::create_contact_request($user2->id, $user1->id);
+        \core_message\api::create_contact_request($user3->id, $user1->id);
+
+        $requests = core_message_external::get_contact_requests($user1->id);
+        $requests = external_api::clean_returnvalue(core_message_external::get_contact_requests_returns(), $requests);
+
+        $this->assertCount(1, $requests);
+
+        $request = reset($requests);
+
+        $this->assertEquals($user2->id, $request['id']);
+        $this->assertEquals($user2->picture, $request['picture']);
+        $this->assertEquals($user2->firstname, $request['firstname']);
+        $this->assertEquals($user2->lastname, $request['lastname']);
+        $this->assertEquals($user2->firstnamephonetic, $request['firstnamephonetic']);
+        $this->assertEquals($user2->lastnamephonetic, $request['lastnamephonetic']);
+        $this->assertEquals($user2->middlename, $request['middlename']);
+        $this->assertEquals($user2->alternatename, $request['alternatename']);
+        $this->assertEquals($user2->email, $request['email']);
+    }
+
+    /**
+     * Test getting contact requests with messaging disabled.
+     */
+    public function test_get_contact_requests_messaging_disabled() {
+        global $CFG;
+
+        $this->resetAfterTest();
+
+        // Create some skeleton data just so we can call the WS.
+        $user1 = self::getDataGenerator()->create_user();
+
+        $this->setUser($user1);
+
+        // Disable messaging.
+        $CFG->messaging = 0;
+
+        // Ensure an exception is thrown.
+        $this->expectException('moodle_exception');
+        core_message_external::get_contact_requests($user1->id);
+    }
+
+    /**
+     * Test getting contact requests with no permission.
+     */
+    public function test_get_contact_requests_no_permission() {
+        $this->resetAfterTest();
+
+        // Create some skeleton data just so we can call the WS.
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+        $user3 = self::getDataGenerator()->create_user();
+
+        $this->setUser($user3);
+
+        // Ensure an exception is thrown.
+        $this->expectException('required_capability_exception');
+        core_message_external::create_contact_request($user1->id, $user2->id);
+    }
+
+    /**
+     * Test creating a contact request.
+     */
+    public function test_create_contact_request() {
+        global $CFG, $DB;
+
+        $this->resetAfterTest();
+
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        $this->setUser($user1);
+
+        // Allow users to message anyone site-wide.
+        $CFG->messagingallusers = 1;
+
+        $return = core_message_external::create_contact_request($user1->id, $user2->id);
+        $return = external_api::clean_returnvalue(core_message_external::create_contact_request_returns(), $return);
+        $this->assertEquals(array(), $return);
+
+        $request = $DB->get_records('message_contact_requests');
+
+        $this->assertCount(1, $request);
+
+        $request = reset($request);
+
+        $this->assertEquals($user1->id, $request->userid);
+        $this->assertEquals($user2->id, $request->requesteduserid);
+    }
+
+    /**
+     * Test creating a contact request when not allowed.
+     */
+    public function test_create_contact_request_not_allowed() {
+        global $CFG;
+
+        $this->resetAfterTest();
+
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        $this->setUser($user1);
+
+        $CFG->messagingallusers = 0;
+
+        $return = core_message_external::create_contact_request($user1->id, $user2->id);
+        $return = external_api::clean_returnvalue(core_message_external::create_contact_request_returns(), $return);
+
+        $warning = reset($return);
+
+        $this->assertEquals('user', $warning['item']);
+        $this->assertEquals($user2->id, $warning['itemid']);
+        $this->assertEquals('cannotcreatecontactrequest', $warning['warningcode']);
+        $this->assertEquals('You are unable to create a contact request for this user', $warning['message']);
+    }
+
+    /**
+     * Test creating a contact request with messaging disabled.
+     */
+    public function test_create_contact_request_messaging_disabled() {
+        global $CFG;
+
+        $this->resetAfterTest();
+
+        // Create some skeleton data just so we can call the WS.
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        $this->setUser($user1);
+
+        // Disable messaging.
+        $CFG->messaging = 0;
+
+        // Ensure an exception is thrown.
+        $this->expectException('moodle_exception');
+        core_message_external::create_contact_request($user1->id, $user2->id);
+    }
+
+    /**
+     * Test creating a contact request with no permission.
+     */
+    public function test_create_contact_request_no_permission() {
+        $this->resetAfterTest();
+
+        // Create some skeleton data just so we can call the WS.
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+        $user3 = self::getDataGenerator()->create_user();
+
+        $this->setUser($user3);
+
+        // Ensure an exception is thrown.
+        $this->expectException('required_capability_exception');
+        core_message_external::create_contact_request($user1->id, $user2->id);
+    }
+
+    /**
+     * Test confirming a contact request.
+     */
+    public function test_confirm_contact_request() {
+        global $DB;
+
+        $this->resetAfterTest();
+
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        $this->setUser($user1);
+
+        \core_message\api::create_contact_request($user1->id, $user2->id);
+
+        $this->setUser($user2);
+
+        $return = core_message_external::confirm_contact_request($user1->id, $user2->id);
+        $return = external_api::clean_returnvalue(core_message_external::confirm_contact_request_returns(), $return);
+        $this->assertEquals(array(), $return);
+
+        $this->assertEquals(0, $DB->count_records('message_contact_requests'));
+
+        $contact = $DB->get_records('message_contacts');
+
+        $this->assertCount(1, $contact);
+
+        $contact = reset($contact);
+
+        $this->assertEquals($user1->id, $contact->userid);
+        $this->assertEquals($user2->id, $contact->contactid);
+    }
+
+    /**
+     * Test confirming a contact request with messaging disabled.
+     */
+    public function test_confirm_contact_request_messaging_disabled() {
+        global $CFG;
+
+        $this->resetAfterTest();
+
+        // Create some skeleton data just so we can call the WS.
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        $this->setUser($user1);
+
+        // Disable messaging.
+        $CFG->messaging = 0;
+
+        // Ensure an exception is thrown.
+        $this->expectException('moodle_exception');
+        core_message_external::confirm_contact_request($user1->id, $user2->id);
+    }
+
+    /**
+     * Test confirming a contact request with no permission.
+     */
+    public function test_confirm_contact_request_no_permission() {
+        $this->resetAfterTest();
+
+        // Create some skeleton data just so we can call the WS.
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+        $user3 = self::getDataGenerator()->create_user();
+
+        $this->setUser($user3);
+
+        // Ensure an exception is thrown.
+        $this->expectException('required_capability_exception');
+        core_message_external::confirm_contact_request($user1->id, $user2->id);
+    }
+
+    /**
+     * Test declining a contact request.
+     */
+    public function test_decline_contact_request() {
+        global $DB;
+
+        $this->resetAfterTest();
+
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        $this->setUser($user1);
+
+        \core_message\api::create_contact_request($user1->id, $user2->id);
+
+        $this->setUser($user2);
+
+        $return = core_message_external::decline_contact_request($user1->id, $user2->id);
+        $return = external_api::clean_returnvalue(core_message_external::decline_contact_request_returns(), $return);
+        $this->assertEquals(array(), $return);
+
+        $this->assertEquals(0, $DB->count_records('message_contact_requests'));
+        $this->assertEquals(0, $DB->count_records('message_contacts'));
+    }
+
+    /**
+     * Test declining a contact request with messaging disabled.
+     */
+    public function test_decline_contact_request_messaging_disabled() {
+        global $CFG;
+
+        $this->resetAfterTest();
+
+        // Create some skeleton data just so we can call the WS.
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        $this->setUser($user1);
+
+        // Disable messaging.
+        $CFG->messaging = 0;
+
+        // Ensure an exception is thrown.
+        $this->expectException('moodle_exception');
+        core_message_external::decline_contact_request($user1->id, $user2->id);
+    }
+
+    /**
+     * Test declining a contact request with no permission.
+     */
+    public function test_decline_contact_request_no_permission() {
+        $this->resetAfterTest();
+
+        // Create some skeleton data just so we can call the WS.
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+        $user3 = self::getDataGenerator()->create_user();
+
+        $this->setUser($user3);
+
+        // Ensure an exception is thrown.
+        $this->expectException('required_capability_exception');
+        core_message_external::decline_contact_request($user1->id, $user2->id);
+    }
+
+    /**
+     * Test blocking a user.
+     */
+    public function test_block_user() {
+        global $DB;
+
+        $this->resetAfterTest(true);
+
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        $this->setUser($user1);
+
+        // Blocking a user.
+        $return = core_message_external::block_user($user1->id, $user2->id);
+        $return = external_api::clean_returnvalue(core_message_external::block_user_returns(), $return);
+        $this->assertEquals(array(), $return);
+
+        // Get list of blocked users.
+        $record = $DB->get_record('message_users_blocked', []);
+
+        $this->assertEquals($user1->id, $record->userid);
+        $this->assertEquals($user2->id, $record->blockeduserid);
+
+        // Blocking a user who is already blocked.
+        $return = core_message_external::block_user($user1->id, $user2->id);
+        $return = external_api::clean_returnvalue(core_message_external::block_user_returns(), $return);
+        $this->assertEquals(array(), $return);
+
+        $this->assertEquals(1, $DB->count_records('message_users_blocked'));
+    }
+
+    /**
+     * Test blocking a user with messaging disabled.
+     */
+    public function test_block_user_messaging_disabled() {
+        global $CFG;
+
+        $this->resetAfterTest();
+
+        // Create some skeleton data just so we can call the WS.
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        $this->setUser($user1);
+
+        // Disable messaging.
+        $CFG->messaging = 0;
+
+        // Ensure an exception is thrown.
+        $this->expectException('moodle_exception');
+        core_message_external::block_user($user1->id, $user2->id);
+    }
+
+    /**
+     * Test blocking a user with no permission.
+     */
+    public function test_block_user_no_permission() {
+        $this->resetAfterTest();
+
+        // Create some skeleton data just so we can call the WS.
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+        $user3 = self::getDataGenerator()->create_user();
+
+        $this->setUser($user3);
+
+        // Ensure an exception is thrown.
+        $this->expectException('required_capability_exception');
+        core_message_external::block_user($user1->id, $user2->id);
+    }
+
+    /**
+     * Test unblocking a user.
+     */
+    public function test_unblock_user() {
+        global $DB;
+
+        $this->resetAfterTest(true);
+
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        $this->setUser($user1);
+
+        // Block the user.
+        \core_message\api::block_user($user1->id, $user2->id);
+
+        // Unblocking a user.
+        $return = core_message_external::unblock_user($user1->id, $user2->id);
+        $return = external_api::clean_returnvalue(core_message_external::unblock_user_returns(), $return);
+        $this->assertEquals(array(), $return);
+
+        $this->assertEquals(0, $DB->count_records('message_users_blocked'));
+
+        // Unblocking a user who is already unblocked.
+        $return = core_message_external::unblock_user($user1->id, $user2->id);
+        $return = external_api::clean_returnvalue(core_message_external::unblock_user_returns(), $return);
+        $this->assertEquals(array(), $return);
+
+        $this->assertEquals(0, $DB->count_records('message_users_blocked'));
+    }
+
+    /**
+     * Test unblocking a user with messaging disabled.
+     */
+    public function test_unblock_user_messaging_disabled() {
+        global $CFG;
+
+        $this->resetAfterTest();
+
+        // Create some skeleton data just so we can call the WS.
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        $this->setUser($user1);
+
+        // Disable messaging.
+        $CFG->messaging = 0;
+
+        // Ensure an exception is thrown.
+        $this->expectException('moodle_exception');
+        core_message_external::unblock_user($user1->id, $user2->id);
+    }
+
+    /**
+     * Test unblocking a user with no permission.
+     */
+    public function test_unblock_user_no_permission() {
+        $this->resetAfterTest();
+
+        // Create some skeleton data just so we can call the WS.
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+        $user3 = self::getDataGenerator()->create_user();
+
+        $this->setUser($user3);
+
+        // Ensure an exception is thrown.
+        $this->expectException('required_capability_exception');
+        core_message_external::unblock_user($user1->id, $user2->id);
     }
 
     /**
@@ -348,8 +999,10 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
 
         // Login as user1.
         $this->setUser($user1);
-        $this->assertEquals(array(), core_message_external::create_contacts(
-            array($user_offline1->id, $user_offline2->id, $user_offline3->id, $user_online->id)));
+        \core_message\api::add_contact($user1->id, $user_offline1->id);
+        \core_message\api::add_contact($user1->id, $user_offline2->id);
+        \core_message\api::add_contact($user1->id, $user_offline3->id);
+        \core_message\api::add_contact($user1->id, $user_online->id);
 
         // User_stranger sends a couple of messages to user1.
         $this->send_message($user_stranger, $user1, 'Hello there!');
@@ -368,6 +1021,7 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
         $this->assertCount(1, $contacts['online']);
         $this->assertCount(3, $contacts['strangers']);
         core_message_external::block_contacts(array($user_blocked->id));
+        $this->assertDebuggingCalled();
         $contacts = core_message_external::get_contacts();
         $contacts = external_api::clean_returnvalue(core_message_external::get_contacts_returns(), $contacts);
         $this->assertCount(3, $contacts['offline']);
@@ -772,8 +1426,9 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
 
         // Login as user1.
         $this->setUser($user1);
-        $this->assertEquals(array(), core_message_external::create_contacts(
-            array($useroffline1->id, $useroffline2->id)));
+
+        \core_message\api::add_contact($user1->id, $useroffline1->id);
+        \core_message\api::add_contact($user1->id, $useroffline2->id);
 
         // The userstranger sends a couple of messages to user1.
         $this->send_message($userstranger, $user1, 'Hello there!');
@@ -791,6 +1446,7 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
 
         // Block the $userblocked and retrieve again the list.
         core_message_external::block_contacts(array($userblocked->id));
+        $this->assertDebuggingCalled();
         $blockedusers = core_message_external::get_blocked_users($user1->id);
         $blockedusers = external_api::clean_returnvalue(core_message_external::get_blocked_users_returns(), $blockedusers);
         $this->assertCount(1, $blockedusers['users']);
@@ -800,7 +1456,6 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
         $blockedusers = core_message_external::get_blocked_users($user1->id);
         $blockedusers = external_api::clean_returnvalue(core_message_external::get_blocked_users_returns(), $blockedusers);
         $this->assertCount(0, $blockedusers['users']);
-
     }
 
     /**
@@ -815,8 +1470,8 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
 
         // Login as user1.
         $this->setUser($user1);
-        $this->assertEquals(array(), core_message_external::create_contacts(
-            array($user2->id, $user3->id)));
+        \core_message\api::add_contact($user1->id, $user2->id);
+        \core_message\api::add_contact($user1->id, $user3->id);
 
         // The user2 sends a couple of messages to user1.
         $this->send_message($user2, $user1, 'Hello there!');
@@ -874,8 +1529,8 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
 
         // Login as user1.
         $this->setUser($user1);
-        $this->assertEquals(array(), core_message_external::create_contacts(
-            array($user2->id, $user3->id)));
+        \core_message\api::add_contact($user1->id, $user2->id);
+        \core_message\api::add_contact($user1->id, $user3->id);
 
         // The user2 sends a couple of notifications to user1.
         $this->send_message($user2, $user1, 'Hello there!', 1);
@@ -936,7 +1591,8 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
 
         // Login as user1.
         $this->setUser($user1);
-        $this->assertEquals(array(), core_message_external::create_contacts(array($user2->id, $user3->id)));
+        \core_message\api::add_contact($user1->id, $user2->id);
+        \core_message\api::add_contact($user1->id, $user3->id);
 
         // User user1 does not interchange messages with user3.
         $m1to2 = message_post_message($user1, $user2, 'some random text 1', FORMAT_MOODLE);
@@ -1165,7 +1821,7 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
         $user2 = self::getDataGenerator()->create_user($user2);
 
         // Block the second user.
-        message_block_contact($user2->id, $user1->id);
+        \core_message\api::block_user($user1->id, $user2->id);
 
         $user3 = new stdClass();
         $user3->firstname = 'User';
@@ -1375,9 +2031,9 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
         $this->getDataGenerator()->enrol_user($user1->id, $course3->id, 'student');
 
         // Add some users as contacts.
-        message_add_contact($user2->id, 0, $user1->id);
-        message_add_contact($user3->id, 0, $user1->id);
-        message_add_contact($user4->id, 0, $user1->id);
+        \core_message\api::add_contact($user1->id, $user2->id);
+        \core_message\api::add_contact($user1->id, $user3->id);
+        \core_message\api::add_contact($user1->id, $user4->id);
 
         // Perform a search.
         $result = core_message_external::data_for_messagearea_search_users($user1->id, 'search');
@@ -1463,9 +2119,9 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
         $course3 = $this->getDataGenerator()->create_course($course3);
 
         // Add some users as contacts.
-        message_add_contact($user2->id, 0, $user1->id);
-        message_add_contact($user3->id, 0, $user1->id);
-        message_add_contact($user4->id, 0, $user1->id);
+        \core_message\api::add_contact($user1->id, $user2->id);
+        \core_message\api::add_contact($user1->id, $user3->id);
+        \core_message\api::add_contact($user1->id, $user4->id);
 
         // Perform a search.
         $result = core_message_external::data_for_messagearea_search_users($user1->id, 'search');
@@ -1913,9 +2569,9 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
         $user5 = self::getDataGenerator()->create_user($user5);
 
         // Add some users as contacts.
-        message_add_contact($user2->id, 0, $user1->id);
-        message_add_contact($user3->id, 0, $user1->id);
-        message_add_contact($user4->id, 0, $user1->id);
+        \core_message\api::add_contact($user1->id, $user2->id);
+        \core_message\api::add_contact($user1->id, $user3->id);
+        \core_message\api::add_contact($user1->id, $user4->id);
 
         // Retrieve the contacts.
         $result = core_message_external::data_for_messagearea_contacts($user1->id);
@@ -1926,6 +2582,7 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
 
         // Confirm the data is correct.
         $contacts = $result['contacts'];
+        usort($contacts, ['static', 'sort_contacts']);
         $this->assertCount(3, $contacts);
 
         $contact1 = $contacts[0];
@@ -1995,9 +2652,9 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
         $user5 = self::getDataGenerator()->create_user($user5);
 
         // Add some users as contacts.
-        message_add_contact($user2->id, 0, $user1->id);
-        message_add_contact($user3->id, 0, $user1->id);
-        message_add_contact($user4->id, 0, $user1->id);
+        \core_message\api::add_contact($user1->id, $user2->id);
+        \core_message\api::add_contact($user1->id, $user3->id);
+        \core_message\api::add_contact($user1->id, $user4->id);
 
         // Retrieve the contacts.
         $result = core_message_external::data_for_messagearea_contacts($user1->id);
@@ -2008,6 +2665,7 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
 
         // Confirm the data is correct.
         $contacts = $result['contacts'];
+        usort($contacts, ['static', 'sort_contacts']);
         $this->assertCount(3, $contacts);
 
         $contact1 = $contacts[0];
@@ -2894,4 +3552,15 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
         $this->expectException('moodle_exception');
         $prefs = core_message_external::get_user_message_preferences($otheruser->id);
     }
+
+    /**
+     * Comparison function for sorting contacts.
+     *
+     * @param array $a
+     * @param array $b
+     * @return bool
+     */
+    protected static function sort_contacts($a, $b) {
+        return $a['userid'] > $b['userid'];
+    }
 }
index e2c2dcd..0aaac07 100644 (file)
@@ -106,6 +106,8 @@ class core_message_messagelib_testcase extends advanced_testcase {
      * Test message_get_blocked_users.
      */
     public function test_message_get_blocked_users() {
+        global $USER;
+
         // Set this user as the admin.
         $this->setAdminUser();
 
@@ -113,15 +115,13 @@ class core_message_messagelib_testcase extends advanced_testcase {
         $user1 = $this->getDataGenerator()->create_user();
         $user2 = $this->getDataGenerator()->create_user();
 
-        // Add users to the admin's contact list.
-        message_add_contact($user1->id);
-        message_add_contact($user2->id, 1);
+        \core_message\api::block_user($USER->id, $user2->id);
 
         $this->assertCount(1, message_get_blocked_users());
         $this->assertDebuggingCalled();
 
         // Block other user.
-        message_block_contact($user1->id);
+        \core_message\api::block_user($USER->id, $user1->id);
         $this->assertCount(2, message_get_blocked_users());
         $this->assertDebuggingCalled();
 
@@ -149,8 +149,8 @@ class core_message_messagelib_testcase extends advanced_testcase {
         $user3 = $this->getDataGenerator()->create_user(); // Stranger.
 
         // Add users to the admin's contact list.
-        message_add_contact($user1->id);
-        message_add_contact($user2->id);
+        \core_message\api::add_contact($USER->id, $user1->id);
+        \core_message\api::add_contact($USER->id, $user2->id);
 
         // Send some messages.
         $this->send_fake_message($user1, $USER);
@@ -173,7 +173,7 @@ class core_message_messagelib_testcase extends advanced_testcase {
         $this->assertCount(3, $strangers);
 
         // Block 1 user.
-        message_block_contact($user2->id);
+        \core_message\api::block_user($USER->id, $user2->id);
         list($onlinecontacts, $offlinecontacts, $strangers) = message_get_contacts();
         $this->assertDebuggingCalled();
         $this->assertCount(0, $onlinecontacts);
@@ -286,29 +286,34 @@ class core_message_messagelib_testcase extends advanced_testcase {
      * Test message_add_contact.
      */
     public function test_message_add_contact() {
+        global $DB, $USER;
+
         // Set this user as the admin.
         $this->setAdminUser();
 
         // Create a user to add to the admin's contact list.
         $user1 = $this->getDataGenerator()->create_user();
         $user2 = $this->getDataGenerator()->create_user();
-        $user3 = $this->getDataGenerator()->create_user();
 
         message_add_contact($user1->id);
-        message_add_contact($user2->id, 0);
-        // Add duplicate contact and make sure only 1 record exists.
+        $this->assertDebuggingCalled();
+        $this->assertEquals(1, $DB->count_records('message_contact_requests'));
+
         message_add_contact($user2->id, 1);
+        $this->assertDebuggingCalled();
+        $this->assertEquals(1, $DB->count_records('message_users_blocked'));
 
-        $this->assertNotEmpty(message_get_contact($user1->id));
-        $this->assertNotEmpty(message_get_contact($user2->id));
-        $this->assertEquals(false, message_get_contact($user3->id));
-        $this->assertEquals(1, \core_message\api::count_blocked_users());
+        message_add_contact($user2->id, 0);
+        $this->assertDebuggingCalled();
+        $this->assertEquals(0, $DB->count_records('message_users_blocked'));
     }
 
     /**
      * Test message_remove_contact.
      */
     public function test_message_remove_contact() {
+        global $USER;
+
         // Set this user as the admin.
         $this->setAdminUser();
 
@@ -316,18 +321,21 @@ class core_message_messagelib_testcase extends advanced_testcase {
         $user = $this->getDataGenerator()->create_user();
 
         // Add the user to the admin's contact list.
-        message_add_contact($user->id);
-        $this->assertNotEmpty(message_get_contact($user->id));
+        \core_message\api::add_contact($USER->id, $user->id);
 
         // Remove user from admin's contact list.
         message_remove_contact($user->id);
+        $this->assertDebuggingCalled();
         $this->assertEquals(false, message_get_contact($user->id));
+        $this->assertDebuggingCalled();
     }
 
     /**
      * Test message_block_contact.
      */
     public function test_message_block_contact() {
+        global $USER;
+
         // Set this user as the admin.
         $this->setAdminUser();
 
@@ -336,13 +344,14 @@ class core_message_messagelib_testcase extends advanced_testcase {
         $user2 = $this->getDataGenerator()->create_user();
 
         // Add users to the admin's contact list.
-        message_add_contact($user1->id);
-        message_add_contact($user2->id);
+        \core_message\api::add_contact($USER->id, $user1->id);
+        \core_message\api::add_contact($USER->id, $user2->id);
 
         $this->assertEquals(0, \core_message\api::count_blocked_users());
 
         // Block 1 user.
         message_block_contact($user2->id);
+        $this->assertDebuggingCalled();
         $this->assertEquals(1, \core_message\api::count_blocked_users());
 
     }
@@ -351,21 +360,21 @@ class core_message_messagelib_testcase extends advanced_testcase {
      * Test message_unblock_contact.
      */
     public function test_message_unblock_contact() {
+        global $USER;
+
         // Set this user as the admin.
         $this->setAdminUser();
 
         // Create a user to add to the admin's contact list.
         $user1 = $this->getDataGenerator()->create_user();
-        $user2 = $this->getDataGenerator()->create_user();
-
-        // Add users to the admin's contact list.
-        message_add_contact($user1->id);
-        message_add_contact($user2->id, 1); // Add blocked contact.
 
+        // Add users to the admin's blocked list.
+        \core_message\api::block_user($USER->id, $user1->id);
         $this->assertEquals(1, \core_message\api::count_blocked_users());
 
         // Unblock user.
-        message_unblock_contact($user2->id);
+        message_unblock_contact($user1->id);
+        $this->assertDebuggingCalled();
         $this->assertEquals(0, \core_message\api::count_blocked_users());
     }
 
@@ -373,6 +382,8 @@ class core_message_messagelib_testcase extends advanced_testcase {
      * Test message_search_users.
      */
     public function test_message_search_users() {
+        global $USER;
+
         // Set this user as the admin.
         $this->setAdminUser();
 
@@ -381,8 +392,8 @@ class core_message_messagelib_testcase extends advanced_testcase {
         $user2 = $this->getDataGenerator()->create_user(array('firstname' => 'Test2', 'lastname' => 'user2'));
 
         // Add users to the admin's contact list.
-        message_add_contact($user1->id);
-        message_add_contact($user2->id); // Add blocked contact.
+        \core_message\api::add_contact($USER->id, $user1->id);
+        \core_message\api::add_contact($USER->id, $user2->id);
 
         $this->assertCount(1, message_search_users(0, 'Test1'));
         $this->assertCount(2, message_search_users(0, 'Test'));
index 976df59..3500c79 100644 (file)
@@ -45,7 +45,7 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
         $collection = new collection('core_message');
         $newcollection = provider::get_metadata($collection);
         $itemcollection = $newcollection->get_collection();
-        $this->assertCount(6, $itemcollection);
+        $this->assertCount(8, $itemcollection);
 
         $messagestable = array_shift($itemcollection);
         $this->assertEquals('messages', $messagestable->get_name());
@@ -59,6 +59,12 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
         $messagecontacts = array_shift($itemcollection);
         $this->assertEquals('message_contacts', $messagecontacts->get_name());
 
+        $messagecontactrequests = array_shift($itemcollection);
+        $this->assertEquals('message_contact_requests', $messagecontactrequests->get_name());
+
+        $messageusersblocked = array_shift($itemcollection);
+        $this->assertEquals('message_users_blocked', $messageusersblocked->get_name());
+
         $notificationstable = array_shift($itemcollection);
         $this->assertEquals('notifications', $notificationstable->get_name());
 
@@ -92,9 +98,21 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
         $privacyfields = $messagecontacts->get_privacy_fields();
         $this->assertArrayHasKey('userid', $privacyfields);
         $this->assertArrayHasKey('contactid', $privacyfields);
-        $this->assertArrayHasKey('blocked', $privacyfields);
+        $this->assertArrayHasKey('timecreated', $privacyfields);
         $this->assertEquals('privacy:metadata:message_contacts', $messagecontacts->get_summary());
 
+        $privacyfields = $messagecontactrequests->get_privacy_fields();
+        $this->assertArrayHasKey('userid', $privacyfields);
+        $this->assertArrayHasKey('requesteduserid', $privacyfields);
+        $this->assertArrayHasKey('timecreated', $privacyfields);
+        $this->assertEquals('privacy:metadata:message_contact_requests', $messagecontactrequests->get_summary());
+
+        $privacyfields = $messageusersblocked->get_privacy_fields();
+        $this->assertArrayHasKey('userid', $privacyfields);
+        $this->assertArrayHasKey('blockeduserid', $privacyfields);
+        $this->assertArrayHasKey('timecreated', $privacyfields);
+        $this->assertEquals('privacy:metadata:message_users_blocked', $messageusersblocked->get_summary());
+
         $privacyfields = $notificationstable->get_privacy_fields();
         $this->assertArrayHasKey('useridfrom', $privacyfields);
         $this->assertArrayHasKey('useridto', $privacyfields);
@@ -198,12 +216,9 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
         $user3 = $this->getDataGenerator()->create_user();
         $user4 = $this->getDataGenerator()->create_user();
 
-        // This user will not be added as a contact.
-        $this->getDataGenerator()->create_user();
-
-        message_add_contact($user2->id, 0, $user1->id);
-        message_add_contact($user3->id, 0, $user1->id);
-        message_add_contact($user4->id, 1, $user1->id);
+        \core_message\api::add_contact($user1->id, $user2->id);
+        \core_message\api::add_contact($user1->id, $user3->id);
+        \core_message\api::add_contact($user1->id, $user4->id);
 
         $this->export_context_data_for_user($user1->id, \context_system::instance(), 'core_message');
 
@@ -216,15 +231,83 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
 
         $contact1 = array_shift($contacts);
         $this->assertEquals($user2->id, $contact1->contact);
-        $this->assertEquals(get_string('no'), $contact1->blocked);
 
         $contact2 = array_shift($contacts);
         $this->assertEquals($user3->id, $contact2->contact);
-        $this->assertEquals(get_string('no'), $contact2->blocked);
 
         $contact3 = array_shift($contacts);
         $this->assertEquals($user4->id, $contact3->contact);
-        $this->assertEquals(get_string('yes'), $contact3->blocked);
+    }
+
+    /**
+     * Test for provider::export_user_data().
+     */
+    public function test_export_for_context_with_contact_requests() {
+        $this->resetAfterTest();
+
+        // Create users to test with.
+        $user1 = $this->getDataGenerator()->create_user();
+        $user2 = $this->getDataGenerator()->create_user();
+        $user3 = $this->getDataGenerator()->create_user();
+        $user4 = $this->getDataGenerator()->create_user();
+
+        \core_message\api::create_contact_request($user1->id, $user2->id);
+        \core_message\api::create_contact_request($user3->id, $user1->id);
+        \core_message\api::create_contact_request($user1->id, $user4->id);
+
+        $this->export_context_data_for_user($user1->id, \context_system::instance(), 'core_message');
+
+        $writer = writer::with_context(\context_system::instance());
+
+        $contactrequests = (array) $writer->get_data([get_string('contactrequests', 'core_message')]);
+
+        $this->assertCount(3, $contactrequests);
+
+        $contactrequest1 = array_shift($contactrequests);
+        $this->assertEquals($user2->id, $contactrequest1->contactrequest);
+        $this->assertEquals(get_string('yes'), $contactrequest1->maderequest);
+
+        $contactrequest2 = array_shift($contactrequests);
+        $this->assertEquals($user3->id, $contactrequest2->contactrequest);
+        $this->assertEquals(get_string('no'), $contactrequest2->maderequest);
+
+        $contactrequest3 = array_shift($contactrequests);
+        $this->assertEquals($user4->id, $contactrequest3->contactrequest);
+        $this->assertEquals(get_string('yes'), $contactrequest3->maderequest);
+    }
+
+    /**
+     * Test for provider::export_user_data().
+     */
+    public function test_export_for_context_with_blocked_users() {
+        $this->resetAfterTest();
+
+        // Create users to test with.
+        $user1 = $this->getDataGenerator()->create_user();
+        $user2 = $this->getDataGenerator()->create_user();
+        $user3 = $this->getDataGenerator()->create_user();
+        $user4 = $this->getDataGenerator()->create_user();
+
+        \core_message\api::block_user($user1->id, $user2->id);
+        \core_message\api::block_user($user1->id, $user3->id);
+        \core_message\api::block_user($user1->id, $user4->id);
+
+        $this->export_context_data_for_user($user1->id, \context_system::instance(), 'core_message');
+
+        $writer = writer::with_context(\context_system::instance());
+
+        $blockedusers = (array) $writer->get_data([get_string('blockedusers', 'core_message')]);
+
+        $this->assertCount(3, $blockedusers);
+
+        $blockeduser1 = array_shift($blockedusers);
+        $this->assertEquals($user2->id, $blockeduser1->blockeduser);
+
+        $blockeduser2 = array_shift($blockedusers);
+        $this->assertEquals($user3->id, $blockeduser2->blockeduser);
+
+        $blockeduser3 = array_shift($blockedusers);
+        $this->assertEquals($user4->id, $blockeduser3->blockeduser);
     }
 
     /**
@@ -380,6 +463,8 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
         // Create users to test with.
         $user1 = $this->getDataGenerator()->create_user();
         $user2 = $this->getDataGenerator()->create_user();
+        $user3 = $this->getDataGenerator()->create_user();
+        $user4 = $this->getDataGenerator()->create_user();
 
         $now = time();
         $timeread = $now - DAYSECS;
@@ -387,8 +472,13 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
         $systemcontext = \context_system::instance();
 
         // Create contacts.
-        message_add_contact($user1->id, 0, $user2->id);
-        message_add_contact($user2->id, 0, $user1->id);
+        \core_message\api::add_contact($user1->id, $user2->id);
+
+        // Create contact requests.
+        \core_message\api::create_contact_request($user1->id, $user3->id);
+
+        // Block a user.
+        \core_message\api::block_user($user1->id, $user3->id);
 
         // Create messages.
         $m1 = $this->create_message($user1->id, $user2->id, $now + (9 * DAYSECS), true);
@@ -401,8 +491,14 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
         // Delete one of the messages.
         \core_message\api::delete_message($user1->id, $m2);
 
-        // There should be 2 contacts.
-        $this->assertEquals(2, $DB->count_records('message_contacts'));
+        // There should be 1 contact.
+        $this->assertEquals(1, $DB->count_records('message_contacts'));
+
+        // There should be 1 contact request.
+        $this->assertEquals(1, $DB->count_records('message_contact_requests'));
+
+        // There should be 1 blocked user.
+        $this->assertEquals(1, $DB->count_records('message_users_blocked'));
 
         // There should be two messages.
         $this->assertEquals(2, $DB->count_records('messages'));
@@ -413,13 +509,15 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
         // There should be two conversation members.
         $this->assertEquals(2, $DB->count_records('message_conversation_members'));
 
-        // There should be two notifications.
-        $this->assertEquals(2, $DB->count_records('notifications'));
+        // There should be two notifications + one for the contact request.
+        $this->assertEquals(3, $DB->count_records('notifications'));
 
         provider::delete_data_for_all_users_in_context($systemcontext);
 
         // Confirm all has been deleted.
         $this->assertEquals(0, $DB->count_records('message_contacts'));
+        $this->assertEquals(0, $DB->count_records('message_contact_requests'));
+        $this->assertEquals(0, $DB->count_records('message_users_blocked'));
         $this->assertEquals(0, $DB->count_records('messages'));
         $this->assertEquals(0, $DB->count_records('message_user_actions'));
         $this->assertEquals(0, $DB->count_records('message_conversation_members'));
@@ -438,14 +536,24 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
         $user1 = $this->getDataGenerator()->create_user();
         $user2 = $this->getDataGenerator()->create_user();
         $user3 = $this->getDataGenerator()->create_user();
+        $user4 = $this->getDataGenerator()->create_user();
+        $user5 = $this->getDataGenerator()->create_user();
+        $user6 = $this->getDataGenerator()->create_user();
 
         $now = time();
         $timeread = $now - DAYSECS;
 
         // Create contacts.
-        message_add_contact($user1->id, 0, $user2->id);
-        message_add_contact($user2->id, 0, $user1->id);
-        message_add_contact($user2->id, 0, $user3->id);
+        \core_message\api::add_contact($user1->id, $user2->id);
+        \core_message\api::add_contact($user2->id, $user3->id);
+
+        // Create contact requests.
+        \core_message\api::create_contact_request($user1->id, $user3->id);
+        \core_message\api::create_contact_request($user2->id, $user4->id);
+
+        // Block users.
+        \core_message\api::block_user($user1->id, $user5->id);
+        \core_message\api::block_user($user2->id, $user6->id);
 
         // Create messages.
         $m1 = $this->create_message($user1->id, $user2->id, $now + (9 * DAYSECS), $timeread);
@@ -459,8 +567,14 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
         // Delete one of the messages.
         \core_message\api::delete_message($user1->id, $m2);
 
-        // There should be 3 contacts.
-        $this->assertEquals(3, $DB->count_records('message_contacts'));
+        // There should be 2 contacts.
+        $this->assertEquals(2, $DB->count_records('message_contacts'));
+
+        // There should be 1 contact request.
+        $this->assertEquals(2, $DB->count_records('message_contact_requests'));
+
+        // There should be 1 blocked user.
+        $this->assertEquals(2, $DB->count_records('message_users_blocked'));
 
         // There should be two messages.
         $this->assertEquals(2, $DB->count_records('messages'));
@@ -471,8 +585,8 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
         // There should be two conversation members.
         $this->assertEquals(2, $DB->count_records('message_conversation_members'));
 
-        // There should be three notifications.
-        $this->assertEquals(3, $DB->count_records('notifications'));
+        // There should be three notifications + two for the contact requests.
+        $this->assertEquals(5, $DB->count_records('notifications'));
 
         $systemcontext = \context_system::instance();
         $contextlist = new \core_privacy\local\request\approved_contextlist($user1, 'core_message',
@@ -481,6 +595,8 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
 
         // Confirm the user 2 data still exists.
         $contacts = $DB->get_records('message_contacts');
+        $contactrequests = $DB->get_records('message_contact_requests');
+        $blockedusers = $DB->get_records('message_users_blocked');
         $messages = $DB->get_records('messages');
         $muas = $DB->get_records('message_user_actions');
         $mcms = $DB->get_records('message_conversation_members');
@@ -488,8 +604,18 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
 
         $this->assertCount(1, $contacts);
         $contact = reset($contacts);
-        $this->assertEquals($user3->id, $contact->userid);
-        $this->assertEquals($user2->id, $contact->contactid);
+        $this->assertEquals($user2->id, $contact->userid);
+        $this->assertEquals($user3->id, $contact->contactid);
+
+        $this->assertCount(1, $contactrequests);
+        $contactrequest = reset($contactrequests);
+        $this->assertEquals($user2->id, $contactrequest->userid);
+        $this->assertEquals($user4->id, $contactrequest->requesteduserid);
+
+        $this->assertCount(1, $blockedusers);
+        $blockeduser = reset($blockedusers);
+        $this->assertEquals($user2->id, $blockeduser->userid);
+        $this->assertEquals($user6->id, $blockeduser->blockeduserid);
 
         $this->assertCount(1, $messages);
         $message = reset($messages);
@@ -505,8 +631,14 @@ class core_message_privacy_provider_testcase extends \core_privacy\tests\provide
         $mcm = reset($mcms);
         $this->assertEquals($user2->id, $mcm->userid);
 
-        $this->assertCount(1, $notifications);
-        $notification = reset($notifications);
+        $this->assertCount(2, $notifications);
+        ksort($notifications);
+
+        $notification = array_shift($notifications);
+        $this->assertEquals($user2->id, $notification->useridfrom);
+        $this->assertEquals($user4->id, $notification->useridto);
+
+        $notification = array_shift($notifications);
         $this->assertEquals($user2->id, $notification->useridfrom);
         $this->assertEquals($user3->id, $notification->useridto);
     }
index 4f40560..2ada652 100644 (file)
@@ -19,6 +19,19 @@ information provided here is intended especially for developers.
   * message_can_post_message()
   * message_is_user_non_contact_blocked()
   * message_is_user_blocked()
+* The following functions have been deprecated and should not be used any more:
+  - message_add_contact()
+  - message_remove_contact()
+  - message_unblock_contact()
+  - message_block_contact()
+  - message_get_contact()
+  Please see their declaration in lib/deprecatedlib.php to view their alternatives (if applicable).
+* The following methods have been deprecated and should not be used any more:
+  - \core_message\api::is_user_blocked()
+* The following web services have been deprecated. Please do not call these any more.
+  - core_message_external::block_contacts, please use core_message_external::block_user instead.
+  - core_message_external::unblock_contacts, please use core_message_external::unblock_user instead.
+  - core_message_external::create_contacts, please use core_message_external::create_contact_request instead.
 
 === 3.5 ===
 
index a2dd3e4..bf1b717 100644 (file)
@@ -2371,6 +2371,7 @@ class assign {
         // Submissions are included if all are true:
         //   - The assignment is visible in the gradebook.
         //   - No previous notification has been sent.
+        //   - The grader was a real user, not an automated process.
         //   - If marking workflow is not enabled, the grade was updated in the past 24 hours, or
         //     if marking workflow is enabled, the workflow state is at 'released'.
         $sql = "SELECT g.id as gradeid, a.course, a.name, a.blindmarking, a.revealidentities,
@@ -2384,7 +2385,7 @@ class assign {
             LEFT JOIN {assign_user_mapping} um ON g.id = um.userid AND um.assignment = a.id
                  WHERE ((a.markingworkflow = 0 AND g.timemodified >= :yesterday AND g.timemodified <= :today) OR
                         (a.markingworkflow = 1 AND uf.workflowstate = :wfreleased)) AND
-                       uf.mailed = 0 AND gri.hidden = 0
+                       g.grader > 0 AND uf.mailed = 0 AND gri.hidden = 0
               ORDER BY a.course, cm.id";
 
         $params = array(
@@ -3714,7 +3715,8 @@ class assign {
                 $grade->timemodified = $grade->timecreated;
             }
             $grade->grade = -1;
-            $grade->grader = $USER->id;
+            // Do not set the grader id here as it would be the admin users which is incorrect.
+            $grade->grader = -1;
             if ($attemptnumber >= 0) {
                 $grade->attemptnumber = $attemptnumber;
             }
@@ -5100,8 +5102,10 @@ class assign {
                     $gradefordisplay = $this->display_grade($gradebookgrade->grade, false);
                 }
                 $gradeddate = $gradebookgrade->dategraded;
-                if (isset($grade->grader)) {
+                if (isset($grade->grader) && $grade->grader > 0) {
                     $grader = $DB->get_record('user', array('id' => $grade->grader));
+                } else if (isset($gradebookgrade->usermodified) && $gradebookgrade->usermodified > 0) {
+                    $grader = $DB->get_record('user', array('id' => $gradebookgrade->usermodified));
                 }
             }
 
@@ -5262,10 +5266,12 @@ class assign {
             // First lookup the grader info.
             if (isset($gradercache[$grade->grader])) {
                 $grade->grader = $gradercache[$grade->grader];
-            } else {
+            } else if ($grade->grader > 0) {
                 // Not in cache - need to load the grader record.
                 $grade->grader = $DB->get_record('user', array('id'=>$grade->grader));
-                $gradercache[$grade->grader->id] = $grade->grader;
+                if ($grade->grader) {
+                    $gradercache[$grade->grader->id] = $grade->grader;
+                }
             }
 
             // Now get the gradefordisplay.
index d4b17e9..13e0bd6 100644 (file)
@@ -3887,6 +3887,7 @@ Anchor link 2:<a title=\"bananas\" href=\"../logo-240x60.gif\">Link text</a>
         $assign->get_user_grade($student->id, true);
 
         // Set the grade to something errant.
+        // We don't set the grader here, so we expect it to be -1 as a result.
         $DB->set_field(
             'assign_grades',
             'grade',
@@ -3904,6 +3905,7 @@ Anchor link 2:<a title=\"bananas\" href=\"../logo-240x60.gif\">Link text</a>
         // Check that the gradebook was updated with the assign grade. So we can guarentee test results later on.
         $expectedgrade = $grade == -1 ? null : $grade; // Assign sends null to the gradebook for -1 grades.
         $gradegrade = grade_grade::fetch(array('userid' => $student->id, 'itemid' => $assign->get_grade_item()->id));
+        $this->assertEquals(-1, $gradegrade->usermodified);
         $this->assertEquals($expectedgrade, $gradegrade->rawgrade);
 
         // Call fix_null_grades().
@@ -3915,6 +3917,9 @@ Anchor link 2:<a title=\"bananas\" href=\"../logo-240x60.gif\">Link text</a>
 
         $gradegrade = grade_grade::fetch(array('userid' => $student->id, 'itemid' => $assign->get_grade_item()->id));
 
+        $this->assertEquals(-1, $gradegrade->usermodified);
+        $this->assertEquals($gradebookvalue, $gradegrade->finalgrade);
+
         // Check that the grade was updated in the gradebook by fix_null_grades.
         $this->assertEquals($gradebookvalue, $gradegrade->finalgrade);
     }
index 4a28c23..4101f15 100644 (file)
@@ -2680,8 +2680,8 @@ function forum_get_discussions($cm, $forumsort="", $fullpost=true, $unused=-1, $
     }
 
     $allnames = get_all_user_name_fields(true, 'u');
-    $sql = "SELECT $postdata, d.name, d.timemodified, d.usermodified, d.groupid, d.timestart, d.timeend, d.pinned, $allnames,
-                   u.email, u.picture, u.imagealt $umfields
+    $sql = "SELECT $postdata, d.name, d.timemodified, d.usermodified, d.groupid, d.timestart, d.timeend, d.pinned, p.created,
+                   $allnames, u.email, u.picture, u.imagealt $umfields
               FROM {forum_discussions} d
                    JOIN {forum_posts} p ON p.discussion = d.id
                    JOIN {user} u ON p.userid = u.id
@@ -3465,10 +3465,10 @@ function forum_print_post($post, $discussion, $forum, &$cm, $course, $ownpost=fa
     $output .= html_writer::div($postsubject, 'subject', ['role' => 'heading', 'aria-level' => '2']);
 
     if ($authorhidden) {
-        $bytext = userdate($post->modified);
+        $bytext = userdate($post->created);
     } else {
         $by = new stdClass();
-        $by->date = userdate($post->modified);
+        $by->date = userdate($post->created);
         $by->name = html_writer::link($postuser->profilelink, $postuser->fullname);
         $bytext = get_string('bynameondate', 'forum', $by);
     }
@@ -3918,7 +3918,7 @@ function forum_print_discussion_header(&$post, $forum, $group = -1, $datestring
     }
 
     echo '<td class="lastpost">';
-    $usedate = (empty($post->timemodified)) ? $post->modified : $post->timemodified;  // Just in case
+    $usedate = (empty($post->created)) ? $post->timemodified : $post->created;
     $parenturl = '';
     $usermodified = new stdClass();
     $usermodified->id = $post->usermodified;
index 3ee8253..39a6864 100644 (file)
@@ -29,7 +29,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$version  = 2018092700.01;              // YYYYMMDD      = weekly release date of this DEV branch.
+$version  = 2018092800.05;              // YYYYMMDD      = weekly release date of this DEV branch.
                                         //         RR    = release increments - 00 in DEV branches.
                                         //           .XX = incremental changes.