avatar.py 7.3 KB

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