avatar.py 6.9 KB

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