bgneal@695: """Views for the user_photos application.""" bgneal@722: import json bgneal@722: bgneal@695: from django.conf import settings bgneal@704: from django.contrib.auth import get_user_model bgneal@695: from django.contrib.auth.decorators import login_required bgneal@722: from django.http import (HttpResponse, HttpResponseForbidden, bgneal@971: HttpResponseNotAllowed, JsonResponse) bgneal@704: from django.shortcuts import render, redirect, get_object_or_404 bgneal@1158: from django.views.generic import DetailView bgneal@704: from django.views.generic import ListView bgneal@710: from django.views.decorators.http import require_POST bgneal@704: from django.utils.decorators import method_decorator bgneal@710: from django.contrib import messages bgneal@695: bgneal@971: from user_photos.forms import HotLinkImageForm, UploadForm bgneal@704: from user_photos.models import Photo bgneal@718: from user_photos.s3 import delete_photos bgneal@695: bgneal@695: bgneal@695: @login_required bgneal@695: def upload(request): bgneal@695: """This view function receives an uploaded image file from a user. bgneal@695: The photo will be resized if necessary and a thumbnail image will be bgneal@695: created. The image and thumbnail will then be uploaded to the Amazon bgneal@695: S3 service for storage. bgneal@695: bgneal@695: """ bgneal@695: form = None bgneal@696: uploads_enabled = settings.USER_PHOTOS_ENABLED bgneal@695: bgneal@695: if uploads_enabled: bgneal@695: if request.method == 'POST': bgneal@696: form = UploadForm(request.POST, request.FILES, user=request.user) bgneal@695: if form.is_valid(): bgneal@696: photo = form.save() bgneal@696: return redirect(photo) bgneal@695: else: bgneal@696: form = UploadForm(user=request.user) bgneal@695: bgneal@695: return render(request, 'user_photos/upload_form.html', { bgneal@695: 'enabled': uploads_enabled, bgneal@695: 'form': form, bgneal@1158: 'V3_DESIGN': True, bgneal@695: }, bgneal@695: status=200 if uploads_enabled else 503) bgneal@704: bgneal@704: bgneal@722: def upload_ajax(request): bgneal@722: """This view is the ajax version of the upload function, above. bgneal@722: bgneal@722: We return a JSON object response to the client with the following name bgneal@722: & value pairs: bgneal@722: 'success' : false for failure and true for success bgneal@722: 'msg' : string error message if success is false bgneal@722: 'url' : the image URL as a string if success bgneal@722: bgneal@722: If a non-200 status code is returned the response will simply be a text bgneal@722: string error message. bgneal@722: bgneal@722: """ bgneal@722: if not request.user.is_authenticated(): bgneal@724: return HttpResponseForbidden('Please login to use this service') bgneal@722: if not request.is_ajax() or request.method != 'POST': bgneal@724: return HttpResponseNotAllowed('This method is not allowed') bgneal@722: bgneal@722: ret = {'success': False, 'msg': '', 'url': ''} bgneal@722: if settings.USER_PHOTOS_ENABLED: bgneal@722: form = UploadForm(request.POST, request.FILES, user=request.user) bgneal@722: if form.is_valid(): bgneal@722: photo = form.save() bgneal@722: ret['success'] = True bgneal@722: ret['url'] = photo.url bgneal@722: else: bgneal@722: # gather form error messages bgneal@722: errors = [] bgneal@722: non_field_errors = form.non_field_errors().as_text() bgneal@722: if non_field_errors: bgneal@722: errors.append(non_field_errors) bgneal@722: for field_errors in form.errors.values(): bgneal@722: errors.append(field_errors.as_text()) bgneal@722: ret['msg'] = '\n'.join(errors) bgneal@722: else: bgneal@722: ret['msg'] = 'Photo uploads are temporarily disabled' bgneal@722: bgneal@722: return HttpResponse(json.dumps(ret), content_type='application/json') bgneal@722: bgneal@722: bgneal@1096: def upload_ajax_v3(request): bgneal@1096: """This view is the V3 ajax version of the upload function, above. bgneal@1096: bgneal@1096: We return a JSON object response to the client with the following name bgneal@1096: & value pairs: bgneal@1096: 'msg' : string error message if success is false bgneal@1096: 'url' : the image URL as a string if success bgneal@1096: """ bgneal@1096: ret = {'error_msg': '', 'url': ''} bgneal@1096: status_code = 400 bgneal@1096: bgneal@1096: if not request.user.is_authenticated(): bgneal@1096: ret['error_msg'] = 'Please login to use this service' bgneal@1096: return JsonResponse(ret, status=403) bgneal@1096: if not request.is_ajax() or request.method != 'POST': bgneal@1096: ret['error_msg'] = 'This method is not allowed' bgneal@1096: return JsonResponse(ret, status=405) bgneal@1096: bgneal@1096: if settings.USER_PHOTOS_ENABLED: bgneal@1096: form = UploadForm(request.POST, request.FILES, user=request.user) bgneal@1096: if form.is_valid(): bgneal@1096: try: bgneal@1096: photo = form.save() bgneal@1096: ret['url'] = photo.url bgneal@1096: status_code = 200 bgneal@1096: except Exception as ex: bgneal@1096: ret['error_msg'] = str(ex) bgneal@1096: status_code = 500 bgneal@1096: else: bgneal@1096: # gather form error messages bgneal@1096: errors = [] bgneal@1096: non_field_errors = form.non_field_errors().as_text() bgneal@1096: if non_field_errors: bgneal@1096: errors.append(non_field_errors) bgneal@1096: for field_errors in form.errors.values(): bgneal@1096: errors.append(field_errors.as_text()) bgneal@1096: ret['msg'] = '\n'.join(errors) bgneal@1096: else: bgneal@1096: ret['msg'] = 'Photo uploads are temporarily disabled' bgneal@1096: status_code = 403 bgneal@1096: bgneal@1096: return JsonResponse(ret, status=status_code) bgneal@1096: bgneal@1096: bgneal@704: class GalleryView(ListView): bgneal@704: """A ListView for displaying a user's photos""" bgneal@704: bgneal@704: template_name = 'user_photos/gallery.html' bgneal@704: context_object_name = 'photos' bgneal@704: paginate_by = 50 bgneal@704: allow_empty = True bgneal@704: bgneal@704: def get_queryset(self): bgneal@704: self.gallery_owner = get_object_or_404(get_user_model(), bgneal@704: username=self.kwargs['username']) bgneal@704: return Photo.objects.filter(user=self.gallery_owner).order_by('-upload_date') bgneal@704: bgneal@704: def get_context_data(self, **kwargs): bgneal@704: context = super(GalleryView, self).get_context_data(**kwargs) bgneal@704: context['gallery_owner'] = self.gallery_owner bgneal@1158: context['V3_DESIGN'] = True bgneal@1158: context['page'] = context['page_obj'] bgneal@704: return context bgneal@704: bgneal@704: @method_decorator(login_required) bgneal@704: def dispatch(self, *args, **kwargs): bgneal@704: return super(GalleryView, self).dispatch(*args, **kwargs) bgneal@710: bgneal@710: bgneal@1158: class PhotoView(DetailView): bgneal@1158: """A DetailView for displaying a user's photo""" bgneal@1158: bgneal@1158: model = Photo bgneal@1158: bgneal@1158: def get_context_data(self, **kwargs): bgneal@1158: context = super(PhotoView, self).get_context_data(**kwargs) bgneal@1158: context['V3_DESIGN'] = True bgneal@1158: return context bgneal@1158: bgneal@1158: bgneal@710: @login_required bgneal@710: @require_POST bgneal@710: def delete(request): bgneal@718: """A view function to allow a user to delete their own photos.""" bgneal@718: bgneal@718: ret_view, username = 'user_photos-gallery', request.user.username bgneal@718: bgneal@718: if not settings.USER_PHOTOS_ENABLED: bgneal@718: messages.error(request, "This function is disabled temporarily") bgneal@718: return redirect(ret_view, username) bgneal@718: bgneal@710: photo_ids = [] bgneal@710: for photo_id in request.POST.getlist('photo_id'): bgneal@710: try: bgneal@710: n = int(photo_id) bgneal@710: except ValueError: bgneal@710: continue bgneal@710: photo_ids.append(n) bgneal@710: bgneal@710: count = 0 bgneal@710: if photo_ids: bgneal@710: qs = Photo.objects.filter(user=request.user, pk__in=photo_ids) bgneal@718: count = len(qs) bgneal@718: if count: bgneal@718: delete_photos(qs) bgneal@718: qs.delete() bgneal@710: bgneal@718: msg = "{} photo{} deleted".format(count, '' if count == 1 else 's') bgneal@718: messages.add_message(request, bgneal@718: messages.SUCCESS if count > 0 else messages.WARNING, bgneal@718: msg) bgneal@710: bgneal@718: return redirect(ret_view, username) bgneal@971: bgneal@971: bgneal@971: def hotlink_image(request): bgneal@971: """This view is responsible for accepting an image URL from a user and bgneal@971: converting it to a URL pointing into our S3 bucket if necessary. bgneal@971: bgneal@971: We return a JSON object response to the client with the following name bgneal@971: & value pairs: bgneal@971: 'error_msg': string error message if an error occurred bgneal@971: 'url': the image URL as a string if success bgneal@971: """ bgneal@971: ret = {'error_msg': '', 'url': ''} bgneal@971: status_code = 400 bgneal@971: bgneal@971: if not request.user.is_authenticated(): bgneal@971: ret['error_msg'] = 'Please login to use this service' bgneal@972: return JsonResponse(ret, status=403) bgneal@971: if not request.is_ajax() or request.method != 'POST': bgneal@971: ret['error_msg'] = 'This method is not allowed' bgneal@972: return JsonResponse(ret, status=405) bgneal@971: bgneal@971: if settings.USER_PHOTOS_ENABLED: bgneal@972: form = HotLinkImageForm(request.POST, user=request.user) bgneal@971: if form.is_valid(): bgneal@971: try: bgneal@971: ret['url'] = form.save() bgneal@971: status_code = 200 bgneal@971: except Exception as ex: bgneal@971: ret['error_msg'] = str(ex) bgneal@971: status_code = 500 bgneal@971: else: bgneal@971: # gather form error messages bgneal@971: errors = [] bgneal@971: non_field_errors = form.non_field_errors().as_text() bgneal@971: if non_field_errors: bgneal@971: errors.append(non_field_errors) bgneal@971: for field_errors in form.errors.values(): bgneal@971: errors.append(field_errors.as_text()) bgneal@971: ret['error_msg'] = '\n'.join(errors) bgneal@971: else: bgneal@971: ret['error_msg'] = 'Image linking is temporarily disabled' bgneal@971: status_code = 403 bgneal@971: bgneal@971: return JsonResponse(ret, status=status_code)