Browse Source

Initial Attachment Management

make_mycreole_indenpendent_from_patt
Dirk Alders 3 years ago
parent
commit
057388e3b4
7 changed files with 175 additions and 7 deletions
  1. 5
    0
      __init__.py
  2. 28
    4
      context.py
  3. 17
    0
      templates/mycreole/delete.html
  4. 29
    0
      templates/mycreole/manageuploads.html
  5. 3
    0
      templates/mycreole/upload.html
  6. 1
    0
      urls.py
  7. 92
    3
      views.py

+ 5
- 0
__init__.py View File

89
         return creole.creole2html(text)
89
         return creole.creole2html(text)
90
 
90
 
91
 
91
 
92
+def url_delete(request, rel_path, next_anchor=''):
93
+    nxt = request.GET.get('next', request.get_full_path())
94
+    return reverse('mycreole-delete', kwargs={'rel_path': rel_path}) + '?next=%s' % nxt + ('' if not next_anchor else '#%s' % next_anchor)
95
+
96
+
92
 def url_upload(request, rel_path, next_anchor=''):
97
 def url_upload(request, rel_path, next_anchor=''):
93
     nxt = request.GET.get('next', request.get_full_path())
98
     nxt = request.GET.get('next', request.get_full_path())
94
     return reverse('mycreole-upload', kwargs={'rel_path': rel_path}) + '?next=%s' % nxt + ('' if not next_anchor else '#%s' % next_anchor)
99
     return reverse('mycreole-upload', kwargs={'rel_path': rel_path}) + '?next=%s' % nxt + ('' if not next_anchor else '#%s' % next_anchor)

+ 28
- 4
context.py View File

1
+from django.utils.translation import gettext as _
2
+import logging
3
+from mycreole import url_upload
1
 from users.context import menubar as user_menubar
4
 from users.context import menubar as user_menubar
2
-import themes
5
+from patt.context import navigationbar
6
+from themes import color_icon_url
3
 
7
 
8
+try:
9
+    from config import APP_NAME as ROOT_LOGGER_NAME
10
+except ImportError:
11
+    ROOT_LOGGER_NAME = 'root'
12
+logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
4
 
13
 
5
-def context_adaption(context, request, title, **kwargs):
14
+NEW_ATTACHMENT_UID = 'new_attachment'
15
+
16
+
17
+def context_adaption(context, request, title, rel_path, **kwargs):
6
     context.set_additional_title(title)
18
     context.set_additional_title(title)
7
     user_menubar(context[context.MENUBAR], request)
19
     user_menubar(context[context.MENUBAR], request)
8
-    context[context.NAVIGATIONBAR].append_entry(*themes.empty_entry_parameters(request))
9
-    context[context.ACTIONBAR].append_entry(*themes.empty_entry_parameters(request))
20
+    navigationbar(context, request)
21
+    add_new(request, context[context.ACTIONBAR], rel_path, kwargs.get('next'))
10
     for key in kwargs:
22
     for key in kwargs:
11
         context[key] = kwargs[key]
23
         context[key] = kwargs[key]
24
+    logger.debug("context adapted: %s", repr(context))
25
+
26
+
27
+def add_new(request, bar, rel_path, nxt):
28
+    bar.append_entry(
29
+        NEW_ATTACHMENT_UID,                         # uid
30
+        _('New Attachment'),                        # name
31
+        color_icon_url(request, 'plus.png'),        # icon
32
+        url_upload(request, rel_path, nxt),         # url
33
+        True,                                       # left
34
+        False                                       # active
35
+    )

+ 17
- 0
templates/mycreole/delete.html View File

1
+{% extends "themes/"|add:settings.page_theme|add:"/base.html" %}
2
+{% load static %}
3
+{% load i18n %}
4
+
5
+{% block content %}
6
+<h1>Delete Attachment</h1>
7
+Are you sure deleting {{ filename }}?
8
+
9
+    <form class="form" method="post">
10
+      {% csrf_token %}
11
+      <input type="hidden" name="next" value="{{ next }}">
12
+      <input type="submit" name="yes" value="{% trans "Yes" %}" class="button" />
13
+      <input type="submit" name="no" value="{% trans "No" %}" class="button" />
14
+    </form>
15
+
16
+
17
+{% endblock content %}

+ 29
- 0
templates/mycreole/manageuploads.html View File

1
+{% extends "themes/"|add:settings.page_theme|add:"/base.html" %}
2
+{% load static %}
3
+{% load i18n %}
4
+
5
+{% block content %}
6
+
7
+      <h1>Attachments</h1>
8
+      <table>
9
+        <tbody>
10
+          <tr>
11
+          <th>Name</th>
12
+          <th>Size</th>
13
+          <th>Actions</th>
14
+          </tr>
15
+  {% for key, value in attachments.items %}
16
+          <tr>
17
+          <td>{{ key }}</td>
18
+          <td>{{ value.size }}</td>
19
+          <td>
20
+              {% for icon, url, alt in value.actions %}
21
+              <a href="{{ url }}"><img src="{{ icon }}" alt={{ alt }}></a>
22
+              {% endfor %}
23
+          </td>
24
+          </tr>
25
+  {% endfor %}
26
+        </tbody>
27
+      </table>
28
+
29
+{% endblock content %}

+ 3
- 0
templates/mycreole/upload.html View File

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

+ 1
- 0
urls.py View File

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

+ 92
- 3
views.py View File

2
 from django.conf import settings
2
 from django.conf import settings
3
 from django.shortcuts import render, redirect, HttpResponse
3
 from django.shortcuts import render, redirect, HttpResponse
4
 from django.http import HttpResponseNotFound, HttpResponseForbidden
4
 from django.http import HttpResponseNotFound, HttpResponseForbidden
5
+import fstools
5
 import importlib
6
 import importlib
6
 import mimetypes
7
 import mimetypes
8
+from mycreole import url_upload, url_attachment, url_delete
7
 import logging
9
 import logging
8
 import mycreole
10
 import mycreole
9
 import os
11
 import os
12
+import stringtools
10
 import themes
13
 import themes
11
 from django.contrib import messages
14
 from django.contrib import messages
12
 
15
 
26
     return getattr(module, class_str)
29
     return getattr(module, class_str)
27
 
30
 
28
 
31
 
32
+def get_next(request):
33
+    if not request.POST:
34
+        return request.GET.get('next', '/')
35
+    else:
36
+        return request.POST.get('next', '/')
37
+
38
+
29
 def access(access_type, request, rel_path):
39
 def access(access_type, request, rel_path):
30
     def log_warning(access_type, e):
40
     def log_warning(access_type, e):
31
         logger.warning('Could not import %s_access method for checking access rights: %s - %s', access_type, type(e).__name__, e)
41
         logger.warning('Could not import %s_access method for checking access rights: %s - %s', access_type, type(e).__name__, e)
66
     if access('modify', request, rel_path):
76
     if access('modify', request, rel_path):
67
         if not request.POST:
77
         if not request.POST:
68
             context = themes.Context(request)
78
             context = themes.Context(request)
69
-            context_adaption(context, request, 'Upload %s' % rel_path, next=request.GET.get('next', '/'))
79
+            context_adaption(
80
+                context,
81
+                request,
82
+                'Upload %s' % rel_path,
83
+                os.path.dirname(rel_path),
84
+                rel_path_is_directory=os.path.isdir(mycreole.get_full_path(rel_path)),
85
+                next=request.GET.get('next', '/')
86
+            )
70
             return render(request, 'mycreole/upload.html', context=context)
87
             return render(request, 'mycreole/upload.html', context=context)
71
         else:
88
         else:
89
+            filename = request.POST.get('filename')
72
             full_path = mycreole.get_full_path(rel_path)
90
             full_path = mycreole.get_full_path(rel_path)
91
+            if filename is not None:
92
+                full_path = os.path.join(full_path, filename)
73
             try:
93
             try:
74
                 os.makedirs(os.path.dirname(full_path), exist_ok=True)
94
                 os.makedirs(os.path.dirname(full_path), exist_ok=True)
75
             except PermissionError:
95
             except PermissionError:
77
             else:
97
             else:
78
                 with open(full_path, 'wb') as fh:
98
                 with open(full_path, 'wb') as fh:
79
                     fh.write(request.FILES['file'].read())
99
                     fh.write(request.FILES['file'].read())
100
+                messages.info(request, 'File %s successfully uploaded.' % os.path.basename(full_path))
80
             return redirect(request.POST.get('next', '/'))
101
             return redirect(request.POST.get('next', '/'))
81
     else:
102
     else:
82
         messages.error(request, "Upload: Access denied!")
103
         messages.error(request, "Upload: Access denied!")
83
         return redirect(request.GET.get('next', '/'))
104
         return redirect(request.GET.get('next', '/'))
84
 
105
 
85
 
106
 
107
+def mycreole_delete(request, rel_path):
108
+    context = themes.Context(request)      # needs to be executed first because of time mesurement
109
+    nxt = get_next(request)
110
+    modify_access = access('modify', request, rel_path)
111
+    full_path = mycreole.get_full_path(rel_path)
112
+    if not modify_access:
113
+        messages.error(request, 'Detele Attachment: Access denied!')
114
+    else:
115
+        if not request.POST:
116
+            context_adaption(
117
+                context,
118
+                request,
119
+                'Delete %s' % os.path.basename(rel_path),
120
+                os.path.dirname(rel_path),
121
+                next=nxt,
122
+                filename=os.path.basename(rel_path)
123
+            )
124
+            return render(request, 'mycreole/delete.html', context=context)
125
+        else:
126
+            if request.POST.get('yes') is not None:
127
+                try:
128
+                    os.remove(full_path)
129
+                except Exception:
130
+                    messages.error(request, 'Error while deleting file from filesystem.')
131
+                    logger.exception('Fatal error while deleting Attachment!')
132
+                else:
133
+                    messages.info(request, "Attachment %s deleted..." % os.path.basename(full_path))
134
+            else:
135
+                messages.info(request, 'Delete request aborded.')
136
+    return redirect(nxt)
137
+
138
+
86
 def mycreole_manageuploads(request, rel_path):
139
 def mycreole_manageuploads(request, rel_path):
87
-    messages.error(request, 'Manage Uploads: Not yet implemented!')
88
-    return redirect(request.GET.get('next', '/'))
140
+    context = themes.Context(request)      # needs to be executed first because of time mesurement
141
+    nxt = get_next(request)
142
+    read_access = access('read', request, rel_path)
143
+    modify_access = access('modify', request, rel_path)
144
+    #
145
+    if not read_access:
146
+        messages.error(request, "Manageupload: Access denied!")
147
+        return redirect(nxt)
148
+
149
+    basepath = mycreole.get_full_path(rel_path)
150
+    if not os.path.exists(basepath):
151
+        logger.info('Creating path for attachments: %s', basepath)
152
+        fstools.mkdir(basepath)
153
+    logger.debug("Searching for files in %s", basepath)
154
+    attachments = {}
155
+    for file in fstools.filelist(basepath):
156
+        filename = os.path.basename(file)
157
+        actions = []
158
+        if modify_access:
159
+            actions.append((themes.gray_icon_url(request, 'delete.png'), url_delete(request, os.path.join(rel_path, filename)), 'delete'))
160
+            actions.append((themes.gray_icon_url(request, 'shuffle.png'), url_upload(request, os.path.join(rel_path, filename)), 'replace'))
161
+            actions.append((themes.gray_icon_url(request, 'view.png'), url_attachment(os.path.join(rel_path, filename)), 'view'))
162
+        attachments[filename] = {
163
+            'size': stringtools.physical_value_repr(os.path.getsize(file), 'B'),
164
+            'modify_access': modify_access,
165
+            'actions': actions,
166
+        }
167
+    logger.debug("%d attachments detected: %s", len(attachments), ', '.join(attachments.keys()))
168
+    #
169
+    mycreole.context.context_adaption(
170
+        context,
171
+        request,
172
+        'Manageuploads for %s' % rel_path,
173
+        rel_path,
174
+        next=nxt,
175
+        attachments=attachments
176
+    )
177
+    return render(request, 'mycreole/manageuploads.html', context=context)

Loading…
Cancel
Save