Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/spreed.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Calviño Sánchez <danxuliu@gmail.com>2020-12-30 08:08:17 +0300
committerVitor Mattos <vitor@php.rio>2022-08-31 23:20:10 +0300
commitb2a1185bfb7a0d8677fbed1b50e3529a1e707298 (patch)
tree59469a766e99e9cc223272b52665ce1dab46de3a
parentaf962dea0ed084415431c37167ac2124e26df6c8 (diff)
Replace generic avatar system with specific Talk endpointsadd-backend-for-conversation-avatars
The generic avatar system will not be included in Nextcloud 21, so for the time being Talk needs to provide its own endpoints for room avatars. Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
-rw-r--r--appinfo/routes.php1
-rw-r--r--appinfo/routes/routesRoomAvatarController.php40
-rw-r--r--lib/AppInfo/Application.php2
-rw-r--r--lib/Avatar/RoomAvatarProvider.php3
-rw-r--r--lib/Controller/RoomAvatarController.php233
-rw-r--r--tests/integration/features/bootstrap/AvatarTrait.php6
6 files changed, 278 insertions, 7 deletions
diff --git a/appinfo/routes.php b/appinfo/routes.php
index 6fba9ee5a..642e1a4d4 100644
--- a/appinfo/routes.php
+++ b/appinfo/routes.php
@@ -39,6 +39,7 @@ return array_merge_recursive(
include(__DIR__ . '/routes/routesPublicShareAuthController.php'),
include(__DIR__ . '/routes/routesReactionController.php'),
include(__DIR__ . '/routes/routesRoomController.php'),
+ include(__DIR__ . '/routes/routesRoomAvatarController.php'),
include(__DIR__ . '/routes/routesSettingsController.php'),
include(__DIR__ . '/routes/routesSignalingController.php'),
include(__DIR__ . '/routes/routesTempAvatarController.php'),
diff --git a/appinfo/routes/routesRoomAvatarController.php b/appinfo/routes/routesRoomAvatarController.php
new file mode 100644
index 000000000..abc437dd4
--- /dev/null
+++ b/appinfo/routes/routesRoomAvatarController.php
@@ -0,0 +1,40 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2022, Vitor Mattos <vitor@php.rio>
+ *
+ * @author Vitor Mattos <vitor@php.rio>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+$requirements = [
+ 'apiVersion' => 'v1',
+ 'token' => '^[a-z0-9]{4,30}$',
+];
+
+return [
+ 'ocs' => [
+ /** @see \OCA\Talk\Controller\RoomAvatarController::getAvatar() */
+ ['name' => 'RoomAvatar#getAvatar', 'url' => '/api/{apiVersion}/avatar/{roomToken}/{size}', 'verb' => 'GET', 'requirements' => $requirements],
+ /** @see \OCA\Talk\Controller\RoomAvatarController::setAvatar() */
+ ['name' => 'RoomAvatar#setAvatar', 'url' => '/api/{apiVersion}/avatar/{roomToken}', 'verb' => 'POST', 'requirements' => $requirements],
+ /** @see \OCA\Talk\Controller\RoomAvatarController::deleteAvatar() */
+ ['name' => 'RoomAvatar#deleteAvatar', 'url' => '/api/{apiVersion}/avatar/{roomToken}', 'verb' => 'DELETE', 'requirements' => $requirements],
+ ],
+];
diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php
index 835d4e3a8..cefe4a036 100644
--- a/lib/AppInfo/Application.php
+++ b/lib/AppInfo/Application.php
@@ -31,7 +31,6 @@ use OCA\Circles\Events\RemovingCircleMemberEvent;
use OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent;
use OCA\Talk\Activity\Listener as ActivityListener;
use OCA\Talk\Avatar\Listener as AvatarListener;
-use OCA\Talk\Avatar\RoomAvatarProvider;
use OCA\Talk\Capabilities;
use OCA\Talk\Chat\Changelog\Listener as ChangelogListener;
use OCA\Talk\Chat\ChatManager;
@@ -151,7 +150,6 @@ class Application extends App implements IBootstrap {
$context->registerProfileLinkAction(TalkAction::class);
$context->registerTalkBackend(TalkBackend::class);
- $context->registerAvatarProvider('room', RoomAvatarProvider::class);
}
public function boot(IBootContext $context): void {
diff --git a/lib/Avatar/RoomAvatarProvider.php b/lib/Avatar/RoomAvatarProvider.php
index 2ea0444cb..b44d62b0a 100644
--- a/lib/Avatar/RoomAvatarProvider.php
+++ b/lib/Avatar/RoomAvatarProvider.php
@@ -33,11 +33,10 @@ use OCA\Talk\Room;
use OCP\Files\IAppData;
use OCP\Files\NotFoundException;
use OCP\IAvatar;
-use OCP\IAvatarProvider;
use OCP\IL10N;
use Psr\Log\LoggerInterface;
-class RoomAvatarProvider implements IAvatarProvider {
+class RoomAvatarProvider {
/** @var IAppData */
private $appData;
diff --git a/lib/Controller/RoomAvatarController.php b/lib/Controller/RoomAvatarController.php
new file mode 100644
index 000000000..fd701039a
--- /dev/null
+++ b/lib/Controller/RoomAvatarController.php
@@ -0,0 +1,233 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2020, Daniel Calviño Sánchez (danxuliu@gmail.com)
+ *
+ * @author Daniel Calviño Sánchez <danxuliu@gmail.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Talk\Controller;
+
+use OCA\Talk\Avatar\RoomAvatarProvider;
+use OCP\AppFramework\OCSController;
+use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\DataResponse;
+use OCP\AppFramework\Http\FileDisplayResponse;
+use OCP\AppFramework\Http\Response;
+use OCP\Files\NotFoundException;
+use OCP\IL10N;
+use OCP\Image;
+use OCP\IRequest;
+use Psr\Log\LoggerInterface;
+
+class RoomAvatarController extends OCSController {
+
+ /** @var IL10N */
+ protected $l;
+
+ /** @var LoggerInterface */
+ protected $logger;
+
+ /** @var RoomAvatarProvider */
+ protected $roomAvatarProvider;
+
+ public function __construct($appName,
+ IRequest $request,
+ IL10N $l10n,
+ LoggerInterface $logger,
+ RoomAvatarProvider $roomAvatarProvider) {
+ parent::__construct($appName, $request);
+
+ $this->l = $l10n;
+ $this->logger = $logger;
+ $this->roomAvatarProvider = $roomAvatarProvider;
+ }
+
+ /**
+ * @PublicPage
+ *
+ * @param string $roomToken
+ * @param int $size
+ * @return DataResponse|FileDisplayResponse
+ */
+ public function getAvatar(string $roomToken, int $size): Response {
+ $size = $this->sanitizeSize($size);
+
+ try {
+ $avatar = $this->roomAvatarProvider->getAvatar($roomToken);
+ } catch (\InvalidArgumentException $e) {
+ return new DataResponse([], Http::STATUS_NOT_FOUND);
+ }
+
+ if (!$this->roomAvatarProvider->canBeAccessedByCurrentUser($avatar)) {
+ return new DataResponse([], Http::STATUS_NOT_FOUND);
+ }
+
+ try {
+ $avatarFile = $avatar->getFile($size);
+ $response = new FileDisplayResponse(
+ $avatarFile,
+ Http::STATUS_OK,
+ [
+ 'Content-Type' => $avatarFile->getMimeType(),
+ 'X-NC-IsCustomAvatar' => $avatar->isCustomAvatar() ? '1' : '0',
+ ]
+ );
+ } catch (NotFoundException $e) {
+ return new DataResponse([], Http::STATUS_NOT_FOUND);
+ }
+
+ $cache = $this->roomAvatarProvider->getCacheTimeToLive($avatar);
+ if ($cache !== null) {
+ $response->cacheFor($cache);
+ }
+
+ return $response;
+ }
+
+ /**
+ * Returns the closest value to the predefined set of sizes
+ *
+ * @param int $size the size to sanitize
+ * @return int the sanitized size
+ */
+ private function sanitizeSize(int $size): int {
+ $validSizes = [64, 128, 256, 512];
+
+ if ($size < $validSizes[0]) {
+ return $validSizes[0];
+ }
+
+ if ($size > $validSizes[count($validSizes) - 1]) {
+ return $validSizes[count($validSizes) - 1];
+ }
+
+ for ($i = 0; $i < count($validSizes) - 1; $i++) {
+ if ($size >= $validSizes[$i] && $size <= $validSizes[$i + 1]) {
+ $middlePoint = ($validSizes[$i] + $validSizes[$i + 1]) / 2;
+ if ($size < $middlePoint) {
+ return $validSizes[$i];
+ }
+ return $validSizes[$i + 1];
+ }
+ }
+
+ return $size;
+ }
+
+ /**
+ * @PublicPage
+ *
+ * @param string $roomToken
+ * @return DataResponse
+ */
+ public function setAvatar(string $roomToken): DataResponse {
+ $files = $this->request->getUploadedFile('files');
+
+ if (is_null($files)) {
+ return new DataResponse(
+ ['data' => ['message' => $this->l->t('No file provided')]],
+ Http::STATUS_BAD_REQUEST
+ );
+ }
+
+ if (
+ $files['error'][0] !== 0 ||
+ !is_uploaded_file($files['tmp_name'][0]) ||
+ \OC\Files\Filesystem::isFileBlacklisted($files['tmp_name'][0])
+ ) {
+ return new DataResponse(
+ ['data' => ['message' => $this->l->t('Invalid file provided')]],
+ Http::STATUS_BAD_REQUEST
+ );
+ }
+
+ if ($files['size'][0] > 20 * 1024 * 1024) {
+ return new DataResponse(
+ ['data' => ['message' => $this->l->t('File is too big')]],
+ Http::STATUS_BAD_REQUEST
+ );
+ }
+
+ $content = file_get_contents($files['tmp_name'][0]);
+ unlink($files['tmp_name'][0]);
+
+ $image = new Image();
+ $image->loadFromData($content);
+
+ try {
+ $avatar = $this->roomAvatarProvider->getAvatar($roomToken);
+ } catch (\InvalidArgumentException $e) {
+ return new DataResponse([], Http::STATUS_NOT_FOUND);
+ }
+
+ if (!$this->roomAvatarProvider->canBeModifiedByCurrentUser($avatar)) {
+ return new DataResponse([], Http::STATUS_NOT_FOUND);
+ }
+
+ try {
+ $avatar->set($image);
+ return new DataResponse(
+ ['status' => 'success']
+ );
+ } catch (\OC\NotSquareException $e) {
+ return new DataResponse(
+ ['data' => ['message' => $this->l->t('Crop is not square')]],
+ Http::STATUS_BAD_REQUEST
+ );
+ } catch (\Exception $e) {
+ $this->logger->error('Error when setting avatar', ['app' => 'core', 'exception' => $e]);
+ return new DataResponse(
+ ['data' => ['message' => $this->l->t('An error occurred. Please contact your admin.')]],
+ Http::STATUS_BAD_REQUEST
+ );
+ }
+ }
+
+ /**
+ * @PublicPage
+ *
+ * @param string $roomToken
+ * @return DataResponse
+ */
+ public function deleteAvatar(string $roomToken): DataResponse {
+ try {
+ $avatar = $this->roomAvatarProvider->getAvatar($roomToken);
+ } catch (\InvalidArgumentException $e) {
+ return new DataResponse([], Http::STATUS_NOT_FOUND);
+ }
+
+ if (!$this->roomAvatarProvider->canBeModifiedByCurrentUser($avatar)) {
+ return new DataResponse([], Http::STATUS_NOT_FOUND);
+ }
+
+ try {
+ $avatar->remove();
+ return new DataResponse();
+ } catch (\Exception $e) {
+ $this->logger->error('Error when deleting avatar', ['app' => 'core', 'exception' => $e]);
+ return new DataResponse(
+ ['data' => ['message' => $this->l->t('An error occurred. Please contact your admin.')]],
+ Http::STATUS_BAD_REQUEST
+ );
+ }
+ }
+}
diff --git a/tests/integration/features/bootstrap/AvatarTrait.php b/tests/integration/features/bootstrap/AvatarTrait.php
index d6758a150..8edc61467 100644
--- a/tests/integration/features/bootstrap/AvatarTrait.php
+++ b/tests/integration/features/bootstrap/AvatarTrait.php
@@ -74,7 +74,7 @@ trait AvatarTrait {
*/
public function userGetsAvatarForRoomWithSizeWith(string $user, string $identifier, string $size, string $statusCode) {
$this->setCurrentUser($user);
- $this->sendRequest('GET', '/core/avatar/room/' . FeatureContext::getTokenForIdentifier($identifier) . '/' . $size, null);
+ $this->sendRequest('GET', '/apps/spreed/api/v3/avatar/' . FeatureContext::getTokenForIdentifier($identifier) . '/' . $size, null);
$this->assertStatusCode($this->response, $statusCode);
if ($statusCode !== '200') {
@@ -107,7 +107,7 @@ trait AvatarTrait {
$file = \GuzzleHttp\Psr7\stream_for(fopen($source, 'r'));
$this->setCurrentUser($user);
- $this->sendRequest('POST', '/core/avatar/room/' . FeatureContext::getTokenForIdentifier($identifier),
+ $this->sendRequest('POST', '/apps/spreed/api/v3/avatar/' . FeatureContext::getTokenForIdentifier($identifier),
[
'multipart' => [
[
@@ -138,7 +138,7 @@ trait AvatarTrait {
*/
public function userDeletesAvatarForRoomWith(string $user, string $identifier, string $statusCode) {
$this->setCurrentUser($user);
- $this->sendRequest('DELETE', '/core/avatar/room/' . FeatureContext::getTokenForIdentifier($identifier), null);
+ $this->sendRequest('DELETE', '/apps/spreed/api/v3/avatar/' . FeatureContext::getTokenForIdentifier($identifier), null);
$this->assertStatusCode($this->response, $statusCode);
}