2020-05-16 17:14:17 +02:00
< ? php
namespace OCA\BigBlueButton\BigBlueButton ;
use BigBlueButton\BigBlueButton ;
2020-05-17 11:09:16 +02:00
use BigBlueButton\Core\Record ;
2020-09-23 12:33:09 +02:00
use BigBlueButton\Parameters\CreateMeetingParameters ;
2020-05-17 11:09:16 +02:00
use BigBlueButton\Parameters\DeleteRecordingsParameters ;
2020-09-23 12:33:09 +02:00
use BigBlueButton\Parameters\GetRecordingsParameters ;
2020-05-17 13:39:01 +02:00
use BigBlueButton\Parameters\IsMeetingRunningParameters ;
2020-09-23 12:33:09 +02:00
use BigBlueButton\Parameters\JoinMeetingParameters ;
2021-12-01 15:05:07 +01:00
use OCA\BigBlueButton\AppInfo\Application ;
2022-01-05 09:59:49 +01:00
use OCA\BigBlueButton\AvatarRepository ;
2020-09-23 12:33:09 +02:00
use OCA\BigBlueButton\Crypto ;
2020-05-16 17:14:17 +02:00
use OCA\BigBlueButton\Db\Room ;
2020-09-23 12:33:09 +02:00
use OCA\BigBlueButton\Event\MeetingStartedEvent ;
2020-09-23 09:13:26 +02:00
use OCA\BigBlueButton\UrlHelper ;
2021-12-01 15:05:07 +01:00
use OCP\App\IAppManager ;
2021-04-18 13:14:05 +02:00
use OCP\Defaults ;
2020-09-23 12:33:09 +02:00
use OCP\EventDispatcher\IEventDispatcher ;
2020-05-16 17:14:17 +02:00
use OCP\IConfig ;
2020-09-22 16:08:14 +02:00
use OCP\IL10N ;
2021-12-01 15:05:07 +01:00
use OCP\IRequest ;
2020-09-23 12:33:09 +02:00
use OCP\IURLGenerator ;
2020-05-16 17:14:17 +02:00
2020-06-19 09:28:58 +02:00
class API {
2020-05-16 17:14:17 +02:00
/** @var IConfig */
private $config ;
/** @var IURLGenerator */
private $urlGenerator ;
2020-06-19 10:49:40 +02:00
/** @var BigBlueButton|null */
2020-05-16 17:14:17 +02:00
private $server ;
2020-09-22 12:19:48 +02:00
/** @var Crypto */
private $crypto ;
2020-09-22 13:51:47 +02:00
/** @var IEventDispatcher */
private $eventDispatcher ;
2020-09-22 16:08:14 +02:00
/** @var IL10N */
private $l10n ;
2020-09-23 09:13:26 +02:00
/** @var UrlHelper */
private $urlHelper ;
2021-04-18 13:14:05 +02:00
/** @var Defaults */
private $defaults ;
2021-12-01 15:05:07 +01:00
/** @var IAppManager */
private $appManager ;
2022-01-05 09:59:49 +01:00
/** @var AvatarRepository */
private $avatarRepository ;
2021-12-01 15:05:07 +01:00
/** @var IRequest */
private $request ;
2020-05-16 17:14:17 +02:00
public function __construct (
IConfig $config ,
2020-06-15 17:23:53 +02:00
IURLGenerator $urlGenerator ,
2020-09-22 13:51:47 +02:00
Crypto $crypto ,
2020-09-22 16:08:14 +02:00
IEventDispatcher $eventDispatcher ,
2020-09-23 09:13:26 +02:00
IL10N $l10n ,
2021-04-18 13:14:05 +02:00
UrlHelper $urlHelper ,
2021-12-01 15:05:07 +01:00
Defaults $defaults ,
IAppManager $appManager ,
2022-01-05 09:59:49 +01:00
AvatarRepository $avatarRepository ,
2021-12-01 15:05:07 +01:00
IRequest $request
2020-05-16 17:14:17 +02:00
) {
$this -> config = $config ;
$this -> urlGenerator = $urlGenerator ;
2020-09-22 12:19:48 +02:00
$this -> crypto = $crypto ;
2020-09-22 13:51:47 +02:00
$this -> eventDispatcher = $eventDispatcher ;
2020-09-22 16:08:14 +02:00
$this -> l10n = $l10n ;
2020-09-23 09:13:26 +02:00
$this -> urlHelper = $urlHelper ;
2021-04-18 13:14:05 +02:00
$this -> defaults = $defaults ;
2021-12-01 15:05:07 +01:00
$this -> appManager = $appManager ;
2022-01-05 09:59:49 +01:00
$this -> avatarRepository = $avatarRepository ;
2021-12-01 15:05:07 +01:00
$this -> request = $request ;
2020-05-16 17:14:17 +02:00
}
2021-02-24 15:23:26 +01:00
private function getServer () : BigBlueButton {
2020-05-16 17:14:17 +02:00
if ( ! $this -> server ) {
$apiUrl = $this -> config -> getAppValue ( 'bbb' , 'api.url' );
$secret = $this -> config -> getAppValue ( 'bbb' , 'api.secret' );
$this -> server = new BigBlueButton ( $apiUrl , $secret );
}
return $this -> server ;
}
/**
* Create join url .
*
* @ return string join url
*/
2021-01-22 19:12:55 +01:00
public function createJoinUrl ( Room $room , float $creationTime , string $displayname , bool $isModerator , ? string $uid = null ) {
$password = $isModerator ? $room -> moderatorPassword : $room -> attendeePassword ;
2020-05-16 17:14:17 +02:00
$joinMeetingParams = new JoinMeetingParameters ( $room -> uid , $displayname , $password );
2020-12-17 00:58:31 +01:00
// ensure that float is not converted to a string in scientific notation
2022-01-05 02:40:00 +01:00
$joinMeetingParams -> setCreateTime ( sprintf ( " %.0f " , $creationTime ));
2020-05-16 17:14:17 +02:00
$joinMeetingParams -> setJoinViaHtml5 ( true );
$joinMeetingParams -> setRedirect ( true );
2022-03-08 15:09:18 +01:00
// set the guest parameter for everyone but moderators to send all users to the waiting room if setting is selected
$joinMeetingParams -> setGuest ((( $room -> access === Room :: ACCESS_WAITING_ROOM_ALL ) && ! $isModerator ) || $uid === null );
2020-05-16 17:14:17 +02:00
2021-04-19 14:47:15 +02:00
$joinMeetingParams -> addUserData ( 'bbb_listen_only_mode' , $room -> getListenOnly ());
$joinMeetingParams -> addUserData ( 'bbb_skip_check_audio_on_first_join' , ! $room -> getMediaCheck ()); // 2.3
$joinMeetingParams -> addUserData ( 'bbb_skip_video_preview_on_first_join' , ! $room -> getMediaCheck ()); // 2.3
2021-04-25 20:34:36 +02:00
if ( $room -> getCleanLayout ()) {
$joinMeetingParams -> addUserData ( 'bbb_auto_swap_layout' , true );
$joinMeetingParams -> addUserData ( 'bbb_show_participants_on_login' , false );
$joinMeetingParams -> addUserData ( 'bbb_show_public_chat_on_login' , false );
}
2021-04-19 14:47:15 +02:00
2021-04-18 13:14:05 +02:00
if ( $this -> config -> getAppValue ( 'bbb' , 'join.theme' ) === 'true' ) {
$primaryColor = $this -> defaults -> getColorPrimary ();
$textColor = $this -> defaults -> getTextColorPrimary ();
$joinMeetingParams -> addUserData ( 'bbb_custom_style' , " :root { --nc-primary-color: $primaryColor ;--nc-primary-text-color: $textColor ;--nc-bg-color:#444;--color-primary:var(--nc-primary-color);--btn-primary-color:var(--nc-primary-text-color);--color-text:#222;--loader-bg:var(--nc-bg-color);--user-list-bg:#fff;--user-list-text:#222;--list-item-bg-hover:#f5f5f5;--item-focus-border:var(--nc-primary-color);--color-off-white:#fff;--color-gray-dark:var(--nc-bg-color);}body { background-color:var(--nc-bg-color);}.overlay--1aTlbi { background-color:var(--nc-bg-color);}.userlistPad--o5KDX { border-right: 1px solid #ededed;}.scrollStyle--Ckr4w { background: transparent;}.item--yl1AH:hover, .item--yl1AH:focus { color:--nc-primary-text-color;}#message-input:focus { box-shadow:0 0 0 1px var(--nc-primary-color);border-color:--nc-primary-color;}.active--Z1SuO2X { border-radius:5px;} " );
}
2020-05-16 17:14:17 +02:00
if ( $uid ) {
2022-01-05 09:59:49 +01:00
$avatarUrl = $this -> avatarRepository -> getAvatarUrl ( $room , $uid );
$joinMeetingParams -> setUserID ( $uid );
$joinMeetingParams -> setAvatarURL ( $avatarUrl );
2020-05-16 17:14:17 +02:00
}
return $this -> getServer () -> getJoinMeetingURL ( $joinMeetingParams );
}
/**
* Create meeting room .
*
2021-02-24 16:33:37 +01:00
* @ return float | int creation time
2020-05-16 17:14:17 +02:00
*/
2020-06-19 09:28:58 +02:00
public function createMeeting ( Room $room , Presentation $presentation = null ) {
2020-05-16 17:14:17 +02:00
$bbb = $this -> getServer ();
2020-06-10 13:53:46 +02:00
$meetingParams = $this -> buildMeetingParams ( $room , $presentation );
2020-05-16 17:14:17 +02:00
try {
2020-06-10 13:53:46 +02:00
$response = $bbb -> createMeeting ( $meetingParams );
2020-05-16 17:14:17 +02:00
} catch ( \Exception $e ) {
2020-06-10 13:53:46 +02:00
throw new \Exception ( 'Can not process create request: ' . $bbb -> getCreateMeetingUrl ( $meetingParams ));
2020-05-16 17:14:17 +02:00
}
if ( ! $response -> success ()) {
2021-08-04 16:38:06 +02:00
throw new \Exception ( 'Can not create meeting: ' . $response -> getMessage ());
2020-05-16 17:14:17 +02:00
}
2020-09-22 13:51:47 +02:00
if ( $response -> getMessageKey () !== 'duplicateWarning' ) {
$this -> eventDispatcher -> dispatch ( MeetingStartedEvent :: class , new MeetingStartedEvent ( $room ));
}
2020-05-16 17:14:17 +02:00
return $response -> getCreationTime ();
}
2020-06-19 09:28:58 +02:00
private function buildMeetingParams ( Room $room , Presentation $presentation = null ) : CreateMeetingParameters {
2020-05-16 17:14:17 +02:00
$createMeetingParams = new CreateMeetingParameters ( $room -> uid , $room -> name );
2022-01-05 02:40:00 +01:00
$createMeetingParams -> setAttendeePW ( $room -> attendeePassword );
$createMeetingParams -> setModeratorPW ( $room -> moderatorPassword );
2020-05-16 17:14:17 +02:00
$createMeetingParams -> setRecord ( $room -> record );
$createMeetingParams -> setAllowStartStopRecording ( $room -> record );
2022-01-05 02:40:00 +01:00
$createMeetingParams -> setLogoutURL ( $this -> urlGenerator -> getBaseUrl ());
2021-07-30 12:12:42 +02:00
$createMeetingParams -> setMuteOnStart ( $room -> getJoinMuted ());
2020-05-16 17:14:17 +02:00
2021-12-01 15:05:07 +01:00
$createMeetingParams -> addMeta ( 'bbb-origin-version' , $this -> appManager -> getAppVersion ( Application :: ID ));
$createMeetingParams -> addMeta ( 'bbb-origin' , \method_exists ( $this -> defaults , 'getProductName' ) ? $this -> defaults -> getProductName () : 'Nextcloud' );
$createMeetingParams -> addMeta ( 'bbb-origin-server-name' , $this -> request -> getServerHost ());
2022-03-09 16:58:35 +01:00
$analyticsCallbackUrl = $this -> config -> getAppValue ( 'bbb' , 'api.meta_analytics-callback-url' );
if ( ! empty ( $analyticsCallbackUrl )) {
// For more details: https://github.com/bigbluebutton/bigbluebutton/blob/develop/record-and-playback/core/scripts/post_events/post_events_analytics_callback.rb
$createMeetingParams -> addMeta ( 'analytics-callback-url' , $analyticsCallbackUrl );
$createMeetingParams -> setMeetingKeepEvents ( true );
}
2020-09-22 12:19:48 +02:00
$mac = $this -> crypto -> calculateHMAC ( $room -> uid );
$endMeetingUrl = $this -> urlGenerator -> linkToRouteAbsolute ( 'bbb.hook.meetingEnded' , [ 'token' => $room -> uid , 'mac' => $mac ]);
$createMeetingParams -> setEndCallbackUrl ( $endMeetingUrl );
$recordingReadyUrl = $this -> urlGenerator -> linkToRouteAbsolute ( 'bbb.hook.recordingReady' , [ 'token' => $room -> uid , 'mac' => $mac ]);
$createMeetingParams -> setRecordingReadyCallbackUrl ( $recordingReadyUrl );
2020-09-23 09:13:26 +02:00
$invitationUrl = $this -> urlHelper -> linkToInvitationAbsolute ( $room );
2020-09-22 16:08:14 +02:00
$createMeetingParams -> setModeratorOnlyMessage ( $this -> l10n -> t ( 'To invite someone to the meeting, send them this link: %s' , [ $invitationUrl ]));
2020-05-16 17:14:17 +02:00
if ( ! empty ( $room -> welcome )) {
2022-01-05 02:40:00 +01:00
$createMeetingParams -> setWelcome ( $room -> welcome );
2020-05-16 17:14:17 +02:00
}
if ( $room -> maxParticipants > 0 ) {
$createMeetingParams -> setMaxParticipants ( $room -> maxParticipants );
}
if ( $presentation !== null && $presentation -> isValid ()) {
2021-02-24 16:33:37 +01:00
/** @psalm-suppress InvalidArgument */
2020-05-16 17:14:17 +02:00
$createMeetingParams -> addPresentation ( $presentation -> getUrl (), null , $presentation -> getFilename ());
}
2022-03-08 15:09:18 +01:00
if ( $room -> access === Room :: ACCESS_WAITING_ROOM || $room -> access === Room :: ACCESS_WAITING_ROOM_ALL ) {
2020-06-04 18:56:55 +02:00
$createMeetingParams -> setGuestPolicyAskModerator ();
}
2020-05-16 17:14:17 +02:00
return $createMeetingParams ;
}
2020-05-17 11:09:16 +02:00
2020-06-19 09:28:58 +02:00
public function getRecording ( string $recordId ) {
2020-05-17 11:09:16 +02:00
$recordingParams = new GetRecordingsParameters ();
2022-01-05 02:40:00 +01:00
$recordingParams -> setRecordID ( $recordId );
2020-05-17 11:09:16 +02:00
$recordingParams -> setState ( 'any' );
$response = $this -> getServer () -> getRecordings ( $recordingParams );
if ( ! $response -> success ()) {
throw new \Exception ( 'Could not process get recording request' );
}
$records = $response -> getRecords ();
if ( count ( $records ) === 0 ) {
throw new \Exception ( 'Found no record with given id' );
}
return $this -> recordToArray ( $records [ 0 ]);
}
2021-02-24 15:23:26 +01:00
public function getRecordings ( Room $room ) : array {
2020-05-17 11:09:16 +02:00
$recordingParams = new GetRecordingsParameters ();
2022-01-05 02:40:00 +01:00
$recordingParams -> setMeetingID ( $room -> uid );
2020-05-17 11:09:16 +02:00
$recordingParams -> setState ( 'processing,processed,published,unpublished' );
$response = $this -> getServer () -> getRecordings ( $recordingParams );
if ( ! $response -> success ()) {
throw new \Exception ( 'Could not process get recordings request' );
}
$records = $response -> getRecords ();
return array_map ( function ( $record ) {
return $this -> recordToArray ( $record );
}, $records );
}
2020-06-19 09:28:58 +02:00
public function deleteRecording ( string $recordingId ) : bool {
2020-05-17 11:09:16 +02:00
$deleteParams = new DeleteRecordingsParameters ( $recordingId );
$response = $this -> getServer () -> deleteRecordings ( $deleteParams );
return $response -> isDeleted ();
}
2021-02-24 15:23:26 +01:00
/**
* @ return ( array | bool | int | string )[]
*
* @ psalm - return array { id : string , meetingId : string , name : string , published : bool , state : string , startTime : string , participants : int , type : string , length : string , url : string , metas : array }
*/
private function recordToArray ( Record $record ) : array {
2020-05-17 11:09:16 +02:00
return [
2021-07-26 15:44:14 +02:00
'id' => $record -> getRecordId (),
'meetingId' => $record -> getMeetingId (),
'name' => $record -> getName (),
'published' => $record -> isPublished (),
'state' => $record -> getState (),
'startTime' => $record -> getStartTime (),
2020-05-17 11:09:16 +02:00
'participants' => $record -> getParticipantCount (),
2021-07-26 15:44:14 +02:00
'type' => $record -> getPlaybackType (),
'length' => $record -> getPlaybackLength (),
'url' => $record -> getPlaybackUrl (),
'metas' => $record -> getMetas (),
2020-05-17 11:09:16 +02:00
];
}
2020-05-17 13:39:01 +02:00
2021-02-24 15:23:26 +01:00
public function check ( string $url , string $secret ) : string {
2020-05-17 13:39:01 +02:00
$server = new BigBlueButton ( $url , $secret );
$meetingParams = new IsMeetingRunningParameters ( 'foobar' );
try {
$response = $server -> isMeetingRunning ( $meetingParams );
if ( ! $response -> success () && ! $response -> failed ()) {
return 'invalid-url' ;
}
if ( ! $response -> success ()) {
return 'invalid-secret' ;
}
return 'success' ;
} catch ( \Exception $e ) {
return 'invalid-url' ;
}
}
2021-02-13 16:14:40 +01:00
/**
* @ param null | string $url
*/
public function getVersion ( ? string $url = null ) {
2020-05-17 13:39:01 +02:00
$server = $url === null ? $this -> getServer () : new BigBlueButton ( $url , '' );
return $server -> getApiVersion () -> getVersion ();
}
2020-08-29 14:37:50 +02:00
public function isRunning ( Room $room ) : bool {
$isMeetingRunningParams = new IsMeetingRunningParameters ( $room -> getUid ());
$response = $this -> getServer () -> isMeetingRunning ( $isMeetingRunningParams );
return $response -> success () && $response -> isRunning ();
}
2020-05-16 17:14:17 +02:00
}