'userid' => $userid]);
}
+ /**
+ * Checks if the sender can message the recipient.
+ *
+ * @param \stdClass $recipient The user object.
+ * @param \stdClass $sender The user object.
+ * @return bool true if recipient hasn't blocked sender and sender can contact to recipient, false otherwise.
+ */
+ protected static function can_contact_user(\stdClass $recipient, \stdClass $sender) : bool {
+ if (has_capability('moodle/site:messageanyuser', \context_system::instance(), $sender->id)) {
+ // The sender has the ability to contact any user across the entire site.
+ return true;
+ }
+
+ // The initial value of $cancontact is null to indicate that a value has not been determined.
+ $cancontact = null;
+
+ if (self::is_blocked($recipient->id, $sender->id)) {
+ // The recipient has specifically blocked this sender.
+ $cancontact = false;
+ }
+
+ $sharedcourses = null;
+ if (null === $cancontact) {
+ // There are three user preference options:
+ // - Site: Allow anyone not explicitly blocked to contact me;
+ // - Course members: Allow anyone I am in a course with to contact me; and
+ // - Contacts: Only allow my contacts to contact me.
+ //
+ // The Site option is only possible when the messagingallusers site setting is also enabled.
+
+ $privacypreference = self::get_user_privacy_messaging_preference($recipient->id);
+ if (self::MESSAGE_PRIVACY_SITE === $privacypreference) {
+ // The user preference is to allow any user to contact them.
+ // No need to check anything else.
+ $cancontact = true;
+ } else {
+ // This user only allows their own contacts, and possibly course peers, to contact them.
+ // If the users are contacts then we can avoid the more expensive shared courses check.
+ $cancontact = self::is_contact($sender->id, $recipient->id);
+
+ if (!$cancontact && self::MESSAGE_PRIVACY_COURSEMEMBER === $privacypreference) {
+ // The users are not contacts and the user allows course member messaging.
+ // Check whether these two users share any course together.
+ $sharedcourses = enrol_get_shared_courses($recipient->id, $sender->id, true);
+ $cancontact = (!empty($sharedcourses));
+ }
+ }
+ }
+
+ if (false === $cancontact) {
+ // At the moment the users cannot contact one another.
+ // Check whether the messageanyuser capability applies in any of the shared courses.
+ // This is intended to allow teachers to message students regardless of message settings.
+
+ // Note: You cannot use empty($sharedcourses) here because this may be an empty array.
+ if (null === $sharedcourses) {
+ $sharedcourses = enrol_get_shared_courses($recipient->id, $sender->id, true);
+ }
+
+ foreach ($sharedcourses as $course) {
+ // Note: enrol_get_shared_courses will preload any shared context.
+ if (has_capability('moodle/site:messageanyuser', \context_course::instance($course->id), $sender->id)) {
+ $cancontact = true;
+ break;
+ }
+ }
+ }
+
+ return $cancontact;
+ }
++
+ /**
+ * Add some new members to an existing conversation.
+ *
+ * @param array $userids User ids array to add as members.
+ * @param int $convid The conversation id. Must exists.
+ * @throws \dml_missing_record_exception If convid conversation doesn't exist
+ * @throws \dml_exception If there is a database error
+ * @throws \moodle_exception If trying to add a member(s) to a non-group conversation
+ */
+ public static function add_members_to_conversation(array $userids, int $convid) {
+ global $DB;
+
+ $conversation = $DB->get_record('message_conversations', ['id' => $convid], '*', MUST_EXIST);
+
+ // We can only add members to a group conversation.
+ if ($conversation->type != self::MESSAGE_CONVERSATION_TYPE_GROUP) {
+ throw new \moodle_exception('You can not add members to a non-group conversation.');
+ }
+
+ // Be sure we are not trying to add a non existing user to the conversation. Work only with existing users.
+ list($useridcondition, $params) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
+ $existingusers = array_keys($DB->get_records_select_menu('user',
+ "id $useridcondition", $params, 'id', 'id, id'));
+
+ // Be sure we are not adding a user is already member of the conversation. Take all the members.
+ $memberuserids = array_values($DB->get_records_menu(
+ 'message_conversation_members', ['conversationid' => $convid], 'id', 'id, userid')
+ );
+
+ // Work with existing new members.
+ $members = array();
+ $newuserids = array_diff($existingusers, $memberuserids);
+ foreach ($newuserids as $userid) {
+ $member = new \stdClass();
+ $member->conversationid = $convid;
+ $member->userid = $userid;
+ $member->timecreated = time();
+ $members[] = $member;
+ }
+
+ $DB->insert_records('message_conversation_members', $members);
+ }
+
+ /**
+ * Remove some members from an existing conversation.
+ *
+ * @param array $userids The user ids to remove from conversation members.
+ * @param int $convid The conversation id. Must exists.
+ * @throws \dml_exception
+ * @throws \moodle_exception If trying to remove a member(s) from a non-group conversation
+ */
+ public static function remove_members_from_conversation(array $userids, int $convid) {
+ global $DB;
+
+ $conversation = $DB->get_record('message_conversations', ['id' => $convid], '*', MUST_EXIST);
+
+ if ($conversation->type != self::MESSAGE_CONVERSATION_TYPE_GROUP) {
+ throw new \moodle_exception('You can not remove members from a non-group conversation.');
+ }
+
+ list($useridcondition, $params) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
+ $params['convid'] = $convid;
+
+ $DB->delete_records_select('message_conversation_members',
+ "conversationid = :convid AND userid $useridcondition", $params);
+ }
+
+ /**
+ * Count conversation members.
+ *
+ * @param int $convid The conversation id.
+ * @return int Number of conversation members.
+ * @throws \dml_exception
+ */
+ public static function count_conversation_members(int $convid) : int {
+ global $DB;
+
+ return $DB->count_records('message_conversation_members', ['conversationid' => $convid]);
+ }
}