diff --git a/zerver/lib/transfer.py b/zerver/lib/transfer.py index a20cfb40bc..28c2591a9a 100644 --- a/zerver/lib/transfer.py +++ b/zerver/lib/transfer.py @@ -9,6 +9,7 @@ from django.db import connection from zerver.lib.avatar_hash import user_avatar_path from zerver.lib.mime_types import guess_type +from zerver.lib.upload import upload_emoji_image from zerver.lib.upload.s3 import S3UploadBackend, upload_image_to_s3 from zerver.models import Attachment, RealmEmoji, UserProfile @@ -102,7 +103,7 @@ def _transfer_emoji_to_s3(realm_emoji: RealmEmoji) -> None: emoji_path = os.path.join(settings.LOCAL_AVATARS_DIR, emoji_path) + ".original" try: with open(emoji_path, "rb") as f: - s3backend.upload_emoji_image(f, realm_emoji.file_name, realm_emoji.author) + upload_emoji_image(f, realm_emoji.file_name, realm_emoji.author, backend=s3backend) logging.info("Uploaded emoji file in path %s", emoji_path) except FileNotFoundError: # nocoverage pass diff --git a/zerver/lib/upload/__init__.py b/zerver/lib/upload/__init__.py index 7c2d6755e9..af32cb93d6 100644 --- a/zerver/lib/upload/__init__.py +++ b/zerver/lib/upload/__init__.py @@ -1,5 +1,6 @@ import io import logging +import os from datetime import datetime from typing import IO, Any, BinaryIO, Callable, Iterator, List, Optional, Tuple, Union from urllib.parse import unquote, urljoin @@ -11,6 +12,7 @@ from django.utils.translation import gettext as _ from zerver.lib.exceptions import ErrorCode, JsonableError from zerver.lib.mime_types import guess_type from zerver.lib.outgoing_http import OutgoingSession +from zerver.lib.thumbnail import resize_emoji from zerver.lib.upload.base import ZulipUploadBackend from zerver.models import Attachment, Message, Realm, RealmEmoji, ScheduledMessage, UserProfile @@ -172,9 +174,33 @@ def upload_logo_image(user_file: IO[bytes], user_profile: UserProfile, night: bo def upload_emoji_image( - emoji_file: IO[bytes], emoji_file_name: str, user_profile: UserProfile + emoji_file: IO[bytes], + emoji_file_name: str, + user_profile: UserProfile, + backend: Optional[ZulipUploadBackend] = None, ) -> bool: - return upload_backend.upload_emoji_image(emoji_file, emoji_file_name, user_profile) + if backend is None: + backend = upload_backend + content_type = guess_type(emoji_file_name)[0] + emoji_path = RealmEmoji.PATH_ID_TEMPLATE.format( + realm_id=user_profile.realm_id, + emoji_file_name=emoji_file_name, + ) + + image_data = emoji_file.read() + backend.upload_single_emoji_image( + f"{emoji_path}.original", content_type, user_profile, image_data + ) + resized_image_data, is_animated, still_image_data = resize_emoji(image_data) + backend.upload_single_emoji_image(emoji_path, content_type, user_profile, resized_image_data) + if is_animated: + assert still_image_data is not None + still_path = RealmEmoji.STILL_PATH_ID_TEMPLATE.format( + realm_id=user_profile.realm_id, + emoji_filename_without_extension=os.path.splitext(emoji_file_name)[0], + ) + backend.upload_single_emoji_image(still_path, content_type, user_profile, still_image_data) + return is_animated def get_emoji_file_content( diff --git a/zerver/lib/upload/base.py b/zerver/lib/upload/base.py index d5e76c1599..59b3b0265c 100644 --- a/zerver/lib/upload/base.py +++ b/zerver/lib/upload/base.py @@ -125,9 +125,13 @@ class ZulipUploadBackend: def get_emoji_url(self, emoji_file_name: str, realm_id: int, still: bool = False) -> str: raise NotImplementedError - def upload_emoji_image( - self, emoji_file: IO[bytes], emoji_file_name: str, user_profile: UserProfile - ) -> bool: + def upload_single_emoji_image( + self, + path: str, + content_type: Optional[str], + user_profile: UserProfile, + image_data: bytes, + ) -> None: raise NotImplementedError # Export tarballs diff --git a/zerver/lib/upload/local.py b/zerver/lib/upload/local.py index 9e8d0c59f5..d53a74e30c 100644 --- a/zerver/lib/upload/local.py +++ b/zerver/lib/upload/local.py @@ -10,7 +10,7 @@ from django.conf import settings from typing_extensions import override from zerver.lib.avatar_hash import user_avatar_path -from zerver.lib.thumbnail import MEDIUM_AVATAR_SIZE, resize_avatar, resize_emoji, resize_logo +from zerver.lib.thumbnail import MEDIUM_AVATAR_SIZE, resize_avatar, resize_logo from zerver.lib.timestamp import timestamp_to_datetime from zerver.lib.upload.base import ZulipUploadBackend, create_attachment, sanitize_name from zerver.lib.utils import assert_is_not_none @@ -238,26 +238,10 @@ class LocalUploadBackend(ZulipUploadBackend): ) @override - def upload_emoji_image( - self, emoji_file: IO[bytes], emoji_file_name: str, user_profile: UserProfile - ) -> bool: - emoji_path = RealmEmoji.PATH_ID_TEMPLATE.format( - realm_id=user_profile.realm_id, - emoji_file_name=emoji_file_name, - ) - - image_data = emoji_file.read() - write_local_file("avatars", f"{emoji_path}.original", image_data) - resized_image_data, is_animated, still_image_data = resize_emoji(image_data) - write_local_file("avatars", emoji_path, resized_image_data) - if is_animated: - assert still_image_data is not None - still_path = RealmEmoji.STILL_PATH_ID_TEMPLATE.format( - realm_id=user_profile.realm_id, - emoji_filename_without_extension=os.path.splitext(emoji_file_name)[0], - ) - write_local_file("avatars", still_path, still_image_data) - return is_animated + def upload_single_emoji_image( + self, path: str, content_type: Optional[str], user_profile: UserProfile, image_data: bytes + ) -> None: + write_local_file("avatars", path, image_data) @override def get_export_tarball_url(self, realm: Realm, export_path: str) -> str: diff --git a/zerver/lib/upload/s3.py b/zerver/lib/upload/s3.py index 185dc4a270..cd6cf0eb3a 100644 --- a/zerver/lib/upload/s3.py +++ b/zerver/lib/upload/s3.py @@ -14,7 +14,7 @@ from typing_extensions import override from zerver.lib.avatar_hash import user_avatar_path from zerver.lib.mime_types import guess_type -from zerver.lib.thumbnail import MEDIUM_AVATAR_SIZE, resize_avatar, resize_emoji, resize_logo +from zerver.lib.thumbnail import MEDIUM_AVATAR_SIZE, resize_avatar, resize_logo from zerver.lib.upload.base import ( INLINE_MIME_TYPES, ZulipUploadBackend, @@ -442,48 +442,17 @@ class S3UploadBackend(ZulipUploadBackend): return self.get_public_upload_url(emoji_path) @override - def upload_emoji_image( - self, emoji_file: IO[bytes], emoji_file_name: str, user_profile: UserProfile - ) -> bool: - content_type = guess_type(emoji_file_name)[0] - emoji_path = RealmEmoji.PATH_ID_TEMPLATE.format( - realm_id=user_profile.realm_id, - emoji_file_name=emoji_file_name, - ) - - image_data = emoji_file.read() + def upload_single_emoji_image( + self, path: str, content_type: Optional[str], user_profile: UserProfile, image_data: bytes + ) -> None: upload_image_to_s3( self.avatar_bucket, - f"{emoji_path}.original", + path, content_type, user_profile, image_data, ) - resized_image_data, is_animated, still_image_data = resize_emoji(image_data) - upload_image_to_s3( - self.avatar_bucket, - emoji_path, - content_type, - user_profile, - resized_image_data, - ) - if is_animated: - still_path = RealmEmoji.STILL_PATH_ID_TEMPLATE.format( - realm_id=user_profile.realm_id, - emoji_filename_without_extension=os.path.splitext(emoji_file_name)[0], - ) - assert still_image_data is not None - upload_image_to_s3( - self.avatar_bucket, - still_path, - "image/png", - user_profile, - still_image_data, - ) - - return is_animated - @override def get_export_tarball_url(self, realm: Realm, export_path: str) -> str: # export_path has a leading / diff --git a/zerver/tests/test_upload_s3.py b/zerver/tests/test_upload_s3.py index 7474072e71..9357e3fa7f 100644 --- a/zerver/tests/test_upload_s3.py +++ b/zerver/tests/test_upload_s3.py @@ -483,9 +483,7 @@ class S3Test(ZulipTestCase): user_profile = self.example_user("hamlet") emoji_name = "emoji.png" with get_test_image_file("img.png") as image_file: - zerver.lib.upload.upload_backend.upload_emoji_image( - image_file, emoji_name, user_profile - ) + zerver.lib.upload.upload_emoji_image(image_file, emoji_name, user_profile) emoji_path = RealmEmoji.PATH_ID_TEMPLATE.format( realm_id=user_profile.realm_id,