Просмотр исходного кода

Delete and rename of pages added

master
Dirk Alders 2 месяцев назад
Родитель
Сommit
beb645e4f6

+ 8
- 0
pages/__init__.py Просмотреть файл

@@ -18,10 +18,18 @@ def url_helpview(page):
18 18
     return reverse('page-helpview', kwargs={'page': page})
19 19
 
20 20
 
21
+def url_delete(rel_path, **kwargs):
22
+    return reverse('page-delete', kwargs={'rel_path': rel_path}) + params(**kwargs)
23
+
24
+
21 25
 def url_edit(rel_path, **kwargs):
22 26
     return reverse('page-edit', kwargs={'rel_path': rel_path}) + params(**kwargs)
23 27
 
24 28
 
29
+def url_rename(rel_path, **kwargs):
30
+    return reverse('page-rename', kwargs={'rel_path': rel_path}) + params(**kwargs)
31
+
32
+
25 33
 def get_search_query(request):
26 34
     return request.GET.get('q')
27 35
 

+ 53
- 34
pages/context.py Просмотреть файл

@@ -73,13 +73,13 @@ def menubar(context, request, caller_name, **kwargs):
73 73
 def actionbar(context, request, caller_name, **kwargs):
74 74
     bar = context[context.ACTIONBAR]
75 75
     if not cms_mode_active(request):
76
-        if caller_name == 'page':
76
+        if caller_name in ['page', 'edit', 'delete', 'rename']:
77 77
             if access.write_page(request, kwargs["rel_path"]):
78
-                add_edit_menu(request, bar, kwargs["rel_path"])
78
+                add_page_menu(request, bar, kwargs["rel_path"], kwargs.get('is_available', False))
79 79
             if access.modify_attachment(request, kwargs["rel_path"]):
80
-                add_manageupload_menu(request, bar, kwargs['upload_path'])
80
+                add_manageupload_menu(request, bar, kwargs['upload_path'], kwargs.get('is_available', False))
81 81
             if access.read_page(request, kwargs["rel_path"]):
82
-                add_meta_menu(request, bar, kwargs["rel_path"])
82
+                add_meta_menu(request, bar, kwargs["rel_path"], kwargs.get('is_available', False))
83 83
         elif caller_name == 'helpview':
84 84
             actionbar_add_help(context, request, **kwargs)
85 85
         finalise_bar(request, bar)
@@ -137,49 +137,68 @@ def add_nav_links(request, bar, rel_path):
137 137
     )
138 138
 
139 139
 
140
-def add_edit_menu(request, bar, rel_path):
140
+def add_page_menu(request, bar, rel_path, is_available):
141 141
     bar.append_entry(
142 142
         EDIT_UID,                                   # uid
143 143
         _('Edit'),                                  # name
144 144
         color_icon_url(request, 'edit2.png'),       # icon
145 145
         pages.url_edit(rel_path),                   # url
146 146
         True,                                       # left
147
-        False                                       # active
148
-    )
149
-
150
-
151
-def add_manageupload_menu(request, bar, upload_path):
152
-    bar.append_entry(
153
-        ATTACHMENT_UID,                                                     # uid
154
-        _("Attachments"),                                                   # name
155
-        color_icon_url(request, 'upload.png'),                              # icon
156
-        mycreole.url_manage_uploads(request, upload_path),                  # url
157
-        True,                                                               # left
158
-        False,                                                              # active
147
+        request.path == pages.url_edit(rel_path)    # active
159 148
     )
160
-
161
-
162
-def add_meta_menu(request, bar, rel_path):
163
-    if "meta" in request.GET:
149
+    if is_available:
164 150
         bar.append_entry(
165
-            EDIT_UID,                                       # uid
166
-            _('Page'),                                      # name
167
-            color_icon_url(request, 'display.png'),         # icon
168
-            pages.url_page(rel_path),                       # url
169
-            True,                                           # left
170
-            False                                           # active
151
+            EDIT_UID,                                   # uid
152
+            _('Rename'),                                # name
153
+            color_icon_url(request, 'shuffle.png'),     # icon
154
+            pages.url_rename(rel_path),                 # url
155
+            True,                                       # left
156
+            request.path == pages.url_rename(rel_path)  # active
171 157
         )
172
-    else:
173 158
         bar.append_entry(
174
-            EDIT_UID,                                       # uid
175
-            _('Meta'),                                      # name
176
-            color_icon_url(request, 'info.png'),            # icon
177
-            pages.url_page(rel_path, meta=None),            # url
178
-            True,                                           # left
179
-            False                                           # active
159
+            EDIT_UID,                                   # uid
160
+            _('Delete'),                                # name
161
+            color_icon_url(request, 'delete.png'),      # icon
162
+            pages.url_delete(rel_path),                 # url
163
+            True,                                       # left
164
+            request.path == pages.url_delete(rel_path)  # active
180 165
         )
181 166
 
182 167
 
168
+def add_manageupload_menu(request, bar, upload_path, is_available):
169
+    if is_available:
170
+        bar.append_entry(
171
+            ATTACHMENT_UID,                                                     # uid
172
+            _("Attachments"),                                                   # name
173
+            color_icon_url(request, 'upload.png'),                              # icon
174
+            mycreole.url_manage_uploads(request, upload_path),                  # url
175
+            True,                                                               # left
176
+            False,                                                              # active
177
+        )
178
+
179
+
180
+def add_meta_menu(request, bar, rel_path, is_available):
181
+    if is_available:
182
+        if "meta" in request.GET:
183
+            bar.append_entry(
184
+                EDIT_UID,                                       # uid
185
+                _('Page'),                                      # name
186
+                color_icon_url(request, 'display.png'),         # icon
187
+                pages.url_page(rel_path),                       # url
188
+                True,                                           # left
189
+                False                                           # active
190
+            )
191
+        else:
192
+            bar.append_entry(
193
+                EDIT_UID,                                       # uid
194
+                _('Meta'),                                      # name
195
+                color_icon_url(request, 'info.png'),            # icon
196
+                pages.url_page(rel_path, meta=None),            # url
197
+                True,                                           # left
198
+                False                                           # active
199
+            )
200
+
201
+
183 202
 def finalise_bar(request, bar):
184 203
     if len(bar) == 0:
185 204
         bar.append_entry(*empty_entry_parameters(request))

+ 10
- 1
pages/forms.py Просмотреть файл

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

+ 16
- 1
pages/messages.py Просмотреть файл

@@ -18,11 +18,26 @@ def edit_success(request):
18 18
     messages.success(request, _('Thanks for editing, page stored.'))
19 19
 
20 20
 
21
-def edit_no_change(request):
21
+def no_change(request):
22 22
     # TODO: Add translation for this message
23 23
     messages.info(request, _("Nothing changed, no storage needed."))
24 24
 
25 25
 
26
+def operation_canceled(request):
27
+    # TODO: Add translation for this message
28
+    messages.info(request, _('Operation caneled, no change to the content.'))
29
+
30
+
31
+def page_deleted(request, title):
32
+    # TODO: Add translation for this message
33
+    messages.info(request, _('The page "%s" has been deleted.') % title)
34
+
35
+
36
+def page_renamed(request):
37
+    # TODO: Add translation for this message
38
+    messages.info(request, _('The page has been renamed.'))
39
+
40
+
26 41
 def history_version_display(request, rel_path, history_version):
27 42
     # TODO: Add translation for this message
28 43
     messages.warning(request, _("You see an old version of the page (Version = %d). Click <a href='%s'>here</a> to recover this Version.") % (

+ 47
- 3
pages/page.py Просмотреть файл

@@ -22,8 +22,24 @@ def full_path_all_pages(expression="*"):
22 22
     system_pages = fstools.dirlist(settings.SYSTEM_PAGES_ROOT, expression=expression, rekursive=False)
23 23
     system_pages = [os.path.join(settings.PAGES_ROOT, os.path.basename(path)) for path in system_pages]
24 24
     pages = fstools.dirlist(settings.PAGES_ROOT, expression=expression, rekursive=False)
25
-    # TODO: strip path, if page or meta.json is missing
26
-    return list(set(system_pages + pages))
25
+    rv = []
26
+    for path in set(system_pages + pages):
27
+        p = page_wrapped(None, path)
28
+        if p.is_available():
29
+            rv.append(path)
30
+    return rv
31
+
32
+
33
+def full_path_deleted_pages(expression="*"):
34
+    system_pages = fstools.dirlist(settings.SYSTEM_PAGES_ROOT, expression=expression, rekursive=False)
35
+    system_pages = [os.path.join(settings.PAGES_ROOT, os.path.basename(path)) for path in system_pages]
36
+    pages = fstools.dirlist(settings.PAGES_ROOT, expression=expression, rekursive=False)
37
+    rv = []
38
+    for path in set(system_pages + pages):
39
+        p = page_wrapped(None, path)
40
+        if not p.is_available():
41
+            rv.append(path)
42
+    return rv
27 43
 
28 44
 
29 45
 class meta_data(dict):
@@ -45,6 +61,9 @@ class meta_data(dict):
45 61
         except (FileNotFoundError, json.decoder.JSONDecodeError) as e:
46 62
             super().__init__()
47 63
 
64
+    def delete(self):
65
+        os.remove(self.filename)
66
+
48 67
     @property
49 68
     def filename(self):
50 69
         if not self._history_version:
@@ -106,6 +125,20 @@ class page_data(object):
106 125
             except FileNotFoundError:
107 126
                 self._raw_page_src = ""
108 127
 
128
+    def delete(self):
129
+        os.remove(self.filename)
130
+
131
+    def rename(self, page_name):
132
+        # Change backslash to slash and remove double slashes
133
+        page_name = page_name.replace("\\", "/")
134
+        while "//" in page_name:
135
+            page_name = page_name.replace("//", "/")
136
+        # move path
137
+        target_path = os.path.join(settings.PAGES_ROOT, page_name.replace("/", 2*SPLITCHAR))
138
+        shutil.move(self._path, target_path)
139
+        # set my path
140
+        self._path = target_path
141
+
109 142
     def update_required(self, page_txt):
110 143
         return page_txt.replace("\r\n", "\n") != self.raw_page_src
111 144
 
@@ -143,7 +176,7 @@ class page_data(object):
143 176
 
144 177
     @property
145 178
     def title(self):
146
-        return os.path.basename(self._path).split("::")[-1]
179
+        return os.path.basename(self._path).split(2*SPLITCHAR)[-1]
147 180
 
148 181
     @property
149 182
     def raw_page_src(self):
@@ -421,6 +454,11 @@ class page_wrapped(object):
421 454
         rv = meta.get(meta.KEY_CREATION_TIME)
422 455
         return rv
423 456
 
457
+    def delete(self):
458
+        self.__store_history__()
459
+        self._page.delete()
460
+        self._page_meta.delete()
461
+
424 462
     @property
425 463
     def modified_time(self):
426 464
         meta = self.__meta_choose__()
@@ -433,6 +471,9 @@ class page_wrapped(object):
433 471
         rv = meta.get(meta.KEY_MODIFIED_USER)
434 472
         return rv
435 473
 
474
+    def rename(self, page_name):
475
+        self._page.rename(page_name)
476
+
436 477
     @property
437 478
     def tags(self):
438 479
         meta = self.__meta_choose__()
@@ -451,6 +492,9 @@ class page_wrapped(object):
451 492
     def is_available(self):
452 493
         return self._page.is_available() or self._system_page.is_available()
453 494
 
495
+    def userpage_is_available(self):
496
+        return self._page.is_available()
497
+
454 498
     @property
455 499
     def raw_page_src(self):
456 500
         page = self.__page_choose__()

+ 1
- 1
pages/search.py Просмотреть файл

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

+ 16
- 0
pages/templates/pages/page_delete.html Просмотреть файл

@@ -0,0 +1,16 @@
1
+{% extends "themes/"|add:settings.page_theme|add:"/base.html" %}
2
+{% load static %}
3
+{% load i18n %}
4
+
5
+{% block content %}
6
+  <form class="form" method="post">
7
+    {% csrf_token %}
8
+    {{ form.as_p }}
9
+    <label for="id_page_name">{% trans "Do you really want to delete this page?" %}</label>
10
+    <input type="submit" name="delete" value="{% trans "Delete" %}" class="button" />
11
+    <input type="submit" name="cancel" value="{% trans "Cancel" %}" class="button" />
12
+  </form>
13
+
14
+  {{ page_content|safe }}
15
+
16
+{% endblock content %}

pages/templates/pages/page_form.html → pages/templates/pages/page_edit.html Просмотреть файл


+ 15
- 0
pages/templates/pages/page_rename.html Просмотреть файл

@@ -0,0 +1,15 @@
1
+{% extends "themes/"|add:settings.page_theme|add:"/base.html" %}
2
+{% load static %}
3
+{% load i18n %}
4
+
5
+{% block content %}
6
+  <form class="form" method="post">
7
+    {% csrf_token %}
8
+    {{ form.as_p }}
9
+    <input type="submit" name="rename" value="{% trans "Rename" %}" class="button" />
10
+    <input type="submit" name="cancel" value="{% trans "Cancel" %}" class="button" />
11
+  </form>
12
+
13
+  {{ page_content|safe }}
14
+    
15
+{% endblock content %}

+ 101
- 6
pages/views.py Просмотреть файл

@@ -12,11 +12,11 @@ from . import url_page
12 12
 from . import get_search_query
13 13
 import config
14 14
 from .context import context_adaption
15
-from .forms import EditForm
15
+from .forms import EditForm, RenameForm
16 16
 from .help import help_pages
17 17
 import mycreole
18 18
 from .page import page_wrapped, page_list
19
-from .search import whoosh_search
19
+from .search import whoosh_search, load_index, delete_item, add_item
20 20
 from themes import Context
21 21
 
22 22
 logger = logging.getLogger(settings.ROOT_LOGGER_NAME).getChild(__name__)
@@ -52,7 +52,8 @@ def page(request, rel_path):
52 52
         rel_path=rel_path,
53 53
         title=p.title,
54 54
         upload_path=p.attachment_path,
55
-        page_content=page_content
55
+        page_content=page_content,
56
+        is_available=p.userpage_is_available()
56 57
     )
57 58
     return render(request, 'pages/page.html', context=context)
58 59
 
@@ -73,12 +74,14 @@ def edit(request, rel_path):
73 74
             context_adaption(
74 75
                 context,
75 76
                 request,
77
+                rel_path=rel_path,
78
+                is_available=p.userpage_is_available(),
76 79
                 form=form,
77 80
                 # TODO: Add translation
78 81
                 title=_("Edit page %s") % repr(p.title),
79 82
                 upload_path=p.attachment_path,
80 83
             )
81
-            return render(request, 'pages/page_form.html', context=context)
84
+            return render(request, 'pages/page_edit.html', context=context)
82 85
         else:
83 86
             p = page_wrapped(request, rel_path)
84 87
             #
@@ -91,7 +94,7 @@ def edit(request, rel_path):
91 94
                 if p.update_page(page_txt, tags):
92 95
                     messages.edit_success(request)
93 96
                 else:
94
-                    messages.edit_no_change(request)
97
+                    messages.no_change(request)
95 98
                 return HttpResponseRedirect(url_page(rel_path))
96 99
             elif preview is not None:
97 100
                 form = EditForm(page_data=page_txt, page_tags=tags)
@@ -99,13 +102,15 @@ def edit(request, rel_path):
99 102
                 context_adaption(
100 103
                     context,
101 104
                     request,
105
+                    rel_path=rel_path,
106
+                    is_available=p.userpage_is_available(),
102 107
                     form=form,
103 108
                     # TODO: Add translation
104 109
                     title=_("Edit page %s") % repr(p.title),
105 110
                     upload_path=p.attachment_path,
106 111
                     page_content=p.render_text(request, page_txt)
107 112
                 )
108
-                return render(request, 'pages/page_form.html', context=context)
113
+                return render(request, 'pages/page_edit.html', context=context)
109 114
             else:
110 115
                 return HttpResponseRedirect(url_page(rel_path))
111 116
     else:
@@ -113,6 +118,96 @@ def edit(request, rel_path):
113 118
         return HttpResponseRedirect(url_page(rel_path))
114 119
 
115 120
 
121
+def delete(request, rel_path):
122
+    if access.write_page(request, rel_path):
123
+        context = Context(request)      # needs to be executed first because of time mesurement
124
+        #
125
+        if not request.POST:
126
+            p = page_wrapped(request, rel_path)
127
+            #
128
+            # form = DeleteForm(page_data=p.raw_page_src, page_tags=p.tags)
129
+            #
130
+            context_adaption(
131
+                context,
132
+                request,
133
+                rel_path=rel_path,
134
+                is_available=p.userpage_is_available(),
135
+                # form=form,
136
+                # TODO: Add translation
137
+                title=_("Delete page %s") % repr(p.title),
138
+                upload_path=p.attachment_path,
139
+                page_content=p.render_to_html(),
140
+            )
141
+        else:
142
+            p = page_wrapped(request, rel_path)
143
+            #
144
+            delete = request.POST.get("delete")
145
+            #
146
+            if delete:
147
+                # delete page from search index
148
+                ix = load_index()
149
+                delete_item(ix, p)
150
+                # delete move files to history
151
+                p.delete()
152
+                # add delete message
153
+                messages.page_deleted(request, p.title)
154
+                return HttpResponseRedirect("/")
155
+            else:
156
+                messages.operation_canceled(request)
157
+            return HttpResponseRedirect(url_page(rel_path))
158
+        return render(request, 'pages/page_delete.html', context=context)
159
+    else:
160
+        messages.permission_denied_msg_page(request, rel_path)
161
+        return HttpResponseRedirect(url_page(rel_path))
162
+
163
+
164
+def rename(request, rel_path):
165
+    if access.write_page(request, rel_path):
166
+        context = Context(request)      # needs to be executed first because of time mesurement
167
+        #
168
+        if not request.POST:
169
+            p = page_wrapped(request, rel_path)
170
+            #
171
+            form = RenameForm(page_name=p.rel_path)
172
+            #
173
+            context_adaption(
174
+                context,
175
+                request,
176
+                rel_path=rel_path,
177
+                is_available=p.userpage_is_available(),
178
+                form=form,
179
+                # TODO: Add translation
180
+                title=_("Delete page %s") % repr(p.title),
181
+                upload_path=p.attachment_path,
182
+                page_content=p.render_to_html(),
183
+            )
184
+        else:
185
+            p = page_wrapped(request, rel_path)
186
+            #
187
+            rename = request.POST.get("rename")
188
+            page_name = request.POST.get("page_name")
189
+            if rename:
190
+                if page_name == p.rel_path:
191
+                    messages.no_change(request)
192
+                else:
193
+                    # delete page from search index
194
+                    ix = load_index()
195
+                    delete_item(ix, p)
196
+                    # rename the storage folder
197
+                    p.rename(page_name)
198
+                    # add the renamed page to the search index
199
+                    add_item(ix, p)
200
+                    # add rename message
201
+                    messages.page_renamed(request)
202
+            else:
203
+                messages.operation_canceled(request)
204
+            return HttpResponseRedirect(url_page(p.rel_path))
205
+        return render(request, 'pages/page_rename.html', context=context)
206
+    else:
207
+        messages.permission_denied_msg_page(request, rel_path)
208
+        return HttpResponseRedirect(url_page(rel_path))
209
+
210
+
116 211
 def search(request):
117 212
     context = Context(request)      # needs to be executed first because of time mesurement
118 213
     #

+ 2
- 0
piki/urls.py Просмотреть файл

@@ -30,6 +30,8 @@ urlpatterns = [
30 30
     path('page/', pages.views.root, name='page-root'),
31 31
     path('page/<path:rel_path>', pages.views.page, name='page-page'),
32 32
     path('pageedit/<path:rel_path>/', pages.views.edit, name='page-edit'),
33
+    path('pagedelete/<path:rel_path>/', pages.views.delete, name='page-delete'),
34
+    path('pagerename/<path:rel_path>/', pages.views.rename, name='page-rename'),
33 35
     path('helpview/', pages.views.helpview, name='page-helpview'),
34 36
     path('helpview/<str:page>', pages.views.helpview, name='page-helpview'),
35 37
     # theme

Загрузка…
Отмена
Сохранить