History view and recovery implemented

This commit is contained in:
Dirk Alders 2024-10-11 21:28:04 +02:00
parent a0401241ef
commit b7e58e256c
4 changed files with 125 additions and 40 deletions

View File

@ -3,19 +3,23 @@ from django.urls.base import reverse
import zoneinfo import zoneinfo
def url_page(request, rel_path, **kwargs): def params(**kwargs):
params = "&".join([f"{key}" + ("" if kwargs[key] is None else f"={kwargs[key]}") for key in kwargs]) params = "&".join([f"{key}" + ("" if kwargs[key] is None else f"={kwargs[key]}") for key in kwargs])
if len(params) > 0: if len(params) > 0:
params = "?" + params params = "?" + params
return reverse('page-page', kwargs={'rel_path': rel_path}) + params return params
def url_page(request, rel_path, **kwargs):
return reverse('page-page', kwargs={'rel_path': rel_path}) + params(**kwargs)
def url_helpview(request, page): def url_helpview(request, page):
return reverse('page-helpview', kwargs={'page': page}) return reverse('page-helpview', kwargs={'page': page})
def url_edit(request, rel_path): def url_edit(request, rel_path, **kwargs):
return reverse('page-edit', kwargs={'rel_path': rel_path}) return reverse('page-edit', kwargs={'rel_path': rel_path}) + params(**kwargs)
def get_search_query(request): def get_search_query(request):

View File

@ -1,5 +1,6 @@
from django.contrib import messages from django.contrib import messages
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
import pages
def permission_denied_msg_page(request, rel_path): def permission_denied_msg_page(request, rel_path):
@ -20,3 +21,11 @@ def edit_success(request):
def edit_no_change(request): def edit_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 history_version_display(request, rel_path, history_version):
# 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.") % (
history_version,
pages.url_edit(request, rel_path, history=history_version)
))

View File

@ -60,7 +60,9 @@ class base_page(object):
HISTORY_FOLDER_NAME = 'history' HISTORY_FOLDER_NAME = 'history'
SPLITCHAR = ":" SPLITCHAR = ":"
def __init__(self, path): def __init__(self, path, history_version=None):
self._history_version = history_version
#
if path.startswith(settings.PAGES_ROOT): if path.startswith(settings.PAGES_ROOT):
self._path = path self._path = path
else: else:
@ -69,6 +71,10 @@ class base_page(object):
# #
self._meta_data = meta_data(self._meta_filename, self.is_available()) self._meta_data = meta_data(self._meta_filename, self.is_available())
@property
def modified_time(self):
return self._meta_data.get(self._meta_data.KEY_MODIFIED_TIME)
def _load_page_src(self): def _load_page_src(self):
if self._raw_page_src is None: if self._raw_page_src is None:
try: try:
@ -77,22 +83,26 @@ class base_page(object):
except FileNotFoundError: except FileNotFoundError:
self._raw_page_src = "" self._raw_page_src = ""
def _store_history(self): def history_numbers_list(self):
history_folder = os.path.join(self._path, self.HISTORY_FOLDER_NAME) history_folder = os.path.join(self._path, self.HISTORY_FOLDER_NAME)
# create folder if needed
fstools.mkdir(history_folder) fstools.mkdir(history_folder)
# identify last_history number # identify last_history number
flist = fstools.filelist(history_folder) return list(set([int(os.path.basename(filename)[:5]) for filename in fstools.filelist(history_folder)]))
flist.sort()
if flist: def _store_history(self):
hist_number = int(os.path.basename(flist[-1])[:5]) + 1 try:
else: hist_number = max(self.history_numbers_list()) + 1
hist_number = 1 except ValueError:
hist_number = 1 # no history yet
# copy file to history folder # copy file to history folder
shutil.copy(self.filename, os.path.join(history_folder, "%05d_%s" % (hist_number, self.PAGE_FILE_NAME))) shutil.copy(self.filename, self.history_filename(hist_number))
shutil.copy(self._meta_filename, os.path.join(history_folder, "%05d_%s" % (hist_number, self.META_FILE_NAME))) shutil.copy(self._meta_filename, self._history_meta_filename(hist_number))
def update_page(self, page_txt, tags): def update_page(self, page_txt, tags):
if self._history_version:
logger.error("A history version %05d can not be updated!", self._history_version)
return False
else:
from .search import update_item from .search import update_item
if page_txt.replace("\r\n", "\n") != self.raw_page_src: if page_txt.replace("\r\n", "\n") != self.raw_page_src:
# Store page history # Store page history
@ -113,11 +123,23 @@ class base_page(object):
@property @property
def filename(self): def filename(self):
if not self._history_version:
return os.path.join(self._path, self.PAGE_FILE_NAME) return os.path.join(self._path, self.PAGE_FILE_NAME)
else:
return self.history_filename(self._history_version)
def history_filename(self, history_version):
return os.path.join(self._path, self.HISTORY_FOLDER_NAME, "%05d_%s" % (history_version, self.PAGE_FILE_NAME))
@property @property
def _meta_filename(self): def _meta_filename(self):
if not self._history_version:
return os.path.join(self._path, self.META_FILE_NAME) return os.path.join(self._path, self.META_FILE_NAME)
else:
return self._history_meta_filename(self._history_version)
def _history_meta_filename(self, history_version):
return os.path.join(self._path, self.HISTORY_FOLDER_NAME, "%05d_%s" % (history_version, self.META_FILE_NAME))
@property @property
def rel_path(self): def rel_path(self):
@ -160,9 +182,9 @@ class base_page(object):
class creole_page(base_page): class creole_page(base_page):
FOLDER_ATTACHMENTS = "attachments" FOLDER_ATTACHMENTS = "attachments"
def __init__(self, request, path) -> None: def __init__(self, request, path, history_version=None) -> None:
self._request = request self._request = request
super().__init__(path) super().__init__(path, history_version=history_version)
@property @property
def attachment_path(self): def attachment_path(self):
@ -181,12 +203,51 @@ class creole_page(base_page):
user = self._meta_data.get(self._meta_data.KEY_MODIFIED_USER) user = self._meta_data.get(self._meta_data.KEY_MODIFIED_USER)
tags = self._meta_data.get(self._meta_data.KEY_TAGS, "-") tags = self._meta_data.get(self._meta_data.KEY_TAGS, "-")
# #
meta = f'|{_("Created")}:|{ctime}|\n' meta = f'=== {_("Meta data")}\n'
meta += f'|{_("Created")}:|{ctime}|\n'
meta += f'|{_("Modified")}:|{mtime}|\n' meta += f'|{_("Modified")}:|{mtime}|\n'
meta += f'|{_("Editor")}|{user}|\n' meta += f'|{_("Editor")}|{user}|\n'
meta += f'|{_("Tags")}|{tags}|\n' meta += f'|{_("Tags")}|{tags}|\n'
#
hnl = self.history_numbers_list()
if hnl:
meta += f'=== {_("History")}\n'
meta += f'| ={_("Version")} | ={_("Date")} | ={_("Page")} | ={_("Meta data")} | \n'
# Current
name = _("Current")
meta += f"| {name} \
| {timestamp_to_datetime(self._request, self.modified_time)} \
| [[{url_page(self._request, self.rel_path)} | Page]] \
| [[{url_page(self._request, self.rel_path, meta=None)} | Meta]]\n"
# History
for num in reversed(hnl):
p = creole_page(self._request, self._path, history_version=num)
meta += f"| {num} \
| {timestamp_to_datetime(self._request, p.modified_time)} \
| [[{url_page(self._request, p.rel_path, history=num)} | Page]] \
| [[{url_page(self._request, p.rel_path, meta=None, history=num)} | Meta]]\n"
#
meta += f'=== {_("Page content")}\n' meta += f'=== {_("Page content")}\n'
if not self._history_version:
meta += '{{{\n%s\n}}}\n' % self.raw_page_src meta += '{{{\n%s\n}}}\n' % self.raw_page_src
else:
c = creole_page(self._request, self.rel_path)
meta += "| =Current | =This |\n"
left_lines = c.raw_page_src.splitlines()
right_lines = self.raw_page_src.splitlines()
while len(left_lines) + len(right_lines) > 0:
try:
left = left_lines.pop(0)
except IndexError:
left = ""
try:
right = right_lines.pop(0)
except IndexError:
right = ""
if left == right:
meta += "| {{{ %s }}} | {{{ %s }}} |\n" % (left, right)
else:
meta += "| **{{{ %s }}}** | **{{{ %s }}}** |\n" % (left, right)
# #
return mycreole.render_simple(meta) return mycreole.render_simple(meta)

View File

@ -30,13 +30,18 @@ def page(request, rel_path):
context = Context(request) # needs to be executed first because of time mesurement context = Context(request) # needs to be executed first because of time mesurement
# #
meta = "meta" in request.GET meta = "meta" in request.GET
history = request.GET.get("history")
if history:
history = int(history)
# #
p = creole_page(request, rel_path) p = creole_page(request, rel_path, history_version=history)
if access.read_page(request, rel_path): if access.read_page(request, rel_path):
if meta: if meta:
page_content = p.render_meta() page_content = p.render_meta()
else: else:
page_content = p.render_to_html() page_content = p.render_to_html()
if history:
messages.history_version_display(request, rel_path, history)
else: else:
messages.permission_denied_msg_page(request, rel_path) messages.permission_denied_msg_page(request, rel_path)
page_content = "" page_content = ""
@ -56,9 +61,13 @@ def edit(request, rel_path):
if access.write_page(request, rel_path): if access.write_page(request, rel_path):
context = Context(request) # needs to be executed first because of time mesurement context = Context(request) # needs to be executed first because of time mesurement
# #
p = creole_page(request, rel_path)
#
if not request.POST: if not request.POST:
history = request.GET.get("history")
if history:
history = int(history)
#
p = creole_page(request, rel_path, history_version=history)
#
form = EditForm(page_data=p.raw_page_src, page_tags=p.page_tags) form = EditForm(page_data=p.raw_page_src, page_tags=p.page_tags)
# #
context_adaption( context_adaption(
@ -71,6 +80,8 @@ def edit(request, rel_path):
) )
return render(request, 'pages/page_form.html', context=context) return render(request, 'pages/page_form.html', context=context)
else: else:
p = creole_page(request, rel_path)
#
save = request.POST.get("save") save = request.POST.get("save")
page_txt = request.POST.get("page_txt") page_txt = request.POST.get("page_txt")
tags = request.POST.get("page_tags") tags = request.POST.get("page_tags")