This commit is contained in:
Xes
2025-08-14 22:39:38 +02:00
parent 3641e93527
commit 5403f346e3
3370 changed files with 327179 additions and 0 deletions

61
plugin/bbb/CHANGELOG.md Normal file
View File

@@ -0,0 +1,61 @@
Version 2.11 - 2022-04
----------------------
* Add option to close all rooms on all campus on a multi-url enviroment
Version 2.10 - 2021-10
----------------------
* Add support for multiple recording formats
Version 2.9 - 2021-08
---------------------
* Remove interface option (HTML5/Flash)
Version 2.8 - 2019-07
---------------------
* Add rooms + internal meeting id
Version 2.7 - 2018-07
---------------------
* Add interface option (HTML5 or Flash)
Version 2.6 - 2017-05
---------------------
* Add max users limit
Version 2.5 - 2016-07
---------------------
* User global conference support
Version 2.4 - 2016-05
------------------------
* Global conference support (Requires to update the plugin settings).
* Course group conference support
Version 2.3 - 2015-05-18
------------------------
Changes:
* Added support for variable voiceBridge to be sent on meeting creation. See:
https://code.google.com/p/bigbluebutton/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Status%20Priority%20Milestone%20Owner%20Component%20Summary&groupby=&sort=&id=1186
and https://support.chamilo.org/issues/7669 for details.
Version 2.2 - 2014-10-15
------------------------
Changes:
* Add a pseudo-random guid to avoid clashing conferences when several Chamilo portals use the same server.
* Add possibility to hide recordings of previous conferences from the list.
* Show action icons in the action column
* Hide the ID of the meeting (this was an internal ID, useless to the final user). It is still in the HTML source, however
* Show number of minutes of the recording (in the recordings list)
Version 2.1
-----------
Released with: Chamilo LMS 1.9.8
* Session support
Version 2.0
-----------
Version 1.0
-----------
Released with: Chamilo LMS 1.9.0

95
plugin/bbb/README.md Normal file
View File

@@ -0,0 +1,95 @@
BigBlueButton Chamilo plugin
============================
This plugin allows you to have videoconference rooms in each course.
It requires you to have a BigBlueButton videoconference server installed on another server (ideally).
Check www.bigbluebutton.org for more about BigBlueButton.
## Migrating to Chamilo LMS 1.10.x
For Chamilo 1.10.x, the Videoconference plugin has two new settings options:
*Enable global conference* and *Enable conference in course groups*.
##### Database changes
You need execute these SQL queries in your database after making the migration process from 1.9.x.
```sql
ALTER TABLE plugin_bbb_meeting ADD voice_bridge int NOT NULL DEFAULT 1;
ALTER TABLE plugin_bbb_meeting ADD group_id int unsigned NOT NULL DEFAULT 0;
```
## Migrating to Chamilo LMS 1.11.x
For Chamilo 1.11.x, Videoconference plugin has one new setting option:
*Disable Course Settings*.
##### Database changes
You need execute this SQL query in your database after making the Chamilo migration process from 1.10.x.
> If you are migrating from 1.9.x versions, you need execute the SQL queries from the migration to 1.10.x before.
```sql
ALTER TABLE plugin_bbb_meeting ADD user_id int unsigned NOT NULL DEFAULT 0;
ALTER TABLE plugin_bbb_meeting ADD access_url int NOT NULL DEFAULT 1;
```
For version 2.5 you need execute these SQL queries
```sql
CREATE TABLE IF NOT EXISTS plugin_bbb_room (
id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
meeting_id int(10) unsigned NOT NULL,
participant_id int(11) NOT NULL,
in_at datetime NOT NULL,
out_at datetime NOT NULL
);
ALTER TABLE plugin_bbb_meeting ADD COLUMN video_url TEXT NULL;
ALTER TABLE plugin_bbb_meeting ADD COLUMN has_video_m4v TINYINT NOT NULL DEFAULT 0;
ALTER TABLE plugin_bbb_meeting ADD COLUMN user_id INT DEFAULT 0;
ALTER TABLE plugin_bbb_meeting ADD COLUMN access_url INT DEFAULT 0;
ALTER TABLE plugin_bbb_meeting ADD COLUMN remote_id char(30);
ALTER TABLE plugin_bbb_meeting ADD COLUMN visibility TINYINT NOT NULL DEFAULT 1;
ALTER TABLE plugin_bbb_meeting ADD COLUMN session_id INT DEFAULT 0;
```
For version 2.6 (adding limits) you need execute these SQL queries
```sql
INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url, access_url_changeable, access_url_locked) VALUES ('bbb_max_users_limit', 'bbb', 'setting', 'Plugins', '3', 'bbb', null, null, null, 1, 1, 0);
INSERT INTO extra_field (extra_field_type, field_type, variable, display_text, default_value, field_order, visible_to_self, visible_to_others, changeable, filter, created_at) VALUES (2, 15, 'plugin_bbb_course_users_limit', 'MaxUsersInConferenceRoom', '0', 1, 1, 0, 1, null, '2017-05-28 01:19:32');
INSERT INTO extra_field (extra_field_type, field_type, variable, display_text, default_value, field_order, visible_to_self, visible_to_others, changeable, filter, created_at) VALUES (3, 15, 'plugin_bbb_session_users_limit', 'MaxUsersInConferenceRoom', null, 1, 1, 0, 1, null, '2017-05-28 01:19:32');
```
For version 2.7
```sql
ALTER TABLE plugin_bbb_meeting ADD COLUMN interface INT NOT NULL DEFAULT 0;
ALTER TABLE plugin_bbb_room ADD COLUMN interface INT NOT NULL DEFAULT 0;
ALTER TABLE plugin_bbb_room MODIFY COLUMN in_at datetime;
ALTER TABLE plugin_bbb_room MODIFY COLUMN out_at datetime;
```
For version 2.8
```sql
ALTER TABLE plugin_bbb_meeting ADD COLUMN internal_meeting_id VARCHAR(255) DEFAULT NULL;
ALTER TABLE plugin_bbb_room ADD close INT NOT NULL DEFAULT 0;
```
For version 2.9 (Optional, requires an update version of BBB)
```sql
ALTER TABLE plugin_bbb_room DROP COLUMN interface;
ALTER TABLE plugin_bbb_meeting DROP COLUMN interface;
```
For version 2.10 (Handles multiple recording formats - Check https://github.com/chamilo/chamilo-lms/issues/3703)
```sql
CREATE TABLE plugin_bbb_meeting_format (
id int unsigned not null PRIMARY KEY AUTO_INCREMENT,
meeting_id int unsigned not null,
format_type varchar(255) not null,
resource_url text not null
)
```
## Improve access tracking in BBB
You need to configure the cron using the *cron_close_meeting.php* file.
# Digital ocean VM
In order to use DigitalOceanVM classes a new package is required:
```
composer requires toin0u/digitalocean
```

166
plugin/bbb/admin.php Normal file
View File

@@ -0,0 +1,166 @@
<?php
/* For license terms, see /license.txt */
use Chamilo\UserBundle\Entity\User;
/**
* This script initiates a video conference session, calling the BigBlueButton API.
*/
$course_plugin = 'bbb'; //needed in order to load the plugin lang variables
$cidReset = true;
require_once __DIR__.'/../../main/inc/global.inc.php';
api_protect_admin_script();
$plugin = BBBPlugin::create();
$tool_name = $plugin->get_lang('Videoconference');
$isGlobal = isset($_GET['global']) ? true : false;
$bbb = new bbb('', '', $isGlobal);
$action = isset($_GET['action']) ? $_GET['action'] : null;
$currentMonth = date('n');
$dateStart = isset($_REQUEST['search_meeting_start']) ? $_REQUEST['search_meeting_start'] : date('Y-m-d', mktime(1, 1, 1, $currentMonth, 1, date('Y')));
$dateEnd = isset($_REQUEST['search_meeting_end']) ? $_REQUEST['search_meeting_end'] : date('Y-m-d', mktime(1, 1, 1, ++$currentMonth, 0, date('Y')));
$dateRange = [
'search_meeting_start' => $dateStart,
'search_meeting_end' => $dateEnd,
];
$form = new FormValidator(get_lang('Search'));
$form->addDatePicker('search_meeting_start', get_lang('DateStart'));
$form->addDatePicker('search_meeting_end', get_lang('DateEnd'));
$form->addButtonSearch(get_lang('Search'));
$form->setDefaults($dateRange);
if ($form->validate()) {
$dateRange = $form->getSubmitValues();
}
$meetings = $bbb->getMeetings(0, 0, 0, true, $dateRange);
foreach ($meetings as &$meeting) {
$participants = $bbb->findConnectedMeetingParticipants($meeting['id']);
foreach ($participants as $meetingParticipant) {
/** @var User $participant */
$participant = $meetingParticipant['participant'];
if ($participant) {
$meeting['participants'][] = $participant->getCompleteName().' ('.$participant->getEmail().')';
}
}
}
if ($action) {
switch ($action) {
case 'export':
$dataToExport = [
[$tool_name, $plugin->get_lang('RecordList')],
[],
[
get_lang('CreatedAt'),
get_lang('Status'),
$plugin->get_lang('Records'),
get_lang('Course'),
get_lang('Session'),
get_lang('Participants'),
],
];
foreach ($meetings as $meeting) {
$dataToExport[] = [
$meeting['created_at'],
$meeting['status'] == 1 ? $plugin->get_lang('MeetingOpened') : $plugin->get_lang('MeetingClosed'),
$meeting['record'] == 1 ? get_lang('Yes') : get_lang('No'),
$meeting['course'] ? $meeting['course']->getTitle() : '-',
$meeting['session'] ? $meeting['session']->getName() : '-',
isset($meeting['participants']) ? implode(PHP_EOL, $meeting['participants']) : null,
];
}
Export::arrayToXls($dataToExport);
break;
}
}
if (!empty($meetings)) {
$meetings = array_reverse($meetings);
}
if (!$bbb->isServerRunning()) {
Display::addFlash(
Display::return_message(get_lang('ServerIsNotRunning'), 'error')
);
}
$htmlHeadXtra[] = api_get_js_simple(
api_get_path(WEB_PLUGIN_PATH).'bbb/resources/utils.js'
);
$htmlHeadXtra[] = "<script> _p.web_plugin = '".api_get_path(WEB_PLUGIN_PATH)."'</script>";
$tpl = new Template($tool_name);
$tpl->assign('meetings', $meetings);
$tpl->assign('search_form', $form->returnForm());
$settingsForm = new FormValidator('settings', api_get_self());
$settingsForm->addHeader($plugin->get_lang('UpdateAllCourseSettings'));
$settingsForm->addHtml(Display::return_message($plugin->get_lang('ThisWillUpdateAllSettingsInAllCourses')));
$settings = $plugin->course_settings;
$defaults = [];
foreach ($settings as $setting) {
$setting = $setting['name'];
$text = $settingsForm->addText($setting, $plugin->get_lang($setting), false);
$text->freeze();
$defaults[$setting] = api_get_plugin_setting('bbb', $setting) === 'true' ? get_lang('Yes') : get_lang('No');
}
$settingsForm->addButtonSave($plugin->get_lang('UpdateAllCourses'));
if ($settingsForm->validate()) {
$table = Database::get_course_table(TABLE_COURSE_SETTING);
foreach ($settings as $setting) {
$setting = $setting['name'];
$setting = Database::escape_string($setting);
if (empty($setting)) {
continue;
}
$value = api_get_plugin_setting('bbb', $setting);
if ($value === 'true') {
$value = 1;
} else {
$value = '';
}
$sql = "UPDATE $table SET value = '$value' WHERE variable = '$setting'";
Database::query($sql);
}
Display::addFlash(Display::return_message(get_lang('Updated')));
header('Location: '.api_get_self());
exit;
}
$settingsForm->setDefaults($defaults);
$tpl->assign('settings_form', $settingsForm->returnForm());
$content = $tpl->fetch('bbb/view/admin.tpl');
if ($meetings) {
$actions = Display::toolbarButton(
get_lang('ExportInExcel'),
api_get_self().'?'.http_build_query([
'action' => 'export',
'search_meeting_start' => $dateStart,
'search_meeting_end' => $dateEnd,
]),
'file-excel-o',
'success'
);
$tpl->assign(
'actions',
Display::toolbarAction('toolbar', [$actions])
);
}
$tpl->assign('content', $content);
$tpl->display_one_col_template();

47
plugin/bbb/ajax.php Normal file
View File

@@ -0,0 +1,47 @@
<?php
/**
* This script initiates a video conference session, calling the BigBlueButton API.
*
* @package chamilo.plugin.bigbluebutton
*/
$course_plugin = 'bbb'; //needed in order to load the plugin lang variables
$cidReset = true;
require_once __DIR__.'/../../main/inc/global.inc.php';
$action = isset($_REQUEST['a']) ? $_REQUEST['a'] : null;
$meetingId = isset($_REQUEST['meeting']) ? intval($_REQUEST['meeting']) : 0;
$bbb = new bbb('', '');
switch ($action) {
case 'check_m4v':
if (!api_is_platform_admin()) {
api_not_allowed();
exit;
}
if (!$meetingId) {
exit;
}
if ($bbb->checkDirectMeetingVideoUrl($meetingId)) {
$meetingInfo = Database::select(
'*',
'plugin_bbb_meeting',
['where' => ['id = ?' => intval($meetingId)]],
'first'
);
$url = $meetingInfo['video_url'].'/capture.m4v';
$link = Display::url(
Display::return_icon('save.png', get_lang('DownloadFile')),
$meetingInfo['video_url'].'/capture.m4v',
['target' => '_blank']
);
header('Content-Type: application/json');
echo json_encode(['url' => $url, 'link' => $link]);
}
break;
}

6
plugin/bbb/config.php Normal file
View File

@@ -0,0 +1,6 @@
<?php
/* For licensing terms, see /license.txt */
/* bbb parameters that will be registered in the course settings */
require_once __DIR__.'/../../main/inc/global.inc.php';

View File

@@ -0,0 +1,38 @@
<?php
/**
* DO vm_min_size_id/vm_max_size_id sizes.
*
* ID Name
* 66 512MB
* 63 1GB
* 62 2GB
* 64 4GB
* 65 8GB
* 61 16GB
* 60 32GB
* 70 48GB
* 70 48GB
* 69 64GB
* 68 96GB
*/
return [
'enabled' => true,
'vms' => [
[
'enabled' => true,
'name' => 'DigitalOcean',
'vm_client_id' => 'client_id',
'api_key' => '123456',
'vm_id' => '123456', // The VM ID we want to access
'vm_min_size_id' => '66', // VM size ID example for 512mb use 66
'vm_max_size_id' => '65', // For 1GB use 63
],
// The Amazon hook is not implemented yet
[
'enabled' => false,
'name' => 'Amazon',
],
],
];

View File

@@ -0,0 +1,11 @@
<?php
/**
* This script is included by the course_home.php script (indirectly) and is
* used to show the link to the plugin from inside the course's tools list.
*
* @package chamilo.plugin.bigbluebutton
*/
/**
* Display course tool title.
*/
echo "Videoconference";

28
plugin/bbb/cron.php Normal file
View File

@@ -0,0 +1,28 @@
<?php
/* For license terms, see /license.txt */
require_once __DIR__.'/../../vendor/autoload.php';
if (file_exists(__DIR__.'/config.vm.php')) {
require_once __DIR__.'/config.php';
require __DIR__.'/lib/vm/AbstractVM.php';
require __DIR__.'/lib/vm/VMInterface.php';
require __DIR__.'/lib/vm/DigitalOceanVM.php';
require __DIR__.'/lib/VM.php';
$config = require __DIR__.'/config.vm.php';
$vm = new VM($config);
if ($vm->isEnabled()) {
$bbb = new bbb();
if ($bbb->pluginEnabled) {
$activeSessions = $bbb->getActiveSessionsCount();
if (empty($activeSessions)) {
$vm->runCron();
} else {
echo "Can't run cron active sessions found: ".$activeSessions;
}
}
}
}

View File

@@ -0,0 +1,113 @@
<?php
/**
* This script initiates a video conference session, calling the BigBlueButton API.
*/
$course_plugin = 'bbb'; //needed in order to load the plugin lang variables
require_once __DIR__.'/config.php';
$plugin = BBBPlugin::create();
$meetingTable = Database::get_main_table('plugin_bbb_meeting');
$roomTable = Database::get_main_table('plugin_bbb_room');
$applyAllUrls = 'true' === $plugin->get('plugin_bbb_multiple_urls_cron_apply_to_all');
$bbb = new bbb();
if (!$bbb->pluginEnabled) {
return;
}
$activeSessions = $bbb->getActiveSessions($applyAllUrls);
if (empty($activeSessions)) {
return;
}
foreach ($activeSessions as $value) {
$meetingId = $value['id'];
$courseCode = null;
$courseInfo = api_get_course_info_by_id($value['c_id']);
if (!empty($courseInfo)) {
$courseCode = $courseInfo['code'];
}
$meetingBBB = $bbb->getMeetingInfo(
[
'meetingId' => $value['remote_id'],
'password' => $value['moderator_pw'],
]
);
if ($meetingBBB === false) {
//checking with the remote_id didn't work, so just in case and
// to provide backwards support, check with the id
$params = [
'meetingId' => $value['id'],
'password' => $value['moderator_pw'],
];
$meetingBBB = $bbb->getMeetingInfo($params);
}
if (empty($meetingBBB)) {
continue;
}
if (!isset($meetingBBB['returncode'])) {
continue;
}
$action = (string) $meetingBBB['returncode'];
switch ($action) {
case 'FAILED':
$bbb->endMeeting($value['id'], $courseCode);
break;
case 'SUCCESS':
Database::update(
$roomTable,
['close' => BBBPlugin::ROOM_CHECK],
['meeting_id = ? AND close= ?' => [$meetingId, BBBPlugin::ROOM_OPEN]]
);
$i = 0;
while ($i < $meetingBBB['participantCount']) {
$participantId = $meetingBBB[$i]['userId'];
$roomData = Database::select(
'*',
$roomTable,
[
'where' => [
'meeting_id = ? AND participant_id = ? AND close = ?' => [
$meetingId,
$participantId,
BBBPlugin::ROOM_CHECK,
],
],
'order' => 'id DESC',
],
'first'
);
if (!empty($roomData)) {
$roomId = $roomData['id'];
if (!empty($roomId)) {
Database::update(
$roomTable,
['out_at' => api_get_utc_datetime(), 'close' => BBBPlugin::ROOM_OPEN],
['id = ? ' => $roomId]
);
}
}
$i++;
}
Database::update(
$roomTable,
['out_at' => api_get_utc_datetime(), 'close' => BBBPlugin::ROOM_CLOSE],
['meeting_id = ? AND close= ?' => [$meetingId, BBBPlugin::ROOM_CHECK]]
);
break;
}
}

1
plugin/bbb/index.php Normal file
View File

@@ -0,0 +1 @@
<?php

10
plugin/bbb/install.php Normal file
View File

@@ -0,0 +1,10 @@
<?php
/* For license terms, see /license.txt */
/**
* This script is included by main/admin/settings.lib.php and generally
* includes things to execute in the main database (settings_current table).
*/
require_once __DIR__.'/config.php';
BBBPlugin::create()->install();

View File

@@ -0,0 +1,78 @@
<?php
/* License: see /license.txt */
// Needed in order to show the plugin title
$strings['plugin_title'] = "Videoconference";
$strings['plugin_comment'] = "Add a videoconference room in a Chamilo course using BigBlueButton (BBB)";
$strings['Videoconference'] = "Videoconference";
$strings['MeetingOpened'] = "Meeting opened";
$strings['MeetingClosed'] = "Meeting closed";
$strings['MeetingClosedComment'] = "If you have asked for your sessions to be recorded, the recording will be available in the list below when it has been completely generated.";
$strings['CloseMeeting'] = "Close meeting";
$strings['VideoConferenceXCourseX'] = "Videoconference #%s course %s";
$strings['VideoConferenceAddedToTheCalendar'] = "Videoconference added to the calendar";
$strings['VideoConferenceAddedToTheLinkTool'] = "Videoconference added to the link tool";
$strings['GoToTheVideoConference'] = "Go to the videoconference";
$strings['Records'] = "Recording";
$strings['Meeting'] = "Meeting";
$strings['ViewRecord'] = "View recording";
$strings['CopyToLinkTool'] = "Copy to link tool";
$strings['EnterConference'] = "Enter the videoconference";
$strings['RecordList'] = "Recording list";
$strings['ServerIsNotRunning'] = "Videoconference server is not running";
$strings['ServerIsNotConfigured'] = "Videoconference server is not configured";
$strings['XUsersOnLine'] = "%s user(s) online";
$strings['host'] = 'BigBlueButton host';
$strings['host_help'] = 'This is the name of the server where your BigBlueButton server is running.
Might be localhost, an IP address (e.g. http://192.168.13.54) or a domain name (e.g. http://my.video.com).';
$strings['salt'] = 'BigBlueButton salt';
$strings['salt_help'] = 'This is the security key of your BigBlueButton server, which will allow your server to authentify the Chamilo installation. Refer to the BigBlueButton documentation to locate it. Try bbb-conf --salt';
$strings['tool_enable'] = 'BigBlueButton videoconference tool enabled';
$strings['tool_enable_help'] = "Choose whether you want to enable the BigBlueButton videoconference tool.
Once enabled, it will show as an additional course tool in all courses' homepage, and teachers will be able to launch a conference at any time. Students will not be able to launch a conference, only join one. If you don't have a BigBlueButton server, please <a target=\"_blank\" href=\"http://bigbluebutton.org/\">set one up</a> or ask the Chamilo official providers for a quote. BigBlueButton is a free (as in freedom *and* beer), but its installation requires a set of technical skills that might not be immediately available to all. You can install it on your own or seek professional help to assist you or do it for you. This help, however, will generate a certain cost. In the pure logic of the free software, we offer you the tools to make your work easier and recommend professionals (the Chamilo Official Providers) that will be able to help you if this were too difficult.<br />";
$strings['big_blue_button_welcome_message'] = 'Welcome message';
$strings['enable_global_conference'] = 'Enable global conference';
$strings['enable_global_conference_per_user'] = 'Enable global conference per user';
$strings['enable_conference_in_course_groups'] = 'Enable conference in course groups';
$strings['enable_global_conference_link'] = 'Enable the link to the global conference in the homepage';
$strings['disable_download_conference_link'] = 'Disable download conference';
$strings['big_blue_button_record_and_store'] = 'Record and store sessions';
$strings['bbb_enable_conference_in_groups'] = 'Allow conference in groups';
$strings['plugin_tool_bbb'] = 'Video';
$strings['ThereAreNotRecordingsForTheMeetings'] = 'There are not recording for the meeting sessions';
$strings['NoRecording'] = 'No recording';
$strings['ClickToContinue'] = 'Click to continue';
$strings['NoGroup'] = 'No group';
$strings['UrlMeetingToShare'] = 'URL to share';
$strings['AdminView'] = 'View for administrators';
$strings['max_users_limit'] = 'Max users limit';
$strings['max_users_limit_help'] = 'Set this to the maximum number of users you want to allow by course or session-course. Leave empty or set to 0 to disable this limit.';
$strings['MaxXUsersWarning'] = 'This conference room has a maximum number of %s simultaneous users.';
$strings['MaxXUsersReached'] = 'The limit of %s simultaneous users has been reached for this conference room. Please wait for one seat to be freed or for another conference to start in order to join.';
$strings['MaxXUsersReachedManager'] = 'The limit of %s simultaneous users has been reached for this conference room. To increase this limit, please contact the platform administrator.';
$strings['MaxUsersInConferenceRoom'] = 'Max simultaneous users in a conference room';
$strings['global_conference_allow_roles'] = "Global conference link only visible for these user roles";
$strings['CreatedAt'] = 'Created at';
$strings['allow_regenerate_recording'] = 'Allow regenerate recording';
$strings['bbb_force_record_generation'] = 'Force record generation at the end of the meeting';
$strings['disable_course_settings'] = 'Disable course settings';
$strings['UpdateAllCourses'] = 'Update all courses';
$strings['UpdateAllCourseSettings'] = 'Update all course settings';
$strings['ThisWillUpdateAllSettingsInAllCourses'] = 'This will update at once all your course settings.';
$strings['ThereIsNoVideoConferenceActive'] = 'There is no videoconference currently active';
$strings['RoomClosed'] = 'Room closed';
$strings['RoomClosedComment'] = ' ';
$strings['meeting_duration'] = 'Meeting duration (in minutes)';
$strings['big_blue_button_students_start_conference_in_groups'] = 'Allow students to start conference in their groups.';
$strings['plugin_bbb_multiple_urls_cron_apply_to_all'] = 'Automatically closes all rooms on ALL campuses.';
$strings['plugin_bbb_multiple_urls_cron_apply_to_all_help'] = 'Option for multi-url environments. Allows the CRON task to close all open rooms on mother and child campus.';

View File

@@ -0,0 +1,71 @@
<?php
/* License: see /license.txt */
//Needed in order to show the plugin title
$strings['plugin_title'] = "Vidéoconférence";
$strings['plugin_comment'] = "Ajoutez un espace de vidéoconférences aux cours de Chamilo avec BigBlueButton (BBB)";
$strings['Videoconference'] = "Vidéoconférence";
$strings['MeetingOpened'] = "Session ouverte";
$strings['MeetingClosed'] = "Session fermée";
$strings['MeetingClosedComment'] = "Si vous avez demandé l'enregistrement des sessions de conférence, cet enregistrement apparaîtra dans la liste ci-dessous dans quelques instants.";
$strings['CloseMeeting'] = "Fermer la session";
$strings['VideoConferenceXCourseX'] = "Vidéoconférence #%s, cours %s";
$strings['VideoConferenceAddedToTheCalendar'] = "Vidéoconférence ajoutée au calendrier";
$strings['VideoConferenceAddedToTheLinkTool'] = "Vidéoconférence ajoutée comme lien. Vous pouvez éditer et publier le lien sur la page principale du cours depuis l'outil liens.";
$strings['GoToTheVideoConference'] = "Entrer dans la salle de conférence";
$strings['Records'] = "Enregistrement";
$strings['Meeting'] = "Salle de conférence";
$strings['ViewRecord'] = "Voir l'enregistrement";
$strings['CopyToLinkTool'] = "Ajouter comme lien du cours";
$strings['EnterConference'] = "Entrer dans la salle de conférence";
$strings['RecordList'] = "Liste des enregistrements";
$strings['ServerIsNotRunning'] = "Le serveur de vidéoconférence ne fonctionne pas";
$strings['ServerIsNotConfigured'] = "Le serveur de vidéoconférence n'est pas configuré correctement";
$strings['XUsersOnLine'] = "%s utilisateurs dans la salle";
$strings['host'] = 'Hôte de BigBlueButton';
$strings['host_help'] = "C'est le nom du serveur où le serveur de vidéoconférence a été habilité.
Cela peut être localhost, une adresse IP (du genre http://192.168.13.54) ou un nom de domaine (du genre http://ma.video.com).";
$strings['salt'] = 'Clef BigBlueButton';
$strings['salt_help'] = "C'est la clef de sécurité de votre serveur BigBlueButton (appelée 'salt' en anglais) qui permet à votre serveur de vérifier l'identité de votre installation de Chamilo et ainsi l'autoriser à se connecter. Veuillez vous référer à la documentation de BigBlueButton pour la localiser, ou utilisez la commande 'bbb-conf --salt' si vous disposez d'un accès en ligne de commande au serveur de vidéoconférence.";
$strings['tool_enable'] = 'Outil de vidéoconférence BigBlueButton activé';
$strings['tool_enable_help'] = "Choisissez si vous souhaitez activer l'outil de vidéoconférence BigBlueButton.
Une fois activé, il apparaîtra comme un outil additionnel sur toutes les pages principales de cours, et les enseignants pourront démarrer une conférence à n'importe quel moment. Les étudiants ne pourront pas lancer de nouvelle session de conférence, seulement se joindre à une session existante. Si vous ne disposez pas d'un serveur de vidéoconférence BigBlueButton, veuillez <a target=\"_blank\" href=\"http://bigbluebutton.org/\">en installer un</a> avant de poursuivre, ou demander un devis à l'un des fournisseurs officiels de Chamilo. BigBlueButton est un outil de logiciel libre (et gratuit), mais son installation pourrait présenter une certaine complexité et demander des compétences qui ne sont peut-être pas à la portée de tous. Vous pouvez l'installer vous-même à partir de la documentation (disponible publiquement) de BigBlueButton, ou recherchez un soutien professionnel. Ce soutien pourrait générer certains coûts (au moins le temps de la personne qui vous assiste dans l'opération). Dans le plus pur esprit du logiciel libre, nous vous fournissons les outils pour simplifier votre travail dans la mesure de nos possibilités, et nous vous recommandons des professionnels (les fournisseurs officiels de Chamilo) pour vous venir en aide au cas où ceux-ci seraient insuffisants.<br />";
$strings['big_blue_button_welcome_message'] = 'Message de bienvenue de BigBlueButton';
$strings['enable_global_conference'] = 'Activer les conférences globales';
$strings['enable_global_conference_per_user'] = 'Activer les conférences globales par utilisateur';
$strings['enable_conference_in_course_groups'] = 'Activer les conférences dans les groupes';
$strings['enable_global_conference_link'] = 'Activer le lien vers la conférence globale sur la page principale';
$strings['big_blue_button_record_and_store'] = 'Enregistrer les sessions de vidéoconférence';
$strings['bbb_enable_conference_in_groups'] = 'Permettre la création de vidéoconférence pour les groupes';
$strings['plugin_tool_bbb'] = 'Vidéo';
$strings['ThereAreNotRecordingsForTheMeetings'] = 'Aucun enregistrement disponible';
$strings['NoRecording'] = "Pas d'enregistrement";
$strings['ClickToContinue'] = 'Cliquez pour continuer';
$strings['NoGroup'] = 'Sans groupe';
$strings['UrlMeetingToShare'] = 'URL à partager';
$strings['AdminView'] = 'Vue administrateur';
$strings['max_users_limit'] = 'Utilisateurs maximum';
$strings['max_users_limit_help'] = 'Nombre maximum d\'utilisateurs simultanés dans une salle de vidéoconférence de cours ou cours-session. Laisser vide ou sur 0 pour ne pas assigner de limite.';
$strings['MaxXUsersWarning'] = 'Cette salle de conférence est limitée à %s utilisateurs simultanés.';
$strings['MaxXUsersReached'] = 'La limite de %s utilisateurs simultanés a été atteinte dans cette salle de conférence. Veuillez rafraîchir dans quelque minutes pour voir si un siège s\'est libéré, ou attendre l\'ouverture d\'une nouvelle salle de conférence pour participer.';
$strings['MaxXUsersReachedManager'] = 'La limite de %s utilisateurs simultanés a été atteinte dans cette salle de conférence. Pour augmenter la limite, prenez contact avec l\'administrateur du portail.';
$strings['MaxUsersInConferenceRoom'] = 'Nombre max d\'utilisateurs simultanés dans une salle de conférence';
$strings['global_conference_allow_roles'] = "Visibilité du lien de vidéo conférence global pour les profils suivant";
$strings['CreatedAt'] = "Créé à";
$strings['bbb_force_record_generation'] = 'Forcer la génération de l\'enregistrement à la fin de la session';
$strings['ThereIsNoVideoConferenceActive'] = "Il n'y a aucune vidéoconférence actuellement active";
$strings['meeting_duration'] = 'Durée de la conférence (en minutes)';
$strings['big_blue_button_students_start_conference_in_groups'] = 'Permettre aux apprenants de démarrer les vidéoconferénces de leurs groupes';

View File

@@ -0,0 +1,65 @@
<?php
/* License: see /license.txt */
// Needed in order to show the plugin title
$strings['plugin_title'] = "Videokonferenz";
$strings['plugin_comment'] = "Add a videoconference room in a Chamilo course using BigBlueButton (BBB)";
$strings['Videoconference'] = "Videokonferenz";
$strings['MeetingOpened'] = "Offen";
$strings['MeetingClosed'] = "Beendet";
$strings['MeetingClosedComment'] = "Wenn Sie die Videokonferenz aufgezeichnet haben, ist die Videoaufzeichnung in der Liste unten in wenigen Augenblicken verfügbar.";
$strings['CloseMeeting'] = "Meeting beenden";
$strings['VideoConferenceXCourseX'] = "Videokonferenz #%s Lerninsel %s";
$strings['VideoConferenceAddedToTheCalendar'] = "Videokonferenz zum Kalender hinzugefügt";
$strings['VideoConferenceAddedToTheLinkTool'] = "Videokonferenz zum Link-Tool hinzugefügt";
$strings['GoToTheVideoConference'] = "Gehe zur Videokonferenz";
$strings['Records'] = "Videoaufzeichnung";
$strings['Meeting'] = "Meeting";
$strings['ViewRecord'] = "Videoaufzeichnung ansehen";
$strings['CopyToLinkTool'] = "Copy to link tool";
$strings['EnterConference'] = "Videokonferenz starten";
$strings['RecordList'] = "Liste der Videoaufzeichnungen";
$strings['ServerIsNotRunning'] = "Videokonferenzserver läuft nicht";
$strings['ServerIsNotConfigured'] = "Videokonferenzserver ist nicht konfiguriert";
$strings['XUsersOnLine'] = "%s Benutzer online";
$strings['host'] = 'BigBlueButton host';
$strings['host_help'] = 'This is the name of the server where your BigBlueButton server is running. Might be localhost, an IP address (e.g. http://192.168.13.54) or a domain name (e.g. http://my.video.com).';
$strings['salt'] = 'BigBlueButton salt';
$strings['salt_help'] = 'This is the security key of your BigBlueButton server, which will allow your server to authentify the Chamilo installation. Refer to the BigBlueButton documentation to locate it. Try bbb-conf --salt';
$strings['tool_enable'] = 'BigBlueButton Videokonferenz-Tool aktiviert';
$strings['tool_enable_help'] = "Choose whether you want to enable the BigBlueButton videoconference tool.
Once enabled, it will show as an additional course tool in all courses' homepage, and teachers will be able to launch a conference at any time. Students will not be able to launch a conference, only join one. If you don't have a BigBlueButton server, please <a target=\"_blank\" href=\"http://bigbluebutton.org/\">set one up</a> or ask the Chamilo official providers for a quote. BigBlueButton is a free (as in freedom *and* beer), but its installation requires a set of technical skills that might not be immediately available to all. You can install it on your own or seek professional help to assist you or do it for you. This help, however, will generate a certain cost. In the pure logic of the free software, we offer you the tools to make your work easier and recommend professionals (the Chamilo Official Providers) that will be able to help you if this were too difficult.<br />";
$strings['big_blue_button_welcome_message'] = 'Willkommensnachricht';
$strings['enable_global_conference'] = 'Globale Konferenz aktivieren';
$strings['enable_global_conference_per_user'] = 'Globale Konferenz pro Benutzer aktivieren';
$strings['enable_conference_in_course_groups'] = 'Konferenz in Kurs-Gruppen aktivieren';
$strings['enable_global_conference_link'] = 'Aktivieren Sie den Link zur globalen Konferenz auf der Website';
$strings['big_blue_button_record_and_store'] = 'Aufnehmen und Speichern von Meetings';
$strings['bbb_enable_conference_in_groups'] = 'Konferenz in Gruppen zulassen';
$strings['plugin_tool_bbb'] = 'Video';
$strings['ThereAreNotRecordingsForTheMeetings'] = 'Es gibt keine Aufnahmen von den Meetings';
$strings['NoRecording'] = 'Keine Videoaufzeichnung';
$strings['ClickToContinue'] = 'Klicken um fortzufahren';
$strings['NoGroup'] = 'Keine Gruppe';
$strings['UrlMeetingToShare'] = 'URL zu teilen';
$strings['AdminView'] = 'Administrator Ansicht';
$strings['max_users_limit'] = 'Maximale Anzahl von Benutzern';
$strings['max_users_limit_help'] = 'Setzen Sie diese auf die maximale Anzahl der Benutzer, die Sie nach Kurs oder Session-Kurs erlauben möchten. Leer lassen oder auf 0 setzen, um dieses Limit zu deaktivieren.';
$strings['MaxXUsersWarning'] = 'Dieser Konferenzraum hat eine maximale Anzahl von %s gleichzeitigen Benutzern.';
$strings['MaxXUsersReached'] = 'The limit of %s simultaneous users has been reached for this conference room. Please wait for one seat to be freed or for another conference to start in order to join.';
$strings['MaxXUsersReachedManager'] = 'The limit of %s simultaneous users has been reached for this conference room. To increase this limit, please contact the platform administrator.';
$strings['MaxUsersInConferenceRoom'] = 'Max simultaneous users in a conference room';
$strings['global_conference_allow_roles'] = "Globaler Konferenz-Link nur für diese Benutzerrollen sichtbar";
$strings['CreatedAt'] = "Erstellt am";

View File

@@ -0,0 +1,71 @@
<?php
/* License: see /license.txt */
// Needed in order to show the plugin title
$strings['plugin_title'] = "Videoconferencia";
$strings['plugin_comment'] = "Añade una sala de videoconferencia en los cursos de Chamilo con BigBlueButton (BBB)";
$strings['Videoconference'] = "Videoconferencia";
$strings['MeetingOpened'] = "Sala abierta";
$strings['MeetingClosed'] = "Sala cerrada";
$strings['MeetingClosedComment'] = "Si ha pedido grabar la sesión de videoconferencia en los parámetros del curso, esta grabación aparecerá en la lista siguiente una vez generada.";
$strings['CloseMeeting'] = "Cerrar sala";
$strings['RoomExit'] = "Ha salido de la sesión de Videoconferencia";
$strings['VideoConferenceXCourseX'] = "Videoconferencia #%s, curso %s";
$strings['VideoConferenceAddedToTheCalendar'] = "Videoconferencia añadida al calendario";
$strings['VideoConferenceAddedToTheLinkTool'] = "Videoconferencia añadida como enlace. Puede editar y publicar el enlace en la página principal del curso desde la herramienta de enlace.";
$strings['GoToTheVideoConference'] = "Ir a la videoconferencia";
$strings['Records'] = "Grabación";
$strings['Meeting'] = "Sala de conferencia";
$strings['ViewRecord'] = "Ver grabación";
$strings['CopyToLinkTool'] = "Añadir como enlace del curso";
$strings['EnterConference'] = "Entrar a la videoconferencia";
$strings['RecordList'] = "Lista de grabaciones";
$strings['ServerIsNotRunning'] = "El servidor de videoconferencia no está funcionando";
$strings['ServerIsNotConfigured'] = "El servidor de videoconferencia no está configurado correctamente";
$strings['XUsersOnLine'] = "%s usuario(s) en la sala";
$strings['host'] = 'Host de BigBlueButton';
$strings['host_help'] = 'Este es el nombre del servidor donde su servidor BigBlueButton está corriendo. Puede ser localhost, una dirección IP (ej: http://192.168.13.54) o un nombre de dominio (ej: http://mi.video.com).';
$strings['salt'] = 'Clave BigBlueButton';
$strings['salt_help'] = 'Esta es la llave de seguridad de su servidor BigBlueButton (llamada "salt" en inglés), que permitirá a su servidor de autentifica la instalación de Chamilo (como autorizada). Refiérese a la documentación de BigBlueButton para ubicarla, o use el comando "bbb-conf --salt" si tiene acceso al servidor en línea de comando.';
$strings['tool_enable'] = 'Herramienta de videoconferencia BigBlueButton activada';
$strings['tool_enable_help'] = "Escoja si desea activar la herramienta de videoconferencia BigBlueButton.
Una vez activada, se mostrará como una herramienta adicional en todas las páginas principales de cursos, y los profesores podrán iniciar una conferencia en cualquier momento. Los estudiantes no podrían lanzar una conferencia, solo juntarse a una existente. Si no tiene un servidor de videoconferencia BigBlueButton, por favor <a target=\"_blank\" href=\"http://bigbluebutton.org/\">configure uno</a> antes de seguir, o pida una cotización a uno de los proveedores oficiales de Chamilo. BigBlueButton es una herramienta de software libre (y gratuita), pero su instalación requiere de competencias que quizás no sean inmediatamente disponibles para todos. Puede instalarla usted mismo o buscar ayuda profesional. Esta ayuda podría no obstante generar algunos costos (por lo menos el tiempo de la persona quien lo ayude). En el puro espíritu del software libre, le ofrecemos las herramientas para hacer su trabajo más simple, en la medida de nuestras posibilidades, y le recomendamos profesionales (los proveedores oficiales de Chamilo) para ayudarlo en caso esto fuera demasiado complicado.<br />";
$strings['big_blue_button_welcome_message'] = 'Mensaje de bienvenida de BigBlueButton';
$strings['enable_global_conference'] = 'Activar la conferencia global';
$strings['enable_global_conference_per_user'] = 'Activar la conferencia global por usuario';
$strings['enable_conference_in_course_groups'] = 'Activar las conferencias en grupos';
$strings['enable_global_conference_link'] = 'Activar el enlace hacia la conferencia global desde la página principal';
$strings['big_blue_button_record_and_store'] = 'Grabar las sesiones de videoconferencia.';
$strings['bbb_enable_conference_in_groups'] = 'Activar la creación de videoconferencia en los grupos.';
$strings['plugin_tool_bbb'] = 'Videoconferencia';
$strings['ThereAreNotRecordingsForTheMeetings'] = 'No hay grabaciones de sesiones de videoconferencia';
$strings['NoRecording'] = 'No hay grabación';
$strings['ClickToContinue'] = 'Hacer click para continuar';
$strings['NoGroup'] = 'No hay grupo';
$strings['UrlMeetingToShare'] = 'URL a compartir';
$strings['AdminView'] = 'Vista para administradores';
$strings['max_users_limit'] = 'Cantidad máxima de usuarios';
$strings['max_users_limit_help'] = 'Este valor indica la cantidad máxima de usuarios simultáneos en una conferencia en un curso o curso-sesión. Dejar vacío o en 0 para no poner límite.';
$strings['MaxXUsersWarning'] = 'Esta sala de conferencia es limitada a un máximo de %s usuarios simultáneos.';
$strings['MaxXUsersReached'] = 'El límite de %s usuarios simultáneos ha sido alcanzado en esta sala de conferencia. Por favor refresque la página en unos minutos para ver si un asiento se ha liberado, o espere la apertura de una nueva sala para poder participar.';
$strings['MaxXUsersReachedManager'] = 'El límite de %s usuarios simultáneos ha sido alcanzado en esta sala de conferencia. Para aumentar el límite, contáctese con el administrador del portal.';
$strings['MaxUsersInConferenceRoom'] = 'Número máximo de usuarios simultáneos en una sala de conferencia';
$strings['global_conference_allow_roles'] = 'El enlace de videoconferencia global es disponible para estos perfiles';
$strings['CreatedAt'] = 'Creado el';
$strings['ThereIsNoVideoConferenceActive'] = "No hay una videoconferencia actualmente activa";
$strings['meeting_duration'] = 'Duración de la reunión (en minutos)';
$strings['big_blue_button_students_start_conference_in_groups'] = 'Permitir a los estudiantes iniciar una videoconferencia en sus grupos.';
$strings['plugin_bbb_multiple_urls_cron_apply_to_all'] = 'Cerrar automáticamente todas las salas sin actividad en TODOS los campus.';
$strings['plugin_bbb_multiple_urls_cron_apply_to_all_help'] = 'Opción para entornos multi-url. Permite a la tarea CRON cerrar todas las salas abiertas del campus madre e hijos.';

116
plugin/bbb/lib/VM.php Normal file
View File

@@ -0,0 +1,116 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Class VM
*/
class VM
{
protected $config;
public $virtualMachine;
/**
* VM constructor.
* @param $config
*/
public function __construct($config)
{
$this->config = $config;
}
/**
* @return array
*/
public function getConfig()
{
return $this->config;
}
/**
* @param bool $checkEnabled Check if, additionnally to being installed, the plugin is enabled
* @return bool
*/
public function isEnabled(bool $checkEnabled = false): bool
{
$config = $this->getConfig();
if (!isset($config)) {
return false;
}
if (!is_array($config)) {
return false;
}
if (isset($config['enabled']) && $config['enabled']) {
return true;
}
return false;
}
/**
* @return VirtualMachineInterface
*/
public function getVirtualMachine()
{
return $this->virtualMachine;
}
/**
* @param VirtualMachineInterface $virtualMachine
*/
public function setVirtualMachine(VirtualMachineInterface $virtualMachine)
{
$this->virtualMachine = $virtualMachine;
}
/**
* @return VirtualMachineInterface
*/
public function getVirtualMachineFromConfig()
{
$vmList = $this->config['vms'];
foreach ($vmList as $vm) {
if (isset($vm['enabled']) && $vm['enabled'] == true) {
$className = $vm['name'].'VM';
return new $className($vm);
break;
}
}
return false;
}
/**
* Resize the VM to the max size
*/
public function resizeToMaxLimit()
{
$virtualMachine = $this->getVirtualMachineFromConfig();
$this->setVirtualMachine($virtualMachine);
$virtualMachine->resizeToMaxLimit();
}
/**
* Resize the VM to the min size
*/
public function resizeToMinLimit()
{
$virtualMachine = $this->getVirtualMachineFromConfig();
$this->setVirtualMachine($virtualMachine);
$virtualMachine->resizeToMinLimit();
}
public function runCron()
{
$virtualMachine = $this->getVirtualMachineFromConfig();
$this->setVirtualMachine($virtualMachine);
$virtualMachine->runCron();
}
}

2232
plugin/bbb/lib/bbb.lib.php Normal file

File diff suppressed because it is too large Load Diff

697
plugin/bbb/lib/bbb_api.php Normal file
View File

@@ -0,0 +1,697 @@
<?php
/*
Copyright 2010 Blindside Networks
This program 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 2 of the License, or
(at your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Versions:
1.0 -- Initial version written by DJP
(email: djp [a t ] architectes DOT .org)
1.1 -- Updated by Omar Shammas and Sebastian Schneider
(email : omar DOT shammas [a t ] g m ail DOT com)
(email : seb DOT sschneider [ a t ] g m ail DOT com)
1.2 -- Updated by Omar Shammas
(email : omar DOT shammas [a t ] g m ail DOT com)
1.3 -- Refactored by Peter Mentzer
(email : peter@petermentzerdesign.com)
- This update will BREAK your external existing code if
you've used the previous versions <= 1.2 already so:
-- update your external code to use new method names if needed
-- update your external code to pass new parameters to methods
- Working example of joinIfRunning.php now included
- Added support for BBB 0.8b recordings
- Now using Zend coding, naming and style conventions
- Refactored methods to accept standardized parameters & match BBB API structure
-- See included samples for usage examples
*/
class BigBlueButtonBN
{
private $_securitySalt;
private $_bbbServerBaseUrl;
private $_bbbServerProtocol;
public function __construct()
{
/*
Establish just our basic elements in the constructor:
*/
// BASE CONFIGS - set these for your BBB server in config.php and they will
// simply flow in here via the constants:
$this->_securitySalt = CONFIG_SECURITY_SALT;
$this->_bbbServerBaseUrl = CONFIG_SERVER_BASE_URL;
$this->_bbbServerProtocol = CONFIG_SERVER_PROTOCOL;
}
private function _processXmlResponse($url)
{
/*
A private utility method used by other public methods to process XML responses.
*/
if (extension_loaded('curl')) {
$ch = curl_init() or die ( curl_error($ch) );
$timeout = 10;
curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt( $ch, CURLOPT_URL, $this->_bbbServerProtocol.$url );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, $timeout);
// Following redirect required to use Scalelite, BBB's Load Balancer
curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true);
$data = curl_exec( $ch );
curl_close( $ch );
if ($data) {
return (new SimpleXMLElement($data));
} else {
return false;
}
}
return (simplexml_load_file($url));
}
private function _requiredParam($param) {
/* Process required params and throw errors if we don't get values */
if ((isset($param)) && ($param != '')) {
return $param;
}
elseif (!isset($param)) {
throw new Exception('Missing parameter.');
}
else {
throw new Exception(''.$param.' is required.');
}
}
private function _optionalParam($param) {
/* Pass most optional params through as set value, or set to '' */
/* Don't know if we'll use this one, but let's build it in case. */
if ((isset($param)) && ($param != '')) {
return $param;
}
else {
$param = '';
return $param;
}
}
/* __________________ BBB ADMINISTRATION METHODS _________________ */
/* The methods in the following section support the following categories of the BBB API:
-- create
-- join
-- end
*/
public function getCreateMeetingUrl($creationParams) {
/*
USAGE:
(see $creationParams array in createMeetingArray method.)
*/
$this->_meetingId = $this->_requiredParam($creationParams['meetingId']);
$this->_meetingName = $this->_requiredParam($creationParams['meetingName']);
// Set up the basic creation URL:
$creationUrl = $this->_bbbServerBaseUrl."api/create?";
// Add params:
$params =
'name='.urlencode($this->_meetingName).
'&meetingID='.urlencode($this->_meetingId).
'&attendeePW='.urlencode($creationParams['attendeePw']).
'&moderatorPW='.urlencode($creationParams['moderatorPw']).
'&dialNumber='.urlencode($creationParams['dialNumber']).
'&voiceBridge='.urlencode($creationParams['voiceBridge']).
'&webVoice='.urlencode($creationParams['webVoice']).
'&logoutURL='.urlencode($creationParams['logoutUrl']).
'&maxParticipants='.urlencode($creationParams['maxParticipants']).
'&record='.urlencode($creationParams['record']).
'&duration='.urlencode($creationParams['duration']).
'&meta_OriginURL'.urlencode($creationParams['meta_OriginURL'])
;
//'&meta_category='.urlencode($creationParams['meta_category']);
$welcomeMessage = $creationParams['welcomeMsg'];
if (trim($welcomeMessage)) {
$params .= '&welcome='.urlencode($welcomeMessage);
}
// Return the complete URL:
return ($creationUrl.$params.'&checksum='.sha1("create".$params.$this->_securitySalt));
}
public function createMeetingWithXmlResponseArray($creationParams)
{
/*
USAGE:
$creationParams = array(
'name' => 'Meeting Name', -- A name for the meeting (or username)
'meetingId' => '1234', -- A unique id for the meeting
'attendeePw' => 'ap', -- Set to 'ap' and use 'ap' to join = no user pass required.
'moderatorPw' => 'mp', -- Set to 'mp' and use 'mp' to join = no user pass required.
'welcomeMsg' => '', -- ''= use default. Change to customize.
'dialNumber' => '', -- The main number to call into. Optional.
'voiceBridge' => '', -- 5 digits PIN to join voice. Required.
'webVoice' => '', -- Alphanumeric to join voice. Optional.
'logoutUrl' => '', -- Default in bigbluebutton.properties. Optional.
'maxParticipants' => '-1', -- Optional. -1 = unlimitted. Not supported in BBB. [number]
'record' => 'false', -- New. 'true' will tell BBB to record the meeting.
'duration' => '0', -- Default = 0 which means no set duration in minutes. [number]
'meta_category' => '', -- Use to pass additional info to BBB server. See API docs to enable.
);
*/
$xml = $this->_processXmlResponse($this->getCreateMeetingURL($creationParams));
if ($xml) {
if ($xml->meetingID) {
return [
'returncode' => $xml->returncode->__toString(),
'message' => $xml->message->__toString(),
'messageKey' => $xml->messageKey->__toString(),
'meetingId' => $xml->meetingID->__toString(),
'attendeePw' => $xml->attendeePW->__toString(),
'moderatorPw' => $xml->moderatorPW->__toString(),
'hasBeenForciblyEnded' => $xml->hasBeenForciblyEnded->__toString(),
'createTime' => $xml->createTime->__toString(),
'internalMeetingID' => $xml->internalMeetingID->__toString(),
];
} else {
return [
'returncode' => $xml->returncode->__toString(),
'message' => $xml->message->__toString(),
'messageKey' => $xml->messageKey->__toString(),
];
}
} else {
return null;
}
}
public function getJoinMeetingURL($joinParams)
{
/*
NOTE: At this point, we don't use a corresponding joinMeetingWithXmlResponse here because the API
doesn't respond on success, but you can still code that method if you need it. Or, you can take the URL
that's returned from this method and simply send your users off to that URL in your code.
USAGE:
$joinParams = array(
'meetingId' => '1234', -- REQUIRED - A unique id for the meeting
'username' => 'Jane Doe', -- REQUIRED - The name that will display for the user in the meeting
'password' => 'ap', -- REQUIRED - The attendee or moderator password, depending on what's passed here
'createTime' => '', -- OPTIONAL - string. Leave blank ('') unless you set this correctly.
'userID' => '', -- OPTIONAL - string
'webVoiceConf' => '' -- OPTIONAL - string
);
*/
$this->_meetingId = $this->_requiredParam($joinParams['meetingId']);
$this->_username = $this->_requiredParam($joinParams['username']);
$this->_password = $this->_requiredParam($joinParams['password']);
// Establish the basic join URL:
$joinUrl = $this->_bbbServerBaseUrl."api/join?";
// Add parameters to the URL:
$params =
'meetingID='.urlencode($this->_meetingId).
'&fullName='.urlencode($this->_username).
'&password='.urlencode($this->_password).
'&userID='.urlencode($joinParams['userID']).
'&webVoiceConf='.urlencode($joinParams['webVoiceConf'])
;
// Only use createTime if we really want to use it. If it's '', then don't pass it:
if ((isset($joinParams['createTime']) && $joinParams['createTime'] != '')) {
$params .= '&createTime='.urlencode($joinParams['createTime']);
}
/*if (isset($joinParams['interface']) && (int) $joinParams['interface'] === BBBPlugin::INTERFACE_HTML5) {
$bbbHost = api_remove_trailing_slash(CONFIG_SERVER_URL_WITH_PROTOCOL);
if (preg_match('#/bigbluebutton$#', $bbbHost)) {
$bbbHost = preg_replace('#/bigbluebutton$#', '', $bbbHost);
}
$params .= '&redirectClient=true&clientURL='.$bbbHost.'/html5client/join';
}*/
// Return the URL:
return $joinUrl.$params.'&checksum='.sha1('join'.$params.$this->_securitySalt);
}
public function getEndMeetingURL($endParams)
{
/* USAGE:
$endParams = array (
'meetingId' => '1234', -- REQUIRED - The unique id for the meeting
'password' => 'mp' -- REQUIRED - The moderator password for the meeting
);
*/
$this->_meetingId = $this->_requiredParam($endParams['meetingId']);
$this->_password = $this->_requiredParam($endParams['password']);
$endUrl = $this->_bbbServerBaseUrl."api/end?";
$params =
'meetingID='.urlencode($this->_meetingId).
'&password='.urlencode($this->_password)
;
return ($endUrl.$params.'&checksum='.sha1("end".$params.$this->_securitySalt));
}
public function endMeetingWithXmlResponseArray($endParams) {
/* USAGE:
$endParams = array (
'meetingId' => '1234', -- REQUIRED - The unique id for the meeting
'password' => 'mp' -- REQUIRED - The moderator password for the meeting
);
*/
$xml = $this->_processXmlResponse($this->getEndMeetingURL($endParams));
if ($xml) {
return array(
'returncode' => $xml->returncode->__toString(),
'message' => $xml->message->__toString(),
'messageKey' => $xml->messageKey->__toString()
);
}
else {
return null;
}
}
/* __________________ BBB MONITORING METHODS _________________ */
/* The methods in the following section support the following categories of the BBB API:
-- isMeetingRunning
-- getMeetings
-- getMeetingInfo
*/
public function getIsMeetingRunningUrl($meetingId) {
/* USAGE:
$meetingId = '1234' -- REQUIRED - The unique id for the meeting
*/
$this->_meetingId = $this->_requiredParam($meetingId);
$runningUrl = $this->_bbbServerBaseUrl."api/isMeetingRunning?";
$params =
'meetingID='.urlencode($this->_meetingId);
return ($runningUrl.$params.'&checksum='.sha1("isMeetingRunning".$params.$this->_securitySalt));
}
public function isMeetingRunningWithXmlResponseArray($meetingId) {
/* USAGE:
$meetingId = '1234' -- REQUIRED - The unique id for the meeting
*/
$xml = $this->_processXmlResponse($this->getIsMeetingRunningUrl($meetingId));
if($xml) {
return array(
'returncode' => $xml->returncode->__toString(),
'running' => $xml->running->__toString() // -- Returns true/false.
);
}
else {
return null;
}
}
public function getGetMeetingsUrl() {
/* Simply formulate the getMeetings URL
We do this in a separate function so we have the option to just get this
URL and print it if we want for some reason.
*/
$getMeetingsUrl = $this->_bbbServerBaseUrl."api/getMeetings?checksum=".sha1("getMeetings".$this->_securitySalt);
return $getMeetingsUrl;
}
public function getMeetingsWithXmlResponseArray()
{
/* USAGE:
We don't need to pass any parameters with this one, so we just send the query URL off to BBB
and then handle the results that we get in the XML response.
*/
$xml = $this->_processXmlResponse($this->getGetMeetingsUrl());
if($xml) {
// If we don't get a success code, stop processing and return just the returncode:
if ($xml->returncode != 'SUCCESS') {
$result = array(
'returncode' => $xml->returncode->__toString()
);
return $result;
}
elseif ($xml->messageKey == 'noMeetings') {
/* No meetings on server, so return just this info: */
$result = array(
'returncode' => $xml->returncode->__toString(),
'messageKey' => $xml->messageKey->__toString(),
'message' => $xml->message->__toString()
);
return $result;
}
else {
// In this case, we have success and meetings. First return general response:
$result = array(
'returncode' => $xml->returncode->__toString(),
'messageKey' => $xml->messageKey->__toString(),
'message' => $xml->message->__toString()
);
// Then interate through meeting results and return them as part of the array:
foreach ($xml->meetings->meeting as $m) {
$result[] = array(
'meetingId' => $m->meetingID->__toString(),
'meetingName' => $m->meetingName->__toString(),
'createTime' => $m->createTime->__toString(),
'attendeePw' => $m->attendeePW->__toString(),
'moderatorPw' => $m->moderatorPW->__toString(),
'hasBeenForciblyEnded' => $m->hasBeenForciblyEnded->__toString(),
'running' => $m->running->__toString()
);
}
return $result;
}
}
else {
return null;
}
}
public function getMeetingInfoUrl($infoParams) {
/* USAGE:
$infoParams = array(
'meetingId' => '1234', -- REQUIRED - The unique id for the meeting
'password' => 'mp' -- REQUIRED - The moderator password for the meeting
);
*/
$this->_meetingId = $this->_requiredParam($infoParams['meetingId']);
$this->_password = $this->_requiredParam($infoParams['password']);
$infoUrl = $this->_bbbServerBaseUrl."api/getMeetingInfo?";
$params =
'meetingID='.urlencode($this->_meetingId).
'&password='.urlencode($this->_password);
return ($infoUrl.$params.'&checksum='.sha1("getMeetingInfo".$params.$this->_securitySalt));
}
public function getMeetingInfoWithXmlResponseArray($infoParams) {
/* USAGE:
$infoParams = array(
'meetingId' => '1234', -- REQUIRED - The unique id for the meeting
'password' => 'mp' -- REQUIRED - The moderator password for the meeting
);
*/
$xml = $this->_processXmlResponse($this->getMeetingInfoUrl($infoParams));
if($xml) {
// If we don't get a success code or messageKey, find out why:
if (($xml->returncode != 'SUCCESS') || ($xml->messageKey == null)) {
$result = array(
'returncode' => $xml->returncode->__toString(),
'messageKey' => $xml->messageKey->__toString(),
'message' => $xml->message->__toString()
);
return $result;
} else {
// In this case, we have success and meeting info:
$result = array(
'returncode' => $xml->returncode->__toString(),
'meetingName' => $xml->meetingName->__toString(),
'meetingId' => $xml->meetingID->__toString(),
'createTime' => $xml->createTime->__toString(),
'voiceBridge' => $xml->voiceBridge->__toString(),
'attendeePw' => $xml->attendeePW->__toString(),
'moderatorPw' => $xml->moderatorPW->__toString(),
'running' => $xml->running->__toString(),
'recording' => $xml->recording->__toString(),
'hasBeenForciblyEnded' => $xml->hasBeenForciblyEnded->__toString(),
'startTime' => $xml->startTime->__toString(),
'endTime' => $xml->endTime->__toString(),
'participantCount' => $xml->participantCount->__toString(),
'maxUsers' => $xml->maxUsers->__toString(),
'moderatorCount' => $xml->moderatorCount->__toString(),
'internalMeetingID' => $xml->internalMeetingID->__toString()
);
// Then interate through attendee results and return them as part of the array:
foreach ($xml->attendees->attendee as $a) {
$result[] = array(
'userId' => $a->userID->__toString(),
'fullName' => $a->fullName->__toString(),
'role' => $a->role->__toString()
);
}
return $result;
}
}
else {
return null;
}
}
/* __________________ BBB RECORDING METHODS _________________ */
/* The methods in the following section support the following categories of the BBB API:
-- getRecordings
-- publishRecordings
-- deleteRecordings
*/
public function getRecordingsUrl($recordingParams) {
/* USAGE:
$recordingParams = array(
'meetingId' => '1234', -- OPTIONAL - comma separate if multiple ids
);
*/
$recordingsUrl = $this->_bbbServerBaseUrl."api/getRecordings?";
$params = 'meetingID='.urlencode($recordingParams['meetingId']);
return ($recordingsUrl.$params.'&checksum='.sha1("getRecordings".$params.$this->_securitySalt));
}
public function getRecordingsWithXmlResponseArray($recordingParams) {
/* USAGE:
$recordingParams = array(
'meetingId' => '1234', -- OPTIONAL - comma separate if multiple ids
);
NOTE: 'duration' DOES work when creating a meeting, so if you set duration
when creating a meeting, it will kick users out after the duration. Should
probably be required in user code when 'recording' is set to true.
*/
$xml = $this->_processXmlResponse($this->getRecordingsUrl($recordingParams));
if($xml) {
// If we don't get a success code or messageKey, find out why:
if (($xml->returncode != 'SUCCESS') || ($xml->messageKey == null)) {
$result = array(
'returncode' => $xml->returncode->__toString(),
'messageKey' => $xml->messageKey->__toString(),
'message' => $xml->message->__toString()
);
return $result;
}
else {
// In this case, we have success and recording info:
$result = array(
'returncode' => $xml->returncode->__toString(),
'messageKey' => $xml->messageKey->__toString(),
'message' => $xml->message->__toString()
);
$formats = [];
foreach ($xml->recordings->recording as $r) {
foreach ($r->playback->format as $format) {
$formats[] = $format;
}
$result[] = array(
'recordId' => $r->recordID->__toString(),
'meetingId' => $r->meetingID->__toString(),
'name' => $r->name->__toString(),
'published' => $r->published->__toString(),
'startTime' => $r->startTime->__toString(),
'endTime' => $r->endTime->__toString(),
'playbackFormat' => $formats,
'playbackFormatType' => $r->playback->format->type->__toString(),
'playbackFormatUrl' => $r->playback->format->url->__toString(),
'playbackFormatLength' => $r->playback->format->length->__toString(),
'metadataTitle' => $r->metadata->title->__toString(),
'metadataSubject' => $r->metadata->subject->__toString(),
'metadataDescription' => $r->metadata->description->__toString(),
'metadataCreator' => $r->metadata->creator->__toString(),
'metadataContributor' => $r->metadata->contributor->__toString(),
'metadataLanguage' => $r->metadata->language->__toString(),
// Add more here as needed for your app depending on your
// use of metadata when creating recordings.
);
}
return $result;
}
}
else {
return null;
}
}
/**
* @param $array recordingParams
*
* @return array|null
*/
public function getRecordings($recordingParams)
{
/* USAGE:
$recordingParams = array(
'meetingId' => '1234', -- OPTIONAL - comma separate if multiple ids
);
NOTE: 'duration' DOES work when creating a meeting, so if you set duration
when creating a meeting, it will kick users out after the duration. Should
probably be required in user code when 'recording' is set to true.
*/
$xml = $this->_processXmlResponse($this->getRecordingsUrl($recordingParams));
if($xml) {
// If we don't get a success code or messageKey, find out why:
if (($xml->returncode != 'SUCCESS') || ($xml->messageKey == null)) {
$result = array(
'returncode' => $xml->returncode->__toString(),
'messageKey' => $xml->messageKey->__toString(),
'message' => $xml->message->__toString()
);
return $result;
}
else {
// In this case, we have success and recording info:
$result = array(
'returncode' => $xml->returncode->__toString(),
'messageKey' => $xml->messageKey->__toString(),
'message' => $xml->message->__toString()
);
$result['records'] = [];
if (!empty($xml->recordings->recording)) {
$formats = [];
foreach ($xml->recordings->recording as $r) {
foreach ($r->playback->format as $format) {
$formats[] = $format;
}
$result['records'][] = array(
'recordId' => $r->recordID->__toString(),
'meetingId' => $r->meetingID->__toString(),
'name' => $r->name->__toString(),
'published' => $r->published->__toString(),
'startTime' => $r->startTime->__toString(),
'endTime' => $r->endTime->__toString(),
'playbackFormat' => $formats,
'playbackFormatType' => $r->playback->format->type->__toString(),
'playbackFormatUrl' => $r->playback->format->url->__toString(),
'playbackFormatLength' => $r->playback->format->length->__toString(),
'metadataTitle' => $r->metadata->title->__toString(),
'metadataSubject' => $r->metadata->subject->__toString(),
'metadataDescription' => $r->metadata->description->__toString(),
'metadataCreator' => $r->metadata->creator->__toString(),
'metadataContributor' => $r->metadata->contributor->__toString(),
'metadataLanguage' => $r->metadata->language->__toString(),
);
}
}
return $result;
}
}
return null;
}
public function getPublishRecordingsUrl($recordingParams) {
/* USAGE:
$recordingParams = array(
'recordId' => '1234', -- REQUIRED - comma separate if multiple ids
'publish' => 'true', -- REQUIRED - boolean: true/false
);
*/
$recordingsUrl = $this->_bbbServerBaseUrl."api/publishRecordings?";
$params =
'recordID='.urlencode($recordingParams['recordId']).
'&publish='.urlencode($recordingParams['publish']);
return ($recordingsUrl.$params.'&checksum='.sha1("publishRecordings".$params.$this->_securitySalt));
}
public function publishRecordingsWithXmlResponseArray($recordingParams) {
/* USAGE:
$recordingParams = array(
'recordId' => '1234', -- REQUIRED - comma separate if multiple ids
'publish' => 'true', -- REQUIRED - boolean: true/false
);
*/
$xml = $this->_processXmlResponse($this->getPublishRecordingsUrl($recordingParams));
if($xml) {
return array(
'returncode' => $xml->returncode->__toString(),
'published' => $xml->published->__toString() // -- Returns true/false.
);
}
else {
return null;
}
}
public function getDeleteRecordingsUrl($recordingParams) {
/* USAGE:
$recordingParams = array(
'recordId' => '1234', -- REQUIRED - comma separate if multiple ids
);
*/
$recordingsUrl = $this->_bbbServerBaseUrl."api/deleteRecordings?";
$params =
'recordID='.urlencode($recordingParams['recordId']);
return ($recordingsUrl.$params.'&checksum='.sha1("deleteRecordings".$params.$this->_securitySalt));
}
public function deleteRecordingsWithXmlResponseArray($recordingParams) {
/* USAGE:
$recordingParams = array(
'recordId' => '1234', -- REQUIRED - comma separate if multiple ids
);
*/
$xml = $this->_processXmlResponse($this->getDeleteRecordingsUrl($recordingParams));
if($xml) {
return array(
'returncode' => $xml->returncode->__toString(),
'deleted' => $xml->deleted->__toString() // -- Returns true/false.
);
}
else {
return null;
}
}
/** USAGE:
* $recordingParams = array(
* 'recordId' => '1234', -- REQUIRED - comma separate if multiple ids
* );
*/
public function generateRecording($recordingParams)
{
if (empty($recordingParams)) {
return false;
}
$recordingsUrl = $this->_bbbServerBaseUrl.'../demo/regenerateRecord.jsp?';
$params = 'recordID='.urlencode($recordingParams['recordId']);
$url = $recordingsUrl.$params.'&checksum='.sha1('regenerateRecord'.$params.$this->_securitySalt);
$ch = curl_init() or die ( curl_error($ch) );
$timeout = 10;
curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt( $ch, CURLOPT_URL, $url );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, $timeout);
$data = curl_exec( $ch );
curl_close( $ch );
return true;
}
}

View File

@@ -0,0 +1,407 @@
<?php
/* For licensing terms, see /license.txt */
/* To show the plugin course icons you need to add these icons:
* main/img/icons/22/plugin_name.png
* main/img/icons/64/plugin_name.png
* main/img/icons/64/plugin_name_na.png
*/
/**
* Videoconference plugin with BBB
*/
class BBBPlugin extends Plugin
{
const ROOM_OPEN = 0;
const ROOM_CLOSE = 1;
const ROOM_CHECK = 2;
public $isCoursePlugin = true;
// When creating a new course this settings are added to the course
public $course_settings = [
[
'name' => 'big_blue_button_record_and_store',
'type' => 'checkbox',
],
[
'name' => 'bbb_enable_conference_in_groups',
'type' => 'checkbox',
],
[
'name' => 'bbb_force_record_generation',
'type' => 'checkbox',
],
[
'name' => 'big_blue_button_students_start_conference_in_groups',
'type' => 'checkbox',
],
];
/**
* BBBPlugin constructor.
*/
protected function __construct()
{
$settings = [
'tool_enable' => 'boolean',
'host' => 'text',
'salt' => 'text',
'enable_global_conference' => 'boolean',
'enable_global_conference_per_user' => 'boolean',
'enable_conference_in_course_groups' => 'boolean',
'enable_global_conference_link' => 'boolean',
'disable_download_conference_link' => 'boolean',
'max_users_limit' => 'text',
'global_conference_allow_roles' => [
'type' => 'select',
'options' => [
PLATFORM_ADMIN => get_lang('Administrator'),
COURSEMANAGER => get_lang('Teacher'),
STUDENT => get_lang('Student'),
STUDENT_BOSS => get_lang('StudentBoss'),
],
'attributes' => ['multiple' => 'multiple'],
],
'allow_regenerate_recording' => 'boolean',
// Default course settings, must be the same as $course_settings
'big_blue_button_record_and_store' => 'checkbox',
'bbb_enable_conference_in_groups' => 'checkbox',
'bbb_force_record_generation' => 'checkbox',
'disable_course_settings' => 'boolean',
'meeting_duration' => 'text',
];
if (1 === (int) api_get_current_access_url_id()) {
$settings['plugin_bbb_multiple_urls_cron_apply_to_all'] = 'checkbox';
}
parent::__construct(
'2.11',
'Julio Montoya, Yannick Warnier, Angel Fernando Quiroz Campos, Jose Angel Ruiz, Ghazi Triki, Adnen Manssouri',
$settings
);
$this->isAdminPlugin = true;
}
/**
* @return BBBPlugin|null
*/
public static function create()
{
static $result = null;
return $result ? $result : $result = new self();
}
/**
* @param string $variable
*
* @return bool
*/
public function validateCourseSetting($variable)
{
if ($this->get('disable_course_settings') === 'true') {
return false;
}
$result = true;
switch ($variable) {
case 'bbb_enable_conference_in_groups':
$result = $this->get('enable_conference_in_course_groups') === 'true';
break;
case 'bbb_force_record_generation':
$result = $this->get('allow_regenerate_recording') === 'true';
break;
case 'big_blue_button_record_and_store':
}
return $result;
}
/**
*
* @return array
*/
public function getCourseSettings()
{
$settings = [];
if ($this->get('disable_course_settings') !== 'true') {
$settings = parent::getCourseSettings();
}
return $settings;
}
/**
*
* @return \Plugin
*/
public function performActionsAfterConfigure()
{
$result = $this->get('disable_course_settings') === 'true';
if ($result) {
$valueConference = $this->get('bbb_enable_conference_in_groups') === 'true' ? 1 : 0;
self::update_course_field_in_all_courses('bbb_enable_conference_in_groups', $valueConference);
$valueForceRecordGeneration = $this->get('bbb_force_record_generation') === 'true' ? 1 : 0;
self::update_course_field_in_all_courses('bbb_force_record_generation', $valueForceRecordGeneration);
$valueForceRecordStore = $this->get('big_blue_button_record_and_store') === 'true' ? 1 : 0;
self::update_course_field_in_all_courses('big_blue_button_record_and_store', $valueForceRecordStore);
}
return $this;
}
/**
* Install
*/
public function install()
{
$sql = "CREATE TABLE IF NOT EXISTS plugin_bbb_meeting (
id INT unsigned NOT NULL auto_increment PRIMARY KEY,
c_id INT unsigned NOT NULL DEFAULT 0,
group_id INT unsigned NOT NULL DEFAULT 0,
user_id INT unsigned NOT NULL DEFAULT 0,
meeting_name VARCHAR(255) NOT NULL DEFAULT '',
attendee_pw VARCHAR(255) NOT NULL DEFAULT '',
moderator_pw VARCHAR(255) NOT NULL DEFAULT '',
record INT NOT NULL DEFAULT 0,
status INT NOT NULL DEFAULT 0,
created_at VARCHAR(255) NOT NULL,
closed_at VARCHAR(255) NOT NULL,
calendar_id INT DEFAULT 0,
welcome_msg VARCHAR(255) NOT NULL DEFAULT '',
session_id INT unsigned DEFAULT 0,
remote_id CHAR(30),
internal_meeting_id VARCHAR(255) DEFAULT NULL,
visibility TINYINT NOT NULL DEFAULT 1,
voice_bridge INT NOT NULL DEFAULT 1,
access_url INT NOT NULL DEFAULT 1,
video_url TEXT NULL,
has_video_m4v TINYINT NOT NULL DEFAULT 0
)";
Database::query($sql);
Database::query(
"CREATE TABLE IF NOT EXISTS plugin_bbb_room (
id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
meeting_id int NOT NULL,
participant_id int(11) NOT NULL,
in_at datetime,
out_at datetime,
close INT NOT NULL DEFAULT 0
);"
);
$fieldLabel = 'plugin_bbb_course_users_limit';
$fieldType = ExtraField::FIELD_TYPE_INTEGER;
$fieldTitle = $this->get_lang('MaxUsersInConferenceRoom');
$fieldDefault = '0';
$extraField = new ExtraField('course');
$fieldId = CourseManager::create_course_extra_field(
$fieldLabel,
$fieldType,
$fieldTitle,
$fieldDefault
);
$extraField->find($fieldId);
$extraField->update(
[
'id' => $fieldId,
'variable' => 'plugin_bbb_course_users_limit',
'changeable' => 1,
'visible_to_self' => 1,
'visible_to_others' => 0,
]
);
$fieldLabel = 'plugin_bbb_session_users_limit';
$extraField = new ExtraField('session');
$fieldId = SessionManager::create_session_extra_field(
$fieldLabel,
$fieldType,
$fieldTitle,
$fieldDefault
);
$extraField->find($fieldId);
$extraField->update(
[
'id' => $fieldId,
'variable' => 'plugin_bbb_session_users_limit',
'changeable' => 1,
'visible_to_self' => 1,
'visible_to_others' => 0,
]
);
Database::query(
"CREATE TABLE IF NOT EXISTS plugin_bbb_meeting_format (
id int unsigned not null PRIMARY KEY AUTO_INCREMENT,
meeting_id int unsigned not null,
format_type varchar(255) not null,
resource_url text not null
);"
);
// Copy icons into the main/img/icons folder
$iconName = 'bigbluebutton';
$iconsList = [
'64/'.$iconName.'.png',
'64/'.$iconName.'_na.png',
'32/'.$iconName.'.png',
'32/'.$iconName.'_na.png',
'22/'.$iconName.'.png',
'22/'.$iconName.'_na.png',
];
$sourceDir = api_get_path(SYS_PLUGIN_PATH).'bbb/resources/img/';
$destinationDir = api_get_path(SYS_CODE_PATH).'img/icons/';
foreach ($iconsList as $icon) {
$src = $sourceDir.$icon;
$dest = $destinationDir.$icon;
copy($src, $dest);
}
// Installing course settings
$this->install_course_fields_in_all_courses(true);
}
/**
* Uninstall
*/
public function uninstall()
{
$t_settings = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
$t_options = Database::get_main_table(TABLE_MAIN_SETTINGS_OPTIONS);
$t_tool = Database::get_course_table(TABLE_TOOL_LIST);
$variables = [
'bbb_salt',
'bbb_host',
'bbb_tool_enable',
'enable_global_conference',
'enable_global_conference_per_user',
'enable_global_conference_link',
'disable_download_conference_link',
'enable_conference_in_course_groups',
'bbb_plugin',
'bbb_plugin_host',
'bbb_plugin_salt',
'max_users_limit',
'global_conference_allow_roles'
];
$urlId = api_get_current_access_url_id();
foreach ($variables as $variable) {
$sql = "DELETE FROM $t_settings WHERE variable = '$variable' AND access_url = $urlId";
Database::query($sql);
}
$em = Database::getManager();
$sm = $em->getConnection()->getSchemaManager();
if ($sm->tablesExist('plugin_bbb_meeting')) {
Database::query("DELETE FROM plugin_bbb_meeting WHERE access_url = $urlId");
}
// Only delete tables if it's uninstalled from main url.
if (1 == $urlId) {
$extraField = new ExtraField('course');
$extraFieldInfo = $extraField->get_handler_field_info_by_field_variable(
'plugin_bbb_course_users_limit'
);
if (!empty($extraFieldInfo)) {
$extraField->delete($extraFieldInfo['id']);
}
$extraField = new ExtraField('session');
$extraFieldInfo = $extraField->get_handler_field_info_by_field_variable(
'plugin_bbb_session_users_limit'
);
if (!empty($extraFieldInfo)) {
$extraField->delete($extraFieldInfo['id']);
}
$sql = "DELETE FROM $t_options WHERE variable = 'bbb_plugin'";
Database::query($sql);
// hack to get rid of Database::query warning (please add c_id...)
$sql = "DELETE FROM $t_tool WHERE name = 'bbb' AND c_id != 0";
Database::query($sql);
if ($sm->tablesExist('plugin_bbb_meeting_format')) {
Database::query('DROP TABLE IF EXISTS plugin_bbb_meeting_format');
}
if ($sm->tablesExist('plugin_bbb_room')) {
Database::query('DROP TABLE IF EXISTS plugin_bbb_room');
}
if ($sm->tablesExist('plugin_bbb_meeting')) {
Database::query('DROP TABLE IF EXISTS plugin_bbb_meeting');
}
// Deleting course settings
$this->uninstall_course_fields_in_all_courses($this->course_settings);
// Remove icons from the main/img/icons folder
$iconName = 'bigbluebutton';
$iconsList = [
'64/'.$iconName.'.png',
'64/'.$iconName.'_na.png',
'32/'.$iconName.'.png',
'32/'.$iconName.'_na.png',
'22/'.$iconName.'.png',
'22/'.$iconName.'_na.png',
];
$destinationDir = api_get_path(SYS_CODE_PATH).'img/icons/';
foreach ($iconsList as $icon) {
$dest = $destinationDir.$icon;
if (is_file($dest)) {
@unlink($dest);
}
}
}
}
/**
* Update
*/
public function update()
{
$sql = "SHOW COLUMNS FROM plugin_bbb_room WHERE Field = 'close'";
$res = Database::query($sql);
if (Database::num_rows($res) === 0) {
$sql = "ALTER TABLE plugin_bbb_room ADD close int unsigned NULL";
$res = Database::query($sql);
if (!$res) {
echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning');
}
Database::update(
'plugin_bbb_room',
['close' => BBBPlugin::ROOM_CLOSE]
);
}
}
/**
* Set the course setting in all courses
*
* @param bool $variable Course setting to update
* @param bool $value New values of the course setting
*/
public function update_course_field_in_all_courses($variable, $value)
{
// Update existing courses to add the new course setting value
$table = Database::get_main_table(TABLE_MAIN_COURSE);
$sql = "SELECT id FROM $table ORDER BY id";
$res = Database::query($sql);
$courseSettingTable = Database::get_course_table(TABLE_COURSE_SETTING);
while ($row = Database::fetch_assoc($res)) {
Database::update(
$courseSettingTable,
['value' => $value],
['variable = ? AND c_id = ?' => [$variable, $row['id']]]
);
}
}
}

View File

@@ -0,0 +1,50 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Class AbstractVM
*/
abstract class AbstractVM
{
public $name;
public $host;
public $user;
public $vmId;
public $vmMinSize;
public $vmMaxSize;
public $apiKey;
public $vmClientId;
public $messages = array();
protected $connector;
/**
* @param array $settings
*/
public function __construct($settings)
{
$this->name = $settings['name'];
$this->host = $settings['host'];
$this->user = $settings['user'];
$this->apiKey = $settings['api_key'];
$this->vmId = $settings['vm_id'];
$this->vmMinSize = $settings['vm_min_size_id'];
$this->vmMaxSize = $settings['vm_max_size_id'];
$this->vmClientId = $settings['vm_client_id'];
}
/**
* @param string $message
*/
public function addMessage($message)
{
$this->messages[] = $message;
}
/**
* @return string
*/
public function getMessageToString()
{
return implode(PHP_EOL, $this->messages);
}
}

View File

@@ -0,0 +1,44 @@
<?php
/* For licensing terms, see /license.txt */
use DigitalOcean\DigitalOcean;
use DigitalOcean\Credentials;
/**
* Class AmazonVM
*/
class AmazonVM extends AbstractVM implements VirtualMachineInterface
{
/**
* @inheritdoc
*/
public function connect()
{
}
/**
* @inheritdoc
*/
public function runCron()
{
}
/**
* @inheritdoc
*/
public function resizeToMaxLimit()
{
}
/**
* @inheritdoc
*/
public function resizeToMinLimit()
{
}
}

View File

@@ -0,0 +1,180 @@
<?php
/* For licensing terms, see /license.txt */
use DigitalOcean\DigitalOcean;
use DigitalOcean\Credentials;
/**
* Class DigitalOceanVM
*/
class DigitalOceanVM extends AbstractVM implements VirtualMachineInterface
{
/**
*
*/
public function __construct($settings)
{
parent::__construct($settings);
$this->connect();
}
/**
* @inheritdoc
*/
public function connect()
{
// Set up your credentials.
$credentials = new Credentials($this->vmClientId, $this->apiKey);
// Use the default adapter, CurlHttpAdapter.
$this->connector = new DigitalOcean($credentials);
// Or use BuzzHttpAdapter.
//$this->connector = new DigitalOcean($credentials, new BuzzHttpAdapter());
}
/**
* @return DigitalOcean
*/
public function getConnector()
{
return $this->connector;
}
/**
* @param string $type min or max
*/
public function resizeTo($type = 'min')
{
try {
$droplets = $this->getConnector()->droplets();
$sizes = $this->getConnector()->sizes();
$availableSizes = $sizes->getAll();
if (isset($availableSizes->status) && $availableSizes->status == 'OK') {
$minSizeIdExists = false;
$maxSizeIdExists = false;
foreach ($availableSizes->sizes as $size) {
if ($size->id == $this->vmMaxSize) {
$maxSizeIdExists = true;
}
if ($size->id == $this->vmMinSizeSize) {
$minSizeIdExists = true;
}
}
if ($maxSizeIdExists && $minSizeIdExists) {
throw new \Exception('Sizes are not well configured');
}
} else {
throw new \Exception('Sizes not available');
}
// Returns all active droplets that are currently running in your account.
//$allActive = $droplets->showAllActive();
$dropletInfo = $droplets->show($this->vmId);
if ($dropletInfo->status == 'OK') {
switch ($type) {
case 'min':
if ($dropletInfo->droplet->size_id == $this->vmMinSize) {
// No resize
$this->addMessage(
'Nothing to execute. The size was already reduced.'
);
} else {
$this->resize($this->vmMinSize);
}
break;
case 'max':
if ($dropletInfo->droplet->size_id == $this->vmMaxSize) {
// No resize
$this->addMessage(
'Nothing to execute. The size was already boost.'
);
} else {
$this->resize($this->vmMaxSize);
}
break;
}
} else {
throw new \Exception(" Id ".$this->vmId." doesn't exists.");
}
} catch (Exception $e) {
die($e->getMessage());
}
}
/**
* Turns off / resize / turns on
* @param int $sizeId
*/
public function resize($sizeId)
{
$droplets = $this->getConnector()->droplets();
$dropletInfo = $droplets->show($this->vmId);
$powerOff = $droplets->powerOff($this->vmId);
$this->addMessage('Power off droplet #'.$this->vmId);
$this->waitForEvent($powerOff->event_id);
$this->addMessage('Current status: '.$dropletInfo->droplet->status);
$resizeDroplet = $droplets->resize(
$this->vmId,
array('size_id' => intval($sizeId))
);
$this->addMessage('Resize droplet to size id: '.$sizeId);
$this->waitForEvent($resizeDroplet->event_id);
$powerOn = $droplets->powerOn($this->vmId);
$this->waitForEvent($powerOn->event_id);
$this->addMessage('Power on droplet #'.$this->vmId);
}
/**
* Loops until an event answer 100 percentage
* @param int $eventId
*/
public function waitForEvent($eventId)
{
$events = $this->getConnector()->events();
$status = false;
while ($status == false) {
$infoStatus = $events->show($eventId);
if ($infoStatus->status == 'OK' && $infoStatus->event->percentage == 100) {
$status = true;
}
}
}
/**
* @inheritdoc
*/
public function runCron()
{
$this->resizeToMinLimit();
echo $this->getMessageToString();
}
/**
* @inheritdoc
*/
public function resizeToMaxLimit()
{
$this->resizeTo('max');
}
/**
* @inheritdoc
*/
public function resizeToMinLimit()
{
$this->resizeTo('min');
}
}

View File

@@ -0,0 +1,29 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Interface VirtualMachineInterface
*/
interface VirtualMachineInterface
{
/**
* @return mixed
*/
function connect();
/**
* @return mixed
*/
function runCron();
/**
* @return mixed
*/
function resizeToMaxLimit();
/**
* @return mixed
*/
function resizeToMinLimit();
}

540
plugin/bbb/listing.php Normal file
View File

@@ -0,0 +1,540 @@
<?php
/* For license terms, see /license.txt */
/**
* This script initiates a video conference session, calling the BigBlueButton API.
*/
$course_plugin = 'bbb'; //needed in order to load the plugin lang variables
$isGlobal = isset($_GET['global']);
$isGlobalPerUser = isset($_GET['user_id']) ? (int) $_GET['user_id'] : false;
// If global setting is used then we delete the course sessions (cidReq/id_session)
if ($isGlobalPerUser || $isGlobal) {
$cidReset = true;
}
require_once __DIR__.'/config.php';
$plugin = BBBPlugin::create();
$tool_name = $plugin->get_lang('Videoconference');
$roomTable = Database::get_main_table('plugin_bbb_room');
$htmlHeadXtra[] = api_get_js_simple(api_get_path(WEB_PLUGIN_PATH).'bbb/resources/utils.js');
$action = $_GET['action'] ?? '';
$userId = api_get_user_id();
$groupId = api_get_group_id();
$sessionId = api_get_session_id();
$courseInfo = api_get_course_info();
$bbb = new bbb('', '', $isGlobal, $isGlobalPerUser);
$conferenceManager = $bbb->isConferenceManager();
if ($bbb->isGlobalConference()) {
api_block_anonymous_users();
} else {
api_protect_course_script(true);
}
$allowStudentAsConferenceManager = false;
if (!empty($courseInfo) && !empty($groupId) && !api_is_allowed_to_edit()) {
$groupEnabled = api_get_course_plugin_setting(
'bbb',
'bbb_enable_conference_in_groups',
$courseInfo
) === '1';
if ($groupEnabled) {
$isSubscribed = GroupManager::is_user_in_group(api_get_user_id(), GroupManager::get_group_properties($groupId));
if ($isSubscribed) {
$allowStudentAsConferenceManager = api_get_course_plugin_setting(
'bbb',
'big_blue_button_students_start_conference_in_groups',
$courseInfo
) === '1';
}
}
}
$allowToEdit = $conferenceManager;
// Disable students edit permissions.
if ($allowStudentAsConferenceManager) {
$allowToEdit = false;
}
$courseCode = $courseInfo['code'] ?? '';
$message = '';
if ($conferenceManager && $allowToEdit) {
switch ($action) {
case 'add_to_calendar':
if ($bbb->isGlobalConference()) {
return false;
}
$courseInfo = api_get_course_info();
$agenda = new Agenda('course');
$id = (int) $_GET['id'];
$title = sprintf($plugin->get_lang('VideoConferenceXCourseX'), $id, $courseInfo['name']);
$content = Display::url($plugin->get_lang('GoToTheVideoConference'), $_GET['url']);
$eventId = $agenda->addEvent(
$_REQUEST['start'],
null,
'true',
$title,
$content,
['everyone']
);
if (!empty($eventId)) {
$message = Display::return_message($plugin->get_lang('VideoConferenceAddedToTheCalendar'), 'success');
} else {
$message = Display::return_message(get_lang('Error'), 'error');
}
break;
case 'copy_record_to_link_tool':
$result = $bbb->copyRecordingToLinkTool($_GET['id']);
if ($result) {
$message = Display::return_message($plugin->get_lang('VideoConferenceAddedToTheLinkTool'), 'success');
} else {
$message = Display::return_message(get_lang('Error'), 'error');
}
break;
case 'regenerate_record':
if ($plugin->get('allow_regenerate_recording') !== 'true') {
api_not_allowed(true);
}
$recordId = isset($_GET['record_id']) ? $_GET['record_id'] : '';
$result = $bbb->regenerateRecording($_GET['id'], $recordId);
if ($result) {
$message = Display::return_message(get_lang('Success'), 'success');
} else {
$message = Display::return_message(get_lang('Error'), 'error');
}
Display::addFlash($message);
header('Location: '.$bbb->getListingUrl());
exit;
break;
case 'delete_record':
$result = $bbb->deleteRecording($_GET['id']);
if ($result) {
$message = Display::return_message(get_lang('Deleted'), 'success');
} else {
$message = Display::return_message(get_lang('Error'), 'error');
}
Display::addFlash($message);
header('Location: '.$bbb->getListingUrl());
exit;
break;
case 'end':
$bbb->endMeeting($_GET['id']);
$message = Display::return_message(
$plugin->get_lang('MeetingClosed').'<br />'.$plugin->get_lang('MeetingClosedComment'),
'success',
false
);
if (file_exists(__DIR__.'/config.vm.php')) {
require __DIR__.'/../../vendor/autoload.php';
require __DIR__.'/lib/vm/AbstractVM.php';
require __DIR__.'/lib/vm/VMInterface.php';
require __DIR__.'/lib/vm/DigitalOceanVM.php';
require __DIR__.'/lib/VM.php';
$config = require __DIR__.'/config.vm.php';
$vm = new VM($config);
$vm->resizeToMinLimit();
}
Display::addFlash($message);
header('Location: '.$bbb->getListingUrl());
exit;
break;
case 'publish':
$bbb->publishMeeting($_GET['id']);
Display::addFlash(Display::return_message(get_lang('Updated')));
header('Location: '.$bbb->getListingUrl());
exit;
break;
case 'unpublish':
$bbb->unpublishMeeting($_GET['id']);
Display::addFlash(Display::return_message(get_lang('Updated')));
header('Location: '.$bbb->getListingUrl());
exit;
break;
case 'logout':
if ($plugin->get('allow_regenerate_recording') === 'true') {
$setting = api_get_course_plugin_setting('bbb', 'bbb_force_record_generation', $courseInfo);
$allow = $setting == 1 ? true : false;
if ($allow) {
$result = $bbb->getMeetingByRemoteId($_GET['remote_id']);
if (!empty($result)) {
$result = $bbb->regenerateRecording($result['id']);
if ($result) {
Display::addFlash(Display::return_message(get_lang('Success')));
} else {
Display::addFlash(Display::return_message(get_lang('Error'), 'error'));
}
}
}
}
$remoteId = Database::escape_string($_GET['remote_id']);
$meetingData = Database::select(
'*',
Database::get_main_table('plugin_bbb_meeting'),
['where' => ['remote_id = ? AND access_url = ?' => [$remoteId, api_get_current_access_url_id()]]],
'first'
);
if (empty($meetingData) || !is_array($meetingData)) {
error_log("meeting does not exist - remote_id: $remoteId");
} else {
$meetingId = $meetingData['id'];
// If creator -> update
if ($meetingData['creator_id'] == api_get_user_id()) {
$pass = $bbb->getModMeetingPassword($courseCode);
$meetingBBB = $bbb->getMeetingInfo(
[
'meetingId' => $remoteId,
'password' => $pass,
]
);
if ($meetingBBB === false) {
//checking with the remote_id didn't work, so just in case and
// to provide backwards support, check with the id
$params = [
'meetingId' => $meetingId,
// -- REQUIRED - The unique id for the meeting
'password' => $pass,
// -- REQUIRED - The moderator password for the meeting
];
$meetingBBB = $bbb->getMeetingInfo($params);
}
if (!empty($meetingBBB)) {
if (isset($meetingBBB['returncode'])) {
$status = (string) $meetingBBB['returncode'];
switch ($status) {
case 'FAILED':
$bbb->endMeeting($meetingId, $courseCode);
break;
case 'SUCCESS':
$i = 0;
while ($i < $meetingBBB['participantCount']) {
$participantId = $meetingBBB[$i]['userId'];
$roomData = Database::select(
'*',
$roomTable,
[
'where' => [
'meeting_id = ? AND participant_id = ? AND close = ?' => [
$meetingId,
$participantId,
BBBPlugin::ROOM_OPEN,
],
],
'order' => 'id DESC',
],
'first'
);
if (!empty($roomData)) {
$roomId = $roomData['id'];
if (!empty($roomId)) {
Database::update(
$roomTable,
['out_at' => api_get_utc_datetime()],
['id = ? ' => $roomId]
);
}
}
$i++;
}
break;
}
}
}
}
// Update out_at field of user
$roomData = Database::select(
'*',
$roomTable,
[
'where' => ['meeting_id = ? AND participant_id = ?' => [$meetingId, $userId]],
'order' => 'id DESC',
],
'first'
);
if (!empty($roomData)) {
$roomId = $roomData['id'];
if (!empty($roomId)) {
Database::update(
$roomTable,
['out_at' => api_get_utc_datetime(), 'close' => BBBPlugin::ROOM_CLOSE],
['id = ? ' => $roomId]
);
}
}
$message = Display::return_message(
$plugin->get_lang('RoomClosed').'<br />'.$plugin->get_lang('RoomClosedComment'),
'success',
false
);
Display::addFlash($message);
}
header('Location: '.$bbb->getListingUrl());
exit;
break;
default:
break;
}
} else {
if ($action === 'logout') {
// Update out_at field of user
$remoteId = Database::escape_string($_GET['remote_id']);
$meetingData = Database::select(
'*',
Database::get_main_table('plugin_bbb_meeting'),
['where' => ['remote_id = ? AND access_url = ?' => [$remoteId, api_get_current_access_url_id()]]],
'first'
);
if (empty($meetingData) || !is_array($meetingData)) {
error_log("meeting does not exist - remote_id: $remoteId");
} else {
$meetingId = $meetingData['id'];
$roomData = Database::select(
'*',
$roomTable,
[
'where' => [
'meeting_id = ? AND participant_id = ? AND close = ?' => [
$meetingId,
$userId,
BBBPlugin::ROOM_OPEN,
],
],
'order' => 'id DESC',
]
);
$i = 0;
foreach ($roomData as $item) {
$roomId = $item['id'];
if (!empty($roomId)) {
if ($i == 0) {
Database::update(
$roomTable,
['out_at' => api_get_utc_datetime(), 'close' => BBBPlugin::ROOM_CLOSE],
['id = ? ' => $roomId]
);
} else {
Database::update($roomTable, ['close' => BBBPlugin::ROOM_CLOSE], ['id = ? ' => $roomId]);
}
$i++;
}
}
$message = Display::return_message(
$plugin->get_lang('RoomExit'),
'success',
false
);
Display::addFlash($message);
}
header('Location: '.$bbb->getListingUrl());
exit;
}
}
if (isset($_GET['page_id'])) {
$pageId = (int) $_GET['page_id'];
}
$meetingsCount = $bbb->getCountMeetings(
api_get_course_int_id(),
api_get_session_id(),
api_get_group_id()
);
$limit = 10;
$pageNumber = ceil($meetingsCount / $limit);
if (!isset($pageId)) {
$pageId = 1;
}
$start = ($pageId - 1) * $limit;
$meetings = $bbb->getMeetings(
api_get_course_int_id(),
api_get_session_id(),
api_get_group_id(),
false,
[],
$start,
$limit,
"DESC"
);
if (empty($meetings)) {
$pageId = 0;
}
$usersOnline = $bbb->getUsersOnlineInCurrentRoom();
$maxUsers = $bbb->getMaxUsersLimit();
$status = $bbb->isServerRunning();
$currentOpenConference = $bbb->getCurrentVideoConference();
$videoConferenceName = $currentOpenConference
? $currentOpenConference['meeting_name']
: $bbb->generateVideoConferenceName();
$meetingExists = $bbb->meetingExists($videoConferenceName);
$showJoinButton = false;
// Only conference manager can see the join button
$userCanSeeJoinButton = $conferenceManager;
if ($bbb->isGlobalConference() && $bbb->isGlobalConferencePerUserEnabled()) {
// Any user can see the "join button" BT#12620
$userCanSeeJoinButton = true;
}
if (($meetingExists || $userCanSeeJoinButton) && ($maxUsers == 0 || $maxUsers > $usersOnline)) {
$showJoinButton = true;
}
$conferenceUrl = $bbb->getConferenceUrl();
$courseInfo = api_get_course_info();
$formToString = '';
if ($bbb->isGlobalConference() === false &&
!empty($courseInfo) &&
$plugin->get('enable_conference_in_course_groups') === 'true'
) {
$url = api_get_self().'?'.api_get_cidreq(true, false).'&gidReq=';
$htmlHeadXtra[] = '<script>
$(function() {
$("#group_select").on("change", function() {
var groupId = $(this).find("option:selected").val();
var url = "'.$url.'";
window.location.replace(url+groupId);
});
});
</script>';
$form = new FormValidator(api_get_self().'?'.api_get_cidreq());
if ($conferenceManager && false === $allowStudentAsConferenceManager) {
$groups = GroupManager::get_group_list(null, $courseInfo, null, $sessionId);
} else {
if (!empty($groupId)) {
$groupInfo = GroupManager::get_group_properties($groupId);
if ($groupInfo) {
$isSubscribed = GroupManager::is_user_in_group(api_get_user_id(), $groupInfo);
if (false === $isSubscribed) {
api_not_allowed(true);
}
}
}
$groups = GroupManager::getAllGroupPerUserSubscription(
api_get_user_id(),
api_get_course_int_id(),
api_get_session_id()
);
}
if ($groups) {
$meetingsInGroup = $bbb->getAllMeetingsInCourse(api_get_course_int_id(), api_get_session_id(), 1);
$meetingsGroup = array_column($meetingsInGroup, 'status', 'group_id');
$groupList[0] = get_lang('Select');
foreach ($groups as $groupData) {
$itemGroupId = $groupData['iid'];
if (isset($meetingsGroup[$itemGroupId]) && $meetingsGroup[$itemGroupId] == 1) {
$groupData['name'] .= ' ('.get_lang('Active').')';
}
$groupList[$itemGroupId] = $groupData['name'];
}
$form->addSelect('group_id', get_lang('Groups'), $groupList, ['id' => 'group_select']);
$form->setDefaults(['group_id' => $groupId]);
$formToString = $form->returnForm();
}
}
$frmEnterConference = new FormValidator(
'enter_conference',
'get',
api_get_path(WEB_PLUGIN_PATH).'bbb/start.php',
'_blank'
);
$frmEnterConference->addText('name', get_lang('Name'));
$frmEnterConference->applyFilter('name', 'trim');
$frmEnterConference->addButtonNext($plugin->get_lang('EnterConference'));
$conferenceUrlQueryParams = [];
parse_str(
parse_url($conferenceUrl, PHP_URL_QUERY),
$conferenceUrlQueryParams
);
foreach ($conferenceUrlQueryParams as $key => $value) {
$frmEnterConference->addHidden($key, $value);
}
if ($meetingExists) {
$meetingInfo = $bbb->getMeetingByName($videoConferenceName);
if (1 === (int) $meetingInfo['status']) {
$frmEnterConference->freeze(['name']);
}
}
$frmEnterConference->setDefaults(['name' => $videoConferenceName]);
// Default URL
$enterConferenceLink = $frmEnterConference->returnForm();
$tpl = new Template($tool_name);
$tpl->assign('allow_to_edit', $allowToEdit);
$tpl->assign('meetings', $meetings);
$tpl->assign('conference_url', $conferenceUrl);
$tpl->assign('users_online', $usersOnline);
$tpl->assign('conference_manager', $conferenceManager);
$tpl->assign('max_users_limit', $maxUsers);
$tpl->assign('bbb_status', $status);
$tpl->assign('show_join_button', $showJoinButton);
$tpl->assign('message', $message);
$tpl->assign('form', $formToString);
$tpl->assign('enter_conference_links', $enterConferenceLink);
$tpl->assign('page_number', $pageNumber);
$tpl->assign('page_id', $pageId);
$content = $tpl->fetch('bbb/view/listing.tpl');
$actionLinks = '';
if (api_is_platform_admin()) {
$actionLinks .= Display::toolbarButton(
$plugin->get_lang('AdminView'),
api_get_path(WEB_PLUGIN_PATH).'bbb/admin.php',
'list',
'primary'
);
$tpl->assign(
'actions',
Display::toolbarAction('toolbar', [$actionLinks])
);
}
$tpl->assign('content', $content);
$tpl->display_one_col_template();

6
plugin/bbb/plugin.php Normal file
View File

@@ -0,0 +1,6 @@
<?php
/* For license terms, see /license.txt */
require_once __DIR__.'/config.php';
$plugin_info = BBBPlugin::create()->get_info();

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 740 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 774 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 977 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -0,0 +1,284 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="124"
height="124"
viewBox="0 0 124 124"
version="1.1"
preserveAspectRatio="xMidYMid"
id="svg68"
sodipodi:docname="bigbluebutton.svg"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
<metadata
id="metadata72">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1860"
inkscape:window-height="1016"
id="namedview70"
showgrid="false"
inkscape:zoom="2.4986848"
inkscape:cx="61.749408"
inkscape:cy="127.97701"
inkscape:window-x="60"
inkscape:window-y="1107"
inkscape:window-maximized="1"
inkscape:current-layer="g916" />
<defs
id="defs37">
<linearGradient
inkscape:collect="always"
id="linearGradient950">
<stop
style="stop-color:#007266;stop-opacity:1;"
offset="0"
id="stop946" />
<stop
style="stop-color:#007266;stop-opacity:0;"
offset="1"
id="stop948" />
</linearGradient>
<linearGradient
x1="71.77459"
y1="187.32983"
x2="206.34541"
y2="53.078278"
id="linearGradient-1"
gradientTransform="matrix(0.35264551,0,0,0.40800991,10.373845,5.6478575)"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#058B7E"
offset="0%"
id="stop2" />
<stop
stop-color="#058D80"
offset="0%"
id="stop4" />
<stop
stop-color="#058D7F"
offset="100%"
id="stop6" />
</linearGradient>
<path
d="M 127.68269,0 C 57.165748,0 0,55.790933 0,124.61227 0,193.43392 57.165748,249.22453 127.68269,249.22453 l 0.27148,46.91374 c 65.60585,-37.27075 128,-87.01882 128,-171.526 C 255.95417,55.790933 198.19994,0 127.68269,0 Z"
id="path-2"
inkscape:connector-curvature="0" />
<filter
x="-0.0040000002"
y="-0.003"
width="1.008"
height="1.007"
filterUnits="objectBoundingBox"
id="filter-3">
<feOffset
dx="0"
dy="2"
in="SourceAlpha"
result="shadowOffsetInner1"
id="feOffset10" />
<feComposite
in="shadowOffsetInner1"
in2="SourceAlpha"
operator="arithmetic"
k2="-1"
k3="1"
result="shadowInnerInner1"
id="feComposite12"
k1="0"
k4="0" />
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.2 0"
type="matrix"
in="shadowInnerInner1"
id="feColorMatrix14" />
</filter>
<path
d="M 127.68269,0 C 57.165748,0 0,55.790933 0,124.61227 0,193.43392 57.165748,249.22453 127.68269,249.22453 l 0.27148,46.91374 c 65.60585,-37.27075 128,-87.01882 128,-171.526 C 255.95417,55.790933 198.19994,0 127.68269,0 Z"
id="path-4"
inkscape:connector-curvature="0" />
<filter
x="-0.0040000002"
y="-0.003"
width="1.008"
height="1.007"
filterUnits="objectBoundingBox"
id="filter-5">
<feOffset
dx="0"
dy="-2"
in="SourceAlpha"
result="shadowOffsetInner1"
id="feOffset18" />
<feComposite
in="shadowOffsetInner1"
in2="SourceAlpha"
operator="arithmetic"
k2="-1"
k3="1"
result="shadowInnerInner1"
id="feComposite20"
k1="0"
k4="0" />
<feColorMatrix
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"
type="matrix"
in="shadowInnerInner1"
id="feColorMatrix22" />
</filter>
<path
d="M 127.68269,0 C 57.165748,0 0,55.790933 0,124.61227 0,193.43392 57.165748,249.22453 127.68269,249.22453 l 0.27148,46.91374 c 65.60585,-37.27075 128,-87.01882 128,-171.526 C 255.95417,55.790933 198.19994,0 127.68269,0 Z"
id="path-6"
inkscape:connector-curvature="0" />
<linearGradient
x1="119.3371"
y1="153.50986"
x2="236.07732"
y2="282.50131"
id="linearGradient-8"
gradientTransform="scale(1.1271732,0.88717512)"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#000000"
offset="0%"
id="stop26" />
<stop
stop-color="#D8D8D8"
stop-opacity="0"
offset="100%"
id="stop28" />
</linearGradient>
<path
d="m 55.580192,128.1054 v 34.23858 c 0,9.03901 7.395555,16.43457 16.434567,16.43457 h 85.016021 c 9.03901,0 16.43457,-7.39556 16.43457,-16.43457 v -21.62283 l 33.65926,33.65925 v -46.275 z"
id="path-9"
inkscape:connector-curvature="0" />
<filter
x="-0.0099999998"
y="-0.029999999"
width="1.026"
height="1.118"
filterUnits="objectBoundingBox"
id="filter-10">
<feOffset
dx="1"
dy="3"
in="SourceAlpha"
result="shadowOffsetOuter1"
id="feOffset32" />
<feColorMatrix
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"
type="matrix"
in="shadowOffsetOuter1"
id="feColorMatrix34" />
</filter>
<mask
id="mask-7"
fill="white">
<use
xlink:href="#path-6"
id="use49"
x="0"
y="0"
width="100%"
height="100%" />
</mask>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient950"
id="linearGradient952"
x1="80.086624"
y1="68.221016"
x2="96.365685"
y2="82.936577"
gradientUnits="userSpaceOnUse" />
</defs>
<g
id="g1001"
inkscape:export-filename="/var/www/chamilo11/plugin/google_meet/resources/img/64/meet.png"
inkscape:export-xdpi="49.548386"
inkscape:export-ydpi="49.548386">
<rect
y="8"
x="4"
height="124"
width="124"
id="rect967"
style="opacity:0;fill:#008072;fill-opacity:1;stroke:none;stroke-width:1.63019824;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.63019823, 3.26039649;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke fill markers" />
<g
id="g965">
<g
style="fill:#000000;fill-opacity:1"
id="g43"
transform="matrix(0.37931894,0,0,0.37931894,14.373845,13.647858)">
<use
height="100%"
width="100%"
y="0"
x="0"
style="filter:url(#filter-3)"
id="use41"
xlink:href="#path-2" />
</g>
<path
style="fill:#283375;stroke-width:0.37931895;fill-opacity:1"
inkscape:connector-curvature="0"
id="path39"
d="m 62.806308,13.647858 c -26.748411,0 -48.432463,21.162557 -48.432463,47.267794 0,26.105355 21.684052,47.267788 48.432463,47.267788 v 0 c 26.086173,-0.13013 48.655802,-15.212614 48.655802,-47.267788 0,-26.105237 -21.907273,-47.267794 -48.655802,-47.267794 z"
sodipodi:nodetypes="ssccss" />
<g
id="g916"
transform="matrix(0.96698632,0,0,0.96698632,6.218741,11.135255)">
<g
transform="matrix(0.39226919,0,0,0.39226919,10.501808,-9.8113047)"
id="g62">
<use
xlink:href="#path-9"
id="use58"
style="fill:#000000;fill-opacity:1;filter:url(#filter-10)"
x="0"
y="0"
width="100%"
height="100%" />
</g>
<path
d="M 89.682129,50.417748 V 32.213918 L 76.478638,45.389265 V 36.98701 c 0,-3.54585 -2.90105,-6.446774 -6.446775,-6.446774 H 36.682698 c -3.894966,0.343564 -6.344512,3.012167 -6.44679,6.446726 V 50.4177 Z"
id="path64"
inkscape:connector-curvature="0"
style="fill:#e2e2e2;stroke-width:0.39226919"
sodipodi:nodetypes="cccsscccc" />
<use
transform="matrix(0.39226919,0,0,0.39226919,8.4335144,0.16594801)"
xlink:href="#path-9"
id="use60"
style="fill:#f6f6f6;fill-rule:evenodd"
x="0"
y="0"
width="100%"
height="100%" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

@@ -0,0 +1,336 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="124"
height="124"
viewBox="0 0 124 124"
version="1.1"
preserveAspectRatio="xMidYMid"
id="svg68"
sodipodi:docname="bigbluebutton_na.svg"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
inkscape:export-filename="/var/www/chamilo111x/plugin/bbb/resources/img/128/bigbluebutton.png"
inkscape:export-xdpi="99.099998"
inkscape:export-ydpi="99.099998">
<metadata
id="metadata72">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1860"
inkscape:window-height="1016"
id="namedview70"
showgrid="false"
inkscape:zoom="2.4986848"
inkscape:cx="61.749408"
inkscape:cy="93.250306"
inkscape:window-x="60"
inkscape:window-y="1107"
inkscape:window-maximized="1"
inkscape:current-layer="g965" />
<defs
id="defs37">
<linearGradient
inkscape:collect="always"
id="linearGradient950">
<stop
style="stop-color:#007266;stop-opacity:1;"
offset="0"
id="stop946" />
<stop
style="stop-color:#007266;stop-opacity:0;"
offset="1"
id="stop948" />
</linearGradient>
<linearGradient
x1="71.77459"
y1="187.32983"
x2="206.34541"
y2="53.078278"
id="linearGradient-1"
gradientTransform="matrix(0.35264551,0,0,0.40800991,10.373845,5.6478575)"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#058B7E"
offset="0%"
id="stop2" />
<stop
stop-color="#058D80"
offset="0%"
id="stop4" />
<stop
stop-color="#058D7F"
offset="100%"
id="stop6" />
</linearGradient>
<path
d="M 127.68269,0 C 57.165748,0 0,55.790933 0,124.61227 0,193.43392 57.165748,249.22453 127.68269,249.22453 l 0.27148,46.91374 c 65.60585,-37.27075 128,-87.01882 128,-171.526 C 255.95417,55.790933 198.19994,0 127.68269,0 Z"
id="path-2"
inkscape:connector-curvature="0" />
<filter
x="-0.0040000002"
y="-0.003"
width="1.008"
height="1.007"
filterUnits="objectBoundingBox"
id="filter-3">
<feOffset
dx="0"
dy="2"
in="SourceAlpha"
result="shadowOffsetInner1"
id="feOffset10" />
<feComposite
in="shadowOffsetInner1"
in2="SourceAlpha"
operator="arithmetic"
k2="-1"
k3="1"
result="shadowInnerInner1"
id="feComposite12"
k1="0"
k4="0" />
<feColorMatrix
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.2 0"
type="matrix"
in="shadowInnerInner1"
id="feColorMatrix14" />
</filter>
<path
d="M 127.68269,0 C 57.165748,0 0,55.790933 0,124.61227 0,193.43392 57.165748,249.22453 127.68269,249.22453 l 0.27148,46.91374 c 65.60585,-37.27075 128,-87.01882 128,-171.526 C 255.95417,55.790933 198.19994,0 127.68269,0 Z"
id="path-4"
inkscape:connector-curvature="0" />
<filter
x="-0.0040000002"
y="-0.003"
width="1.008"
height="1.007"
filterUnits="objectBoundingBox"
id="filter-5">
<feOffset
dx="0"
dy="-2"
in="SourceAlpha"
result="shadowOffsetInner1"
id="feOffset18" />
<feComposite
in="shadowOffsetInner1"
in2="SourceAlpha"
operator="arithmetic"
k2="-1"
k3="1"
result="shadowInnerInner1"
id="feComposite20"
k1="0"
k4="0" />
<feColorMatrix
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"
type="matrix"
in="shadowInnerInner1"
id="feColorMatrix22" />
</filter>
<path
d="M 127.68269,0 C 57.165748,0 0,55.790933 0,124.61227 0,193.43392 57.165748,249.22453 127.68269,249.22453 l 0.27148,46.91374 c 65.60585,-37.27075 128,-87.01882 128,-171.526 C 255.95417,55.790933 198.19994,0 127.68269,0 Z"
id="path-6"
inkscape:connector-curvature="0" />
<linearGradient
x1="119.3371"
y1="153.50986"
x2="236.07732"
y2="282.50131"
id="linearGradient-8"
gradientTransform="scale(1.1271732,0.88717512)"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#000000"
offset="0%"
id="stop26" />
<stop
stop-color="#D8D8D8"
stop-opacity="0"
offset="100%"
id="stop28" />
</linearGradient>
<path
d="m 55.580192,128.1054 v 34.23858 c 0,9.03901 7.395555,16.43457 16.434567,16.43457 h 85.016021 c 9.03901,0 16.43457,-7.39556 16.43457,-16.43457 v -21.62283 l 33.65926,33.65925 v -46.275 z"
id="path-9"
inkscape:connector-curvature="0" />
<filter
x="-0.0099999998"
y="-0.029999999"
width="1.026"
height="1.118"
filterUnits="objectBoundingBox"
id="filter-10">
<feOffset
dx="1"
dy="3"
in="SourceAlpha"
result="shadowOffsetOuter1"
id="feOffset32" />
<feColorMatrix
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"
type="matrix"
in="shadowOffsetOuter1"
id="feColorMatrix34" />
</filter>
<mask
id="mask-7"
fill="white">
<use
xlink:href="#path-6"
id="use49"
x="0"
y="0"
width="100%"
height="100%" />
</mask>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient950"
id="linearGradient952"
x1="80.086624"
y1="68.221016"
x2="96.365685"
y2="82.936577"
gradientUnits="userSpaceOnUse" />
<filter
style="color-interpolation-filters:sRGB;"
inkscape:label="Greyscale"
id="filter50475">
<feColorMatrix
values="0.21 0.72 0.072 0 0 0.21 0.72 0.072 0 0 0.21 0.72 0.072 0 0 0 0 0 1 0 "
id="feColorMatrix50473" />
</filter>
<filter
style="color-interpolation-filters:sRGB;"
inkscape:label="Greyscale"
id="filter50479">
<feColorMatrix
values="0.21 0.72 0.072 0 0 0.21 0.72 0.072 0 0 0.21 0.72 0.072 0 0 0 0 0 1 0 "
id="feColorMatrix50477" />
</filter>
<filter
style="color-interpolation-filters:sRGB;"
inkscape:label="Greyscale"
id="filter50483">
<feColorMatrix
values="0.21 0.72 0.072 0 0 0.21 0.72 0.072 0 0 0.21 0.72 0.072 0 0 0 0 0 1 0 "
id="feColorMatrix50481" />
</filter>
<filter
style="color-interpolation-filters:sRGB;"
inkscape:label="Greyscale"
id="filter50487">
<feColorMatrix
values="0.21 0.72 0.072 0 0 0.21 0.72 0.072 0 0 0.21 0.72 0.072 0 0 0 0 0 1 0 "
id="feColorMatrix50485" />
</filter>
<filter
style="color-interpolation-filters:sRGB;"
inkscape:label="Greyscale"
id="filter50491">
<feColorMatrix
values="0.21 0.72 0.072 0 0 0.21 0.72 0.072 0 0 0.21 0.72 0.072 0 0 0 0 0 1 0 "
id="feColorMatrix50489" />
</filter>
<filter
style="color-interpolation-filters:sRGB;"
inkscape:label="Greyscale"
id="filter50495">
<feColorMatrix
values="0.21 0.72 0.072 0 0 0.21 0.72 0.072 0 0 0.21 0.72 0.072 0 0 0 0 0 1 0 "
id="feColorMatrix50493" />
</filter>
</defs>
<g
id="g1001"
inkscape:export-filename="/var/www/chamilo11/plugin/google_meet/resources/img/64/meet.png"
inkscape:export-xdpi="49.548386"
inkscape:export-ydpi="49.548386">
<rect
y="8"
x="4"
height="124"
width="124"
id="rect967"
style="opacity:0;fill:#008072;fill-opacity:1;stroke:none;stroke-width:1.63019824;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.63019823, 3.26039649000000020;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke fill markers;filter:url(#filter50495)" />
<g
id="g965">
<g
style="fill:#000000;fill-opacity:1;filter:url(#filter50491)"
id="g43"
transform="matrix(0.37931894,0,0,0.37931894,14.373845,13.647858)">
<use
height="100%"
width="100%"
y="0"
x="0"
style="filter:url(#filter-3)"
id="use41"
xlink:href="#path-2" />
</g>
<path
style="fill:#283375;stroke-width:0.37931895;fill-opacity:1;filter:url(#filter50487);opacity:0.584"
inkscape:connector-curvature="0"
id="path39"
d="m 62.806308,13.647858 c -26.748411,0 -48.432463,21.162557 -48.432463,47.267794 0,26.105355 21.684052,47.267788 48.432463,47.267788 v 0 c 26.086173,-0.13013 48.655802,-15.212614 48.655802,-47.267788 0,-26.105237 -21.907273,-47.267794 -48.655802,-47.267794 z"
sodipodi:nodetypes="ssccss" />
<g
id="g916"
transform="matrix(0.96698632,0,0,0.96698632,6.218741,11.135255)">
<g
transform="matrix(0.39226919,0,0,0.39226919,10.501808,-9.8113047)"
id="g62"
style="filter:url(#filter50483)">
<use
xlink:href="#path-9"
id="use58"
style="fill:#000000;fill-opacity:1;filter:url(#filter-10)"
x="0"
y="0"
width="100%"
height="100%" />
</g>
<path
d="M 89.682129,50.417748 V 32.213918 L 76.478638,45.389265 V 36.98701 c 0,-3.54585 -2.90105,-6.446774 -6.446775,-6.446774 H 36.682698 c -3.894966,0.343564 -6.344512,3.012167 -6.44679,6.446726 V 50.4177 Z"
id="path64"
inkscape:connector-curvature="0"
style="fill:#e2e2e2;stroke-width:0.39226919;filter:url(#filter50479)"
sodipodi:nodetypes="cccsscccc" />
<use
transform="matrix(0.39226919,0,0,0.39226919,8.4335144,0.16594801)"
xlink:href="#path-9"
id="use60"
style="fill:#f6f6f6;fill-rule:evenodd;filter:url(#filter50475)"
x="0"
y="0"
width="100%"
height="100%" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,282 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="128"
height="128"
viewBox="0 0 33.866666 33.866668"
version="1.1"
id="svg8"
inkscape:version="0.92.3 (unknown)"
sodipodi:docname="videoconference_flash.svg">
<defs
id="defs2">
<linearGradient
inkscape:collect="always"
id="linearGradient1680">
<stop
style="stop-color:#ffe88e;stop-opacity:1;"
offset="0"
id="stop1676" />
<stop
style="stop-color:#ffe88e;stop-opacity:0;"
offset="1"
id="stop1678" />
</linearGradient>
<linearGradient
id="linearGradient1668"
inkscape:collect="always">
<stop
id="stop1664"
offset="0"
style="stop-color:#ad0000;stop-opacity:1" />
<stop
id="stop1666"
offset="1"
style="stop-color:#f03737;stop-opacity:1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient2460">
<stop
style="stop-color:#bb0000;stop-opacity:1"
offset="0"
id="stop2456" />
<stop
style="stop-color:#ec3232;stop-opacity:1"
offset="1"
id="stop2458" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1668"
id="linearGradient2462"
x1="16.727715"
y1="284.77112"
x2="16.727715"
y2="266.64716"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2460"
id="linearGradient2470"
x1="28.580872"
y1="287.96329"
x2="28.580872"
y2="277.84299"
gradientUnits="userSpaceOnUse" />
<filter
inkscape:collect="always"
style="color-interpolation-filters:sRGB"
id="filter2519"
x="-0.020552492"
width="1.041105"
y="-0.0289836"
height="1.0579672">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="0.25927001"
id="feGaussianBlur2521" />
</filter>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1680"
id="linearGradient1682"
x1="-0.63847286"
y1="271.38345"
x2="7.0625629"
y2="283.27271"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8284271"
inkscape:cx="137.28651"
inkscape:cy="31.290536"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1920"
inkscape:window-height="1025"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-263.13332)">
<rect
style="opacity:0;fill:#abfaff;fill-opacity:1;stroke:none;stroke-width:0.71147865;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect2553"
width="33.866665"
height="33.866665"
x="3.1582752e-09"
y="263.13333"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/64/videoconference_flash.png"
inkscape:export-xdpi="48"
inkscape:export-ydpi="48" />
<g
id="g1984"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/64/videoconference_flash.png"
inkscape:export-xdpi="48"
inkscape:export-ydpi="48">
<g
inkscape:export-ydpi="96"
inkscape:export-xdpi="96"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/128/videoconference_flash.png"
style="opacity:0.5;fill:#333333;filter:url(#filter2519)"
transform="matrix(1.033576,0,0,1.0310555,-0.27797462,-8.5225467)"
id="g2489">
<path
inkscape:connector-curvature="0"
id="path2485"
d="m 25.935038,279.09892 5.855173,-3.90345 v 15.61379 l -5.855173,-3.90345 z"
style="fill:#333333;fill-opacity:1;stroke-width:0.97586221" />
<path
inkscape:connector-curvature="0"
id="path2487"
d="m 24.934867,279.09892 v 7.80689 c 0,2.15568 -1.747769,3.90345 -3.903449,3.90345 H 5.4176225 c -2.1556796,0 -3.9034489,-1.74777 -3.9034489,-3.90345 v -7.80689 c 0,-2.15568 1.7477693,-3.90345 3.9034489,-3.90345 h 9.7586225 c 0,-1.07833 0.873397,-1.95173 1.951724,-1.95173 v -1.95172 H 6.3934848 c -0.5396519,0 -0.9758623,-0.43621 -0.9758623,-0.97586 0,-0.53966 0.4362104,-0.97587 0.9758623,-0.97587 H 18.103832 c 0.539651,0 0.975862,0.43621 0.975862,0.97587 v 2.92758 c 1.078328,0 1.951724,0.8734 1.951724,1.95173 2.15568,0 3.903449,1.74777 3.903449,3.90345 z"
style="fill:#333333;fill-opacity:1;stroke-width:0.97586221" />
</g>
<g
inkscape:export-ydpi="96"
inkscape:export-xdpi="96"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/128/videoconference_flash.png"
transform="matrix(1.033576,0,0,1.033576,-0.51968757,-9.9364024)"
id="g2477">
<path
style="fill:url(#linearGradient2470);fill-opacity:1;stroke-width:0.97586221"
d="m 25.935038,279.09892 5.855173,-3.90345 v 15.61379 l -5.855173,-3.90345 z"
id="path2263"
inkscape:connector-curvature="0" />
<path
style="fill:url(#linearGradient2462);fill-opacity:1;stroke-width:0.97586221"
d="m 24.934867,279.09892 v 7.80689 c 0,2.15568 -1.747769,3.90345 -3.903449,3.90345 H 5.4176225 c -2.1556796,0 -3.9034489,-1.74777 -3.9034489,-3.90345 v -7.80689 c 0,-2.15568 1.7477693,-3.90345 3.9034489,-3.90345 h 9.7586225 c 0,-1.07833 0.873397,-1.95173 1.951724,-1.95173 v -1.95172 H 6.3934848 c -0.5396519,0 -0.9758623,-0.43621 -0.9758623,-0.97586 0,-0.53966 0.4362104,-0.97587 0.9758623,-0.97587 H 18.103832 c 0.539651,0 0.975862,0.43621 0.975862,0.97587 v 2.92758 c 1.078328,0 1.951724,0.8734 1.951724,1.95173 2.15568,0 3.903449,1.74777 3.903449,3.90345 z"
id="path2261"
inkscape:connector-curvature="0" />
</g>
<path
inkscape:export-ydpi="96"
inkscape:export-xdpi="96"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/128/videoconference_flash.png"
sodipodi:nodetypes="csscc"
inkscape:connector-curvature="0"
style="opacity:0.7;fill:#9c1300;fill-opacity:1;stroke-width:1.00862777"
d="m 25.252393,278.53354 v 8.06902 c 0,2.22806 -1.806453,4.03451 -4.034512,4.03451 H 5.079837 c 11.391015,-1.7019 17.402266,-2.12976 20.172556,-12.10353 z"
id="path2356" />
<path
inkscape:export-ydpi="96"
inkscape:export-xdpi="96"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/128/videoconference_flash.png"
inkscape:connector-curvature="0"
style="fill:#ab0a01;fill-opacity:1;stroke-width:1.00862777"
d="m 21.389761,279.89888 c 0,-0.55777 -0.450856,-1.00863 -1.008628,-1.00863 h -4.034511 c -0.557771,0 -1.008627,0.45086 -1.008627,1.00863 0,0.55777 0.450856,1.00862 1.008627,1.00862 h 4.034511 c 0.557772,0 1.008628,-0.45085 1.008628,-1.00862 z"
id="path2434" />
<path
inkscape:export-ydpi="96"
inkscape:export-xdpi="96"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/128/videoconference_flash.png"
id="path2207"
d="m 21.217881,279.54217 c 0,-0.55777 -0.450856,-1.00863 -1.008627,-1.00863 h -4.034511 c -0.557772,0 -1.008628,0.45086 -1.008628,1.00863 0,0.55777 0.450856,1.00862 1.008628,1.00862 h 4.034511 c 0.557771,0 1.008627,-0.45085 1.008627,-1.00862 z"
style="fill:#ffffff;stroke-width:1.00862777"
inkscape:connector-curvature="0" />
<path
inkscape:export-ydpi="96"
inkscape:export-xdpi="96"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/128/videoconference_flash.png"
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="path2430"
d="m 26.291487,284.38104 6.050875,3.79017 8.96e-4,2.468 -6.051766,-4.03451 z"
style="opacity:0.7;fill:#a20d00;fill-opacity:1;stroke:none;stroke-width:0.27346697px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:export-ydpi="96"
inkscape:export-xdpi="96"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/128/videoconference_flash.png"
sodipodi:nodetypes="ccsc"
inkscape:connector-curvature="0"
id="path2432"
d="m 16.141917,274.71969 h 4.20274 c 0.0129,-0.99533 -1.095516,-1.3931 -2.138799,-1.39021 -1.40938,0.004 -2.038277,0.59352 -2.063941,1.39021 z"
style="opacity:0.6;fill:#ac0b02;fill-opacity:1;stroke:none;stroke-width:0.24197747px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:export-ydpi="96"
inkscape:export-xdpi="96"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/128/videoconference_flash.png"
inkscape:connector-curvature="0"
id="rect2451"
d="m 5.2273919,269.72519 c 0.2058204,0.52195 0.5953864,0.73877 0.8213089,0.73877 H 17.426583 v -0.73877 z"
style="opacity:0.6;fill:#ad0c03;fill-opacity:1;stroke:none;stroke-width:1.04796004;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<path
inkscape:export-ydpi="96"
inkscape:export-xdpi="96"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/128/videoconference_flash.png"
inkscape:connector-curvature="0"
id="path2479"
d="m 32.611328,273.98828 -6.599609,4.39844 v 0.14648 8.21485 l 6.599609,4.40039 z m -0.546875,1.02149 v 15.11718 l -5.503906,-3.66992 v -7.77734 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#9c1300;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.54693401;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<path
inkscape:export-ydpi="96"
inkscape:export-xdpi="96"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/128/videoconference_flash.png"
id="path1674"
d="m 1.0453253,286.60256 v -8.06902 c 0,-2.22806 1.806453,-4.03451 4.034512,-4.03451 H 21.217881 c -11.3910147,1.7019 -17.4022657,2.12976 -20.1725557,12.10353 z"
style="opacity:0.5;fill:url(#linearGradient1682);fill-opacity:1;stroke-width:1.00862777"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csscc" />
<path
inkscape:export-ydpi="96"
inkscape:export-xdpi="96"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/128/videoconference_flash.png"
inkscape:connector-curvature="0"
id="path2481"
d="m 6.0878906,268.17383 c -0.7051347,0 -1.28125,0.5761 -1.28125,1.28125 0,0.70513 0.5761121,1.2832 1.28125,1.2832 H 16.910156 v 1.58399 c -1.010704,0.1395 -1.761677,0.89199 -1.902344,1.90234 H 5.0800781 c -2.3758718,0 -4.30859372,1.93272 -4.30859372,4.30859 v 8.07032 c 0,2.37587 1.93272192,4.30664 4.30859372,4.30664 H 21.21875 c 2.375872,0 4.306641,-1.93077 4.306641,-4.30664 v -8.07032 c 0,-2.3142 -1.842033,-4.17671 -4.132813,-4.27343 -0.124647,-1.02913 -0.894024,-1.79603 -1.917969,-1.9375 v -2.86719 c 0,-0.70515 -0.578069,-1.28125 -1.283203,-1.28125 z m 0,0.54687 H 18.191406 c 0.410407,0 0.736328,0.32396 0.736328,0.73438 v 3.30078 h 0.273438 c 0.96687,0 1.74414,0.77726 1.74414,1.74414 v 0.27344 h 0.273438 c 2.080246,0 3.759766,1.67952 3.759766,3.75976 v 8.07032 c 0,2.08024 -1.67952,3.75976 -3.759766,3.75976 H 5.0800781 c -2.0802456,0 -3.7617187,-1.67952 -3.7617187,-3.75976 v -8.07032 c 0,-2.08024 1.6814731,-3.75976 3.7617187,-3.75976 H 15.439453 V 274.5 c 0,-0.96688 0.777271,-1.74414 1.744141,-1.74414 h 0.273437 v -2.56445 H 6.0878906 c -0.4104046,0 -0.734375,-0.32593 -0.734375,-0.73633 0,-0.41042 0.3239671,-0.73438 0.734375,-0.73438 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#9c1300;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.54693401;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<g
inkscape:export-ydpi="96"
inkscape:export-xdpi="96"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/128/videoconference_flash.png"
transform="matrix(0.03254278,0,0,0.03254278,-6.6373643e-5,274.37654)"
style="enable-background:new"
id="形状_1_2_">
<g
id="形状_1_1_">
<g
id="g1935">
<path
id="path1933"
style="clip-rule:evenodd;fill:#ffffff;fill-rule:evenodd"
d="M 360.478,162.082 V 115.396 C 208.547,119.891 254.603,335.028 144.275,340.232 v 46.685 c 72.696,-2.188 101.252,-54.152 124.335,-112.438 h 63.695 v -46.642 h -46.02 c 16.378,-33.651 30.595,-63.699 74.193,-65.755 z"
inkscape:connector-curvature="0" />
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,327 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="128"
height="128"
viewBox="0 0 33.866666 33.866668"
version="1.1"
id="svg8"
inkscape:version="0.92.3 (unknown)"
sodipodi:docname="videoconference_html5.svg">
<defs
id="defs2">
<linearGradient
inkscape:collect="always"
id="linearGradient1680">
<stop
style="stop-color:#ffe88e;stop-opacity:1;"
offset="0"
id="stop1676" />
<stop
style="stop-color:#ffe88e;stop-opacity:0;"
offset="1"
id="stop1678" />
</linearGradient>
<linearGradient
id="linearGradient1668"
inkscape:collect="always">
<stop
id="stop1664"
offset="0"
style="stop-color:#f16529;stop-opacity:1;" />
<stop
id="stop1666"
offset="1"
style="stop-color:#ff9f00;stop-opacity:1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient2460">
<stop
style="stop-color:#f16529;stop-opacity:1;"
offset="0"
id="stop2456" />
<stop
style="stop-color:#ff9e00;stop-opacity:1"
offset="1"
id="stop2458" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1668"
id="linearGradient2462"
x1="16.727715"
y1="284.77112"
x2="16.727715"
y2="266.64716"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2460"
id="linearGradient2470"
x1="28.580872"
y1="287.96329"
x2="28.580872"
y2="277.84299"
gradientUnits="userSpaceOnUse" />
<filter
inkscape:collect="always"
style="color-interpolation-filters:sRGB"
id="filter2519"
x="-0.020552492"
width="1.041105"
y="-0.0289836"
height="1.0579672">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="0.25927001"
id="feGaussianBlur2521" />
</filter>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1680"
id="linearGradient1682"
x1="-0.63847286"
y1="271.38345"
x2="7.0625629"
y2="283.27271"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4142136"
inkscape:cx="301.31637"
inkscape:cy="27.83582"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1920"
inkscape:window-height="1025"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-263.13332)">
<rect
style="opacity:0;fill:#abfaff;fill-opacity:1;stroke:none;stroke-width:0.71147865;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect2553"
width="33.866665"
height="33.866665"
x="3.8077737e-09"
y="263.13333"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/128/videoconference_html5.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<g
id="g2489"
transform="matrix(1.033576,0,0,1.0310555,-0.27797462,-8.5225467)"
style="opacity:0.5;fill:#333333;filter:url(#filter2519)"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/128/videoconference_html5.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<path
style="fill:#333333;fill-opacity:1;stroke-width:0.97586221"
d="m 25.935038,279.09892 5.855173,-3.90345 v 15.61379 l -5.855173,-3.90345 z"
id="path2485"
inkscape:connector-curvature="0" />
<path
style="fill:#333333;fill-opacity:1;stroke-width:0.97586221"
d="m 24.934867,279.09892 v 7.80689 c 0,2.15568 -1.747769,3.90345 -3.903449,3.90345 H 5.4176225 c -2.1556796,0 -3.9034489,-1.74777 -3.9034489,-3.90345 v -7.80689 c 0,-2.15568 1.7477693,-3.90345 3.9034489,-3.90345 h 9.7586225 c 0,-1.07833 0.873397,-1.95173 1.951724,-1.95173 v -1.95172 H 6.3934848 c -0.5396519,0 -0.9758623,-0.43621 -0.9758623,-0.97586 0,-0.53966 0.4362104,-0.97587 0.9758623,-0.97587 H 18.103832 c 0.539651,0 0.975862,0.43621 0.975862,0.97587 v 2.92758 c 1.078328,0 1.951724,0.8734 1.951724,1.95173 2.15568,0 3.903449,1.74777 3.903449,3.90345 z"
id="path2487"
inkscape:connector-curvature="0" />
</g>
<g
id="g2477"
transform="matrix(1.033576,0,0,1.033576,-0.51968757,-9.9364024)"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/128/videoconference_html5.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<path
inkscape:connector-curvature="0"
id="path2263"
d="m 25.935038,279.09892 5.855173,-3.90345 v 15.61379 l -5.855173,-3.90345 z"
style="fill:url(#linearGradient2470);fill-opacity:1;stroke-width:0.97586221" />
<path
inkscape:connector-curvature="0"
id="path2261"
d="m 24.934867,279.09892 v 7.80689 c 0,2.15568 -1.747769,3.90345 -3.903449,3.90345 H 5.4176225 c -2.1556796,0 -3.9034489,-1.74777 -3.9034489,-3.90345 v -7.80689 c 0,-2.15568 1.7477693,-3.90345 3.9034489,-3.90345 h 9.7586225 c 0,-1.07833 0.873397,-1.95173 1.951724,-1.95173 v -1.95172 H 6.3934848 c -0.5396519,0 -0.9758623,-0.43621 -0.9758623,-0.97586 0,-0.53966 0.4362104,-0.97587 0.9758623,-0.97587 H 18.103832 c 0.539651,0 0.975862,0.43621 0.975862,0.97587 v 2.92758 c 1.078328,0 1.951724,0.8734 1.951724,1.95173 2.15568,0 3.903449,1.74777 3.903449,3.90345 z"
style="fill:url(#linearGradient2462);fill-opacity:1;stroke-width:0.97586221" />
</g>
<path
id="path2356"
d="m 25.252393,278.53354 v 8.06902 c 0,2.22806 -1.806453,4.03451 -4.034512,4.03451 H 5.079837 c 11.391015,-1.7019 17.402266,-2.12976 20.172556,-12.10353 z"
style="opacity:0.7;fill:#e44d26;fill-opacity:1;stroke-width:1.00862777"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csscc"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/128/videoconference_html5.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<path
id="path2434"
d="m 21.389761,279.89888 c 0,-0.55777 -0.450856,-1.00863 -1.008628,-1.00863 h -4.034511 c -0.557771,0 -1.008627,0.45086 -1.008627,1.00863 0,0.55777 0.450856,1.00862 1.008627,1.00862 h 4.034511 c 0.557772,0 1.008628,-0.45085 1.008628,-1.00862 z"
style="fill:#cf411b;fill-opacity:1;stroke-width:1.00862777"
inkscape:connector-curvature="0"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/128/videoconference_html5.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<path
inkscape:connector-curvature="0"
style="fill:#ffffff;stroke-width:1.00862777"
d="m 21.217881,279.54217 c 0,-0.55777 -0.450856,-1.00863 -1.008627,-1.00863 h -4.034511 c -0.557772,0 -1.008628,0.45086 -1.008628,1.00863 0,0.55777 0.450856,1.00862 1.008628,1.00862 h 4.034511 c 0.557771,0 1.008627,-0.45085 1.008627,-1.00862 z"
id="path2207"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/128/videoconference_html5.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<g
id="g2402"
style="fill:#cf411b;fill-opacity:1"
transform="matrix(0.98483395,0,0,0.98483395,13.502964,4.0896839)"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/128/videoconference_html5.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<polygon
style="fill:#cf411b;fill-opacity:1"
points="354.668,398.63 376.684,152.138 135.317,152.138 157.294,398.619 255.94,426 "
id="polygon2377"
transform="matrix(0.03238923,0,0,0.03238923,-13.225862,275.14988)" />
<polygon
style="fill:#cf411b;fill-opacity:1"
points="172.827,101.188 172.827,86 157.462,86 157.462,131.97 172.827,131.97 172.827,116.572 186.877,116.572 186.877,131.97 202.254,131.97 202.254,86 186.877,86 186.877,101.188 "
id="polygon2379"
transform="matrix(0.03238923,0,0,0.03238923,-13.225862,275.14988)" />
<polygon
style="fill:#cf411b;fill-opacity:1"
points="251.334,101.249 251.334,86 208.934,86 208.934,101.249 222.448,101.249 222.448,131.97 237.819,131.97 237.819,101.249 "
id="polygon2381"
transform="matrix(0.03238923,0,0,0.03238923,-13.225862,275.14988)" />
<polygon
style="fill:#cf411b;fill-opacity:1"
points="294.514,131.97 309.823,131.97 309.823,86 293.793,86 283.944,102.177 274.096,86 258.081,86 258.081,131.97 273.105,131.97 273.105,109.195 283.688,125.528 283.944,125.528 294.514,109.195 "
id="polygon2383"
transform="matrix(0.03238923,0,0,0.03238923,-13.225862,275.14988)" />
<polygon
style="fill:#cf411b;fill-opacity:1"
points="332.829,116.782 332.829,86 317.463,86 317.463,131.97 354.437,131.97 354.437,116.782 "
id="polygon2385"
transform="matrix(0.03238923,0,0,0.03238923,-13.225862,275.14988)" />
</g>
<g
id="g2354"
transform="matrix(0.45852162,0,0,0.45852162,22.902026,168.84098)"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/128/videoconference_html5.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<polygon
transform="matrix(0.06956708,0,0,0.06956708,-49.576762,231.22172)"
id="polygon2309"
points="135.317,152.138 157.294,398.619 255.94,426 354.668,398.63 376.684,152.138 "
style="fill:#ffffff" />
<polygon
transform="matrix(0.06956708,0,0,0.06956708,-49.576762,231.22172)"
id="polygon2311"
points="172.827,131.97 172.827,116.572 186.877,116.572 186.877,131.97 202.254,131.97 202.254,86 186.877,86 186.877,101.188 172.827,101.188 172.827,86 157.462,86 157.462,131.97 "
style="fill:#ffffff" />
<polygon
transform="matrix(0.06956708,0,0,0.06956708,-49.576762,231.22172)"
id="polygon2313"
points="251.334,101.249 251.334,86 208.934,86 208.934,101.249 222.448,101.249 222.448,131.97 237.819,131.97 237.819,101.249 "
style="fill:#ffffff" />
<polygon
transform="matrix(0.06956708,0,0,0.06956708,-49.576762,231.22172)"
id="polygon2315"
points="274.096,86 258.081,86 258.081,131.97 273.105,131.97 273.105,109.195 283.688,125.528 283.944,125.528 294.514,109.195 294.514,131.97 309.823,131.97 309.823,86 293.793,86 283.944,102.177 "
style="fill:#ffffff" />
<polygon
transform="matrix(0.06956708,0,0,0.06956708,-49.576762,231.22172)"
id="polygon2317"
points="354.437,131.97 354.437,116.782 332.829,116.782 332.829,86 317.463,86 317.463,131.97 "
style="fill:#ffffff" />
<polygon
transform="matrix(0.06956708,0,0,0.06956708,-49.576762,231.22172)"
id="polygon2319"
points="220.043,307.984 222.292,333.174 255.96,342.271 256.009,342.258 289.709,333.16 293.22,293.949 256.009,293.949 188.448,293.949 180.285,202.532 256.001,202.532 331.703,202.532 329.009,232.743 256.001,232.743 213.323,232.743 216.081,263.702 256.001,263.702 326.263,263.702 317.973,356.543 256.001,373.709 255.953,373.723 194.022,356.543 189.702,307.984 "
style="fill:#f16629;fill-opacity:1" />
</g>
<path
style="opacity:0.7;fill:#e44d26;fill-opacity:1;stroke:none;stroke-width:0.27346697px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 26.291487,284.38104 6.050875,3.79017 8.96e-4,2.468 -6.051766,-4.03451 z"
id="path2430"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/128/videoconference_html5.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<path
style="opacity:0.6;fill:#f14329;fill-opacity:1;stroke:none;stroke-width:0.24197747px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 16.141917,274.71969 h 4.20274 c 0.0129,-0.99533 -1.095516,-1.3931 -2.138799,-1.39021 -1.40938,0.004 -2.038277,0.59352 -2.063941,1.39021 z"
id="path2432"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccsc"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/128/videoconference_html5.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<path
style="opacity:0.6;fill:#f14329;fill-opacity:1;stroke:none;stroke-width:1.04796004;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 5.2273919,269.72519 c 0.2058204,0.52195 0.5953864,0.73877 0.8213089,0.73877 H 17.426583 v -0.73877 z"
id="rect2451"
inkscape:connector-curvature="0"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/128/videoconference_html5.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#e36e00;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.54693401;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 32.611328,273.98828 -6.599609,4.39844 v 0.14648 8.21485 l 6.599609,4.40039 z m -0.546875,1.02149 v 15.11718 l -5.503906,-3.66992 v -7.77734 z"
id="path2479"
inkscape:connector-curvature="0"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/128/videoconference_html5.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<path
sodipodi:nodetypes="csscc"
inkscape:connector-curvature="0"
style="opacity:0.5;fill:url(#linearGradient1682);fill-opacity:1;stroke-width:1.00862777"
d="m 1.0453253,286.60256 v -8.06902 c 0,-2.22806 1.806453,-4.03451 4.034512,-4.03451 H 21.217881 c -11.3910147,1.7019 -17.4022657,2.12976 -20.1725557,12.10353 z"
id="path1674"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/128/videoconference_html5.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#e36e00;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.54693401;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 6.0878906,268.17383 c -0.7051347,0 -1.28125,0.5761 -1.28125,1.28125 0,0.70513 0.5761121,1.2832 1.28125,1.2832 H 16.910156 v 1.58399 c -1.010704,0.1395 -1.761677,0.89199 -1.902344,1.90234 H 5.0800781 c -2.3758718,0 -4.30859372,1.93272 -4.30859372,4.30859 v 8.07032 c 0,2.37587 1.93272192,4.30664 4.30859372,4.30664 H 21.21875 c 2.375872,0 4.306641,-1.93077 4.306641,-4.30664 v -8.07032 c 0,-2.3142 -1.842033,-4.17671 -4.132813,-4.27343 -0.124647,-1.02913 -0.894024,-1.79603 -1.917969,-1.9375 v -2.86719 c 0,-0.70515 -0.578069,-1.28125 -1.283203,-1.28125 z m 0,0.54687 H 18.191406 c 0.410407,0 0.736328,0.32396 0.736328,0.73438 v 3.30078 h 0.273438 c 0.96687,0 1.74414,0.77726 1.74414,1.74414 v 0.27344 h 0.273438 c 2.080246,0 3.759766,1.67952 3.759766,3.75976 v 8.07032 c 0,2.08024 -1.67952,3.75976 -3.759766,3.75976 H 5.0800781 c -2.0802456,0 -3.7617187,-1.67952 -3.7617187,-3.75976 v -8.07032 c 0,-2.08024 1.6814731,-3.75976 3.7617187,-3.75976 H 15.439453 V 274.5 c 0,-0.96688 0.777271,-1.74414 1.744141,-1.74414 h 0.273437 v -2.56445 H 6.0878906 c -0.4104046,0 -0.734375,-0.32593 -0.734375,-0.73633 0,-0.41042 0.3239671,-0.73438 0.734375,-0.73438 z"
id="path2481"
inkscape:connector-curvature="0"
inkscape:export-filename="/var/www/chamilo11/plugin/bbb/resources/img/128/videoconference_html5.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -0,0 +1,27 @@
$(function () {
$('.check-meeting-video').on('click', function (e) {
e.preventDefault();
var $self = $(this),
meetingId = $self.data('id') || 0;
if (!meetingId) {
return;
}
var $loader = $('<span>', {
'aria-hidden': 'true'
}).addClass('fa fa-spinner fa-spin fa-fw');
$self.replaceWith($loader);
$.get(_p.web_plugin + 'bbb/ajax.php', {
a: 'check_m4v',
meeting: meetingId
}, function (response) {
$loader.replaceWith(response.link);
window.open(response.url);
});
});
});

112
plugin/bbb/start.php Normal file
View File

@@ -0,0 +1,112 @@
<?php
/* For license terms, see /license.txt */
/**
* This script initiates a video conference session, calling the BigBlueButton API.
*/
require_once __DIR__.'/../../vendor/autoload.php';
$course_plugin = 'bbb'; //needed in order to load the plugin lang variables
$isGlobal = isset($_GET['global']);
$isGlobalPerUser = isset($_GET['user_id']) ? (int) $_GET['user_id'] : false;
// If global setting is used then we delete the course sessions (cidReq/id_session)
if ($isGlobalPerUser || $isGlobal) {
$cidReset = true;
}
require_once __DIR__.'/config.php';
$logInfo = [
'tool' => 'Videoconference',
];
Event::registerLog($logInfo);
$tool_name = get_lang('Videoconference');
$tpl = new Template($tool_name);
$vmIsEnabled = false;
$host = '';
$salt = '';
$bbb = new bbb('', '', $isGlobal, $isGlobalPerUser);
$conferenceManager = $bbb->isConferenceManager();
if ($bbb->isGlobalConference()) {
api_block_anonymous_users();
} else {
api_protect_course_script(true);
}
$message = null;
if ($bbb->pluginEnabled) {
if ($bbb->isServerConfigured()) {
if ($bbb->isServerRunning()) {
if (isset($_GET['launch']) && $_GET['launch'] == 1) {
if (file_exists(__DIR__.'/config.vm.php')) {
$config = require __DIR__.'/config.vm.php';
$vmIsEnabled = true;
$host = '';
$salt = '';
require __DIR__.'/lib/vm/AbstractVM.php';
require __DIR__.'/lib/vm/VMInterface.php';
require __DIR__.'/lib/vm/DigitalOceanVM.php';
require __DIR__.'/lib/VM.php';
$vm = new VM($config);
if ($vm->isEnabled()) {
try {
$vm->resizeToMaxLimit();
} catch (\Exception $e) {
echo $e->getMessage();
exit;
}
}
}
$meetingParams = [];
$meetingParams['meeting_name'] = $bbb->generateVideoConferenceName($_GET['name'] ?? null);
$url = null;
if ($bbb->meetingExists($meetingParams['meeting_name'])) {
$joinUrl = $bbb->joinMeeting($meetingParams['meeting_name']);
if ($joinUrl) {
$url = $joinUrl;
}
} else {
if ($bbb->isConferenceManager()) {
$url = $bbb->createMeeting($meetingParams);
}
}
$meetingInfo = $bbb->findMeetingByName($meetingParams['meeting_name']);
if (!empty($meetingInfo) && $url) {
$bbb->saveParticipant($meetingInfo['id'], api_get_user_id());
$bbb->redirectToBBB($url);
} else {
Display::addFlash(
Display::return_message($bbb->plugin->get_lang('ThereIsNoVideoConferenceActive'))
);
$url = $bbb->getListingUrl();
header('Location: '.$url);
exit;
}
} else {
$url = $bbb->getListingUrl();
header('Location: '.$url);
exit;
}
} else {
$message = Display::return_message(get_lang('ServerIsNotRunning'), 'warning');
}
} else {
$message = Display::return_message(get_lang('ServerIsNotConfigured'), 'warning');
}
} else {
$message = Display::return_message(get_lang('ServerIsNotConfigured'), 'warning');
}
$tpl->assign('message', $message);
$tpl->display_one_col_template();

12
plugin/bbb/uninstall.php Normal file
View File

@@ -0,0 +1,12 @@
<?php
/* For license terms, see /license.txt */
/**
* This script is included by main/admin/settings.lib.php when unselecting a plugin
* and is meant to remove things installed by the install.php script in both
* the global database and the courses tables.
*
* @package chamilo.plugin.bigbluebutton
*/
require_once __DIR__.'/config.php';
BBBPlugin::create()->uninstall();

14
plugin/bbb/update.php Normal file
View File

@@ -0,0 +1,14 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Update the plugin.
*
* @package chamilo.plugin.bigbluebutton
*/
require_once __DIR__.'/config.php';
if (!api_is_platform_admin()) {
exit('You must have admin permissions to install plugins');
}
BBBPlugin::create()->update();

58
plugin/bbb/view/admin.tpl Normal file
View File

@@ -0,0 +1,58 @@
{{ settings_form }}
{{ 'RecordList'|get_plugin_lang('BBBPlugin') }}
{{ search_form }}
<table class="table table-hover table-striped">
<thead>
<tr>
<th>{{ 'DateStart'|get_lang }}</th>
<th>{{ 'DateEnd'|get_lang }}</th>
<th>{{ 'Status'|get_lang }}</th>
<th>{{ 'Records'|get_plugin_lang('BBBPlugin') }}</th>
<th>{{ 'Course'|get_lang }}</th>
<th>{{ 'Session'|get_lang }}</th>
<th>{{ 'Participants'|get_lang }}</th>
<th>{{ 'CountUsers'|get_lang }}</th>
<th>{{ 'Actions'|get_lang }}</th>
</tr>
</thead>
<tbody>
{% for meeting in meetings %}
<tr id="meeting-{{ meeting.id }}">
{% if meeting.visibility == 0 %}
<td class="muted">{{ meeting.created_at }}</td>
{% else %}
<td>{{ meeting.created_at }}</td>
{% endif %}
<td>{{ meeting.closed_at }}</td>
<td>
{% if meeting.status == 1 %}
<span class="label label-success">{{ 'MeetingOpened'|get_plugin_lang('BBBPlugin') }}</span>
{% else %}
<span class="label label-info">{{ 'MeetingClosed'|get_plugin_lang('BBBPlugin') }}</span>
{% endif %}
</td>
<td>
{% if meeting.record == 1 %}
{# Record list #}
{{ meeting.show_links }}
{% else %}
{{ 'NoRecording'|get_plugin_lang('BBBPlugin') }}
{% endif %}
</td>
<td>{{ meeting.course ?: '-' }}</td>
<td>{{ meeting.session ?: '-' }}</td>
<td>
{{ meeting.participants ? meeting.participants|join('<br>') : '-' }}
</td>
<td>
{{ meeting.participants ? meeting.participants | length : 0 }}
</td>
<td>
{{ meeting.action_links }}
</td>
</tr>
{% endfor %}
</tbody>
</table>

146
plugin/bbb/view/listing.tpl Normal file
View File

@@ -0,0 +1,146 @@
<style>
.conference .url{
padding: 5px;
margin-bottom: 5px;
}
.conference .share{
padding: 10px;
margin-top: 5px;
margin-bottom: 5px;
font-weight: bold;
}
</style>
<div class ="row">
{% if bbb_status == true %}
<div class ="col-md-12">
{{ form }}
{% if show_join_button == true %}
{{ enter_conference_links }}
<div class="text-center">
<strong>{{ 'UrlMeetingToShare'| get_plugin_lang('BBBPlugin') }}</strong>
<div class="well">
<div class="form-inline">
<div class="form-group">
<input id="share_button"
type="text"
style="width:600px"
class="form-control" readonly value="{{ conference_url }}">
<button onclick="copyTextToClipBoard('share_button');" class="btn btn-default">
<span class="fa fa-copy"></span> {{ 'CopyTextToClipboard' | get_lang }}
</button>
</div>
</div>
</div>
<p>
<span id="users_online" class="label label-warning">
{{ 'XUsersOnLine'| get_plugin_lang('BBBPlugin') | format(users_online) }}
</span>
</p>
{% if max_users_limit > 0 %}
{% if conference_manager == true %}
<p>{{ 'MaxXUsersWarning' | get_plugin_lang('BBBPlugin') | format(max_users_limit) }}</p>
{% elseif users_online >= max_users_limit/2 %}
<p>{{ 'MaxXUsersWarning' | get_plugin_lang('BBBPlugin') | format(max_users_limit) }}</p>
{% endif %}
{% endif %}
</div>
</div>
{% elseif max_users_limit > 0 %}
{% if conference_manager == true %}
<p>{{ 'MaxXUsersReachedManager' | get_plugin_lang('BBBPlugin') | format(max_users_limit) }}</p>
{% elseif users_online > 0 %}
<p>{{ 'MaxXUsersReached' | get_plugin_lang('BBBPlugin') | format(max_users_limit) }}</p>
{% endif %}
{% endif %}
</div>
<div class ="col-md-12">
<div class="page-header">
<h2>{{ 'RecordList'| get_plugin_lang('BBBPlugin') }}</h2>
</div>
<table class="table">
<tr>
<th>{{ 'Name'|get_lang }}</th>
<th>{{ 'CreatedAt'| get_plugin_lang('BBBPlugin') }}</th>
<th>{{ 'Status'| get_lang }}</th>
<th>{{ 'Records'| get_plugin_lang('BBBPlugin') }}</th>
{% if allow_to_edit %}
<th>{{ 'Actions'| get_lang }}</th>
{% endif %}
</tr>
{% for meeting in meetings %}
<tr>
<!-- td>{{ meeting.id }}</td -->
<td>{{ meeting.metting_name }}</td>
{% if meeting.visibility == 0 %}
<td class="muted">{{ meeting.created_at }}</td>
{% else %}
<td>{{ meeting.created_at }}</td>
{% endif %}
<td>
{% if meeting.status == 1 %}
<span class="label label-success">{{ 'MeetingOpened'|get_plugin_lang('BBBPlugin') }}</span>
{% else %}
<span class="label label-info">{{ 'MeetingClosed'|get_plugin_lang('BBBPlugin') }}</span>
{% endif %}
</td>
<td>
{% if meeting.show_links.record %}
{# Record list #}
{% for link in meeting.show_links %}
{% if link is not iterable %}
{{ link }}
{% endif %}
{% endfor %}
{% else %}
{{ 'NoRecording'|get_plugin_lang('BBBPlugin') }}
{% endif %}
</td>
{% if allow_to_edit %}
<td>
{% if meeting.status == 1 %}
<a class="btn btn-default" href="{{ meeting.end_url }} ">
{{ 'CloseMeeting'|get_plugin_lang('BBBPlugin') }}
</a>
{% endif %}
{{ meeting.action_links }}
</td>
{% endif %}
</tr>
{% endfor %}
</table>
</div>
<nav>
<ul class="pagination">
<li class="page-item {% if page_id <= 1 %} disabled {% endif %} ">
<a class="page-link"
href= "{{ _p.web_self_query_vars ~ '&' ~ {'page_id' : page_id - 1 }|url_encode() }}" >
{{ 'Previous' | get_lang }}
</a>
</li>
{% if page_number > 0 %}
{% for i in 1..page_number %}
<li class="page-item {% if page_id == i %} active {% endif %} ">
<a class="page-link" href="{{ _p.web_self_query_vars ~ '&' ~ {'page_id': i }|url_encode() }}" >
{{ i }}
</a>
</li>
{% endfor %}
{% endif %}
<li class="page-item {% if page_number <= page_id %} disabled {% endif %} ">
<a class="page-link" href= "{{ _p.web_self_query_vars ~ '&' ~ {'page_id' : page_id + 1 }|url_encode() }}">
{{ 'Next' | get_lang }}
</a>
</li>
</ul>
</nav>
{% else %}
<div class ="col-md-12" style="text-align:center">
{{ 'ServerIsNotRunning' | get_plugin_lang('BBBPlugin') | return_message('warning') }}
</div>
{% endif %}
</div>