AutorÃa | Ultima modificación | Ver Log |
<?php// This file is part of the Zoom plugin for 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/>./*** Library of interface functions and constants for module zoom** All the core Moodle functions, neeeded to allow the module to work* integrated in Moodle should be placed here.** All the zoom specific functions, needed to implement all the module* logic, should go to locallib.php. This will help to save some memory when* Moodle is performing actions across all modules.** @package mod_zoom* @copyright 2015 UC Regents* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*//* Moodle core API *//*** Returns the information on whether the module supports a feature.** @param string $feature FEATURE_xx constant for requested feature* @return mixed true if the feature is supported, null if unknown*/function zoom_supports($feature) {// Adding support for FEATURE_MOD_PURPOSE (MDL-71457) and providing backward compatibility (pre-v4.0).if (defined('FEATURE_MOD_PURPOSE') && $feature === FEATURE_MOD_PURPOSE) {return MOD_PURPOSE_COMMUNICATION;}switch ($feature) {case FEATURE_BACKUP_MOODLE2:case FEATURE_COMPLETION_TRACKS_VIEWS:case FEATURE_GRADE_HAS_GRADE:case FEATURE_GROUPINGS:case FEATURE_GROUPMEMBERSONLY:case FEATURE_MOD_INTRO:case FEATURE_SHOW_DESCRIPTION:return true;default:return null;}}/*** Saves a new instance of the zoom object into the database.** Given an object containing all the necessary data (defined by the form in mod_form.php), this function* will create a new instance and return the id number of the new instance.** @param stdClass $zoom Submitted data from the form in mod_form.php* @param mod_zoom_mod_form|null $mform The form instance (included because the function is used as a callback)* @return int The id of the newly inserted zoom record*/function zoom_add_instance(stdClass $zoom, ?mod_zoom_mod_form $mform = null) {global $CFG, $DB;require_once($CFG->dirroot . '/mod/zoom/locallib.php');if (defined('PHPUNIT_TEST') && PHPUNIT_TEST) {$zoom->id = $DB->insert_record('zoom', $zoom);zoom_grade_item_update($zoom);zoom_calendar_item_update($zoom);return $zoom->id;}// Deals with password manager issues.$zoom->password = $zoom->meetingcode;unset($zoom->meetingcode);if (empty($zoom->requirepasscode)) {$zoom->password = '';}// Handle weekdays if weekly recurring meeting selected.if ($zoom->recurring && $zoom->recurrence_type == ZOOM_RECURRINGTYPE_WEEKLY) {$zoom->weekly_days = zoom_handle_weekly_days($zoom);}$zoom->course = (int) $zoom->course;$zoom->breakoutrooms = [];if (!empty($zoom->rooms)) {$breakoutrooms = zoom_build_instance_breakout_rooms_array_for_api($zoom);$zoom->breakoutrooms = $breakoutrooms['zoom'];}$response = zoom_webservice()->create_meeting($zoom);$zoom = populate_zoom_from_response($zoom, $response);$zoom->timemodified = time();if (!empty($zoom->schedule_for)) {// Wait until after receiving a successful response from zoom to update the host// based on the schedule_for field. Zoom handles the schedule for on their// end, but returns the host as the person who created the meeting, not the person// that it was scheduled for.$correcthostzoomuser = zoom_get_user($zoom->schedule_for);$zoom->host_id = $correcthostzoomuser->id;}if (isset($zoom->recurring) && isset($response->occurrences) && empty($response->occurrences)) {// Recurring meetings did not create any occurrencces.// This means invalid options selected.// Need to rollback created meeting.zoom_webservice()->delete_meeting($zoom->meeting_id, $zoom->webinar);$redirecturl = new moodle_url('/course/view.php', ['id' => $zoom->course]);throw new moodle_exception('erroraddinstance', 'zoom', $redirecturl->out());}$zoom->id = $DB->insert_record('zoom', $zoom);if (!empty($zoom->breakoutrooms)) {// We ignore the API response and save the local data for breakout rooms to support dynamic users and groups.zoom_insert_instance_breakout_rooms($zoom->id, $breakoutrooms['db']);}// Store tracking field data for meeting.zoom_sync_meeting_tracking_fields($zoom->id, $response->tracking_fields ?? []);zoom_calendar_item_update($zoom);zoom_grade_item_update($zoom);return $zoom->id;}/*** Updates an instance of the zoom in the database and on Zoom servers.** Given an object containing all the necessary data (defined by the form in mod_form.php), this function* will update an existing instance with new data.** @param stdClass $zoom An object from the form in mod_form.php* @param mod_zoom_mod_form|null $mform The form instance (included because the function is used as a callback)* @return boolean Success/Failure*/function zoom_update_instance(stdClass $zoom, ?mod_zoom_mod_form $mform = null) {global $CFG, $DB;require_once($CFG->dirroot . '/mod/zoom/locallib.php');// The object received from mod_form.php returns instance instead of id for some reason.if (isset($zoom->instance)) {$zoom->id = $zoom->instance;}$zoom->timemodified = time();// Deals with password manager issues.if (isset($zoom->meetingcode)) {$zoom->password = $zoom->meetingcode;unset($zoom->meetingcode);}if (property_exists($zoom, 'requirepasscode') && empty($zoom->requirepasscode)) {$zoom->password = '';}// Handle weekdays if weekly recurring meeting selected.if ($zoom->recurring && $zoom->recurrence_type == ZOOM_RECURRINGTYPE_WEEKLY) {$zoom->weekly_days = zoom_handle_weekly_days($zoom);}$DB->update_record('zoom', $zoom);$zoom->breakoutrooms = [];if (!empty($zoom->rooms)) {$breakoutrooms = zoom_build_instance_breakout_rooms_array_for_api($zoom);zoom_update_instance_breakout_rooms($zoom->id, $breakoutrooms['db']);$zoom->breakoutrooms = $breakoutrooms['zoom'];}$updatedzoomrecord = $DB->get_record('zoom', ['id' => $zoom->id]);$zoom->meeting_id = $updatedzoomrecord->meeting_id;$zoom->webinar = $updatedzoomrecord->webinar;// Update meeting on Zoom.try {zoom_webservice()->update_meeting($zoom);if (!empty($zoom->schedule_for)) {// Only update this if we actually get a valid user.if ($correcthostzoomuser = zoom_get_user($zoom->schedule_for)) {$zoom->host_id = $correcthostzoomuser->id;$DB->update_record('zoom', $zoom);}}} catch (moodle_exception $error) {return false;}// Get the updated meeting info from zoom, before updating calendar events.$response = zoom_webservice()->get_meeting_webinar_info($zoom->meeting_id, $zoom->webinar);$zoom = populate_zoom_from_response($zoom, $response);$DB->update_record('zoom', $zoom);// Update tracking field data for meeting.zoom_sync_meeting_tracking_fields($zoom->id, $response->tracking_fields ?? []);zoom_calendar_item_update($zoom);zoom_grade_item_update($zoom);return true;}/*** Function to handle selected weekdays, for recurring weekly meeting.** @param stdClass $zoom The zoom instance* @return string The comma separated string for selected weekdays*/function zoom_handle_weekly_days($zoom) {$weekdaynumbers = [];for ($i = 1; $i <= 7; $i++) {$key = 'weekly_days_' . $i;if (!empty($zoom->$key)) {$weekdaynumbers[] = $i;}}return implode(',', $weekdaynumbers);}/*** Function to unset the weekly options in postprocessing.** @param stdClass $data The form data object* @return stdClass $data The form data object minus weekly options.*/function zoom_remove_weekly_options($data) {// Unset the weekly_days options.for ($i = 1; $i <= 7; $i++) {$key = 'weekly_days_' . $i;unset($data->$key);}return $data;}/*** Function to unset the monthly options in postprocessing.** @param stdClass $data The form data object* @return stdClass $data The form data object minus monthly options.*/function zoom_remove_monthly_options($data) {// Unset the monthly options.unset($data->monthly_repeat_option);unset($data->monthly_day);unset($data->monthly_week);unset($data->monthly_week_day);return $data;}/*** Populates a zoom meeting or webinar from a response object.** Given a zoom meeting object from mod_form.php, this function uses the response to repopulate some of the object properties.** @param stdClass $zoom An object from the form in mod_form.php* @param stdClass $response A response from an API call like 'create meeting' or 'update meeting'* @return stdClass A $zoom object ready to be added to the database.*/function populate_zoom_from_response(stdClass $zoom, stdClass $response) {global $CFG;// Inlcuded for constants.require_once($CFG->dirroot . '/mod/zoom/locallib.php');$newzoom = clone $zoom;$samefields = ['join_url', 'created_at', 'timezone'];foreach ($samefields as $field) {if (isset($response->$field)) {$newzoom->$field = $response->$field;}}if (isset($response->duration)) {$newzoom->duration = $response->duration * 60;}$newzoom->meeting_id = $response->id;$newzoom->name = $response->topic;if (isset($response->start_time)) {$newzoom->start_time = strtotime($response->start_time);}$recurringtypes = [ZOOM_RECURRING_MEETING,ZOOM_RECURRING_FIXED_MEETING,ZOOM_RECURRING_WEBINAR,ZOOM_RECURRING_FIXED_WEBINAR,];$newzoom->recurring = in_array($response->type, $recurringtypes);if (!empty($response->occurrences)) {$newzoom->occurrences = [];// Normalise the occurrence times.foreach ($response->occurrences as $occurrence) {$occurrence->start_time = strtotime($occurrence->start_time);$occurrence->duration = $occurrence->duration * 60;$newzoom->occurrences[] = $occurrence;}}if (isset($response->password)) {$newzoom->password = $response->password;}if (isset($response->settings->encryption_type)) {$newzoom->option_encryption_type = $response->settings->encryption_type;}if (isset($response->settings->join_before_host)) {$newzoom->option_jbh = $response->settings->join_before_host;}if (isset($response->settings->participant_video)) {$newzoom->option_participants_video = $response->settings->participant_video;}if (isset($response->settings->alternative_hosts)) {$newzoom->alternative_hosts = $response->settings->alternative_hosts;}if (isset($response->settings->mute_upon_entry)) {$newzoom->option_mute_upon_entry = $response->settings->mute_upon_entry;}if (isset($response->settings->meeting_authentication)) {$newzoom->option_authenticated_users = $response->settings->meeting_authentication;}if (isset($response->settings->waiting_room)) {$newzoom->option_waiting_room = $response->settings->waiting_room;}if (isset($response->settings->auto_recording)) {$newzoom->option_auto_recording = $response->settings->auto_recording;}return $newzoom;}/*** Removes an instance of the zoom from the database** Given an ID of an instance of this module, this function will permanently delete the instance and any data that depends on it.** @param int $id Id of the module instance* @return boolean Success/Failure* @throws moodle_exception if failed to delete and zoom did not issue a not found error*/function zoom_delete_instance($id) {global $CFG, $DB;require_once($CFG->dirroot . '/mod/zoom/locallib.php');if (!$zoom = $DB->get_record('zoom', ['id' => $id])) {// For some reason already deleted, so let Moodle take care of the rest.return true;}// If the meeting is missing from zoom, don't bother with the webservice.if ($zoom->exists_on_zoom == ZOOM_MEETING_EXISTS) {try {zoom_webservice()->delete_meeting($zoom->meeting_id, $zoom->webinar);} catch (\mod_zoom\not_found_exception $error) {// Meeting not on Zoom, so continue.mtrace('Meeting not on Zoom; continuing');}}// If we delete a meeting instance, do we want to delete the participants?$meetinginstances = $DB->get_records('zoom_meeting_details', ['zoomid' => $zoom->id]);foreach ($meetinginstances as $meetinginstance) {$DB->delete_records('zoom_meeting_participants', ['detailsid' => $meetinginstance->id]);}$DB->delete_records('zoom_meeting_details', ['zoomid' => $zoom->id]);// Delete tracking field data for deleted meetings.$DB->delete_records('zoom_meeting_tracking_fields', ['meeting_id' => $zoom->id]);// Delete any dependent records here.zoom_calendar_item_delete($zoom);zoom_grade_item_delete($zoom);$DB->delete_records('zoom', ['id' => $zoom->id]);// Delete breakout rooms.zoom_delete_instance_breakout_rooms($zoom->id);return true;}/*** Callback function to update the Zoom event in the database and on Zoom servers.** The function is triggered when the course module name is set via quick edit.** @param int $courseid* @param stdClass $zoom Zoom Module instance object.* @param stdClass $cm Course Module object.* @return bool*/function zoom_refresh_events($courseid, $zoom, $cm) {global $CFG;require_once($CFG->dirroot . '/mod/zoom/locallib.php');try {// Get the updated meeting info from zoom, before updating calendar events.$response = zoom_webservice()->get_meeting_webinar_info($zoom->meeting_id, $zoom->webinar);$fullzoom = populate_zoom_from_response($zoom, $response);// Only if the name has changed, update meeting on Zoom.if ($zoom->name !== $fullzoom->name) {$fullzoom->name = $zoom->name;zoom_webservice()->update_meeting($zoom);}zoom_calendar_item_update($fullzoom);zoom_grade_item_update($fullzoom);} catch (moodle_exception $error) {return false;}return true;}/*** Given a course and a time, this module should find recent activity that has occurred in zoom activities and print it out.** @param stdClass $course The course record* @param bool $viewfullnames Should we display full names* @param int $timestart Print activity since this timestamp* @return boolean True if anything was printed, otherwise false*/function zoom_print_recent_activity($course, $viewfullnames, $timestart) {return false;}/*** Prepares the recent activity data** This callback function is supposed to populate the passed array with* custom activity records. These records are then rendered into HTML* zoom_print_recent_mod_activity().** Returns void, it adds items into $activities and increases $index.** @param array $activities sequentially indexed array of objects with added 'cmid' property* @param int $index the index in the $activities to use for the next record* @param int $timestart append activity since this time* @param int $courseid the id of the course we produce the report for* @param int $cmid course module id* @param int $userid check for a particular user's activity only, defaults to 0 (all users)* @param int $groupid check for a particular group's activity only, defaults to 0 (all groups)*/function zoom_get_recent_mod_activity(&$activities, &$index, $timestart, $courseid, $cmid, $userid = 0, $groupid = 0) {}/*** Prints single activity item prepared by zoom_get_recent_mod_activity()** @param stdClass $activity activity record with added 'cmid' property* @param int $courseid the id of the course we produce the report for* @param bool $detail print detailed report* @param array $modnames as returned by get_module_types_names()* @param bool $viewfullnames display users' full names*/function zoom_print_recent_mod_activity($activity, $courseid, $detail, $modnames, $viewfullnames) {}/*** Returns all other caps used in the module** For example, this could be array('moodle/site:accessallgroups') if the* module uses that capability.** @return array*/function zoom_get_extra_capabilities() {return [];}/*** Create or update Moodle calendar event of the Zoom instance.** @param stdClass $zoom*/function zoom_calendar_item_update(stdClass $zoom) {global $CFG, $DB;require_once($CFG->dirroot . '/calendar/lib.php');// Based on data passed back from zoom, create/update/delete events based on data.$newevents = [];if (!$zoom->recurring) {$newevents[''] = zoom_populate_calender_item($zoom);} else if (!empty($zoom->occurrences)) {foreach ($zoom->occurrences as $occurrence) {$uuid = $occurrence->occurrence_id;$newevents[$uuid] = zoom_populate_calender_item($zoom, $occurrence);}}// Fetch all the events related to this zoom instance.$conditions = ['modulename' => 'zoom','instance' => $zoom->id,];$events = $DB->get_records('event', $conditions);$eventfields = ['name', 'timestart', 'timeduration'];foreach ($events as $event) {$uuid = $event->uuid;if (isset($newevents[$uuid])) {// This event already exists in Moodle.$changed = false;$newevent = $newevents[$uuid];// Check if the important fields have actually changed.foreach ($eventfields as $field) {if ($newevent->$field !== $event->$field) {$changed = true;}}if ($changed) {calendar_event::load($event)->update($newevent);}// Event has been updated, remove from the list.unset($newevents[$uuid]);} else {// Event does not exist in Zoom, so delete from Moodle.calendar_event::load($event)->delete();}}// Any remaining events in the array don't exist on Moodle, so create a new event.foreach ($newevents as $uuid => $newevent) {calendar_event::create($newevent, false);}}/*** Return an array with the days of the week.** @return array*/function zoom_get_weekday_options() {return [1 => get_string('sunday', 'calendar'),2 => get_string('monday', 'calendar'),3 => get_string('tuesday', 'calendar'),4 => get_string('wednesday', 'calendar'),5 => get_string('thursday', 'calendar'),6 => get_string('friday', 'calendar'),7 => get_string('saturday', 'calendar'),];}/*** Return an array with the weeks of the month.** @return array*/function zoom_get_monthweek_options() {return [1 => get_string('weekoption_first', 'zoom'),2 => get_string('weekoption_second', 'zoom'),3 => get_string('weekoption_third', 'zoom'),4 => get_string('weekoption_fourth', 'zoom'),-1 => get_string('weekoption_last', 'zoom'),];}/*** Populate the calendar event object, based on the zoom instance** @param stdClass $zoom The zoom instance.* @param stdClass|null $occurrence The occurrence object passed from the zoom api.* @return stdClass The calendar event object.*/function zoom_populate_calender_item(stdClass $zoom, ?stdClass $occurrence = null) {$event = new stdClass();$event->type = CALENDAR_EVENT_TYPE_ACTION;$event->modulename = 'zoom';$event->eventtype = 'zoom';$event->courseid = $zoom->course;$event->instance = $zoom->id;$event->visible = true;$event->name = $zoom->name;if ($zoom->intro) {$event->description = $zoom->intro;$event->format = $zoom->introformat;}if (!$occurrence) {$event->timesort = $zoom->start_time;$event->timestart = $zoom->start_time;$event->timeduration = $zoom->duration;} else {$event->timesort = $occurrence->start_time;$event->timestart = $occurrence->start_time;$event->timeduration = $occurrence->duration;$event->uuid = $occurrence->occurrence_id;}// Recurring meetings/webinars with no fixed time are created as invisible events.// For recurring meetings/webinars with a fixed time, we want to see the events on the calendar.if ($zoom->recurring && $zoom->recurrence_type == ZOOM_RECURRINGTYPE_NOTIME) {$event->visible = false;}return $event;}/*** Delete Moodle calendar events of the Zoom instance.** @param stdClass $zoom*/function zoom_calendar_item_delete(stdClass $zoom) {global $CFG, $DB;require_once($CFG->dirroot . '/calendar/lib.php');$events = $DB->get_records('event', ['modulename' => 'zoom','instance' => $zoom->id,]);foreach ($events as $event) {calendar_event::load($event)->delete();}}/*** This function receives a calendar event and returns the action associated with it, or null if there is none.** This is used by block_myoverview in order to display the event appropriately. If null is returned then the event* is not displayed on the block.** @param calendar_event $event* @param \core_calendar\action_factory $factory* @param int $userid User id override* @return \core_calendar\local\event\entities\action_interface|null*/function mod_zoom_core_calendar_provide_event_action(calendar_event $event,\core_calendar\action_factory $factory,$userid = null) {global $CFG, $DB, $USER;require_once($CFG->dirroot . '/mod/zoom/locallib.php');if (empty($userid)) {$userid = $USER->id;}$cm = get_fast_modinfo($event->courseid, $userid)->instances['zoom'][$event->instance];$zoom = $DB->get_record('zoom', ['id' => $cm->instance], '*');[$inprogress, $available, $finished] = zoom_get_state($zoom);if ($finished) {return null; // No point to showing finished meetings in overview.} else {return $factory->create_instance(get_string('join_meeting', 'zoom'),new \moodle_url('/mod/zoom/view.php', ['id' => $cm->id]),1,$available);}}/* Gradebook API *//*** Checks if scale is being used by any instance of zoom.** This is used to find out if scale used anywhere.** @param int $scaleid ID of the scale* @return boolean true if the scale is used by any zoom instance*/function zoom_scale_used_anywhere($scaleid) {global $DB;if ($scaleid && $DB->record_exists('zoom', ['grade' => -$scaleid])) {return true;} else {return false;}}/*** Creates or updates grade item for the given zoom instance** Needed by grade_update_mod_grades().** @param stdClass $zoom instance object with extra cmidnumber and modname property* @param array $grades optional array/object of grade(s); 'reset' means reset grades in gradebook* @return void*/function zoom_grade_item_update(stdClass $zoom, $grades = null) {global $CFG;require_once($CFG->libdir . '/gradelib.php');$item = [];$item['itemname'] = clean_param($zoom->name, PARAM_NOTAGS);$item['gradetype'] = GRADE_TYPE_VALUE;if ($zoom->grade > 0) {$item['gradetype'] = GRADE_TYPE_VALUE;$item['grademax'] = $zoom->grade;$item['grademin'] = 0;} else if ($zoom->grade < 0) {$item['gradetype'] = GRADE_TYPE_SCALE;$item['scaleid'] = -$zoom->grade;} else {$gradebook = grade_get_grades($zoom->course, 'mod', 'zoom', $zoom->id);// Prevent the gradetype from switching to None if grades exist.if (empty($gradebook->items[0]->grades)) {$item['gradetype'] = GRADE_TYPE_NONE;} else {return;}}if ($grades === 'reset') {$item['reset'] = true;$grades = null;}grade_update('mod/zoom', $zoom->course, 'mod', 'zoom', $zoom->id, 0, $grades, $item);}/*** Delete grade item for given zoom instance** @param stdClass $zoom instance object* @return int*/function zoom_grade_item_delete($zoom) {global $CFG;require_once($CFG->libdir . '/gradelib.php');return grade_update('mod/zoom', $zoom->course, 'mod', 'zoom', $zoom->id, 0, null, ['deleted' => 1]);}/*** Update zoom grades in the gradebook** Needed by grade_update_mod_grades().** @param stdClass $zoom instance object with extra cmidnumber and modname property* @param int $userid update grade of specific user only, 0 means all participants*/function zoom_update_grades(stdClass $zoom, $userid = 0) {global $CFG;require_once($CFG->libdir . '/gradelib.php');// Populate array of grade objects indexed by userid.if ($zoom->grade == 0) {zoom_grade_item_update($zoom);} else if ($userid != 0) {$grade = grade_get_grades($zoom->course, 'mod', 'zoom', $zoom->id, $userid)->items[0]->grades[$userid];$grade->userid = $userid;if ($grade->grade == -1) {$grade->grade = null;}zoom_grade_item_update($zoom, $grade);} else if ($userid == 0) {$context = context_course::instance($zoom->course);$enrollusersid = array_keys(get_enrolled_users($context));$grades = grade_get_grades($zoom->course, 'mod', 'zoom', $zoom->id, $enrollusersid)->items[0]->grades;foreach ($grades as $k => $v) {$grades[$k]->userid = $k;if ($v->grade == -1) {$grades[$k]->grade = null;}}zoom_grade_item_update($zoom, $grades);} else {zoom_grade_item_update($zoom);}}/*** Removes all zoom grades from gradebook by course id** @param int $courseid*/function zoom_reset_gradebook($courseid) {global $DB;$params = [$courseid];$sql = "SELECT z.*, cm.idnumber as cmidnumber, z.course as courseidFROM {zoom} zJOIN {course_modules} cm ON cm.instance = z.idJOIN {modules} m ON m.id = cm.module AND m.name = 'zoom'WHERE z.course = ?";if ($zooms = $DB->get_records_sql($sql, $params)) {foreach ($zooms as $zoom) {zoom_grade_item_update($zoom, 'reset');}}}/*** This function is used by the reset_course_userdata function in moodlelib.* This function will remove all user data from zoom activites* and clean up any related data.** @param object $data the data submitted from the reset course.* @return array status array*/function zoom_reset_userdata($data) {global $CFG, $DB;$componentstr = get_string('modulenameplural', 'zoom');$status = [];if (!empty($data->reset_zoom_all)) {// Reset tables that record user data.$DB->delete_records_select('zoom_meeting_participants','detailsid IN (SELECT zmd.idFROM {zoom_meeting_details} zmdJOIN {zoom} z ON z.id = zmd.zoomidWHERE z.course = ?)',[$data->courseid]);$status[] = ['component' => $componentstr,'item' => get_string('meetingparticipantsdeleted', 'zoom'),'error' => false,];$DB->delete_records_select('zoom_meeting_recordings_view','recordingsid IN (SELECT zmr.idFROM {zoom_meeting_recordings} zmrJOIN {zoom} z ON z.id = zmr.zoomidWHERE z.course = ?)',[$data->courseid]);$status[] = ['component' => $componentstr,'item' => get_string('meetingrecordingviewsdeleted', 'zoom'),'error' => false,];}return $status;}/*** Called by course/reset.php** @param object $mform the course reset form that is being built.*/function zoom_reset_course_form_definition($mform) {$mform->addElement('header', 'zoomheader', get_string('modulenameplural', 'zoom'));$mform->addElement('checkbox', 'reset_zoom_all', get_string('resetzoomsall', 'zoom'));}/*** Course reset form defaults.** @param object $course data passed by the form.* @return array the defaults.*/function zoom_reset_course_form_defaults($course) {return ['reset_zoom_all' => 1];}/* File API *//*** Returns the lists of all browsable file areas within the given module context** The file area 'intro' for the activity introduction field is added automatically* by file_browser::get_file_info_context_module()** @param stdClass $course* @param stdClass $cm* @param stdClass $context* @return array of [(string)filearea] => (string)description*/function zoom_get_file_areas($course, $cm, $context) {return [];}/*** File browsing support for zoom file areas** @package mod_zoom* @category files** @param file_browser $browser* @param array $areas* @param stdClass $course* @param stdClass $cm* @param stdClass $context* @param string $filearea* @param int $itemid* @param string $filepath* @param string $filename* @return file_info instance or null if not found*/function zoom_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) {return null;}/*** Serves the files from the zoom file areas** @package mod_zoom* @category files** @param stdClass $course the course object* @param stdClass $cm the course module object* @param stdClass $context the zoom's context* @param string $filearea the name of the file area* @param array $args extra arguments (itemid, path)* @param bool $forcedownload whether or not force download* @param array $options additional options affecting the file serving*/function zoom_pluginfile($course, $cm, $context, $filearea, array $args, $forcedownload, array $options = []) {if ($context->contextlevel != CONTEXT_MODULE) {send_file_not_found();}require_login($course, true, $cm);send_file_not_found();}/* Navigation API *//*** Extends the global navigation tree by adding zoom nodes if there is a relevant content** This can be called by an AJAX request so do not rely on $PAGE as it might not be set up properly.** @param navigation_node $navref An object representing the navigation tree node of the zoom module instance* @param stdClass $course current course record* @param stdClass $module current zoom instance record* @param cm_info $cm course module information*/function zoom_extend_navigation(navigation_node $navref, stdClass $course, stdClass $module, cm_info $cm) {}/*** Extends the settings navigation with the zoom settings** This function is called when the context for the page is a zoom module. This is not called by AJAX* so it is safe to rely on the $PAGE.** @param settings_navigation $settingsnav complete settings navigation tree* @param navigation_node|null $zoomnode zoom administration node*/function zoom_extend_settings_navigation(settings_navigation $settingsnav, ?navigation_node $zoomnode = null) {}/*** Get icon mapping for font-awesome.** @see https://docs.moodle.org/dev/Moodle_icons*/function mod_zoom_get_fontawesome_icon_map() {return ['mod_zoom:i/calendar' => 'fa-calendar',];}/*** This function updates the tracking field settings in config_plugins.*/function mod_zoom_update_tracking_fields() {global $DB;try {$defaulttrackingfields = zoom_clean_tracking_fields();$zoomprops = ['id', 'field', 'required', 'visible', 'recommended_values'];$confignames = [];if (!empty($defaulttrackingfields)) {$zoomtrackingfields = zoom_list_tracking_fields();foreach ($zoomtrackingfields as $field => $zoomtrackingfield) {if (isset($defaulttrackingfields[$field])) {foreach ($zoomprops as $zoomprop) {$configname = 'tf_' . $field . '_' . $zoomprop;$confignames[] = $configname;if ($zoomprop === 'recommended_values') {$configvalue = implode(', ', $zoomtrackingfield[$zoomprop]);} else {$configvalue = $zoomtrackingfield[$zoomprop];}set_config($configname, $configvalue, 'zoom');}}}}$config = get_config('zoom');$proparray = get_object_vars($config);$properties = array_keys($proparray);$oldconfigs = array_diff($properties, $confignames);$pattern = '/^tf_(?P<oldfield>.*)_(' . implode('|', $zoomprops) . ')$/';foreach ($oldconfigs as $oldconfig) {if (preg_match($pattern, $oldconfig, $matches)) {set_config($oldconfig, null, 'zoom');$DB->delete_records('zoom_meeting_tracking_fields', ['tracking_field' => $matches['oldfield']]);}}} catch (Exception $e) {// Fail gracefully because the callback function might be called directly.return false;}return true;}/*** Insert zoom instance breakout rooms** @param int $zoomid* @param array $breakoutrooms zoom breakout rooms*/function zoom_insert_instance_breakout_rooms($zoomid, $breakoutrooms) {global $DB;foreach ($breakoutrooms as $breakoutroom) {$item = new stdClass();$item->name = $breakoutroom['name'];$item->zoomid = $zoomid;$breakoutroomid = $DB->insert_record('zoom_meeting_breakout_rooms', $item);foreach ($breakoutroom['participants'] as $participant) {$item = new stdClass();$item->userid = $participant;$item->breakoutroomid = $breakoutroomid;$DB->insert_record('zoom_breakout_participants', $item);}foreach ($breakoutroom['groups'] as $group) {$item = new stdClass();$item->groupid = $group;$item->breakoutroomid = $breakoutroomid;$DB->insert_record('zoom_breakout_groups', $item);}}}/*** Update zoom instance breakout rooms** @param int $zoomid* @param array $breakoutrooms*/function zoom_update_instance_breakout_rooms($zoomid, $breakoutrooms) {global $DB;zoom_delete_instance_breakout_rooms($zoomid);zoom_insert_instance_breakout_rooms($zoomid, $breakoutrooms);}/*** Delete zoom instance breakout rooms** @param int $zoomid*/function zoom_delete_instance_breakout_rooms($zoomid) {global $DB;$zoomcurrentbreakoutroomsids = $DB->get_fieldset_select('zoom_meeting_breakout_rooms', 'id', "zoomid = {$zoomid}");foreach ($zoomcurrentbreakoutroomsids as $id) {$DB->delete_records('zoom_breakout_participants', ['breakoutroomid' => $id]);$DB->delete_records('zoom_breakout_groups', ['breakoutroomid' => $id]);}$DB->delete_records('zoom_meeting_breakout_rooms', ['zoomid' => $zoomid]);}/*** Build zoom instance breakout rooms array for api** @param stdClass $zoom Submitted data from the form in mod_form.php.* @return array The meeting breakout rooms array.*/function zoom_build_instance_breakout_rooms_array_for_api($zoom) {$context = context_course::instance($zoom->course);$users = get_enrolled_users($context);$groups = groups_get_all_groups($zoom->course);// Building meeting breakout rooms array.$breakoutrooms = [];if (!empty($zoom->rooms)) {foreach ($zoom->rooms as $roomid => $roomname) {// Getting meeting rooms participants.$roomparticipants = [];$dbroomparticipants = [];if (!empty($zoom->roomsparticipants[$roomid])) {foreach ($zoom->roomsparticipants[$roomid] as $participantid) {if (isset($users[$participantid])) {$roomparticipants[] = $users[$participantid]->email;$dbroomparticipants[] = $participantid;}}}// Getting meeting rooms groups members.$roomgroupsmembers = [];$dbroomgroupsmembers = [];if (!empty($zoom->roomsgroups[$roomid])) {foreach ($zoom->roomsgroups[$roomid] as $groupid) {if (isset($groups[$groupid])) {$groupmembers = groups_get_members($groupid);$roomgroupsmembers[] = array_column(array_values($groupmembers), 'email');$dbroomgroupsmembers[] = $groupid;}}$roomgroupsmembers = array_merge(...$roomgroupsmembers);}$zoomdata = ['name' => $roomname,'participants' => array_values(array_unique(array_merge($roomparticipants, $roomgroupsmembers))),];$dbdata = ['name' => $roomname,'participants' => $dbroomparticipants,'groups' => $dbroomgroupsmembers,];$breakoutrooms['zoom'][] = $zoomdata;$breakoutrooms['db'][] = $dbdata;}}return $breakoutrooms;}/*** Build zoom instance breakout rooms array for view.** @param int $zoomid* @param array $courseparticipants* @param array $coursegroups* @return array The meeting breakout rooms array.*/function zoom_build_instance_breakout_rooms_array_for_view($zoomid, $courseparticipants, $coursegroups) {$breakoutrooms = zoom_get_instance_breakout_rooms($zoomid);$rooms = [];if (!empty($breakoutrooms)) {foreach ($breakoutrooms as $key => $breakoutroom) {$roomparticipants = $courseparticipants;if (!empty($breakoutroom['participants'])) {$participants = $breakoutroom['participants'];$roomparticipants = array_map(function ($roomparticipant) use ($participants) {if (isset($participants[$roomparticipant['participantid']])) {$roomparticipant['selected'] = true;}return $roomparticipant;}, $courseparticipants);}$roomgroups = $coursegroups;if (!empty($breakoutroom['groups'])) {$groups = $breakoutroom['groups'];$roomgroups = array_map(function ($roomgroup) use ($groups) {if (isset($groups[$roomgroup['groupid']])) {$roomgroup['selected'] = true;}return $roomgroup;}, $coursegroups);}$rooms[] = ['roomid' => $breakoutroom['roomid'],'roomname' => $breakoutroom['roomname'],'courseparticipants' => $roomparticipants,'coursegroups' => $roomgroups,];}$rooms[0]['roomactive'] = true;}return $rooms;}/*** Get zoom instance breakout rooms.** @param int $zoomid* @return array*/function zoom_get_instance_breakout_rooms($zoomid) {global $DB;$breakoutrooms = [];$params = [$zoomid];$sql = "SELECT id, nameFROM {zoom_meeting_breakout_rooms}WHERE zoomid = ?";$rooms = $DB->get_records_sql($sql, $params);foreach ($rooms as $room) {$breakoutrooms[$room->id] = ['roomid' => $room->id,'roomname' => $room->name,'participants' => [],'groups' => [],];// Get breakout room participants.$params = [$room->id];$sql = "SELECT useridFROM {zoom_breakout_participants}WHERE breakoutroomid = ?";$participants = $DB->get_records_sql($sql, $params);if (!empty($participants)) {foreach ($participants as $participant) {$breakoutrooms[$room->id]['participants'][$participant->userid] = $participant->userid;}}// Get breakout room groups.$sql = "SELECT groupidFROM {zoom_breakout_groups}WHERE breakoutroomid = ?";$groups = $DB->get_records_sql($sql, $params);if (!empty($groups)) {foreach ($groups as $group) {$breakoutrooms[$room->id]['groups'][$group->groupid] = $group->groupid;}}}return $breakoutrooms;}/*** Print zoom meeting date and time in the course listing page** Given a course_module object, this function returns any "extra" information that may be needed* when printing this activity in a course listing. See get_array_of_activities() in course/lib.php.** @param stdClass $coursemodule The coursemodule object* @return cached_cm_info An object on information that the courses will know about*/function zoom_get_coursemodule_info($coursemodule) {global $DB;$dbparams = ['id' => $coursemodule->instance];$fields = 'id, intro, introformat, start_time, recurring, recurrence_type, duration';if (!$zoom = $DB->get_record('zoom', $dbparams, $fields)) {return false;}$result = new cached_cm_info();if ($coursemodule->showdescription) {// Convert intro to html. Do not filter cached version, filters run at display time.$result->content = format_module_intro('zoom', $zoom, $coursemodule->id, false);}// Populate some other values that can be used in calendar or on dashboard.if ($zoom->start_time) {$result->customdata['start_time'] = $zoom->start_time;}if ($zoom->duration) {$result->customdata['duration'] = $zoom->duration;}// Skip the if condition for recurring and recurrence_type, the values of NULL and 0 are needed in other functions.$result->customdata['recurring'] = $zoom->recurring;$result->customdata['recurrence_type'] = $zoom->recurrence_type;return $result;}/*** Sets dynamic information about a course module** This function is called from cm_info when displaying the module** @param cm_info $cm*/function zoom_cm_info_dynamic(cm_info $cm) {global $CFG, $DB;require_once($CFG->dirroot . '/mod/zoom/locallib.php');if (method_exists($cm, 'override_customdata')) {$moduleinstance = $DB->get_record('zoom', ['id' => $cm->instance], '*', MUST_EXIST);// Get meeting state from Zoom.[$inprogress, $available, $finished] = zoom_get_state($moduleinstance);// For unfinished meetings, override start_time with the next occurrence.// If this is a recurring meeting without fixed time, do not override - it will set start_time = 0.if (!$finished && $moduleinstance->recurrence_type != ZOOM_RECURRINGTYPE_NOTIME) {$cm->override_customdata('start_time', zoom_get_next_occurrence($moduleinstance));}}}