fargo/fargo/fargo/views.py

230 lines
8.3 KiB
Python

import urlparse
import urllib
import logging
from json import dumps
from django.views.generic import CreateView, DeleteView, View, TemplateView
from django.core.urlresolvers import reverse
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, resolve_url
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden
from django.core import signing
from django.contrib import messages
from django.contrib.auth import get_user_model, REDIRECT_FIELD_NAME
from django.contrib.auth import logout as auth_logout
from django.contrib.auth import views as auth_views
from django.utils.translation import ugettext as _
from django.utils.decorators import method_decorator
from django_tables2 import SingleTableMixin
from . import models, forms, tables
try:
from mellon.utils import get_idps
except ImportError:
get_idps = lambda: []
class Logger(object):
def __init__(self, *args, **kwargs):
self.logger = logging.getLogger(__name__)
class CommonUpload(Logger, CreateView):
form_class = forms.UploadForm
model = models.Document
template_name = 'fargo/upload.html'
def get_form_kwargs(self, **kwargs):
kwargs = super(CommonUpload, self).get_form_kwargs(**kwargs)
kwargs['user'] = self.request.user
return kwargs
def form_valid(self, form):
result = super(CommonUpload, self).form_valid(form)
self.logger.info('user uploaded file %r(%s)',
self.object.document_filename, self.object.pk)
return result
class Upload(CommonUpload):
def get_success_url(self):
homepage = reverse('home')
return self.request.GET.get(REDIRECT_FIELD_NAME, homepage)
def post(self, request, *args, **kwargs):
if 'cancel' in request.POST:
return HttpResponseRedirect(self.get_success_url())
return super(Upload, self).post(request, *args, **kwargs)
class Documents(object):
def get_queryset(self):
return models.Document.objects.filter(user=self.request.user)
class Homepage(Documents, SingleTableMixin, CommonUpload):
'''Show documents of users, eventually paginate and sort them.
If a pick_url parameter is passed, allow picking a file and returning a
download URL to the pick_url.
'''
template_name = 'fargo/home.html'
form_class = forms.UploadForm
table_class = tables.DocumentTable
table_pagination = {
'per_page': 5,
}
def get_success_url(self):
return ''
def can_pick(self):
'''Check if user is currently picking a file'''
# FIXME: we should check the pick URL is authorized
return 'pick' in self.request.GET
def get_context_data(self, **kwargs):
ctx = super(Homepage, self).get_context_data(**kwargs)
if self.can_pick():
ctx['pick'] = True
return ctx
def post(self, request, *args, **kwargs):
if self.can_pick() and 'cancel' in request.POST:
url = request.GET['pick']
scheme, netloc, path, query, fragment = urlparse.urlsplit(url)
query = urlparse.parse_qs(query)
query['cancel'] = ['']
query = urllib.urlencode(query)
url = urlparse.urlunsplit((scheme, netloc, path, query, fragment))
return HttpResponseRedirect(url)
return super(Homepage, self).post(request, *args, **kwargs)
class Document(TemplateView):
template_name = 'fargo/document.html'
class Delete(Logger, DeleteView):
model = models.Document
def delete(self, request, *args, **kwargs):
result = super(Delete, self).delete(request, *args, **kwargs)
messages.info(request, _('File %s deleted') % self.object.document_filename)
self.logger.info('user deleted file %r(%s)',
self.object.document_filename, self.object.pk)
return result
def get_success_url(self):
return '../..?%s' % self.request.META['QUERY_STRING']
class Pick(Logger, View):
http_method_allowed = ['post']
def post(self, request, pk):
document = get_object_or_404(models.Document, pk=pk, user=self.request.user)
pick = self.request.GET['pick']
token = signing.dumps(document.pk)
url = reverse('remote_download', kwargs={'filename': document.document_filename})
url += '?%s' % urllib.urlencode({'token': token})
url = request.build_absolute_uri(url)
scheme, netloc, path, qs, fragment = urlparse.urlsplit(pick)
qs = urlparse.parse_qs(qs)
print url
qs['url'] = url
qs = urllib.urlencode(qs, True)
redirect = urlparse.urlunsplit((scheme, netloc, path, qs, fragment))
print 'redirect', redirect
self.logger.info('user picked file %r(%s) returned to %s',
document.document_filename, document.pk, pick)
return HttpResponseRedirect(redirect)
class Download(View):
def get(self, request, pk, filename):
document = get_object_or_404(models.Document, pk=pk, user=self.request.user)
return self.return_document(document)
def return_document(self, document):
response = HttpResponse(document.document_file.chunks(),
content_type='application/octet-stream')
response['Content-disposition'] = 'attachment'
return response
class RemoteDownload(Download):
'''Allow downloading any file given the URL contains a signed token'''
def get(self, request, filename):
if 'token' not in request.GET:
return HttpResponseForbidden('missing token')
# FIXME: maybe we should mark token as invalid after use using the
# cache ?
token = request.GET['token']
# token are valid only 1 minute
try:
pk = signing.loads(token, max_age=60)
except signing.SignatureExpired:
return HttpResponseForbidden('token has expired')
except signing.BadSignature:
return HttpResponseForbidden('token signature is invalid')
document = get_object_or_404(models.Document, pk=pk)
return self.return_document(document)
class JSONP(Documents, View):
def get_data(self, request):
d = []
for document in self.get_queryset():
url = reverse('download', kwargs={'pk': document.pk,
'filename': document.document_filename})
url = request.build_absolute_uri(url)
d.append({
'filename': document.document_filename,
'url': url,
})
return d
def get(self, request):
callback = request.GET.get('callback', 'callback')
s = '%s(%s)' % (callback.encode('ascii'),
dumps(self.get_data(request)))
return HttpResponse(s, content_type='application/javascript')
class JSON(JSONP):
def get(self, request):
username = request.GET.get('username')
if username:
User = get_user_model()
request.user = get_object_or_404(User, username=username)
elif not request.user.is_authenticated():
return method_decorator(login_required)(JSON.get)(self, request)
response = HttpResponse(dumps(self.get_data(request)),
content_type='application/json')
response['Access-Control-Allow-Origin'] = '*'
return response
def login(request, *args, **kwargs):
if any(get_idps()):
if not 'next' in request.GET:
return HttpResponseRedirect(resolve_url('mellon_login'))
return HttpResponseRedirect(resolve_url('mellon_login') + '?next='
+ urllib.quote(request.GET.get('next')))
return auth_views.login(request, *args, **kwargs)
def logout(request, next_page=None):
if any(get_idps()):
return HttpResponseRedirect(resolve_url('mellon_logout'))
auth_logout(request)
if next_page is not None:
next_page = resolve_url(next_page)
else:
next_page = '/'
return HttpResponseRedirect(next_page)
home = login_required(Homepage.as_view())
document = login_required(Document.as_view())
download = login_required(Download.as_view())
upload = login_required(Upload.as_view())
remote_download = RemoteDownload.as_view()
delete = login_required(Delete.as_view())
pick = login_required(Pick.as_view())
jsonp = login_required(JSONP.as_view())
json = login_required(JSON.as_view())