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 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
namespace mod_bigbluebuttonbn;
18
 
19
use cm_info;
20
use context;
21
use context_course;
22
use context_module;
23
use core\dml\table;
24
use mod_bigbluebuttonbn\local\config;
25
use mod_bigbluebuttonbn\local\helpers\files;
26
use mod_bigbluebuttonbn\local\helpers\roles;
27
use mod_bigbluebuttonbn\local\proxy\bigbluebutton_proxy;
28
use moodle_url;
29
use stdClass;
30
 
31
/**
32
 * Instance record for mod_bigbluebuttonbn.
33
 *
34
 * @package   mod_bigbluebuttonbn
35
 * @copyright 2021 Andrew Lyons <andrew@nicols.co.uk>
36
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37
 */
38
class instance {
39
 
40
    /** @var int Defines an instance type that includes room and recordings */
41
    public const TYPE_ALL = 0;
42
 
43
    /** @var int Defines an instance type that includes only room */
44
    public const TYPE_ROOM_ONLY = 1;
45
 
46
    /** @var int Defines an instance type that includes only recordings */
47
    public const TYPE_RECORDING_ONLY = 2;
48
 
49
    /** @var cm_info The cm_info object relating to the instance */
50
    protected $cm;
51
 
52
    /** @var stdClass The course that the instance is in */
53
    protected $course;
54
 
55
    /** @var stdClass The instance data for the instance */
56
    protected $instancedata;
57
 
58
    /** @var context The current context */
59
    protected $context;
60
 
61
    /** @var array The list of participants */
62
    protected $participantlist;
63
 
64
    /** @var int The current groupid if set */
65
    protected $groupid;
66
 
67
    /** @var int The course module id. */
68
    protected $cmid;
69
 
70
    /**
71
     * Instance constructor.
72
     *
73
     * Never called directly. Use self::get_from_instanceid or self::get_from_cmid.
74
     *
75
     * @param int $cmid
76
     * @param stdClass $course
77
     * @param stdClass $instancedata
78
     * @param int|null $groupid
79
     */
80
    private function __construct(int $cmid, stdClass $course, stdClass $instancedata, ?int $groupid = null) {
81
        $this->cmid = $cmid;
82
        $this->cm = null; // This is not retrieved later, whenever we call ::get_cm() it will be retrieved.
83
        $this->course = $course;
84
        $this->instancedata = $instancedata;
85
        $this->groupid = $groupid;
86
    }
87
 
88
    /**
89
     * Get a group instance of the specified instance.
90
     *
91
     * @param self $originalinstance
92
     * @param int $groupid
93
     * @return null|self
94
     */
95
    public static function get_group_instance_from_instance(self $originalinstance, int $groupid): ?self {
96
        return new self(
97
            $originalinstance->get_cm_id(),
98
            $originalinstance->get_course(),
99
            $originalinstance->get_instance_data(),
100
            $groupid
101
        );
102
    }
103
 
104
    /**
105
     * Get the instance information from an instance id.
106
     *
107
     * @param int $instanceid The id from the bigbluebuttonbn table
108
     * @return null|self
109
     */
110
    public static function get_from_instanceid(int $instanceid): ?self {
111
        return self::get_instance_info_retriever($instanceid, self::IDTYPE_INSTANCEID);
112
    }
113
 
114
    /**
115
     * Get the instance information from a cmid.
116
     *
117
     * @param int $cmid
118
     * @return null|self
119
     */
120
    public static function get_from_cmid(int $cmid): ?self {
121
        return self::get_instance_info_retriever($cmid, self::IDTYPE_CMID);
122
    }
123
 
124
    /**
125
     * Get the instance information from a cmid.
126
     */
127
    const IDTYPE_CMID = 0;
128
    /**
129
     * Get the instance information from an id.
130
     */
131
    const IDTYPE_INSTANCEID = 1;
132
 
133
    /**
134
     * Helper to get the instance information from an id.
135
     *
136
     * Used by self::get_from_id and self::get_cmid.
137
     *
138
     * @param int $id The id to look for.
139
     * @param int $idtype self::IDTYPE_CMID or self::IDTYPE_INSTANCEID
140
     * @return null|self
141
     * @throws \moodle_exception
142
     */
143
    private static function get_instance_info_retriever(int $id, int $idtype = self::IDTYPE_INSTANCEID): ?self {
144
        global $DB;
145
 
146
        if (!in_array($idtype, [self::IDTYPE_CMID, self::IDTYPE_INSTANCEID])) {
147
            throw new \moodle_exception('Invalid idtype');
148
        }
149
 
150
        [
151
            'coursetable' => $coursetable,
152
            'courseselect' => $courseselect,
153
            'coursefrom' => $coursefrom,
154
            'cmfrom' => $cmfrom,
155
            'cmselect' => $cmselect,
156
            'bbbtable' => $bbbtable,
157
            'bbbselect' => $bbbselect,
158
            'bbbfrom' => $bbbfrom,
159
            'subplugintables' => $subplugintables,
160
            'subpluginselects' => $subpluginselects,
161
            'subpluginfroms' => $subpluginfroms
162
        ] = self::get_tables_info();
163
 
164
        $select = implode(', ', array_merge([$courseselect, $bbbselect, $cmselect], $subpluginselects));
165
        $subpluginsleftjoins = '';
166
        foreach ($subpluginfroms as $tablealias => $subpluginfrom) {
167
            $subpluginsleftjoins .= "LEFT JOIN {$subpluginfrom} ON bbb.id = {$tablealias}.bigbluebuttonbnid\n";
168
        }
169
        $params = [
170
            'modname' => 'bigbluebuttonbn',
171
            'bbbid' => $id,
172
        ];
173
        $where = 'bbb.id = :bbbid';
174
        $from = <<<EOF
175
                {$bbbfrom}
176
                INNER JOIN {$cmfrom} ON cm.instance = bbb.id
177
                INNER JOIN {$coursefrom} ON c.id = cm.course
178
                INNER JOIN {modules} m ON m.id = cm.module AND m.name = :modname
179
EOF;
180
        if ($idtype == self::IDTYPE_CMID) {
181
            $params['cmid'] = $id;
182
            $where = 'cm.id = :cmid';
183
            $from = <<<EOF
184
                {$cmfrom}
185
                INNER JOIN {$coursefrom} ON c.id = cm.course
186
                INNER JOIN {modules} m ON m.id = cm.module AND m.name = :modname
187
                INNER JOIN {$bbbfrom} ON cm.instance = bbb.id
188
EOF;
189
        }
190
 
191
        $sql = "SELECT {$select} FROM {$from} {$subpluginsleftjoins} WHERE {$where}";
192
 
193
        $result = $DB->get_record_sql($sql, $params);
194
 
195
        if (empty($result)) {
196
            return null;
197
        }
198
 
199
        $course = $coursetable->extract_from_result($result);
200
        $instancedata = $bbbtable->extract_from_result($result);
201
        self::extract_plugin_table_info($instancedata, $result, $subplugintables);
202
        if ($idtype == self::IDTYPE_INSTANCEID) {
203
            $cmid = $result->cmid;
204
        } else {
205
            $cmid = $id;
206
        }
207
        return new self($cmid, $course, $instancedata);
208
    }
209
 
210
    /**
211
     * Get the instance information from a meetingid.
212
     *
213
     * If a group is specified in the meetingid then this will also be set.
214
     *
215
     * @param string $meetingid
216
     * @return null|self
217
     */
218
    public static function get_from_meetingid(string $meetingid): ?self {
219
        global $DB;
220
        // Here we try to manage cases where the meetingid was actually produced by the old plugin or we have actually
221
        // changed the identifiers for the instance.
222
        $matches = self::parse_meetingid($meetingid);
223
        $existinginstanceid = $DB->get_field('bigbluebuttonbn', 'id', ['meetingid' => $matches['meetingid']]);
224
        if (empty($existinginstanceid)) {
225
            debugging("The meeting id with ID ($meetingid) was not found in the bigbluebuttonbn table", DEBUG_DEVELOPER);
226
            $existinginstanceid = $matches['instanceid']; // We try to "guess" the meeting id from its instance id. We should
227
            // not really do that as this changes simply if we move the course elsewhere.
228
            debugging("Trying to get the instanceid from the meeting ID. This will soon be deprecated", DEBUG_DEVELOPER);
229
        }
230
        $instance = self::get_from_instanceid($existinginstanceid);
231
 
232
        // Check for the group if any.
233
        if ($instance && array_key_exists('groupid', $matches)) {
234
            $instance->set_group_id($matches['groupid']);
235
        }
236
 
237
        return $instance;
238
    }
239
 
240
    /**
241
     * Parse a meetingID for key data.
242
     *
243
     * @param string $meetingid
244
     * @return array
245
     * @throws \moodle_exception
246
     */
247
    public static function parse_meetingid(string $meetingid): array {
248
        $result = preg_match(
249
            '@(?P<meetingid>[^-]*)-(?P<courseid>[^-]*)-(?P<instanceid>\d+)(\[(?P<groupid>\d*)\])?@',
250
            $meetingid,
251
            $matches
252
        );
253
 
254
        if ($result !== 1) {
255
            throw new \moodle_exception("The supplied meeting id '{$meetingid}' is invalid found.");
256
        }
257
 
258
        return $matches;
259
    }
260
 
261
    /**
262
     * Get all instances in the specified course.
263
     *
264
     * @param int $courseid
265
     * @return self[]
266
     */
267
    public static function get_all_instances_in_course(int $courseid): array {
268
        global $DB;
269
        [
270
            'coursetable' => $coursetable,
271
            'courseselect' => $courseselect,
272
            'coursefrom' => $coursefrom,
273
            'cmfrom' => $cmfrom,
274
            'bbbtable' => $bbbtable,
275
            'bbbselect' => $bbbselect,
276
            'bbbfrom' => $bbbfrom,
277
            'subplugintables' => $subplugintables,
278
            'subpluginselects' => $subpluginselects,
279
            'subpluginfroms' => $subpluginfroms
280
        ] = self::get_tables_info();
281
 
282
        $selects = implode(', ', array_merge([$courseselect, $bbbselect], $subpluginselects));
283
        $subpluginsleftjoins = '';
284
        foreach ($subpluginfroms as $tablealias => $subpluginfrom) {
285
            $subpluginsleftjoins .= "LEFT JOIN {$subpluginfrom} ON bbb.id = {$tablealias}.bigbluebuttonbnid\n";
286
        }
287
        $sql = <<<EOF
288
    SELECT cm.id as cmid, {$selects}
289
      FROM {$cmfrom}
290
INNER JOIN {$coursefrom} ON c.id = cm.course
291
INNER JOIN {modules} m ON m.id = cm.module AND m.name = :modname
292
INNER JOIN {$bbbfrom} ON cm.instance = bbb.id
293
{$subpluginsleftjoins}
294
     WHERE cm.course = :courseid
295
EOF;
296
 
297
        $results = $DB->get_records_sql($sql, [
298
            'modname' => 'bigbluebuttonbn',
299
            'courseid' => $courseid,
300
        ]);
301
 
302
        $instances = [];
303
        foreach ($results as $result) {
304
            $course = $coursetable->extract_from_result($result);
305
            $instancedata = $bbbtable->extract_from_result($result);
306
            self::extract_plugin_table_info($instancedata, $result, $subplugintables);
307
            $instances[$result->cmid] = new self($result->cmid, $course, $instancedata);
308
        }
309
 
310
        return $instances;
311
    }
312
 
313
    /**
314
     * Helper method to extract result from subplugin tables.
315
     * @param object $instancedata instance data
316
     * @param object $result result from sql query
317
     * @param array $subplugintables array of subplugin tables
318
     */
319
    private static function extract_plugin_table_info(object &$instancedata, object $result, array $subplugintables) {
320
        foreach ($subplugintables as $subplugintable) {
321
            $subplugindata = (array) $subplugintable->extract_from_result($result);
322
            if (isset($subplugindata['id'])) {
323
                unset($subplugindata['id']); // Make sure that from the subplugin we don't conflict with the bigbluebutton id.
324
            }
325
            $instancedata = (object) array_merge($subplugindata, (array) $instancedata);
326
        }
327
    }
328
 
329
    /**
330
     * Get the additional tables returned from the subplugin.
331
     *
332
     * @return array
333
     */
334
    private static function get_tables_info(): array {
335
        $coursetable = new table('course', 'c', 'c');
336
        $courseselect = $coursetable->get_field_select();
337
        $coursefrom = $coursetable->get_from_sql();
338
 
339
        $cmtable = new table('course_modules', 'cm', 'cm');
340
        $cmselect = $cmtable->get_field_select();
341
        $cmfrom = $cmtable->get_from_sql();
342
 
343
        $bbbtable = new table('bigbluebuttonbn', 'bbb', 'b');
344
        $bbbselect = $bbbtable->get_field_select();
345
        $bbbfrom = $bbbtable->get_from_sql();
346
 
347
        // Look now for additional tables returned from the subplugin.
348
        $subpluginselects = [];
349
        $subpluginfroms = [];
350
        $subplugintables = [];
351
        $subplugintablesnames = extension::get_join_tables();
352
        foreach ($subplugintablesnames as $index => $subplugintablename) {
353
            $tablealias = 'ext'.$index;
354
            $subplugintable = new table($subplugintablename, $tablealias, 'ext'.$index);
355
            $subpluginselects[$tablealias] = $subplugintable->get_field_select();
356
            $subpluginfroms[$tablealias] = $subplugintable->get_from_sql();
357
            $subplugintables[$tablealias] = $subplugintable;
358
        }
359
        return compact(
360
            'coursetable', 'courseselect', 'coursefrom',
361
            'cmtable', 'cmselect', 'cmfrom',
362
            'bbbtable', 'bbbselect', 'bbbfrom',
363
            'subplugintables', 'subpluginselects', 'subpluginfroms',
364
        );
365
    }
366
 
367
    /**
368
     * Set the current group id of the activity.
369
     *
370
     * @param int $groupid
371
     */
372
    public function set_group_id(int $groupid): void {
373
        $this->groupid = $groupid;
374
    }
375
 
376
    /**
377
     * Get the current groupid if set.
378
     *
379
     * @return int
380
     */
381
    public function get_group_id(): int {
382
        return empty($this->groupid) ? 0 : $this->groupid;
383
    }
384
 
385
    /**
386
     * Check whether this instance is configured to use a group.
387
     *
388
     * @return bool
389
     */
390
    public function uses_groups(): bool {
391
        $groupmode = groups_get_activity_groupmode($this->get_cm());
392
        return $groupmode != NOGROUPS;
393
    }
394
 
395
    /**
396
     * Get the group name for the current group, if a group has been set.
397
     *
398
     * @return null|string
399
     */
400
    public function get_group_name(): ?string {
401
        $groupid = $this->get_group_id();
402
 
403
        if (!$this->uses_groups()) {
404
            return null;
405
        }
406
 
407
        if ($groupid == 0) {
408
            return get_string('allparticipants');
409
        }
410
 
411
        return format_string(groups_get_group_name($groupid), true, ['context' => $this->get_context()]);
412
    }
413
 
414
    /**
415
     * Get the course object for the instance.
416
     *
417
     * @return stdClass
418
     */
419
    public function get_course(): stdClass {
420
        return $this->course;
421
    }
422
 
423
    /**
424
     * Get the course id of the course that the instance is in.
425
     *
426
     * @return int
427
     */
428
    public function get_course_id(): int {
429
        return $this->course->id;
430
    }
431
 
432
    /**
433
     * Get the cm_info object for the instance.
434
     *
435
     * @return cm_info
436
     */
437
    public function get_cm(): cm_info {
438
        if ($this->cm === null) {
439
            // We do a sort of late binding here as if we call get_cm on a disabled module or in a call stack where
440
            // get_cm was already called, we will get an exception or infinite loop.
441
            $modinfo = get_fast_modinfo($this->course);
442
            $this->cm = $modinfo->get_cm($this->cmid);
443
        }
444
        return $this->cm;
445
    }
446
 
447
    /**
448
     * Get the id of the course module.
449
     *
450
     * @return int
451
     */
452
    public function get_cm_id(): int {
453
        return $this->cmid;
454
    }
455
 
456
    /**
457
     * Get the context.
458
     *
459
     * @return context_module
460
     */
461
    public function get_context(): context_module {
462
        if ($this->context === null) {
463
            $this->context = context_module::instance($this->get_cm()->id);
464
        }
465
 
466
        return $this->context;
467
    }
468
 
469
    /**
470
     * Get the context ID of the module context.
471
     *
472
     * @return int
473
     */
474
    public function get_context_id(): int {
475
        return $this->get_context()->id;
476
    }
477
 
478
    /**
479
     * Get the course context.
480
     *
481
     * @return context_course
482
     */
483
    public function get_course_context(): context_course {
484
        return $this->get_context()->get_course_context();
485
    }
486
 
487
    /**
488
     * Get the big blue button instance data.
489
     *
490
     * @return stdClass
491
     */
492
    public function get_instance_data(): stdClass {
493
        return $this->instancedata;
494
    }
495
 
496
    /**
497
     * Get the instance id.
498
     *
499
     * @return int
500
     */
501
    public function get_instance_id(): int {
502
        return $this->instancedata->id;
503
    }
504
 
505
    /**
506
     * Helper to get an instance var.
507
     *
508
     * @param string $name
509
     * @return mixed|null
510
     */
511
    public function get_instance_var(string $name) {
512
        $instance = $this->get_instance_data();
513
        if (property_exists($instance, $name)) {
514
            return $instance->{$name};
515
        }
516
 
517
        return null;
518
    }
519
 
520
    /**
521
     * Get the meeting id for this meeting.
522
     *
523
     * @param null|int $groupid
524
     * @return string
525
     */
526
    public function get_meeting_id(?int $groupid = null): string {
527
        $baseid = sprintf(
528
            '%s-%s-%s',
529
            $this->get_instance_var('meetingid'),
530
            $this->get_course_id(),
531
            $this->get_instance_var('id')
532
        );
533
 
534
        if ($groupid === null) {
535
            $groupid = $this->get_group_id();
536
        }
537
 
538
        return sprintf('%s[%s]', $baseid, $groupid);
539
    }
540
 
541
    /**
542
     * Get the name of the meeting, considering any group if set.
543
     *
544
     * @return string
545
     */
546
    public function get_meeting_name(): string {
547
        $meetingname = $this->get_instance_var('name');
548
 
549
        $groupname = $this->get_group_name();
550
        if ($groupname !== null) {
551
            $meetingname .= " ({$groupname})";
552
        }
553
 
554
        return $meetingname;
555
    }
556
 
557
    /**
558
     * Get the meeting description with the pluginfile URLs optionally rewritten.
559
     *
560
     * @param bool $rewritepluginfileurls
561
     * @return string
562
     */
563
    public function get_meeting_description(bool $rewritepluginfileurls = false): string {
564
        $description = $this->get_instance_var('intro');
565
 
566
        if ($rewritepluginfileurls) {
567
            $description = file_rewrite_pluginfile_urls(
568
                $description,
569
                'pluginfile.php',
570
                $this->get_context_id(),
571
                'mod_bigbluebuttonbn',
572
                'intro',
573
                null
574
            );
575
        }
576
 
577
        return $description;
578
    }
579
 
580
    /**
581
     * Get the meeting type if set.
582
     *
583
     * @return null|string
584
     */
585
    public function get_type(): ?string {
586
        return $this->get_instance_var('type');
587
    }
588
 
589
    /**
590
     * Whether this instance is includes both a room, and recordings.
591
     *
592
     * @return bool
593
     */
594
    public function is_type_room_and_recordings(): bool {
595
        return $this->get_type() == self::TYPE_ALL;
596
    }
597
 
598
    /**
599
     * Whether this instance is one that only includes a room.
600
     *
601
     * @return bool
602
     */
603
    public function is_type_room_only(): bool {
604
        return $this->get_type() == self::TYPE_ROOM_ONLY;
605
    }
606
 
607
    /**
608
     * Whether this instance is one that only includes recordings.
609
     *
610
     * @return bool
611
     */
612
    public function is_type_recordings_only(): bool {
613
        return $this->get_type() == self::TYPE_RECORDING_ONLY;
614
    }
615
 
616
    /**
617
     * Get the participant list for the session.
618
     *
619
     * @return array
620
     */
621
    public function get_participant_list(): array {
622
        if ($this->participantlist === null) {
623
            $this->participantlist = roles::get_participant_list(
624
                $this->get_instance_data(),
625
                $this->get_context()
626
            );
627
        }
628
 
629
        return $this->participantlist;
630
    }
631
 
632
    /**
633
     * Get the user.
634
     *
635
     * @return stdClass
636
     */
637
    public function get_user(): stdClass {
638
        global $USER;
639
        return $USER;
640
    }
641
 
642
    /**
643
     * Get the id of the user.
644
     *
645
     * @return int
646
     */
647
    public function get_user_id(): int {
648
        $user = $this->get_user();
649
        return $user->id ?? 0;
650
    }
651
 
652
    /**
653
     * Get the fullname of the current user.
654
     *
655
     * @return string
656
     */
657
    public function get_user_fullname(): string {
658
        $user = $this->get_user();
659
        return fullname($user);
660
    }
661
 
662
    /**
663
     * Whether the current user is an administrator.
664
     *
665
     * @return bool
666
     */
667
    public function is_admin(): bool {
668
        global $USER;
669
 
670
        return is_siteadmin($USER->id);
671
    }
672
 
673
    /**
674
     * Whether the user is a session moderator.
675
     *
676
     * @return bool
677
     */
678
    public function is_moderator(): bool {
679
        return roles::is_moderator(
680
            $this->get_context(),
681
            $this->get_participant_list()
682
        );
683
    }
684
 
685
    /**
686
     * Whether this user can join the conference.
687
     *
688
     * This checks the user right for access against capabilities and group membership
689
     *
690
     * @return bool
691
     */
692
    public function can_join(): bool {
693
        $groupid = $this->get_group_id();
694
        $context = $this->get_context();
695
        $inrightgroup =
696
            groups_group_visible($groupid, $this->get_course(), $this->get_cm());
697
        $hascapability = has_capability('moodle/category:manage', $context)
698
            || (has_capability('mod/bigbluebuttonbn:join', $context) && $inrightgroup);
699
        $canjoin = $this->get_type() != self::TYPE_RECORDING_ONLY && $hascapability; // Recording only cannot be joined ever.
700
        return $canjoin;
701
    }
702
 
703
    /**
704
     * Whether this user can manage recordings.
705
     *
706
     * @return bool
707
     */
708
    public function can_manage_recordings(): bool {
709
        // Note: This will include site administrators.
710
        // The has_capability() function returns truthy for admins unless otherwise directed.
711
        return has_capability('mod/bigbluebuttonbn:managerecordings', $this->get_context());
712
    }
713
 
714
    /**
715
     * Whether this user can publish/unpublish/protect/unprotect/delete recordings.
716
     *
717
     * @param string $action
718
     * @return bool
719
     */
720
    public function can_perform_on_recordings($action): bool {
721
        // Note: This will include site administrators.
722
        // The has_capability() function returns truthy for admins unless otherwise directed.
723
        return has_capability("mod/bigbluebuttonbn:{$action}recordings", $this->get_context());
724
    }
725
 
726
    /**
727
     * Get the configured user limit.
728
     *
729
     * @return int
730
     */
731
    public function get_user_limit(): int {
732
        if ((boolean) config::get('userlimit_editable')) {
733
            return intval($this->get_instance_var('userlimit'));
734
        }
735
 
736
        return intval((int) config::get('userlimit_default'));
737
    }
738
 
739
    /**
740
     * Check whether the user limit has been reached.
741
     *
742
     * @param int $currentusercount The user count to check
743
     * @return bool
744
     */
745
    public function has_user_limit_been_reached(int $currentusercount): bool {
746
        $userlimit = $this->get_user_limit();
747
        if (empty($userlimit)) {
748
            return false;
749
        }
750
 
751
        return $currentusercount >= $userlimit;
752
    }
753
 
754
    /**
755
     * Check whether the current user counts towards the user limit.
756
     *
757
     * @return bool
758
     */
759
    public function does_current_user_count_towards_user_limit(): bool {
760
        if ($this->is_admin()) {
761
            return false;
762
        }
763
 
764
        if ($this->is_moderator()) {
765
            return false;
766
        }
767
 
768
        return true;
769
    }
770
 
771
    /**
772
     * Get the voice bridge details.
773
     *
774
     * @return null|int
775
     */
776
    public function get_voice_bridge(): ?int {
777
        $voicebridge = (int) $this->get_instance_var('voicebridge');
778
        if ($voicebridge > 0) {
779
            return 70000 + $voicebridge;
780
        }
781
 
782
        return null;
783
    }
784
 
785
    /**
786
     * Whether participants are muted on entry.
787
     *
788
     * @return bool
789
     */
790
    public function get_mute_on_start(): bool {
791
        return $this->get_instance_var('muteonstart');
792
    }
793
 
794
    /**
795
     * Get the moderator password.
796
     *
797
     * @return string
798
     */
799
    public function get_moderator_password(): string {
800
        return $this->get_instance_var('moderatorpass');
801
    }
802
 
803
    /**
804
     * Get the viewer password.
805
     *
806
     * @return string
807
     */
808
    public function get_viewer_password(): string {
809
        return $this->get_instance_var('viewerpass');
810
    }
811
 
812
    /**
813
     * Get the appropriate password for the current user.
814
     *
815
     * @return string
816
     */
817
    public function get_current_user_password(): string {
818
        if ($this->is_admin() || $this->is_moderator()) {
819
            return $this->get_moderator_password();
820
        }
821
 
822
        return $this->get_viewer_password();
823
    }
824
 
825
    /**
826
     * Get the appropriate designated role for the current user.
827
     *
828
     * @return string
829
     */
830
    public function get_current_user_role(): string {
831
        if ($this->is_admin() || $this->is_moderator()) {
832
            return 'MODERATOR';
833
        }
834
 
835
        return 'VIEWER';
836
    }
837
 
838
    /**
839
     * Whether to show the recording button
840
     *
841
     * @return bool
842
     */
843
    public function should_show_recording_button(): bool {
844
        global $CFG;
845
        if (!empty($CFG->bigbluebuttonbn_recording_hide_button_editable)) {
846
            $recordhidebutton = (bool) $this->get_instance_var('recordhidebutton');
847
            $recordallfromstart = (bool) $this->get_instance_var('recordallfromstart');
848
            return !($recordhidebutton || $recordallfromstart);
849
        }
850
 
851
        return !$CFG->bigbluebuttonbn_recording_hide_button_default;
852
    }
853
 
854
    /**
855
     * Whether this instance is recorded.
856
     *
857
     * @return bool
858
     */
859
    public function is_recorded(): bool {
860
        return (bool) $this->get_instance_var('record');
861
    }
862
 
863
    /**
864
     * Moderator approval required ?
865
     *
866
     * By default we leave it as false as "ALWAYS_ACCEPT" is the default value for
867
     * the guestPolicy create parameter (https://docs.bigbluebutton.org/dev/api.html)
868
     * @return bool
869
     */
870
    public function is_moderator_approval_required(): bool {
871
        return $this->get_instance_var('mustapproveuser') ?? false;
872
    }
873
    /**
874
     * Whether this instance can import recordings from another instance.
875
     *
876
     * @return bool
877
     */
878
    public function can_import_recordings(): bool {
879
        if (!config::get('importrecordings_enabled')) {
880
            return false;
881
        }
882
        if (!$this->can_manage_recordings()) {
883
            return false;
884
        }
885
 
886
        return $this->is_feature_enabled('importrecordings');
887
    }
888
 
889
    /**
890
     * Get recordings_imported from instancedata.
891
     *
892
     * @return bool
893
     */
894
    public function get_recordings_imported(): bool {
895
        if (config::get('recordings_imported_editable')) {
896
            return (bool) $this->get_instance_var('recordings_imported');
897
        }
898
        return config::get('recordings_imported_default');
899
    }
900
 
901
    /**
902
     * Whether this instance is recorded from the start.
903
     *
904
     * @return bool
905
     */
906
    public function should_record_from_start(): bool {
907
        if (!$this->is_recorded()) {
908
            // This meeting is not recorded.
909
            return false;
910
        }
911
 
912
        return (bool) $this->get_instance_var('recordallfromstart');
913
    }
914
 
915
    /**
916
     * Whether recording can be started and stopped.
917
     *
918
     * @return bool
919
     */
920
    public function allow_recording_start_stop(): bool {
921
        if (!$this->is_recorded()) {
922
            // If the meeting is not configured for recordings, do not allow it to be recorded.
923
            return false;
924
        }
925
 
926
        return $this->should_show_recording_button();
927
    }
928
 
929
    /**
930
     * Get the welcome message to display.
931
     *
932
     * @return string
933
     */
934
    public function get_welcome_message(): string {
935
        $welcomestring = $this->get_instance_var('welcome');
936
        if (!config::get('welcome_editable') || empty($welcomestring)) {
937
            $welcomestring = config::get('welcome_default');
938
        }
939
        if (empty($welcomestring)) {
940
            $welcomestring = get_string('mod_form_field_welcome_default', 'bigbluebuttonbn');
941
        }
942
 
943
        $welcome = [$welcomestring];
944
 
945
        if ($this->is_recorded()) {
946
            if ($this->should_record_from_start()) {
947
                $welcome[] = get_string('bbbrecordallfromstartwarning', 'bigbluebuttonbn');
948
            } else {
949
                $welcome[] = get_string('bbbrecordwarning', 'bigbluebuttonbn');
950
            }
951
        }
952
 
953
        return implode('<br><br>', $welcome);
954
    }
955
 
956
    /**
957
     * Get the presentation data for internal use.
958
     *
959
     * The URL returned for the presentation will be accessible through moodle with checks about user being logged in.
960
     *
961
     * @return array|null
962
     */
963
    public function get_presentation(): ?array {
964
        return $this->do_get_presentation_with_nonce(false);
965
    }
966
 
967
    /**
968
     * Get the presentation data for external API url.
969
     *
970
     * The URL returned for the presentation will be accessible publicly but once and with a specific URL.
971
     *
972
     * @return array|null
973
     */
974
    public function get_presentation_for_bigbluebutton_upload(): ?array {
975
        return $this->do_get_presentation_with_nonce(true);
976
    }
977
 
978
    /**
979
     * Generate Presentation URL.
980
     *
981
     * @param bool $withnonce The generated url will have a nonce included
982
     * @return array|null
983
     */
984
    protected function do_get_presentation_with_nonce(bool $withnonce): ?array {
985
        if ($this->has_ended()) {
986
            return files::get_presentation(
987
                $this->get_context(),
988
                $this->get_instance_var('presentation'),
989
                null,
990
                $withnonce
991
            );
992
        } else if ($this->is_currently_open()) {
993
            return files::get_presentation(
994
                $this->get_context(),
995
                $this->get_instance_var('presentation'),
996
                $this->get_instance_id(),
997
                $withnonce
998
            );
999
        } else {
1000
            return [];
1001
        }
1002
    }
1003
 
1004
    /**
1005
     * Whether the current time is before the scheduled start time.
1006
     *
1007
     * @return bool
1008
     */
1009
    public function before_start_time(): bool {
1010
        $openingtime = $this->get_instance_var('openingtime');
1011
        if (empty($openingtime)) {
1012
            return false;
1013
        }
1014
 
1015
        return $openingtime >= time();
1016
    }
1017
 
1018
    /**
1019
     * Whether the meeting time has passed.
1020
     *
1021
     * @return bool
1022
     */
1023
    public function has_ended(): bool {
1024
        $closingtime = $this->get_instance_var('closingtime');
1025
        if (empty($closingtime)) {
1026
            return false;
1027
        }
1028
 
1029
        return $closingtime < time();
1030
    }
1031
 
1032
    /**
1033
     * Whether this session is currently open.
1034
     *
1035
     * @return bool
1036
     */
1037
    public function is_currently_open(): bool {
1038
        if ($this->before_start_time()) {
1039
            return false;
1040
        }
1041
 
1042
        if ($this->has_ended()) {
1043
            return false;
1044
        }
1045
 
1046
        return true;
1047
    }
1048
 
1049
    /**
1050
     * Whether the user must wait to join the session.
1051
     *
1052
     * @return bool
1053
     */
1054
    public function user_must_wait_to_join(): bool {
1055
        if ($this->is_admin() || $this->is_moderator()) {
1056
            return false;
1057
        }
1058
 
1059
        return (bool) $this->get_instance_var('wait');
1060
    }
1061
 
1062
    /**
1063
     * Whether the user can force join in all cases
1064
     *
1065
     * @return bool
1066
     */
1067
    public function user_can_force_join(): bool {
1068
        return $this->is_admin() || $this->is_moderator();
1069
    }
1070
 
1071
    /**
1072
     * Whether the user can end a meeting
1073
     *
1074
     * @return bool
1075
     */
1076
    public function user_can_end_meeting(): bool {
1077
        return $this->is_admin() || $this->is_moderator();
1078
    }
1079
 
1080
    /**
1081
     * Get information about the origin.
1082
     *
1083
     * @return stdClass
1084
     */
1085
    public function get_origin_data(): stdClass {
1086
        global $CFG;
1087
 
1088
        $parsedurl = parse_url($CFG->wwwroot);
1089
        return (object) [
1090
            'origin' => 'Moodle',
1091
            'originVersion' => $CFG->release,
1092
            'originServerName' => $parsedurl['host'],
1093
            'originServerUrl' => $CFG->wwwroot,
1094
            'originServerCommonName' => '',
1095
            'originTag' => sprintf('moodle-mod_bigbluebuttonbn (%s)', get_config('mod_bigbluebuttonbn', 'version')),
1096
        ];
1097
    }
1098
 
1099
    /**
1100
     * Whether this is a server belonging to blindside networks.
1101
     *
1102
     * @return bool
1103
     */
1104
    public function is_blindside_network_server(): bool {
1105
        return bigbluebutton_proxy::is_bn_server();
1106
    }
1107
 
1108
    /**
1109
     * Get the URL used to access the course that the instance is in.
1110
     *
1111
     * @return moodle_url
1112
     */
1113
    public function get_course_url(): moodle_url {
1114
        return new moodle_url('/course/view.php', ['id' => $this->get_course_id()]);
1115
    }
1116
 
1117
    /**
1118
     * Get the URL used to view the instance as a user.
1119
     *
1120
     * @return moodle_url
1121
     */
1122
    public function get_view_url(): moodle_url {
1123
        return new moodle_url('/mod/bigbluebuttonbn/view.php', [
1124
            'id' => $this->get_cm()->id,
1125
        ]);
1126
    }
1127
 
1128
    /**
1129
     * Get the logout URL used to log out of the meeting.
1130
     *
1131
     * @return moodle_url
1132
     */
1133
    public function get_logout_url(): moodle_url {
1134
        return new moodle_url('/mod/bigbluebuttonbn/bbb_view.php', [
1135
            'action' => 'logout',
1136
            'id' => $this->get_cm()->id,
1137
            'courseid' => $this->get_cm()->course // Used to find the course if ever the activity is deleted
1138
            // while the meeting is running.
1139
        ]);
1140
    }
1141
 
1142
    /**
1143
     * Get the URL that the remote server will use to notify that the recording is ready.
1144
     *
1145
     * @return moodle_url
1146
     */
1147
    public function get_record_ready_url(): moodle_url {
1148
        return new moodle_url('/mod/bigbluebuttonbn/bbb_broker.php', [
1149
            'action' => 'recording_ready',
1150
            'bigbluebuttonbn' => $this->instancedata->id,
1151
        ]);
1152
    }
1153
 
1154
    /**
1155
     * Get the URL that the remote server will use to notify of meeting events.
1156
     *
1157
     * @return moodle_url
1158
     */
1159
    public function get_meeting_event_notification_url(): moodle_url {
1160
        return new moodle_url('/mod/bigbluebuttonbn/bbb_broker.php', [
1161
            'action' => 'meeting_events',
1162
            'bigbluebuttonbn' => $this->instancedata->id,
1163
        ]);
1164
    }
1165
 
1166
    /**
1167
     * Get the URL used to join a meeting.
1168
     *
1169
     * @return moodle_url
1170
     */
1171
    public function get_join_url(): moodle_url {
1172
        return new moodle_url('/mod/bigbluebuttonbn/bbb_view.php', [
1173
            'action' => 'join',
1174
            'id' => $this->get_cm()->id,
1175
            'bn' => $this->instancedata->id,
1176
        ]);
1177
    }
1178
 
1179
    /**
1180
     * Get the URL used for the import page.
1181
     *
1182
     * @return moodle_url
1183
     */
1184
    public function get_import_url(): moodle_url {
1185
        return new moodle_url('/mod/bigbluebuttonbn/import_view.php', [
1186
            'destbn' => $this->instancedata->id,
1187
        ]);
1188
    }
1189
 
1190
    /**
1191
     * Get the list of enabled features for this instance.
1192
     *
1193
     * @return array
1194
     */
1195
    public function get_enabled_features(): array {
1196
        return config::get_enabled_features(
1197
            bigbluebutton_proxy::get_instance_type_profiles(),
1198
            $this->get_instance_var('type') ?? null
1199
        );
1200
    }
1201
 
1202
    /**
1203
     * Check whetherthe named features is enabled.
1204
     *
1205
     * @param string $feature
1206
     * @return bool
1207
     */
1208
    public function is_feature_enabled(string $feature): bool {
1209
        $features = $this->get_enabled_features();
1210
 
1211
        return !empty($features[$feature]);
1212
    }
1213
 
1214
    /**
1215
     * Check if meeting is recorded.
1216
     *
1217
     * @return bool
1218
     */
1219
    public function should_record() {
1220
        return (boolean) config::recordings_enabled() && $this->is_recorded();
1221
    }
1222
 
1223
    /**
1224
     * Get recordings for this instance
1225
     *
1226
     * @param string[] $excludedid
1227
     * @param bool $viewdeleted view deleted recordings ?
1228
     * @return recording[]
1229
     */
1230
    public function get_recordings(array $excludedid = [], bool $viewdeleted = false): array {
1231
        // Fetch the list of recordings depending on the status of the instance.
1232
        // show room is enabled for TYPE_ALL and TYPE_ROOM_ONLY.
1233
        if ($this->is_feature_enabled('showroom')) {
1234
            // Not in the import page.
1235
            return recording::get_recordings_for_instance(
1236
                $this,
1237
                $this->is_feature_enabled('importrecordings'),
1238
                $this->get_instance_var('recordings_imported'),
1239
            );
1240
        }
1241
        // We show all recording from this course as this is TYPE_RECORDING.
1242
        return recording::get_recordings_for_course(
1243
            $this->get_course_id(),
1244
            $excludedid,
1245
            $this->is_feature_enabled('importrecordings'),
1246
            false,
1247
            $viewdeleted
1248
        );
1249
    }
1250
 
1251
    /**
1252
     * Check if this is a valid group for this user/instance,
1253
     *
1254
     *
1255
     * @param stdClass $user
1256
     * @param int $groupid
1257
     * @return bool
1258
     */
1259
    public function user_has_group_access($user, $groupid) {
1260
        $cm = $this->get_cm();
1261
        $context = $this->get_context();
1262
        // Then validate group.
1263
        $groupmode = groups_get_activity_groupmode($cm);
1264
        if ($groupmode && $groupid) {
1265
            $accessallgroups = has_capability('moodle/site:accessallgroups', $context);
1266
            if ($accessallgroups || $groupmode == VISIBLEGROUPS) {
1267
                $allowedgroups = groups_get_all_groups($cm->course, 0, $cm->groupingid);
1268
            } else {
1269
                $allowedgroups = groups_get_all_groups($cm->course, $user->id, $cm->groupingid);
1270
            }
1271
            if (!array_key_exists($groupid, $allowedgroups)) {
1272
                return false;
1273
            }
1274
            if (!groups_group_visible($groupid, $this->get_course(), $this->get_cm())) {
1275
                return false;
1276
            }
1277
        }
1278
        return true;
1279
    }
1280
 
1281
    /**
1282
     * Get current guest link url
1283
     *
1284
     * @return moodle_url
1285
     */
1286
    public function get_guest_access_url(): moodle_url {
1287
        $guestlinkuid = $this->get_instance_var('guestlinkuid');
1288
        if (empty($guestlinkuid)) {
1289
            $this->generate_guest_credentials();
1290
            $guestlinkuid = $this->get_instance_var('guestlinkuid');
1291
        }
1292
        return new moodle_url('/mod/bigbluebuttonbn/guest.php', ['uid' => $guestlinkuid]);
1293
    }
1294
 
1295
    /**
1296
     * Is guest access allowed in this instance.
1297
     *
1298
     * @return bool
1299
     */
1300
    public function is_guest_allowed(): bool {
1301
        return !$this->is_type_recordings_only() &&
1302
                config::get('guestaccess_enabled') && $this->get_instance_var('guestallowed');
1303
    }
1304
 
1305
    /**
1306
     * Get current meeting password
1307
     *
1308
     * @return string
1309
     */
1310
    public function get_guest_access_password(): string {
1311
        $guestpassword = $this->get_instance_var('guestpassword');
1312
        if (empty($guestpassword)) {
1313
            $this->generate_guest_credentials();
1314
            $guestpassword = $this->get_instance_var('guestpassword');
1315
        }
1316
        return $guestpassword;
1317
    }
1318
 
1319
    /**
1320
     * Generate credentials for this instance and persist the value in the database
1321
     *
1322
     * @return void
1323
     */
1324
    private function generate_guest_credentials(): void {
1325
        global $DB;
1326
        [$this->instancedata->guestlinkuid, $this->instancedata->guestpassword] =
1327
            \mod_bigbluebuttonbn\plugin::generate_guest_meeting_credentials();
1328
        $DB->update_record('bigbluebuttonbn', $this->instancedata);
1329
    }
1330
 
1331
    /**
1332
     * Is this meeting configured to display avatars of the users ?
1333
     *
1334
     * Note: this is for now a global setting.
1335
     *
1336
     * @return bool
1337
     */
1338
    public function is_profile_picture_enabled(): bool {
1339
        return (bool) config::get('profile_picture_enabled');
1340
    }
1341
}