diff --git a/composer.json b/composer.json
index a94cfa39820..76bbc659d6b 100755
--- a/composer.json
+++ b/composer.json
@@ -67,6 +67,7 @@
"a2lix/translation-form-bundle": "^3.0",
"api-platform/core": "^3.0",
"beberlei/doctrineextensions": "^1.3",
+ "bigbluebutton/bigbluebutton-api-php": "^2.0",
"chamilo/google-map-form-type-bundle": "1.7",
"chamilo/settings-bundle": "1.3.0",
"clue/graph": "^0.9.2",
diff --git a/composer.lock b/composer.lock
index 7d06fe3bb7c..85b799d43a5 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "b16dee0d9d9a7985b03aaa06bf9d2932",
+ "content-hash": "24552378ff953d490750d5da53b10ba8",
"packages": [
{
"name": "a2lix/auto-form-bundle",
@@ -903,6 +903,73 @@
"abandoned": true,
"time": "2022-03-30T09:27:43+00:00"
},
+ {
+ "name": "bigbluebutton/bigbluebutton-api-php",
+ "version": "2.3.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/bigbluebutton/bigbluebutton-api-php.git",
+ "reference": "0a6b15bb650c54ab721a9c7e8d276c2d321570f1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/bigbluebutton/bigbluebutton-api-php/zipball/0a6b15bb650c54ab721a9c7e8d276c2d321570f1",
+ "reference": "0a6b15bb650c54ab721a9c7e8d276c2d321570f1",
+ "shasum": ""
+ },
+ "require": {
+ "ext-curl": "*",
+ "ext-json": "*",
+ "ext-mbstring": "*",
+ "ext-simplexml": "*",
+ "marc-mabe/php-enum": "^v4.7.0",
+ "php": ">=7.4"
+ },
+ "require-dev": {
+ "bmitch/churn-php": "^1.7.1",
+ "fakerphp/faker": "^v1.23.1",
+ "friendsofphp/php-cs-fixer": "^v3.48.0",
+ "nunomaduro/phpinsights": "^v2.11.0",
+ "phploc/phploc": "^7.0.2",
+ "phpmetrics/phpmetrics": "^v2.8.2",
+ "phpstan/phpstan": "^1.10",
+ "phpunit/php-code-coverage": "9.2.30",
+ "phpunit/phpunit": "^9.6.16",
+ "squizlabs/php_codesniffer": "^3.8.1",
+ "tracy/tracy": "2.9",
+ "vlucas/phpdotenv": "^5.6",
+ "wapmorgan/php-deprecation-detector": "^2.0.33"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "BigBlueButton\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-3.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Ghazi Triki",
+ "email": "ghazi.triki@riadvice.tn",
+ "role": "Developer"
+ }
+ ],
+ "description": "BigBlueButton PHP API Library for PHP",
+ "homepage": "https://bigbluebutton.org/",
+ "keywords": [
+ "api",
+ "bbb",
+ "bigbluebutton"
+ ],
+ "support": {
+ "issues": "https://github.com/bigbluebutton/bigbluebutton-api-php/issues",
+ "source": "https://github.com/bigbluebutton/bigbluebutton-api-php/tree/2.3.1"
+ },
+ "time": "2024-05-07T13:31:15+00:00"
+ },
{
"name": "brick/math",
"version": "0.13.1",
@@ -7310,6 +7377,79 @@
],
"time": "2022-12-08T12:29:14+00:00"
},
+ {
+ "name": "marc-mabe/php-enum",
+ "version": "v4.7.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/marc-mabe/php-enum.git",
+ "reference": "7159809e5cfa041dca28e61f7f7ae58063aae8ed"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/marc-mabe/php-enum/zipball/7159809e5cfa041dca28e61f7f7ae58063aae8ed",
+ "reference": "7159809e5cfa041dca28e61f7f7ae58063aae8ed",
+ "shasum": ""
+ },
+ "require": {
+ "ext-reflection": "*",
+ "php": "^7.1 | ^8.0"
+ },
+ "require-dev": {
+ "phpbench/phpbench": "^0.16.10 || ^1.0.4",
+ "phpstan/phpstan": "^1.3.1",
+ "phpunit/phpunit": "^7.5.20 | ^8.5.22 | ^9.5.11",
+ "vimeo/psalm": "^4.17.0 | ^5.26.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-3.x": "3.2-dev",
+ "dev-master": "4.7-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "MabeEnum\\": "src/"
+ },
+ "classmap": [
+ "stubs/Stringable.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Marc Bennewitz",
+ "email": "dev@mabe.berlin",
+ "homepage": "https://mabe.berlin/",
+ "role": "Lead"
+ }
+ ],
+ "description": "Simple and fast implementation of enumerations with native PHP",
+ "homepage": "https://github.com/marc-mabe/php-enum",
+ "keywords": [
+ "enum",
+ "enum-map",
+ "enum-set",
+ "enumeration",
+ "enumerator",
+ "enummap",
+ "enumset",
+ "map",
+ "set",
+ "type",
+ "type-hint",
+ "typehint"
+ ],
+ "support": {
+ "issues": "https://github.com/marc-mabe/php-enum/issues",
+ "source": "https://github.com/marc-mabe/php-enum/tree/v4.7.1"
+ },
+ "time": "2024-11-28T04:54:44+00:00"
+ },
{
"name": "markbaker/complex",
"version": "3.0.2",
@@ -23032,7 +23172,7 @@
],
"aliases": [],
"minimum-stability": "stable",
- "stability-flags": [],
+ "stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
@@ -23057,7 +23197,7 @@
"ext-zip": "*",
"ext-zlib": "*"
},
- "platform-dev": [],
+ "platform-dev": {},
"platform-overrides": {
"php": "8.2"
},
diff --git a/public/main/inc/ajax/plugin.ajax.php b/public/main/inc/ajax/plugin.ajax.php
index 11b4c2ff687..fd7da866e14 100644
--- a/public/main/inc/ajax/plugin.ajax.php
+++ b/public/main/inc/ajax/plugin.ajax.php
@@ -1,7 +1,9 @@
true, 'message' => "Plugin action '$action' applied to '$pluginTitle'."]);
break;
+ case 'list_documents':
+ $courseId = api_get_course_int_id();
+ $em = Database::getManager();
+ $repo = $em->getRepository(ResourceNode::class);
+
+ $qb = $em->createQueryBuilder()
+ ->select('DISTINCT d')
+ ->from(CDocument::class, 'd')
+ ->innerJoin('d.resourceNode','rn')
+ ->innerJoin('rn.resourceFiles','rf')
+ ->where('d.filetype = :type')
+ ->setParameter('type','file');
+
+ if ($courseId > 0) {
+ $qb->innerJoin('rn.resourceLinks','rl')
+ ->andWhere('rl.course = :c')
+ ->setParameter('c',$courseId);
+ }
+
+ $docs = $qb->getQuery()->getResult();
+ $out = [];
+
+ foreach ($docs as $doc) {
+ $files = $doc->getResourceNode()->getResourceFiles();
+ if ($files->isEmpty()) continue;
+
+ $file = $files->first();
+ $orig = $file->getOriginalName();
+ $ext = strtolower(pathinfo($orig, PATHINFO_EXTENSION));
+ if (! in_array($ext, ['pdf','ppt','pptx','odp'], true)) {
+ continue;
+ }
+
+ $path = '/var/upload/resource'.$repo->getFilename($file);
+ $base = str_replace('/public/', '', api_get_path(SYS_PATH));
+ $sysPath = $base . $path;
+
+ $out[] = [
+ 'id' => $doc->getIid(),
+ 'url' => $sysPath,
+ 'filename' => $orig,
+ ];
+ }
+ header('Content-Type: application/json');
+ echo json_encode($out);
+ exit;
default:
echo json_encode(['error' => 'Invalid action']);
}
diff --git a/public/plugin/Bbb/lib/bbb.lib.php b/public/plugin/Bbb/lib/bbb.lib.php
index 1993b231a33..21ad4f78661 100644
--- a/public/plugin/Bbb/lib/bbb.lib.php
+++ b/public/plugin/Bbb/lib/bbb.lib.php
@@ -150,7 +150,7 @@ public function __construct(
define('CONFIG_SERVER_BASE_URL', $this->url);
define('CONFIG_SERVER_PROTOCOL', $this->protocol);
- $this->api = new BigBlueButtonBN();
+ $this->api = new BigBlueButtonBN(CONFIG_SERVER_URL_WITH_PROTOCOL, CONFIG_SECURITY_SALT);
$this->pluginEnabled = true;
$this->logoutUrl = $this->getListingUrl();
}
@@ -463,6 +463,7 @@ public function createMeeting($params)
'maxParticipants' => $max,
'record' => $record,
'duration' => $duration,
+ 'documents' => $params['documents'],
];
$status = false;
@@ -636,9 +637,15 @@ public function joinMeeting($meetingName)
'username' => $this->userCompleteName,
'password' => $pass,
'userID' => api_get_user_id(),
+ 'moderatorPw' => $this->getModMeetingPassword(),
+ 'userID' => api_get_user_id(),
'webVoiceConf' => '',
];
$url = $this->api->getJoinMeetingURL($joinParams);
+ if (preg_match('#^https?://#i', $url)) {
+ return $url;
+ }
+
return $this->protocol . $url;
}
diff --git a/public/plugin/Bbb/lib/bbb_api.php b/public/plugin/Bbb/lib/bbb_api.php
index 5d25576d622..953ba84246f 100644
--- a/public/plugin/Bbb/lib/bbb_api.php
+++ b/public/plugin/Bbb/lib/bbb_api.php
@@ -37,637 +37,352 @@
-- See included samples for usage examples
*/
+use BigBlueButton\BigBlueButton;
+use BigBlueButton\Parameters\Config\DocumentOptionsStore;
+use BigBlueButton\Parameters\CreateMeetingParameters;
+use BigBlueButton\Parameters\JoinMeetingParameters;
+use BigBlueButton\Parameters\EndMeetingParameters;
+use BigBlueButton\Parameters\IsMeetingRunningParameters;
+use BigBlueButton\Parameters\GetMeetingInfoParameters;
+use BigBlueButton\Parameters\GetRecordingsParameters;
+use BigBlueButton\Parameters\PublishRecordingsParameters;
+use BigBlueButton\Parameters\DeleteRecordingsParameters;
+use BigBlueButton\Enum\Role;
+use BigBlueButton\Responses\CreateMeetingResponse;
+use BigBlueButton\Responses\IsMeetingRunningResponse;
+use BigBlueButton\Responses\GetMeetingsResponse;
+use BigBlueButton\Responses\GetMeetingInfoResponse;
+use BigBlueButton\Responses\GetRecordingsResponse;
+use BigBlueButton\Responses\PublishRecordingsResponse;
+use BigBlueButton\Responses\DeleteRecordingsResponse;
+
+/**
+ * Adapter for Chamilo Video Conference (Bbb) to use the official BigBlueButton PHP client 2.x.
+ */
class BigBlueButtonBN
{
- private $_securitySalt;
- private $_bbbServerBaseUrl;
- private $_bbbServerProtocol;
+ private BigBlueButton $client;
- public function __construct()
+ public function __construct(string $baseUrl, string $secret, array $curlOpts = [])
{
- /*
- 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)
+ // Initialize the official BBB client with server URL, secret and optional cURL options
+ $this->client = new BigBlueButton($baseUrl, $secret, ['curl' => $curlOpts]);
+ }
+
+ /**
+ * Build a create meeting URL.
+ *
+ * @param array $p meetingId, meetingName, attendeePw, moderatorPw, and optional settings
+ * @return string URL to call the BBB create API
+ */
+ public function getCreateMeetingUrl(array $p): string
{
- /*
- 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;
+ // Prepare parameters object
+ $cp = new CreateMeetingParameters($p['meetingId'], $p['meetingName']);
+ $cp->setAttendeePassword($p['attendeePw']);
+ $cp->setModeratorPassword($p['moderatorPw']);
+
+ // Optional settings
+ if (!empty($p['welcomeMsg'])) $cp->setWelcomeMessage($p['welcomeMsg']);
+ if (isset($p['dialNumber'])) $cp->setDialNumber($p['dialNumber']);
+ if (isset($p['voiceBridge'])) $cp->setVoiceBridge((int)$p['voiceBridge']);
+ if (isset($p['webVoice'])) $cp->setWebVoice($p['webVoice']);
+ if (isset($p['logoutUrl'])) $cp->setLogoutUrl($p['logoutUrl']);
+ if (isset($p['maxParticipants'])) $cp->setMaxParticipants((int)$p['maxParticipants']);
+ if (isset($p['record'])) $cp->setRecord((bool)$p['record']);
+ if (isset($p['duration'])) $cp->setDuration((int)$p['duration']);
+
+ // Delegate to the official client
+ return $this->client->getCreateMeetingUrl($cp);
+ }
+
+ /**
+ * Create a meeting and return the XML response as an array.
+ *
+ * @param array $p
+ * @return array
+ */
+ public function createMeetingWithXmlResponseArray(array $p): array
+ {
+ $cp = new CreateMeetingParameters($p['meetingId'], $p['meetingName']);
+ $cp->setAttendeePassword($p['attendeePw']);
+ $cp->setModeratorPassword($p['moderatorPw']);
+ if (!empty($p['welcomeMsg'])) $cp->setWelcomeMessage($p['welcomeMsg']);
+ if (isset($p['dialNumber'])) $cp->setDialNumber($p['dialNumber']);
+ if (isset($p['voiceBridge'])) $cp->setVoiceBridge((int)$p['voiceBridge']);
+ if (isset($p['webVoice'])) $cp->setWebVoice($p['webVoice']);
+ if (isset($p['logoutUrl'])) $cp->setLogoutUrl($p['logoutUrl']);
+ if (isset($p['maxParticipants'])) $cp->setMaxParticipants((int)$p['maxParticipants']);
+ if (isset($p['record'])) $cp->setRecord((bool)$p['record']);
+ if (isset($p['duration'])) $cp->setDuration((int)$p['duration']);
+
+ if (!empty($p['documents']) && is_array($p['documents'])) {
+ foreach ($p['documents'] as $doc) {
+ $options = new DocumentOptionsStore();
+ $options->addAttribute('removable', (bool) $doc['removable']);
+ $cp->addPresentation(
+ $doc['filename'],
+ file_get_contents($doc['url']),
+ $doc['filename'],
+ $options
+ );
+
}
- }
- 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_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)
+ }
+
+ /** @var CreateMeetingResponse $r */
+ $r = $this->client->createMeeting($cp);
+ $xml = $r->getRawXml();
+
+ // Map XML fields to array
+ return [
+ 'returncode' => (string) $xml->returncode,
+ 'message' => (string) $xml->message,
+ 'messageKey' => (string) $xml->messageKey,
+ 'meetingId' => (string) $xml->meetingID,
+ 'attendeePw' => (string) $xml->attendeePW,
+ 'moderatorPw' => (string) $xml->moderatorPW,
+ 'hasBeenForciblyEnded' => (string) $xml->hasBeenForciblyEnded,
+ 'createTime' => (string) $xml->createTime,
+ 'internalMeetingID' => (string) $xml->internalMeetingID,
+ ];
+ }
+
+ /**
+ * Generate a join meeting URL.
+ *
+ * @param array $p meetingId, username, password, moderatorPw, userID, webVoiceConf
+ * @return string
+ */
+ public function getJoinMeetingURL(array $p): string
{
- /*
- 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(),
+ // 1) Determine role: if password matches moderatorPw, user is moderator
+ $role = Role::VIEWER;
+ if (
+ !empty($p['password']) &&
+ !empty($p['moderatorPw']) &&
+ $p['password'] === $p['moderatorPw']
+ ) {
+ $role = Role::MODERATOR;
+ }
+
+ // 2) Construct parameters object (password passed in constructor)
+ $jp = new JoinMeetingParameters(
+ $p['meetingId'],
+ $p['username'],
+ $p['password']
+ );
+
+ // 3) Assign the role explicitly
+ $jp->setRole($role);
+
+ // 4) Optional parameters
+ if (!empty($p['userID'])) {
+ $jp->setUserId((string) $p['userID']);
+ }
+ if (!empty($p['webVoiceConf'])) {
+ $jp->setWebVoiceConf($p['webVoiceConf']);
+ }
+
+ // 5) Delegate to the official client (returns full URL with protocol)
+ return $this->client->getJoinMeetingUrl($jp);
+ }
+
+ /**
+ * Generate an end meeting URL.
+ *
+ * @param array $p meetingId, password
+ * @return string
+ */
+ public function getEndMeetingURL(array $p): string
+ {
+ // Only moderators can end
+ $ep = new EndMeetingParameters($p['meetingId'], Role::MODERATOR);
+ $ep->setPassword($p['password']);
+ return $this->client->getEndMeetingURL($ep);
+ }
+
+ /**
+ * Call endMeeting and return XML response as array.
+ *
+ * @param array $p
+ * @return array
+ */
+ public function endMeetingWithXmlResponseArray(array $p): array
+ {
+ $ep = new EndMeetingParameters($p['meetingId'], Role::MODERATOR);
+ $ep->setPassword($p['password']);
+ /** @var \BigBlueButton\Responses\EndMeetingResponse $r */
+ $r = $this->client->endMeeting($ep);
+
+ return [
+ 'returncode' => $r->getReturnCode(),
+ 'message' => $r->getMessage(),
+ 'messageKey' => $r->getMessageKey(),
+ ];
+ }
+
+ /**
+ * Build URL to check if meeting is running.
+ */
+ public function getIsMeetingRunningUrl(string $meetingId): string
+ {
+ $p = new IsMeetingRunningParameters($meetingId);
+ return $this->client->getIsMeetingRunningUrl($p);
+ }
+
+ /**
+ * Check if meeting is running and return result as array.
+ */
+ public function isMeetingRunningWithXmlResponseArray(string $meetingId): array
+ {
+ $p = new IsMeetingRunningParameters($meetingId);
+ /** @var IsMeetingRunningResponse $r */
+ $r = $this->client->isMeetingRunning($p);
+
+ return [
+ 'returncode' => $r->getReturnCode(),
+ 'running' => $r->isRunning(),
+ ];
+ }
+
+ /**
+ * List all meetings (raw XML to array).
+ */
+ public function getMeetingsWithXmlResponseArray(): array
+ {
+ /** @var GetMeetingsResponse $r */
+ $r = $this->client->getMeetings();
+ $xml = $r->getRawXml();
+
+ $out = [
+ 'returncode' => (string) $xml->returncode,
+ 'messageKey' => (string) $xml->messageKey,
+ 'message' => (string) $xml->message,
+ ];
+
+ if (isset($xml->meetings->meeting)) {
+ foreach ($xml->meetings->meeting as $m) {
+ $out[] = [
+ 'meetingId' => (string) $m->meetingID,
+ 'meetingName' => (string) $m->meetingName,
+ 'createTime' => (string) $m->createTime,
+ 'running' => (string) $m->running,
];
}
- } 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);
+
+ return $out;
+ }
+
+ /**
+ * Get detailed meeting info as array.
+ */
+ public function getMeetingInfoWithXmlResponseArray(array $p): array
+ {
+ /** @var GetMeetingInfoResponse $r */
+ $r = $this->client->getMeetingInfo(
+ new GetMeetingInfoParameters($p['meetingId'])
+ );
+ $xml = $r->getRawXml();
+
+ $out = [
+ 'returncode' => (string) $xml->returncode,
+ 'messageKey' => (string) $xml->messageKey,
+ 'message' => (string) $xml->message,
+ 'meetingName' => (string) $xml->meetingName,
+ 'meetingId' => (string) $xml->meetingID,
+ 'createTime' => (string) $xml->createTime,
+ 'voiceBridge' => (string) $xml->voiceBridge,
+ 'attendeePw' => (string) $xml->attendeePW,
+ 'moderatorPw' => (string) $xml->moderatorPW,
+ 'running' => (string) $xml->running,
+ 'recording' => (string) $xml->recording,
+ 'hasBeenForciblyEnded' => (string) $xml->hasBeenForciblyEnded,
+ 'startTime' => (string) $xml->startTime,
+ 'endTime' => (string) $xml->endTime,
+ 'participantCount' => (string) $xml->participantCount,
+ 'maxUsers' => (string) $xml->maxUsers,
+ 'moderatorCount' => (string) $xml->moderatorCount,
+ 'internalMeetingID' => (string) $xml->internalMeetingID,
+ ];
+
+ if (isset($xml->attendees->attendee)) {
+ foreach ($xml->attendees->attendee as $a) {
+ $out[] = [
+ 'userId' => (string) $a->userID,
+ 'fullName' => (string) $a->fullName,
+ 'role' => (string) $a->role,
+ ];
}
- $params .= '&redirectClient=true&clientURL='.$bbbHost.'/html5client/join';
}
- // Return the URL:
- $url = $joinUrl.$params.'&checksum='.sha1('join'.$params.$this->_securitySalt);
-
- return $url;
- }
-
- 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()
- );
-
- foreach ($xml->recordings->recording as $r) {
- $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(),
- '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)) {
- foreach ($xml->recordings->recording as $r) {
- $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(),
- '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;
+ return $out;
+ }
+
+ /**
+ * Fetch recordings list as array.
+ */
+ public function getRecordingsWithXmlResponseArray(array $p): array
+ {
+ /** @var GetRecordingsResponse $r */
+ $r = $this->client->getRecordings(
+ (new GetRecordingsParameters())->setMeetingId($p['meetingId'])
+ );
+ $xml = $r->getRawXml();
+
+ $out = [
+ 'returncode' => (string) $xml->returncode,
+ 'messageKey' => (string) $xml->messageKey,
+ 'message' => (string) $xml->message,
+ ];
+
+ if (isset($xml->recordings->recording)) {
+ foreach ($xml->recordings->recording as $rec) {
+ $out[] = [
+ 'recordId' => (string) $rec->recordID,
+ 'meetingId' => (string) $rec->meetingID,
+ 'name' => (string) $rec->name,
+ 'published' => (string) $rec->published,
+ 'startTime' => (string) $rec->startTime,
+ 'endTime' => (string) $rec->endTime,
+ 'playbackFormatType' => (string) $rec->playback->format->type,
+ 'playbackFormatUrl' => (string) $rec->playback->format->url,
+ 'playbackFormatLength' => (string) $rec->playback->format->length,
+ ];
+ }
}
- $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;
- }
+ return $out;
+ }
+
+ /**
+ * Publish or unpublish recordings.
+ */
+ public function publishRecordingsWithXmlResponseArray(array $p): array
+ {
+ /** @var PublishRecordingsResponse $r */
+ $q = new PublishRecordingsParameters($p['recordId'], (bool) $p['publish']);
+ $r = $this->client->publishRecordings($q);
+
+ return [
+ 'returncode' => $r->getReturnCode(),
+ 'published' => $r->isPublished(),
+ ];
+ }
+
+ /**
+ * Delete recordings by ID.
+ */
+ public function deleteRecordingsWithXmlResponseArray(array $p): array
+ {
+ /** @var DeleteRecordingsResponse $r */
+ $q = new DeleteRecordingsParameters($p['recordId']);
+ $r = $this->client->deleteRecordings($q);
+
+ return [
+ 'returncode' => $r->getReturnCode(),
+ 'deleted' => $r->isDeleted(),
+ ];
+ }
}
diff --git a/public/plugin/Bbb/listing.php b/public/plugin/Bbb/listing.php
index a3dec4303b6..03549a90c94 100644
--- a/public/plugin/Bbb/listing.php
+++ b/public/plugin/Bbb/listing.php
@@ -8,6 +8,7 @@
use Chamilo\CoreBundle\Entity\ConferenceActivity;
use Chamilo\CoreBundle\Entity\ConferenceMeeting;
+use Chamilo\CourseBundle\Entity\CGroup;
$course_plugin = 'bbb'; //needed in order to load the plugin lang variables
$isGlobal = isset($_GET['global']) ? true : false;
@@ -408,7 +409,7 @@
$meetingsGroup = array_column($meetingsInGroup, 'status', 'group_id');
$groupList[0] = get_lang('Select');
foreach ($groups as $groupData) {
- if ($groupData instanceof \Chamilo\CourseBundle\Entity\CGroup) {
+ if ($groupData instanceof CGroup) {
$itemGroupId = $groupData->getIid();
$name = $groupData->getTitle();
} else {
@@ -427,12 +428,75 @@
}
}
-// Default URL
-$urlList[] = Display::url(
- $plugin->get_lang('EnterConference'),
- $conferenceUrl,
- ['target' => '_blank', 'class' => 'btn btn--primary btn-large']
-);
+$urlList[] = '';
+if ($conferenceManager && $allowToEdit) {
+ $form = new FormValidator('start_conference', 'post', $conferenceUrl);
+ $form->addElement('hidden', 'action', 'start');
+ $ajaxUrl = api_get_path(WEB_PATH).'main/inc/ajax/plugin.ajax.php?plugin=bbb&a=list_documents';
+
+ $preuploadHtml = '
+ ' . get_lang('Select the PDF or PPTX files you want to pre-load as slides for the conference.') . '' . get_lang('Pre-upload Documents') . '
+
- - {{ 'XUsersOnLine'| get_plugin_lang('BBBPlugin') | format(users_online) }} - -
- +- {{ 'MaxXUsersWarning' | get_plugin_lang('BBBPlugin') | format(max_users_limit) }} +
+ {{ 'MaxXUsersWarning'|get_plugin_lang('BBBPlugin')|format(max_users_limit) }}
{% elseif max_users_limit > 0 %}@@ -67,50 +77,49 @@
{{ 'CreatedAt'| get_plugin_lang('BBBPlugin') }} | -{{ 'Status'| get_lang }} | -{{ 'Records'| get_plugin_lang('BBBPlugin') }} | +{{ 'CreatedAt'|get_plugin_lang('BBBPlugin') }} | +{{ 'Status'|get_lang }} | +{{ 'Records'|get_plugin_lang('BBBPlugin') }} | {% if allow_to_edit %} -{{ 'Actions'| get_lang }} | +{{ 'Actions'|get_lang }} | {% endif %}
---|---|---|---|---|---|---|---|
+ | |||||||
{{ meeting.created_at }} | {% if meeting.status == 1 %} - + {{ 'MeetingOpened'|get_plugin_lang('BBBPlugin') }} + {% else %} - + + {{ 'MeetingClosed'|get_plugin_lang('BBBPlugin') }} + {% endif %} | +{% if meeting.record == 1 %} {{ meeting.show_links }} {% else %} - {{ 'NoRecording'|get_plugin_lang('BBBPlugin') }} + {{ 'NoRecording'|get_plugin_lang('BBBPlugin') }} {% endif %} | {% if allow_to_edit %}
{% if meeting.status == 1 %}
-
+
{{ 'CloseMeeting'|get_plugin_lang('BBBPlugin') }}
{% endif %}
@@ -124,8 +133,8 @@
{% else %}
-
- {{ 'ServerIsNotRunning' | get_plugin_lang('BBBPlugin') }}
+
+ {{ 'ServerIsNotRunning'|get_plugin_lang('BBBPlugin') }}
{% endif %}
|