MDL-65093 core_message: users can't block any user they want
authorMark Nelson <mdjnelson@gmail.com>
Thu, 4 Jul 2019 06:55:30 +0000 (14:55 +0800)
committerMark Nelson <mdjnelson@gmail.com>
Mon, 29 Jul 2019 02:39:57 +0000 (10:39 +0800)
If blocking will have no effect (ie. you are attempting to block
the admin) the 'Block' button will not be shown and instead a
message will be shown explaining why you can not block the user.

16 files changed:
lang/en/message.php
message/amd/build/message_drawer_view_conversation.min.js
message/amd/build/message_drawer_view_conversation.min.js.map
message/amd/build/message_drawer_view_conversation_renderer.min.js
message/amd/build/message_drawer_view_conversation_renderer.min.js.map
message/amd/build/message_drawer_view_conversation_state_manager.min.js
message/amd/build/message_drawer_view_conversation_state_manager.min.js.map
message/amd/src/message_drawer_view_conversation.js
message/amd/src/message_drawer_view_conversation_renderer.js
message/amd/src/message_drawer_view_conversation_state_manager.js
message/classes/api.php
message/classes/helper.php
message/externallib.php
message/tests/api_test.php
message/tests/behat/block_user.feature [new file with mode: 0644]
message/tests/externallib_test.php

index e7b4d21..21389c4 100644 (file)
@@ -39,6 +39,7 @@ $string['blockuserconfirm'] = 'Are you sure you want to block {$a}?';
 $string['blockuserconfirmbutton'] = 'Block';
 $string['blocknoncontacts'] = 'Prevent non-contacts from messaging me';
 $string['cancelselection'] = 'Cancel message selection';
+$string['cantblockuser'] = 'You are unable to block {$a} because they have a role with permission to message all users';
 $string['contactableprivacy'] = 'Accept messages from:';
 $string['contactableprivacy_onlycontacts'] = 'My contacts only';
 $string['contactableprivacy_coursemember'] = 'My contacts and anyone in my courses';
index 65d3133..aa9cb9f 100644 (file)
Binary files a/message/amd/build/message_drawer_view_conversation.min.js and b/message/amd/build/message_drawer_view_conversation.min.js differ
index 703107b..0cada90 100644 (file)
Binary files a/message/amd/build/message_drawer_view_conversation.min.js.map and b/message/amd/build/message_drawer_view_conversation.min.js.map differ
index 78699a4..b9d05dd 100644 (file)
Binary files a/message/amd/build/message_drawer_view_conversation_renderer.min.js and b/message/amd/build/message_drawer_view_conversation_renderer.min.js differ
index 3d58f3b..e174469 100644 (file)
Binary files a/message/amd/build/message_drawer_view_conversation_renderer.min.js.map and b/message/amd/build/message_drawer_view_conversation_renderer.min.js.map differ
index a2a59b9..c827c07 100644 (file)
Binary files a/message/amd/build/message_drawer_view_conversation_state_manager.min.js and b/message/amd/build/message_drawer_view_conversation_state_manager.min.js differ
index 5982194..7da84f3 100644 (file)
Binary files a/message/amd/build/message_drawer_view_conversation_state_manager.min.js.map and b/message/amd/build/message_drawer_view_conversation_state_manager.min.js.map differ
index f706343..a3cd6b7 100644 (file)
@@ -182,7 +182,8 @@ function(
             isblocked: null,
             iscontact: null,
             isdeleted: null,
-            canmessage:  null,
+            canmessage: null,
+            canmessageevenifblocked: null,
             requirescontact: null,
             contactrequests: []
         };
index b4b3226..4639d0f 100644 (file)
@@ -1069,10 +1069,17 @@ function(
      */
     var renderConfirmBlockUser = function(header, body, footer, user) {
         if (user) {
-            return Str.get_string('blockuserconfirm', 'core_message', user.fullname)
-                .then(function(string) {
-                    return showConfirmDialogue(header, body, footer, [SELECTORS.ACTION_CONFIRM_BLOCK], string, '', true, false);
-                });
+            if (user.canmessageevenifblocked) {
+                return Str.get_string('cantblockuser', 'core_message', user.fullname)
+                    .then(function(string) {
+                        return showConfirmDialogue(header, body, footer, [], string, '', true, false);
+                    });
+            } else {
+                return Str.get_string('blockuserconfirm', 'core_message', user.fullname)
+                    .then(function(string) {
+                        return showConfirmDialogue(header, body, footer, [SELECTORS.ACTION_CONFIRM_BLOCK], string, '', true, false);
+                    });
+            }
         } else {
             return hideConfirmDialogue(header, body, footer);
         }
index 61c7a28..f6b61e8 100644 (file)
@@ -88,7 +88,8 @@ define(['jquery'], function($) {
                 isblocked: member.isblocked,
                 iscontact: member.iscontact,
                 isdeleted: member.isdeleted,
-                canmessage:  member.canmessage,
+                canmessage: member.canmessage,
+                canmessageevenifblocked: member.canmessageevenifblocked,
                 requirescontact: member.requirescontact,
                 contactrequests: member.contactrequests || []
             };
index 49824d7..851e38d 100644 (file)
@@ -1869,9 +1869,11 @@ class api {
      *
      * @param int $recipientid The recipient user id.
      * @param int $senderid The sender user id.
+     * @param bool $evenifblocked This lets the user know, that even if the recipient has blocked the user
+     *        the user is still able to send a message.
      * @return bool true if user is permitted, false otherwise.
      */
-    public static function can_send_message(int $recipientid, int $senderid) : bool {
+    public static function can_send_message(int $recipientid, int $senderid, bool $evenifblocked = false) : bool {
         $systemcontext = \context_system::instance();
 
         if (!has_capability('moodle/site:sendmessage', $systemcontext, $senderid)) {
@@ -1883,7 +1885,7 @@ class api {
         }
 
         // Check if the recipient can be messaged by the sender.
-        return self::can_contact_user($recipientid, $senderid);
+        return self::can_contact_user($recipientid, $senderid, $evenifblocked);
     }
 
     /**
@@ -2966,9 +2968,11 @@ class api {
      *
      * @param int $recipientid
      * @param int $senderid
+     * @param bool $evenifblocked This lets the user know, that even if the recipient has blocked the user
+     *        the user is still able to send a message.
      * @return bool true if recipient hasn't blocked sender and sender can contact to recipient, false otherwise.
      */
-    protected static function can_contact_user(int $recipientid, int $senderid) : bool {
+    protected static function can_contact_user(int $recipientid, int $senderid, bool $evenifblocked = false) : bool {
         if (has_capability('moodle/site:messageanyuser', \context_system::instance(), $senderid) ||
             $recipientid == $senderid) {
             // The sender has the ability to contact any user across the entire site or themselves.
@@ -2978,7 +2982,7 @@ class api {
         // The initial value of $cancontact is null to indicate that a value has not been determined.
         $cancontact = null;
 
-        if (self::is_blocked($recipientid, $senderid)) {
+        if (self::is_blocked($recipientid, $senderid) || $evenifblocked) {
             // The recipient has specifically blocked this sender.
             $cancontact = false;
         }
index e1440ee..99daf90 100644 (file)
@@ -589,9 +589,14 @@ class helper {
 
             $data->requirescontact = null;
             $data->canmessage = null;
+            $data->canmessageevenifblocked = null;
             if ($includeprivacyinfo) {
                 $privacysetting = api::get_user_privacy_messaging_preference($member->id);
                 $data->requirescontact = $privacysetting == api::MESSAGE_PRIVACY_ONLYCONTACTS;
+
+                // Here we check that if the sender wanted to block the recipient, the recipient would
+                // still be able to message them regardless.
+                $data->canmessageevenifblocked = api::can_send_message($referenceuserid, $member->id, true);
                 $data->canmessage = !$data->isdeleted && api::can_send_message($member->id, $referenceuserid);
             }
 
index 6933c20..3fbff4d 100644 (file)
@@ -583,6 +583,11 @@ class core_message_external extends external_api {
             throw new required_capability_exception($context, $capability, 'nopermissions', '');
         }
 
+        // If the blocking is going to be useless then don't do it.
+        if (\core_message\api::can_send_message($userid, $blockeduserid, true)) {
+            return [];
+        }
+
         if (!\core_message\api::is_blocked($params['userid'], $params['blockeduserid'])) {
             \core_message\api::block_user($params['userid'], $params['blockeduserid']);
         }
@@ -1290,6 +1295,8 @@ class core_message_external extends external_api {
             'isblocked' => new external_value(PARAM_BOOL, 'If the user has been blocked'),
             'iscontact' => new external_value(PARAM_BOOL, 'Is the user a contact?'),
             'isdeleted' => new external_value(PARAM_BOOL, 'Is the user deleted?'),
+            'canmessageevenifblocked' => new external_value(PARAM_BOOL,
+                'If the user can still message even if they get blocked'),
             'canmessage' => new external_value(PARAM_BOOL, 'If the user can be messaged'),
             'requirescontact' => new external_value(PARAM_BOOL, 'If the user requires to be contacts'),
         ];
index ad5d59e..3fe5c33 100644 (file)
@@ -3613,6 +3613,98 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
         $this->assertTrue(\core_message\api::can_send_message($student2->id, $teacher1->id));
     }
 
+    /**
+     * Tests the user when blocked will not be able to send messages if they are blocked.
+     */
+    public function test_can_send_message_even_if_blocked() {
+        $this->resetAfterTest();
+
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        $this->assertFalse(\core_message\api::can_send_message($user2->id, $user1->id, true));
+    }
+
+    /**
+     * Tests the user will be able to send a message even if they are blocked as the user
+     * has the capability 'moodle/site:messageanyuser'.
+     */
+    public function test_can_send_message_even_if_blocked_with_message_any_user_cap() {
+        global $DB;
+
+        $this->resetAfterTest();
+
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        $authenticateduserrole = $DB->get_record('role', array('shortname' => 'user'));
+        assign_capability('moodle/site:messageanyuser', CAP_ALLOW, $authenticateduserrole->id, context_system::instance(), true);
+
+        $this->assertTrue(\core_message\api::can_send_message($user2->id, $user1->id, true));
+    }
+
+    /**
+     * Tests the user will be able to send a message even if they are blocked as the user
+     * has the capability 'moodle/site:readallmessages'.
+     */
+    public function test_can_send_message_even_if_blocked_with_read_all_message_cap() {
+        global $DB;
+
+        $this->resetAfterTest();
+
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        $authenticateduserrole = $DB->get_record('role', array('shortname' => 'user'));
+        assign_capability('moodle/site:readallmessages', CAP_ALLOW, $authenticateduserrole->id, context_system::instance(), true);
+
+        $this->assertTrue(\core_message\api::can_send_message($user2->id, $user1->id, true));
+    }
+
+    /**
+     * Tests the user can not always send a message if they are blocked just because they share a course.
+     */
+    public function test_can_send_message_even_if_blocked_shared_course() {
+        $this->resetAfterTest();
+
+        // Create some users.
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        $course = self::getDataGenerator()->create_course();
+
+        $this->getDataGenerator()->enrol_user($user1->id, $course->id);
+        $this->getDataGenerator()->enrol_user($user2->id, $course->id);
+
+        $this->assertFalse(\core_message\api::can_send_message($user2->id, $user1->id, true));
+    }
+
+    /**
+     * Tests the user can always send a message even if they are blocked because they share a course and
+     * have the capability 'moodle/site:messageanyuser' at the course context.
+     */
+    public function test_can_send_message_even_if_blocked_shared_course_with_message_any_user_cap() {
+        global $DB;
+
+        $this->resetAfterTest();
+
+        $editingteacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
+
+        $teacher = self::getDataGenerator()->create_user();
+        $student = self::getDataGenerator()->create_user();
+
+        $course = self::getDataGenerator()->create_course();
+
+        $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $editingteacherrole->id);
+        $this->getDataGenerator()->enrol_user($student->id, $course->id);
+
+        assign_capability('moodle/site:messageanyuser', CAP_ALLOW, $editingteacherrole->id,
+            context_course::instance($course->id), true);
+
+        // Check that the second user can no longer send the first user a message.
+        $this->assertTrue(\core_message\api::can_send_message($student->id, $teacher->id, true));
+    }
+
     /**
      * Tests the user can post a message.
      */
diff --git a/message/tests/behat/block_user.feature b/message/tests/behat/block_user.feature
new file mode 100644 (file)
index 0000000..02fd075
--- /dev/null
@@ -0,0 +1,43 @@
+@core @core_message @javascript
+Feature: To be able to block users that we are able to or to see a message if we can not
+  In order to attempt to block a user
+  As a user
+  I need to be able to block a user or to see a message if we can not
+
+  Background:
+    Given the following "courses" exist:
+      | fullname | shortname | category |
+      | Course 1 | C1        | 0        |
+    And the following "users" exist:
+      | username | firstname | lastname | email                |
+      | teacher1 | Teacher   | 1        | teacher1@emample.com |
+      | student1 | Student   | 1        | student1@example.com |
+      | student2 | Student   | 2        | student2@example.com |
+    And the following "course enrolments" exist:
+      | user     | course | role    |
+      | teacher1 | C1     | teacher |
+      | student1 | C1     | student |
+      | student2 | C1     | student |
+    And the following config values are set as admin:
+      | messaging | 1 |
+
+  Scenario: Block a user
+    Given I log in as "student1"
+    And I select "Student 2" user in messaging
+    And I open contact menu
+    And I click on "Block" "link" in the "[data-region='header-container']" "css_element"
+    And I should see "Are you sure you want to block Student 2?"
+    And I click on "Block" "button" in the "[data-region='confirm-dialogue']" "css_element"
+    And I should see "You have blocked this user."
+    And I log out
+    When I log in as "student2"
+    And I open messaging
+    And I select "Student 1" user in messaging
+    Then I should see "You are unable to message this user"
+
+  Scenario: Unable to block a user
+    Given I log in as "student1"
+    And I select "Teacher 1" user in messaging
+    And I open contact menu
+    When I click on "Block" "link" in the "[data-region='header-container']" "css_element"
+    Then I should see "You are unable to block Teacher 1"
index 207eea7..224a587 100644 (file)
@@ -1216,6 +1216,30 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
         $this->assertEquals(1, $DB->count_records('message_users_blocked'));
     }
 
+    /**
+     * Test blocking a user.
+     */
+    public function test_block_user_when_ineffective() {
+        global $DB;
+
+        $this->resetAfterTest(true);
+
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        $this->setUser($user1);
+
+        $authenticateduser = $DB->get_record('role', array('shortname' => 'user'));
+        assign_capability('moodle/site:messageanyuser', CAP_ALLOW, $authenticateduser->id, context_system::instance(), true);
+
+        // 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);
+
+        $this->assertEquals(0, $DB->count_records('message_users_blocked'));
+    }
+
     /**
      * Test blocking a user with messaging disabled.
      */