From 057388e3b44b5fe42b133fe030c41aa6a254de58 Mon Sep 17 00:00:00 2001 From: Dirk Alders Date: Sat, 10 Apr 2021 19:58:45 +0200 Subject: [PATCH] Initial Attachment Management --- __init__.py | 5 ++ context.py | 32 +++++++-- templates/mycreole/delete.html | 17 +++++ templates/mycreole/manageuploads.html | 29 ++++++++ templates/mycreole/upload.html | 3 + urls.py | 1 + views.py | 95 ++++++++++++++++++++++++++- 7 files changed, 175 insertions(+), 7 deletions(-) create mode 100644 templates/mycreole/delete.html create mode 100644 templates/mycreole/manageuploads.html diff --git a/__init__.py b/__init__.py index 0942a76..2098a35 100644 --- a/__init__.py +++ b/__init__.py @@ -89,6 +89,11 @@ def render(request, text, attachment_target_path, next_anchor=''): 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=''): 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) diff --git a/context.py b/context.py index 298ca0a..c738181 100644 --- a/context.py +++ b/context.py @@ -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 -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) user_menubar(context[context.MENUBAR], request) - context[context.NAVIGATIONBAR].append_entry(*themes.empty_entry_parameters(request)) - context[context.ACTIONBAR].append_entry(*themes.empty_entry_parameters(request)) + navigationbar(context, request) + add_new(request, context[context.ACTIONBAR], rel_path, kwargs.get('next')) for key in kwargs: 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 + ) diff --git a/templates/mycreole/delete.html b/templates/mycreole/delete.html new file mode 100644 index 0000000..4e86a56 --- /dev/null +++ b/templates/mycreole/delete.html @@ -0,0 +1,17 @@ +{% extends "themes/"|add:settings.page_theme|add:"/base.html" %} +{% load static %} +{% load i18n %} + +{% block content %} +

Delete Attachment

+Are you sure deleting {{ filename }}? + +
+ {% csrf_token %} + + + +
+ + +{% endblock content %} diff --git a/templates/mycreole/manageuploads.html b/templates/mycreole/manageuploads.html new file mode 100644 index 0000000..7328353 --- /dev/null +++ b/templates/mycreole/manageuploads.html @@ -0,0 +1,29 @@ +{% extends "themes/"|add:settings.page_theme|add:"/base.html" %} +{% load static %} +{% load i18n %} + +{% block content %} + +

Attachments

+ + + + + + + + {% for key, value in attachments.items %} + + + + + + {% endfor %} + +
NameSizeActions
{{ key }}{{ value.size }} + {% for icon, url, alt in value.actions %} + {{ + {% endfor %} +
+ +{% endblock content %} diff --git a/templates/mycreole/upload.html b/templates/mycreole/upload.html index e0819f2..ca375d0 100644 --- a/templates/mycreole/upload.html +++ b/templates/mycreole/upload.html @@ -5,6 +5,9 @@
{% csrf_token %} + {% if rel_path_is_directory %} +

+ {% endif %} diff --git a/urls.py b/urls.py index 53c0837..fe1f416 100644 --- a/urls.py +++ b/urls.py @@ -5,5 +5,6 @@ from . import views urlpatterns = [ path('attachment/', views.mycreole_attachment, name='mycreole-attachment'), path('upload/', views.mycreole_upload, name='mycreole-upload'), + path('delete/', views.mycreole_delete, name='mycreole-delete'), path('manageuploads/', views.mycreole_manageuploads, name='mycreole-manageuploads'), ] diff --git a/views.py b/views.py index b32774c..6e7d13f 100644 --- a/views.py +++ b/views.py @@ -2,11 +2,14 @@ from .context import context_adaption from django.conf import settings from django.shortcuts import render, redirect, HttpResponse from django.http import HttpResponseNotFound, HttpResponseForbidden +import fstools import importlib import mimetypes +from mycreole import url_upload, url_attachment, url_delete import logging import mycreole import os +import stringtools import themes from django.contrib import messages @@ -26,6 +29,13 @@ def get_method(import_string): 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 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) @@ -66,10 +76,20 @@ def mycreole_upload(request, rel_path): if access('modify', request, rel_path): if not request.POST: 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) else: + filename = request.POST.get('filename') full_path = mycreole.get_full_path(rel_path) + if filename is not None: + full_path = os.path.join(full_path, filename) try: os.makedirs(os.path.dirname(full_path), exist_ok=True) except PermissionError: @@ -77,12 +97,81 @@ def mycreole_upload(request, rel_path): else: with open(full_path, 'wb') as fh: fh.write(request.FILES['file'].read()) + messages.info(request, 'File %s successfully uploaded.' % os.path.basename(full_path)) return redirect(request.POST.get('next', '/')) else: messages.error(request, "Upload: Access denied!") 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): - messages.error(request, 'Manage Uploads: Not yet implemented!') - return redirect(request.GET.get('next', '/')) + context = themes.Context(request) # needs to be executed first because of time mesurement + 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)