views.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. from path import path
  2. from PIL import Image
  3. from zipfile import is_zipfile
  4. from django.conf import settings
  5. from django.core.exceptions import ValidationError
  6. from django.core.urlresolvers import reverse
  7. from django.shortcuts import redirect
  8. from django.utils.encoding import smart_str
  9. from django.utils.translation import ugettext as _
  10. from misago.apps.errors import error404
  11. from misago.decorators import block_guest
  12. from misago.forms import FormLayout
  13. from misago.messages import Message
  14. from misago.utils.strings import random_string
  15. from misago.utils.avatars import resizeimage
  16. from misago.apps.usercp.template import RequestContext
  17. from misago.apps.usercp.avatar.forms import UploadAvatarForm
  18. def avatar_view(f):
  19. def decorator(*args, **kwargs):
  20. request = args[0]
  21. if request.user.avatar_ban:
  22. return request.theme.render_to_response('usercp/avatar_banned.html',
  23. context_instance=RequestContext(request, {
  24. 'tab': 'avatar',
  25. }));
  26. return f(*args, **kwargs)
  27. return decorator
  28. @block_guest
  29. @avatar_view
  30. def avatar(request):
  31. message = request.messages.get_message('usercp_avatar')
  32. return request.theme.render_to_response('usercp/avatar.html',
  33. context_instance=RequestContext(request, {
  34. 'message': message,
  35. 'tab': 'avatar',
  36. }));
  37. @block_guest
  38. @avatar_view
  39. def gravatar(request):
  40. if not 'gravatar' in request.settings.avatars_types:
  41. return error404(request)
  42. if request.user.avatar_type != 'gravatar':
  43. if request.csrf.request_secure(request):
  44. request.user.delete_avatar()
  45. request.user.avatar_type = 'gravatar'
  46. request.user.save(force_update=True)
  47. request.messages.set_flash(Message(_("Your avatar has been changed to Gravatar.")), 'success', 'usercp_avatar')
  48. else:
  49. request.messages.set_flash(Message(_("Request authorisation is invalid.")), 'error', 'usercp_avatar')
  50. return redirect(reverse('usercp_avatar'))
  51. @block_guest
  52. @avatar_view
  53. def gallery(request):
  54. if not 'gallery' in request.settings.avatars_types:
  55. return error404(request)
  56. allowed_avatars = []
  57. galleries = []
  58. for directory in path(settings.STATICFILES_DIRS[0]).joinpath('avatars').dirs():
  59. if directory[-7:] != '_locked' and directory[-8:] != '_default':
  60. gallery = {'name': directory[-7:], 'avatars': []}
  61. avatars = directory.files('*.gif')
  62. avatars += directory.files('*.jpg')
  63. avatars += directory.files('*.jpeg')
  64. avatars += directory.files('*.png')
  65. for item in avatars:
  66. gallery['avatars'].append('/'.join(path(item).splitall()[-2:]))
  67. galleries.append(gallery)
  68. allowed_avatars += gallery['avatars']
  69. if not allowed_avatars:
  70. request.messages.set_flash(Message(_("No avatar galleries are available at the moment.")), 'info', 'usercp_avatar')
  71. return redirect(reverse('usercp_avatar'))
  72. message = request.messages.get_message('usercp_avatar')
  73. if request.method == 'POST':
  74. if request.csrf.request_secure(request):
  75. new_avatar = request.POST.get('avatar_image')
  76. if new_avatar in allowed_avatars:
  77. request.user.delete_avatar()
  78. request.user.avatar_type = 'gallery'
  79. request.user.avatar_image = new_avatar
  80. request.user.save(force_update=True)
  81. request.messages.set_flash(Message(_("Your avatar has been changed to one from gallery.")), 'success', 'usercp_avatar')
  82. return redirect(reverse('usercp_avatar'))
  83. message = Message(_("Selected Avatar is incorrect."), 'error')
  84. else:
  85. message = Message(_("Request authorisation is invalid."), 'error')
  86. return request.theme.render_to_response('usercp/avatar_gallery.html',
  87. context_instance=RequestContext(request, {
  88. 'message': message,
  89. 'galleries': galleries,
  90. 'tab': 'avatar',
  91. }));
  92. @block_guest
  93. @avatar_view
  94. def upload(request):
  95. if not 'upload' in request.settings.avatars_types:
  96. return error404(request)
  97. message = request.messages.get_message('usercp_avatar')
  98. if request.method == 'POST':
  99. form = UploadAvatarForm(request.POST, request.FILES, request=request)
  100. if form.is_valid():
  101. request.user.delete_avatar_temp()
  102. image = form.cleaned_data['avatar_upload']
  103. image_name, image_extension = path(smart_str(image.name.lower())).splitext()
  104. image_name = '%s_tmp_%s%s' % (request.user.pk, random_string(8), image_extension)
  105. image_path = settings.MEDIA_ROOT + 'avatars/' + image_name
  106. request.user.avatar_temp = image_name
  107. with open(image_path, 'wb+') as destination:
  108. for chunk in image.chunks():
  109. destination.write(chunk)
  110. request.user.save()
  111. try:
  112. if is_zipfile(image_path):
  113. # Composite file upload
  114. raise ValidationError()
  115. image = Image.open(image_path)
  116. if not image.format in ['GIF', 'PNG', 'JPEG']:
  117. raise ValidationError()
  118. image.seek(0)
  119. image.save(image_path)
  120. if request.POST.get('js_check'):
  121. return redirect(reverse('usercp_avatar_upload_crop'))
  122. # Redirect to crop page didnt happen, handle avatar with old school hollywood way
  123. image_path = settings.MEDIA_ROOT + 'avatars/'
  124. source = Image.open(image_path + request.user.avatar_temp)
  125. image_name, image_extension = path(request.user.avatar_temp).splitext()
  126. image_name = '%s_%s%s' % (request.user.pk, random_string(8), image_extension)
  127. resizeimage(source, settings.AVATAR_SIZES[0], image_path + image_name, info=source.info, format=source.format)
  128. for size in settings.AVATAR_SIZES[1:]:
  129. resizeimage(source, size, image_path + str(size) + '_' + image_name, info=source.info, format=source.format)
  130. # Update user model one more time
  131. request.user.delete_avatar_image()
  132. request.user.delete_avatar_original()
  133. request.user.avatar_type = 'upload'
  134. request.user.avatar_original = '%s_org_%s%s' % (request.user.pk, random_string(8), image_extension)
  135. source.save(image_path + request.user.avatar_original)
  136. request.user.delete_avatar_temp()
  137. request.user.avatar_image = image_name
  138. request.user.save(force_update=True)
  139. # Set message and adios!
  140. request.messages.set_flash(Message(_("Your avatar has changed.")), 'success', 'usercp_avatar')
  141. return redirect(reverse('usercp_avatar'))
  142. except ValidationError:
  143. request.user.delete_avatar()
  144. request.user.default_avatar(request.settings)
  145. message = Message(_("Only gif, jpeg and png files are allowed for member avatars."), 'error')
  146. else:
  147. message = Message(form.non_field_errors()[0], 'error')
  148. else:
  149. form = UploadAvatarForm(request=request)
  150. return request.theme.render_to_response('usercp/avatar_upload.html',
  151. context_instance=RequestContext(request, {
  152. 'message': message,
  153. 'form': FormLayout(form),
  154. 'tab': 'avatar',
  155. }));
  156. @block_guest
  157. @avatar_view
  158. def crop(request, upload=False):
  159. if upload and (not request.user.avatar_temp or not 'upload' in request.settings.avatars_types):
  160. return error404(request)
  161. if not upload and request.user.avatar_type != 'upload':
  162. request.messages.set_flash(Message(_("Crop Avatar option is avaiable only when you use uploaded image as your avatar.")), 'error', 'usercp_avatar')
  163. return redirect(reverse('usercp_avatar'))
  164. message = request.messages.get_message('usercp_avatar')
  165. if request.method == 'POST':
  166. if request.csrf.request_secure(request):
  167. try:
  168. image_path = settings.MEDIA_ROOT + 'avatars/'
  169. if upload:
  170. source = Image.open(image_path + request.user.avatar_temp)
  171. else:
  172. source = Image.open(image_path + request.user.avatar_original)
  173. width, height = source.size
  174. aspect = float(width) / float(request.POST['crop_b'])
  175. crop_x = int(aspect * float(request.POST['crop_x']))
  176. crop_y = int(aspect * float(request.POST['crop_y']))
  177. crop_w = int(aspect * float(request.POST['crop_w']))
  178. crop = source.crop((crop_x, crop_y, crop_x + crop_w, crop_y + crop_w))
  179. if upload:
  180. image_name, image_extension = path(request.user.avatar_temp).splitext()
  181. else:
  182. image_name, image_extension = path(request.user.avatar_original).splitext()
  183. image_name = '%s_%s%s' % (request.user.pk, random_string(8), image_extension)
  184. resizeimage(crop, settings.AVATAR_SIZES[0], image_path + image_name, info=source.info, format=source.format)
  185. for size in settings.AVATAR_SIZES[1:]:
  186. resizeimage(crop, size, image_path + str(size) + '_' + image_name, info=source.info, format=source.format)
  187. request.user.delete_avatar_image()
  188. if upload:
  189. request.user.delete_avatar_original()
  190. request.user.avatar_type = 'upload'
  191. request.user.avatar_original = '%s_org_%s%s' % (request.user.pk, random_string(8), image_extension)
  192. source.save(image_path + request.user.avatar_original)
  193. request.user.delete_avatar_temp()
  194. request.user.avatar_image = image_name
  195. request.user.save(force_update=True)
  196. request.messages.set_flash(Message(_("Your avatar has been cropped.")), 'success', 'usercp_avatar')
  197. return redirect(reverse('usercp_avatar'))
  198. except Exception:
  199. message = Message(_("Form contains errors."), 'error')
  200. else:
  201. message = Message(_("Request authorisation is invalid."), 'error')
  202. return request.theme.render_to_response('usercp/avatar_crop.html',
  203. context_instance=RequestContext(request, {
  204. 'message': message,
  205. 'after_upload': upload,
  206. 'avatar_size': settings.AVATAR_SIZES[0],
  207. 'source': 'avatars/%s' % (request.user.avatar_temp if upload else request.user.avatar_original),
  208. 'tab': 'avatar',
  209. }));