History view and recovery implemented
This commit is contained in:
parent
a0401241ef
commit
b7e58e256c
@ -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):
|
||||||
|
@ -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)
|
||||||
|
))
|
||||||
|
127
pages/page.py
127
pages/page.py
@ -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,47 +83,63 @@ 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):
|
||||||
from .search import update_item
|
if self._history_version:
|
||||||
if page_txt.replace("\r\n", "\n") != self.raw_page_src:
|
logger.error("A history version %05d can not be updated!", self._history_version)
|
||||||
# Store page history
|
return False
|
||||||
if self.raw_page_src:
|
|
||||||
self._store_history()
|
|
||||||
# save the new page content
|
|
||||||
fstools.mkdir(os.path.dirname(self.filename))
|
|
||||||
with open(self.filename, 'w') as fh:
|
|
||||||
fh.write(page_txt)
|
|
||||||
# update metadata
|
|
||||||
page_changed = True
|
|
||||||
else:
|
else:
|
||||||
page_changed = False
|
from .search import update_item
|
||||||
self._update_metadata(tags)
|
if page_txt.replace("\r\n", "\n") != self.raw_page_src:
|
||||||
# update search index
|
# Store page history
|
||||||
update_item(self)
|
if self.raw_page_src:
|
||||||
return page_changed
|
self._store_history()
|
||||||
|
# save the new page content
|
||||||
|
fstools.mkdir(os.path.dirname(self.filename))
|
||||||
|
with open(self.filename, 'w') as fh:
|
||||||
|
fh.write(page_txt)
|
||||||
|
# update metadata
|
||||||
|
page_changed = True
|
||||||
|
else:
|
||||||
|
page_changed = False
|
||||||
|
self._update_metadata(tags)
|
||||||
|
# update search index
|
||||||
|
update_item(self)
|
||||||
|
return page_changed
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def filename(self):
|
def filename(self):
|
||||||
return os.path.join(self._path, self.PAGE_FILE_NAME)
|
if not self._history_version:
|
||||||
|
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):
|
||||||
return os.path.join(self._path, self.META_FILE_NAME)
|
if not self._history_version:
|
||||||
|
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'
|
||||||
meta += '{{{\n%s\n}}}\n' % self.raw_page_src
|
if not self._history_version:
|
||||||
|
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)
|
||||||
|
|
||||||
|
@ -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")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user