230 lines
8.3 KiB
Python
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())
|