diff --git a/README.md b/README.md index 95634f1..4f5edec 100644 --- a/README.md +++ b/README.md @@ -71,28 +71,6 @@ curl -i -X PUT -H 'Authorization: Bearer syt_YWRtaW4_KXUwoITuowupgGEIGNuK_4MLu3S http://localhost:8001/_matrix/client/r0/rooms/!IWCtGkszxDkBUrIsGg%3Asynapse/state/m.room.name' ``` - - -## More information - - -In addition to the standard endpoints, additional endpoints are provided for setting up data which a test requires or expects: -``` -/backoffice/createMeeting -/backoffice/createRecording -``` - -In addition, the following endpoint can be used to trigger a reset between tests: -``` -/backoffice/reset -``` - -And the following endpoints exist to view the current meetings and recordings: -``` -/backoffice/meetings -/backoffice/recordings -``` - ## Automated tests You need to define `TEST_MOD_SYNAPSE_MOCK_SERVER` in your config.php when running automated tests like PHPUnit and Behat. Otherwise, most of the BigBlueButton tests will be marked skipped. @@ -102,7 +80,6 @@ For example, add the following to your config.php after the `$CFG->wwwroot` line define('TEST_MOD_SYNAPSE_MOCK_SERVER', "http://localhost:8001/hash" . sha1($CFG->wwwroot)); ``` - ## Local development * Symphony : `wget https://get.symfony.com/cli/installer -O - | bash` diff --git a/application/src/Component/HttpFoundation/ErrorResponse.php b/application/src/Component/HttpFoundation/ErrorResponse.php deleted file mode 100644 index 4f8264a..0000000 --- a/application/src/Component/HttpFoundation/ErrorResponse.php +++ /dev/null @@ -1,16 +0,0 @@ -getMeetingInfo(), $returnCode, $status, $headers); - } -} diff --git a/application/src/Component/HttpFoundation/MeetingSummaryResponse.php b/application/src/Component/HttpFoundation/MeetingSummaryResponse.php deleted file mode 100644 index ceeedef..0000000 --- a/application/src/Component/HttpFoundation/MeetingSummaryResponse.php +++ /dev/null @@ -1,15 +0,0 @@ -getMeetingSummary(), $returnCode, $status, $headers); - } -} diff --git a/application/src/Component/HttpFoundation/MessageResponse.php b/application/src/Component/HttpFoundation/MessageResponse.php deleted file mode 100644 index 701c461..0000000 --- a/application/src/Component/HttpFoundation/MessageResponse.php +++ /dev/null @@ -1,21 +0,0 @@ - $key, - 'message' => $message, - ]; - - parent::__construct($response, $returnCode, $status, $headers); - } -} diff --git a/application/src/Component/HttpFoundation/XmlResponse.php b/application/src/Component/HttpFoundation/XmlResponse.php deleted file mode 100644 index 2fb6359..0000000 --- a/application/src/Component/HttpFoundation/XmlResponse.php +++ /dev/null @@ -1,67 +0,0 @@ -returncode = $returnCode; - - $document = new SimpleXMLElement(''); - $this->convertToXml($document, $responseData); - - $content = $document->saveXML(); - parent::__construct($content, $status, array_merge($headers, [ - 'Content-Type' => 'text/xml', - ])); - } - - /** - * Convert the data object to XML using SimpleXML. - * - * @param SimpleXMLElement $node - * @param mixed $data - */ - protected function convertToXml(SimpleXMLElement $node, $data): void { - foreach ((array) $data as $key => $value) { - if (is_array($value)) { - $value = (object) $value; - } - if (is_object($value) && !empty($value->forcexmlarraytype)) { - $newkey = $value->forcexmlarraytype; - $value = array_values($value->array); - $subnode = $node->addChild($key); - foreach ($value as $val) { - if(is_scalar($val)) { - $subnode->addChild($newkey, $val); - } else { - $arraynode = $subnode->addChild($newkey); - $this->convertToXml($arraynode, $val); - } - } - - } else { - if (is_object($value)) { - $value = (array) $value; - } - if (is_numeric($key)) { - $key = "_{$key}"; - } - if (is_array($value)) { - $subnode = $node->addChild($key); - $this->convertToXml($subnode, $value); - } else { - $node->addChild((string) $key, htmlspecialchars($value)); - } - } - } - } -} diff --git a/application/src/Controller/BackOfficeController.php b/application/src/Controller/BackOfficeController.php deleted file mode 100644 index c9846e3..0000000 --- a/application/src/Controller/BackOfficeController.php +++ /dev/null @@ -1,455 +0,0 @@ -setServerID($serverID); - // Deal first with Recording that are part of a breakout room meeting. - // We autogenerate a meeting ID for this meeting. - if (!$request->query->has('meetingID')) { - $meeting->setMeetingId(sha1(rand())); // Random ID. - } else { - $meeting->setMeetingId($request->query->get('meetingID')); - } - $meeting->setAttendeePW($request->query->get('attendeePW')); - $meeting->setModeratorPW($request->query->get('moderatorPW')); - $meeting->setRunning(true); - - if ($request->query->has('name')) { - $meeting->setMeetingName($request->query->get('name')); - } else if ($request->query->has('meetingName')) { - $meeting->setMeetingName($request->query->get('meetingName')); - } - $meeting->setMetadata($this->getMetadataFromRequest($request)); - - if ($request->query->has('maxUsers')) { - $meeting->setMaxUsers($request->query->get('maxUsers')); - } - - if ($request->query->has('voiceBridge')) { - if ($voiceBridge = $request->query->get('voiceBridge')) { - $meeting->setVoiceBridge($voiceBridge); - } - } - - if ($request->query->has('dialNumber')) { - if ($dialNumber = $request->query->get('dialNumber')) { - $meeting->setDialNumber($dialNumber); - } - } - - $entityManager = $this->getDoctrine()->getManager(); - - if ($request->query->has('moderators')) { - $moderators = $request->query->get('moderators'); - if ($moderators) { - $moderatorsList = explode(',', $moderators); - foreach ($moderatorsList as $i => $moderatorID) { - $attendee = new Attendee(); - $attendee->setUserId(trim($moderatorID)); - $attendee->setFullName("Moderator {$i}"); - $attendee->setRole(Attendee::ROLE_MODERATOR); - $attendee->setIsPresenter(true); - - $entityManager->persist($attendee); - $meeting->addAttendee($attendee); - } - } - } - - if ($request->query->has('participants')) { - $participants = $request->query->get('participants'); - if ($participants) { - if (is_numeric($participants)) { - // Create X random participants. - $participantCount = intval($participants); - $maxuserid = $participantCount * 1000; - for($i = 0; $i < $participantCount; $i++) { - $attendee = new Attendee(); - $attendee->setUserId(rand(1, $maxuserid)); - $attendee->setFullName("Participant {$i}"); - $attendee->setServerID($serverID); - $entityManager->persist($attendee); - $meeting->addAttendee($attendee); - } - } else { - $participantsList = json_decode($participants); - foreach ($participantsList as $i => $participantID) { - $attendee = new Attendee(); - $attendee->setUserId(trim($participantID)); - $attendee->setFullName("Participant {$i}"); - $attendee->setServerID($serverID); - - $entityManager->persist($attendee); - $meeting->addAttendee($attendee); - } - } - } - } - - if ($request->query->has('isBreakout') && ((int)$request->query->get('isBreakout'))) { - $meeting->setIsBreakout(true); - if ($request->query->has('parentMeetingID')) { - $parentMeeting = $this->getDoctrine() - ->getRepository(Meeting::class) - ->findOneBy(['meetingID' => $request->query->get('parentMeetingID')]); - $meeting->setParentMeeting($parentMeeting); - } - if ($request->query->has('freeJoin')) { - $meeting->setIsFreeJoin($request->query->get('freeJoin')); - } - if ($request->query->has('breakoutRoomsEnabled')) { - $meeting->setIsBreakoutRoomsEnabled($request->query->get('breakoutRoomsEnabled')); - } - if ($request->query->has('breakoutRoomsPrivateChatEnabled')) { - $meeting->setIsBreakoutRoomsPrivateChatEnabled($request->query->get('breakoutRoomsPrivateChatEnabled')); - } - if ($request->query->has('breakoutRoomsRecord')) { - $meeting->setIsBreakoutRoomsRecord($request->query->get('breakoutRoomsRecord')); - } - if ($request->query->has('sequence')) { - $meeting->setBreakoutSequence($request->query->get('sequence')); - } - } - if ($request->query->has('meta_analytics-callback-url')) { - $meeting->setAnalyticsCallbackURL($request->query->get('meta_analytics-callback-url')); - } - $entityManager->persist($meeting); - $entityManager->flush(); - - return new MeetingSummaryResponse($meeting); - } - - /** - * @Route("/createRecording", name="backOfficeRecordingCreate") - */ - public function backOfficeRecordingCreate(string $serverID, Request $request): XmlResponse - { - $meeting = null; - // Deal first with Recording that are part of a breakout room meeting. - // We find the right meeting through the sequence and parentID. - if (!$request->query->has('meetingID')) { - // This is a recording from a breakout Room. - $sequence = 1; - if ($request->query->has('sequence')) { - $sequence = (int) $request->query->get('sequence'); - } - if (!$request->query->has('parentMeetingID')) { - return $this->handleParentRoomNotFound(''); - } - $parentMeetingID = $request->query->get('parentMeetingID'); - - $parentMeeting = $this->findRoomConfiguration($serverID, $parentMeetingID); - foreach ($parentMeeting->getChildMeetings() as $childMeeting) { - if (!(--$sequence)) { - $meeting = $childMeeting; - break; - } - } - } else { - $meetingID = $request->query->get('meetingID'); - $meeting = $this->findRoomConfiguration($serverID, $meetingID); - } - if (empty($meeting)) { - return $this->handleRoomNotFound($meetingID); - } - - $recording = new Recording(); - $recording->setServerID($serverID); - $meeting->addRecording($recording); - - if ($request->query->has('recordID')) { - $recording->setRecordID($request->query->get('recordID')); - } - - if ($request->query->has('published')) { - $recording->setPublished(!empty($request->query->get('published'))); - } - - if ($request->query->has('protect')) { - $recording->setProtected(!empty($request->query->get('protect'))); - } - - if ($request->query->has('startTime')) { - $recording->setStartTime(new \DateTime("@" . $request->query->get('startTime'))); - } - if ($request->query->has('endTime')) { - $recording->setEndTime(new \DateTime("@" . $request->query->get('endTime'))); - } - - $recording->setMetadata($this->getRecordingMetadataFromRequest($request)); - - $entityManager = $this->getDoctrine()->getManager(); - $entityManager->persist($recording); - $entityManager->persist($meeting); - $entityManager->flush(); - - return new XmlResponse((object) [ - 'recordID' => $recording->getRecordID(), - ]); - } - - /** - * @Route("/recordings", name="backOfficeListRecordings") - */ - public function backOfficeListRecordings(string $serverID): XmlResponse - { - $entities = $this->getDoctrine() - ->getRepository(Recording::class) - ->findBy([ - 'serverID' => $serverID, - ]); - - $items = array_map(function($entity): array { - return $entity->getRecordingInfo(); - }, $entities); - - return new XmlResponse((object) ['recordings' => $items]); - } - - /** - * @Route("/meetings", name="backOfficeListMeetings") - */ - public function backOfficeListMeetings(string $serverID): XmlResponse - { - $meetingEntities = $this->getDoctrine() - ->getRepository(Meeting::class) - ->findBy([ - 'serverID' => $serverID, - ]); - - $meetings = array_map(function($meeting): stdClass { - return $meeting->getMeetingInfo(); - }, $meetingEntities); - - return new XmlResponse((object) ['meetings' => $meetings]); - } - - /** - * The sendRecordingReadyNotification callback. The old route (sendNotifications) is still - * maintained but will be deprecated. - * - * @Route("/sendRecordingReadyNotifications") - * @Route("/sendNotifications") - * @deprecated - */ - public function sendRecordingReadyNotifications(Request $request, string $serverID): XmlResponse - { - $entities = $this->getDoctrine() - ->getRepository(Recording::class) - ->findBy([ - 'serverID' => $serverID, - 'brokerNotified' => false, - ]); - - $client = HttpClient::create(); - $entityManager = $this->getDoctrine()->getManager(); - - // Get the secret to use. - $secret = $request->get('secret', self::DEFAULT_SHARED_SECRET); - - $notified = []; - foreach ($entities as $entity) { - $url = htmlspecialchars_decode($entity->getMetadataValue('bn-recording-ready-url')); - $jwtparams = JWT::encode((object) [ - 'record_id' => $entity->getRecordID(), - ], $secret, 'HS256'); - - $response = $client->request('GET', $url, [ - 'query' => [ - 'signed_parameters' => $jwtparams, - ], - ]); - - $statusCode = $response->getStatusCode(); - if ($statusCode === 200 || $statusCode === 202) { - $notified[] = $entity->getRecordingInfo(); - $entity->setBrokerNotified(true); - $entityManager->persist($entity); - } - } - - $entityManager->flush(); - - return new XmlResponse((object) [ - 'recordings' => [ - 'forcexmlarraytype' => 'recording', - 'array' => $notified, - ], - ]); - } - - /** - * @Route("/addMeetingEvent") - */ - public function addMeetingEvent(Request $request, string $serverID): XmlResponse - { - $entityManager = $this->getDoctrine()->getManager(); - $meetingID = $request->query->get('meetingID'); - $meeting = $this->findRoomConfiguration($serverID, $meetingID); - if (empty($meeting)) { - return $this->handleRoomNotFound($meetingID); - } - $attendeeID = $request->query->get('attendeeID'); - - $attendee = $this->getDoctrine() - ->getRepository(Attendee::class) - ->findOneBy([ - 'userID' => $attendeeID, - ]); - if (empty($attendee)) { - return $this->handleAttendeeNotFound($attendeeID); - } - $eventData = $request->get('eventData', null); - $eventType = $request->get('eventType', Event::TYPE_JOIN); - $event = new Event($attendee, $eventType, $eventData); - $meeting->addEvent($event); - $entityManager->persist($event); - $entityManager->persist($meeting); - $entityManager->flush(); - - return new XmlResponse((object) ['error' => false]); - } - - /** - * @Route("/sendAllEvents") - */ - public function sendAllEvents(Request $request, string $serverID): XmlResponse - { - $entityManager = $this->getDoctrine()->getManager(); - $meetingID = $request->query->get('meetingID'); - $meeting = $this->findRoomConfiguration($serverID, $meetingID); - if (empty($meeting)) { - return $this->handleRoomNotFound($meetingID); - } - $events = $meeting->getEvents(); - $attendees = []; - foreach ($events as $event) { - $attendeeId = $event->getAttendee()->getUserID(); - if (empty($attendees[$attendeeId])) { - $attendees[$attendeeId] = (object) [ - 'ext_user_id' => $attendeeId, - 'engagement' => (object) [ - 'chats' => 0, - "talks" => 0, - "raisehand" => 0, - "emojis" => 0, - "poll_votes" => 0, - "talk_time" => 0, - ], - 'duration' => 0 - ]; - } - - if ($event->getType() === Event::TYPE_ATTENDANCE) { - $data = $event->getData(); - if (!empty($data)) { - $attendees[$attendeeId]->duration += intval($data); // In seconds. - } - } else { - $engagement = $attendees[$attendeeId]->engagement; - $engagement->{$event->getType()} += 1; - $attendees[$attendeeId]->engagement = $engagement; - } - } - $eventsdata = (object) [ - 'meeting_id' => $meeting->getMeetingID(), - 'internal_meeting_id' => $meeting->getId(), - 'data' => (object) [ - 'metadata' => (object) [ - 'is_breakout' => $meeting->isBreakout(), - 'analytics_callback_url' => '', - ], // There is more here but we just output the necessary info. - 'duration' => $meeting->getEndTime() ? - $meeting->getEndTime()->getTimestamp() - $meeting->getStartTime()->getTimestamp() - : time() - $meeting->getStartTime()->getTimestamp(), - 'start' => $meeting->getStartTime(), - 'finish' => $meeting->getEndTime(), - 'attendees' => array_values($attendees), - ], - ]; - if ($request->get('sendQuery', false)) { - // When using PHP Unit there is no point sending the query back. - $secret = $request->get('secret', self::DEFAULT_SHARED_SECRET); - $client = HttpClient::create(); - $url = htmlspecialchars_decode($meeting->getAnalyticsCallbackURL()); - $token = JWT::encode( - ['exp' => time() + 24 * 3600], - $secret, - 'HS512' - ); - $response = $client->request('POST', $url, [ - 'headers' => [ - 'Authorization' => "Bearer " . $token, - 'Content-Type' => 'application/json', - 'User-Agent' => 'BigBlueButton Analytics Callback' - ], - 'body' => json_encode($eventsdata) - ]); - $statusCode = $response->getStatusCode(); - if ($statusCode === 200 || $statusCode === 202) { - return new XmlResponse((object) ['error' => false]); - } - - return new XmlResponse((object) ['error' => true]); - } - - return new XmlResponse((object) ['error' => false, 'data' => $eventsdata]); - } - - /** - * @Route("/reset", name="backOfficeReset") - */ - public function backOfficeReset(string $serverID): XmlResponse - { - $entities = [ - Attendee::class, - Recording::class, - Meeting::class, - ]; - - $entityManager = $this->getDoctrine()->getManager(); - foreach ($entities as $entityClass) { - $entities = $this->getDoctrine() - ->getRepository($entityClass) - ->findBy(['serverID' => $serverID]); - foreach ($entities as $entity) { - $entityManager->remove($entity); - $entityManager->flush(); - } - } - - return new XmlResponse((object) ['reset' => true]); - } -} diff --git a/application/src/Controller/MatrixController.php b/application/src/Controller/MatrixController.php index 30a2922..d737348 100644 --- a/application/src/Controller/MatrixController.php +++ b/application/src/Controller/MatrixController.php @@ -2,6 +2,7 @@ namespace App\Controller; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\HttpFoundation\Request; @@ -13,7 +14,7 @@ * * @Route("/{serverID}/_matrix/client/r0") */ -class MatrixController extends DataController { +class MatrixController extends AbstractController { /** * @Route("", name="endpoint") diff --git a/application/src/Controller/MediaController.php b/application/src/Controller/MediaController.php index 183d164..81eb869 100644 --- a/application/src/Controller/MediaController.php +++ b/application/src/Controller/MediaController.php @@ -2,6 +2,7 @@ namespace App\Controller; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\HttpFoundation\Request; @@ -13,7 +14,7 @@ * * @Route("/{serverID}/_matrix/media/r0") */ -class MediaController extends DataController { +class MediaController extends AbstractController { /** * @Route("", name="endpoint") diff --git a/application/src/Controller/SynapseController.php b/application/src/Controller/SynapseController.php index 0076090..1897e63 100644 --- a/application/src/Controller/SynapseController.php +++ b/application/src/Controller/SynapseController.php @@ -2,6 +2,7 @@ namespace App\Controller; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use App\Entity\Externalids; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Annotation\Route; @@ -15,7 +16,7 @@ * * @Route("/{serverID}/_synapse/admin/v2") */ -class SynapseController extends DataController { +class SynapseController extends AbstractController { /** * @Route("", name="endpoint") diff --git a/application/src/Entity/Attendee.php b/application/src/Entity/Attendee.php deleted file mode 100644 index fafc5fe..0000000 --- a/application/src/Entity/Attendee.php +++ /dev/null @@ -1,238 +0,0 @@ -id; - } - - public function getUserID(): ?string - { - return $this->userID; - } - - public function setUserID(string $userID): self - { - $this->userID = $userID; - - return $this; - } - - public function getRole(): ?string - { - return $this->role; - } - - public function setRole(string $role): self - { - $this->role = $role; - - return $this; - } - - public function isModerator(): bool - { - return $this->role === self::ROLE_MODERATOR; - } - - public function isParticipant(): bool - { - return $this->role === self::ROLE_VIEWER; - } - - public function getFullName(): ?string - { - return $this->fullName; - } - - public function setFullName(string $fullName): self - { - $this->fullName = $fullName; - - return $this; - } - - public function getIsListeningOnly(): ?bool - { - return $this->isListeningOnly; - } - - public function setIsListeningOnly(bool $isListeningOnly): self - { - $this->isListeningOnly = $isListeningOnly; - - return $this; - } - - public function getHasJoinedVoice(): ?bool - { - return $this->hasJoinedVoice; - } - - public function hasJoinedVoice(): bool - { - return $this->hasJoinedVoice; - } - - - public function setHasJoinedVoice(bool $hasJoinedVoice): self - { - $this->hasJoinedVoice = $hasJoinedVoice; - - return $this; - } - - public function getHasVideo(): ?bool - { - return $this->hasVideo; - } - - public function hasVideo(): bool - { - return $this->hasVideo; - } - - public function setHasVideo(bool $hasVideo): self - { - $this->hasVideo = $hasVideo; - - return $this; - } - - public function getClientType(): ?string - { - return $this->clientType; - } - - public function setClientType(string $clientType): self - { - $this->clientType = $clientType; - - return $this; - } - - public function getCustomData(): ?array - { - return $this->customData; - } - - public function setCustomData(array $customData): self - { - $this->customData = $customData; - - return $this; - } - - public function getIsPresenter(): ?bool - { - return $this->isPresenter; - } - - public function setIsPresenter(bool $isPresenter): self - { - $this->isPresenter = $isPresenter; - - return $this; - } - - public function getMeeting(): ?Meeting - { - return $this->meeting; - } - - public function setMeeting(?Meeting $meeting): self - { - $this->meeting = $meeting; - - return $this; - } - - public function getServerID(): ?string - { - return $this->serverID; - } - - public function setServerID(string $serverID): self - { - $this->serverID = $serverID; - - return $this; - } -} diff --git a/application/src/Entity/Event.php b/application/src/Entity/Event.php deleted file mode 100644 index f2bb33f..0000000 --- a/application/src/Entity/Event.php +++ /dev/null @@ -1,120 +0,0 @@ -createTime = new \DateTime(); - $this->attendee = $attendee; - $this->type = $type; - $this->data = $data; - } - - public function getId(): ?int - { - return $this->id; - } - - public function getAttendee(): Attendee - { - return $this->attendee; - } - - public function getMeeting(): ?Meeting - { - return $this->meeting; - } - - public function setMeeting(?Meeting $meeting): self - { - $this->meeting = $meeting; - $this->serverID = $meeting->getServerID(); - - return $this; - } - - public function getServerID(): ?string - { - return $this->serverID; - } - - public function setServerID(string $serverID): self - { - $this->serverID = $serverID; - - return $this; - } - - public function getType(): string - { - return $this->type; - } - - public function getData(): string - { - return $this->data; - } -} diff --git a/application/src/Entity/Meeting.php b/application/src/Entity/Meeting.php deleted file mode 100644 index 1f066f3..0000000 --- a/application/src/Entity/Meeting.php +++ /dev/null @@ -1,751 +0,0 @@ - '', - 'bbb-context-id' => '', - 'bbb-context-label' => '', - 'bbb-context-name' => '', - 'bbb-origin' => 'Moodle', - 'bbb-origin-server-common-name' => 'HOSTNAME', - 'bbb-origin-server-name' => 'BBB Moodle', - 'bbb-origin-tag' => "moodle-mod_bigbluebuttonbn (PLUGINVERSION)", - 'bbb-origin-version' => "RELEASE", - ]; - - /** - * @ORM\Column(type="datetime") - */ - private $createTime; - - /** - * @ORM\Column(type="datetime") - */ - private $startTime; - - /** - * @ORM\Column(type="datetime", nullable=true) - */ - private $endTime; - - /** - * @ORM\Column(type="boolean") - */ - private $hasUserJoined = false; - - /** - * @ORM\Column(type="boolean") - */ - private $hasBeenForciblyEnded = false; - - /** - * @ORM\Column(type="boolean") - */ - private $recording = true; - - /** - * @ORM\Column(type="integer") - */ - private $maxUsers = 0; - - /** - * @ORM\Column(type="boolean") - */ - private $running = false; - - /** - * @ORM\OneToMany(targetEntity=Attendee::class, mappedBy="meeting", orphanRemoval=true, fetch="EAGER") - */ - private $attendees; - - /** - * @ORM\OneToMany(targetEntity=Event::class, mappedBy="meeting", orphanRemoval=true, fetch="EAGER") - */ - private $events; - - /** - * @ORM\OneToMany(targetEntity=Recording::class, mappedBy="meeting", orphanRemoval=true) - */ - private $recordings; - - /** - * @ORM\Column(type="string", length=255) - */ - private $serverID; - - - /** - * @ORM\Column(type="boolean") - */ - private $isBreakout = false; - - /** - * @ORM\Column(type="boolean") - */ - private $freeJoin = false; - - /** - * @ORM\Column(type="boolean") - */ - private $breakoutRoomsEnabled = true; - - /** - * @ORM\Column(type="boolean") - */ - private $breakoutRoomsPrivateChatEnabled = true; - - /** - * @ORM\Column(type="boolean") - */ - private $breakoutRoomsRecord = true; - - /** - * @ORM\Column(type="integer") - */ - private $sequence = 0; - - /** - * @ORM\ManyToOne(targetEntity=Meeting::class, inversedBy="childMeetings") - * @ORM\JoinColumn(nullable=true) - */ - private $parentMeeting = null; - - /** - * @ORM\OneToMany(targetEntity=Meeting::class, mappedBy="parentMeeting", orphanRemoval=true, fetch="EAGER") - */ - private $childMeetings; - - /** - * @ORM\Column(type="string", nullable=true) - */ - private $analyticsCallbackURL = null; - - public function __construct() - { - $this->createTime = new \DateTime(); - $this->startTime = $this->createTime; - $this->attendees = new ArrayCollection(); - $this->recordings = new ArrayCollection(); - } - - public function getMeetingInfo(): stdClass { - // Filter out attendee's object as it does not translate well in XML as such. - $attendees = $this->getAttendees()->toArray(); - if ($attendees) { - foreach ($attendees as $key => $val) { - $attendee = (array) $val; - $attendee = - array_intersect_key(['userID', 'fullName', 'role', 'isPresenter', 'isListeningOnly', 'hasVideo', 'clientType'], - $attendee); - $attendees[$key] = (object) $attendee; - } - } - $meetingInfo = (object) [ - 'meetingName' => $this->meetingName, - 'meetingID' => $this->meetingID, - //'internalMeetingID' => $this->internalMeetingID, - //'parentMeetingID' => $this->parentMeetingID, - 'createTime' => $this->createTime->format('U'), - 'createDate' => $this->createTime->format('D M d H:i:s e Y'), - 'voiceBridge' => sprintf("%04d", $this->voiceBridge), - 'dialNumber' => sprintf("%04d", $this->dialNumber), - 'attendeePW' => $this->attendeePW, - 'moderatorPW' => $this->moderatorPW, - 'running' => $this->stringifyBool($this->running), - 'hasUserJoined' => $this->stringifyBool($this->hasUserJoined), - 'recording' => $this->stringifyBool($this->recording), - 'hasBeenForciblyEnded' => $this->stringifyBool($this->hasBeenForciblyEnded), - 'startTime' => $this->startTime->format('U'), - 'endTime' => $this->endTime ? $this->endTime->format('U') : 0, - 'participantCount' => $this->getParticipantCount(), - 'listenerCount' => $this->getListenerCount(), - 'videoCount' => $this->getVideoCount(), - 'moderatorCount' => $this->getModeratorCount(), - 'attendees' => $attendees, - 'metadata' => $this->metadata, - 'duration' => (new \DateTime())->diff($this->createTime)->s, - 'isBreakout' => $this->isBreakout() - ]; - if ($this->hasSubMeetings()) { - $breakoutRooms = []; - foreach($this->getChildMeetings() as $childMeeting) { - $breakoutRooms[] = $childMeeting->getMeetingID(); - } - $meetingInfo->breakoutRooms = (object) [ - 'forcexmlarraytype' => 'breakout', - 'array' => $breakoutRooms, - ]; - } - if ($this->isBreakout()) { - $meetingInfo->breakout = (object) [ - 'parentMeetingID' =>$this->getParentMeeting()->getMeetingID(), - 'sequence'=> $this->getBreakoutSequence(), - 'freeJoin' => $this->isFreeJoin() - ]; - } - return $meetingInfo; - } - - public function getMeetingSummary(): stdClass { - return (object) [ - 'meetingID' => $this->meetingID, - 'attendeePW' => $this->attendeePW, - 'moderatorPW' => $this->moderatorPW, - 'createDate' => $this->createTime->format('D M d H:i:s e Y'), - 'createTime' => $this->createTime->format('U'), - 'dialNumber' => sprintf("%04d", $this->dialNumber), - 'voiceBridge' => sprintf("%04d", $this->voiceBridge), - 'hasBeenForciblyEnded' => $this->stringifyBool($this->hasBeenForciblyEnded), - 'hasUserJoined' => $this->stringifyBool($this->hasUserJoined), - ]; - } - - protected function stringifyBool(bool $value): string - { - return $value ? 'true' : 'false'; - } - - public function getId(): ?int - { - return $this->id; - } - - public function getMeetingID(): ?string - { - return $this->meetingID; - } - - public function setMeetingID(string $meetingID): self - { - $this->meetingID = $meetingID; - - return $this; - } - - public function getAttendeePW(): ?string - { - return $this->attendeePW; - } - - public function setAttendeePW(string $attendeePW): self - { - $this->attendeePW = $attendeePW; - - return $this; - } - - public function checkAttendeePW(string $attendeePW): bool - { - return $attendeePW === $this->attendeePW; - } - - public function getModeratorPW(): ?string - { - return $this->moderatorPW; - } - - public function setModeratorPW(string $moderatorPW): self - { - $this->moderatorPW = $moderatorPW; - - return $this; - } - - public function checkModeratorPW(string $moderatorPW): bool - { - return $moderatorPW === $this->moderatorPW; - } - - public function getVoiceBridge(): ?int - { - return $this->voiceBridge; - } - - public function setVoiceBridge(int $voiceBridge): self - { - $this->voiceBridge = $voiceBridge; - - return $this; - } - - public function getDialNumber(): ?int - { - return $this->dialNumber; - } - - public function setDialNumber(int $dialNumber): self - { - $this->dialNumber = $dialNumber; - - return $this; - } - - public function getMeetingName(): ?string - { - return $this->meetingName; - } - - public function setMeetingName(string $meetingName): self - { - $this->meetingName = $meetingName; - - return $this; - } - - public function getMetadata(): ?array - { - return $this->metadata; - } - - public function setMetadata(array $metadata): self - { - $this->metadata = $metadata; - - return $this; - } - - public function getCreateTime(): ?\DateTimeInterface - { - return $this->createTime; - } - - public function setCreateTime(\DateTimeInterface $createTime): self - { - $this->createTime = $createTime; - - return $this; - } - - public function getStartTime(): ?\DateTimeInterface - { - return $this->startTime; - } - - public function setStartTime(\DateTimeInterface $startTime): self - { - $this->startTime = $startTime; - - return $this; - } - - public function getEndTime(): ?\DateTimeInterface - { - return $this->endTime; - } - - public function setEndTime(?\DateTimeInterface $endTime): self - { - $this->endTime = $endTime; - - return $this; - } - - public function getHasUserJoined(): ?bool - { - return $this->hasUserJoined; - } - - public function setHasUserJoined(bool $hasUserJoined): self - { - $this->hasUserJoined = $hasUserJoined; - - return $this; - } - - public function getHasBeenForciblyEnded(): ?bool - { - return $this->hasBeenForciblyEnded; - } - - public function setHasBeenForciblyEnded(bool $hasBeenForciblyEnded): self - { - $this->hasBeenForciblyEnded = $hasBeenForciblyEnded; - - return $this; - } - - public function getRecording(): ?bool - { - return $this->recording; - } - - public function setRecording(bool $recording): self - { - $this->recording = $recording; - - return $this; - } - - public function getMaxUsers(): ?int - { - return $this->maxUsers; - } - - public function setMaxUsers(int $maxUsers): self - { - $this->maxUsers = $maxUsers; - - return $this; - } - - public function getRunning(): ?bool - { - return $this->running; - } - - public function setRunning(bool $running): self - { - $this->running = $running; - - return $this; - } - - /** - * @return Collection|Attendee[] - */ - public function getAttendees(): Collection - { - return $this->attendees; - } - - - /** - * @return Collection|Event[] - */ - public function getEvents(): Collection - { - return $this->events; - } - - public function addAttendee(Attendee $attendee): self - { - if (!$this->attendees->contains($attendee)) { - $this->attendees[] = $attendee; - $attendee->setMeeting($this); - } - - return $this; - } - - public function addEvent(Event $event): self - { - if (!$this->events->contains($event)) { - $this->events[] = $event; - $event->setMeeting($this); - } - - return $this; - } - - public function removeAttendee(Attendee $attendee): self - { - if ($this->attendees->removeElement($attendee)) { - // set the owning side to null (unless already changed) - if ($attendee->getMeeting() === $this) { - $attendee->setMeeting(null); - } - } - - return $this; - } - - public function getParticipantCount(): int - { - return count($this->getAttendees()); - } - - public function getModeratorCount(): int - { - return count($this->getAttendees()->filter(function($attendee): bool - { - return $attendee->isModerator(); - })); - } - - public function getVideoCount(): int - { - return count($this->getAttendees()->filter(function($attendee): bool - { - return $attendee->hasVideo(); - })); - } - - public function getListenerCount(): int - { - return count($this->getAttendees()->filter(function($attendee): bool - { - return $attendee->hasJoinedVoice(); - })); - } - - /** - * @return Collection|Recording[] - */ - public function getRecordings(): Collection - { - return $this->recordings; - } - - public function addRecording(Recording $recording): self - { - if (!$this->recordings->contains($recording)) { - $this->recordings[] = $recording; - } - $recording->setMeeting($this); - return $this; - } - - public function removeRecording(Recording $recording): self - { - if ($this->recordings->removeElement($recording)) { - // set the owning side to null (unless already changed) - if ($recording->getMeeting() === $this) { - $recording->setMeeting(null); - } - } - - return $this; - } - - public function getServerID(): ?string - { - return $this->serverID; - } - - public function setServerID(string $serverID): self - { - $this->serverID = $serverID; - return $this; - } - - public function setLockSetting(string $lockName, bool $value) { - if (property_exists($this, $lockName)) { - $this->$lockName = $value; - } - } - - public function getLockSetting(string $lockName): bool { - if (property_exists($this, $lockName)) { - // If lockOnJoin not set to true, this is false for every lock. - return $this->lockOnJoin && $this->$lockName; - } - return false; - } - - public function isBreakout(): ?bool - { - return $this->isBreakout; - } - - public function setIsBreakout(bool $val): self - { - $this->isBreakout = $val; - return $this; - } - - public function isFreeJoin(): ?bool - { - return $this->freeJoin; - } - - public function setIsFreeJoin(bool $val): self - { - $this->freeJoin = $val; - return $this; - } - - public function isBreakoutRoomsEnabled(): ?bool - { - return $this->breakoutRoomsEnabled; - } - - public function setIsBreakoutRoomsEnabled(bool $val): self - { - $this->breakoutRoomsEnabled = $val; - return $this; - } - - public function isBreakoutRoomsPrivateChatEnabled(): ?bool - { - return $this->breakoutRoomsPrivateChatEnabled; - } - - public function setIsBreakoutRoomsPrivateChatEnabled(bool $val): self - { - $this->breakoutRoomsPrivateChatEnabled = $val; - return $this; - } - - public function isBreakoutRoomsRecord(): ?bool - { - return $this->breakoutRoomsRecord; - } - - public function setIsBreakoutRoomsRecord(bool $val): self - { - $this->breakoutRoomsRecord = $val; - return $this; - } - - public function getBreakoutSequence(): ?int - { - return $this->sequence; - } - - public function setBreakoutSequence(int $val): self - { - $this->sequence = $val; - return $this; - } - - public function addChildMeeting(Meeting $childMeeting): self - { - if (!$this->childMeetings->contains($childMeeting)) { - $this->childMeetings[] = $childMeeting; - } - $childMeeting->parentMeeting = $this; - return $this; - } - - public function removeChildMeeting(Meeting $childMeeting): self - { - if ($this->childMeetings->removeElement($childMeeting)) { - // set the owning side to null (unless already changed) - if ($childMeeting->getParentMeeting() === $this) { - $childMeeting->setParentMeeting(null); - } - } - return $this; - } - - /** - * @return Collection|Meeting[] - */ - public function getChildMeetings(): Collection - { - return $this->childMeetings; - } - - public function setParentMeeting(?Meeting $parentMeeting): self { - if ($parentMeeting) { - $parentMeeting->addChildMeeting($this); - } else { - $this->parentMeeting = null; - } - return $this; - } - public function getParentMeeting(): ?Meeting { - return $this->parentMeeting ; - } - - public function hasSubMeetings(): ?bool - { - return !empty($this->childMeetings); - } - - public function setAnalyticsCallbackURL(?string $url) { - $this->analyticsCallbackURL = $url; - } - - public function getAnalyticsCallbackURL(): string { - return $this->analyticsCallbackURL; - } - -} diff --git a/application/src/Entity/Recording.php b/application/src/Entity/Recording.php deleted file mode 100644 index 1c39095..0000000 --- a/application/src/Entity/Recording.php +++ /dev/null @@ -1,385 +0,0 @@ -startTime = new \DateTime(); - } - - public function getRecordingInfo(): array - { - $recordingInfo = [ - 'meetingID' => $this->getMeeting()->getMeetingID(), - 'recordID' => $this->getRecordID(), - 'published' => $this->stringifyBool($this->published), - 'protected' => $this->stringifyBool($this->protected), - 'startTime' => $this->startTime->format('Uu') / 1000, - 'endTime' => $this->endTime->format('Uu') / 1000, - 'participants' => $this->participants, - 'playback' => $this->getPlayback(), - 'metadata' => $this->getMetadata(), - 'isBreakout' => $this->isBreakout() - ]; - if ($this->getMeeting()->hasSubMeetings()) { - $breakoutRooms = []; - foreach($this->getMeeting()->getChildMeetings() as $childMeeting) { - foreach($childMeeting->getRecordings() as $childRecording) { - $breakoutRooms[] = $childRecording->getRecordID(); - } - } - $recordingInfo['breakoutRooms'] = (object) [ - 'forcexmlarraytype' => 'breakoutRoom', - 'array' => $breakoutRooms, - ]; - } - return $recordingInfo; - } - - public function getId(): ?int - { - return $this->id; - } - - public function getMeeting(): ?Meeting - { - return $this->meeting; - } - - public function setMeeting(?Meeting $meeting): self - { - $this->meeting = $meeting; - - return $this; - } - - public function getRecordID(): ?string - { - return $this->recordID; - } - - public function setRecordID(string $recordID): self - { - $this->recordID = $recordID; - - return $this; - } - - /** - * @ORM\PrePersist - */ - public function setRecordIDFromMeeting(): void - { - $seed = $this->getMeeting()->getMeetingID(); - if ($this->getMeeting()->isBreakout()) { - $seed .= $this->getMeeting()->getBreakoutSequence(); - } - $this->recordID = sprintf( - "%s-%s", - md5($seed), - time() + random_int(0, PHP_INT_MAX/2) - ); - } - - public function getPublished(): ?bool - { - return $this->published; - } - - public function setPublished(bool $published): self - { - $this->published = $published; - - return $this; - } - - public function getProtected(): ?bool - { - return $this->protected; - } - - public function setProtected(bool $protected): self - { - $this->protected = $protected; - - return $this; - } - - public function getStartTime(): ?\DateTimeInterface - { - return $this->startTime; - } - - public function setStartTime(\DateTimeInterface $startTime): self - { - $this->startTime = $startTime; - - return $this; - } - - /** - * @ORM\PrePersist - */ - public function setPrePersistStartTime(): void - { - if (empty($this->startTime)) { - $this->startTime = new \DateTime(); - - $interval = 3600 * rand(1, 15) * 24; - $this->startTime->subtract(new \DateInterval("PT{$interval}S")); - } - } - - public function getEndTime(): ?\DateTimeInterface - { - return $this->endTime; - } - - public function setEndTime(?\DateTimeInterface $endTime): self - { - $this->endTime = $endTime; - - return $this; - } - - /** - * @ORM\PrePersist - */ - public function setPrePersistEndTime(): void - { - if (empty($this->endTime)) { - $this->endTime = clone $this->startTime; - - $interval = 3600 * rand(1, 15) * 24; - $this->endTime->add(new \DateInterval("PT{$interval}S")); - } - } - - public function getParticipants(): ?int - { - return $this->participants; - } - - public function setParticipants(int $participants): self - { - $this->participants = $participants; - - return $this; - } - - public function getMetadata(): ?array - { - $metadata = array_merge($this->metadata, []); - $metadata['isBreakout'] = $this->stringifyBool(!empty($metadata['isBreakout'])); - - return $metadata; - } - - public function getMetadataValue(string $key) - { - $metadata = $this->getMetadata(); - if (array_key_exists($key, $metadata)) { - return $metadata[$key]; - } - - return null; - } - - public function setMetadata(array $metadata): self - { - $this->metadata = $metadata; - - return $this; - } - - public function getPlayback(): ?array - { - return $this->playback; - } - - /** - * @ORM\PrePersist - */ - public function calculatePlayback(): void - { - if (empty($this->playback)) { - error_log(var_export($this->getEndTime()->diff($this->getStartTime()), true)); - $this->playback = [ - 'format' => [ - 'type' => 'presentation', - 'url' => '', - 'length' => $this->getEndTime()->getTimestamp() - $this->getStartTime()->getTimestamp(), - ], - ]; - } - } - - public function setPlayback(array $playback): self - { - $this->playback = $playback; - - return $this; - } - - public function isBreakout(): ?bool - { - return $this->getMeeting()->isBreakout(); - } - - public function isHeadless(): ?bool - { - return $this->headless; - } - - public function setHeadless(bool $headless): self - { - $this->headless = $headless; - - return $this; - } - - public function isImported(): ?bool - { - return $this->imported; - } - - public function setImported(bool $imported): self - { - $this->imported = $imported; - - return $this; - } - - public function getRecording(): ?array - { - return $this->recording; - } - - public function setRecording(array $recording): self - { - $this->recording = $recording; - - return $this; - } - - protected function stringifyBool(bool $value): string - { - return $value ? 'true' : 'false'; - } - - public function getBrokerNotified(): ?bool - { - return $this->brokerNotified; - } - - public function setBrokerNotified(bool $brokerNotified): self - { - $this->brokerNotified = $brokerNotified; - - return $this; - } - - public function getServerID(): ?string - { - return $this->serverID; - } - - public function setServerID(string $serverID): self - { - $this->serverID = $serverID; - - return $this; - } -} diff --git a/application/src/Repository/AttendeeRepository.php b/application/src/Repository/AttendeeRepository.php deleted file mode 100644 index 5f00075..0000000 --- a/application/src/Repository/AttendeeRepository.php +++ /dev/null @@ -1,50 +0,0 @@ -createQueryBuilder('a') - ->andWhere('a.exampleField = :val') - ->setParameter('val', $value) - ->orderBy('a.id', 'ASC') - ->setMaxResults(10) - ->getQuery() - ->getResult() - ; - } - */ - - /* - public function findOneBySomeField($value): ?Attendee - { - return $this->createQueryBuilder('a') - ->andWhere('a.exampleField = :val') - ->setParameter('val', $value) - ->getQuery() - ->getOneOrNullResult() - ; - } - */ -} diff --git a/application/src/Repository/EventRepository.php b/application/src/Repository/EventRepository.php deleted file mode 100644 index 476d782..0000000 --- a/application/src/Repository/EventRepository.php +++ /dev/null @@ -1,21 +0,0 @@ -createQueryBuilder('m') - ->andWhere('m.exampleField = :val') - ->setParameter('val', $value) - ->orderBy('m.id', 'ASC') - ->setMaxResults(10) - ->getQuery() - ->getResult() - ; - } - */ - - /* - public function findOneBySomeField($value): ?Meeting - { - return $this->createQueryBuilder('m') - ->andWhere('m.exampleField = :val') - ->setParameter('val', $value) - ->getQuery() - ->getOneOrNullResult() - ; - } - */ -} diff --git a/application/src/Repository/RecordingRepository.php b/application/src/Repository/RecordingRepository.php deleted file mode 100644 index 1af68a5..0000000 --- a/application/src/Repository/RecordingRepository.php +++ /dev/null @@ -1,50 +0,0 @@ -createQueryBuilder('r') - ->andWhere('r.exampleField = :val') - ->setParameter('val', $value) - ->orderBy('r.id', 'ASC') - ->setMaxResults(10) - ->getQuery() - ->getResult() - ; - } - */ - - /* - public function findOneBySomeField($value): ?Recording - { - return $this->createQueryBuilder('r') - ->andWhere('r.exampleField = :val') - ->setParameter('val', $value) - ->getQuery() - ->getOneOrNullResult() - ; - } - */ -}