Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.

Differences Between: [Versions 400 and 401] [Versions 400 and 402] [Versions 400 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  
 540          return $USER;
 541      }
 542  
 543      /**
 544       * Get the id of the user.
 545       *
 546       * @return int
 547       */
 548      public function get_user_id(): int {
 549          $user = $this->get_user();
 550  
 551          return $user->id;
 552      }
 553  
 554      /**
 555       * Get the fullname of the current user.
 556       *
 557       * @return string
 558       */
 559      public function get_user_fullname(): string {
 560          $user = $this->get_user();
 561  
 562          return fullname($user);
 563      }
 564  
 565      /**
 566       * Whether the current user is an administrator.
 567       *
 568       * @return bool
 569       */
 570      public function is_admin(): bool {
 571          global $USER;
 572  
 573          return is_siteadmin($USER->id);
 574      }
 575  
 576      /**
 577       * Whether the user is a session moderator.
 578       *
 579       * @return bool
 580       */
 581      public function is_moderator(): bool {
 582          return roles::is_moderator(
 583              $this->get_context(),
 584              $this->get_participant_list()
 585          );
 586      }
 587  
 588      /**
 589       * Whether this user can join the conference.
 590       *
 591       * This checks the user right for access against capabilities and group membership
 592       *
 593       * @return bool
 594       */
 595      public function can_join(): bool {
 596          $groupid = $this->get_group_id();
 597          $context = $this->get_context();
 598          $inrightgroup =
 599              groups_group_visible($groupid, $this->get_course(), $this->get_cm());
 600          $hascapability = has_capability('moodle/category:manage', $context)
 601              || (has_capability('mod/bigbluebuttonbn:join', $context) && $inrightgroup);
 602          $canjoin = $this->get_type() != self::TYPE_RECORDING_ONLY && $hascapability; // Recording only cannot be joined ever.
 603          return $canjoin;
 604      }
 605  
 606      /**
 607       * Whether this user can manage recordings.
 608       *
 609       * @return bool
 610       */
 611      public function can_manage_recordings(): bool {
 612          // Note: This will include site administrators.
 613          // The has_capability() function returns truthy for admins unless otherwise directed.
 614          return has_capability('mod/bigbluebuttonbn:managerecordings', $this->get_context());
 615      }
 616  
 617      /**
 618       * Whether this user can publish/unpublish/protect/unprotect/delete recordings.
 619       *
 620       * @param string $action
 621       * @return bool
 622       */
 623      public function can_perform_on_recordings($action): bool {
 624          // Note: This will include site administrators.
 625          // The has_capability() function returns truthy for admins unless otherwise directed.
 626          return has_capability("mod/bigbluebuttonbn:{$action}recordings", $this->get_context());
 627      }
 628  
 629      /**
 630       * Get the configured user limit.
 631       *
 632       * @return int
 633       */
 634      public function get_user_limit(): int {
 635          if ((boolean) config::get('userlimit_editable')) {
 636              return intval($this->get_instance_var('userlimit'));
 637          }
 638  
 639          return intval((int) config::get('userlimit_default'));
 640      }
 641  
 642      /**
 643       * Check whether the user limit has been reached.
 644       *
 645       * @param int $currentusercount The user count to check
 646       * @return bool
 647       */
 648      public function has_user_limit_been_reached(int $currentusercount): bool {
 649          $userlimit = $this->get_user_limit();
 650          if (empty($userlimit)) {
 651              return false;
 652          }
 653  
 654          return $currentusercount >= $userlimit;
 655      }
 656  
 657      /**
 658       * Check whether the current user counts towards the user limit.
 659       *
 660       * @return bool
 661       */
 662      public function does_current_user_count_towards_user_limit(): bool {
 663          if ($this->is_admin()) {
 664              return false;
 665          }
 666  
 667          if ($this->is_moderator()) {
 668              return false;
 669          }
 670  
 671          return true;
 672      }
 673  
 674      /**
 675       * Get the voice bridge details.
 676       *
 677       * @return null|int
 678       */
 679      public function get_voice_bridge(): ?int {
 680          $voicebridge = (int) $this->get_instance_var('voicebridge');
 681          if ($voicebridge > 0) {
 682              return 70000 + $voicebridge;
 683          }
 684  
 685          return null;
 686      }
 687  
 688      /**
 689       * Whether participants are muted on entry.
 690       *
 691       * @return bool
 692       */
 693      public function get_mute_on_start(): bool {
 694          return $this->get_instance_var('muteonstart');
 695      }
 696  
 697      /**
 698       * Get the moderator password.
 699       *
 700       * @return string
 701       */
 702      public function get_moderator_password(): string {
 703          return $this->get_instance_var('moderatorpass');
 704      }
 705  
 706      /**
 707       * Get the viewer password.
 708       *
 709       * @return string
 710       */
 711      public function get_viewer_password(): string {
 712          return $this->get_instance_var('viewerpass');
 713      }
 714  
 715      /**
 716       * Get the appropriate password for the current user.
 717       *
 718       * @return string
 719       */
 720      public function get_current_user_password(): string {
 721          if ($this->is_admin() || $this->is_moderator()) {
 722              return $this->get_moderator_password();
 723          }
 724  
 725          return $this->get_viewer_password();
 726      }
 727  
 728      /**
 729       * Get the appropriate designated role for the current user.
 730       *
 731       * @return string
 732       */
 733      public function get_current_user_role(): string {
 734          if ($this->is_admin() || $this->is_moderator()) {
 735              return 'MODERATOR';
 736          }
 737  
 738          return 'VIEWER';
 739      }
 740  
 741      /**
 742       * Whether to show the recording button
 743       *
 744       * @return bool
 745       */
 746      public function should_show_recording_button(): bool {
 747          global $CFG;
 748          if (!empty($CFG->bigbluebuttonbn_recording_hide_button_editable)) {
 749              $recordhidebutton = (bool) $this->get_instance_var('recordhidebutton');
 750              $recordallfromstart = (bool) $this->get_instance_var('recordallfromstart');
 751              return !($recordhidebutton || $recordallfromstart);
 752          }
 753  
 754          return !$CFG->bigbluebuttonbn_recording_hide_button_default;
 755      }
 756  
 757      /**
 758       * Whether this instance is recorded.
 759       *
 760       * @return bool
 761       */
 762      public function is_recorded(): bool {
 763          return (bool) $this->get_instance_var('record');
 764      }
 765  
 766      /**
 767       * Whether this instance can import recordings from another instance.
 768       *
 769       * @return bool
 770       */
 771      public function can_import_recordings(): bool {
 772          if (!config::get('importrecordings_enabled')) {
 773              return false;
 774          }
 775          if ($this->can_manage_recordings()) {
 776              return true;
 777          }
 778  
 779          return $this->is_feature_enabled('importrecordings');
 780      }
 781  
 782      /**
 783       * Get recordings_imported from instancedata.
 784       *
 785       * @return bool
 786       */
 787      public function get_recordings_imported(): bool {
 788          if (config::get('recordings_imported_editable')) {
 789              return (bool) $this->get_instance_var('recordings_imported');
 790          }
 791          return config::get('recordings_imported_default');
 792      }
 793  
 794      /**
 795       * Whether this instance is recorded from the start.
 796       *
 797       * @return bool
 798       */
 799      public function should_record_from_start(): bool {
 800          if (!$this->is_recorded()) {
 801              // This meeting is not recorded.
 802              return false;
 803          }
 804  
 805          return (bool) $this->get_instance_var('recordallfromstart');
 806      }
 807  
 808      /**
 809       * Whether recording can be started and stopped.
 810       *
 811       * @return bool
 812       */
 813      public function allow_recording_start_stop(): bool {
 814          if (!$this->is_recorded()) {
 815              // If the meeting is not configured for recordings, do not allow it to be recorded.
 816              return false;
 817          }
 818  
 819          return $this->should_show_recording_button();
 820      }
 821  
 822      /**
 823       * Get the welcome message to display.
 824       *
 825       * @return string
 826       */
 827      public function get_welcome_message(): string {
 828          $welcomestring = $this->get_instance_var('welcome');
 829          if (!config::get('welcome_editable') || empty($welcomestring)) {
 830              $welcomestring = config::get('welcome_default');
 831          }
 832          if (empty($welcomestring)) {
 833              $welcomestring = get_string('mod_form_field_welcome_default', 'bigbluebuttonbn');
 834          }
 835  
 836          $welcome = [$welcomestring];
 837  
 838          if ($this->is_recorded()) {
 839              if ($this->should_record_from_start()) {
 840                  $welcome[] = get_string('bbbrecordallfromstartwarning', 'bigbluebuttonbn');
 841              } else {
 842                  $welcome[] = get_string('bbbrecordwarning', 'bigbluebuttonbn');
 843              }
 844          }
 845  
 846          return implode('<br><br>', $welcome);
 847      }
 848  
 849      /**
 850       * Get the presentation data for internal use.
 851       *
 852       * The URL returned for the presentation will be accessible through moodle with checks about user being logged in.
 853       *
 854       * @return array|null
 855       */
 856      public function get_presentation(): ?array {
 857          return $this->do_get_presentation_with_nonce(false);
 858      }
 859  
 860      /**
 861       * Get the presentation data for external API url.
 862       *
 863       * The URL returned for the presentation will be accessible publicly but once and with a specific URL.
 864       *
 865       * @return array|null
 866       */
 867      public function get_presentation_for_bigbluebutton_upload(): ?array {
 868          return $this->do_get_presentation_with_nonce(true);
 869      }
 870  
 871      /**
 872       * Generate Presentation URL.
 873       *
 874       * @param bool $withnonce The generated url will have a nonce included
 875       * @return array|null
 876       */
 877      protected function do_get_presentation_with_nonce(bool $withnonce): ?array {
 878          if ($this->has_ended()) {
 879              return files::get_presentation(
 880                  $this->get_context(),
 881                  $this->get_instance_var('presentation'),
 882                  null,
 883                  $withnonce
 884              );
 885          } else if ($this->is_currently_open()) {
 886              return files::get_presentation(
 887                  $this->get_context(),
 888                  $this->get_instance_var('presentation'),
 889                  $this->get_instance_id(),
 890                  $withnonce
 891              );
 892          } else {
 893              return [];
 894          }
 895      }
 896  
 897      /**
 898       * Whether the current time is before the scheduled start time.
 899       *
 900       * @return bool
 901       */
 902      public function before_start_time(): bool {
 903          $openingtime = $this->get_instance_var('openingtime');
 904          if (empty($openingtime)) {
 905              return false;
 906          }
 907  
 908          return $openingtime >= time();
 909      }
 910  
 911      /**
 912       * Whether the meeting time has passed.
 913       *
 914       * @return bool
 915       */
 916      public function has_ended(): bool {
 917          $closingtime = $this->get_instance_var('closingtime');
 918          if (empty($closingtime)) {
 919              return false;
 920          }
 921  
 922          return $closingtime < time();
 923      }
 924  
 925      /**
 926       * Whether this session is currently open.
 927       *
 928       * @return bool
 929       */
 930      public function is_currently_open(): bool {
 931          if ($this->before_start_time()) {
 932              return false;
 933          }
 934  
 935          if ($this->has_ended()) {
 936              return false;
 937          }
 938  
 939          return true;
 940      }
 941  
 942      /**
 943       * Whether the user must wait to join the session.
 944       *
 945       * @return bool
 946       */
 947      public function user_must_wait_to_join(): bool {
 948          if ($this->is_admin() || $this->is_moderator()) {
 949              return false;
 950          }
 951  
 952          return (bool) $this->get_instance_var('wait');
 953      }
 954  
 955      /**
 956       * Whether the user can force join in all cases
 957       *
 958       * @return bool
 959       */
 960      public function user_can_force_join(): bool {
 961          return $this->is_admin() || $this->is_moderator();
 962      }
 963  
 964      /**
 965       * Whether the user can end a meeting
 966       *
 967       * @return bool
 968       */
 969      public function user_can_end_meeting(): bool {
 970          return $this->is_admin() || $this->is_moderator();
 971      }
 972  
 973      /**
 974       * Get information about the origin.
 975       *
 976       * @return stdClass
 977       */
 978      public function get_origin_data(): stdClass {
 979          global $CFG;
 980  
 981          $parsedurl = parse_url($CFG->wwwroot);
 982          return (object) [
 983              'origin' => 'Moodle',
 984              'originVersion' => $CFG->release,
 985              'originServerName' => $parsedurl['host'],
 986              'originServerUrl' => $CFG->wwwroot,
 987              'originServerCommonName' => '',
 988              'originTag' => sprintf('moodle-mod_bigbluebuttonbn (%s)', get_config('mod_bigbluebuttonbn', 'version')),
 989          ];
 990      }
 991  
 992      /**
 993       * Whether this is a server belonging to blindside networks.
 994       *
 995       * @return bool
 996       */
 997      public function is_blindside_network_server(): bool {
 998          return bigbluebutton_proxy::is_bn_server();
 999      }
1000  
1001      /**
1002       * Get the URL used to access the course that the instance is in.
1003       *
1004       * @return moodle_url
1005       */
1006      public function get_course_url(): moodle_url {
1007          return new moodle_url('/course/view.php', ['id' => $this->get_course_id()]);
1008      }
1009  
1010      /**
1011       * Get the URL used to view the instance as a user.
1012       *
1013       * @return moodle_url
1014       */
1015      public function get_view_url(): moodle_url {
1016          return new moodle_url('/mod/bigbluebuttonbn/view.php', [
1017              'id' => $this->cm->id,
1018          ]);
1019      }
1020  
1021      /**
1022       * Get the logout URL used to log out of the meeting.
1023       *
1024       * @return moodle_url
1025       */
1026      public function get_logout_url(): moodle_url {
1027          return new moodle_url('/mod/bigbluebuttonbn/bbb_view.php', [
1028              'action' => 'logout',
1029              'id' => $this->cm->id,
1030          ]);
1031      }
1032  
1033      /**
1034       * Get the URL that the remote server will use to notify that the recording is ready.
1035       *
1036       * @return moodle_url
1037       */
1038      public function get_record_ready_url(): moodle_url {
1039          return new moodle_url('/mod/bigbluebuttonbn/bbb_broker.php', [
1040              'action' => 'recording_ready',
1041              'bigbluebuttonbn' => $this->instancedata->id,
1042          ]);
1043      }
1044  
1045      /**
1046       * Get the URL that the remote server will use to notify of meeting events.
1047       *
1048       * @return moodle_url
1049       */
1050      public function get_meeting_event_notification_url(): moodle_url {
1051          return new moodle_url('/mod/bigbluebuttonbn/bbb_broker.php', [
1052              'action' => 'meeting_events',
1053              'bigbluebuttonbn' => $this->instancedata->id,
1054          ]);
1055      }
1056  
1057      /**
1058       * Get the URL used to join a meeting.
1059       *
1060       * @return moodle_url
1061       */
1062      public function get_join_url(): moodle_url {
1063          return new moodle_url('/mod/bigbluebuttonbn/bbb_view.php', [
1064              'action' => 'join',
1065              'id' => $this->cm->id,
1066              'bn' => $this->instancedata->id,
1067          ]);
1068      }
1069  
1070      /**
1071       * Get the URL used for the import page.
1072       *
1073       * @return moodle_url
1074       */
1075      public function get_import_url(): moodle_url {
1076          return new moodle_url('/mod/bigbluebuttonbn/import_view.php', [
1077              'destbn' => $this->instancedata->id,
1078          ]);
1079      }
1080  
1081      /**
1082       * Get the list of enabled features for this instance.
1083       *
1084       * @return array
1085       */
1086      public function get_enabled_features(): array {
1087          return config::get_enabled_features(
1088              bigbluebutton_proxy::get_instance_type_profiles(),
1089              $this->get_instance_var('type') ?? null
1090          );
1091      }
1092  
1093      /**
1094       * Check whetherthe named features is enabled.
1095       *
1096       * @param string $feature
1097       * @return bool
1098       */
1099      public function is_feature_enabled(string $feature): bool {
1100          $features = $this->get_enabled_features();
1101  
1102          return !empty($features[$feature]);
1103      }
1104  
1105      /**
1106       * Check if meeting is recorded.
1107       *
1108       * @return bool
1109       */
1110      public function should_record() {
1111          return (boolean) config::recordings_enabled() && $this->is_recorded();
1112      }
1113  
1114      /**
1115       * Get recordings for this instance
1116       *
1117       * @param string[] $excludedid
1118       * @param bool $viewdeleted view deleted recordings ?
1119       * @return recording[]
1120       */
1121      public function get_recordings(array $excludedid = [], bool $viewdeleted = false): array {
1122          // Fetch the list of recordings depending on the status of the instance.
1123          // show room is enabled for TYPE_ALL and TYPE_ROOM_ONLY.
1124          if ($this->is_feature_enabled('showroom')) {
1125              // Not in the import page.
1126              return recording::get_recordings_for_instance(
1127                  $this,
1128                  $this->is_feature_enabled('importrecordings'),
1129                  $this->get_instance_var('recordings_imported'),
1130              );
1131          }
1132          // We show all recording from this course as this is TYPE_RECORDING.
1133          return recording::get_recordings_for_course(
1134              $this->get_course_id(),
1135              $excludedid,
1136              $this->is_feature_enabled('importrecordings'),
1137              false,
1138              $viewdeleted
1139          );
1140      }
1141  
1142      /**
1143       * Check if this is a valid group for this user/instance,
1144       *
1145       *
1146       * @param stdClass $user
1147       * @param int $groupid
1148       * @return bool
1149       */
1150      public function user_has_group_access($user, $groupid) {
1151          $cm = $this->get_cm();
1152          $context = $this->get_context();
1153          // Then validate group.
1154          $groupmode = groups_get_activity_groupmode($cm);
1155          if ($groupmode && $groupid) {
1156              $accessallgroups = has_capability('moodle/site:accessallgroups', $context);
1157              if ($accessallgroups || $groupmode == VISIBLEGROUPS) {
1158                  $allowedgroups = groups_get_all_groups($cm->course, 0, $cm->groupingid);
1159              } else {
1160                  $allowedgroups = groups_get_all_groups($cm->course, $user->id, $cm->groupingid);
1161              }
1162              if (!array_key_exists($groupid, $allowedgroups)) {
1163                  return false;
1164              }
1165              if (!groups_group_visible($groupid, $this->get_course(), $this->get_cm())) {
1166                  return false;
1167              }
1168          }
1169          return true;
1170      }
1171  }