Ver código fonte

History view and recovery implemented

master
Dirk Alders 2 meses atrás
pai
commit
b7e58e256c
4 arquivos alterados com 125 adições e 40 exclusões
  1. 8
    4
      pages/__init__.py
  2. 9
    0
      pages/messages.py
  3. 94
    33
      pages/page.py
  4. 14
    3
      pages/views.py

+ 8
- 4
pages/__init__.py Ver arquivo

@@ -3,19 +3,23 @@ from django.urls.base import reverse
3 3
 import zoneinfo
4 4
 
5 5
 
6
-def url_page(request, rel_path, **kwargs):
6
+def params(**kwargs):
7 7
     params = "&".join([f"{key}" + ("" if kwargs[key] is None else f"={kwargs[key]}") for key in kwargs])
8 8
     if len(params) > 0:
9 9
         params = "?" + params
10
-    return reverse('page-page', kwargs={'rel_path': rel_path}) + params
10
+    return params
11
+
12
+
13
+def url_page(request, rel_path, **kwargs):
14
+    return reverse('page-page', kwargs={'rel_path': rel_path}) + params(**kwargs)
11 15
 
12 16
 
13 17
 def url_helpview(request, page):
14 18
     return reverse('page-helpview', kwargs={'page': page})
15 19
 
16 20
 
17
-def url_edit(request, rel_path):
18
-    return reverse('page-edit', kwargs={'rel_path': rel_path})
21
+def url_edit(request, rel_path, **kwargs):
22
+    return reverse('page-edit', kwargs={'rel_path': rel_path}) + params(**kwargs)
19 23
 
20 24
 
21 25
 def get_search_query(request):

+ 9
- 0
pages/messages.py Ver arquivo

@@ -1,5 +1,6 @@
1 1
 from django.contrib import messages
2 2
 from django.utils.translation import gettext as _
3
+import pages
3 4
 
4 5
 
5 6
 def permission_denied_msg_page(request, rel_path):
@@ -20,3 +21,11 @@ def edit_success(request):
20 21
 def edit_no_change(request):
21 22
     # TODO: Add translation for this message
22 23
     messages.info(request, _("Nothing changed, no storage needed."))
24
+
25
+
26
+def history_version_display(request, rel_path, history_version):
27
+    # TODO: Add translation for this message
28
+    messages.warning(request, _("You see an old version of the page (Version = %d). Click <a href='%s'>here</a> to recover this Version.") % (
29
+        history_version,
30
+        pages.url_edit(request, rel_path, history=history_version)
31
+    ))

+ 94
- 33
pages/page.py Ver arquivo

@@ -60,7 +60,9 @@ class base_page(object):
60 60
     HISTORY_FOLDER_NAME = 'history'
61 61
     SPLITCHAR = ":"
62 62
 
63
-    def __init__(self, path):
63
+    def __init__(self, path, history_version=None):
64
+        self._history_version = history_version
65
+        #
64 66
         if path.startswith(settings.PAGES_ROOT):
65 67
             self._path = path
66 68
         else:
@@ -69,6 +71,10 @@ class base_page(object):
69 71
         #
70 72
         self._meta_data = meta_data(self._meta_filename, self.is_available())
71 73
 
74
+    @property
75
+    def modified_time(self):
76
+        return self._meta_data.get(self._meta_data.KEY_MODIFIED_TIME)
77
+
72 78
     def _load_page_src(self):
73 79
         if self._raw_page_src is None:
74 80
             try:
@@ -77,47 +83,63 @@ class base_page(object):
77 83
             except FileNotFoundError:
78 84
                 self._raw_page_src = ""
79 85
 
80
-    def _store_history(self):
86
+    def history_numbers_list(self):
81 87
         history_folder = os.path.join(self._path, self.HISTORY_FOLDER_NAME)
82
-        # create folder if needed
83 88
         fstools.mkdir(history_folder)
84 89
         # identify last_history number
85
-        flist = fstools.filelist(history_folder)
86
-        flist.sort()
87
-        if flist:
88
-            hist_number = int(os.path.basename(flist[-1])[:5]) + 1
89
-        else:
90
-            hist_number = 1
90
+        return list(set([int(os.path.basename(filename)[:5]) for filename in fstools.filelist(history_folder)]))
91
+
92
+    def _store_history(self):
93
+        try:
94
+            hist_number = max(self.history_numbers_list()) + 1
95
+        except ValueError:
96
+            hist_number = 1     # no history yet
91 97
         # copy file to history folder
92
-        shutil.copy(self.filename, os.path.join(history_folder, "%05d_%s" % (hist_number, self.PAGE_FILE_NAME)))
93
-        shutil.copy(self._meta_filename, os.path.join(history_folder, "%05d_%s" % (hist_number, self.META_FILE_NAME)))
98
+        shutil.copy(self.filename, self.history_filename(hist_number))
99
+        shutil.copy(self._meta_filename, self._history_meta_filename(hist_number))
94 100
 
95 101
     def update_page(self, page_txt, tags):
96
-        from .search import update_item
97
-        if page_txt.replace("\r\n", "\n") != self.raw_page_src:
98
-            # Store page history
99
-            if self.raw_page_src:
100
-                self._store_history()
101
-            # save the new page content
102
-            fstools.mkdir(os.path.dirname(self.filename))
103
-            with open(self.filename, 'w') as fh:
104
-                fh.write(page_txt)
105
-            # update metadata
106
-            page_changed = True
102
+        if self._history_version:
103
+            logger.error("A history version %05d can not be updated!", self._history_version)
104
+            return False
107 105
         else:
108
-            page_changed = False
109
-        self._update_metadata(tags)
110
-        # update search index
111
-        update_item(self)
112
-        return page_changed
106
+            from .search import update_item
107
+            if page_txt.replace("\r\n", "\n") != self.raw_page_src:
108
+                # Store page history
109
+                if self.raw_page_src:
110
+                    self._store_history()
111
+                # save the new page content
112
+                fstools.mkdir(os.path.dirname(self.filename))
113
+                with open(self.filename, 'w') as fh:
114
+                    fh.write(page_txt)
115
+                # update metadata
116
+                page_changed = True
117
+            else:
118
+                page_changed = False
119
+            self._update_metadata(tags)
120
+            # update search index
121
+            update_item(self)
122
+            return page_changed
113 123
 
114 124
     @property
115 125
     def filename(self):
116
-        return os.path.join(self._path, self.PAGE_FILE_NAME)
126
+        if not self._history_version:
127
+            return os.path.join(self._path, self.PAGE_FILE_NAME)
128
+        else:
129
+            return self.history_filename(self._history_version)
130
+
131
+    def history_filename(self, history_version):
132
+        return os.path.join(self._path, self.HISTORY_FOLDER_NAME, "%05d_%s" % (history_version, self.PAGE_FILE_NAME))
117 133
 
118 134
     @property
119 135
     def _meta_filename(self):
120
-        return os.path.join(self._path, self.META_FILE_NAME)
136
+        if not self._history_version:
137
+            return os.path.join(self._path, self.META_FILE_NAME)
138
+        else:
139
+            return self._history_meta_filename(self._history_version)
140
+
141
+    def _history_meta_filename(self, history_version):
142
+        return os.path.join(self._path, self.HISTORY_FOLDER_NAME, "%05d_%s" % (history_version, self.META_FILE_NAME))
121 143
 
122 144
     @property
123 145
     def rel_path(self):
@@ -160,9 +182,9 @@ class base_page(object):
160 182
 class creole_page(base_page):
161 183
     FOLDER_ATTACHMENTS = "attachments"
162 184
 
163
-    def __init__(self, request, path) -> None:
185
+    def __init__(self, request, path, history_version=None) -> None:
164 186
         self._request = request
165
-        super().__init__(path)
187
+        super().__init__(path, history_version=history_version)
166 188
 
167 189
     @property
168 190
     def attachment_path(self):
@@ -181,12 +203,51 @@ class creole_page(base_page):
181 203
         user = self._meta_data.get(self._meta_data.KEY_MODIFIED_USER)
182 204
         tags = self._meta_data.get(self._meta_data.KEY_TAGS, "-")
183 205
         #
184
-        meta = f'|{_("Created")}:|{ctime}|\n'
206
+        meta = f'=== {_("Meta data")}\n'
207
+        meta += f'|{_("Created")}:|{ctime}|\n'
185 208
         meta += f'|{_("Modified")}:|{mtime}|\n'
186 209
         meta += f'|{_("Editor")}|{user}|\n'
187 210
         meta += f'|{_("Tags")}|{tags}|\n'
211
+        #
212
+        hnl = self.history_numbers_list()
213
+        if hnl:
214
+            meta += f'=== {_("History")}\n'
215
+            meta += f'| ={_("Version")} | ={_("Date")} | ={_("Page")} | ={_("Meta data")} | \n'
216
+            # Current
217
+            name = _("Current")
218
+            meta += f"| {name} \
219
+                      | {timestamp_to_datetime(self._request, self.modified_time)} \
220
+                      | [[{url_page(self._request, self.rel_path)} | Page]] \
221
+                      | [[{url_page(self._request, self.rel_path, meta=None)} | Meta]]\n"
222
+            # History
223
+            for num in reversed(hnl):
224
+                p = creole_page(self._request, self._path, history_version=num)
225
+                meta += f"| {num} \
226
+                          | {timestamp_to_datetime(self._request, p.modified_time)} \
227
+                          | [[{url_page(self._request, p.rel_path, history=num)} | Page]] \
228
+                          | [[{url_page(self._request, p.rel_path, meta=None, history=num)} | Meta]]\n"
229
+        #
188 230
         meta += f'=== {_("Page content")}\n'
189
-        meta += '{{{\n%s\n}}}\n' % self.raw_page_src
231
+        if not self._history_version:
232
+            meta += '{{{\n%s\n}}}\n' % self.raw_page_src
233
+        else:
234
+            c = creole_page(self._request, self.rel_path)
235
+            meta += "| =Current | =This |\n"
236
+            left_lines = c.raw_page_src.splitlines()
237
+            right_lines = self.raw_page_src.splitlines()
238
+            while len(left_lines) + len(right_lines) > 0:
239
+                try:
240
+                    left = left_lines.pop(0)
241
+                except IndexError:
242
+                    left = ""
243
+                try:
244
+                    right = right_lines.pop(0)
245
+                except IndexError:
246
+                    right = ""
247
+                if left == right:
248
+                    meta += "| {{{ %s }}} | {{{ %s }}} |\n" % (left, right)
249
+                else:
250
+                    meta += "| **{{{ %s }}}** | **{{{ %s }}}** |\n" % (left, right)
190 251
         #
191 252
         return mycreole.render_simple(meta)
192 253
 

+ 14
- 3
pages/views.py Ver arquivo

@@ -30,13 +30,18 @@ def page(request, rel_path):
30 30
     context = Context(request)      # needs to be executed first because of time mesurement
31 31
     #
32 32
     meta = "meta" in request.GET
33
+    history = request.GET.get("history")
34
+    if history:
35
+        history = int(history)
33 36
     #
34
-    p = creole_page(request, rel_path)
37
+    p = creole_page(request, rel_path, history_version=history)
35 38
     if access.read_page(request, rel_path):
36 39
         if meta:
37 40
             page_content = p.render_meta()
38 41
         else:
39 42
             page_content = p.render_to_html()
43
+        if history:
44
+            messages.history_version_display(request, rel_path, history)
40 45
     else:
41 46
         messages.permission_denied_msg_page(request, rel_path)
42 47
         page_content = ""
@@ -56,9 +61,13 @@ def edit(request, rel_path):
56 61
     if access.write_page(request, rel_path):
57 62
         context = Context(request)      # needs to be executed first because of time mesurement
58 63
         #
59
-        p = creole_page(request, rel_path)
60
-        #
61 64
         if not request.POST:
65
+            history = request.GET.get("history")
66
+            if history:
67
+                history = int(history)
68
+            #
69
+            p = creole_page(request, rel_path, history_version=history)
70
+            #
62 71
             form = EditForm(page_data=p.raw_page_src, page_tags=p.page_tags)
63 72
             #
64 73
             context_adaption(
@@ -71,6 +80,8 @@ def edit(request, rel_path):
71 80
             )
72 81
             return render(request, 'pages/page_form.html', context=context)
73 82
         else:
83
+            p = creole_page(request, rel_path)
84
+            #
74 85
             save = request.POST.get("save")
75 86
             page_txt = request.POST.get("page_txt")
76 87
             tags = request.POST.get("page_tags")

Carregando…
Cancelar
Salvar