Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of the Zoom plugin for Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
/**
18
 * Library of interface functions and constants for module zoom
19
 *
20
 * All the core Moodle functions, neeeded to allow the module to work
21
 * integrated in Moodle should be placed here.
22
 *
23
 * All the zoom specific functions, needed to implement all the module
24
 * logic, should go to locallib.php. This will help to save some memory when
25
 * Moodle is performing actions across all modules.
26
 *
27
 * @package    mod_zoom
28
 * @copyright  2015 UC Regents
29
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
30
 */
31
 
32
/* Moodle core API */
33
 
34
/**
35
 * Returns the information on whether the module supports a feature.
36
 *
37
 * @param string $feature FEATURE_xx constant for requested feature
38
 * @return mixed true if the feature is supported, null if unknown
39
 */
40
function zoom_supports($feature) {
41
    // Adding support for FEATURE_MOD_PURPOSE (MDL-71457) and providing backward compatibility (pre-v4.0).
42
    if (defined('FEATURE_MOD_PURPOSE') && $feature === FEATURE_MOD_PURPOSE) {
43
        return MOD_PURPOSE_COMMUNICATION;
44
    }
45
 
46
    switch ($feature) {
47
        case FEATURE_BACKUP_MOODLE2:
48
        case FEATURE_COMPLETION_TRACKS_VIEWS:
49
        case FEATURE_GRADE_HAS_GRADE:
50
        case FEATURE_GROUPINGS:
51
        case FEATURE_GROUPMEMBERSONLY:
52
        case FEATURE_MOD_INTRO:
53
        case FEATURE_SHOW_DESCRIPTION:
54
            return true;
55
        default:
56
            return null;
57
    }
58
}
59
 
60
/**
61
 * Saves a new instance of the zoom object into the database.
62
 *
63
 * Given an object containing all the necessary data (defined by the form in mod_form.php), this function
64
 * will create a new instance and return the id number of the new instance.
65
 *
66
 * @param stdClass $zoom Submitted data from the form in mod_form.php
67
 * @param mod_zoom_mod_form|null $mform The form instance (included because the function is used as a callback)
68
 * @return int The id of the newly inserted zoom record
69
 */
70
function zoom_add_instance(stdClass $zoom, ?mod_zoom_mod_form $mform = null) {
71
    global $CFG, $DB;
72
    require_once($CFG->dirroot . '/mod/zoom/locallib.php');
73
 
74
    if (defined('PHPUNIT_TEST') && PHPUNIT_TEST) {
75
        $zoom->id = $DB->insert_record('zoom', $zoom);
76
        zoom_grade_item_update($zoom);
77
        zoom_calendar_item_update($zoom);
78
        return $zoom->id;
79
    }
80
 
81
    // Deals with password manager issues.
82
    $zoom->password = $zoom->meetingcode;
83
    unset($zoom->meetingcode);
84
 
85
    if (empty($zoom->requirepasscode)) {
86
        $zoom->password = '';
87
    }
88
 
89
    // Handle weekdays if weekly recurring meeting selected.
90
    if ($zoom->recurring && $zoom->recurrence_type == ZOOM_RECURRINGTYPE_WEEKLY) {
91
        $zoom->weekly_days = zoom_handle_weekly_days($zoom);
92
    }
93
 
94
    $zoom->course = (int) $zoom->course;
95
 
96
    $zoom->breakoutrooms = [];
97
    if (!empty($zoom->rooms)) {
98
        $breakoutrooms = zoom_build_instance_breakout_rooms_array_for_api($zoom);
99
        $zoom->breakoutrooms = $breakoutrooms['zoom'];
100
    }
101
 
102
    $response = zoom_webservice()->create_meeting($zoom);
103
    $zoom = populate_zoom_from_response($zoom, $response);
104
    $zoom->timemodified = time();
105
    if (!empty($zoom->schedule_for)) {
106
        // Wait until after receiving a successful response from zoom to update the host
107
        // based on the schedule_for field. Zoom handles the schedule for on their
108
        // end, but returns the host as the person who created the meeting, not the person
109
        // that it was scheduled for.
110
        $correcthostzoomuser = zoom_get_user($zoom->schedule_for);
111
        $zoom->host_id = $correcthostzoomuser->id;
112
    }
113
 
114
    if (isset($zoom->recurring) && isset($response->occurrences) && empty($response->occurrences)) {
115
        // Recurring meetings did not create any occurrencces.
116
        // This means invalid options selected.
117
        // Need to rollback created meeting.
118
        zoom_webservice()->delete_meeting($zoom->meeting_id, $zoom->webinar);
119
 
120
        $redirecturl = new moodle_url('/course/view.php', ['id' => $zoom->course]);
121
        throw new moodle_exception('erroraddinstance', 'zoom', $redirecturl->out());
122
    }
123
 
124
    $zoom->id = $DB->insert_record('zoom', $zoom);
125
    if (!empty($zoom->breakoutrooms)) {
126
        // We ignore the API response and save the local data for breakout rooms to support dynamic users and groups.
127
        zoom_insert_instance_breakout_rooms($zoom->id, $breakoutrooms['db']);
128
    }
129
 
130
    // Store tracking field data for meeting.
131
    zoom_sync_meeting_tracking_fields($zoom->id, $response->tracking_fields ?? []);
132
 
133
    zoom_calendar_item_update($zoom);
134
    zoom_grade_item_update($zoom);
135
 
136
    return $zoom->id;
137
}
138
 
139
/**
140
 * Updates an instance of the zoom in the database and on Zoom servers.
141
 *
142
 * Given an object containing all the necessary data (defined by the form in mod_form.php), this function
143
 * will update an existing instance with new data.
144
 *
145
 * @param stdClass $zoom An object from the form in mod_form.php
146
 * @param mod_zoom_mod_form|null $mform The form instance (included because the function is used as a callback)
147
 * @return boolean Success/Failure
148
 */
149
function zoom_update_instance(stdClass $zoom, ?mod_zoom_mod_form $mform = null) {
150
    global $CFG, $DB;
151
    require_once($CFG->dirroot . '/mod/zoom/locallib.php');
152
 
153
    // The object received from mod_form.php returns instance instead of id for some reason.
154
    if (isset($zoom->instance)) {
155
        $zoom->id = $zoom->instance;
156
    }
157
 
158
    $zoom->timemodified = time();
159
 
160
    // Deals with password manager issues.
161
    if (isset($zoom->meetingcode)) {
162
        $zoom->password = $zoom->meetingcode;
163
        unset($zoom->meetingcode);
164
    }
165
 
166
    if (property_exists($zoom, 'requirepasscode') && empty($zoom->requirepasscode)) {
167
        $zoom->password = '';
168
    }
169
 
170
    // Handle weekdays if weekly recurring meeting selected.
171
    if ($zoom->recurring && $zoom->recurrence_type == ZOOM_RECURRINGTYPE_WEEKLY) {
172
        $zoom->weekly_days = zoom_handle_weekly_days($zoom);
173
    }
174
 
175
    $DB->update_record('zoom', $zoom);
176
 
177
    $zoom->breakoutrooms = [];
178
    if (!empty($zoom->rooms)) {
179
        $breakoutrooms = zoom_build_instance_breakout_rooms_array_for_api($zoom);
180
        zoom_update_instance_breakout_rooms($zoom->id, $breakoutrooms['db']);
181
        $zoom->breakoutrooms = $breakoutrooms['zoom'];
182
    }
183
 
184
    $updatedzoomrecord = $DB->get_record('zoom', ['id' => $zoom->id]);
185
    $zoom->meeting_id = $updatedzoomrecord->meeting_id;
186
    $zoom->webinar = $updatedzoomrecord->webinar;
187
 
188
    // Update meeting on Zoom.
189
    try {
190
        zoom_webservice()->update_meeting($zoom);
191
        if (!empty($zoom->schedule_for)) {
192
            // Only update this if we actually get a valid user.
193
            if ($correcthostzoomuser = zoom_get_user($zoom->schedule_for)) {
194
                $zoom->host_id = $correcthostzoomuser->id;
195
                $DB->update_record('zoom', $zoom);
196
            }
197
        }
198
    } catch (moodle_exception $error) {
199
        return false;
200
    }
201
 
202
    // Get the updated meeting info from zoom, before updating calendar events.
203
    $response = zoom_webservice()->get_meeting_webinar_info($zoom->meeting_id, $zoom->webinar);
204
    $zoom = populate_zoom_from_response($zoom, $response);
205
    $DB->update_record('zoom', $zoom);
206
 
207
    // Update tracking field data for meeting.
208
    zoom_sync_meeting_tracking_fields($zoom->id, $response->tracking_fields ?? []);
209
 
210
    zoom_calendar_item_update($zoom);
211
    zoom_grade_item_update($zoom);
212
 
213
    return true;
214
}
215
 
216
/**
217
 * Function to handle selected weekdays, for recurring weekly meeting.
218
 *
219
 * @param stdClass $zoom The zoom instance
220
 * @return string The comma separated string for selected weekdays
221
 */
222
function zoom_handle_weekly_days($zoom) {
223
    $weekdaynumbers = [];
224
    for ($i = 1; $i <= 7; $i++) {
225
        $key = 'weekly_days_' . $i;
226
        if (!empty($zoom->$key)) {
227
            $weekdaynumbers[] = $i;
228
        }
229
    }
230
 
231
    return implode(',', $weekdaynumbers);
232
}
233
 
234
/**
235
 * Function to unset the weekly options in postprocessing.
236
 *
237
 * @param stdClass $data The form data object
238
 * @return stdClass $data The form data object minus weekly options.
239
 */
240
function zoom_remove_weekly_options($data) {
241
    // Unset the weekly_days options.
242
    for ($i = 1; $i <= 7; $i++) {
243
        $key = 'weekly_days_' . $i;
244
        unset($data->$key);
245
    }
246
 
247
    return $data;
248
}
249
 
250
/**
251
 * Function to unset the monthly options in postprocessing.
252
 *
253
 * @param stdClass $data The form data object
254
 * @return stdClass $data The form data object minus monthly options.
255
 */
256
function zoom_remove_monthly_options($data) {
257
    // Unset the monthly options.
258
    unset($data->monthly_repeat_option);
259
    unset($data->monthly_day);
260
    unset($data->monthly_week);
261
    unset($data->monthly_week_day);
262
    return $data;
263
}
264
 
265
/**
266
 * Populates a zoom meeting or webinar from a response object.
267
 *
268
 * Given a zoom meeting object from mod_form.php, this function uses the response to repopulate some of the object properties.
269
 *
270
 * @param stdClass $zoom An object from the form in mod_form.php
271
 * @param stdClass $response A response from an API call like 'create meeting' or 'update meeting'
272
 * @return stdClass A $zoom object ready to be added to the database.
273
 */
274
function populate_zoom_from_response(stdClass $zoom, stdClass $response) {
275
    global $CFG;
276
    // Inlcuded for constants.
277
    require_once($CFG->dirroot . '/mod/zoom/locallib.php');
278
 
279
    $newzoom = clone $zoom;
280
 
281
    $samefields = ['join_url', 'created_at', 'timezone'];
282
    foreach ($samefields as $field) {
283
        if (isset($response->$field)) {
284
            $newzoom->$field = $response->$field;
285
        }
286
    }
287
 
288
    if (isset($response->duration)) {
289
        $newzoom->duration = $response->duration * 60;
290
    }
291
 
292
    $newzoom->meeting_id = $response->id;
293
    $newzoom->name = $response->topic;
294
    if (isset($response->start_time)) {
295
        $newzoom->start_time = strtotime($response->start_time);
296
    }
297
 
298
    $recurringtypes = [
299
        ZOOM_RECURRING_MEETING,
300
        ZOOM_RECURRING_FIXED_MEETING,
301
        ZOOM_RECURRING_WEBINAR,
302
        ZOOM_RECURRING_FIXED_WEBINAR,
303
    ];
304
    $newzoom->recurring = in_array($response->type, $recurringtypes);
305
    if (!empty($response->occurrences)) {
306
        $newzoom->occurrences = [];
307
        // Normalise the occurrence times.
308
        foreach ($response->occurrences as $occurrence) {
309
            $occurrence->start_time = strtotime($occurrence->start_time);
310
            $occurrence->duration = $occurrence->duration * 60;
311
            $newzoom->occurrences[] = $occurrence;
312
        }
313
    }
314
 
315
    if (isset($response->password)) {
316
        $newzoom->password = $response->password;
317
    }
318
 
319
    if (isset($response->settings->encryption_type)) {
320
        $newzoom->option_encryption_type = $response->settings->encryption_type;
321
    }
322
 
323
    if (isset($response->settings->join_before_host)) {
324
        $newzoom->option_jbh = $response->settings->join_before_host;
325
    }
326
 
327
    if (isset($response->settings->participant_video)) {
328
        $newzoom->option_participants_video = $response->settings->participant_video;
329
    }
330
 
331
    if (isset($response->settings->alternative_hosts)) {
332
        $newzoom->alternative_hosts = $response->settings->alternative_hosts;
333
    }
334
 
335
    if (isset($response->settings->mute_upon_entry)) {
336
        $newzoom->option_mute_upon_entry = $response->settings->mute_upon_entry;
337
    }
338
 
339
    if (isset($response->settings->meeting_authentication)) {
340
        $newzoom->option_authenticated_users = $response->settings->meeting_authentication;
341
    }
342
 
343
    if (isset($response->settings->waiting_room)) {
344
        $newzoom->option_waiting_room = $response->settings->waiting_room;
345
    }
346
 
347
    if (isset($response->settings->auto_recording)) {
348
        $newzoom->option_auto_recording = $response->settings->auto_recording;
349
    }
350
 
351
    return $newzoom;
352
}
353
 
354
/**
355
 * Removes an instance of the zoom from the database
356
 *
357
 * Given an ID of an instance of this module, this function will permanently delete the instance and any data that depends on it.
358
 *
359
 * @param int $id Id of the module instance
360
 * @return boolean Success/Failure
361
 * @throws moodle_exception if failed to delete and zoom did not issue a not found error
362
 */
363
function zoom_delete_instance($id) {
364
    global $CFG, $DB;
365
    require_once($CFG->dirroot . '/mod/zoom/locallib.php');
366
 
367
    if (!$zoom = $DB->get_record('zoom', ['id' => $id])) {
368
        // For some reason already deleted, so let Moodle take care of the rest.
369
        return true;
370
    }
371
 
372
    // If the meeting is missing from zoom, don't bother with the webservice.
373
    if ($zoom->exists_on_zoom == ZOOM_MEETING_EXISTS) {
374
        try {
375
            zoom_webservice()->delete_meeting($zoom->meeting_id, $zoom->webinar);
376
        } catch (\mod_zoom\not_found_exception $error) {
377
            // Meeting not on Zoom, so continue.
378
            mtrace('Meeting not on Zoom; continuing');
379
        }
380
    }
381
 
382
    // If we delete a meeting instance, do we want to delete the participants?
383
    $meetinginstances = $DB->get_records('zoom_meeting_details', ['zoomid' => $zoom->id]);
384
    foreach ($meetinginstances as $meetinginstance) {
385
        $DB->delete_records('zoom_meeting_participants', ['detailsid' => $meetinginstance->id]);
386
    }
387
 
388
    $DB->delete_records('zoom_meeting_details', ['zoomid' => $zoom->id]);
389
 
390
    // Delete tracking field data for deleted meetings.
391
    $DB->delete_records('zoom_meeting_tracking_fields', ['meeting_id' => $zoom->id]);
392
 
393
    // Delete any dependent records here.
394
    zoom_calendar_item_delete($zoom);
395
    zoom_grade_item_delete($zoom);
396
 
397
    $DB->delete_records('zoom', ['id' => $zoom->id]);
398
 
399
    // Delete breakout rooms.
400
    zoom_delete_instance_breakout_rooms($zoom->id);
401
 
402
    return true;
403
}
404
 
405
/**
406
 * Callback function to update the Zoom event in the database and on Zoom servers.
407
 *
408
 * The function is triggered when the course module name is set via quick edit.
409
 *
410
 * @param int $courseid
411
 * @param stdClass $zoom Zoom Module instance object.
412
 * @param stdClass $cm Course Module object.
413
 * @return bool
414
 */
415
function zoom_refresh_events($courseid, $zoom, $cm) {
416
    global $CFG;
417
 
418
    require_once($CFG->dirroot . '/mod/zoom/locallib.php');
419
 
420
    try {
421
        // Get the updated meeting info from zoom, before updating calendar events.
422
        $response = zoom_webservice()->get_meeting_webinar_info($zoom->meeting_id, $zoom->webinar);
423
        $fullzoom = populate_zoom_from_response($zoom, $response);
424
 
425
        // Only if the name has changed, update meeting on Zoom.
426
        if ($zoom->name !== $fullzoom->name) {
427
            $fullzoom->name = $zoom->name;
428
            zoom_webservice()->update_meeting($zoom);
429
        }
430
 
431
        zoom_calendar_item_update($fullzoom);
432
        zoom_grade_item_update($fullzoom);
433
    } catch (moodle_exception $error) {
434
        return false;
435
    }
436
 
437
    return true;
438
}
439
 
440
/**
441
 * Given a course and a time, this module should find recent activity that has occurred in zoom activities and print it out.
442
 *
443
 * @param stdClass $course The course record
444
 * @param bool $viewfullnames Should we display full names
445
 * @param int $timestart Print activity since this timestamp
446
 * @return boolean True if anything was printed, otherwise false
447
 */
448
function zoom_print_recent_activity($course, $viewfullnames, $timestart) {
449
    return false;
450
}
451
 
452
/**
453
 * Prepares the recent activity data
454
 *
455
 * This callback function is supposed to populate the passed array with
456
 * custom activity records. These records are then rendered into HTML
457
 * zoom_print_recent_mod_activity().
458
 *
459
 * Returns void, it adds items into $activities and increases $index.
460
 *
461
 * @param array $activities sequentially indexed array of objects with added 'cmid' property
462
 * @param int $index the index in the $activities to use for the next record
463
 * @param int $timestart append activity since this time
464
 * @param int $courseid the id of the course we produce the report for
465
 * @param int $cmid course module id
466
 * @param int $userid check for a particular user's activity only, defaults to 0 (all users)
467
 * @param int $groupid check for a particular group's activity only, defaults to 0 (all groups)
468
 */
469
function zoom_get_recent_mod_activity(&$activities, &$index, $timestart, $courseid, $cmid, $userid = 0, $groupid = 0) {
470
}
471
 
472
/**
473
 * Prints single activity item prepared by zoom_get_recent_mod_activity()
474
 *
475
 * @param stdClass $activity activity record with added 'cmid' property
476
 * @param int $courseid the id of the course we produce the report for
477
 * @param bool $detail print detailed report
478
 * @param array $modnames as returned by get_module_types_names()
479
 * @param bool $viewfullnames display users' full names
480
 */
481
function zoom_print_recent_mod_activity($activity, $courseid, $detail, $modnames, $viewfullnames) {
482
}
483
 
484
/**
485
 * Returns all other caps used in the module
486
 *
487
 * For example, this could be array('moodle/site:accessallgroups') if the
488
 * module uses that capability.
489
 *
490
 * @return array
491
 */
492
function zoom_get_extra_capabilities() {
493
    return [];
494
}
495
 
496
/**
497
 * Create or update Moodle calendar event of the Zoom instance.
498
 *
499
 * @param stdClass $zoom
500
 */
501
function zoom_calendar_item_update(stdClass $zoom) {
502
    global $CFG, $DB;
503
    require_once($CFG->dirroot . '/calendar/lib.php');
504
 
505
    // Based on data passed back from zoom, create/update/delete events based on data.
506
    $newevents = [];
507
    if (!$zoom->recurring) {
508
        $newevents[''] = zoom_populate_calender_item($zoom);
509
    } else if (!empty($zoom->occurrences)) {
510
        foreach ($zoom->occurrences as $occurrence) {
511
            $uuid = $occurrence->occurrence_id;
512
            $newevents[$uuid] = zoom_populate_calender_item($zoom, $occurrence);
513
        }
514
    }
515
 
516
    // Fetch all the events related to this zoom instance.
517
    $conditions = [
518
        'modulename' => 'zoom',
519
        'instance' => $zoom->id,
520
    ];
521
    $events = $DB->get_records('event', $conditions);
522
    $eventfields = ['name', 'timestart', 'timeduration'];
523
    foreach ($events as $event) {
524
        $uuid = $event->uuid;
525
        if (isset($newevents[$uuid])) {
526
            // This event already exists in Moodle.
527
            $changed = false;
528
            $newevent = $newevents[$uuid];
529
            // Check if the important fields have actually changed.
530
            foreach ($eventfields as $field) {
531
                if ($newevent->$field !== $event->$field) {
532
                    $changed = true;
533
                }
534
            }
535
 
536
            if ($changed) {
537
                calendar_event::load($event)->update($newevent);
538
            }
539
 
540
            // Event has been updated, remove from the list.
541
            unset($newevents[$uuid]);
542
        } else {
543
            // Event does not exist in Zoom, so delete from Moodle.
544
            calendar_event::load($event)->delete();
545
        }
546
    }
547
 
548
    // Any remaining events in the array don't exist on Moodle, so create a new event.
549
    foreach ($newevents as $uuid => $newevent) {
550
        calendar_event::create($newevent, false);
551
    }
552
}
553
 
554
/**
555
 * Return an array with the days of the week.
556
 *
557
 * @return array
558
 */
559
function zoom_get_weekday_options() {
560
    return [
561
        1 => get_string('sunday', 'calendar'),
562
        2 => get_string('monday', 'calendar'),
563
        3 => get_string('tuesday', 'calendar'),
564
        4 => get_string('wednesday', 'calendar'),
565
        5 => get_string('thursday', 'calendar'),
566
        6 => get_string('friday', 'calendar'),
567
        7 => get_string('saturday', 'calendar'),
568
    ];
569
}
570
 
571
/**
572
 * Return an array with the weeks of the month.
573
 *
574
 * @return array
575
 */
576
function zoom_get_monthweek_options() {
577
    return [
578
        1 => get_string('weekoption_first', 'zoom'),
579
        2 => get_string('weekoption_second', 'zoom'),
580
        3 => get_string('weekoption_third', 'zoom'),
581
        4 => get_string('weekoption_fourth', 'zoom'),
582
        -1 => get_string('weekoption_last', 'zoom'),
583
    ];
584
}
585
 
586
/**
587
 * Populate the calendar event object, based on the zoom instance
588
 *
589
 * @param stdClass $zoom The zoom instance.
590
 * @param stdClass|null $occurrence The occurrence object passed from the zoom api.
591
 * @return stdClass The calendar event object.
592
 */
593
function zoom_populate_calender_item(stdClass $zoom, ?stdClass $occurrence = null) {
594
    $event = new stdClass();
595
    $event->type = CALENDAR_EVENT_TYPE_ACTION;
596
    $event->modulename = 'zoom';
597
    $event->eventtype = 'zoom';
598
    $event->courseid = $zoom->course;
599
    $event->instance = $zoom->id;
600
    $event->visible = true;
601
    $event->name = $zoom->name;
602
    if ($zoom->intro) {
603
        $event->description = $zoom->intro;
604
        $event->format = $zoom->introformat;
605
    }
606
 
607
    if (!$occurrence) {
608
        $event->timesort = $zoom->start_time;
609
        $event->timestart = $zoom->start_time;
610
        $event->timeduration = $zoom->duration;
611
    } else {
612
        $event->timesort = $occurrence->start_time;
613
        $event->timestart = $occurrence->start_time;
614
        $event->timeduration = $occurrence->duration;
615
        $event->uuid = $occurrence->occurrence_id;
616
    }
617
 
618
    // Recurring meetings/webinars with no fixed time are created as invisible events.
619
    // For recurring meetings/webinars with a fixed time, we want to see the events on the calendar.
620
    if ($zoom->recurring && $zoom->recurrence_type == ZOOM_RECURRINGTYPE_NOTIME) {
621
        $event->visible = false;
622
    }
623
 
624
    return $event;
625
}
626
 
627
/**
628
 * Delete Moodle calendar events of the Zoom instance.
629
 *
630
 * @param stdClass $zoom
631
 */
632
function zoom_calendar_item_delete(stdClass $zoom) {
633
    global $CFG, $DB;
634
    require_once($CFG->dirroot . '/calendar/lib.php');
635
 
636
    $events = $DB->get_records('event', [
637
        'modulename' => 'zoom',
638
        'instance' => $zoom->id,
639
    ]);
640
    foreach ($events as $event) {
641
        calendar_event::load($event)->delete();
642
    }
643
}
644
 
645
/**
646
 * This function receives a calendar event and returns the action associated with it, or null if there is none.
647
 *
648
 * This is used by block_myoverview in order to display the event appropriately. If null is returned then the event
649
 * is not displayed on the block.
650
 *
651
 * @param calendar_event $event
652
 * @param \core_calendar\action_factory $factory
653
 * @param int $userid User id override
654
 * @return \core_calendar\local\event\entities\action_interface|null
655
 */
656
function mod_zoom_core_calendar_provide_event_action(
657
    calendar_event $event,
658
    \core_calendar\action_factory $factory,
659
    $userid = null
660
) {
661
    global $CFG, $DB, $USER;
662
 
663
    require_once($CFG->dirroot . '/mod/zoom/locallib.php');
664
 
665
    if (empty($userid)) {
666
        $userid = $USER->id;
667
    }
668
 
669
    $cm = get_fast_modinfo($event->courseid, $userid)->instances['zoom'][$event->instance];
670
    $zoom = $DB->get_record('zoom', ['id' => $cm->instance], '*');
671
    [$inprogress, $available, $finished] = zoom_get_state($zoom);
672
 
673
    if ($finished) {
674
        return null; // No point to showing finished meetings in overview.
675
    } else {
676
        return $factory->create_instance(
677
            get_string('join_meeting', 'zoom'),
678
            new \moodle_url('/mod/zoom/view.php', ['id' => $cm->id]),
679
            1,
680
            $available
681
        );
682
    }
683
}
684
 
685
/* Gradebook API */
686
 
687
/**
688
 * Checks if scale is being used by any instance of zoom.
689
 *
690
 * This is used to find out if scale used anywhere.
691
 *
692
 * @param int $scaleid ID of the scale
693
 * @return boolean true if the scale is used by any zoom instance
694
 */
695
function zoom_scale_used_anywhere($scaleid) {
696
    global $DB;
697
 
698
    if ($scaleid && $DB->record_exists('zoom', ['grade' => -$scaleid])) {
699
        return true;
700
    } else {
701
        return false;
702
    }
703
}
704
 
705
/**
706
 * Creates or updates grade item for the given zoom instance
707
 *
708
 * Needed by grade_update_mod_grades().
709
 *
710
 * @param stdClass $zoom instance object with extra cmidnumber and modname property
711
 * @param array $grades optional array/object of grade(s); 'reset' means reset grades in gradebook
712
 * @return void
713
 */
714
function zoom_grade_item_update(stdClass $zoom, $grades = null) {
715
    global $CFG;
716
    require_once($CFG->libdir . '/gradelib.php');
717
 
718
    $item = [];
719
    $item['itemname'] = clean_param($zoom->name, PARAM_NOTAGS);
720
    $item['gradetype'] = GRADE_TYPE_VALUE;
721
 
722
    if ($zoom->grade > 0) {
723
        $item['gradetype'] = GRADE_TYPE_VALUE;
724
        $item['grademax'] = $zoom->grade;
725
        $item['grademin'] = 0;
726
    } else if ($zoom->grade < 0) {
727
        $item['gradetype'] = GRADE_TYPE_SCALE;
728
        $item['scaleid'] = -$zoom->grade;
729
    } else {
730
        $gradebook = grade_get_grades($zoom->course, 'mod', 'zoom', $zoom->id);
731
        // Prevent the gradetype from switching to None if grades exist.
732
        if (empty($gradebook->items[0]->grades)) {
733
            $item['gradetype'] = GRADE_TYPE_NONE;
734
        } else {
735
            return;
736
        }
737
    }
738
 
739
    if ($grades === 'reset') {
740
        $item['reset'] = true;
741
        $grades = null;
742
    }
743
 
744
    grade_update('mod/zoom', $zoom->course, 'mod', 'zoom', $zoom->id, 0, $grades, $item);
745
}
746
 
747
/**
748
 * Delete grade item for given zoom instance
749
 *
750
 * @param stdClass $zoom instance object
751
 * @return int
752
 */
753
function zoom_grade_item_delete($zoom) {
754
    global $CFG;
755
    require_once($CFG->libdir . '/gradelib.php');
756
 
757
    return grade_update('mod/zoom', $zoom->course, 'mod', 'zoom', $zoom->id, 0, null, ['deleted' => 1]);
758
}
759
 
760
/**
761
 * Update zoom grades in the gradebook
762
 *
763
 * Needed by grade_update_mod_grades().
764
 *
765
 * @param stdClass $zoom instance object with extra cmidnumber and modname property
766
 * @param int $userid update grade of specific user only, 0 means all participants
767
 */
768
function zoom_update_grades(stdClass $zoom, $userid = 0) {
769
    global $CFG;
770
    require_once($CFG->libdir . '/gradelib.php');
771
 
772
    // Populate array of grade objects indexed by userid.
773
    if ($zoom->grade == 0) {
774
        zoom_grade_item_update($zoom);
775
    } else if ($userid != 0) {
776
        $grade = grade_get_grades($zoom->course, 'mod', 'zoom', $zoom->id, $userid)->items[0]->grades[$userid];
777
        $grade->userid = $userid;
778
        if ($grade->grade == -1) {
779
            $grade->grade = null;
780
        }
781
 
782
        zoom_grade_item_update($zoom, $grade);
783
    } else if ($userid == 0) {
784
        $context = context_course::instance($zoom->course);
785
        $enrollusersid = array_keys(get_enrolled_users($context));
786
        $grades = grade_get_grades($zoom->course, 'mod', 'zoom', $zoom->id, $enrollusersid)->items[0]->grades;
787
        foreach ($grades as $k => $v) {
788
            $grades[$k]->userid = $k;
789
            if ($v->grade == -1) {
790
                $grades[$k]->grade = null;
791
            }
792
        }
793
 
794
        zoom_grade_item_update($zoom, $grades);
795
    } else {
796
        zoom_grade_item_update($zoom);
797
    }
798
}
799
 
800
 
801
/**
802
 * Removes all zoom grades from gradebook by course id
803
 *
804
 * @param int $courseid
805
 */
806
function zoom_reset_gradebook($courseid) {
807
    global $DB;
808
 
809
    $params = [$courseid];
810
 
811
    $sql = "SELECT z.*, cm.idnumber as cmidnumber, z.course as courseid
812
          FROM {zoom} z
813
          JOIN {course_modules} cm ON cm.instance = z.id
814
          JOIN {modules} m ON m.id = cm.module AND m.name = 'zoom'
815
         WHERE z.course = ?";
816
 
817
    if ($zooms = $DB->get_records_sql($sql, $params)) {
818
        foreach ($zooms as $zoom) {
819
            zoom_grade_item_update($zoom, 'reset');
820
        }
821
    }
822
}
823
 
824
/**
825
 * This function is used by the reset_course_userdata function in moodlelib.
826
 * This function will remove all user data from zoom activites
827
 * and clean up any related data.
828
 *
829
 * @param object $data the data submitted from the reset course.
830
 * @return array status array
831
 */
832
function zoom_reset_userdata($data) {
833
    global $CFG, $DB;
834
 
835
    $componentstr = get_string('modulenameplural', 'zoom');
836
    $status = [];
837
 
838
    if (!empty($data->reset_zoom_all)) {
839
        // Reset tables that record user data.
840
        $DB->delete_records_select(
841
            'zoom_meeting_participants',
842
            'detailsid IN (SELECT zmd.id
843
                             FROM {zoom_meeting_details} zmd
844
                             JOIN {zoom} z ON z.id = zmd.zoomid
845
                            WHERE z.course = ?)',
846
            [$data->courseid]
847
        );
848
        $status[] = [
849
            'component' => $componentstr,
850
            'item' => get_string('meetingparticipantsdeleted', 'zoom'),
851
            'error' => false,
852
        ];
853
 
854
        $DB->delete_records_select(
855
            'zoom_meeting_recordings_view',
856
            'recordingsid IN (SELECT zmr.id
857
                             FROM {zoom_meeting_recordings} zmr
858
                             JOIN {zoom} z ON z.id = zmr.zoomid
859
                            WHERE z.course = ?)',
860
            [$data->courseid]
861
        );
862
        $status[] = [
863
            'component' => $componentstr,
864
            'item' => get_string('meetingrecordingviewsdeleted', 'zoom'),
865
            'error' => false,
866
        ];
867
    }
868
 
869
    return $status;
870
}
871
 
872
/**
873
 * Called by course/reset.php
874
 *
875
 * @param object $mform the course reset form that is being built.
876
 */
877
function zoom_reset_course_form_definition($mform) {
878
    $mform->addElement('header', 'zoomheader', get_string('modulenameplural', 'zoom'));
879
 
880
    $mform->addElement('checkbox', 'reset_zoom_all', get_string('resetzoomsall', 'zoom'));
881
}
882
 
883
/**
884
 * Course reset form defaults.
885
 *
886
 * @param object $course data passed by the form.
887
 * @return array the defaults.
888
 */
889
function zoom_reset_course_form_defaults($course) {
890
    return ['reset_zoom_all' => 1];
891
}
892
 
893
/* File API */
894
 
895
/**
896
 * Returns the lists of all browsable file areas within the given module context
897
 *
898
 * The file area 'intro' for the activity introduction field is added automatically
899
 * by file_browser::get_file_info_context_module()
900
 *
901
 * @param stdClass $course
902
 * @param stdClass $cm
903
 * @param stdClass $context
904
 * @return array of [(string)filearea] => (string)description
905
 */
906
function zoom_get_file_areas($course, $cm, $context) {
907
    return [];
908
}
909
 
910
/**
911
 * File browsing support for zoom file areas
912
 *
913
 * @package mod_zoom
914
 * @category files
915
 *
916
 * @param file_browser $browser
917
 * @param array $areas
918
 * @param stdClass $course
919
 * @param stdClass $cm
920
 * @param stdClass $context
921
 * @param string $filearea
922
 * @param int $itemid
923
 * @param string $filepath
924
 * @param string $filename
925
 * @return file_info instance or null if not found
926
 */
927
function zoom_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) {
928
    return null;
929
}
930
 
931
/**
932
 * Serves the files from the zoom file areas
933
 *
934
 * @package mod_zoom
935
 * @category files
936
 *
937
 * @param stdClass $course the course object
938
 * @param stdClass $cm the course module object
939
 * @param stdClass $context the zoom's context
940
 * @param string $filearea the name of the file area
941
 * @param array $args extra arguments (itemid, path)
942
 * @param bool $forcedownload whether or not force download
943
 * @param array $options additional options affecting the file serving
944
 */
945
function zoom_pluginfile($course, $cm, $context, $filearea, array $args, $forcedownload, array $options = []) {
946
    if ($context->contextlevel != CONTEXT_MODULE) {
947
        send_file_not_found();
948
    }
949
 
950
    require_login($course, true, $cm);
951
 
952
    send_file_not_found();
953
}
954
 
955
/* Navigation API */
956
 
957
/**
958
 * Extends the global navigation tree by adding zoom nodes if there is a relevant content
959
 *
960
 * This can be called by an AJAX request so do not rely on $PAGE as it might not be set up properly.
961
 *
962
 * @param navigation_node $navref An object representing the navigation tree node of the zoom module instance
963
 * @param stdClass $course current course record
964
 * @param stdClass $module current zoom instance record
965
 * @param cm_info $cm course module information
966
 */
967
function zoom_extend_navigation(navigation_node $navref, stdClass $course, stdClass $module, cm_info $cm) {
968
}
969
 
970
/**
971
 * Extends the settings navigation with the zoom settings
972
 *
973
 * This function is called when the context for the page is a zoom module. This is not called by AJAX
974
 * so it is safe to rely on the $PAGE.
975
 *
976
 * @param settings_navigation $settingsnav complete settings navigation tree
977
 * @param navigation_node|null $zoomnode zoom administration node
978
 */
979
function zoom_extend_settings_navigation(settings_navigation $settingsnav, ?navigation_node $zoomnode = null) {
980
}
981
 
982
/**
983
 * Get icon mapping for font-awesome.
984
 *
985
 * @see https://docs.moodle.org/dev/Moodle_icons
986
 */
987
function mod_zoom_get_fontawesome_icon_map() {
988
    return [
989
        'mod_zoom:i/calendar' => 'fa-calendar',
990
    ];
991
}
992
 
993
/**
994
 * This function updates the tracking field settings in config_plugins.
995
 */
996
function mod_zoom_update_tracking_fields() {
997
    global $DB;
998
 
999
    try {
1000
        $defaulttrackingfields = zoom_clean_tracking_fields();
1001
        $zoomprops = ['id', 'field', 'required', 'visible', 'recommended_values'];
1002
        $confignames = [];
1003
 
1004
        if (!empty($defaulttrackingfields)) {
1005
            $zoomtrackingfields = zoom_list_tracking_fields();
1006
            foreach ($zoomtrackingfields as $field => $zoomtrackingfield) {
1007
                if (isset($defaulttrackingfields[$field])) {
1008
                    foreach ($zoomprops as $zoomprop) {
1009
                        $configname = 'tf_' . $field . '_' . $zoomprop;
1010
                        $confignames[] = $configname;
1011
                        if ($zoomprop === 'recommended_values') {
1012
                            $configvalue = implode(', ', $zoomtrackingfield[$zoomprop]);
1013
                        } else {
1014
                            $configvalue = $zoomtrackingfield[$zoomprop];
1015
                        }
1016
 
1017
                        set_config($configname, $configvalue, 'zoom');
1018
                    }
1019
                }
1020
            }
1021
        }
1022
 
1023
        $config = get_config('zoom');
1024
        $proparray = get_object_vars($config);
1025
        $properties = array_keys($proparray);
1026
        $oldconfigs = array_diff($properties, $confignames);
1027
        $pattern = '/^tf_(?P<oldfield>.*)_(' . implode('|', $zoomprops) . ')$/';
1028
        foreach ($oldconfigs as $oldconfig) {
1029
            if (preg_match($pattern, $oldconfig, $matches)) {
1030
                set_config($oldconfig, null, 'zoom');
1031
                $DB->delete_records('zoom_meeting_tracking_fields', ['tracking_field' => $matches['oldfield']]);
1032
            }
1033
        }
1034
    } catch (Exception $e) {
1035
        // Fail gracefully because the callback function might be called directly.
1036
        return false;
1037
    }
1038
 
1039
    return true;
1040
}
1041
 
1042
/**
1043
 * Insert zoom instance breakout rooms
1044
 *
1045
 * @param int $zoomid
1046
 * @param array $breakoutrooms zoom breakout rooms
1047
 */
1048
function zoom_insert_instance_breakout_rooms($zoomid, $breakoutrooms) {
1049
    global $DB;
1050
 
1051
    foreach ($breakoutrooms as $breakoutroom) {
1052
        $item = new stdClass();
1053
        $item->name = $breakoutroom['name'];
1054
        $item->zoomid = $zoomid;
1055
 
1056
        $breakoutroomid = $DB->insert_record('zoom_meeting_breakout_rooms', $item);
1057
 
1058
        foreach ($breakoutroom['participants'] as $participant) {
1059
            $item = new stdClass();
1060
            $item->userid = $participant;
1061
            $item->breakoutroomid = $breakoutroomid;
1062
            $DB->insert_record('zoom_breakout_participants', $item);
1063
        }
1064
 
1065
        foreach ($breakoutroom['groups'] as $group) {
1066
            $item = new stdClass();
1067
            $item->groupid = $group;
1068
            $item->breakoutroomid = $breakoutroomid;
1069
            $DB->insert_record('zoom_breakout_groups', $item);
1070
        }
1071
    }
1072
}
1073
 
1074
/**
1075
 * Update zoom instance breakout rooms
1076
 *
1077
 * @param int $zoomid
1078
 * @param array $breakoutrooms
1079
 */
1080
function zoom_update_instance_breakout_rooms($zoomid, $breakoutrooms) {
1081
    global $DB;
1082
 
1083
    zoom_delete_instance_breakout_rooms($zoomid);
1084
    zoom_insert_instance_breakout_rooms($zoomid, $breakoutrooms);
1085
}
1086
 
1087
/**
1088
 * Delete zoom instance breakout rooms
1089
 *
1090
 * @param int $zoomid
1091
 */
1092
function zoom_delete_instance_breakout_rooms($zoomid) {
1093
    global $DB;
1094
 
1095
    $zoomcurrentbreakoutroomsids = $DB->get_fieldset_select('zoom_meeting_breakout_rooms', 'id', "zoomid = {$zoomid}");
1096
 
1097
    foreach ($zoomcurrentbreakoutroomsids as $id) {
1098
        $DB->delete_records('zoom_breakout_participants', ['breakoutroomid' => $id]);
1099
        $DB->delete_records('zoom_breakout_groups', ['breakoutroomid' => $id]);
1100
    }
1101
 
1102
    $DB->delete_records('zoom_meeting_breakout_rooms', ['zoomid' => $zoomid]);
1103
}
1104
 
1105
/**
1106
 * Build zoom instance breakout rooms array for api
1107
 *
1108
 * @param stdClass $zoom Submitted data from the form in mod_form.php.
1109
 * @return array The meeting breakout rooms array.
1110
 */
1111
function zoom_build_instance_breakout_rooms_array_for_api($zoom) {
1112
    $context = context_course::instance($zoom->course);
1113
    $users = get_enrolled_users($context);
1114
    $groups = groups_get_all_groups($zoom->course);
1115
 
1116
    // Building meeting breakout rooms array.
1117
    $breakoutrooms = [];
1118
    if (!empty($zoom->rooms)) {
1119
        foreach ($zoom->rooms as $roomid => $roomname) {
1120
            // Getting meeting rooms participants.
1121
            $roomparticipants = [];
1122
            $dbroomparticipants = [];
1123
            if (!empty($zoom->roomsparticipants[$roomid])) {
1124
                foreach ($zoom->roomsparticipants[$roomid] as $participantid) {
1125
                    if (isset($users[$participantid])) {
1126
                        $roomparticipants[] = $users[$participantid]->email;
1127
                        $dbroomparticipants[] = $participantid;
1128
                    }
1129
                }
1130
            }
1131
 
1132
            // Getting meeting rooms groups members.
1133
            $roomgroupsmembers = [];
1134
            $dbroomgroupsmembers = [];
1135
            if (!empty($zoom->roomsgroups[$roomid])) {
1136
                foreach ($zoom->roomsgroups[$roomid] as $groupid) {
1137
                    if (isset($groups[$groupid])) {
1138
                        $groupmembers = groups_get_members($groupid);
1139
                        $roomgroupsmembers[] = array_column(array_values($groupmembers), 'email');
1140
                        $dbroomgroupsmembers[] = $groupid;
1141
                    }
1142
                }
1143
 
1144
                $roomgroupsmembers = array_merge(...$roomgroupsmembers);
1145
            }
1146
 
1147
            $zoomdata = [
1148
                'name' => $roomname,
1149
                'participants' => array_values(array_unique(array_merge($roomparticipants, $roomgroupsmembers))),
1150
            ];
1151
 
1152
            $dbdata = [
1153
                'name' => $roomname,
1154
                'participants' => $dbroomparticipants,
1155
                'groups' => $dbroomgroupsmembers,
1156
            ];
1157
 
1158
            $breakoutrooms['zoom'][] = $zoomdata;
1159
            $breakoutrooms['db'][] = $dbdata;
1160
        }
1161
    }
1162
 
1163
    return $breakoutrooms;
1164
}
1165
 
1166
/**
1167
 * Build zoom instance breakout rooms array for view.
1168
 *
1169
 * @param int $zoomid
1170
 * @param array $courseparticipants
1171
 * @param array $coursegroups
1172
 * @return array The meeting breakout rooms array.
1173
 */
1174
function zoom_build_instance_breakout_rooms_array_for_view($zoomid, $courseparticipants, $coursegroups) {
1175
    $breakoutrooms = zoom_get_instance_breakout_rooms($zoomid);
1176
    $rooms = [];
1177
 
1178
    if (!empty($breakoutrooms)) {
1179
        foreach ($breakoutrooms as $key => $breakoutroom) {
1180
            $roomparticipants = $courseparticipants;
1181
            if (!empty($breakoutroom['participants'])) {
1182
                $participants = $breakoutroom['participants'];
1183
                $roomparticipants = array_map(function ($roomparticipant) use ($participants) {
1184
                    if (isset($participants[$roomparticipant['participantid']])) {
1185
                        $roomparticipant['selected'] = true;
1186
                    }
1187
 
1188
                    return $roomparticipant;
1189
                }, $courseparticipants);
1190
            }
1191
 
1192
            $roomgroups = $coursegroups;
1193
            if (!empty($breakoutroom['groups'])) {
1194
                $groups = $breakoutroom['groups'];
1195
                $roomgroups = array_map(function ($roomgroup) use ($groups) {
1196
                    if (isset($groups[$roomgroup['groupid']])) {
1197
                        $roomgroup['selected'] = true;
1198
                    }
1199
 
1200
                    return $roomgroup;
1201
                }, $coursegroups);
1202
            }
1203
 
1204
            $rooms[] = [
1205
                'roomid' => $breakoutroom['roomid'],
1206
                'roomname' => $breakoutroom['roomname'],
1207
                'courseparticipants' => $roomparticipants,
1208
                'coursegroups' => $roomgroups,
1209
            ];
1210
        }
1211
 
1212
        $rooms[0]['roomactive'] = true;
1213
    }
1214
 
1215
    return $rooms;
1216
}
1217
 
1218
/**
1219
 * Get zoom instance breakout rooms.
1220
 *
1221
 * @param int $zoomid
1222
 * @return array
1223
 */
1224
function zoom_get_instance_breakout_rooms($zoomid) {
1225
    global $DB;
1226
 
1227
    $breakoutrooms = [];
1228
    $params = [$zoomid];
1229
 
1230
    $sql = "SELECT id, name
1231
        FROM {zoom_meeting_breakout_rooms}
1232
        WHERE zoomid = ?";
1233
 
1234
    $rooms = $DB->get_records_sql($sql, $params);
1235
 
1236
    foreach ($rooms as $room) {
1237
        $breakoutrooms[$room->id] = [
1238
            'roomid' => $room->id,
1239
            'roomname' => $room->name,
1240
            'participants' => [],
1241
            'groups' => [],
1242
        ];
1243
 
1244
        // Get breakout room participants.
1245
        $params = [$room->id];
1246
        $sql = "SELECT userid
1247
        FROM {zoom_breakout_participants}
1248
        WHERE breakoutroomid = ?";
1249
 
1250
        $participants = $DB->get_records_sql($sql, $params);
1251
 
1252
        if (!empty($participants)) {
1253
            foreach ($participants as $participant) {
1254
                $breakoutrooms[$room->id]['participants'][$participant->userid] = $participant->userid;
1255
            }
1256
        }
1257
 
1258
        // Get breakout room groups.
1259
        $sql = "SELECT groupid
1260
        FROM {zoom_breakout_groups}
1261
        WHERE breakoutroomid = ?";
1262
 
1263
        $groups = $DB->get_records_sql($sql, $params);
1264
 
1265
        if (!empty($groups)) {
1266
            foreach ($groups as $group) {
1267
                $breakoutrooms[$room->id]['groups'][$group->groupid] = $group->groupid;
1268
            }
1269
        }
1270
    }
1271
 
1272
    return $breakoutrooms;
1273
}
1274
 
1275
/**
1276
 * Print zoom meeting date and time in the course listing page
1277
 *
1278
 * Given a course_module object, this function returns any "extra" information that may be needed
1279
 * when printing this activity in a course listing. See get_array_of_activities() in course/lib.php.
1280
 *
1281
 * @param stdClass $coursemodule The coursemodule object
1282
 * @return cached_cm_info An object on information that the courses will know about
1283
 */
1284
function zoom_get_coursemodule_info($coursemodule) {
1285
    global $DB;
1286
 
1287
    $dbparams = ['id' => $coursemodule->instance];
1288
    $fields = 'id, intro, introformat, start_time, recurring, recurrence_type, duration';
1289
    if (!$zoom = $DB->get_record('zoom', $dbparams, $fields)) {
1290
        return false;
1291
    }
1292
 
1293
    $result = new cached_cm_info();
1294
 
1295
    if ($coursemodule->showdescription) {
1296
        // Convert intro to html. Do not filter cached version, filters run at display time.
1297
        $result->content = format_module_intro('zoom', $zoom, $coursemodule->id, false);
1298
    }
1299
 
1300
    // Populate some other values that can be used in calendar or on dashboard.
1301
    if ($zoom->start_time) {
1302
        $result->customdata['start_time'] = $zoom->start_time;
1303
    }
1304
 
1305
    if ($zoom->duration) {
1306
        $result->customdata['duration'] = $zoom->duration;
1307
    }
1308
 
1309
    // Skip the if condition for recurring and recurrence_type, the values of NULL and 0 are needed in other functions.
1310
    $result->customdata['recurring'] = $zoom->recurring;
1311
    $result->customdata['recurrence_type'] = $zoom->recurrence_type;
1312
 
1313
    return $result;
1314
}
1315
 
1316
/**
1317
 * Sets dynamic information about a course module
1318
 *
1319
 * This function is called from cm_info when displaying the module
1320
 *
1321
 * @param cm_info $cm
1322
 */
1323
function zoom_cm_info_dynamic(cm_info $cm) {
1324
    global $CFG, $DB;
1325
 
1326
    require_once($CFG->dirroot . '/mod/zoom/locallib.php');
1327
 
1328
    if (method_exists($cm, 'override_customdata')) {
1329
        $moduleinstance = $DB->get_record('zoom', ['id' => $cm->instance], '*', MUST_EXIST);
1330
 
1331
        // Get meeting state from Zoom.
1332
        [$inprogress, $available, $finished] = zoom_get_state($moduleinstance);
1333
 
1334
        // For unfinished meetings, override start_time with the next occurrence.
1335
        // If this is a recurring meeting without fixed time, do not override - it will set start_time = 0.
1336
        if (!$finished && $moduleinstance->recurrence_type != ZOOM_RECURRINGTYPE_NOTIME) {
1337
            $cm->override_customdata('start_time', zoom_get_next_occurrence($moduleinstance));
1338
        }
1339
    }
1340
}