summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoas Schilling <213943+nickvergessen@users.noreply.github.com>2023-01-24 16:35:00 +0100
committerGitHub <noreply@github.com>2023-01-24 16:35:00 +0100
commitbfee410ec653a1022e78fc8ce660299ba4529dcf (patch)
tree550b32a53c826cd0182908244de3630b98056eb3
parentbc863268b420341c0930f65f2c32f91e197ecf8a (diff)
parentcca8617cd70298e205aa3a4f68dc174d73009f15 (diff)
Merge pull request #8550 from nextcloud/feature/notify-recording-stored
Notify recording stored
-rw-r--r--appinfo/routes/routesRecordingController.php4
-rw-r--r--docs/recording.md37
-rw-r--r--lib/Chat/SystemMessage/Listener.php4
-rw-r--r--lib/Controller/RecordingController.php35
-rw-r--r--lib/Notification/Notifier.php70
-rw-r--r--lib/Service/RecordingService.php115
-rw-r--r--tests/integration/features/callapi/recording.feature3
-rw-r--r--tests/php/Notification/NotifierTest.php9
-rw-r--r--tests/php/Service/RecordingServiceTest.php32
9 files changed, 301 insertions, 8 deletions
diff --git a/appinfo/routes/routesRecordingController.php b/appinfo/routes/routesRecordingController.php
index 4e9dc7d4a..ef1facc12 100644
--- a/appinfo/routes/routesRecordingController.php
+++ b/appinfo/routes/routesRecordingController.php
@@ -36,5 +36,9 @@ return [
['name' => 'Recording#stop', 'url' => '/api/{apiVersion}/recording/{token}', 'verb' => 'DELETE', 'requirements' => $requirements],
/** @see \OCA\Talk\Controller\RecordingController::store() */
['name' => 'Recording#store', 'url' => '/api/{apiVersion}/recording/{token}/store', 'verb' => 'POST', 'requirements' => $requirements],
+ /** @see \OCA\Talk\Controller\RecordingController::notificationDismiss() */
+ ['name' => 'Recording#notificationDismiss', 'url' => '/api/{apiVersion}/recording/{token}/notification', 'verb' => 'DELETE', 'requirements' => $requirements],
+ /** @see \OCA\Talk\Controller\RecordingController::shareToChat() */
+ ['name' => 'Recording#shareToChat', 'url' => '/api/{apiVersion}/recording/{token}/share-chat', 'verb' => 'POST', 'requirements' => $requirements],
],
];
diff --git a/docs/recording.md b/docs/recording.md
index a73feb8a8..b0cb04e56 100644
--- a/docs/recording.md
+++ b/docs/recording.md
@@ -71,3 +71,40 @@
+ `401 Unauthorized` When the validation as SIP bridge failed
+ `404 Not Found` Room not found
+ `429 Too Many Request` Brute force protection
+
+## Dismiss store call recording notification
+
+* Required capability: `recording-v1`
+* Method: `DELETE`
+* Endpoint: `/recording/{token}/notification`
+* Data:
+
+| field | type | Description |
+| ----------- | ------ | --------------------------------------------------------------------- |
+| `timestamp` | string | Timestamp in seconds and UTC time zone that notification was created. |
+
+* Response:
+ - Status code:
+ + `200 OK`
+ + `403 Forbidden` When the user is not a moderator/owner.
+ + `404 Not Found` Room not found
+
+## Share store call recording
+
+* Required capability: `recording-v1`
+* Method: `POST`
+* Endpoint: `/recording/{token}/share-chat`
+* Data:
+
+| field | type | Description |
+| ----------- | ------- | --------------------------------------------------------------------- |
+| `timestamp` | string | Timestamp in seconds and UTC time zone that notification was created. |
+| `fileId` | integer | File id of recording to share at the room. |
+
+* Response:
+ - Status code:
+ + `200 OK`
+ + `400 Bad Request` Error: `file`: Shared file is invalid
+ + `400 Bad Request` Error: `system`: Internal system error
+ + `403 Forbidden` When the user is not a moderator/owne
+ + `404 Not Found` Room not found
diff --git a/lib/Chat/SystemMessage/Listener.php b/lib/Chat/SystemMessage/Listener.php
index fecc384eb..9e33a8aca 100644
--- a/lib/Chat/SystemMessage/Listener.php
+++ b/lib/Chat/SystemMessage/Listener.php
@@ -382,6 +382,10 @@ class Listener implements IEventListener {
$listener = Server::get(self::class);
$manager = Server::get(Manager::class);
+ $request = Server::get(IRequest::class);
+ if ($request->getParam('_route') === 'ocs.spreed.Recording.shareToChat') {
+ return;
+ }
$room = $manager->getRoomByToken($share->getSharedWith());
$metaData = Server::get(IRequest::class)->getParam('talkMetaData') ?? '';
$metaData = json_decode($metaData, true);
diff --git a/lib/Controller/RecordingController.php b/lib/Controller/RecordingController.php
index 0948fe538..d5ed53fa0 100644
--- a/lib/Controller/RecordingController.php
+++ b/lib/Controller/RecordingController.php
@@ -100,4 +100,39 @@ class RecordingController extends AEnvironmentAwareController {
}
return new DataResponse();
}
+
+ /**
+ * @NoAdminRequired
+ * @RequireModeratorParticipant
+ */
+ public function notificationDismiss(int $timestamp): DataResponse {
+ try {
+ $this->recordingService->notificationDismiss(
+ $this->getRoom(),
+ $this->participant,
+ $timestamp
+ );
+ } catch (InvalidArgumentException $e) {
+ return new DataResponse(['error' => $e->getMessage()], Http::STATUS_BAD_REQUEST);
+ }
+ return new DataResponse();
+ }
+
+ /**
+ * @NoAdminRequired
+ * @RequireModeratorParticipant
+ */
+ public function shareToChat(int $fileId, int $timestamp): DataResponse {
+ try {
+ $this->recordingService->shareToChat(
+ $this->getRoom(),
+ $this->participant,
+ $fileId,
+ $timestamp
+ );
+ } catch (InvalidArgumentException $e) {
+ return new DataResponse(['error' => $e->getMessage()], Http::STATUS_BAD_REQUEST);
+ }
+ return new DataResponse();
+ }
}
diff --git a/lib/Notification/Notifier.php b/lib/Notification/Notifier.php
index 46c6b7b77..b9e65147e 100644
--- a/lib/Notification/Notifier.php
+++ b/lib/Notification/Notifier.php
@@ -41,6 +41,7 @@ use OCA\Talk\Webinary;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Comments\ICommentsManager;
use OCP\Comments\NotFoundException;
+use OCP\Files\IRootFolder;
use OCP\HintException;
use OCP\IL10N;
use OCP\IURLGenerator;
@@ -69,6 +70,8 @@ class Notifier implements INotifier {
protected INotificationManager $notificationManager;
protected ICommentsManager $commentManager;
protected MessageParser $messageParser;
+ protected IURLGenerator $urlGenerator;
+ protected IRootFolder $rootFolder;
protected ITimeFactory $timeFactory;
protected Definitions $definitions;
protected AddressHandler $addressHandler;
@@ -89,6 +92,8 @@ class Notifier implements INotifier {
INotificationManager $notificationManager,
CommentsManager $commentManager,
MessageParser $messageParser,
+ IURLGenerator $urlGenerator,
+ IRootFolder $rootFolder,
ITimeFactory $timeFactory,
Definitions $definitions,
AddressHandler $addressHandler) {
@@ -103,6 +108,8 @@ class Notifier implements INotifier {
$this->notificationManager = $notificationManager;
$this->commentManager = $commentManager;
$this->messageParser = $messageParser;
+ $this->urlGenerator = $urlGenerator;
+ $this->rootFolder = $rootFolder;
$this->timeFactory = $timeFactory;
$this->definitions = $definitions;
$this->addressHandler = $addressHandler;
@@ -248,6 +255,9 @@ class Notifier implements INotifier {
->setLink($this->url->linkToRouteAbsolute('spreed.Page.showCall', ['token' => $room->getToken()]));
$subject = $notification->getSubject();
+ if ($subject === 'record_file_stored') {
+ return $this->parseStoredRecording($notification, $room, $participant, $l);
+ }
if ($subject === 'invitation') {
return $this->parseInvitation($notification, $room, $l);
}
@@ -287,6 +297,66 @@ class Notifier implements INotifier {
return $temp;
}
+ private function parseStoredRecording(
+ INotification $notification,
+ Room $room,
+ Participant $participant,
+ IL10N $l
+ ): INotification {
+ $parameters = $notification->getSubjectParameters();
+ try {
+ $userFolder = $this->rootFolder->getUserFolder($notification->getUser());
+ /** @var \OCP\Files\File[] */
+ $files = $userFolder->getById($parameters['objectId']);
+ $file = array_shift($files);
+ } catch (\Throwable $th) {
+ throw new AlreadyProcessedException();
+ }
+ $shareAction = $notification->createAction()
+ ->setParsedLabel($l->t('Share to chat'))
+ ->setPrimary(true)
+ ->setLink(
+ $this->urlGenerator->linkToOCSRouteAbsolute(
+ 'spreed.Recording.shareToChat',
+ [
+ 'apiVersion' => 'v1',
+ 'fileId' => $file->getId(),
+ 'timestamp' => $notification->getDateTime()->getTimestamp(),
+ 'token' => $room->getToken()
+ ]
+ ),
+ IAction::TYPE_POST
+ );
+ $dismissAction = $notification->createAction()
+ ->setParsedLabel($l->t('Dismiss notification'))
+ ->setLink(
+ $this->urlGenerator->linkToOCSRouteAbsolute(
+ 'spreed.Recording.notificationDismiss',
+ [
+ 'apiVersion' => 'v1',
+ 'token' => $room->getToken(),
+ 'timestamp' => $notification->getDateTime()->getTimestamp(),
+ ]
+ ),
+ IAction::TYPE_DELETE
+ );
+
+ $notification
+ ->setRichSubject(
+ $l->t('Recording for the call in {call} was uploaded.'),
+ [
+ 'call' => [
+ 'type' => 'call',
+ 'id' => $room->getId(),
+ 'name' => $room->getDisplayName($participant->getAttendee()->getActorId()),
+ 'call-type' => $this->getRoomType($room),
+ ],
+ ])
+ ->addParsedAction($shareAction)
+ ->addParsedAction($dismissAction);
+ return $notification;
+ }
+
/**
* @throws HintException
*/
diff --git a/lib/Service/RecordingService.php b/lib/Service/RecordingService.php
index 408ebf066..2eaafb999 100644
--- a/lib/Service/RecordingService.php
+++ b/lib/Service/RecordingService.php
@@ -27,14 +27,23 @@ namespace OCA\Talk\Service;
use InvalidArgumentException;
use OC\User\NoUserException;
+use OCA\Talk\Chat\ChatManager;
use OCA\Talk\Config;
use OCA\Talk\Exceptions\ParticipantNotFoundException;
+use OCA\Talk\Manager;
+use OCA\Talk\Participant;
use OCA\Talk\Room;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\IMimeTypeDetector;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
+use OCP\Notification\IManager;
+use OCP\Share\IManager as ShareManager;
+use OCP\Share\IShare;
+use Psr\Log\LoggerInterface;
class RecordingService {
public const DEFAULT_ALLOWED_RECORDING_FORMATS = [
@@ -44,11 +53,17 @@ class RecordingService {
];
public function __construct(
- private IMimeTypeDetector $mimeTypeDetector,
- private ParticipantService $participantService,
- private IRootFolder $rootFolder,
- private Config $config,
- private RoomService $roomService
+ protected IMimeTypeDetector $mimeTypeDetector,
+ protected ParticipantService $participantService,
+ protected IRootFolder $rootFolder,
+ protected IManager $notificationManager,
+ protected Manager $roomManager,
+ protected ITimeFactory $timeFactory,
+ protected Config $config,
+ protected RoomService $roomService,
+ protected ShareManager $shareManager,
+ protected ChatManager $chatManager,
+ protected LoggerInterface $logger,
) {
}
@@ -80,14 +95,15 @@ class RecordingService {
$this->validateFileFormat($fileName, $content);
try {
- $this->participantService->getParticipant($room, $owner);
+ $participant = $this->participantService->getParticipant($room, $owner);
} catch (ParticipantNotFoundException $e) {
throw new InvalidArgumentException('owner_participant');
}
try {
$recordingFolder = $this->getRecordingFolder($owner, $room->getToken());
- $recordingFolder->newFile($fileName, $content);
+ $file = $recordingFolder->newFile($fileName, $content);
+ $this->notifyStoredRecording($room, $participant, $file);
} catch (NoUserException $e) {
throw new InvalidArgumentException('owner_invalid');
} catch (NotPermittedException $e) {
@@ -142,4 +158,89 @@ class RecordingService {
}
return $recordingFolder;
}
+
+ public function notifyStoredRecording(Room $room, Participant $participant, File $file): void {
+ $attendee = $participant->getAttendee();
+
+ $notification = $this->notificationManager->createNotification();
+
+ $notification
+ ->setApp('spreed')
+ ->setDateTime($this->timeFactory->getDateTime())
+ ->setObject('chat', $room->getToken())
+ ->setUser($attendee->getActorId())
+ ->setSubject('record_file_stored', [
+ 'objectId' => $file->getId(),
+ ]);
+ $this->notificationManager->notify($notification);
+ }
+
+ public function notificationDismiss(Room $room, Participant $participant, int $timestamp): void {
+ $notification = $this->notificationManager->createNotification();
+ $notification->setApp('spreed')
+ ->setObject('chat', $room->getToken())
+ ->setSubject('record_file_stored')
+ ->setDateTime($this->timeFactory->getDateTime('@' . $timestamp))
+ ->setUser($participant->getAttendee()->getActorId());
+ $this->notificationManager->markProcessed($notification);
+ }
+
+ private function getTypeOfShare(string $mimetype): string {
+ if (str_starts_with($mimetype, 'video/')) {
+ return 'record-video';
+ }
+ return 'record-audio';
+ }
+
+ public function shareToChat(Room $room, Participant $participant, int $fileId, int $timestamp): void {
+ try {
+ $userFolder = $this->rootFolder->getUserFolder(
+ $participant->getAttendee()->getActorId()
+ );
+ /** @var \OCP\Files\File[] */
+ $files = $userFolder->getById($fileId);
+ $file = array_shift($files);
+ } catch (\Throwable $th) {
+ throw new InvalidArgumentException('file');
+ }
+
+ $creationDateTime = $this->timeFactory->getDateTime();
+
+ $share = $this->shareManager->newShare();
+ $share->setNodeId($fileId)
+ ->setShareTime($creationDateTime)
+ ->setSharedBy($participant->getAttendee()->getActorId())
+ ->setNode($file)
+ ->setShareType(IShare::TYPE_ROOM)
+ ->setSharedWith($room->getToken())
+ ->setPermissions(\OCP\Constants::PERMISSION_READ);
+
+ $share = $this->shareManager->createShare($share);
+
+ $message = json_encode([
+ 'message' => 'file_shared',
+ 'parameters' => [
+ 'share' => $share->getId(),
+ 'metaData' => [
+ 'mimeType' => $file->getMimeType(),
+ 'messageType' => $this->getTypeOfShare($file->getMimeType()),
+ ],
+ ],
+ ]);
+
+ try {
+ $this->chatManager->addSystemMessage(
+ $room,
+ $participant->getAttendee()->getActorType(),
+ $participant->getAttendee()->getActorId(),
+ $message,
+ $creationDateTime,
+ true
+ );
+ } catch (\Exception $e) {
+ $this->logger->error($e->getMessage(), ['exception' => $e]);
+ throw new InvalidArgumentException('system');
+ }
+ $this->notificationDismiss($room, $participant, $timestamp);
+ }
}
diff --git a/tests/integration/features/callapi/recording.feature b/tests/integration/features/callapi/recording.feature
index 37d6cf326..5b4919723 100644
--- a/tests/integration/features/callapi/recording.feature
+++ b/tests/integration/features/callapi/recording.feature
@@ -121,3 +121,6 @@ Feature: callapi/recording
| roomName | room1 |
And user "participant1" joins room "room1" with 200 (v4)
Then user "participant1" store recording file "/img/join_call.ogg" in room "room1" with 200 (v1)
+ And user "participant1" has the following notifications
+ | app | object_type | object_id | subject |
+ | spreed | chat | room1 | Recording for the call in room1 was uploaded. |
diff --git a/tests/php/Notification/NotifierTest.php b/tests/php/Notification/NotifierTest.php
index 6ec9a77c6..7c1b29419 100644
--- a/tests/php/Notification/NotifierTest.php
+++ b/tests/php/Notification/NotifierTest.php
@@ -37,6 +37,7 @@ use OCA\Talk\Room;
use OCA\Talk\Service\ParticipantService;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Comments\IComment;
+use OCP\Files\IRootFolder;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\IUser;
@@ -73,6 +74,10 @@ class NotifierTest extends TestCase {
protected $commentsManager;
/** @var MessageParser|MockObject */
protected $messageParser;
+ /** @var IURLGenerator|MockObject */
+ protected $urlGenerator;
+ /** @var IRootFolder|MockObject */
+ protected $rootFolder;
/** @var ITimeFactory|MockObject */
protected $timeFactory;
/** @var Definitions|MockObject */
@@ -95,6 +100,8 @@ class NotifierTest extends TestCase {
$this->notificationManager = $this->createMock(INotificationManager::class);
$this->commentsManager = $this->createMock(CommentsManager::class);
$this->messageParser = $this->createMock(MessageParser::class);
+ $this->urlGenerator = $this->createMock(IURLGenerator::class);
+ $this->rootFolder = $this->createMock(IRootFolder::class);
$this->timeFactory = $this->createMock(ITimeFactory::class);
$this->definitions = $this->createMock(Definitions::class);
$this->addressHandler = $this->createMock(AddressHandler::class);
@@ -111,6 +118,8 @@ class NotifierTest extends TestCase {
$this->notificationManager,
$this->commentsManager,
$this->messageParser,
+ $this->urlGenerator,
+ $this->rootFolder,
$this->timeFactory,
$this->definitions,
$this->addressHandler
diff --git a/tests/php/Service/RecordingServiceTest.php b/tests/php/Service/RecordingServiceTest.php
index e49147526..998e012b1 100644
--- a/tests/php/Service/RecordingServiceTest.php
+++ b/tests/php/Service/RecordingServiceTest.php
@@ -35,13 +35,19 @@ function is_uploaded_file($filename) {
namespace OCA\Talk\Tests\php\Service;
+use OCA\Talk\Chat\ChatManager;
use OCA\Talk\Config;
+use OCA\Talk\Manager;
use OCA\Talk\Service\ParticipantService;
use OCA\Talk\Service\RecordingService;
use OCA\Talk\Service\RoomService;
+use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Files\IMimeTypeDetector;
use OCP\Files\IRootFolder;
+use OCP\Notification\IManager;
+use OCP\Share\IManager as ShareManager;
use PHPUnit\Framework\MockObject\MockObject;
+use Psr\Log\LoggerInterface;
use Test\TestCase;
class RecordingServiceTest extends TestCase {
@@ -53,8 +59,20 @@ class RecordingServiceTest extends TestCase {
private $rootFolder;
/** @var Config|MockObject */
private $config;
+ /** @var IManager|MockObject */
+ private $notificationManager;
+ /** @var Manager|MockObject */
+ private $roomManager;
+ /** @var ITimeFactory|MockObject */
+ private