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