df_site 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- df_site/__init__.py +1 -0
- df_site/__main__.py +37 -0
- df_site/admin.py +130 -0
- df_site/apps.py +57 -0
- df_site/components/__init__.py +1 -0
- df_site/components/base.py +82 -0
- df_site/components/detail.py +191 -0
- df_site/components/list.py +446 -0
- df_site/components/list_filters.py +74 -0
- df_site/components/registry.py +55 -0
- df_site/constants.py +71 -0
- df_site/context_processors.py +61 -0
- df_site/defaults.py +319 -0
- df_site/dynamic_settings.py +37 -0
- df_site/form_fields.py +138 -0
- df_site/management/__init__.py +1 -0
- df_site/management/commands/__init__.py +1 -0
- df_site/management/commands/add_image.py +104 -0
- df_site/management/commands/generate_favicon.py +47 -0
- df_site/middleware.py +20 -0
- df_site/migrations/0001_initial.py +220 -0
- df_site/migrations/0002_alter_alertribbon_message_alter_alertribbon_summary.py +23 -0
- df_site/migrations/__init__.py +0 -0
- df_site/model_fields.py +35 -0
- df_site/models.py +130 -0
- df_site/postman/__init__.py +1 -0
- df_site/postman/forms.py +38 -0
- df_site/postman/urls.py +75 -0
- df_site/postman/views.py +65 -0
- df_site/static/css/app.css +0 -0
- df_site/static/css/base.css +22208 -0
- df_site/static/css/ckeditor5.css +422 -0
- df_site/static/favicon/android-chrome-192x192.png +0 -0
- df_site/static/favicon/android-chrome-512x512.png +0 -0
- df_site/static/favicon/apple-touch-icon.png +0 -0
- df_site/static/favicon/favicon-16x16.png +0 -0
- df_site/static/favicon/favicon-32x32.png +0 -0
- df_site/static/favicon/favicon.ico +0 -0
- df_site/static/favicon/mstile-150x150.png +0 -0
- df_site/static/favicon/safari-pinned-tab.svg +46 -0
- df_site/static/images/accessibility.svg +1 -0
- df_site/static/images/align-bottom.svg +1 -0
- df_site/static/images/align-center.svg +1 -0
- df_site/static/images/align-justify.svg +1 -0
- df_site/static/images/align-left.svg +1 -0
- df_site/static/images/align-middle.svg +1 -0
- df_site/static/images/align-right.svg +1 -0
- df_site/static/images/align-top.svg +1 -0
- df_site/static/images/bold.svg +1 -0
- df_site/static/images/browse-files.svg +1 -0
- df_site/static/images/bulletedlist.svg +1 -0
- df_site/static/images/cancel.svg +1 -0
- df_site/static/images/caption.svg +1 -0
- df_site/static/images/check.svg +1 -0
- df_site/static/images/code.svg +1 -0
- df_site/static/images/codeblock.svg +1 -0
- df_site/static/images/cog.svg +1 -0
- df_site/static/images/color-palette.svg +1 -0
- df_site/static/images/color-tile-check.svg +1 -0
- df_site/static/images/drag-handle.svg +1 -0
- df_site/static/images/drag-indicator.svg +1 -0
- df_site/static/images/dropdown-arrow.svg +1 -0
- df_site/static/images/eraser.svg +1 -0
- df_site/static/images/file-arrow-up-solid.svg +1 -0
- df_site/static/images/find-replace.svg +1 -0
- df_site/static/images/font-background.svg +1 -0
- df_site/static/images/font-color.svg +1 -0
- df_site/static/images/font-family.svg +1 -0
- df_site/static/images/font-size.svg +1 -0
- df_site/static/images/heading1.svg +1 -0
- df_site/static/images/heading2.svg +1 -0
- df_site/static/images/heading3.svg +1 -0
- df_site/static/images/heading4.svg +1 -0
- df_site/static/images/heading5.svg +1 -0
- df_site/static/images/heading6.svg +1 -0
- df_site/static/images/history.svg +1 -0
- df_site/static/images/horizontalline.svg +1 -0
- df_site/static/images/html.svg +1 -0
- df_site/static/images/image-asset-manager.svg +1 -0
- df_site/static/images/image-upload.svg +1 -0
- df_site/static/images/image-url.svg +1 -0
- df_site/static/images/image.svg +1 -0
- df_site/static/images/importexport.svg +1 -0
- df_site/static/images/indent.svg +1 -0
- df_site/static/images/italic.svg +1 -0
- df_site/static/images/link.svg +1 -0
- df_site/static/images/liststylecircle.svg +1 -0
- df_site/static/images/liststyledecimal.svg +1 -0
- df_site/static/images/liststyledecimalleadingzero.svg +1 -0
- df_site/static/images/liststyledisc.svg +1 -0
- df_site/static/images/liststylelowerlatin.svg +1 -0
- df_site/static/images/liststylelowerroman.svg +1 -0
- df_site/static/images/liststylesquare.svg +1 -0
- df_site/static/images/liststyleupperlatin.svg +1 -0
- df_site/static/images/liststyleupperroman.svg +1 -0
- df_site/static/images/loupe.svg +1 -0
- df_site/static/images/low-vision.svg +1 -0
- df_site/static/images/marker.svg +1 -0
- df_site/static/images/media-placeholder.svg +1 -0
- df_site/static/images/media.svg +1 -0
- df_site/static/images/next-arrow.svg +1 -0
- df_site/static/images/numberedlist.svg +1 -0
- df_site/static/images/object-center.svg +1 -0
- df_site/static/images/object-full-width.svg +1 -0
- df_site/static/images/object-inline-left.svg +1 -0
- df_site/static/images/object-inline-right.svg +1 -0
- df_site/static/images/object-inline.svg +1 -0
- df_site/static/images/object-left.svg +1 -0
- df_site/static/images/object-right.svg +1 -0
- df_site/static/images/object-size-custom.svg +1 -0
- df_site/static/images/object-size-full.svg +1 -0
- df_site/static/images/object-size-large.svg +1 -0
- df_site/static/images/object-size-medium.svg +1 -0
- df_site/static/images/object-size-small.svg +1 -0
- df_site/static/images/outdent.svg +1 -0
- df_site/static/images/paragraph.svg +1 -0
- df_site/static/images/pen.svg +1 -0
- df_site/static/images/pencil.svg +1 -0
- df_site/static/images/pilcrow.svg +1 -0
- df_site/static/images/plus.svg +1 -0
- df_site/static/images/previous-arrow.svg +1 -0
- df_site/static/images/project-logo.svg +1 -0
- df_site/static/images/quote.svg +1 -0
- df_site/static/images/redo.svg +1 -0
- df_site/static/images/remove-format.svg +1 -0
- df_site/static/images/return-arrow.svg +1 -0
- df_site/static/images/select-all.svg +1 -0
- df_site/static/images/show-blocks.svg +1 -0
- df_site/static/images/source-editing.svg +1 -0
- df_site/static/images/specialcharacters.svg +1 -0
- df_site/static/images/strikethrough.svg +1 -0
- df_site/static/images/subscript.svg +1 -0
- df_site/static/images/superscript.svg +1 -0
- df_site/static/images/table-cell-properties.svg +1 -0
- df_site/static/images/table-column.svg +1 -0
- df_site/static/images/table-merge-cell.svg +1 -0
- df_site/static/images/table-properties.svg +1 -0
- df_site/static/images/table-row.svg +1 -0
- df_site/static/images/table.svg +1 -0
- df_site/static/images/text-alternative.svg +1 -0
- df_site/static/images/text.svg +1 -0
- df_site/static/images/three-vertical-dots.svg +1 -0
- df_site/static/images/todolist.svg +1 -0
- df_site/static/images/underline.svg +1 -0
- df_site/static/images/undo.svg +1 -0
- df_site/static/images/unlink.svg +1 -0
- df_site/static/js/app.js +98 -0
- df_site/static/js/app.js.map +1 -0
- df_site/static/js/base.js +161181 -0
- df_site/static/js/base.js.map +1 -0
- df_site/static/translations/af.js +1 -0
- df_site/static/translations/ar.js +1 -0
- df_site/static/translations/ast.js +1 -0
- df_site/static/translations/az.js +1 -0
- df_site/static/translations/bg.js +1 -0
- df_site/static/translations/bn.js +1 -0
- df_site/static/translations/bs.js +1 -0
- df_site/static/translations/ca.js +1 -0
- df_site/static/translations/cs.js +1 -0
- df_site/static/translations/da.js +1 -0
- df_site/static/translations/de-ch.js +1 -0
- df_site/static/translations/de.js +1 -0
- df_site/static/translations/el.js +1 -0
- df_site/static/translations/en-au.js +1 -0
- df_site/static/translations/en-gb.js +1 -0
- df_site/static/translations/en.js +1 -0
- df_site/static/translations/eo.js +1 -0
- df_site/static/translations/es-co.js +1 -0
- df_site/static/translations/es.js +1 -0
- df_site/static/translations/et.js +1 -0
- df_site/static/translations/eu.js +1 -0
- df_site/static/translations/fa.js +1 -0
- df_site/static/translations/fi.js +1 -0
- df_site/static/translations/gl.js +1 -0
- df_site/static/translations/gu.js +1 -0
- df_site/static/translations/he.js +1 -0
- df_site/static/translations/hi.js +1 -0
- df_site/static/translations/hr.js +1 -0
- df_site/static/translations/hu.js +1 -0
- df_site/static/translations/hy.js +1 -0
- df_site/static/translations/id.js +1 -0
- df_site/static/translations/it.js +1 -0
- df_site/static/translations/ja.js +1 -0
- df_site/static/translations/jv.js +1 -0
- df_site/static/translations/kk.js +1 -0
- df_site/static/translations/km.js +1 -0
- df_site/static/translations/kn.js +1 -0
- df_site/static/translations/ko.js +1 -0
- df_site/static/translations/ku.js +1 -0
- df_site/static/translations/lt.js +1 -0
- df_site/static/translations/lv.js +1 -0
- df_site/static/translations/ms.js +1 -0
- df_site/static/translations/nb.js +1 -0
- df_site/static/translations/ne.js +1 -0
- df_site/static/translations/nl.js +1 -0
- df_site/static/translations/no.js +1 -0
- df_site/static/translations/oc.js +1 -0
- df_site/static/translations/pl.js +1 -0
- df_site/static/translations/pt-br.js +1 -0
- df_site/static/translations/pt.js +1 -0
- df_site/static/translations/ro.js +1 -0
- df_site/static/translations/ru.js +1 -0
- df_site/static/translations/si.js +1 -0
- df_site/static/translations/sk.js +1 -0
- df_site/static/translations/sl.js +1 -0
- df_site/static/translations/sq.js +1 -0
- df_site/static/translations/sr-latn.js +1 -0
- df_site/static/translations/sr.js +1 -0
- df_site/static/translations/sv.js +1 -0
- df_site/static/translations/th.js +1 -0
- df_site/static/translations/ti.js +1 -0
- df_site/static/translations/tk.js +1 -0
- df_site/static/translations/tr.js +1 -0
- df_site/static/translations/tt.js +1 -0
- df_site/static/translations/ug.js +1 -0
- df_site/static/translations/uk.js +1 -0
- df_site/static/translations/ur.js +1 -0
- df_site/static/translations/uz.js +1 -0
- df_site/static/translations/vi.js +1 -0
- df_site/static/translations/zh-cn.js +1 -0
- df_site/static/translations/zh.js +1 -0
- df_site/static/webfonts/fa-brands-400.ttf +0 -0
- df_site/static/webfonts/fa-brands-400.woff2 +0 -0
- df_site/static/webfonts/fa-regular-400.ttf +0 -0
- df_site/static/webfonts/fa-regular-400.woff2 +0 -0
- df_site/static/webfonts/fa-solid-900.ttf +0 -0
- df_site/static/webfonts/fa-solid-900.woff2 +0 -0
- df_site/static/webfonts/fa-v4compatibility.ttf +0 -0
- df_site/static/webfonts/fa-v4compatibility.woff2 +0 -0
- df_site/templates/account/email.html +78 -0
- df_site/templates/account/password_change.html +28 -0
- df_site/templates/account/snippets/warn_no_email.html +6 -0
- df_site/templates/allauth/elements/alert.html +6 -0
- df_site/templates/allauth/elements/badge.html +4 -0
- df_site/templates/allauth/elements/button.html +14 -0
- df_site/templates/allauth/elements/button_group.html +5 -0
- df_site/templates/allauth/elements/field.html +72 -0
- df_site/templates/allauth/elements/fields.html +3 -0
- df_site/templates/allauth/elements/form.html +10 -0
- df_site/templates/allauth/elements/h1.html +1 -0
- df_site/templates/allauth/elements/h2.html +1 -0
- df_site/templates/allauth/elements/img.html +4 -0
- df_site/templates/allauth/elements/p.html +1 -0
- df_site/templates/allauth/elements/panel.html +14 -0
- df_site/templates/allauth/elements/provider.html +6 -0
- df_site/templates/allauth/elements/provider_list.html +5 -0
- df_site/templates/allauth/elements/table.html +5 -0
- df_site/templates/allauth/layouts/base.html +14 -0
- df_site/templates/allauth/layouts/entrance.html +20 -0
- df_site/templates/allauth/layouts/manage.html +1 -0
- df_site/templates/cookie_consent/_cookie_group.html +64 -0
- df_site/templates/cookie_consent/cookiegroup_list.html +23 -0
- df_site/templates/df_components/base.html +0 -0
- df_site/templates/df_components/detail.html +12 -0
- df_site/templates/df_components/detail_fieldset.html +46 -0
- df_site/templates/df_components/list.html +42 -0
- df_site/templates/df_components/list_filter.html +13 -0
- df_site/templates/df_components/list_filters.html +36 -0
- df_site/templates/df_components/list_hierarchy.html +25 -0
- df_site/templates/df_components/list_pagination.html +39 -0
- df_site/templates/df_components/list_search_form.html +38 -0
- df_site/templates/df_components/list_table.html +35 -0
- df_site/templates/df_site/app.html +1 -0
- df_site/templates/df_site/base.html +221 -0
- df_site/templates/df_site/detail.html +8 -0
- df_site/templates/df_site/humans.txt +11 -0
- df_site/templates/df_site/manage_base.html +51 -0
- df_site/templates/df_site/popup_app.html +1 -0
- df_site/templates/df_site/popup_base.html +29 -0
- df_site/templates/df_site/security.txt +5 -0
- df_site/templates/django_bootstrap5/breadcrumb.html +17 -0
- df_site/templates/django_bootstrap5/pagination.html +40 -0
- df_site/templates/django_ckeditor_5/widget.html +13 -0
- df_site/templates/favicon/browserconfig.xml +9 -0
- df_site/templates/mfa/index.html +115 -0
- df_site/templates/mfa/recovery_codes/index.html +33 -0
- df_site/templates/mfa/webauthn/authenticator_list.html +74 -0
- df_site/templates/pipeline/css.html +1 -0
- df_site/templates/pipeline/js.html +1 -0
- df_site/templates/postman/archives.html +8 -0
- df_site/templates/postman/base.html +20 -0
- df_site/templates/postman/base_folder.html +71 -0
- df_site/templates/postman/base_write.html +26 -0
- df_site/templates/postman/inbox.html +7 -0
- df_site/templates/postman/inc_subject_ex.html +21 -0
- df_site/templates/postman/trash.html +12 -0
- df_site/templates/postman/view.html +64 -0
- df_site/templates/users/settings.html +26 -0
- df_site/templates/usersessions/usersession_list.html +70 -0
- df_site/templatetags/__init__.py +1 -0
- df_site/templatetags/df_site.py +241 -0
- df_site/templatetags/images.py +515 -0
- df_site/templatetags/pipeline_sri.py +97 -0
- df_site/testing/__init__.py +1 -0
- df_site/testing/multiple_views.py +369 -0
- df_site/testing/requests.py +299 -0
- df_site/urls.py +41 -0
- df_site/user_settings.py +69 -0
- df_site/users/__init__.py +1 -0
- df_site/users/forms.py +35 -0
- df_site/users/notifications.py +14 -0
- df_site/users/urls.py +17 -0
- df_site/users/views.py +75 -0
- df_site/views.py +122 -0
- df_site-0.1.0.dist-info/LICENSE +519 -0
- df_site-0.1.0.dist-info/METADATA +217 -0
- df_site-0.1.0.dist-info/RECORD +309 -0
- df_site-0.1.0.dist-info/WHEEL +4 -0
- df_site-0.1.0.dist-info/entry_points.txt +3 -0
df_site/urls.py
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
"""List of URLs for the df_site app."""
|
2
|
+
|
3
|
+
from django.conf import settings
|
4
|
+
from django.urls import include, path
|
5
|
+
from django.utils.module_loading import import_string
|
6
|
+
from django.views.generic import RedirectView
|
7
|
+
from django_prometheus import exports
|
8
|
+
|
9
|
+
from df_site.views import (
|
10
|
+
BrowserConfigView,
|
11
|
+
HumansTxtView,
|
12
|
+
SecurityTxtView,
|
13
|
+
csp_report_view,
|
14
|
+
security_gpg_view,
|
15
|
+
site_webmanifest_view,
|
16
|
+
thumbnail_view,
|
17
|
+
)
|
18
|
+
|
19
|
+
urlpatterns = [
|
20
|
+
path(
|
21
|
+
".well-known/change-password",
|
22
|
+
RedirectView.as_view(pattern_name="account_change_password", permanent=True),
|
23
|
+
name="well-known-change-password",
|
24
|
+
),
|
25
|
+
path(".well-known/security.txt", SecurityTxtView.as_view(), name="well-known-security"),
|
26
|
+
path(".well-known/humans.txt", HumansTxtView.as_view(), name="well-known-humans"),
|
27
|
+
path(".well-known/gpg.txt", security_gpg_view, name="well-known-gpg"),
|
28
|
+
path("site.webmanifest", site_webmanifest_view, name="site_webmanifest"),
|
29
|
+
path("browserconfig.xml", BrowserConfigView.as_view(), name="browserconfig"),
|
30
|
+
path("metrics", exports.ExportToDjangoView, name="prometheus-django-metrics"),
|
31
|
+
path(settings.CSP_REPORT_URI[1:], csp_report_view, name="csp_report"),
|
32
|
+
path("users/", include("df_site.users.urls", namespace="users")),
|
33
|
+
path("thumbnails/<path:path>", thumbnail_view, name="thumbnails"),
|
34
|
+
path("messages/", include("df_site.postman.urls", namespace="postman")),
|
35
|
+
path("cookies/", include("cookie_consent.urls")),
|
36
|
+
path(
|
37
|
+
"upload_file/",
|
38
|
+
import_string(settings.CK_EDITOR_5_UPLOAD_FILE_VIEW),
|
39
|
+
name=settings.CK_EDITOR_5_UPLOAD_FILE_VIEW_NAME,
|
40
|
+
),
|
41
|
+
]
|
df_site/user_settings.py
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
"""Manage user settings."""
|
2
|
+
|
3
|
+
from functools import lru_cache
|
4
|
+
from typing import Any, Optional
|
5
|
+
|
6
|
+
from cookie_consent.util import get_cookie_value_from_request
|
7
|
+
from django.conf import settings
|
8
|
+
from django.contrib.auth import get_user_model
|
9
|
+
from django.core.exceptions import ValidationError
|
10
|
+
from django.db.models import Field
|
11
|
+
from django.forms import Widget
|
12
|
+
from django.http import HttpRequest, HttpResponse
|
13
|
+
|
14
|
+
from df_site.models import AbstractPreferences
|
15
|
+
|
16
|
+
|
17
|
+
@lru_cache
|
18
|
+
def get_user_instance() -> AbstractPreferences:
|
19
|
+
"""Return a blank user instance, required for validation purposes."""
|
20
|
+
return get_user_model()()
|
21
|
+
|
22
|
+
|
23
|
+
def get_user_setting(
|
24
|
+
attr: str, user: Optional[AbstractPreferences] = None, request: Optional[HttpRequest] = None
|
25
|
+
) -> Any:
|
26
|
+
"""Return the specified user setting.
|
27
|
+
|
28
|
+
If the user is given, the value is read from the user instance.
|
29
|
+
If the cookie provides a value, it is validated and used.
|
30
|
+
Otherwise, the default value is returned.
|
31
|
+
"""
|
32
|
+
if user:
|
33
|
+
return getattr(user, attr)
|
34
|
+
elif request and request.user.is_authenticated:
|
35
|
+
return getattr(request.user, attr)
|
36
|
+
user_instance = get_user_instance()
|
37
|
+
opts = user_instance._meta
|
38
|
+
field = opts.get_field(attr)
|
39
|
+
raw_value = request.COOKIES.get(attr) if request else None
|
40
|
+
value = field.default
|
41
|
+
if raw_value is not None:
|
42
|
+
try:
|
43
|
+
value = field.formfield().clean(raw_value)
|
44
|
+
except ValidationError:
|
45
|
+
pass
|
46
|
+
return value
|
47
|
+
|
48
|
+
|
49
|
+
def set_user_setting(
|
50
|
+
attr: str,
|
51
|
+
value: Any,
|
52
|
+
user: Optional[AbstractPreferences] = None,
|
53
|
+
request: Optional[HttpRequest] = None,
|
54
|
+
response: Optional[HttpResponse] = None,
|
55
|
+
) -> None:
|
56
|
+
"""Set the user's preferences."""
|
57
|
+
if user:
|
58
|
+
setattr(user, attr, value)
|
59
|
+
user.save()
|
60
|
+
elif request and request.user.is_authenticated:
|
61
|
+
setattr(request.user, attr, value)
|
62
|
+
request.user.save()
|
63
|
+
elif get_cookie_value_from_request(request, "user-settings"):
|
64
|
+
user_instance = get_user_instance()
|
65
|
+
opts = user_instance._meta
|
66
|
+
field: Field = opts.get_field(attr)
|
67
|
+
widget: Widget = field.formfield().hidden_widget()
|
68
|
+
raw_value: str = widget.format_value(value)
|
69
|
+
response.set_cookie(attr, raw_value, samesite="Strict", secure=settings.USE_SSL)
|
@@ -0,0 +1 @@
|
|
1
|
+
"""Module for user management."""
|
df_site/users/forms.py
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
"""Forms for the users app."""
|
2
|
+
|
3
|
+
from django import forms
|
4
|
+
from django.contrib.auth import get_user_model
|
5
|
+
from django.http import HttpRequest
|
6
|
+
from django.utils.translation import gettext_lazy as _
|
7
|
+
from django_recaptcha.fields import ReCaptchaField
|
8
|
+
from django_recaptcha.widgets import ReCaptchaV2Checkbox
|
9
|
+
|
10
|
+
|
11
|
+
class UserSettingsForm(forms.ModelForm):
|
12
|
+
"""Form for the user settings page."""
|
13
|
+
|
14
|
+
class Meta:
|
15
|
+
"""Meta options for the form."""
|
16
|
+
|
17
|
+
model = get_user_model()
|
18
|
+
fields = [
|
19
|
+
"first_name",
|
20
|
+
"last_name",
|
21
|
+
"color_theme",
|
22
|
+
"email_notifications",
|
23
|
+
"display_online",
|
24
|
+
]
|
25
|
+
|
26
|
+
|
27
|
+
class ReCaptchaForm(forms.Form):
|
28
|
+
"""Form for the reCAPTCHA field."""
|
29
|
+
|
30
|
+
captcha = ReCaptchaField(widget=ReCaptchaV2Checkbox, label=_("I'm not a robot"))
|
31
|
+
|
32
|
+
# noinspection PyMethodMayBeStatic,PyUnusedLocal
|
33
|
+
def signup(self, request: HttpRequest, user):
|
34
|
+
"""Configure the user as required by django-allauth."""
|
35
|
+
return user
|
@@ -0,0 +1,14 @@
|
|
1
|
+
"""This module contains the functions related to the notifications of the users."""
|
2
|
+
|
3
|
+
from typing import Optional, Union
|
4
|
+
|
5
|
+
from django.contrib.auth.models import AbstractUser
|
6
|
+
from django.contrib.sites.models import Site
|
7
|
+
|
8
|
+
|
9
|
+
# noinspection PyUnusedLocal
|
10
|
+
def email_address_on_message(user: Optional[AbstractUser], action: str, site: Site) -> Union[bool, str]:
|
11
|
+
"""Return the email address of the user if it is authenticated and if its accepts emails."""
|
12
|
+
if user and user.is_authenticated and user.email_notifications:
|
13
|
+
return user.email
|
14
|
+
return False
|
df_site/users/urls.py
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
"""List of URLs for the df_site app."""
|
2
|
+
|
3
|
+
from django.conf import settings
|
4
|
+
from django.urls import path
|
5
|
+
from django.utils.module_loading import import_string
|
6
|
+
|
7
|
+
from df_site.users.views import theme_switch
|
8
|
+
|
9
|
+
app_name = "users"
|
10
|
+
urlpatterns = [
|
11
|
+
path("theme-switch", theme_switch, name="theme_switch"),
|
12
|
+
]
|
13
|
+
if settings.AUTH_USER_SETTINGS_VIEW:
|
14
|
+
user_settings_view = import_string(settings.AUTH_USER_SETTINGS_VIEW)
|
15
|
+
urlpatterns.append(
|
16
|
+
path("settings", user_settings_view.as_view(), name="settings"),
|
17
|
+
)
|
df_site/users/views.py
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
"""Groups all views related to the users."""
|
2
|
+
|
3
|
+
from django.conf import settings
|
4
|
+
from django.contrib import messages
|
5
|
+
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect, JsonResponse
|
6
|
+
from django.urls import reverse
|
7
|
+
from django.utils.http import url_has_allowed_host_and_scheme, urlencode
|
8
|
+
from django.utils.translation import gettext as _
|
9
|
+
from django.views.decorators.cache import never_cache
|
10
|
+
from django.views.generic import FormView
|
11
|
+
|
12
|
+
from df_site.user_settings import set_user_setting
|
13
|
+
from df_site.users.forms import UserSettingsForm
|
14
|
+
|
15
|
+
|
16
|
+
class UserSettingsView(FormView):
|
17
|
+
"""View for the user settings page."""
|
18
|
+
|
19
|
+
template_name = "users/settings.html"
|
20
|
+
form_class = UserSettingsForm
|
21
|
+
|
22
|
+
def get_success_url(self):
|
23
|
+
"""Get the URL to redirect to after a successful form submission."""
|
24
|
+
return reverse("users:settings")
|
25
|
+
|
26
|
+
def get_form_kwargs(self):
|
27
|
+
"""Return the keyword arguments for instantiating the form."""
|
28
|
+
kwargs = super().get_form_kwargs()
|
29
|
+
kwargs["instance"] = self.request.user
|
30
|
+
return kwargs
|
31
|
+
|
32
|
+
def form_valid(self, form):
|
33
|
+
"""Save the form data and redirect to the success URL."""
|
34
|
+
form.save()
|
35
|
+
messages.success(self.request, _("Your settings have been saved."))
|
36
|
+
return super().form_valid(form)
|
37
|
+
|
38
|
+
|
39
|
+
@never_cache
|
40
|
+
def theme_switch(request: HttpRequest) -> HttpResponse:
|
41
|
+
"""Change the color theme of the site."""
|
42
|
+
# noinspection PyTypeChecker
|
43
|
+
current_theme: str = request.GET.get("current", "auto")
|
44
|
+
# noinspection PyTypeChecker
|
45
|
+
redirect_to = sanitize_redirection(request)
|
46
|
+
|
47
|
+
if settings.DF_SITE_THEMES:
|
48
|
+
next_theme, __, next_icon = settings.DF_SITE_THEMES[-1]
|
49
|
+
for theme, label, icon in settings.DF_SITE_THEMES:
|
50
|
+
if theme == current_theme:
|
51
|
+
break
|
52
|
+
next_theme, next_icon = theme, icon
|
53
|
+
else:
|
54
|
+
next_theme, next_icon = "auto", "toggle-on"
|
55
|
+
next_url = reverse("users:theme_switch")
|
56
|
+
args = urlencode({"next": redirect_to, "current": next_theme})
|
57
|
+
result = {"theme": next_theme, "icon": next_icon, "href": f"{next_url}?{args}"}
|
58
|
+
if request.META["HTTP_ACCEPT"] == "application/json":
|
59
|
+
response = JsonResponse(result)
|
60
|
+
else:
|
61
|
+
response = HttpResponseRedirect(redirect_to)
|
62
|
+
set_user_setting("color_theme", next_theme, request=request, response=response)
|
63
|
+
return response
|
64
|
+
|
65
|
+
|
66
|
+
def sanitize_redirection(request, param="next"):
|
67
|
+
"""Sanitize the redirection URL, only keeping allowed hosts."""
|
68
|
+
redirect_to: str = request.GET.get(param, "/")
|
69
|
+
if not url_has_allowed_host_and_scheme(
|
70
|
+
url=redirect_to,
|
71
|
+
allowed_hosts={request.get_host()},
|
72
|
+
require_https=request.is_secure(),
|
73
|
+
):
|
74
|
+
redirect_to = "/"
|
75
|
+
return redirect_to
|
df_site/views.py
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
"""Views for the df_site app."""
|
2
|
+
|
3
|
+
import datetime
|
4
|
+
import json
|
5
|
+
import logging
|
6
|
+
|
7
|
+
from django.conf import settings
|
8
|
+
from django.contrib import messages
|
9
|
+
from django.http import Http404, HttpRequest, HttpResponse, JsonResponse
|
10
|
+
from django.templatetags.static import static
|
11
|
+
from django.urls import reverse
|
12
|
+
from django.utils.safestring import mark_safe
|
13
|
+
from django.views.decorators.csrf import csrf_exempt
|
14
|
+
from django.views.generic import TemplateView
|
15
|
+
from django.views.static import serve
|
16
|
+
|
17
|
+
from df_site.templatetags.df_site import abs_url
|
18
|
+
from df_site.templatetags.images import CachedImage
|
19
|
+
|
20
|
+
logger = logging.getLogger(__name__)
|
21
|
+
|
22
|
+
|
23
|
+
def site_webmanifest_view(request: HttpRequest) -> HttpResponse:
|
24
|
+
"""Generate a site.webmanifest view."""
|
25
|
+
result = {
|
26
|
+
"name": settings.DF_SITE_TITLE,
|
27
|
+
"short_name": settings.DF_SITE_TITLE,
|
28
|
+
"icons": [
|
29
|
+
{"src": static("favicon/android-chrome-192x192.png"), "sizes": "192x192", "type": "image/png"},
|
30
|
+
{"src": static("favicon/android-chrome-512x512.png"), "sizes": "512x512", "type": "image/png"},
|
31
|
+
],
|
32
|
+
"theme_color": settings.DF_ANDROID_THEME_COLOR,
|
33
|
+
"background_color": settings.DF_ANDROID_BACKGROUND_COLOR,
|
34
|
+
"display": "standalone",
|
35
|
+
}
|
36
|
+
return JsonResponse(result)
|
37
|
+
|
38
|
+
|
39
|
+
@csrf_exempt
|
40
|
+
def csp_report_view(request: HttpRequest) -> HttpResponse:
|
41
|
+
"""View to receive CSP reports, displaying them to the user in DEBUG mode."""
|
42
|
+
logger.info("CSP report: %s", request.body)
|
43
|
+
if settings.DEBUG:
|
44
|
+
try:
|
45
|
+
content = json.loads(request.body)
|
46
|
+
csp_report = content["csp-report"]
|
47
|
+
msg = (
|
48
|
+
f"<strong>CSP violation</strong> on this <a href='{csp_report['document-uri']}'>page</a>:"
|
49
|
+
f" {csp_report['effective-directive']} forbids the use of"
|
50
|
+
f" '{csp_report['blocked-uri']}' URIs."
|
51
|
+
)
|
52
|
+
messages.error(request, mark_safe(msg)) # noqa
|
53
|
+
except ValueError:
|
54
|
+
pass
|
55
|
+
return HttpResponse(status=204)
|
56
|
+
|
57
|
+
|
58
|
+
class BrowserConfigView(TemplateView):
|
59
|
+
"""View for the browserconfig.xml file."""
|
60
|
+
|
61
|
+
template_name = "favicon/browserconfig.xml"
|
62
|
+
content_type = "application/xml"
|
63
|
+
|
64
|
+
|
65
|
+
def security_gpg_view(request: HttpRequest) -> HttpResponse:
|
66
|
+
"""Return the GPG key to communicate with about security problems."""
|
67
|
+
if settings.DF_SITE_SECURITY_GPG_CONTENT:
|
68
|
+
raise Http404
|
69
|
+
response = HttpResponse(settings.DF_SITE_SECURITY_GPG_CONTENT, content_type="text/plain")
|
70
|
+
response["Content-Disposition"] = 'attachment; filename="gpg.txt"'
|
71
|
+
return response
|
72
|
+
|
73
|
+
|
74
|
+
class SecurityTxtView(TemplateView):
|
75
|
+
"""View for the ./well-known/security.txt file."""
|
76
|
+
|
77
|
+
template_name = "df_site/security.txt"
|
78
|
+
content_type = "plain/text"
|
79
|
+
|
80
|
+
def get_context_data(self, **kwargs):
|
81
|
+
"""Return the context data for the view."""
|
82
|
+
context = super().get_context_data(**kwargs)
|
83
|
+
context["security_txt_path"] = abs_url(reverse("well-known-security"))
|
84
|
+
context["security_email"] = settings.DF_SITE_SECURITY_EMAIL
|
85
|
+
context["security_language_code"] = settings.DF_SITE_SECURITY_LANGUAGE_CODE
|
86
|
+
gpg_key = None
|
87
|
+
if settings.DF_SITE_SECURITY_GPG_CONTENT:
|
88
|
+
gpg_key = abs_url(reverse("well-known-gpg"))
|
89
|
+
context["security_gpg_key"] = gpg_key
|
90
|
+
now = datetime.datetime.now(tz=datetime.UTC)
|
91
|
+
now = now - datetime.timedelta(microseconds=now.microsecond)
|
92
|
+
context["security_expires"] = now + datetime.timedelta(days=30)
|
93
|
+
return context
|
94
|
+
|
95
|
+
|
96
|
+
class HumansTxtView(TemplateView):
|
97
|
+
"""View for the ./well-known/humans.txt file."""
|
98
|
+
|
99
|
+
template_name = "df_site/humans.txt"
|
100
|
+
content_type = "plain/text"
|
101
|
+
|
102
|
+
def get_context_data(self, **kwargs):
|
103
|
+
"""Return the context data for the view."""
|
104
|
+
context = super().get_context_data(**kwargs)
|
105
|
+
now = datetime.datetime.now(tz=datetime.UTC)
|
106
|
+
now = now - datetime.timedelta(microseconds=now.microsecond)
|
107
|
+
context["humans_description"] = settings.DF_SITE_DESCRIPTION
|
108
|
+
context["humans_social_networks"] = settings.DF_SITE_SOCIAL_NETWORKS
|
109
|
+
context["humans_keywords"] = settings.DF_SITE_KEYWORDS
|
110
|
+
context["humans_update"] = now
|
111
|
+
return context
|
112
|
+
|
113
|
+
|
114
|
+
def thumbnail_view(request: HttpRequest, path: str) -> HttpResponse:
|
115
|
+
"""Return a thumbnail image."""
|
116
|
+
img = CachedImage.from_target_path(path)
|
117
|
+
if not img.src_storage_obj.exists(path):
|
118
|
+
img.process()
|
119
|
+
return serve(request, path=path, document_root=settings.STATIC_ROOT)
|
120
|
+
|
121
|
+
|
122
|
+
#
|