Initial Editor for pages implemented

This commit is contained in:
Dirk Alders 2024-10-05 16:22:40 +02:00
parent 30b872ee2d
commit dfcf8e15fd
10 changed files with 168 additions and 216 deletions

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

View File

@ -2,8 +2,12 @@ from django.urls.base import reverse
def url_page(request, rel_path):
return reverse('pages-pages', kwargs={'rel_path': rel_path})
return reverse('page-page', kwargs={'rel_path': rel_path})
def url_helpview(request, page):
return reverse('pages-helpview', kwargs={'page': page})
return reverse('page-helpview', kwargs={'page': page})
def url_edit(request, rel_path):
return reverse('page-edit', kwargs={'rel_path': rel_path})

View File

@ -1,8 +1,16 @@
# TODO: Implement access control for pages
def read_attachment(request, rel_path):
def read_page(request, rel_path):
return True
def write_page(request, rel_path):
return request.user.is_authenticated and request.user.username in ['root', 'dirk']
def read_attachment(request, rel_path):
return read_page(request, rel_path)
def modify_attachment(request, rel_path):
return True
return write_page(request, rel_path)

View File

@ -4,6 +4,7 @@ import logging
from django.utils.translation import gettext as _
from pages import access
from .help import actionbar as actionbar_add_help
import mycreole
import pages
@ -18,6 +19,7 @@ logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
ATTACHMENT_UID = 'attachment'
BACK_UID = 'back'
EDIT_UID = 'edit'
HELP_UID = 'help'
@ -56,12 +58,7 @@ def add_back_menu(request, bar):
def menubar(context, request, caller_name, **kwargs):
bar = context[context.MENUBAR]
# replace_profile(request, bar)
add_help_menu(request, bar)
# add_tasklist_menu(request, bar)
# add_filter_submenu(request, bar, VIEW_TASKLIST_UID)
# add_projectlist_menu(request, bar)
# add_printview_menu(request, bar)
finalise_bar(request, bar)
@ -78,18 +75,27 @@ def add_help_menu(request, bar):
def actionbar(context, request, caller_name, **kwargs):
bar = context[context.ACTIONBAR]
if caller_name == 'pages':
# acc = acc_task(kwargs['task'], request.user)
# if acc.modify or acc.modify_limited:
# add_edittask_menu(request, bar, kwargs['task'].id)
# if acc.add_comments:
# add_newcomment_menu(request, bar, kwargs['task'].id)
if caller_name == 'page':
if access.write_page(request, kwargs["rel_path"]):
add_edit_menu(request, bar, kwargs["rel_path"])
if access.modify_attachment(request, kwargs["rel_path"]):
add_manageupload_menu(request, bar, kwargs['upload_path'])
elif caller_name == 'helpview':
actionbar_add_help(context, request, **kwargs)
finalise_bar(request, bar)
def add_edit_menu(request, bar, rel_path):
bar.append_entry(
EDIT_UID, # uid
_('Edit'), # name
color_icon_url(request, 'edit.png'), # icon
pages.url_edit(request, rel_path), # url
True, # left
False # active
)
def add_manageupload_menu(request, bar, upload_path):
bar.append_entry(
ATTACHMENT_UID, # uid
@ -105,185 +111,3 @@ def finalise_bar(request, bar):
if len(bar) == 0:
bar.append_entry(*empty_entry_parameters(request))
"""from .access import create_project_possible, create_task_possible, acc_task
from django.db.models.functions import Lower
import patt
from .search import common_searches
from themes import empty_entry_parameters, color_icon_url, gray_icon_url
from users.context import PROFILE_ENTRY_UID
COMMENTNEW_UID = 'commentnew'
CREATE_PROJECT_UID = 'create-project'
CREATE_TASK_UID = 'create-task'
PRINTVIEW_UID = 'printview'
TASKEDIT_UID = 'taskedit'
VIEW_PROJECTLIST_UID = 'view-projectlist'
VIEW_TASKLIST_UID = 'view-tasklist'
def replace_profile(request, bar):
try:
bar.replace_entry(
PROFILE_ENTRY_UID,
PROFILE_ENTRY_UID, # uid
request.user.username, # name
color_icon_url(request, 'user.png'), # icon
patt.url_profile(request), # url
False, # left
False # active
)
except ValueError:
pass # Profile entry does not exist, so exchange is not needed (e.g. no user is logged in)
def add_tasklist_menu(request, bar):
bar.append_entry(
VIEW_TASKLIST_UID, # uid
_('Tasklist'), # name
color_icon_url(request, 'task.png'), # icon
patt.url_tasklist(request), # url
True, # left
patt.is_tasklistview(request) # active
)
def add_projectlist_menu(request, bar):
bar.append_entry(
VIEW_PROJECTLIST_UID, # uid
_('Projectlist'), # name
color_icon_url(request, 'folder.png'), # icon
patt.url_projectlist(request), # url
True, # left
patt.is_projectlistview(request) # active
)
def add_printview_menu(request, bar):
bar.append_entry(
PRINTVIEW_UID, # uid
_('Printview'), # name
color_icon_url(request, 'print.png'), # icon
patt.url_printview(request), # url
True, # left
patt.is_printview(request) # active
)
def add_newtask_menu(request, bar, project_id):
bar.append_entry(
CREATE_TASK_UID, # uid
_('New Task'), # name
color_icon_url(request, 'plus.png'), # icon
patt.url_tasknew(request, project_id), # url
True, # left
False # active
)
def add_edittask_menu(request, bar, task_id):
bar.append_entry(
TASKEDIT_UID, # uid
_('Edit'), # name
color_icon_url(request, 'edit.png'), # icon
patt.url_taskedit(request, task_id), # url
True, # left
False # active
)
def add_newcomment_menu(request, bar, task_id):
bar.append_entry(
COMMENTNEW_UID, # uid
_('Add Comment'), # name
color_icon_url(request, 'edit2.png'), # icon
patt.url_commentnew(request, task_id), # url
True, # left
False # active
)
def add_newproject_menu(request, bar):
bar.append_entry(
CREATE_PROJECT_UID, # uid
_('New Project'), # name
color_icon_url(request, 'plus.png'), # icon
patt.url_projectnew(request), # url
True, # left
False # active
)
def add_filter_submenu(request, bar, menu_uid):
bar.append_entry_to_entry(
menu_uid,
menu_uid + '-easysearch', # uid
_('Easysearch'), # name
gray_icon_url(request, 'search.png'), # icon
patt.url_easysearch(request), # url
True, # left
False # active
)
if patt.get_search_query(request) is not None:
bar.append_entry_to_entry(
menu_uid,
menu_uid + '-save', # uid
_('Save Search as Filter'), # name
gray_icon_url(request, 'save.png'), # icon
patt.url_filteredit(request), # url
True, # left
False # active
)
bar.append_entry_to_entry(
menu_uid,
menu_uid + '-all', # uid
_('All Tasks'), # name
gray_icon_url(request, 'task.png'), # icon
patt.url_tasklist(request), # url
True, # left
False # active
)
cs = common_searches(request)
for common_filter_id in cs:
bar.append_entry_to_entry(
menu_uid,
menu_uid + '-common', # uid
_(cs[common_filter_id][0]), # name
gray_icon_url(request, 'filter.png'), # icon
patt.url_tasklist(request, common_filter_id=common_filter_id), # url
True, # left
False # active
)
for s in request.user.search_set.order_by(Lower('name')):
active = patt.is_tasklistview(request, s.id)
if active is True:
url = patt.url_filteredit(request, s.id)
else:
url = patt.url_tasklist(request, user_filter_id=s.id)
if active:
icon = 'settings.png'
else:
icon = 'favourite.png'
bar.append_entry_to_entry(
menu_uid,
menu_uid + '-sub', # uid
s.name, # name
gray_icon_url(request, icon), # icon
url, # url
True, # left
active # active
)
"""

14
pages/forms.py Normal file
View File

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

12
pages/messages.py Normal file
View File

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

View File

@ -1,10 +1,12 @@
from django.conf import settings
import fstools
from pages import messages
import mycreole
import os
class page(object):
class creol_page(object):
SPLITCHAR = ":"
FOLDER_ATTACHMENTS = "attachments"
FOLDER_CONTENT = 'content'
@ -35,13 +37,24 @@ class page(object):
def content_file_name(self):
return os.path.join(settings.PAGES_ROOT, self.content_folder_name, self.FOLDER_CONTENT, self.FILE_NAME)
def __read_content__(self):
if self.is_available():
@property
def raw_page_src(self):
try:
with open(self.content_file_name, 'r') as fh:
return fh.read()
else:
# TODO: Create message for creation or no content dependent of user has write access
return "Page not available. Create it."
except FileNotFoundError:
return ""
def update_page(self, page_txt):
folder = os.path.dirname(self.content_file_name)
if not os.path.exists(folder):
fstools.mkdir(folder)
with open(self.content_file_name, 'w') as fh:
fh.write(page_txt)
def render_to_html(self, request):
return mycreole.render(request, self.__read_content__(), self.attachment_path, "next_anchor")
if self.is_available():
return mycreole.render(request, self.raw_page_src, self.attachment_path, "next_anchor")
else:
messages.unavailable_msg_page(request, self._rel_path)
return ""

View File

@ -0,0 +1,20 @@
{% extends "themes/"|add:settings.page_theme|add:"/base.html" %}
{% load static %}
{% load i18n %}
{% block content %}
<form class="form" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" name="save" value="{% trans "Save" %}" class="button" />
{% if not disable_preview %}<input type="submit" formaction="#preview" name="preview" value="{% trans "Preview" %}" class="button" />{% endif %}
{{ form_comment.as_p }}
</form>
{% if page_content %}
<div class="preview-spacer" id="preview"></div>
<div class="preview">
{{ page_content|safe }}
</div>
{% endif %}
{% endblock content %}

View File

@ -4,11 +4,15 @@ from django.utils.translation import gettext as _
import logging
import config
from . import access
from . import messages
from . import url_page
import config
from .context import context_adaption
from .forms import EditForm
from .help import help_pages
from .page import page
import mycreole
from .page import creol_page
from themes import Context
logger = logging.getLogger(__name__)
@ -18,21 +22,73 @@ def root(request):
return HttpResponseRedirect(url_page(request, config.STARTPAGE))
def pages(request, rel_path=''):
def page(request, rel_path):
context = Context(request) # needs to be executed first because of time mesurement
#
p = page(rel_path)
p = creol_page(rel_path)
if access.read_page(request, rel_path):
page_content = p.render_to_html(request)
else:
messages.permission_denied_msg_page(request, rel_path)
page_content = ""
#
context_adaption(
context,
request,
rel_path=rel_path,
title=p.title,
upload_path=p.attachment_path,
page_content=p.render_to_html(request)
page_content=page_content
)
return render(request, 'pages/page.html', context=context)
def edit(request, rel_path):
if access.write_page(request, rel_path):
context = Context(request) # needs to be executed first because of time mesurement
#
p = creol_page(rel_path)
#
if not request.POST:
form = EditForm(page_data=p.raw_page_src)
#
context_adaption(
context,
request,
form=form,
# TODO: Add translation
title=_("Edit page %s") % repr(p.title),
upload_path=p.attachment_path,
)
return render(request, 'pages/page_form.html', context=context)
else:
save = request.POST.get("save")
page_txt = request.POST.get("page_txt")
preview = request.POST.get("preview")
#
if save is not None:
p.update_page(page_txt)
return HttpResponseRedirect(url_page(request, rel_path))
elif preview is not None:
form = EditForm(page_data=page_txt)
#
context_adaption(
context,
request,
form=form,
# TODO: Add translation
title=_("Edit page %s") % repr(p.title),
upload_path=p.attachment_path,
page_content=mycreole.render(request, page_txt, p.attachment_path, 'next-anchor')
)
return render(request, 'pages/page_form.html', context=context)
else:
return HttpResponseRedirect(url_page(request, rel_path))
else:
messages.permission_denied_msg_page(request, rel_path)
return HttpResponseRedirect(url_page(request, rel_path))
def search(request):
context = Context(request) # needs to be executed first because of time mesurement
context_adaption(

View File

@ -24,11 +24,12 @@ import pages.views
urlpatterns = [
path('admin/', admin.site.urls),
#
path('', pages.views.root, name='pages-root'),
path('pages/', pages.views.root, name='pages-root'),
path('pages/<path:rel_path>', pages.views.pages, name='pages-pages'),
path('helpview/', pages.views.helpview, name='pages-helpview'),
path('helpview/<str:page>', pages.views.helpview, name='pages-helpview'),
path('', pages.views.root, name='page-root'),
path('page/', pages.views.root, name='page-root'),
path('page/<path:rel_path>', pages.views.page, name='page-page'),
path('pageedit/<path:rel_path>/', pages.views.edit, name='page-edit'),
path('helpview/', pages.views.helpview, name='page-helpview'),
path('helpview/<str:page>', pages.views.helpview, name='page-helpview'),
path('search/', pages.views.search, name='search'),
path('mycreole/', include('mycreole.urls')),
path('users/', include('users.urls')),