first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
@@ -0,0 +1,324 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* The mod_bigbluebuttonbn files helper
*
* @package mod_bigbluebuttonbn
* @copyright 2021 onwards, Blindside Networks Inc
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @author Laurent David (laurent [at] call-learning [dt] fr)
*/
namespace mod_bigbluebuttonbn\local\helpers;
use cache;
use cache_store;
use context;
use context_module;
use context_system;
use mod_bigbluebuttonbn\instance;
use moodle_url;
use stdClass;
/**
* Utility class for all files routines helper
*
* @package mod_bigbluebuttonbn
* @copyright 2021 onwards, Blindside Networks Inc
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class files {
/**
* Helper for validating pluginfile.
*
* @param stdClass $context context object
* @param string $filearea file area
*
* @return bool|null false if file not valid
*/
public static function pluginfile_valid(stdClass $context, string $filearea): ?bool {
// Can be in context module or in context_system (if is the presentation by default).
if (!in_array($context->contextlevel, [CONTEXT_MODULE, CONTEXT_SYSTEM])) {
return false;
}
if (!array_key_exists($filearea, self::get_file_areas())) {
return false;
}
return true;
}
/**
* Helper for getting pluginfile.
*
* @param stdClass|null $course course object
* @param stdClass|null $cm course module object
* @param context $context context object
* @param string $filearea file area
* @param array $args extra arguments
*
* @return \stored_file|bool
*/
public static function pluginfile_file(?stdClass $course, ?stdClass $cm, context $context, string $filearea, array $args) {
$filename = self::get_plugin_filename($course, $cm, $context, $args);
if (!$filename) {
return false;
}
$fullpath = "/$context->id/mod_bigbluebuttonbn/$filearea/0/" . $filename;
$fs = get_file_storage();
$file = $fs->get_file_by_hash(sha1($fullpath));
if (!$file || $file->is_directory()) {
return false;
}
return $file;
}
/**
* Get a full path to the file attached as a preuploaded presentation
* or if there is none, set the presentation field will be set to blank.
*
* @param stdClass $bigbluebuttonformdata BigBlueButtonBN form data
* Note that $bigbluebuttonformdata->presentation is the id of the filearea whereas the bbb instance table
* stores the file name/path
* @return string
*/
public static function save_media_file(stdClass &$bigbluebuttonformdata): string {
if (!isset($bigbluebuttonformdata->presentation) || $bigbluebuttonformdata->presentation == '') {
return '';
}
$context = context_module::instance($bigbluebuttonformdata->coursemodule);
// Set the filestorage object.
$fs = get_file_storage();
// Save the file if it exists that is currently in the draft area.
file_save_draft_area_files($bigbluebuttonformdata->presentation, $context->id, 'mod_bigbluebuttonbn', 'presentation', 0);
// Get the file if it exists.
$files = $fs->get_area_files(
$context->id,
'mod_bigbluebuttonbn',
'presentation',
0,
'itemid, filepath, filename',
false
);
// Check that there is a file to process.
$filesrc = '';
if (count($files) == 1) {
// Get the first (and only) file.
$file = reset($files);
$filesrc = '/' . $file->get_filename();
}
return $filesrc;
}
/**
* Helper return array containing the file descriptor for a preuploaded presentation.
*
* @param context $context
* @param string $presentation matching presentation file name
* @param int $id bigbluebutton instance id
* @param bool $withnonce add nonce to the url
* @return array|null the representation of the presentation as an associative array
*/
public static function get_presentation(context $context, string $presentation, $id = null, $withnonce = false): ?array {
global $CFG;
$fs = get_file_storage();
$files = [];
$defaultpresentation = $fs->get_area_files(
context_system::instance()->id,
'mod_bigbluebuttonbn',
'presentationdefault',
0,
"filename",
false
);
$activitypresentation = $files = $fs->get_area_files(
$context->id,
'mod_bigbluebuttonbn',
'presentation',
false,
'itemid, filepath, filename',
false
);
// Presentation upload logic based on config settings.
if (empty($defaultpresentation)) {
if (empty($activitypresentation) || !\mod_bigbluebuttonbn\local\config::get('preuploadpresentation_editable')) {
return null;
}
$files = $activitypresentation;
} else {
if (empty($activitypresentation) || !\mod_bigbluebuttonbn\local\config::get('preuploadpresentation_editable')) {
$files = $defaultpresentation;
$id = null;
} else {
$files = $activitypresentation;
}
}
$pnoncevalue = 0;
if ($withnonce) {
$nonceid = 0;
if (!is_null($id)) {
$instance = instance::get_from_instanceid($id);
$nonceid = $instance->get_instance_id();
}
$pnoncevalue = self::generate_nonce($nonceid);
}
$file = null;
foreach ($files as $f) {
if (basename($f->get_filename()) == basename($presentation)) {
$file = $f;
}
}
if (!$file && !empty($files)) {
$file = reset($files);
}
if (empty($file)) {
return null; // File was not found.
}
// Note: $pnoncevalue is an int.
$url = moodle_url::make_pluginfile_url(
$file->get_contextid(),
$file->get_component(),
$file->get_filearea(),
$withnonce ? $pnoncevalue : null, // Hack: item id as a nonce.
$file->get_filepath(),
$file->get_filename()
);
return [
'icondesc' => get_mimetype_description($file),
'iconname' => file_file_icon($file),
'name' => $file->get_filename(),
'url' => $url->out(false),
];
}
/**
* Helper for getting pluginfile name.
*
* @param stdClass|null $course course object
* @param stdClass|null $cm course module object
* @param context $context context object
* @param array $args extra arguments
*
* @return string|null
*/
public static function get_plugin_filename(?stdClass $course, ?stdClass $cm, context $context, array $args): ?string {
global $DB;
if ($context->contextlevel != CONTEXT_SYSTEM) {
// Plugin has a file to use as default in general setting.
// The difference with the standard bigbluebuttonbn_pluginfile_filename() are.
// - Context is system, so we don't need to check the cmid in this case.
// - The area is "presentationdefault_cache".
if (!$DB->get_record('bigbluebuttonbn', ['id' => $cm->instance])) {
return null;
}
}
// Plugin has a file to use as default in general setting.
// The difference with the standard bigbluebuttonbn_pluginfile_filename() are.
// - Context is system, so we don't need to check the cmid in this case.
// - The area is "presentationdefault_cache".
if (count($args) > 1) {
$id = 0;
if ($cm) {
$instance = instance::get_from_cmid($cm->id);
$id = $instance->get_instance_id();
}
$actualnonce = self::get_nonce($id);
return ($args['0'] == $actualnonce) ? $args['1'] : null;
}
if (!empty($course)) {
require_course_login($course, true, $cm, true, true);
} else {
require_login(null, true, $cm, true, true);
}
if (!has_capability('mod/bigbluebuttonbn:join', $context)) {
return null;
}
return implode('/', $args);
}
/**
* Helper generates a salt used for the preuploaded presentation callback url.
*
* @param int $id
* @return int
*/
protected static function get_nonce(int $id): int {
$cache = static::get_nonce_cache();
$pnoncekey = sha1($id);
$existingnoncedata = $cache->get($pnoncekey);
if ($existingnoncedata) {
if ($existingnoncedata->counter > 0) {
$existingnoncedata->counter--;
$cache->set($pnoncekey, $existingnoncedata);
return $existingnoncedata->nonce;
}
}
// The item id was adapted for granting public access to the presentation once in order to allow BigBlueButton to gather
// the file once.
return static::generate_nonce($id);
}
/**
* Generate a nonce and store it in the cache
*
* @param int $id
* @return int
*/
protected static function generate_nonce($id): int {
$cache = static::get_nonce_cache();
$pnoncekey = sha1($id);
// The item id was adapted for granting public access to the presentation once in order to allow BigBlueButton to gather
// the file once.
$pnoncevalue = ((int) microtime()) + mt_rand();
$cache->set($pnoncekey, (object) ['nonce' => $pnoncevalue, 'counter' => 2]);
return $pnoncevalue;
}
/**
* Get cache for nonce
*
* @return \cache_application|\cache_session|cache_store
*/
private static function get_nonce_cache() {
return cache::make_from_params(
cache_store::MODE_APPLICATION,
'mod_bigbluebuttonbn',
'presentation_cache'
);
}
/**
* Returns an array of file areas.
*
* @return array a list of available file areas
*
*/
protected static function get_file_areas(): array {
$areas = [];
$areas['presentation'] = get_string('mod_form_block_presentation', 'bigbluebuttonbn');
$areas['presentationdefault'] = get_string('mod_form_block_presentation_default', 'bigbluebuttonbn');
return $areas;
}
}
@@ -0,0 +1,209 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace mod_bigbluebuttonbn\local\helpers;
use calendar_event;
use mod_bigbluebuttonbn\instance;
use mod_bigbluebuttonbn\logger;
use mod_bigbluebuttonbn\plugin;
use stdClass;
/**
* Utility class for all instance (module) routines helper.
*
* @package mod_bigbluebuttonbn
* @copyright 2021 onwards, Blindside Networks Inc
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @author Laurent David (laurent [at] call-learning [dt] fr)
*/
class mod_helper {
/**
* Runs any processes that must run before a bigbluebuttonbn insert/update.
*
* @param stdClass $bigbluebuttonbn BigBlueButtonBN form data
**/
public static function process_pre_save(stdClass $bigbluebuttonbn) {
self::process_pre_save_instance($bigbluebuttonbn);
self::process_pre_save_checkboxes($bigbluebuttonbn);
self::process_pre_save_common($bigbluebuttonbn);
$bigbluebuttonbn->participants = htmlspecialchars_decode($bigbluebuttonbn->participants, ENT_COMPAT);
}
/**
* Runs process for defining the instance (insert/update).
*
* @param stdClass $bigbluebuttonbn BigBlueButtonBN form data
**/
protected static function process_pre_save_instance(stdClass $bigbluebuttonbn): void {
$bigbluebuttonbn->timemodified = time();
if ((integer) $bigbluebuttonbn->instance == 0) {
$bigbluebuttonbn->meetingid = 0;
$bigbluebuttonbn->timecreated = time();
$bigbluebuttonbn->timemodified = 0;
// As it is a new activity, assign passwords.
$bigbluebuttonbn->moderatorpass = plugin::random_password(12);
$bigbluebuttonbn->viewerpass = plugin::random_password(12, $bigbluebuttonbn->moderatorpass);
}
}
/**
* Runs process for assigning default value to checkboxes.
*
* @param stdClass $bigbluebuttonbn BigBlueButtonBN form data
**/
protected static function process_pre_save_checkboxes($bigbluebuttonbn) {
if (!isset($bigbluebuttonbn->wait)) {
$bigbluebuttonbn->wait = 0;
}
if (!isset($bigbluebuttonbn->record)) {
$bigbluebuttonbn->record = 0;
}
if (!isset($bigbluebuttonbn->recordallfromstart)) {
$bigbluebuttonbn->recordallfromstart = 0;
}
if (!isset($bigbluebuttonbn->recordhidebutton)) {
$bigbluebuttonbn->recordhidebutton = 0;
}
if (!isset($bigbluebuttonbn->recordings_html)) {
$bigbluebuttonbn->recordings_html = 0;
}
if (!isset($bigbluebuttonbn->recordings_deleted)) {
$bigbluebuttonbn->recordings_deleted = 0;
}
if (!isset($bigbluebuttonbn->recordings_imported)) {
$bigbluebuttonbn->recordings_imported = 0;
}
if (!isset($bigbluebuttonbn->recordings_preview)) {
$bigbluebuttonbn->recordings_preview = 0;
}
if (!isset($bigbluebuttonbn->muteonstart)) {
$bigbluebuttonbn->muteonstart = 0;
}
if (!isset($bigbluebuttonbn->disablecam)) {
$bigbluebuttonbn->disablecam = 0;
}
if (!isset($bigbluebuttonbn->disablemic)) {
$bigbluebuttonbn->disablemic = 0;
}
if (!isset($bigbluebuttonbn->disableprivatechat)) {
$bigbluebuttonbn->disableprivatechat = 0;
}
if (!isset($bigbluebuttonbn->disablepublicchat)) {
$bigbluebuttonbn->disablepublicchat = 0;
}
if (!isset($bigbluebuttonbn->disablenote)) {
$bigbluebuttonbn->disablenote = 0;
}
if (!isset($bigbluebuttonbn->hideuserlist)) {
$bigbluebuttonbn->hideuserlist = 0;
}
}
/**
* Runs process for wipping common settings when 'recordings only'.
*
* @param stdClass $bigbluebuttonbn BigBlueButtonBN form data
**/
protected static function process_pre_save_common(stdClass $bigbluebuttonbn): void {
// Make sure common settings are removed when 'recordings only'.
if ($bigbluebuttonbn->type == instance::TYPE_RECORDING_ONLY) {
$bigbluebuttonbn->groupmode = 0;
$bigbluebuttonbn->groupingid = 0;
}
}
/**
* Runs any processes that must be run after a bigbluebuttonbn insert/update.
*
* @param stdClass $bigbluebuttonbn BigBlueButtonBN form data
**/
public static function process_post_save(stdClass $bigbluebuttonbn): void {
self::process_post_save_event($bigbluebuttonbn);
self::process_post_save_completion($bigbluebuttonbn);
}
/**
* Generates an event after a bigbluebuttonbn insert/update.
*
* @param stdClass $bigbluebuttonbn BigBlueButtonBN form data
**/
protected static function process_post_save_event(stdClass $bigbluebuttonbn): void {
global $CFG, $DB;
require_once($CFG->dirroot . '/calendar/lib.php');
$eventid = $DB->get_field('event', 'id', [
'modulename' => 'bigbluebuttonbn',
'instance' => $bigbluebuttonbn->id,
'eventtype' => logger::EVENT_MEETING_START
]);
// Delete the event from calendar when/if openingtime is NOT set.
if (!isset($bigbluebuttonbn->openingtime) || !$bigbluebuttonbn->openingtime) {
if ($eventid) {
$calendarevent = calendar_event::load($eventid);
$calendarevent->delete();
}
return;
}
// Add event to the calendar as openingtime is set.
$event = (object) [
'eventtype' => logger::EVENT_MEETING_START,
'type' => CALENDAR_EVENT_TYPE_ACTION,
'name' => get_string('calendarstarts', 'bigbluebuttonbn', $bigbluebuttonbn->name),
'description' => format_module_intro('bigbluebuttonbn', $bigbluebuttonbn, $bigbluebuttonbn->coursemodule, false),
'format' => FORMAT_HTML,
'courseid' => $bigbluebuttonbn->course,
'groupid' => 0,
'userid' => 0,
'modulename' => 'bigbluebuttonbn',
'instance' => $bigbluebuttonbn->id,
'timestart' => $bigbluebuttonbn->openingtime,
'timeduration' => 0,
'timesort' => $bigbluebuttonbn->openingtime,
'visible' => instance_is_visible('bigbluebuttonbn', $bigbluebuttonbn),
'priority' => null,
];
// Update the event in calendar when/if eventid was found.
if ($eventid) {
$event->id = $eventid;
$calendarevent = calendar_event::load($eventid);
$calendarevent->update($event);
return;
}
calendar_event::create($event);
}
/**
* Generates an event after a bigbluebuttonbn activity is completed.
*
* @param stdClass $bigbluebuttonbn BigBlueButtonBN form data
**/
protected static function process_post_save_completion(stdClass $bigbluebuttonbn): void {
if (empty($bigbluebuttonbn->completionexpected)) {
return;
}
\core_completion\api::update_completion_date_event(
$bigbluebuttonbn->coursemodule,
'bigbluebuttonbn',
$bigbluebuttonbn->id,
$bigbluebuttonbn->completionexpected
);
}
}
@@ -0,0 +1,131 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* The mod_bigbluebuttonbn resetting instance helper
*
* @package mod_bigbluebuttonbn
* @copyright 2021 onwards, Blindside Networks Inc
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @author Laurent David (laurent [at] call-learning [dt] fr)
*/
namespace mod_bigbluebuttonbn\local\helpers;
use context_module;
use core_tag_tag;
use mod_bigbluebuttonbn\local\config;
use mod_bigbluebuttonbn\recording;
/**
* Utility class for resetting instance routines helper
*
* @package mod_bigbluebuttonbn
* @copyright 2021 onwards, Blindside Networks Inc
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class reset {
/**
* Used by the reset_course_userdata for deleting recordings
*
* This will delete recordings in the database and not in the remote BBB server.
*
* @param int $courseid
*/
public static function reset_recordings(int $courseid): void {
// Criteria for search : courseid or bigbluebuttonbn=null or subset=false or includedeleted=true.
$recordings = recording::get_recordings_for_course(
$courseid,
[], // Exclude itself.
false,
true
);
if ($recordings) {
// Remove all the recordings.
foreach ($recordings as $recording) {
$recording->delete();
}
}
}
/**
* Used by the reset_course_userdata for deleting tags linked to bigbluebuttonbn instances in the course.
*
* @param int $courseid
*/
public static function reset_tags(int $courseid): void {
global $DB;
// Remove all the tags linked to the room/activities in this course.
if ($bigbluebuttonbns = $DB->get_records('bigbluebuttonbn', ['course' => $courseid])) {
foreach ($bigbluebuttonbns as $bigbluebuttonbn) {
if (!$cm = get_coursemodule_from_instance('bigbluebuttonbn', $bigbluebuttonbn->id, $courseid)) {
continue;
}
$context = context_module::instance($cm->id);
core_tag_tag::delete_instances('mod_bigbluebuttonbn', null, $context->id);
}
}
}
/**
* Used by the reset_course_userdata for deleting events linked to bigbluebuttonbn instances in the course.
*
* @param string $courseid
* @return bool status
*/
public static function reset_events($courseid) {
global $DB;
// Remove all the events.
return $DB->delete_records('event', ['modulename' => 'bigbluebuttonbn', 'courseid' => $courseid]);
}
/**
* Returns status used on every defined reset action.
*
* @param string $item
* @return array status array
*/
public static function reset_getstatus(string $item): array {
return ['component' => get_string('modulenameplural', 'bigbluebuttonbn'),
'item' => get_string("removed{$item}", 'bigbluebuttonbn'),
'error' => false];
}
/**
* Define items to be reset by course/reset.php
*
* @return array
*/
public static function reset_course_items(): array {
$items = ["events" => 0, "tags" => 0, "logs" => 0];
// Include recordings only if enabled.
if ((boolean) config::recordings_enabled()) {
$items["recordings"] = 0;
}
return $items;
}
/**
* Reset logs for each BBB instance of this course
*
* @param int $courseid
* @return bool status
*/
public static function reset_logs(int $courseid) {
global $DB;
return $DB->delete_records('bigbluebuttonbn_logs', ['courseid' => $courseid]);
}
}
@@ -0,0 +1,452 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* The mod_bigbluebuttonbn roles helper
*
* @package mod_bigbluebuttonbn
* @copyright 2021 onwards, Blindside Networks Inc
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @author Laurent David (laurent [at] call-learning [dt] fr)
*/
namespace mod_bigbluebuttonbn\local\helpers;
use cache;
use cache_store;
use context;
use context_course;
use mod_bigbluebuttonbn\instance;
use mod_bigbluebuttonbn\local\proxy\bigbluebutton_proxy;
use stdClass;
/**
* Utility class for all roles routines helper
*
* @package mod_bigbluebuttonbn
* @copyright 2021 onwards, Blindside Networks Inc
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class roles {
/** @var int The bigbluebutton viewer role */
public const ROLE_VIEWER = 'viewer';
/** @var string The bigbluebutton moderator role */
public const ROLE_MODERATOR = 'moderator';
/**
* Returns user roles in a context.
*
* @param context $context
* @param int $userid
*
* @return array $userroles
*/
public static function get_user_roles(context $context, int $userid) {
global $DB;
$userroles = get_user_roles($context, $userid);
if ($userroles) {
$where = '';
foreach ($userroles as $userrole) {
$where .= (empty($where) ? ' WHERE' : ' OR') . ' id=' . $userrole->roleid;
}
$userroles = $DB->get_records_sql('SELECT * FROM {role}' . $where);
}
return $userroles;
}
/**
* Returns guest role wrapped in an array.
*
* @return array
*/
protected static function get_guest_role() {
$guestrole = get_guest_role();
return [$guestrole->id => $guestrole];
}
/**
* Returns an array containing all the users in a context wrapped for html select element.
*
* @param context_course $context
* @param null $bbactivity
* @return array $users
*/
public static function get_users_array(context_course $context, $bbactivity = null) {
// CONTRIB-7972, check the group of current user and course group mode.
$groups = null;
$users = (array) get_enrolled_users($context, '', 0, 'u.*', null, 0, 0, true);
$course = get_course($context->instanceid);
$groupmode = groups_get_course_groupmode($course);
if ($bbactivity) {
list($bbcourse, $cm) = get_course_and_cm_from_instance($bbactivity->id, 'bigbluebuttonbn');
$groupmode = groups_get_activity_groupmode($cm);
}
if ($groupmode == SEPARATEGROUPS && !has_capability('moodle/site:accessallgroups', $context)) {
global $USER;
$groups = groups_get_all_groups($course->id, $USER->id);
$users = [];
foreach ($groups as $g) {
$users += (array) get_enrolled_users($context, '', $g->id, 'u.*', null, 0, 0, true);
}
}
return array_map(
function($u) {
return ['id' => $u->id, 'name' => fullname($u)];
},
$users);
}
/**
* Can do some administration in this course, likely manage recordings
*
* @param int $courseid
* @param string $capability
*/
public static function has_capability_in_course(int $courseid, string $capability) {
global $DB;
if (empty($courseid) || !$DB->record_exists('course', ['id' => $courseid])) {
return has_capability('moodle/site:config', \context_system::instance());
}
$coursecontext = context_course::instance($courseid);
return has_capability($capability, $coursecontext);
}
/**
* Returns an array containing all the roles in a context.
*
* @param context|null $context $context
* @param bool|null $onlyviewableroles
*
* @return array $roles
*/
public static function get_roles(?context $context = null, ?bool $onlyviewableroles = true) {
global $CFG;
if ($onlyviewableroles == true && $CFG->branch >= 35) {
$roles = (array) get_viewable_roles($context);
foreach ($roles as $key => $value) {
$roles[$key] = $value;
}
} else {
$roles = (array) role_get_names($context);
foreach ($roles as $key => $value) {
$roles[$key] = $value->localname;
}
}
return $roles;
}
/**
* Returns an array containing all the roles in a context wrapped for html select element.
*
* @param context|null $context $context
* @param bool $onlyviewableroles
*
* @return array $users
*/
protected static function get_roles_select(context $context = null, bool $onlyviewableroles = true) {
global $CFG;
if ($onlyviewableroles == true && $CFG->branch >= 35) {
$roles = (array) get_viewable_roles($context);
foreach ($roles as $key => $value) {
$roles[$key] = ['id' => $key, 'name' => $value];
}
} else {
$roles = (array) role_get_names($context);
foreach ($roles as $key => $value) {
$roles[$key] = ['id' => $value->id, 'name' => $value->localname];
}
}
return $roles;
}
/**
* Returns role that corresponds to an id.
*
* @param string|integer $id
*
* @return stdClass|null $role
*/
protected static function get_role($id): ?stdClass {
$roles = (array) role_get_names();
if (is_numeric($id) && isset($roles[$id])) {
return (object) $roles[$id];
}
foreach ($roles as $role) {
if ($role->shortname == $id) {
return $role;
}
}
return null;
}
/**
* Returns an array to populate a list of participants used in mod_form.js.
*
* @param context $context
* @param null|stdClass $bbactivity
* @return array $data
*/
public static function get_participant_data(context $context, ?stdClass $bbactivity = null) {
$data = [
'all' => [
'name' => get_string('mod_form_field_participant_list_type_all', 'bigbluebuttonbn'),
'children' => []
],
];
$data['role'] = [
'name' => get_string('mod_form_field_participant_list_type_role', 'bigbluebuttonbn'),
'children' => self::get_roles_select($context, true)
];
$data['user'] = [
'name' => get_string('mod_form_field_participant_list_type_user', 'bigbluebuttonbn'),
'children' => self::get_users_array($context, $bbactivity),
];
return $data;
}
/**
* Returns an array to populate a list of participants used in mod_form.php.
*
* @param stdClass|null $bigbluebuttonbn
* @param context $context
*
* @return array
*/
public static function get_participant_list(?stdClass $bigbluebuttonbn, context $context): array {
global $USER;
if ($bigbluebuttonbn == null) {
return self::get_participant_rules_encoded(
self::get_participant_list_default($context, $USER->id)
);
}
if (empty($bigbluebuttonbn->participants)) {
$bigbluebuttonbn->participants = "[]";
}
$rules = json_decode($bigbluebuttonbn->participants, true);
if (empty($rules)) {
$rules = self::get_participant_list_default($context,
bigbluebutton_proxy::get_instance_ownerid($bigbluebuttonbn));
}
return self::get_participant_rules_encoded($rules);
}
/**
* Returns an array to populate a list of participants used in mod_form.php with default values.
*
* @param context $context
* @param int|null $ownerid
*
* @return array
*/
protected static function get_participant_list_default(context $context, ?int $ownerid = null) {
$participantlist = [];
$participantlist[] = [
'selectiontype' => 'all',
'selectionid' => 'all',
'role' => self::ROLE_VIEWER,
];
$defaultrules = explode(',', \mod_bigbluebuttonbn\local\config::get('participant_moderator_default'));
foreach ($defaultrules as $defaultrule) {
if ($defaultrule == '0') {
if (!empty($ownerid) && is_enrolled($context, $ownerid)) {
$participantlist[] = [
'selectiontype' => 'user',
'selectionid' => (string) $ownerid,
'role' => self::ROLE_MODERATOR];
}
continue;
}
$participantlist[] = [
'selectiontype' => 'role',
'selectionid' => $defaultrule,
'role' => self::ROLE_MODERATOR];
}
return $participantlist;
}
/**
* Returns an array to populate a list of participants used in mod_form.php with bigbluebuttonbn values.
*
* @param array $rules
*
* @return array
*/
protected static function get_participant_rules_encoded(array $rules): array {
foreach ($rules as $key => $rule) {
if ($rule['selectiontype'] !== 'role' || is_numeric($rule['selectionid'])) {
continue;
}
$role = self::get_role($rule['selectionid']);
if ($role == null) {
unset($rules[$key]);
continue;
}
$rule['selectionid'] = $role->id;
$rules[$key] = $rule;
}
return $rules;
}
/**
* Returns an array to populate a list of participant_selection used in mod_form.php.
*
* @return array
*/
public static function get_participant_selection_data(): array {
return [
'type_options' => [
'all' => get_string('mod_form_field_participant_list_type_all', 'bigbluebuttonbn'),
'role' => get_string('mod_form_field_participant_list_type_role', 'bigbluebuttonbn'),
'user' => get_string('mod_form_field_participant_list_type_user', 'bigbluebuttonbn'),
],
'type_selected' => 'all',
'options' => ['all' => '---------------'],
'selected' => 'all',
];
}
/**
* Evaluate if a user in a context is moderator based on roles and participation rules.
*
* @param context $context
* @param array $participantlist
* @param int $userid
*
* @return bool
*/
public static function is_moderator(context $context, array $participantlist, ?int $userid = null): bool {
global $USER;
// If an admin, then also a moderator.
if (has_capability('moodle/site:config', $context)) {
return true;
}
if (!is_array($participantlist)) {
return false;
}
if (empty($userid)) {
$userid = $USER->id;
}
$userroles = self::get_guest_role();
if (!isguestuser()) {
$userroles = self::get_user_roles($context, $userid);
}
return self::is_moderator_validator($participantlist, $userid, $userroles);
}
/**
* Iterates participant list rules to evaluate if a user is moderator.
*
* @param array $participantlist
* @param int $userid
* @param array $userroles
*
* @return bool
*/
protected static function is_moderator_validator(array $participantlist, int $userid, array $userroles): bool {
// Iterate participant rules.
foreach ($participantlist as $participant) {
if (self::is_moderator_validate_rule($participant, $userid, $userroles)) {
return true;
}
}
return false;
}
/**
* Evaluate if a user is moderator based on roles and a particular participation rule.
*
* @param array $participant
* @param int $userid
* @param array $userroles
*
* @return bool
*/
protected static function is_moderator_validate_rule(array $participant, int $userid, array $userroles): bool {
if ($participant['role'] == self::ROLE_VIEWER) {
return false;
}
// Validation for the 'all' rule.
if ($participant['selectiontype'] == 'all') {
return true;
}
// Validation for a 'user' rule.
if ($participant['selectiontype'] == 'user') {
if ($participant['selectionid'] == $userid) {
return true;
}
return false;
}
// Validation for a 'role' rule.
$role = self::get_role($participant['selectionid']);
if ($role != null && array_key_exists($role->id, $userroles)) {
return true;
}
return false;
}
/**
* Updates the meeting info cached object when a participant has joined.
*
* @param string $meetingid
* @param bool $ismoderator
*
* @return void
*/
public static function participant_joined(string $meetingid, bool $ismoderator): void {
$cache = cache::make_from_params(cache_store::MODE_APPLICATION, 'mod_bigbluebuttonbn', 'meetings_cache');
$result = $cache->get($meetingid);
$meetinginfo = json_decode($result['meeting_info']);
$meetinginfo->participantCount += 1;
if ($ismoderator) {
$meetinginfo->moderatorCount += 1;
}
$cache->set($meetingid, ['creation_time' => $result['creation_time'],
'meeting_info' => json_encode($meetinginfo)]);
}
/**
* Helper function returns a list of courses a user has access to, wrapped in an array that can be used
* by a html select.
*
* @param instance $instance
* @return array
*/
public static function import_get_courses_for_select(instance $instance): array {
if ($instance->is_admin()) {
$courses = get_courses('all', 'c.fullname ASC');
// It includes the name of the site as a course (category 0), so remove the first one.
unset($courses['1']);
} else {
$courses = enrol_get_users_courses($instance->get_user_id(), false, 'id,shortname,fullname');
}
$courses = array_filter($courses, function($course) {
$modules = get_fast_modinfo($course->id);
return !empty($modules->instances['bigbluebuttonbn']);
});
$coursesforselect = [];
foreach ($courses as $course) {
$coursesforselect[$course->id] = $course->fullname . " (" . $course->shortname . ")";
}
return $coursesforselect;
}
}
@@ -0,0 +1,73 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace mod_bigbluebuttonbn\local\helpers;
use cm_info;
use mod_bigbluebuttonbn\instance;
use mod_bigbluebuttonbn\logger;
use stdClass;
/**
* Utility class for all user information
*
* Used mainly in user_outline and user_complete
*
* @package mod_bigbluebuttonbn
* @copyright 2022 onwards, Blindside Networks Inc
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @author Laurent David (laurent [at] call-learning [dt] fr)
*/
class user_info {
/**
* Event to watch for.
*/
const EVENT_TO_WATCH = [
'join' => logger::EVENT_JOIN,
'play_recording' => logger::EVENT_PLAYED
];
/**
* Get user outline and complete info
*
* @param stdClass $course
* @param stdClass $user
* @param cm_info $mod
* @return array[] an array of infos and timestamps (latest timestamp)
*/
public static function get_user_info_outline(stdClass $course, stdClass $user, cm_info $mod): array {
$completion = new \completion_info($course);
$cdata = $completion->get_data($mod, false, $user->id);
$logtimestamps = [];
$infos = [];
if (!empty($cdata->viewed) && $cdata->viewed) {
$infos[] = get_string('report_room_view', 'mod_bigbluebuttonbn');
$logtimestamps[] = $cdata->timemodified;
}
$instance = instance::get_from_cmid($mod->id);
foreach (self::EVENT_TO_WATCH as $eventtype => $logtype) {
$logs = logger::get_user_completion_logs($instance, $user->id, [$logtype]);
if ($logs) {
$infos[] = get_string("report_{$eventtype}_info", 'mod_bigbluebuttonbn', count($logs));
$latesttime = array_reduce($logs,
function($acc, $log) {
return ($acc > $log->timecreated) ? $acc : $log->timecreated;
}, 0);
$logtimestamps[] = $latesttime;
}
}
return [$infos, $logtimestamps];
}
}