Bläddra i källkod

Initial piki commit with first rough functionality

master
Dirk Alders 2 månader sedan
förälder
incheckning
eec925eef1
31 ändrade filer med 856 tillägg och 1 borttagningar
  1. 5
    0
      .gitignore
  2. 12
    0
      .gitmodules
  3. 17
    0
      .vscode/launch.json
  4. 14
    0
      .vscode/settings.json
  5. 3
    1
      README.md
  6. 12
    0
      config.py
  7. Binär
      data/media/theme/logo.png
  8. 1
    0
      fstools
  9. 22
    0
      manage.py
  10. 1
    0
      mycreole
  11. 9
    0
      pages/__init__.py
  12. 8
    0
      pages/access.py
  13. 3
    0
      pages/admin.py
  14. 6
    0
      pages/apps.py
  15. 289
    0
      pages/context.py
  16. 27
    0
      pages/creole.py
  17. 58
    0
      pages/help.py
  18. 0
    0
      pages/migrations/__init__.py
  19. 3
    0
      pages/models.py
  20. 47
    0
      pages/page.py
  21. 5
    0
      pages/templates/pages/page.html
  22. 3
    0
      pages/tests.py
  23. 56
    0
      pages/views.py
  24. 0
    0
      piki/__init__.py
  25. 16
    0
      piki/asgi.py
  26. 178
    0
      piki/settings.py
  27. 38
    0
      piki/urls.py
  28. 16
    0
      piki/wsgi.py
  29. 5
    0
      requirements.txt
  30. 1
    0
      themes
  31. 1
    0
      users

+ 5
- 0
.gitignore Visa fil

@@ -1,3 +1,8 @@
1
+# piki
2
+data/pages
3
+data/static
4
+db.sqlite3
5
+
1 6
 # ---> Python
2 7
 # Byte-compiled / optimized / DLL files
3 8
 __pycache__/

+ 12
- 0
.gitmodules Visa fil

@@ -0,0 +1,12 @@
1
+[submodule "fstools"]
2
+	path = fstools
3
+	url = https://git.mount-mockery.de/pylib/fstools.git
4
+[submodule "mycreole"]
5
+	path = mycreole
6
+	url = https://git.mount-mockery.de/django_lib/mycreole.git
7
+[submodule "themes"]
8
+	path = themes
9
+	url = https://git.mount-mockery.de/django_lib/themes.git
10
+[submodule "users"]
11
+	path = users
12
+	url = https://git.mount-mockery.de/django_lib/users.git

+ 17
- 0
.vscode/launch.json Visa fil

@@ -0,0 +1,17 @@
1
+{
2
+    // Verwendet IntelliSense zum Ermitteln möglicher Attribute.
3
+    // Zeigen Sie auf vorhandene Attribute, um die zugehörigen Beschreibungen anzuzeigen.
4
+    // Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=830387
5
+    "version": "0.2.0",
6
+    "configurations": [
7
+        {
8
+            "name": "Python: Main File execution",
9
+            "type": "debugpy",
10
+            "request": "launch",
11
+            "program": "${workspaceFolder}/manage.py",
12
+            "args": ["runserver"],
13
+            "console": "integratedTerminal",
14
+            "justMyCode": true
15
+        }
16
+    ]
17
+}

+ 14
- 0
.vscode/settings.json Visa fil

@@ -0,0 +1,14 @@
1
+{
2
+  "python.defaultInterpreterPath": "./venv/bin/python",
3
+  "autopep8.args": ["--max-line-length=150"],
4
+  "[python]": {
5
+    "python.formatting.provider": "none",
6
+    "editor.defaultFormatter": "ms-python.autopep8",
7
+    "editor.formatOnSave": true
8
+  },
9
+  "editor.fontSize": 14,
10
+  "emmet.includeLanguages": { "django-html": "html" },
11
+  "python.testing.pytestArgs": ["-v", "--cov", "--cov-report=xml", "__test__"],
12
+  "python.testing.unittestEnabled": false,
13
+  "python.testing.pytestEnabled": true
14
+}

+ 3
- 1
README.md Visa fil

@@ -1,3 +1,5 @@
1 1
 # piki
2 2
 
3
-Piki is a minimal wiki
3
+Piki is a minimal wiki
4
+
5
+# TODO: Add an installation instruction

+ 12
- 0
config.py Visa fil

@@ -0,0 +1,12 @@
1
+import os
2
+#
3
+#
4
+APP_NAME = "piki"
5
+STARTPAGE = "startpage"
6
+
7
+
8
+# SECURITY WARNING: don't run with debug turned on in production!
9
+DEBUG = True
10
+#
11
+SECRET_KEY = "*+=wy%2lo2^@fxxtmx0)14x507%6v73ke-%24%_fb6f+3h^c)-"
12
+#

Binär
data/media/theme/logo.png Visa fil


+ 1
- 0
fstools

@@ -0,0 +1 @@
1
+Subproject commit c10e8792abb05671dab6de51cdadda3bf8ead50f

+ 22
- 0
manage.py Visa fil

@@ -0,0 +1,22 @@
1
+#!/usr/bin/env python
2
+"""Django's command-line utility for administrative tasks."""
3
+import os
4
+import sys
5
+
6
+
7
+def main():
8
+    """Run administrative tasks."""
9
+    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'piki.settings')
10
+    try:
11
+        from django.core.management import execute_from_command_line
12
+    except ImportError as exc:
13
+        raise ImportError(
14
+            "Couldn't import Django. Are you sure it's installed and "
15
+            "available on your PYTHONPATH environment variable? Did you "
16
+            "forget to activate a virtual environment?"
17
+        ) from exc
18
+    execute_from_command_line(sys.argv)
19
+
20
+
21
+if __name__ == '__main__':
22
+    main()

+ 1
- 0
mycreole

@@ -0,0 +1 @@
1
+Subproject commit 057388e3b44b5fe42b133fe030c41aa6a254de58

+ 9
- 0
pages/__init__.py Visa fil

@@ -0,0 +1,9 @@
1
+from django.urls.base import reverse
2
+
3
+
4
+def url_page(request, rel_path):
5
+    return reverse('pages-pages', kwargs={'rel_path': rel_path})
6
+
7
+
8
+def url_helpview(request, page):
9
+    return reverse('pages-helpview', kwargs={'page': page})

+ 8
- 0
pages/access.py Visa fil

@@ -0,0 +1,8 @@
1
+# TODO: Implement access control for pages
2
+
3
+def read_attachment(request, rel_path):
4
+    return True
5
+
6
+
7
+def modify_attachment(request, rel_path):
8
+    return True

+ 3
- 0
pages/admin.py Visa fil

@@ -0,0 +1,3 @@
1
+from django.contrib import admin
2
+
3
+# Register your models here.

+ 6
- 0
pages/apps.py Visa fil

@@ -0,0 +1,6 @@
1
+from django.apps import AppConfig
2
+
3
+
4
+class PagesConfig(AppConfig):
5
+    default_auto_field = 'django.db.models.BigAutoField'
6
+    name = 'pages'

+ 289
- 0
pages/context.py Visa fil

@@ -0,0 +1,289 @@
1
+import inspect
2
+import logging
3
+
4
+from django.utils.translation import gettext as _
5
+
6
+
7
+from .help import actionbar as actionbar_add_help
8
+import mycreole
9
+import pages
10
+from themes import empty_entry_parameters, gray_icon_url, color_icon_url
11
+from users.context import menubar as menubar_users
12
+
13
+try:
14
+    from config import APP_NAME as ROOT_LOGGER_NAME
15
+except ImportError:
16
+    ROOT_LOGGER_NAME = 'root'
17
+logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
18
+
19
+ATTACHMENT_UID = 'attachment'
20
+BACK_UID = 'back'
21
+HELP_UID = 'help'
22
+
23
+
24
+def context_adaption(context, request, **kwargs):
25
+    caller_name = inspect.currentframe().f_back.f_code.co_name
26
+    try:
27
+        context.set_additional_title(kwargs.pop('title'))
28
+    except KeyError:
29
+        pass    # no title in kwargs
30
+    menubar_users(context[context.MENUBAR], request)
31
+    menubar(context, request, caller_name, **kwargs)
32
+    actionbar(context, request, caller_name, **kwargs)
33
+    navigationbar(context, request, caller_name, **kwargs)
34
+    for key in kwargs:
35
+        context[key] = kwargs[key]
36
+    logger.debug("context adapted: %s", repr(context))
37
+
38
+
39
+def navigationbar(context, request, caller_name, **kwargs):
40
+    bar = context[context.NAVIGATIONBAR]
41
+    add_back_menu(request, bar)
42
+    # TODO: Add the pages navigation, if source is pages
43
+    finalise_bar(request, bar)
44
+
45
+
46
+def add_back_menu(request, bar):
47
+    bar.append_entry(
48
+        BACK_UID,                                   # uid
49
+        _('Back'),                                  # name
50
+        gray_icon_url(request, 'back.png'),         # icon
51
+        'javascript:history.back()',                # url
52
+        True,                                       # left
53
+        False                                       # active
54
+    )
55
+
56
+
57
+def menubar(context, request, caller_name, **kwargs):
58
+    bar = context[context.MENUBAR]
59
+    # replace_profile(request, bar)
60
+    add_help_menu(request, bar)
61
+    # add_tasklist_menu(request, bar)
62
+    # add_filter_submenu(request, bar, VIEW_TASKLIST_UID)
63
+    # add_projectlist_menu(request, bar)
64
+    # add_printview_menu(request, bar)
65
+    finalise_bar(request, bar)
66
+
67
+
68
+def add_help_menu(request, bar):
69
+    bar.append_entry(
70
+        HELP_UID,                                   # uid
71
+        _('Help'),                                  # name
72
+        color_icon_url(request, 'help.png'),        # icon
73
+        pages.url_helpview(request, 'main'),        # url
74
+        True,                                       # left
75
+        False                                       # active
76
+    )
77
+
78
+
79
+def actionbar(context, request, caller_name, **kwargs):
80
+    bar = context[context.ACTIONBAR]
81
+    if caller_name == 'pages':
82
+        #    acc = acc_task(kwargs['task'], request.user)
83
+        #    if acc.modify or acc.modify_limited:
84
+        #        add_edittask_menu(request, bar, kwargs['task'].id)
85
+        #    if acc.add_comments:
86
+        #        add_newcomment_menu(request, bar, kwargs['task'].id)
87
+        add_manageupload_menu(request, bar, kwargs['upload_path'])
88
+    elif caller_name == 'helpview':
89
+        actionbar_add_help(context, request, **kwargs)
90
+    finalise_bar(request, bar)
91
+
92
+
93
+def add_manageupload_menu(request, bar, upload_path):
94
+    bar.append_entry(
95
+        ATTACHMENT_UID,                                                     # uid
96
+        _("Attachments"),                                                   # name
97
+        color_icon_url(request, 'upload.png'),                              # icon
98
+        mycreole.url_manage_uploads(request, upload_path),                  # url
99
+        True,                                                               # left
100
+        False,                                                              # active
101
+    )
102
+
103
+
104
+def finalise_bar(request, bar):
105
+    if len(bar) == 0:
106
+        bar.append_entry(*empty_entry_parameters(request))
107
+
108
+
109
+"""from .access import create_project_possible, create_task_possible, acc_task
110
+from django.db.models.functions import Lower
111
+import patt
112
+from .search import common_searches
113
+from themes import empty_entry_parameters, color_icon_url, gray_icon_url
114
+from users.context import PROFILE_ENTRY_UID
115
+
116
+
117
+COMMENTNEW_UID = 'commentnew'
118
+CREATE_PROJECT_UID = 'create-project'
119
+CREATE_TASK_UID = 'create-task'
120
+PRINTVIEW_UID = 'printview'
121
+TASKEDIT_UID = 'taskedit'
122
+VIEW_PROJECTLIST_UID = 'view-projectlist'
123
+VIEW_TASKLIST_UID = 'view-tasklist'
124
+
125
+
126
+
127
+
128
+
129
+
130
+
131
+
132
+def replace_profile(request, bar):
133
+    try:
134
+        bar.replace_entry(
135
+            PROFILE_ENTRY_UID,
136
+            PROFILE_ENTRY_UID,                          # uid
137
+            request.user.username,                      # name
138
+            color_icon_url(request, 'user.png'),        # icon
139
+            patt.url_profile(request),                  # url
140
+            False,                                      # left
141
+            False                                       # active
142
+        )
143
+    except ValueError:
144
+        pass        # Profile entry does not exist, so exchange is not needed (e.g. no user is logged in)
145
+
146
+
147
+
148
+
149
+def add_tasklist_menu(request, bar):
150
+    bar.append_entry(
151
+        VIEW_TASKLIST_UID,                          # uid
152
+        _('Tasklist'),                              # name
153
+        color_icon_url(request, 'task.png'),        # icon
154
+        patt.url_tasklist(request),                 # url
155
+        True,                                       # left
156
+        patt.is_tasklistview(request)               # active
157
+    )
158
+
159
+
160
+def add_projectlist_menu(request, bar):
161
+    bar.append_entry(
162
+        VIEW_PROJECTLIST_UID,                       # uid
163
+        _('Projectlist'),                           # name
164
+        color_icon_url(request, 'folder.png'),      # icon
165
+        patt.url_projectlist(request),              # url
166
+        True,                                       # left
167
+        patt.is_projectlistview(request)            # active
168
+    )
169
+
170
+
171
+def add_printview_menu(request, bar):
172
+    bar.append_entry(
173
+        PRINTVIEW_UID,                              # uid
174
+        _('Printview'),                             # name
175
+        color_icon_url(request, 'print.png'),       # icon
176
+        patt.url_printview(request),                # url
177
+        True,                                       # left
178
+        patt.is_printview(request)                  # active
179
+    )
180
+
181
+
182
+def add_newtask_menu(request, bar, project_id):
183
+    bar.append_entry(
184
+        CREATE_TASK_UID,                            # uid
185
+        _('New Task'),                              # name
186
+        color_icon_url(request, 'plus.png'),        # icon
187
+        patt.url_tasknew(request, project_id),      # url
188
+        True,                                       # left
189
+        False                                       # active
190
+    )
191
+
192
+
193
+def add_edittask_menu(request, bar, task_id):
194
+    bar.append_entry(
195
+        TASKEDIT_UID,                               # uid
196
+        _('Edit'),                                  # name
197
+        color_icon_url(request, 'edit.png'),        # icon
198
+        patt.url_taskedit(request, task_id),        # url
199
+        True,                                       # left
200
+        False                                       # active
201
+    )
202
+
203
+
204
+def add_newcomment_menu(request, bar, task_id):
205
+    bar.append_entry(
206
+        COMMENTNEW_UID,                             # uid
207
+        _('Add Comment'),                           # name
208
+        color_icon_url(request, 'edit2.png'),       # icon
209
+        patt.url_commentnew(request, task_id),      # url
210
+        True,                                       # left
211
+        False                                       # active
212
+    )
213
+
214
+
215
+def add_newproject_menu(request, bar):
216
+    bar.append_entry(
217
+        CREATE_PROJECT_UID,                         # uid
218
+        _('New Project'),                           # name
219
+        color_icon_url(request, 'plus.png'),        # icon
220
+        patt.url_projectnew(request),               # url
221
+        True,                                       # left
222
+        False                                       # active
223
+    )
224
+
225
+
226
+
227
+
228
+
229
+
230
+def add_filter_submenu(request, bar, menu_uid):
231
+    bar.append_entry_to_entry(
232
+        menu_uid,
233
+        menu_uid + '-easysearch',             # uid
234
+        _('Easysearch'),                        # name
235
+        gray_icon_url(request, 'search.png'),   # icon
236
+        patt.url_easysearch(request),           # url
237
+        True,                                   # left
238
+        False                                   # active
239
+    )
240
+    if patt.get_search_query(request) is not None:
241
+        bar.append_entry_to_entry(
242
+            menu_uid,
243
+            menu_uid + '-save',                     # uid
244
+            _('Save Search as Filter'),             # name
245
+            gray_icon_url(request, 'save.png'),     # icon
246
+            patt.url_filteredit(request),           # url
247
+            True,                                   # left
248
+            False                                   # active
249
+        )
250
+    bar.append_entry_to_entry(
251
+        menu_uid,
252
+        menu_uid + '-all',                      # uid
253
+        _('All Tasks'),                         # name
254
+        gray_icon_url(request, 'task.png'),     # icon
255
+        patt.url_tasklist(request),             # url
256
+        True,                                   # left
257
+        False                                   # active
258
+    )
259
+    cs = common_searches(request)
260
+    for common_filter_id in cs:
261
+        bar.append_entry_to_entry(
262
+            menu_uid,
263
+            menu_uid + '-common',                                           # uid
264
+            _(cs[common_filter_id][0]),                                     # name
265
+            gray_icon_url(request, 'filter.png'),                           # icon
266
+            patt.url_tasklist(request, common_filter_id=common_filter_id),  # url
267
+            True,                                                           # left
268
+            False                                                           # active
269
+        )
270
+    for s in request.user.search_set.order_by(Lower('name')):
271
+        active = patt.is_tasklistview(request, s.id)
272
+        if active is True:
273
+            url = patt.url_filteredit(request, s.id)
274
+        else:
275
+            url = patt.url_tasklist(request, user_filter_id=s.id)
276
+        if active:
277
+            icon = 'settings.png'
278
+        else:
279
+            icon = 'favourite.png'
280
+        bar.append_entry_to_entry(
281
+            menu_uid,
282
+            menu_uid + '-sub',              # uid
283
+            s.name,                         # name
284
+            gray_icon_url(request, icon),   # icon
285
+            url,                            # url
286
+            True,                           # left
287
+            active                          # active
288
+        )
289
+"""

+ 27
- 0
pages/creole.py Visa fil

@@ -0,0 +1,27 @@
1
+from django.urls.base import reverse
2
+
3
+# TODO: Add a filter to show all subpages <<piki-subpages>> and add it to settings and help
4
+
5
+
6
+"""
7
+def page_link_filter(text):
8
+    render_txt = ''
9
+    while len(text) > 0:
10
+        try:
11
+            pos = text.index('[[page:')
12
+        except ValueError:
13
+            pos = len(text)
14
+        print(pos)
15
+        render_txt += text[:pos]
16
+        text = text[pos + 7:]
17
+        if len(text):
18
+            pos = text.index(']]')
19
+            try:
20
+                rel_path = int(text[:pos])
21
+            except ValueError:
22
+                render_txt += "[[page:" + text[:pos + 2]
23
+            else:
24
+                render_txt += '[[%s|%s]]' % (reverse('pages-pages', kwargs={'rel_path': rel_path}), rel_path)
25
+            text = text[pos + 2:]
26
+    return render_txt
27
+"""

+ 58
- 0
pages/help.py Visa fil

@@ -0,0 +1,58 @@
1
+from django.utils.translation import gettext as _
2
+import mycreole
3
+import pages
4
+from themes import color_icon_url
5
+
6
+
7
+HELP_UID = 'help'
8
+
9
+MAIN = mycreole.render_simple(_(
10
+    """
11
+= Piki
12
+
13
+**piki** is a minimal wiki implemented with python and django.
14
+
15
+== Help
16
+* [[creole|Creole Markup Language]]
17
+* [[access|Access Control for the site content]]
18
+* [[search|Help on Search]]
19
+"""))
20
+
21
+CREOLE = mycreole.mycreole_help_pagecontent()
22
+CREOLE += mycreole.render_simple("""
23
+= Piki Markup
24
+{{{[[rel_path_to_page|Name]]}}} will result in a Link to the given wiki page.
25
+""")
26
+
27
+ACCESS = mycreole.render_simple(_("""
28
+= TBD
29
+"""))
30
+
31
+SEARCH = mycreole.render_simple(_("""
32
+= TBD
33
+"""))
34
+
35
+help_pages = {
36
+    'main': MAIN,
37
+    'creole': CREOLE,
38
+    'access': ACCESS,
39
+    'search': SEARCH,
40
+}
41
+
42
+
43
+def actionbar(context, request, current_help_page=None, **kwargs):
44
+    actionbar_entries = (
45
+        ('1', 'Main'),
46
+        ('2', 'Creole'),
47
+        ('3', 'Access'),
48
+        ('4', 'Search'),
49
+    )
50
+    for num, name in actionbar_entries:
51
+        context[context.ACTIONBAR].append_entry(
52
+            HELP_UID + '-%s' % name.lower(),                        # uid
53
+            _(name),                                                # name
54
+            color_icon_url(request, num + '.png'),                  # icon
55
+            pages.url_helpview(request, name.lower()),              # url
56
+            True,                                                   # left
57
+            name.lower() == current_help_page,                      # active
58
+        )

+ 0
- 0
pages/migrations/__init__.py Visa fil


+ 3
- 0
pages/models.py Visa fil

@@ -0,0 +1,3 @@
1
+from django.db import models
2
+
3
+# Create your models here.

+ 47
- 0
pages/page.py Visa fil

@@ -0,0 +1,47 @@
1
+from django.conf import settings
2
+
3
+import mycreole
4
+import os
5
+
6
+
7
+class page(object):
8
+    SPLITCHAR = ":"
9
+    FOLDER_ATTACHMENTS = "attachments"
10
+    FOLDER_CONTENT = 'content'
11
+    FILE_NAME = 'page'
12
+
13
+    def __init__(self, rel_path) -> None:
14
+        self._rel_path = rel_path
15
+
16
+    def rel_path_is_valid(self):
17
+        return not self.SPLITCHAR in self._rel_path
18
+
19
+    def is_available(self):
20
+        return os.path.isfile(self.content_file_name)
21
+
22
+    @property
23
+    def title(self):
24
+        return os.path.basename(self._rel_path)
25
+
26
+    @property
27
+    def attachment_path(self):
28
+        return os.path.join(self.content_folder_name, self.FOLDER_ATTACHMENTS)
29
+
30
+    @property
31
+    def content_folder_name(self):
32
+        return self._rel_path.replace('/', '::')
33
+
34
+    @property
35
+    def content_file_name(self):
36
+        return os.path.join(settings.PAGES_ROOT, self.content_folder_name, self.FOLDER_CONTENT, self.FILE_NAME)
37
+
38
+    def __read_content__(self):
39
+        if self.is_available():
40
+            with open(self.content_file_name, 'r') as fh:
41
+                return fh.read()
42
+        else:
43
+            # TODO: Create message for creation or no content dependent of user has write access
44
+            return "Page not available. Create it."
45
+
46
+    def render_to_html(self, request):
47
+        return mycreole.render(request, self.__read_content__(), self.attachment_path, "next_anchor")

+ 5
- 0
pages/templates/pages/page.html Visa fil

@@ -0,0 +1,5 @@
1
+{% extends "themes/"|add:settings.page_theme|add:"/base.html" %}
2
+
3
+{% block content %}
4
+{{ page_content|safe }}
5
+{% endblock content %}

+ 3
- 0
pages/tests.py Visa fil

@@ -0,0 +1,3 @@
1
+from django.test import TestCase
2
+
3
+# Create your tests here.

+ 56
- 0
pages/views.py Visa fil

@@ -0,0 +1,56 @@
1
+from django.shortcuts import render
2
+from django.http import HttpResponse, HttpResponseRedirect
3
+from django.utils.translation import gettext as _
4
+
5
+import logging
6
+
7
+import config
8
+from . import url_page
9
+from .context import context_adaption
10
+from .help import help_pages
11
+from .page import page
12
+from themes import Context
13
+
14
+logger = logging.getLogger(__name__)
15
+
16
+
17
+def root(request):
18
+    return HttpResponseRedirect(url_page(request, config.STARTPAGE))
19
+
20
+
21
+def pages(request, rel_path=''):
22
+    context = Context(request)      # needs to be executed first because of time mesurement
23
+    #
24
+    p = page(rel_path)
25
+    #
26
+    context_adaption(
27
+        context,
28
+        request,
29
+        title=p.title,
30
+        upload_path=p.attachment_path,
31
+        page_content=p.render_to_html(request)
32
+    )
33
+    return render(request, 'pages/page.html', context=context)
34
+
35
+
36
+def search(request):
37
+    context = Context(request)      # needs to be executed first because of time mesurement
38
+    context_adaption(
39
+        context,
40
+        request,
41
+        page_content="Search is not yet implemented..."
42
+    )
43
+    return render(request, 'pages/page.html', context=context)
44
+
45
+
46
+def helpview(request, page='main'):
47
+    context = Context(request)      # needs to be executed first because of time mesurement
48
+    page_content = help_pages[page]
49
+    context_adaption(
50
+        context,                            # the base context
51
+        request,                            # the request object to be used in context_adaption
52
+        current_help_page=page,             # the current help_page to identify which taskbar entry has to be highlighted
53
+        page_content=page_content,          # the help content itself (template)
54
+        title=_('Help')                     # the title for the page (template)
55
+    )
56
+    return render(request, 'pages/page.html', context=context)

+ 0
- 0
piki/__init__.py Visa fil


+ 16
- 0
piki/asgi.py Visa fil

@@ -0,0 +1,16 @@
1
+"""
2
+ASGI config for piki project.
3
+
4
+It exposes the ASGI callable as a module-level variable named ``application``.
5
+
6
+For more information on this file, see
7
+https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/
8
+"""
9
+
10
+import os
11
+
12
+from django.core.asgi import get_asgi_application
13
+
14
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'piki.settings')
15
+
16
+application = get_asgi_application()

+ 178
- 0
piki/settings.py Visa fil

@@ -0,0 +1,178 @@
1
+"""
2
+Django settings for piki project.
3
+
4
+Generated by 'django-admin startproject' using Django 5.1.1.
5
+
6
+For more information on this file, see
7
+https://docs.djangoproject.com/en/5.1/topics/settings/
8
+
9
+For the full list of settings and their values, see
10
+https://docs.djangoproject.com/en/5.1/ref/settings/
11
+"""
12
+
13
+import config
14
+from config import APP_NAME as ROOT_LOGGER_NAME
15
+import os
16
+from pathlib import Path
17
+import random
18
+import stat
19
+import sys
20
+
21
+# Build paths inside the project like this: BASE_DIR / 'subdir'.
22
+BASE_DIR = Path(__file__).resolve().parent.parent
23
+
24
+# Check permission of config.py
25
+#
26
+if sys.platform == 'linux' or sys.platform == 'linux2':
27
+    st = os.stat(os.path.join(BASE_DIR, 'config.py'))
28
+    if st.st_mode & stat.S_IRGRP or st.st_mode & stat.S_IROTH:
29
+        raise PermissionError("conig.py is readable by group or others.")
30
+
31
+# Default values, if not defined in config.py
32
+#
33
+USER_CONFIG_DEFAULTS = {
34
+    'DEBUG': False,
35
+    'SECRET_KEY': None,
36
+    'DEFAULT_THEME': 'clear-blue',
37
+    'ALLOWED_HOSTS': ['127.0.0.1', 'localhost', ],
38
+    'CSRF_TRUSTED_ORIGINS': [],
39
+}
40
+
41
+# Set configuration parameters
42
+#
43
+thismodule = sys.modules[__name__]
44
+for property_name in USER_CONFIG_DEFAULTS:
45
+    try:
46
+        value = getattr(config, property_name)
47
+    except AttributeError:
48
+        value = USER_CONFIG_DEFAULTS[property_name]
49
+    setattr(thismodule, property_name, value)
50
+
51
+# SECURITY WARNING: keep the secret key used in production secret!
52
+#
53
+if SECRET_KEY is None:
54
+    chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)'
55
+    s_key = ''.join([random.choice(chars) for n in range(50)])
56
+    secret_key_warning = "You need to create a config.py file including at least a SECRET_KEY definition (e.g.: --> %s <--).    " % repr(s_key)
57
+    raise KeyError(secret_key_warning)
58
+
59
+# Quick-start development settings - unsuitable for production
60
+# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
61
+
62
+
63
+# Application definition
64
+
65
+INSTALLED_APPS = [
66
+    'pages.apps.PagesConfig',
67
+    'themes.apps.ThemesConfig',
68
+    'mycreole.apps.MycreoleConfig',
69
+    'users.apps.UsersConfig',
70
+    #
71
+    'django.contrib.admin',
72
+    'django.contrib.auth',
73
+    'django.contrib.contenttypes',
74
+    'django.contrib.sessions',
75
+    'django.contrib.messages',
76
+    'django.contrib.staticfiles',
77
+]
78
+
79
+MIDDLEWARE = [
80
+    'django.middleware.security.SecurityMiddleware',
81
+    'django.contrib.sessions.middleware.SessionMiddleware',
82
+    'django.middleware.common.CommonMiddleware',
83
+    'django.middleware.csrf.CsrfViewMiddleware',
84
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
85
+    'django.contrib.messages.middleware.MessageMiddleware',
86
+    'django.middleware.clickjacking.XFrameOptionsMiddleware',
87
+]
88
+
89
+ROOT_URLCONF = 'piki.urls'
90
+
91
+TEMPLATES = [
92
+    {
93
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
94
+        'DIRS': [],
95
+        'APP_DIRS': True,
96
+        'OPTIONS': {
97
+            'context_processors': [
98
+                'django.template.context_processors.debug',
99
+                'django.template.context_processors.request',
100
+                'django.contrib.auth.context_processors.auth',
101
+                'django.contrib.messages.context_processors.messages',
102
+            ],
103
+        },
104
+    },
105
+]
106
+
107
+WSGI_APPLICATION = 'piki.wsgi.application'
108
+
109
+
110
+# Database
111
+# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
112
+
113
+DATABASES = {
114
+    'default': {
115
+        'ENGINE': 'django.db.backends.sqlite3',
116
+        'NAME': BASE_DIR / 'db.sqlite3',
117
+    }
118
+}
119
+
120
+
121
+# Password validation
122
+# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
123
+
124
+AUTH_PASSWORD_VALIDATORS = [
125
+    {
126
+        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
127
+    },
128
+    {
129
+        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
130
+    },
131
+    {
132
+        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
133
+    },
134
+    {
135
+        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
136
+    },
137
+]
138
+
139
+
140
+# Internationalization
141
+# https://docs.djangoproject.com/en/5.1/topics/i18n/
142
+
143
+LANGUAGE_CODE = 'en-us'
144
+
145
+TIME_ZONE = 'UTC'
146
+
147
+USE_I18N = True
148
+
149
+USE_TZ = True
150
+
151
+
152
+# Static files (CSS, JavaScript, Images)
153
+# https://docs.djangoproject.com/en/5.1/howto/static-files/
154
+
155
+STATIC_ROOT = os.path.join(BASE_DIR, 'data', 'static')
156
+STATIC_URL = 'static/'
157
+
158
+MEDIA_ROOT = os.path.join(BASE_DIR, 'data', 'media')
159
+MEDIA_URL = '/media/'
160
+
161
+PAGES_ROOT = os.path.join(BASE_DIR, 'data', 'pages')
162
+
163
+MYCREOLE_ROOT = os.path.join(BASE_DIR, 'data', 'pages')
164
+MYCREOLE_ATTACHMENT_ACCESS = {
165
+    'read': 'pages.access.read_attachment',
166
+    'modify': 'pages.access.modify_attachment',
167
+}
168
+MYCREOLE_BAR = {
169
+    'navibar': 'pages.context.navigationbar',
170
+    'menubar': 'pages.context.menubar',
171
+}
172
+MYCREOLE_EXT_FILTERS = [
173
+    # 'pages.creole.page_link_filter',
174
+]
175
+# Default primary key field type
176
+# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
177
+
178
+DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

+ 38
- 0
piki/urls.py Visa fil

@@ -0,0 +1,38 @@
1
+"""
2
+URL configuration for piki project.
3
+
4
+The `urlpatterns` list routes URLs to views. For more information please see:
5
+    https://docs.djangoproject.com/en/5.1/topics/http/urls/
6
+Examples:
7
+Function views
8
+    1. Add an import:  from my_app import views
9
+    2. Add a URL to urlpatterns:  path('', views.home, name='home')
10
+Class-based views
11
+    1. Add an import:  from other_app.views import Home
12
+    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
13
+Including another URLconf
14
+    1. Import the include() function: from django.urls import include, path
15
+    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
16
+"""
17
+from django.conf import settings
18
+from django.conf.urls.static import static
19
+from django.contrib import admin
20
+from django.urls import path, include
21
+
22
+import pages.views
23
+
24
+urlpatterns = [
25
+    path('admin/', admin.site.urls),
26
+    #
27
+    path('', pages.views.root, name='pages-root'),
28
+    path('pages/', pages.views.root, name='pages-root'),
29
+    path('pages/<path:rel_path>', pages.views.pages, name='pages-pages'),
30
+    path('helpview/', pages.views.helpview, name='pages-helpview'),
31
+    path('helpview/<str:page>', pages.views.helpview, name='pages-helpview'),
32
+    path('search/', pages.views.search, name='search'),
33
+    path('mycreole/', include('mycreole.urls')),
34
+    path('users/', include('users.urls')),
35
+]
36
+
37
+if settings.DEBUG:
38
+    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

+ 16
- 0
piki/wsgi.py Visa fil

@@ -0,0 +1,16 @@
1
+"""
2
+WSGI config for piki project.
3
+
4
+It exposes the WSGI callable as a module-level variable named ``application``.
5
+
6
+For more information on this file, see
7
+https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/
8
+"""
9
+
10
+import os
11
+
12
+from django.core.wsgi import get_wsgi_application
13
+
14
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'piki.settings')
15
+
16
+application = get_wsgi_application()

+ 5
- 0
requirements.txt Visa fil

@@ -0,0 +1,5 @@
1
+Django
2
+Pillow
3
+python-creole
4
+pytz
5
+

+ 1
- 0
themes

@@ -0,0 +1 @@
1
+Subproject commit 13a8d8ebc4c82d2000af4464b863cf3b38a7a1fe

+ 1
- 0
users

@@ -0,0 +1 @@
1
+Subproject commit 0827a5311fdbbef689365c6db5762881e048ba9c

Laddar…
Avbryt
Spara