296 lines
11 KiB
Python
296 lines
11 KiB
Python
# -*- coding: utf-8 -*-
|
|
import codecs
|
|
import datetime
|
|
import logging
|
|
import os
|
|
import re
|
|
import urllib
|
|
import zipfile
|
|
import pytz
|
|
from django.apps import apps
|
|
from django.contrib import messages
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.core.exceptions import PermissionDenied
|
|
from django.http import FileResponse, Http404
|
|
from django.urls import reverse_lazy
|
|
from django.utils import timezone
|
|
from django.utils.decorators import method_decorator
|
|
from django.utils.translation import ugettext as _
|
|
from django.views import generic
|
|
|
|
from .emails import NewSubmissionMail
|
|
from .forms import UploadForm
|
|
|
|
app_config = apps.get_containing_app_config(__package__)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class ListView(generic.ListView):
|
|
template_name = 'dav_submission/list.html'
|
|
|
|
def get_queryset(self):
|
|
base_path = app_config.settings.upload_path
|
|
metadata_file_name = app_config.settings.metadata_file_name
|
|
|
|
subdirs = os.listdir(base_path)
|
|
all_metadata = {}
|
|
for subdir in subdirs:
|
|
metadata_file_path = os.path.join(base_path, subdir, metadata_file_name)
|
|
if hasattr(urllib, 'quote_plus'):
|
|
pk = urllib.quote_plus(subdir)
|
|
else:
|
|
pk = urllib.parse.quote_plus(subdir)
|
|
metadata = {
|
|
'pk': pk,
|
|
'name': None,
|
|
'email_address': None,
|
|
'title': None,
|
|
'group': None,
|
|
'timestamp': None,
|
|
}
|
|
with codecs.open(metadata_file_path, encoding='utf-8') as f:
|
|
for line in f:
|
|
mo = re.match(r'^Absender: (.*) <(.*)>$', line)
|
|
if mo is not None:
|
|
metadata['name'] = mo.group(1)
|
|
metadata['email_address'] = mo.group(2)
|
|
continue
|
|
mo = re.match(r'^Titel: (.*)$', line)
|
|
if mo is not None:
|
|
metadata['title'] = mo.group(1)
|
|
continue
|
|
mo = re.match(r'^Gruppe: (.*)$', line)
|
|
if mo is not None:
|
|
metadata['group'] = mo.group(1)
|
|
continue
|
|
mo = re.match(r'^Datum: ([0-9]{2}.[0-9]{2}.[0-9]{4}) ([0-9]{2}:[0-9]{2}:[0-9]{2})[\s]*(.*)$', line)
|
|
if mo is not None:
|
|
date_str = mo.group(1)
|
|
time_str = mo.group(2)
|
|
zone_str = mo.group(3)
|
|
if not zone_str:
|
|
zone_str = timezone.get_current_timezone_name()
|
|
datetime_str = '{} {}'.format(date_str, time_str)
|
|
timestamp = datetime.datetime.strptime(datetime_str, '%d.%m.%Y %H:%M:%S')
|
|
tz = pytz.timezone(zone_str)
|
|
metadata['timestamp'] = tz.localize(timestamp)
|
|
continue
|
|
mo = re.match(r'^Beschreibung:$', line)
|
|
if mo is not None:
|
|
break
|
|
|
|
all_metadata[subdir] = metadata
|
|
|
|
sorted(subdirs)
|
|
qs = [all_metadata[subdir] for subdir in subdirs]
|
|
return qs
|
|
|
|
@method_decorator(login_required)
|
|
def dispatch(self, request, *args, **kwargs):
|
|
permission_group = app_config.settings.download_group
|
|
if not request.user.groups.filter(name=permission_group).exists():
|
|
raise PermissionDenied()
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
|
|
class DownloadView(generic.DetailView):
|
|
def get(self, request, *args, **kwargs):
|
|
cached_zip_file_name = app_config.settings.cached_zip_file_name
|
|
base_path = app_config.settings.upload_path
|
|
|
|
pk = kwargs.get('pk')
|
|
if hasattr(urllib, 'unquote_plus'):
|
|
subdir = urllib.unquote_plus(pk)
|
|
else:
|
|
subdir = urllib.parse.unquote_plus(pk)
|
|
|
|
submission_dir = os.path.join(base_path, subdir)
|
|
|
|
if not os.path.isdir(submission_dir):
|
|
raise Http404()
|
|
|
|
cached_zip = os.path.join(submission_dir, cached_zip_file_name)
|
|
if not os.path.isfile(cached_zip):
|
|
with open(cached_zip, 'wb') as cache_f:
|
|
with zipfile.ZipFile(cache_f, 'w') as z:
|
|
for filename in os.listdir(submission_dir):
|
|
if filename == cached_zip_file_name:
|
|
continue
|
|
z.write(os.path.join(submission_dir, filename), os.path.join(subdir, filename))
|
|
|
|
zip_f = open(cached_zip, 'rb')
|
|
|
|
file_name = subdir
|
|
file_ext = '.zip'
|
|
mime_type = 'application/zip'
|
|
disposition_file_name = '{file_name}{file_ext}'.format(
|
|
file_name=file_name,
|
|
file_ext=file_ext,
|
|
)
|
|
|
|
response = FileResponse(streaming_content=zip_f, content_type=mime_type)
|
|
disposition_header = 'attachment; filename="{}"'.format(disposition_file_name)
|
|
response['Content-Disposition'] = disposition_header
|
|
|
|
return response
|
|
|
|
@method_decorator(login_required)
|
|
def dispatch(self, request, *args, **kwargs):
|
|
permission_group = app_config.settings.download_group
|
|
if not request.user.groups.filter(name=permission_group).exists():
|
|
raise PermissionDenied()
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
|
|
class UploadView(generic.edit.FormView):
|
|
initial = {
|
|
# 'name': u'heinzel',
|
|
# 'email_address': 'heinzel@heinzelwelt.de',
|
|
# 'group': 'Alte Maschinen',
|
|
# 'title': u'Mein Beitrag',
|
|
# 'description': 'Foobar',
|
|
# 'accepted': True,
|
|
}
|
|
template_name = 'dav_submission/upload_form.html'
|
|
form_class = UploadForm
|
|
success_url = reverse_lazy('dav_submission:success')
|
|
|
|
def _sanitize_filename(self, filename):
|
|
max_length = None
|
|
discard_chars = ''
|
|
replace_chars = {
|
|
'ä': 'ae',
|
|
'ö': 'oe',
|
|
'ü': 'ue',
|
|
'ß': 'ss',
|
|
'Ä': 'Ae',
|
|
'Ö': 'Oe',
|
|
'Ü': 'Ue',
|
|
}
|
|
allowed_chars = ('abcdefghijklmnopqrstuvwxyz'
|
|
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
|
'0123456789'
|
|
'._-')
|
|
non_allowed_substitute = '_'
|
|
space_substitute = '_'
|
|
|
|
if space_substitute is None:
|
|
space_substitute = non_allowed_substitute
|
|
|
|
r = ''
|
|
for c in filename:
|
|
if c in discard_chars:
|
|
continue
|
|
elif c in replace_chars:
|
|
r += replace_chars[c]
|
|
elif allowed_chars is not None and c in allowed_chars:
|
|
r += c
|
|
elif allowed_chars is None and c.isalnum():
|
|
r += c
|
|
elif c.isspace():
|
|
r += space_substitute
|
|
else:
|
|
r += non_allowed_substitute
|
|
|
|
return r[:max_length]
|
|
|
|
def get_context_data(self, **kwargs):
|
|
c = super().get_context_data(**kwargs)
|
|
c['show_upload_form'] = app_config.settings.enable_upload
|
|
return c
|
|
|
|
def form_valid(self, form):
|
|
base_path = app_config.settings.upload_path
|
|
|
|
subdir_format_str = '{datetime}--{title}'
|
|
now = timezone.now()
|
|
subdir_format_kwargs = {'datetime': now.strftime('%Y-%m-%d--%H%M%S'),
|
|
'date': now.strftime('%Y-%m-%d'),
|
|
'time': now.strftime('%H-%M-%S'),
|
|
'title': form.cleaned_data['title']}
|
|
subdir_name = self._sanitize_filename(
|
|
subdir_format_str.format(**subdir_format_kwargs)
|
|
)
|
|
subdir_path = os.path.join(base_path, subdir_name)
|
|
|
|
if os.path.isdir(subdir_path):
|
|
message = _('Es gibt bereits einen Beitrag mit dem Titel "%(title)s".') % subdir_format_kwargs
|
|
messages.error(self.request, message)
|
|
form.add_error('title', message)
|
|
return self.render_to_response(self.get_context_data(form=form))
|
|
|
|
os.makedirs(subdir_path)
|
|
|
|
try:
|
|
metadata_format_str = """Absender: {name} <{email_address}>
|
|
Gruppe: {group}
|
|
Datum: {date} {time}
|
|
Titel: {title}
|
|
Beschreibung:
|
|
{description}
|
|
"""
|
|
metadata_format_kwargs = {
|
|
'date': timezone.localtime(now).strftime('%d.%m.%Y'),
|
|
'time': timezone.localtime(now).strftime('%H:%M:%S') + ' ' + timezone.get_current_timezone_name(),
|
|
'name': form.cleaned_data['name'],
|
|
'email_address': form.cleaned_data['email_address'],
|
|
'group': form.cleaned_data['group'],
|
|
'title': form.cleaned_data['title'],
|
|
'description': form.cleaned_data['description'],
|
|
}
|
|
|
|
metadata = metadata_format_str.format(**metadata_format_kwargs)
|
|
metadata_file_name = app_config.settings.metadata_file_name
|
|
metadata_file_path = os.path.join(subdir_path, metadata_file_name)
|
|
|
|
with codecs.open(metadata_file_path, 'w', encoding='utf-8') as metadata_file:
|
|
metadata_file.write(metadata)
|
|
except Exception as e:
|
|
message = _('Jetzt ist irgendwas schief gelaufen.')
|
|
messages.error(self.request, message)
|
|
logger.error('dav_submission.views.UploadView.form_valid(): Catched Exception #2: %s', str(e))
|
|
return super().form_valid(form)
|
|
|
|
try:
|
|
for input_file in form.files.getlist('files'):
|
|
file_name = self._sanitize_filename(input_file.name)
|
|
file_path = os.path.join(subdir_path, file_name)
|
|
|
|
if os.path.exists(file_path):
|
|
message = _('Die Datei %(name)s haben wir bereits.') % {'name': input_file.name}
|
|
messages.error(self.request, message)
|
|
continue
|
|
|
|
with open(file_path, 'wb+') as local_file:
|
|
for chunk in input_file.chunks():
|
|
local_file.write(chunk)
|
|
|
|
size = os.path.getsize(file_path)
|
|
if size > (1024 * 1024):
|
|
size_str = '%s MiB' % ('%.3f' % (size / 1024.0 / 1024.0)).rstrip('0').rstrip('.')
|
|
elif size > 1024:
|
|
size_str = '%s KiB' % ('%.3f' % (size / 1024.0)).rstrip('0').rstrip('.')
|
|
else:
|
|
size_str = '%d Byte' % size
|
|
message = _('Datei erfolgreich hochgeladen: %(name)s (%(size)s)') % {'name': input_file.name,
|
|
'size': size_str}
|
|
messages.success(self.request, message)
|
|
except Exception as e:
|
|
message = _('Jetzt ist irgendwas schief gelaufen.')
|
|
messages.error(self.request, message)
|
|
logger.error('dav_submission.views.UploadView.form_valid(): Catched Exception #3: %s', str(e))
|
|
return super().form_valid(form)
|
|
|
|
mail = NewSubmissionMail(metadata_format_kwargs)
|
|
mail.send()
|
|
return super().form_valid(form)
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
if not app_config.settings.enable_upload:
|
|
raise PermissionDenied(_('Der Upload ist noch nicht freigeschaltet.'))
|
|
return super().post(request, *args, **kwargs)
|
|
|
|
|
|
class SuccessView(generic.TemplateView):
|
|
template_name = 'dav_submission/success.html'
|