Initial Attachment Management

This commit is contained in:
Dirk Alders 2021-04-10 19:58:45 +02:00
parent 5f30383c83
commit 057388e3b4
7 changed files with 175 additions and 7 deletions

View File

@ -89,6 +89,11 @@ def render(request, text, attachment_target_path, next_anchor=''):
return creole.creole2html(text) return creole.creole2html(text)
def url_delete(request, rel_path, next_anchor=''):
nxt = request.GET.get('next', request.get_full_path())
return reverse('mycreole-delete', kwargs={'rel_path': rel_path}) + '?next=%s' % nxt + ('' if not next_anchor else '#%s' % next_anchor)
def url_upload(request, rel_path, next_anchor=''): def url_upload(request, rel_path, next_anchor=''):
nxt = request.GET.get('next', request.get_full_path()) nxt = request.GET.get('next', request.get_full_path())
return reverse('mycreole-upload', kwargs={'rel_path': rel_path}) + '?next=%s' % nxt + ('' if not next_anchor else '#%s' % next_anchor) return reverse('mycreole-upload', kwargs={'rel_path': rel_path}) + '?next=%s' % nxt + ('' if not next_anchor else '#%s' % next_anchor)

View File

@ -1,11 +1,35 @@
from django.utils.translation import gettext as _
import logging
from mycreole import url_upload
from users.context import menubar as user_menubar from users.context import menubar as user_menubar
import themes from patt.context import navigationbar
from themes import color_icon_url
try:
from config import APP_NAME as ROOT_LOGGER_NAME
except ImportError:
ROOT_LOGGER_NAME = 'root'
logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
NEW_ATTACHMENT_UID = 'new_attachment'
def context_adaption(context, request, title, **kwargs): def context_adaption(context, request, title, rel_path, **kwargs):
context.set_additional_title(title) context.set_additional_title(title)
user_menubar(context[context.MENUBAR], request) user_menubar(context[context.MENUBAR], request)
context[context.NAVIGATIONBAR].append_entry(*themes.empty_entry_parameters(request)) navigationbar(context, request)
context[context.ACTIONBAR].append_entry(*themes.empty_entry_parameters(request)) add_new(request, context[context.ACTIONBAR], rel_path, kwargs.get('next'))
for key in kwargs: for key in kwargs:
context[key] = kwargs[key] context[key] = kwargs[key]
logger.debug("context adapted: %s", repr(context))
def add_new(request, bar, rel_path, nxt):
bar.append_entry(
NEW_ATTACHMENT_UID, # uid
_('New Attachment'), # name
color_icon_url(request, 'plus.png'), # icon
url_upload(request, rel_path, nxt), # url
True, # left
False # active
)

View File

@ -0,0 +1,17 @@
{% extends "themes/"|add:settings.page_theme|add:"/base.html" %}
{% load static %}
{% load i18n %}
{% block content %}
<h1>Delete Attachment</h1>
Are you sure deleting {{ filename }}?
<form class="form" method="post">
{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}">
<input type="submit" name="yes" value="{% trans "Yes" %}" class="button" />
<input type="submit" name="no" value="{% trans "No" %}" class="button" />
</form>
{% endblock content %}

View File

@ -0,0 +1,29 @@
{% extends "themes/"|add:settings.page_theme|add:"/base.html" %}
{% load static %}
{% load i18n %}
{% block content %}
<h1>Attachments</h1>
<table>
<tbody>
<tr>
<th>Name</th>
<th>Size</th>
<th>Actions</th>
</tr>
{% for key, value in attachments.items %}
<tr>
<td>{{ key }}</td>
<td>{{ value.size }}</td>
<td>
{% for icon, url, alt in value.actions %}
<a href="{{ url }}"><img src="{{ icon }}" alt={{ alt }}></a>
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock content %}

View File

@ -5,6 +5,9 @@
<form class="form" method="post" enctype="multipart/form-data"> <form class="form" method="post" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="next" value="{{ next }}"> <input type="hidden" name="next" value="{{ next }}">
{% if rel_path_is_directory %}
<p><label for="id_filename">Filename:</label> <input type="text" name="filename" maxlength="150" required id="id_filename"></p>
{% endif %}
<label class="required" for="f_file">Upload</label> <label class="required" for="f_file">Upload</label>
<input class="required" required="required" type="file" name="file" id="f_file"> <input class="required" required="required" type="file" name="file" id="f_file">
<input type="submit" value="{% trans "Upload" %}" class="button" /> <input type="submit" value="{% trans "Upload" %}" class="button" />

View File

@ -5,5 +5,6 @@ from . import views
urlpatterns = [ urlpatterns = [
path('attachment/<path:rel_path>', views.mycreole_attachment, name='mycreole-attachment'), path('attachment/<path:rel_path>', views.mycreole_attachment, name='mycreole-attachment'),
path('upload/<path:rel_path>', views.mycreole_upload, name='mycreole-upload'), path('upload/<path:rel_path>', views.mycreole_upload, name='mycreole-upload'),
path('delete/<path:rel_path>', views.mycreole_delete, name='mycreole-delete'),
path('manageuploads/<path:rel_path>', views.mycreole_manageuploads, name='mycreole-manageuploads'), path('manageuploads/<path:rel_path>', views.mycreole_manageuploads, name='mycreole-manageuploads'),
] ]

View File

@ -2,11 +2,14 @@ from .context import context_adaption
from django.conf import settings from django.conf import settings
from django.shortcuts import render, redirect, HttpResponse from django.shortcuts import render, redirect, HttpResponse
from django.http import HttpResponseNotFound, HttpResponseForbidden from django.http import HttpResponseNotFound, HttpResponseForbidden
import fstools
import importlib import importlib
import mimetypes import mimetypes
from mycreole import url_upload, url_attachment, url_delete
import logging import logging
import mycreole import mycreole
import os import os
import stringtools
import themes import themes
from django.contrib import messages from django.contrib import messages
@ -26,6 +29,13 @@ def get_method(import_string):
return getattr(module, class_str) return getattr(module, class_str)
def get_next(request):
if not request.POST:
return request.GET.get('next', '/')
else:
return request.POST.get('next', '/')
def access(access_type, request, rel_path): def access(access_type, request, rel_path):
def log_warning(access_type, e): def log_warning(access_type, e):
logger.warning('Could not import %s_access method for checking access rights: %s - %s', access_type, type(e).__name__, e) logger.warning('Could not import %s_access method for checking access rights: %s - %s', access_type, type(e).__name__, e)
@ -66,10 +76,20 @@ def mycreole_upload(request, rel_path):
if access('modify', request, rel_path): if access('modify', request, rel_path):
if not request.POST: if not request.POST:
context = themes.Context(request) context = themes.Context(request)
context_adaption(context, request, 'Upload %s' % rel_path, next=request.GET.get('next', '/')) context_adaption(
context,
request,
'Upload %s' % rel_path,
os.path.dirname(rel_path),
rel_path_is_directory=os.path.isdir(mycreole.get_full_path(rel_path)),
next=request.GET.get('next', '/')
)
return render(request, 'mycreole/upload.html', context=context) return render(request, 'mycreole/upload.html', context=context)
else: else:
filename = request.POST.get('filename')
full_path = mycreole.get_full_path(rel_path) full_path = mycreole.get_full_path(rel_path)
if filename is not None:
full_path = os.path.join(full_path, filename)
try: try:
os.makedirs(os.path.dirname(full_path), exist_ok=True) os.makedirs(os.path.dirname(full_path), exist_ok=True)
except PermissionError: except PermissionError:
@ -77,12 +97,81 @@ def mycreole_upload(request, rel_path):
else: else:
with open(full_path, 'wb') as fh: with open(full_path, 'wb') as fh:
fh.write(request.FILES['file'].read()) fh.write(request.FILES['file'].read())
messages.info(request, 'File %s successfully uploaded.' % os.path.basename(full_path))
return redirect(request.POST.get('next', '/')) return redirect(request.POST.get('next', '/'))
else: else:
messages.error(request, "Upload: Access denied!") messages.error(request, "Upload: Access denied!")
return redirect(request.GET.get('next', '/')) return redirect(request.GET.get('next', '/'))
def mycreole_delete(request, rel_path):
context = themes.Context(request) # needs to be executed first because of time mesurement
nxt = get_next(request)
modify_access = access('modify', request, rel_path)
full_path = mycreole.get_full_path(rel_path)
if not modify_access:
messages.error(request, 'Detele Attachment: Access denied!')
else:
if not request.POST:
context_adaption(
context,
request,
'Delete %s' % os.path.basename(rel_path),
os.path.dirname(rel_path),
next=nxt,
filename=os.path.basename(rel_path)
)
return render(request, 'mycreole/delete.html', context=context)
else:
if request.POST.get('yes') is not None:
try:
os.remove(full_path)
except Exception:
messages.error(request, 'Error while deleting file from filesystem.')
logger.exception('Fatal error while deleting Attachment!')
else:
messages.info(request, "Attachment %s deleted..." % os.path.basename(full_path))
else:
messages.info(request, 'Delete request aborded.')
return redirect(nxt)
def mycreole_manageuploads(request, rel_path): def mycreole_manageuploads(request, rel_path):
messages.error(request, 'Manage Uploads: Not yet implemented!') context = themes.Context(request) # needs to be executed first because of time mesurement
return redirect(request.GET.get('next', '/')) nxt = get_next(request)
read_access = access('read', request, rel_path)
modify_access = access('modify', request, rel_path)
#
if not read_access:
messages.error(request, "Manageupload: Access denied!")
return redirect(nxt)
basepath = mycreole.get_full_path(rel_path)
if not os.path.exists(basepath):
logger.info('Creating path for attachments: %s', basepath)
fstools.mkdir(basepath)
logger.debug("Searching for files in %s", basepath)
attachments = {}
for file in fstools.filelist(basepath):
filename = os.path.basename(file)
actions = []
if modify_access:
actions.append((themes.gray_icon_url(request, 'delete.png'), url_delete(request, os.path.join(rel_path, filename)), 'delete'))
actions.append((themes.gray_icon_url(request, 'shuffle.png'), url_upload(request, os.path.join(rel_path, filename)), 'replace'))
actions.append((themes.gray_icon_url(request, 'view.png'), url_attachment(os.path.join(rel_path, filename)), 'view'))
attachments[filename] = {
'size': stringtools.physical_value_repr(os.path.getsize(file), 'B'),
'modify_access': modify_access,
'actions': actions,
}
logger.debug("%d attachments detected: %s", len(attachments), ', '.join(attachments.keys()))
#
mycreole.context.context_adaption(
context,
request,
'Manageuploads for %s' % rel_path,
rel_path,
next=nxt,
attachments=attachments
)
return render(request, 'mycreole/manageuploads.html', context=context)