Delete and rename of pages added

This commit is contained in:
Dirk Alders 2024-10-18 20:47:33 +02:00
parent 4d77be2721
commit beb645e4f6
11 changed files with 269 additions and 46 deletions

View File

@ -18,10 +18,18 @@ def url_helpview(page):
return reverse('page-helpview', kwargs={'page': page}) return reverse('page-helpview', kwargs={'page': page})
def url_delete(rel_path, **kwargs):
return reverse('page-delete', kwargs={'rel_path': rel_path}) + params(**kwargs)
def url_edit(rel_path, **kwargs): def url_edit(rel_path, **kwargs):
return reverse('page-edit', kwargs={'rel_path': rel_path}) + params(**kwargs) return reverse('page-edit', kwargs={'rel_path': rel_path}) + params(**kwargs)
def url_rename(rel_path, **kwargs):
return reverse('page-rename', kwargs={'rel_path': rel_path}) + params(**kwargs)
def get_search_query(request): def get_search_query(request):
return request.GET.get('q') return request.GET.get('q')

View File

@ -73,13 +73,13 @@ def menubar(context, request, caller_name, **kwargs):
def actionbar(context, request, caller_name, **kwargs): def actionbar(context, request, caller_name, **kwargs):
bar = context[context.ACTIONBAR] bar = context[context.ACTIONBAR]
if not cms_mode_active(request): if not cms_mode_active(request):
if caller_name == 'page': if caller_name in ['page', 'edit', 'delete', 'rename']:
if access.write_page(request, kwargs["rel_path"]): if access.write_page(request, kwargs["rel_path"]):
add_edit_menu(request, bar, kwargs["rel_path"]) add_page_menu(request, bar, kwargs["rel_path"], kwargs.get('is_available', False))
if access.modify_attachment(request, kwargs["rel_path"]): if access.modify_attachment(request, kwargs["rel_path"]):
add_manageupload_menu(request, bar, kwargs['upload_path']) add_manageupload_menu(request, bar, kwargs['upload_path'], kwargs.get('is_available', False))
if access.read_page(request, kwargs["rel_path"]): if access.read_page(request, kwargs["rel_path"]):
add_meta_menu(request, bar, kwargs["rel_path"]) add_meta_menu(request, bar, kwargs["rel_path"], kwargs.get('is_available', False))
elif caller_name == 'helpview': elif caller_name == 'helpview':
actionbar_add_help(context, request, **kwargs) actionbar_add_help(context, request, **kwargs)
finalise_bar(request, bar) finalise_bar(request, bar)
@ -137,49 +137,68 @@ def add_nav_links(request, bar, rel_path):
) )
def add_edit_menu(request, bar, rel_path): def add_page_menu(request, bar, rel_path, is_available):
bar.append_entry( bar.append_entry(
EDIT_UID, # uid EDIT_UID, # uid
_('Edit'), # name _('Edit'), # name
color_icon_url(request, 'edit2.png'), # icon color_icon_url(request, 'edit2.png'), # icon
pages.url_edit(rel_path), # url pages.url_edit(rel_path), # url
True, # left True, # left
False # active request.path == pages.url_edit(rel_path) # active
) )
if is_available:
def add_manageupload_menu(request, bar, upload_path):
bar.append_entry(
ATTACHMENT_UID, # uid
_("Attachments"), # name
color_icon_url(request, 'upload.png'), # icon
mycreole.url_manage_uploads(request, upload_path), # url
True, # left
False, # active
)
def add_meta_menu(request, bar, rel_path):
if "meta" in request.GET:
bar.append_entry( bar.append_entry(
EDIT_UID, # uid EDIT_UID, # uid
_('Page'), # name _('Rename'), # name
color_icon_url(request, 'display.png'), # icon color_icon_url(request, 'shuffle.png'), # icon
pages.url_page(rel_path), # url pages.url_rename(rel_path), # url
True, # left True, # left
False # active request.path == pages.url_rename(rel_path) # active
) )
else:
bar.append_entry( bar.append_entry(
EDIT_UID, # uid EDIT_UID, # uid
_('Meta'), # name _('Delete'), # name
color_icon_url(request, 'info.png'), # icon color_icon_url(request, 'delete.png'), # icon
pages.url_page(rel_path, meta=None), # url pages.url_delete(rel_path), # url
True, # left True, # left
False # active request.path == pages.url_delete(rel_path) # active
) )
def add_manageupload_menu(request, bar, upload_path, is_available):
if is_available:
bar.append_entry(
ATTACHMENT_UID, # uid
_("Attachments"), # name
color_icon_url(request, 'upload.png'), # icon
mycreole.url_manage_uploads(request, upload_path), # url
True, # left
False, # active
)
def add_meta_menu(request, bar, rel_path, is_available):
if is_available:
if "meta" in request.GET:
bar.append_entry(
EDIT_UID, # uid
_('Page'), # name
color_icon_url(request, 'display.png'), # icon
pages.url_page(rel_path), # url
True, # left
False # active
)
else:
bar.append_entry(
EDIT_UID, # uid
_('Meta'), # name
color_icon_url(request, 'info.png'), # icon
pages.url_page(rel_path, meta=None), # url
True, # left
False # active
)
def finalise_bar(request, bar): def finalise_bar(request, bar):
if len(bar) == 0: if len(bar) == 0:
bar.append_entry(*empty_entry_parameters(request)) bar.append_entry(*empty_entry_parameters(request))

View File

@ -6,7 +6,7 @@ from django.forms.utils import ErrorList
class EditForm(forms.Form): # Note that it is not inheriting from forms.ModelForm 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"})) page_txt = forms.CharField(max_length=20000, label="Page source text", widget=forms.Textarea(attrs={"rows": "20"}))
page_tags = forms.CharField(max_length=20000, label="Tags (words separated by spaces)", required=False) page_tags = forms.CharField(max_length=500, label="Tags (words separated by spaces)", required=False)
def __init__(self, *args, **kwargs) -> None: def __init__(self, *args, **kwargs) -> None:
page_data = kwargs.pop("page_data") page_data = kwargs.pop("page_data")
@ -14,3 +14,12 @@ class EditForm(forms.Form): # Note that it is not inheriting from forms.ModelFo
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.fields['page_txt'].initial = page_data self.fields['page_txt'].initial = page_data
self.fields['page_tags'].initial = page_tags self.fields['page_tags'].initial = page_tags
class RenameForm(forms.Form): # Note that it is not inheriting from forms.ModelForm
page_name = forms.CharField(max_length=500, label="Change the page name:", required=True)
def __init__(self, *args, **kwargs) -> None:
page_name = kwargs.pop("page_name")
super().__init__(*args, **kwargs)
self.fields['page_name'].initial = page_name

View File

@ -18,11 +18,26 @@ def edit_success(request):
messages.success(request, _('Thanks for editing, page stored.')) messages.success(request, _('Thanks for editing, page stored.'))
def edit_no_change(request): def no_change(request):
# TODO: Add translation for this message # TODO: Add translation for this message
messages.info(request, _("Nothing changed, no storage needed.")) messages.info(request, _("Nothing changed, no storage needed."))
def operation_canceled(request):
# TODO: Add translation for this message
messages.info(request, _('Operation caneled, no change to the content.'))
def page_deleted(request, title):
# TODO: Add translation for this message
messages.info(request, _('The page "%s" has been deleted.') % title)
def page_renamed(request):
# TODO: Add translation for this message
messages.info(request, _('The page has been renamed.'))
def history_version_display(request, rel_path, history_version): def history_version_display(request, rel_path, history_version):
# TODO: Add translation for this message # TODO: Add translation for this message
messages.warning(request, _("You see an old version of the page (Version = %d). Click <a href='%s'>here</a> to recover this Version.") % ( messages.warning(request, _("You see an old version of the page (Version = %d). Click <a href='%s'>here</a> to recover this Version.") % (

View File

@ -22,8 +22,24 @@ def full_path_all_pages(expression="*"):
system_pages = fstools.dirlist(settings.SYSTEM_PAGES_ROOT, expression=expression, rekursive=False) system_pages = fstools.dirlist(settings.SYSTEM_PAGES_ROOT, expression=expression, rekursive=False)
system_pages = [os.path.join(settings.PAGES_ROOT, os.path.basename(path)) for path in system_pages] system_pages = [os.path.join(settings.PAGES_ROOT, os.path.basename(path)) for path in system_pages]
pages = fstools.dirlist(settings.PAGES_ROOT, expression=expression, rekursive=False) pages = fstools.dirlist(settings.PAGES_ROOT, expression=expression, rekursive=False)
# TODO: strip path, if page or meta.json is missing rv = []
return list(set(system_pages + pages)) for path in set(system_pages + pages):
p = page_wrapped(None, path)
if p.is_available():
rv.append(path)
return rv
def full_path_deleted_pages(expression="*"):
system_pages = fstools.dirlist(settings.SYSTEM_PAGES_ROOT, expression=expression, rekursive=False)
system_pages = [os.path.join(settings.PAGES_ROOT, os.path.basename(path)) for path in system_pages]
pages = fstools.dirlist(settings.PAGES_ROOT, expression=expression, rekursive=False)
rv = []
for path in set(system_pages + pages):
p = page_wrapped(None, path)
if not p.is_available():
rv.append(path)
return rv
class meta_data(dict): class meta_data(dict):
@ -45,6 +61,9 @@ class meta_data(dict):
except (FileNotFoundError, json.decoder.JSONDecodeError) as e: except (FileNotFoundError, json.decoder.JSONDecodeError) as e:
super().__init__() super().__init__()
def delete(self):
os.remove(self.filename)
@property @property
def filename(self): def filename(self):
if not self._history_version: if not self._history_version:
@ -106,6 +125,20 @@ class page_data(object):
except FileNotFoundError: except FileNotFoundError:
self._raw_page_src = "" self._raw_page_src = ""
def delete(self):
os.remove(self.filename)
def rename(self, page_name):
# Change backslash to slash and remove double slashes
page_name = page_name.replace("\\", "/")
while "//" in page_name:
page_name = page_name.replace("//", "/")
# move path
target_path = os.path.join(settings.PAGES_ROOT, page_name.replace("/", 2*SPLITCHAR))
shutil.move(self._path, target_path)
# set my path
self._path = target_path
def update_required(self, page_txt): def update_required(self, page_txt):
return page_txt.replace("\r\n", "\n") != self.raw_page_src return page_txt.replace("\r\n", "\n") != self.raw_page_src
@ -143,7 +176,7 @@ class page_data(object):
@property @property
def title(self): def title(self):
return os.path.basename(self._path).split("::")[-1] return os.path.basename(self._path).split(2*SPLITCHAR)[-1]
@property @property
def raw_page_src(self): def raw_page_src(self):
@ -421,6 +454,11 @@ class page_wrapped(object):
rv = meta.get(meta.KEY_CREATION_TIME) rv = meta.get(meta.KEY_CREATION_TIME)
return rv return rv
def delete(self):
self.__store_history__()
self._page.delete()
self._page_meta.delete()
@property @property
def modified_time(self): def modified_time(self):
meta = self.__meta_choose__() meta = self.__meta_choose__()
@ -433,6 +471,9 @@ class page_wrapped(object):
rv = meta.get(meta.KEY_MODIFIED_USER) rv = meta.get(meta.KEY_MODIFIED_USER)
return rv return rv
def rename(self, page_name):
self._page.rename(page_name)
@property @property
def tags(self): def tags(self):
meta = self.__meta_choose__() meta = self.__meta_choose__()
@ -451,6 +492,9 @@ class page_wrapped(object):
def is_available(self): def is_available(self):
return self._page.is_available() or self._system_page.is_available() return self._page.is_available() or self._system_page.is_available()
def userpage_is_available(self):
return self._page.is_available()
@property @property
def raw_page_src(self): def raw_page_src(self):
page = self.__page_choose__() page = self.__page_choose__()

View File

@ -98,7 +98,7 @@ def whoosh_search(search_txt):
def delete_item(ix, pw: page_wrapped): def delete_item(ix, pw: page_wrapped):
with ix.writer() as w: with ix.writer() as w:
logger.info('Removing document with id=%s from the search index.', pw.rel_path) logger.info('Removing document with id=%s from the search index.', pw.rel_path)
w.delete_by_term("task_id", pw.rel_path) w.delete_by_term("id", pw.rel_path)
def update_item(pw: page_wrapped): def update_item(pw: page_wrapped):

View File

@ -0,0 +1,16 @@
{% 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 }}
<label for="id_page_name">{% trans "Do you really want to delete this page?" %}</label>
<input type="submit" name="delete" value="{% trans "Delete" %}" class="button" />
<input type="submit" name="cancel" value="{% trans "Cancel" %}" class="button" />
</form>
{{ page_content|safe }}
{% endblock content %}

View File

@ -0,0 +1,15 @@
{% 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="rename" value="{% trans "Rename" %}" class="button" />
<input type="submit" name="cancel" value="{% trans "Cancel" %}" class="button" />
</form>
{{ page_content|safe }}
{% endblock content %}

View File

@ -12,11 +12,11 @@ from . import url_page
from . import get_search_query from . import get_search_query
import config import config
from .context import context_adaption from .context import context_adaption
from .forms import EditForm from .forms import EditForm, RenameForm
from .help import help_pages from .help import help_pages
import mycreole import mycreole
from .page import page_wrapped, page_list from .page import page_wrapped, page_list
from .search import whoosh_search from .search import whoosh_search, load_index, delete_item, add_item
from themes import Context from themes import Context
logger = logging.getLogger(settings.ROOT_LOGGER_NAME).getChild(__name__) logger = logging.getLogger(settings.ROOT_LOGGER_NAME).getChild(__name__)
@ -52,7 +52,8 @@ def page(request, rel_path):
rel_path=rel_path, rel_path=rel_path,
title=p.title, title=p.title,
upload_path=p.attachment_path, upload_path=p.attachment_path,
page_content=page_content page_content=page_content,
is_available=p.userpage_is_available()
) )
return render(request, 'pages/page.html', context=context) return render(request, 'pages/page.html', context=context)
@ -73,12 +74,14 @@ def edit(request, rel_path):
context_adaption( context_adaption(
context, context,
request, request,
rel_path=rel_path,
is_available=p.userpage_is_available(),
form=form, form=form,
# TODO: Add translation # TODO: Add translation
title=_("Edit page %s") % repr(p.title), title=_("Edit page %s") % repr(p.title),
upload_path=p.attachment_path, upload_path=p.attachment_path,
) )
return render(request, 'pages/page_form.html', context=context) return render(request, 'pages/page_edit.html', context=context)
else: else:
p = page_wrapped(request, rel_path) p = page_wrapped(request, rel_path)
# #
@ -91,7 +94,7 @@ def edit(request, rel_path):
if p.update_page(page_txt, tags): if p.update_page(page_txt, tags):
messages.edit_success(request) messages.edit_success(request)
else: else:
messages.edit_no_change(request) messages.no_change(request)
return HttpResponseRedirect(url_page(rel_path)) return HttpResponseRedirect(url_page(rel_path))
elif preview is not None: elif preview is not None:
form = EditForm(page_data=page_txt, page_tags=tags) form = EditForm(page_data=page_txt, page_tags=tags)
@ -99,13 +102,15 @@ def edit(request, rel_path):
context_adaption( context_adaption(
context, context,
request, request,
rel_path=rel_path,
is_available=p.userpage_is_available(),
form=form, form=form,
# TODO: Add translation # TODO: Add translation
title=_("Edit page %s") % repr(p.title), title=_("Edit page %s") % repr(p.title),
upload_path=p.attachment_path, upload_path=p.attachment_path,
page_content=p.render_text(request, page_txt) page_content=p.render_text(request, page_txt)
) )
return render(request, 'pages/page_form.html', context=context) return render(request, 'pages/page_edit.html', context=context)
else: else:
return HttpResponseRedirect(url_page(rel_path)) return HttpResponseRedirect(url_page(rel_path))
else: else:
@ -113,6 +118,96 @@ def edit(request, rel_path):
return HttpResponseRedirect(url_page(rel_path)) return HttpResponseRedirect(url_page(rel_path))
def delete(request, rel_path):
if access.write_page(request, rel_path):
context = Context(request) # needs to be executed first because of time mesurement
#
if not request.POST:
p = page_wrapped(request, rel_path)
#
# form = DeleteForm(page_data=p.raw_page_src, page_tags=p.tags)
#
context_adaption(
context,
request,
rel_path=rel_path,
is_available=p.userpage_is_available(),
# form=form,
# TODO: Add translation
title=_("Delete page %s") % repr(p.title),
upload_path=p.attachment_path,
page_content=p.render_to_html(),
)
else:
p = page_wrapped(request, rel_path)
#
delete = request.POST.get("delete")
#
if delete:
# delete page from search index
ix = load_index()
delete_item(ix, p)
# delete move files to history
p.delete()
# add delete message
messages.page_deleted(request, p.title)
return HttpResponseRedirect("/")
else:
messages.operation_canceled(request)
return HttpResponseRedirect(url_page(rel_path))
return render(request, 'pages/page_delete.html', context=context)
else:
messages.permission_denied_msg_page(request, rel_path)
return HttpResponseRedirect(url_page(rel_path))
def rename(request, rel_path):
if access.write_page(request, rel_path):
context = Context(request) # needs to be executed first because of time mesurement
#
if not request.POST:
p = page_wrapped(request, rel_path)
#
form = RenameForm(page_name=p.rel_path)
#
context_adaption(
context,
request,
rel_path=rel_path,
is_available=p.userpage_is_available(),
form=form,
# TODO: Add translation
title=_("Delete page %s") % repr(p.title),
upload_path=p.attachment_path,
page_content=p.render_to_html(),
)
else:
p = page_wrapped(request, rel_path)
#
rename = request.POST.get("rename")
page_name = request.POST.get("page_name")
if rename:
if page_name == p.rel_path:
messages.no_change(request)
else:
# delete page from search index
ix = load_index()
delete_item(ix, p)
# rename the storage folder
p.rename(page_name)
# add the renamed page to the search index
add_item(ix, p)
# add rename message
messages.page_renamed(request)
else:
messages.operation_canceled(request)
return HttpResponseRedirect(url_page(p.rel_path))
return render(request, 'pages/page_rename.html', context=context)
else:
messages.permission_denied_msg_page(request, rel_path)
return HttpResponseRedirect(url_page(rel_path))
def search(request): def search(request):
context = Context(request) # needs to be executed first because of time mesurement context = Context(request) # needs to be executed first because of time mesurement
# #

View File

@ -30,6 +30,8 @@ urlpatterns = [
path('page/', 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('page/<path:rel_path>', pages.views.page, name='page-page'),
path('pageedit/<path:rel_path>/', pages.views.edit, name='page-edit'), path('pageedit/<path:rel_path>/', pages.views.edit, name='page-edit'),
path('pagedelete/<path:rel_path>/', pages.views.delete, name='page-delete'),
path('pagerename/<path:rel_path>/', pages.views.rename, name='page-rename'),
path('helpview/', pages.views.helpview, name='page-helpview'), path('helpview/', pages.views.helpview, name='page-helpview'),
path('helpview/<str:page>', pages.views.helpview, name='page-helpview'), path('helpview/<str:page>', pages.views.helpview, name='page-helpview'),
# theme # theme