avatar.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. import json
  2. from django.core.exceptions import ValidationError
  3. from django.utils.translation import gettext as _
  4. from rest_framework import status
  5. from rest_framework.response import Response
  6. from ... import avatars
  7. from ....conf import settings
  8. from ....core.decorators import require_dict_data
  9. from ....core.utils import format_plaintext_for_html
  10. from ...models import AvatarGallery
  11. from ...serializers import ModerateAvatarSerializer
  12. @require_dict_data
  13. def avatar_endpoint(request, pk=None):
  14. if request.user.is_avatar_locked:
  15. if request.user.avatar_lock_user_message:
  16. reason = format_plaintext_for_html(request.user.avatar_lock_user_message)
  17. else:
  18. reason = None
  19. return Response(
  20. {
  21. "detail": _("Your avatar is locked. You can't change it."),
  22. "reason": reason,
  23. },
  24. status=status.HTTP_403_FORBIDDEN,
  25. )
  26. avatar_options = get_avatar_options(request, request.user)
  27. if request.method == "POST":
  28. return avatar_post(request, avatar_options)
  29. return Response(avatar_options)
  30. def get_avatar_options(request, user):
  31. options = {
  32. "avatars": user.avatars,
  33. "generated": True,
  34. "gravatar": False,
  35. "crop_src": False,
  36. "crop_tmp": False,
  37. "upload": False,
  38. "galleries": False,
  39. }
  40. # Allow existing galleries
  41. if avatars.gallery.galleries_exist():
  42. options["galleries"] = []
  43. for gallery in avatars.gallery.get_available_galleries():
  44. gallery_images = []
  45. for image in gallery["images"]:
  46. gallery_images.append({"id": image.id, "url": image.url})
  47. options["galleries"].append(
  48. {"name": gallery["name"], "images": gallery_images}
  49. )
  50. # Can't have custom avatar?
  51. if not request.settings.allow_custom_avatars:
  52. return options
  53. # Allow Gravatar download
  54. options["gravatar"] = True
  55. # Allow crop if we have uploaded temporary avatar
  56. if avatars.uploaded.has_source_avatar(user):
  57. try:
  58. options["crop_src"] = {
  59. "url": user.avatar_src.url,
  60. "crop": json.loads(user.avatar_crop),
  61. "size": max(settings.MISAGO_AVATARS_SIZES),
  62. }
  63. except (TypeError, ValueError):
  64. pass
  65. # Allow crop of uploaded avatar
  66. if avatars.uploaded.has_temporary_avatar(user):
  67. options["crop_tmp"] = {
  68. "url": user.avatar_tmp.url,
  69. "size": max(settings.MISAGO_AVATARS_SIZES),
  70. }
  71. # Allow upload conditions
  72. options["upload"] = {
  73. "limit": request.settings.avatar_upload_limit * 1024,
  74. "allowed_extensions": avatars.uploaded.ALLOWED_EXTENSIONS,
  75. "allowed_mime_types": avatars.uploaded.ALLOWED_MIME_TYPES,
  76. }
  77. return options
  78. class AvatarError(Exception):
  79. pass
  80. def avatar_post(request, options):
  81. user = request.user
  82. data = request.data
  83. avatar_type = data.get("avatar", "nope")
  84. try:
  85. type_options = options[avatar_type]
  86. if not type_options:
  87. return Response(
  88. {"detail": _("This avatar type is not allowed.")},
  89. status=status.HTTP_400_BAD_REQUEST,
  90. )
  91. avatar_strategy = AVATAR_TYPES[avatar_type]
  92. except KeyError:
  93. return Response(
  94. {"detail": _("Unknown avatar type.")}, status=status.HTTP_400_BAD_REQUEST
  95. )
  96. try:
  97. if avatar_type == "upload":
  98. # avatar_upload strategy requires access to request.settings
  99. response_dict = {"detail": avatar_upload(request, user, data)}
  100. else:
  101. response_dict = {"detail": avatar_strategy(user, data)}
  102. except AvatarError as e:
  103. return Response({"detail": e.args[0]}, status=status.HTTP_400_BAD_REQUEST)
  104. user.save()
  105. updated_options = get_avatar_options(request, user)
  106. response_dict.update(updated_options)
  107. return Response(response_dict)
  108. def avatar_generate(user, data):
  109. avatars.dynamic.set_avatar(user)
  110. return _("New avatar based on your account was set.")
  111. def avatar_gravatar(user, data):
  112. try:
  113. avatars.gravatar.set_avatar(user)
  114. return _("Gravatar was downloaded and set as new avatar.")
  115. except avatars.gravatar.NoGravatarAvailable:
  116. raise AvatarError(_("No Gravatar is associated with your e-mail address."))
  117. except avatars.gravatar.GravatarError:
  118. raise AvatarError(_("Failed to connect to Gravatar servers."))
  119. def avatar_gallery(user, data):
  120. try:
  121. image_pk = int(data.get("image"))
  122. image = AvatarGallery.objects.get(pk=image_pk)
  123. if image.gallery == "__default__":
  124. raise ValueError()
  125. avatars.gallery.set_avatar(user, image)
  126. return _("Avatar from gallery was set.")
  127. except (TypeError, ValueError, AvatarGallery.DoesNotExist):
  128. raise AvatarError(_("Incorrect image."))
  129. def avatar_upload(request, user, data):
  130. new_avatar = data.get("image")
  131. if not new_avatar:
  132. raise AvatarError(_("No file was sent."))
  133. try:
  134. avatars.uploaded.handle_uploaded_file(request, user, new_avatar)
  135. except ValidationError as e:
  136. raise AvatarError(e.args[0])
  137. # send back url for temp image
  138. return user.avatar_tmp.url
  139. def avatar_crop_src(user, data):
  140. avatar_crop(user, data, "src")
  141. return _("Avatar was re-cropped.")
  142. def avatar_crop_tmp(user, data):
  143. avatar_crop(user, data, "tmp")
  144. return _("Uploaded avatar was set.")
  145. def avatar_crop(user, data, suffix):
  146. try:
  147. crop = avatars.uploaded.crop_source_image(user, suffix, data.get("crop", {}))
  148. user.avatar_crop = json.dumps(crop)
  149. except ValidationError as e:
  150. raise AvatarError(e.args[0])
  151. AVATAR_TYPES = {
  152. "generated": avatar_generate,
  153. "gravatar": avatar_gravatar,
  154. "galleries": avatar_gallery,
  155. "upload": avatar_upload,
  156. "crop_src": avatar_crop_src,
  157. "crop_tmp": avatar_crop_tmp,
  158. }
  159. def moderate_avatar_endpoint(request, profile):
  160. if request.method == "POST":
  161. is_avatar_locked = profile.is_avatar_locked
  162. serializer = ModerateAvatarSerializer(profile, data=request.data)
  163. if not serializer.is_valid():
  164. return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
  165. if serializer.validated_data["is_avatar_locked"] and not is_avatar_locked:
  166. avatars.dynamic.set_avatar(profile)
  167. serializer.save()
  168. return Response(
  169. {
  170. "avatars": profile.avatars,
  171. "is_avatar_locked": int(profile.is_avatar_locked),
  172. "avatar_lock_user_message": profile.avatar_lock_user_message,
  173. "avatar_lock_staff_message": profile.avatar_lock_staff_message,
  174. }
  175. )
  176. return Response(
  177. {
  178. "is_avatar_locked": int(profile.is_avatar_locked),
  179. "avatar_lock_user_message": profile.avatar_lock_user_message,
  180. "avatar_lock_staff_message": profile.avatar_lock_staff_message,
  181. }
  182. )