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