Rev 1 | Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
<?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/>.namespace core_communication;use context;use stdClass;/*** Helper method for communication.** @package core_communication* @copyright 2023 Safat Shahin <safat.shahin@moodle.com>* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/class helper {/*** Load the communication instance for group id.** @param int $groupid The group id* @param context $context The context, to make sure any instance using group can load the communication instance* @return api The communication instance.*/public static function load_by_group(int $groupid, context $context): api {return \core_communication\api::load_by_instance(context: $context,component: constants::GROUP_COMMUNICATION_COMPONENT,instancetype: constants::GROUP_COMMUNICATION_INSTANCETYPE,instanceid: $groupid,);}/*** Load the communication instance for course id.** @param int $courseid The course id* @param \context $context The context* @param string|null $provider The provider name* @return api The communication instance*/public static function load_by_course(int $courseid,\context $context,?string $provider = null,): api {return \core_communication\api::load_by_instance(context: $context,component: constants::COURSE_COMMUNICATION_COMPONENT,instancetype: constants::COURSE_COMMUNICATION_INSTANCETYPE,instanceid: $courseid,provider: $provider,);}/*** Communication api call to create room for a group if course has group mode enabled.** @param int $courseid The course id.* @return stdClass*/public static function get_course(int $courseid): stdClass {global $DB;return $DB->get_record(table: 'course',conditions: ['id' => $courseid],strictness: MUST_EXIST,);}/*** Is group mode enabled for the course.** @param stdClass $course The course object*/public static function is_group_mode_enabled_for_course(stdClass $course): bool {// If the communication subsystem is not enabled then just ignore.if (!api::is_available()) {return false;}$groupmode = $course->groupmode ?? get_course(courseid: $course->id)->groupmode;return (int)$groupmode !== NOGROUPS;}/*** Helper to update room membership according to action passed.* This method will help reduce a large amount of duplications of code in different places in core.** @param \stdClass $course The course object.* @param array $userids The user ids to add to the communication room.* @param string $memberaction The action to perform on the communication room.*/public static function update_course_communication_room_membership(\stdClass $course,array $userids,string $memberaction,): void {// If the communication subsystem is not enabled then just ignore.if (!api::is_available()) {return;}// Validate communication api action.$roomuserprovider = new \ReflectionClass(room_user_provider::class);if (!$roomuserprovider->hasMethod($memberaction)) {throw new \coding_exception('Invalid action provided.');}$coursecontext = \context_course::instance(courseid: $course->id);$communication = self::load_by_course(courseid: $course->id,context: $coursecontext,);// Check we have communication correctly set up before proceeding.if ($communication->get_processor() === null) {return;}// Get the group mode for this course.$groupmode = $course->groupmode ?? get_course(courseid: $course->id)->groupmode;if ((int)$groupmode === NOGROUPS) {// If group mode is not set, then just handle the update normally for these users.$communication->$memberaction($userids);} else {// If group mode is set, then handle the update for these users with repect to the group they are in.$coursegroups = groups_get_all_groups(courseid: $course->id);$usershandled = [];// Filter all the users that have the capability to access all groups.$allaccessgroupusers = self::get_users_has_access_to_all_groups(userids: $userids,courseid: $course->id,);foreach ($coursegroups as $coursegroup) {// Get all group members.$groupmembers = groups_get_members(groupid: $coursegroup->id, fields: 'u.id');$groupmembers = array_column($groupmembers, 'id');// Find the common user ids between the group members and incoming userids.$groupuserstohandle = array_intersect($groupmembers,$userids,);// Add users who have the capability to access this group (and haven't been added already).foreach ($allaccessgroupusers as $allaccessgroupuser) {if (!in_array($allaccessgroupuser, $groupuserstohandle, true)) {$groupuserstohandle[] = $allaccessgroupuser;}}// Keep track of the users we have handled already.$usershandled = array_merge($usershandled, $groupuserstohandle);// Let's check if we need to add/remove members from room because of a role change.// First, get all the instance users for this group.$communication = self::load_by_group(groupid: $coursegroup->id,context: $coursecontext,);$instanceusers = $communication->get_processor()->get_all_userids_for_instance();// The difference between the instance users and the group members are the ones we want to check.$roomuserstocheck = array_diff($instanceusers,$groupmembers);if (!empty($roomuserstocheck)) {// Check if they still have the capability to keep their access in the room.$userslostcaps = array_diff($roomuserstocheck,self::get_users_has_access_to_all_groups(userids: $roomuserstocheck,courseid: $course->id,),);// Remove users who no longer have the capability.if (!empty($userslostcaps)) {$communication->remove_members_from_room(userids: $userslostcaps);}}// Check if we have to add any room members who have gained the capability.$usersgainedcaps = array_diff($allaccessgroupusers,$instanceusers,);// If we have users, add them to the room.if (!empty($usersgainedcaps)) {$communication->add_members_to_room(userids: $usersgainedcaps);}// Finally, trigger the update task for the users who need to be handled.$communication->$memberaction($groupuserstohandle);}// If the user was not in any group, but an update/remove action was requested for the user,// then the user must have had a role with the capablity, but made a regular user.$usersnothandled = array_diff($userids, $usershandled);// These users are not handled and not in any group, so logically these users lost their permission to stay in the room.foreach ($coursegroups as $coursegroup) {$communication = self::load_by_group(groupid: $coursegroup->id,context: $coursecontext,);$communication->remove_members_from_room(userids: $usersnothandled);}}}/*** Get users with the capability to access all groups.** @param array $userids user ids to check the permission* @param int $courseid course id* @return array of userids*/public static function get_users_has_access_to_all_groups(array $userids,int $courseid): array {$allgroupsusers = [];$context = \context_course::instance(courseid: $courseid);foreach ($userids as $userid) {if (has_capability(capability: 'moodle/site:accessallgroups',context: $context,user: $userid,)) {$allgroupsusers[] = $userid;}}return $allgroupsusers;}/*** Get the course communication url according to course setup.** @param stdClass $course The course object.* @return string The communication room url.*/public static function get_course_communication_url(stdClass $course): string {// If it's called from site context, then just return.if ($course->id === SITEID) {return '';}// If the communication subsystem is not enabled then just ignore.if (!api::is_available()) {return '';}$url = '';// Get the group mode for this course.$groupmode = $course->groupmode ?? get_course(courseid: $course->id)->groupmode;$coursecontext = \context_course::instance(courseid: $course->id);// If group mode is not set then just handle the course communication for these users.if ((int)$groupmode === NOGROUPS) {$communication = self::load_by_course(courseid: $course->id,context: $coursecontext,);$url = $communication->get_communication_room_url();} else {// If group mode is set then handle the group communication rooms for these users.$coursegroups = groups_get_all_groups(courseid: $course->id);$numberofgroups = count($coursegroups);// If no groups available, nothing to show.if ($numberofgroups === 0) {return '';}$readygroups = [];foreach ($coursegroups as $coursegroup) {$communication = self::load_by_group(groupid: $coursegroup->id,context: $coursecontext,);$roomstatus = $communication->get_communication_room_url() ? 'ready' : 'pending';if ($roomstatus === 'ready') {$readygroups[$communication->get_processor()->get_id()] = $communication->get_communication_room_url();}}if (!empty($readygroups)) {$highestkey = max(array_keys($readygroups));$url = $readygroups[$highestkey];}}return empty($url) ? '' : $url;}/*** Get the enrolled users for course.** @param stdClass $course The course object.* @param bool $onlyactive Only enrolments that are active (e.g. not suspended).* @return array*/public static function get_enrolled_users_for_course(stdClass $course, bool $onlyactive = true): array {global $CFG;require_once($CFG->libdir . '/enrollib.php');return array_column(enrol_get_course_users(courseid: $course->id, onlyactive: $onlyactive),'id',);}/*** Get the course communication status notification for course.** @param \stdClass $course The course object.*/public static function get_course_communication_status_notification(\stdClass $course): void {// If the communication subsystem is not enabled then just ignore.if (!api::is_available()) {return;}// Get the group mode for this course.$groupmode = $course->groupmode ?? get_course(courseid: $course->id)->groupmode;$coursecontext = \context_course::instance(courseid: $course->id);// If group mode is not set then just handle the course communication for these users.if ((int)$groupmode === NOGROUPS) {$communication = self::load_by_course(courseid: $course->id,context: $coursecontext,);$communication->show_communication_room_status_notification();} else {// If group mode is set then handle the group communication rooms for these users.$coursegroups = groups_get_all_groups(courseid: $course->id);$numberofgroups = count($coursegroups);// If no groups available, nothing to show.if ($numberofgroups === 0) {return;}$numberofreadygroups = 0;foreach ($coursegroups as $coursegroup) {$communication = self::load_by_group(groupid: $coursegroup->id,context: $coursecontext,);$roomstatus = $communication->get_communication_room_url() ? 'ready' : 'pending';switch ($roomstatus) {case 'ready':$numberofreadygroups ++;break;case 'pending':$pendincommunicationobject = $communication;break;}}if ($numberofgroups === $numberofreadygroups) {$communication->show_communication_room_status_notification();} else {$pendincommunicationobject->show_communication_room_status_notification();}}}/*** Update course communication according to course data.* Course can have course or group rooms. Group mode enabling will create rooms for groups.** @param stdClass $course The course data* @param bool $changesincoursecat Whether the course moved to a different category*/public static function update_course_communication_instance(stdClass $course,bool $changesincoursecat): void {// If the communication subsystem is not enabled then just ignore.if (!api::is_available()) {return;}// Check if provider is selected.$provider = $course->selectedcommunication ?? null;// If the course moved to hidden category, set provider to none.if ($changesincoursecat && empty($course->visible)) {$provider = processor::PROVIDER_NONE;}// Get the course context.$coursecontext = \context_course::instance(courseid: $course->id);// Get the course image.$courseimage = course_get_courseimage(course: $course);// Get the course communication instance.$coursecommunication = self::load_by_course(courseid: $course->id,context: $coursecontext,);// Attempt to get the communication provider if it wasn't provided in the data.if (empty($provider)) {$provider = $coursecommunication->get_provider();}$roomnameidenfier = $provider . 'roomname';// Determine the communication room name if none was provided and add it to the course data.if (empty($course->$roomnameidenfier)) {$course->$roomnameidenfier = $coursecommunication->get_room_name();if (empty($course->$roomnameidenfier)) {$course->$roomnameidenfier = $course->fullname ?? get_course($course->id)->fullname;}}// List of enrolled users for course communication.$enrolledusers = self::get_enrolled_users_for_course(course: $course);// Check for group mode, we will have to get the course data again as the group info is not always in the object.$groupmode = $course->groupmode ?? get_course(courseid: $course->id)->groupmode;// If group mode is disabled, get the communication information for creating room for a course.if ((int)$groupmode === NOGROUPS) {// Remove all the members from active group rooms if there is any.$coursegroups = groups_get_all_groups(courseid: $course->id);foreach ($coursegroups as $coursegroup) {$communication = self::load_by_group(groupid: $coursegroup->id,context: $coursecontext,);// Remove the members from the group room.$communication->remove_all_members_from_room();// Now delete the group room.$communication->update_room(active: processor::PROVIDER_INACTIVE);}// Now create/update the course room.$communication = self::load_by_course(courseid: $course->id,context: $coursecontext,);$communication->configure_room_and_membership_by_provider(provider: $provider,instance: $course,communicationroomname: $course->$roomnameidenfier,users: $enrolledusers,instanceimage: $courseimage,);} else {// Update the group communication instances.self::update_group_communication_instances_for_course(course: $course,provider: $provider,);// Remove all the members for the course room if instance available.$communication = self::load_by_course(courseid: $course->id,context: $coursecontext,);// Now handle the course communication according to the provider.$communication->configure_room_and_membership_by_provider(provider: $provider,instance: $course,communicationroomname: $course->$roomnameidenfier,users: $enrolledusers,instanceimage: $courseimage,queue: false,);// As the course is in group mode, make sure no users are in the course room.$communication->reload();$communication->remove_all_members_from_room();}}/*** Update the group communication instances.** @param stdClass $course The course object.* @param string $provider The provider name.*/public static function update_group_communication_instances_for_course(stdClass $course,string $provider,): void {$coursegroups = groups_get_all_groups(courseid: $course->id);$coursecontext = \context_course::instance(courseid: $course->id);$allaccessgroupusers = self::get_users_has_access_to_all_groups(userids: self::get_enrolled_users_for_course(course: $course),courseid: $course->id,);foreach ($coursegroups as $coursegroup) {$groupusers = array_column(groups_get_members(groupid: $coursegroup->id),'id',);// Filter out users who are not active in this course.$enrolledusers = self::get_enrolled_users_for_course(course: $course);$groupuserstoadd = array_intersect($groupusers, $enrolledusers);foreach ($allaccessgroupusers as $allaccessgroupuser) {if (!in_array($allaccessgroupuser, $groupuserstoadd, true)) {$groupuserstoadd[] = $allaccessgroupuser;}}// Now create/update the group room.$communication = self::load_by_group(groupid: $coursegroup->id,context: $coursecontext,);$roomnameidenfier = $provider . 'roomname';$communicationroomname = self::format_group_room_name(baseroomname: $course->$roomnameidenfier,groupname: $coursegroup->name,);$communication->configure_room_and_membership_by_provider(provider: $provider,instance: $course,communicationroomname: $communicationroomname,users: $groupuserstoadd,);}}/*** Format a group communication room name with the following syntax: 'Group A (Course 1)'.** @param string $baseroomname The base room name.* @param string $groupname The group name.*/public static function format_group_room_name(string $baseroomname,string $groupname): string {return get_string('communicationgrouproomnameformat', 'core_communication', ['groupname' => $groupname,'baseroomname' => $baseroomname,]);}}