Browse Source

Initial Editor for pages implemented

master
Dirk Alders 2 months ago
parent
commit
dfcf8e15fd
10 changed files with 168 additions and 216 deletions
  1. 1
    1
      mycreole
  2. 6
    2
      pages/__init__.py
  3. 10
    2
      pages/access.py
  4. 18
    194
      pages/context.py
  5. 14
    0
      pages/forms.py
  6. 12
    0
      pages/messages.py
  7. 20
    7
      pages/page.py
  8. 20
    0
      pages/templates/pages/page_form.html
  9. 61
    5
      pages/views.py
  10. 6
    5
      piki/urls.py

+ 1
- 1
mycreole

1
-Subproject commit 057388e3b44b5fe42b133fe030c41aa6a254de58
1
+Subproject commit 8a2da2b84379df346d4f24c8fc5c1e6fe62c75e9

+ 6
- 2
pages/__init__.py View File

2
 
2
 
3
 
3
 
4
 def url_page(request, rel_path):
4
 def url_page(request, rel_path):
5
-    return reverse('pages-pages', kwargs={'rel_path': rel_path})
5
+    return reverse('page-page', kwargs={'rel_path': rel_path})
6
 
6
 
7
 
7
 
8
 def url_helpview(request, page):
8
 def url_helpview(request, page):
9
-    return reverse('pages-helpview', kwargs={'page': page})
9
+    return reverse('page-helpview', kwargs={'page': page})
10
+
11
+
12
+def url_edit(request, rel_path):
13
+    return reverse('page-edit', kwargs={'rel_path': rel_path})

+ 10
- 2
pages/access.py View File

1
 # TODO: Implement access control for pages
1
 # TODO: Implement access control for pages
2
 
2
 
3
-def read_attachment(request, rel_path):
3
+def read_page(request, rel_path):
4
     return True
4
     return True
5
 
5
 
6
 
6
 
7
+def write_page(request, rel_path):
8
+    return request.user.is_authenticated and request.user.username in ['root', 'dirk']
9
+
10
+
11
+def read_attachment(request, rel_path):
12
+    return read_page(request, rel_path)
13
+
14
+
7
 def modify_attachment(request, rel_path):
15
 def modify_attachment(request, rel_path):
8
-    return True
16
+    return write_page(request, rel_path)

+ 18
- 194
pages/context.py View File

4
 from django.utils.translation import gettext as _
4
 from django.utils.translation import gettext as _
5
 
5
 
6
 
6
 
7
+from pages import access
7
 from .help import actionbar as actionbar_add_help
8
 from .help import actionbar as actionbar_add_help
8
 import mycreole
9
 import mycreole
9
 import pages
10
 import pages
18
 
19
 
19
 ATTACHMENT_UID = 'attachment'
20
 ATTACHMENT_UID = 'attachment'
20
 BACK_UID = 'back'
21
 BACK_UID = 'back'
22
+EDIT_UID = 'edit'
21
 HELP_UID = 'help'
23
 HELP_UID = 'help'
22
 
24
 
23
 
25
 
56
 
58
 
57
 def menubar(context, request, caller_name, **kwargs):
59
 def menubar(context, request, caller_name, **kwargs):
58
     bar = context[context.MENUBAR]
60
     bar = context[context.MENUBAR]
59
-    # replace_profile(request, bar)
60
     add_help_menu(request, bar)
61
     add_help_menu(request, bar)
61
-    # add_tasklist_menu(request, bar)
62
-    # add_filter_submenu(request, bar, VIEW_TASKLIST_UID)
63
-    # add_projectlist_menu(request, bar)
64
-    # add_printview_menu(request, bar)
65
     finalise_bar(request, bar)
62
     finalise_bar(request, bar)
66
 
63
 
67
 
64
 
78
 
75
 
79
 def actionbar(context, request, caller_name, **kwargs):
76
 def actionbar(context, request, caller_name, **kwargs):
80
     bar = context[context.ACTIONBAR]
77
     bar = context[context.ACTIONBAR]
81
-    if caller_name == 'pages':
82
-        #    acc = acc_task(kwargs['task'], request.user)
83
-        #    if acc.modify or acc.modify_limited:
84
-        #        add_edittask_menu(request, bar, kwargs['task'].id)
85
-        #    if acc.add_comments:
86
-        #        add_newcomment_menu(request, bar, kwargs['task'].id)
87
-        add_manageupload_menu(request, bar, kwargs['upload_path'])
78
+    if caller_name == 'page':
79
+        if access.write_page(request, kwargs["rel_path"]):
80
+            add_edit_menu(request, bar, kwargs["rel_path"])
81
+        if access.modify_attachment(request, kwargs["rel_path"]):
82
+            add_manageupload_menu(request, bar, kwargs['upload_path'])
88
     elif caller_name == 'helpview':
83
     elif caller_name == 'helpview':
89
         actionbar_add_help(context, request, **kwargs)
84
         actionbar_add_help(context, request, **kwargs)
90
     finalise_bar(request, bar)
85
     finalise_bar(request, bar)
91
 
86
 
92
 
87
 
88
+def add_edit_menu(request, bar, rel_path):
89
+    bar.append_entry(
90
+        EDIT_UID,                                   # uid
91
+        _('Edit'),                                  # name
92
+        color_icon_url(request, 'edit.png'),        # icon
93
+        pages.url_edit(request, rel_path),          # url
94
+        True,                                       # left
95
+        False                                       # active
96
+    )
97
+
98
+
93
 def add_manageupload_menu(request, bar, upload_path):
99
 def add_manageupload_menu(request, bar, upload_path):
94
     bar.append_entry(
100
     bar.append_entry(
95
         ATTACHMENT_UID,                                                     # uid
101
         ATTACHMENT_UID,                                                     # uid
105
     if len(bar) == 0:
111
     if len(bar) == 0:
106
         bar.append_entry(*empty_entry_parameters(request))
112
         bar.append_entry(*empty_entry_parameters(request))
107
 
113
 
108
-
109
-"""from .access import create_project_possible, create_task_possible, acc_task
110
-from django.db.models.functions import Lower
111
-import patt
112
-from .search import common_searches
113
-from themes import empty_entry_parameters, color_icon_url, gray_icon_url
114
-from users.context import PROFILE_ENTRY_UID
115
-
116
-
117
-COMMENTNEW_UID = 'commentnew'
118
-CREATE_PROJECT_UID = 'create-project'
119
-CREATE_TASK_UID = 'create-task'
120
-PRINTVIEW_UID = 'printview'
121
-TASKEDIT_UID = 'taskedit'
122
-VIEW_PROJECTLIST_UID = 'view-projectlist'
123
-VIEW_TASKLIST_UID = 'view-tasklist'
124
-
125
-
126
-
127
-
128
-
129
-
130
-
131
-
132
-def replace_profile(request, bar):
133
-    try:
134
-        bar.replace_entry(
135
-            PROFILE_ENTRY_UID,
136
-            PROFILE_ENTRY_UID,                          # uid
137
-            request.user.username,                      # name
138
-            color_icon_url(request, 'user.png'),        # icon
139
-            patt.url_profile(request),                  # url
140
-            False,                                      # left
141
-            False                                       # active
142
-        )
143
-    except ValueError:
144
-        pass        # Profile entry does not exist, so exchange is not needed (e.g. no user is logged in)
145
-
146
-
147
-
148
-
149
-def add_tasklist_menu(request, bar):
150
-    bar.append_entry(
151
-        VIEW_TASKLIST_UID,                          # uid
152
-        _('Tasklist'),                              # name
153
-        color_icon_url(request, 'task.png'),        # icon
154
-        patt.url_tasklist(request),                 # url
155
-        True,                                       # left
156
-        patt.is_tasklistview(request)               # active
157
-    )
158
-
159
-
160
-def add_projectlist_menu(request, bar):
161
-    bar.append_entry(
162
-        VIEW_PROJECTLIST_UID,                       # uid
163
-        _('Projectlist'),                           # name
164
-        color_icon_url(request, 'folder.png'),      # icon
165
-        patt.url_projectlist(request),              # url
166
-        True,                                       # left
167
-        patt.is_projectlistview(request)            # active
168
-    )
169
-
170
-
171
-def add_printview_menu(request, bar):
172
-    bar.append_entry(
173
-        PRINTVIEW_UID,                              # uid
174
-        _('Printview'),                             # name
175
-        color_icon_url(request, 'print.png'),       # icon
176
-        patt.url_printview(request),                # url
177
-        True,                                       # left
178
-        patt.is_printview(request)                  # active
179
-    )
180
-
181
-
182
-def add_newtask_menu(request, bar, project_id):
183
-    bar.append_entry(
184
-        CREATE_TASK_UID,                            # uid
185
-        _('New Task'),                              # name
186
-        color_icon_url(request, 'plus.png'),        # icon
187
-        patt.url_tasknew(request, project_id),      # url
188
-        True,                                       # left
189
-        False                                       # active
190
-    )
191
-
192
-
193
-def add_edittask_menu(request, bar, task_id):
194
-    bar.append_entry(
195
-        TASKEDIT_UID,                               # uid
196
-        _('Edit'),                                  # name
197
-        color_icon_url(request, 'edit.png'),        # icon
198
-        patt.url_taskedit(request, task_id),        # url
199
-        True,                                       # left
200
-        False                                       # active
201
-    )
202
-
203
-
204
-def add_newcomment_menu(request, bar, task_id):
205
-    bar.append_entry(
206
-        COMMENTNEW_UID,                             # uid
207
-        _('Add Comment'),                           # name
208
-        color_icon_url(request, 'edit2.png'),       # icon
209
-        patt.url_commentnew(request, task_id),      # url
210
-        True,                                       # left
211
-        False                                       # active
212
-    )
213
-
214
-
215
-def add_newproject_menu(request, bar):
216
-    bar.append_entry(
217
-        CREATE_PROJECT_UID,                         # uid
218
-        _('New Project'),                           # name
219
-        color_icon_url(request, 'plus.png'),        # icon
220
-        patt.url_projectnew(request),               # url
221
-        True,                                       # left
222
-        False                                       # active
223
-    )
224
-
225
-
226
-
227
-
228
-
229
-
230
-def add_filter_submenu(request, bar, menu_uid):
231
-    bar.append_entry_to_entry(
232
-        menu_uid,
233
-        menu_uid + '-easysearch',             # uid
234
-        _('Easysearch'),                        # name
235
-        gray_icon_url(request, 'search.png'),   # icon
236
-        patt.url_easysearch(request),           # url
237
-        True,                                   # left
238
-        False                                   # active
239
-    )
240
-    if patt.get_search_query(request) is not None:
241
-        bar.append_entry_to_entry(
242
-            menu_uid,
243
-            menu_uid + '-save',                     # uid
244
-            _('Save Search as Filter'),             # name
245
-            gray_icon_url(request, 'save.png'),     # icon
246
-            patt.url_filteredit(request),           # url
247
-            True,                                   # left
248
-            False                                   # active
249
-        )
250
-    bar.append_entry_to_entry(
251
-        menu_uid,
252
-        menu_uid + '-all',                      # uid
253
-        _('All Tasks'),                         # name
254
-        gray_icon_url(request, 'task.png'),     # icon
255
-        patt.url_tasklist(request),             # url
256
-        True,                                   # left
257
-        False                                   # active
258
-    )
259
-    cs = common_searches(request)
260
-    for common_filter_id in cs:
261
-        bar.append_entry_to_entry(
262
-            menu_uid,
263
-            menu_uid + '-common',                                           # uid
264
-            _(cs[common_filter_id][0]),                                     # name
265
-            gray_icon_url(request, 'filter.png'),                           # icon
266
-            patt.url_tasklist(request, common_filter_id=common_filter_id),  # url
267
-            True,                                                           # left
268
-            False                                                           # active
269
-        )
270
-    for s in request.user.search_set.order_by(Lower('name')):
271
-        active = patt.is_tasklistview(request, s.id)
272
-        if active is True:
273
-            url = patt.url_filteredit(request, s.id)
274
-        else:
275
-            url = patt.url_tasklist(request, user_filter_id=s.id)
276
-        if active:
277
-            icon = 'settings.png'
278
-        else:
279
-            icon = 'favourite.png'
280
-        bar.append_entry_to_entry(
281
-            menu_uid,
282
-            menu_uid + '-sub',              # uid
283
-            s.name,                         # name
284
-            gray_icon_url(request, icon),   # icon
285
-            url,                            # url
286
-            True,                           # left
287
-            active                          # active
288
-        )
289
-"""

+ 14
- 0
pages/forms.py View File

1
+from typing import Any, Mapping
2
+from django import forms
3
+from django.forms.renderers import BaseRenderer
4
+from django.forms.utils import ErrorList
5
+
6
+
7
+class EditForm(forms.Form):  # Note that it is not inheriting from forms.ModelForm
8
+    page_txt = forms.CharField(max_length=20000, label="Page source text", widget=forms.Textarea(attrs={"rows": "20"}))
9
+
10
+    def __init__(self, *args, **kwargs) -> None:
11
+        page_data = kwargs.pop("page_data")
12
+        super().__init__(*args, **kwargs)
13
+        page_txt = self.fields['page_txt']
14
+        page_txt.initial = page_data

+ 12
- 0
pages/messages.py View File

1
+from django.contrib import messages
2
+from django.utils.translation import gettext as _
3
+
4
+
5
+def permission_denied_msg_page(request, rel_path):
6
+    # TODO: Add translation for this message
7
+    messages.error(request, _("Permission denied: You don't have sufficient acces to the Page '%s'. Please contact the adminstrator.") % rel_path)
8
+
9
+
10
+def unavailable_msg_page(request, rel_path):
11
+    # TODO: Add translation for this message
12
+    messages.info(request, _("Unavailable: The Page '%s' is not available. Create it or follow a valid link, please.") % rel_path)

+ 20
- 7
pages/page.py View File

1
 from django.conf import settings
1
 from django.conf import settings
2
 
2
 
3
+import fstools
4
+from pages import messages
3
 import mycreole
5
 import mycreole
4
 import os
6
 import os
5
 
7
 
6
 
8
 
7
-class page(object):
9
+class creol_page(object):
8
     SPLITCHAR = ":"
10
     SPLITCHAR = ":"
9
     FOLDER_ATTACHMENTS = "attachments"
11
     FOLDER_ATTACHMENTS = "attachments"
10
     FOLDER_CONTENT = 'content'
12
     FOLDER_CONTENT = 'content'
35
     def content_file_name(self):
37
     def content_file_name(self):
36
         return os.path.join(settings.PAGES_ROOT, self.content_folder_name, self.FOLDER_CONTENT, self.FILE_NAME)
38
         return os.path.join(settings.PAGES_ROOT, self.content_folder_name, self.FOLDER_CONTENT, self.FILE_NAME)
37
 
39
 
38
-    def __read_content__(self):
39
-        if self.is_available():
40
+    @property
41
+    def raw_page_src(self):
42
+        try:
40
             with open(self.content_file_name, 'r') as fh:
43
             with open(self.content_file_name, 'r') as fh:
41
                 return fh.read()
44
                 return fh.read()
42
-        else:
43
-            # TODO: Create message for creation or no content dependent of user has write access
44
-            return "Page not available. Create it."
45
+        except FileNotFoundError:
46
+            return ""
47
+
48
+    def update_page(self, page_txt):
49
+        folder = os.path.dirname(self.content_file_name)
50
+        if not os.path.exists(folder):
51
+            fstools.mkdir(folder)
52
+        with open(self.content_file_name, 'w') as fh:
53
+            fh.write(page_txt)
45
 
54
 
46
     def render_to_html(self, request):
55
     def render_to_html(self, request):
47
-        return mycreole.render(request, self.__read_content__(), self.attachment_path, "next_anchor")
56
+        if self.is_available():
57
+            return mycreole.render(request, self.raw_page_src, self.attachment_path, "next_anchor")
58
+        else:
59
+            messages.unavailable_msg_page(request, self._rel_path)
60
+            return ""

+ 20
- 0
pages/templates/pages/page_form.html View File

1
+{% extends "themes/"|add:settings.page_theme|add:"/base.html" %}
2
+{% load static %}
3
+{% load i18n %}
4
+
5
+{% block content %}
6
+    <form class="form" method="post">
7
+      {% csrf_token %}
8
+      {{ form.as_p }}
9
+      <input type="submit" name="save" value="{% trans "Save" %}" class="button" />
10
+      {% if not disable_preview %}<input type="submit"  formaction="#preview" name="preview" value="{% trans "Preview" %}" class="button" />{% endif %}
11
+      {{ form_comment.as_p }}
12
+    </form>
13
+
14
+    {% if page_content %}
15
+      <div class="preview-spacer" id="preview"></div>
16
+      <div class="preview">
17
+        {{ page_content|safe }}
18
+      </div>
19
+    {% endif %}
20
+{% endblock content %}

+ 61
- 5
pages/views.py View File

4
 
4
 
5
 import logging
5
 import logging
6
 
6
 
7
-import config
7
+from . import access
8
+from . import messages
8
 from . import url_page
9
 from . import url_page
10
+import config
9
 from .context import context_adaption
11
 from .context import context_adaption
12
+from .forms import EditForm
10
 from .help import help_pages
13
 from .help import help_pages
11
-from .page import page
14
+import mycreole
15
+from .page import creol_page
12
 from themes import Context
16
 from themes import Context
13
 
17
 
14
 logger = logging.getLogger(__name__)
18
 logger = logging.getLogger(__name__)
18
     return HttpResponseRedirect(url_page(request, config.STARTPAGE))
22
     return HttpResponseRedirect(url_page(request, config.STARTPAGE))
19
 
23
 
20
 
24
 
21
-def pages(request, rel_path=''):
25
+def page(request, rel_path):
22
     context = Context(request)      # needs to be executed first because of time mesurement
26
     context = Context(request)      # needs to be executed first because of time mesurement
23
     #
27
     #
24
-    p = page(rel_path)
28
+    p = creol_page(rel_path)
29
+    if access.read_page(request, rel_path):
30
+        page_content = p.render_to_html(request)
31
+    else:
32
+        messages.permission_denied_msg_page(request, rel_path)
33
+        page_content = ""
25
     #
34
     #
26
     context_adaption(
35
     context_adaption(
27
         context,
36
         context,
28
         request,
37
         request,
38
+        rel_path=rel_path,
29
         title=p.title,
39
         title=p.title,
30
         upload_path=p.attachment_path,
40
         upload_path=p.attachment_path,
31
-        page_content=p.render_to_html(request)
41
+        page_content=page_content
32
     )
42
     )
33
     return render(request, 'pages/page.html', context=context)
43
     return render(request, 'pages/page.html', context=context)
34
 
44
 
35
 
45
 
46
+def edit(request, rel_path):
47
+    if access.write_page(request, rel_path):
48
+        context = Context(request)      # needs to be executed first because of time mesurement
49
+        #
50
+        p = creol_page(rel_path)
51
+        #
52
+        if not request.POST:
53
+            form = EditForm(page_data=p.raw_page_src)
54
+            #
55
+            context_adaption(
56
+                context,
57
+                request,
58
+                form=form,
59
+                # TODO: Add translation
60
+                title=_("Edit page %s") % repr(p.title),
61
+                upload_path=p.attachment_path,
62
+            )
63
+            return render(request, 'pages/page_form.html', context=context)
64
+        else:
65
+            save = request.POST.get("save")
66
+            page_txt = request.POST.get("page_txt")
67
+            preview = request.POST.get("preview")
68
+            #
69
+            if save is not None:
70
+                p.update_page(page_txt)
71
+                return HttpResponseRedirect(url_page(request, rel_path))
72
+            elif preview is not None:
73
+                form = EditForm(page_data=page_txt)
74
+                #
75
+                context_adaption(
76
+                    context,
77
+                    request,
78
+                    form=form,
79
+                    # TODO: Add translation
80
+                    title=_("Edit page %s") % repr(p.title),
81
+                    upload_path=p.attachment_path,
82
+                    page_content=mycreole.render(request, page_txt, p.attachment_path, 'next-anchor')
83
+                )
84
+                return render(request, 'pages/page_form.html', context=context)
85
+            else:
86
+                return HttpResponseRedirect(url_page(request, rel_path))
87
+    else:
88
+        messages.permission_denied_msg_page(request, rel_path)
89
+        return HttpResponseRedirect(url_page(request, rel_path))
90
+
91
+
36
 def search(request):
92
 def search(request):
37
     context = Context(request)      # needs to be executed first because of time mesurement
93
     context = Context(request)      # needs to be executed first because of time mesurement
38
     context_adaption(
94
     context_adaption(

+ 6
- 5
piki/urls.py View File

24
 urlpatterns = [
24
 urlpatterns = [
25
     path('admin/', admin.site.urls),
25
     path('admin/', admin.site.urls),
26
     #
26
     #
27
-    path('', pages.views.root, name='pages-root'),
28
-    path('pages/', pages.views.root, name='pages-root'),
29
-    path('pages/<path:rel_path>', pages.views.pages, name='pages-pages'),
30
-    path('helpview/', pages.views.helpview, name='pages-helpview'),
31
-    path('helpview/<str:page>', pages.views.helpview, name='pages-helpview'),
27
+    path('', pages.views.root, name='page-root'),
28
+    path('page/', pages.views.root, name='page-root'),
29
+    path('page/<path:rel_path>', pages.views.page, name='page-page'),
30
+    path('pageedit/<path:rel_path>/', pages.views.edit, name='page-edit'),
31
+    path('helpview/', pages.views.helpview, name='page-helpview'),
32
+    path('helpview/<str:page>', pages.views.helpview, name='page-helpview'),
32
     path('search/', pages.views.search, name='search'),
33
     path('search/', pages.views.search, name='search'),
33
     path('mycreole/', include('mycreole.urls')),
34
     path('mycreole/', include('mycreole.urls')),
34
     path('users/', include('users.urls')),
35
     path('users/', include('users.urls')),

Loading…
Cancel
Save