diff --git a/README.md b/README.md index 325e20ea..4f4ff3c7 100644 --- a/README.md +++ b/README.md @@ -53,13 +53,26 @@ server is properly synchronized with the time servers. ## Changelog +v5.1.0 + +- Feature: Show activity date/time directly on course page #509 (thanks @cdipe) +- Regression: Auto recording was forced off by default #505 (thanks @emmarichardson) + - Introduced in v4.7.0 when adding automatic recording settings. +- Bugfix: Validate meeting name length using Zoom's 200 character limit #512 (thanks @lcollong) +- Bugfix: Resolve database inconsistencies #505 (thanks @fabianbatioja, @foxlapinou) +- Bugfix: Skip grading/completion during pre-registration #507 (thanks @tbeachy) +- Bugfix: Correct error message handling #503 (thanks @jwalits) +- Bugfix: Provide prescribed Promise parameters #499 (thanks @fmido88) + v5.0.0 + - Backward incompatible: Drop support for JWT authentication (thanks @aspark21) - Zoom requires everyone to use Server-to-Server OAuth by September 1, 2023 - Backward incompatible: Require PHP 7.1+ (Moodle 3.7+) (thanks @rlaneIT) - Backward incompatible: Drop Moodle 3.4 mobile support v4.10.3 + - Bugfix: Also use proxy settings for OAuth token request #494 (thanks @adnbes) - Bugfix: Clean up exception handling to avoid notice #482 (thanks @andremenrath) - Bugfix: Avoid course/activity completion form overhead #481 (thanks @phette23) diff --git a/backup/moodle2/restore_zoom_stepslib.php b/backup/moodle2/restore_zoom_stepslib.php index ae86ad5f..58936744 100755 --- a/backup/moodle2/restore_zoom_stepslib.php +++ b/backup/moodle2/restore_zoom_stepslib.php @@ -68,7 +68,6 @@ protected function process_zoom($data) { $data->join_url = ''; $data->meeting_id = 0; $data->exists_on_zoom = ZOOM_MEETING_EXPIRED; - $data->option_auto_recording = 'none'; } $data->course = $this->get_courseid(); diff --git a/classes/dates.php b/classes/dates.php new file mode 100644 index 00000000..4c3edceb --- /dev/null +++ b/classes/dates.php @@ -0,0 +1,71 @@ +. + +/** + * Contains the class for fetching the important dates in mod_zoom for a given module instance and a user. + * + * @package mod_zoom + * @copyright 2021 Shamim Rezaie + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +declare(strict_types=1); + +namespace mod_zoom; + +use core\activity_dates; + +/** + * Class for fetching the important dates in mod_zoom for a given module instance and a user. + * + * @copyright 2021 Shamim Rezaie + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class dates extends activity_dates { + /** + * Returns a list of important dates in mod_zoom + * + * @return array + */ + protected function get_dates(): array { + $starttime = $this->cm->customdata['start_time'] ?? null; + $duration = $this->cm->customdata['duration'] ?? null; + $now = time(); + $dates = []; + + if ($starttime) { + if ($duration && $starttime + $duration < $now) { + // Meeting has ended. + $dataid = 'end_date_time'; + $labelid = 'activitydate:ended'; + $meetimgtimestamp = $starttime + $duration; + } else { + // Meeting hasn't started / in progress, or without fixed time (doesn't have an end date or time). + $dataid = 'start_time'; + $labelid = $starttime > $now ? 'activitydate:starts' : 'activitydate:started'; + $meetimgtimestamp = $starttime; + } + + $dates[] = [ + 'dataid' => $dataid, + 'label' => get_string($labelid, 'mod_zoom'), + 'timestamp' => $meetimgtimestamp, + ]; + } + + return $dates; + } +} diff --git a/db/install.xml b/db/install.xml index 28bf9acf..c12120c7 100755 --- a/db/install.xml +++ b/db/install.xml @@ -1,7 +1,7 @@ - @@ -20,16 +20,16 @@ - - - - - - - - - - + + + + + + + + + + @@ -45,12 +45,12 @@ - - - - - - + + + + + + @@ -125,8 +125,8 @@ - - + + diff --git a/db/upgrade.php b/db/upgrade.php index a4e8aec8..d62d95bb 100755 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -735,19 +735,20 @@ function xmldb_zoom_upgrade($oldversion) { $table = new xmldb_table('zoom'); // Define and conditionally add field show_schedule. - $field = new xmldb_field('show_schedule', XMLDB_TYPE_INTEGER, '1', null, null, null, '1', 'recordings_visible_default'); + $field = new xmldb_field('show_schedule', XMLDB_TYPE_INTEGER, '1', + null, XMLDB_NOTNULL, null, '1', 'recordings_visible_default'); if (!$dbman->field_exists($table, $field)) { $dbman->add_field($table, $field); } // Define and conditionally add field show_security. - $field = new xmldb_field('show_security', XMLDB_TYPE_INTEGER, '1', null, null, null, '1', 'show_schedule'); + $field = new xmldb_field('show_security', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '1', 'show_schedule'); if (!$dbman->field_exists($table, $field)) { $dbman->add_field($table, $field); } // Define and conditionally add field show_media. - $field = new xmldb_field('show_media', XMLDB_TYPE_INTEGER, '1', null, null, null, '1', 'show_security'); + $field = new xmldb_field('show_media', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '1', 'show_security'); if (!$dbman->field_exists($table, $field)) { $dbman->add_field($table, $field); } @@ -830,7 +831,7 @@ function xmldb_zoom_upgrade($oldversion) { $table = new xmldb_table('zoom'); // Define and conditionally add field registration. - $field = new xmldb_field('registration', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '2', 'option_auto_recording'); + $field = new xmldb_field('registration', XMLDB_TYPE_INTEGER, '1', null, null, null, null, 'option_auto_recording'); if (!$dbman->field_exists($table, $field)) { $dbman->add_field($table, $field); } @@ -839,5 +840,29 @@ function xmldb_zoom_upgrade($oldversion) { upgrade_mod_savepoint(true, 2022102700, 'zoom'); } + if ($oldversion < 2023080202) { + // Issue #432: Inconsistency between the DB and schema, this is to verify everything matches. + // Verify show_schedule, show_security, and show_media are all set to NOTNULL. + // Verify option_auto_record is set to NOTNULL and defaults to "none". + $table = new xmldb_table('zoom'); + + // Launch change of nullability for show schedule. + $field = new xmldb_field('show_schedule', XMLDB_TYPE_INTEGER, '1', + null, XMLDB_NOTNULL, null, '1', 'recordings_visible_default'); + $dbman->change_field_notnull($table, $field); + + $field = new xmldb_field('show_security', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '1', 'show_schedule'); + $dbman->change_field_notnull($table, $field); + + $field = new xmldb_field('show_media', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '1', 'show_security'); + $dbman->change_field_notnull($table, $field); + + $field = new xmldb_field('option_auto_recording', XMLDB_TYPE_CHAR, '5', null, null, null, null, 'show_media'); + $dbman->change_field_type($table, $field); + + // Zoom savepoint reached. + upgrade_mod_savepoint(true, 2023080202, 'zoom'); + } + return true; } diff --git a/lang/en/zoom.php b/lang/en/zoom.php index a26e165d..8fa30f8b 100644 --- a/lang/en/zoom.php +++ b/lang/en/zoom.php @@ -28,6 +28,9 @@ $string['accountid'] = 'Zoom account ID'; $string['accountid_desc'] = ''; $string['actions'] = 'Actions'; +$string['activitydate:starts'] = 'Starts: '; +$string['activitydate:started'] = 'Started: '; +$string['activitydate:ended'] = 'Ended: '; $string['addparticipant'] = 'Add a participant'; $string['addparticipantgroup'] = 'Add a group of participants'; $string['addroom'] = 'Add a room'; diff --git a/lib.php b/lib.php index 3a12e116..30cbf23d 100755 --- a/lib.php +++ b/lib.php @@ -346,9 +346,6 @@ function populate_zoom_from_response(stdClass $zoom, stdClass $response) { if (isset($response->settings->auto_recording)) { $newzoom->option_auto_recording = $response->settings->auto_recording; } - if (!isset($newzoom->option_auto_recording)) { - $newzoom->option_auto_recording = 'none'; - } return $newzoom; } @@ -1274,3 +1271,66 @@ function zoom_get_instance_breakout_rooms($zoomid) { 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 = array('id' => $coursemodule->instance); + $fields = 'id, intro, introformat, start_time, 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; + } + + 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', array('id' => $cm->instance), '*', MUST_EXIST); + + // Get meeting state from Zoom. + list($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 != 0) { + $cm->override_customdata('start_time', zoom_get_next_occurrence($moduleinstance)); + } + } +} diff --git a/locallib.php b/locallib.php index 5795c502..44d32dff 100755 --- a/locallib.php +++ b/locallib.php @@ -1001,6 +1001,7 @@ function zoom_load_meeting($id, $context, $usestarturl = true) { list($inprogress, $available, $finished) = zoom_get_state($zoom); $userisregistered = false; + $userisregistering = false; if ($zoom->registration != ZOOM_REGISTRATION_OFF) { // Check if user already registered. $registrantjoinurl = zoom_get_registrant_join_url($USER->email, $zoom->meeting_id, $zoom->webinar); @@ -1008,12 +1009,12 @@ function zoom_load_meeting($id, $context, $usestarturl = true) { // Allow unregistered users to register. if (!$userisregistered) { - $available = true; + $userisregistering = true; } } // If the meeting is not yet available, deny access. - if ($available !== true) { + if (!$available && !$userisregistering) { // Get unavailability note. $returns['error'] = zoom_get_unavailability_note($zoom, $finished); return $returns; @@ -1058,6 +1059,11 @@ function zoom_load_meeting($id, $context, $usestarturl = true) { $returns['nexturl'] = new moodle_url($url, ['uname' => $unamedisplay, 'uemail' => $USER->email]); } + // If the user is pre-registering, skip grading/completion. + if (!$available && $userisregistering) { + return $returns; + } + // Record user's clicking join. \mod_zoom\event\join_meeting_button_clicked::create([ 'context' => $context, diff --git a/mod_form.php b/mod_form.php index 97473e3a..5677bd6f 100644 --- a/mod_form.php +++ b/mod_form.php @@ -168,7 +168,7 @@ public function definition() { $mform->addElement('text', 'name', get_string('title', 'zoom'), ['size' => '64']); $mform->setType('name', PARAM_TEXT); $mform->addRule('name', null, 'required', null, 'client'); - $mform->addRule('name', get_string('maximumchars', '', 300), 'maxlength', 300, 'client'); + $mform->addRule('name', get_string('maximumchars', '', 200), 'maxlength', 200, 'client'); // Add description 'intro' and 'introformat'. $this->standard_intro_elements(); @@ -589,8 +589,14 @@ public function definition() { $options[ZOOM_AUTORECORDING_CLOUD] = get_string('autorecording_cloud', 'mod_zoom'); } + if ($config->recordingoption === ZOOM_AUTORECORDING_USERDEFAULT) { + $defaultsetting = $recordingsettings->auto_recording; + } else { + $defaultsetting = $config->recordingoption; + } + $mform->addElement('select', 'option_auto_recording', get_string('option_auto_recording', 'mod_zoom'), $options); - $mform->setDefault('option_auto_recording', $config->recordingoption); + $mform->setDefault('option_auto_recording', $defaultsetting); $mform->addHelpButton('option_auto_recording', 'option_auto_recording', 'mod_zoom'); } diff --git a/settings.php b/settings.php index 7d03c9a4..f4df7e49 100644 --- a/settings.php +++ b/settings.php @@ -367,7 +367,7 @@ $recordingoption = new admin_setting_configselect('zoom/recordingoption', get_string('option_auto_recording', 'mod_zoom'), get_string('option_auto_recording_help', 'mod_zoom'), - ZOOM_AUTORECORDING_NONE, $autorecordingchoices); + ZOOM_AUTORECORDING_USERDEFAULT, $autorecordingchoices); $settings->add($recordingoption); $allowrecordingchangeoption = new admin_setting_configcheckbox('zoom/allowrecordingchangeoption', diff --git a/version.php b/version.php index 6b6d13d3..155bcb0e 100755 --- a/version.php +++ b/version.php @@ -25,8 +25,8 @@ defined('MOODLE_INTERNAL') || die(); $plugin->component = 'mod_zoom'; -$plugin->version = 2023070300; -$plugin->release = 'v5.0.0'; +$plugin->version = 2023082400; +$plugin->release = 'v5.1.0'; $plugin->requires = 2019052000; $plugin->maturity = MATURITY_STABLE; $plugin->cron = 0;