2025.12.23 17:30:33 (cachyos.cmoser.eu)

This commit is contained in:
2025-12-23 17:30:33 +01:00
parent 3c4647ce49
commit c2e431e43b
14 changed files with 1175 additions and 569 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 MiB

View File

@@ -1,3 +1,5 @@
{
"python-envs.pythonProjects": []
"python-envs.pythonProjects": [],
"python-envs.defaultEnvManager": "ms-python.python:poetry",
"python-envs.defaultPackageManager": "ms-python.python:poetry"
}

View File

@@ -122,6 +122,7 @@ TEMPLATES = [
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'tinywiki.context_processors.sidebar',
],
},
},
@@ -202,6 +203,12 @@ else:
print("Email backend not known falling back to console!",file=sys.stderr)
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
CSP_DEFAULT_SRC = [
"'self'",
"https://youtube.com",
]
_secret_key = LOCAL_DIR / "secret_key.py"
if _secret_key.is_file():
from .local import secret_key

1064
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -15,6 +15,7 @@ dependencies = [
"pillow (>=11.3.0,<12.0.0)",
"bbcode (>=1.1.0,<2.0.0)",
"markdown (>=3.9,<4.0)",
"django-csp (>=4.0,<5.0)",
]
[tool.poetry]

View File

@@ -1,46 +1,84 @@
#!/bin/bash
echo "Migrating database ..."
poetry run python manage.py migrate
if [ $? -ne 0 ]; then
echo "Unable to migrate database!" >&2
exit 5
fi
: ${UWSGI_BUFFER_SIZE:=32768}
: ${UWSGI_SOCKET_NAME:=uwsgi.sock}
: ${UWSGI_SOCKET:=/data/run/${UWSGI_SOCKET_NAME}}
: ${UWSGI_PIDFILE_NAME:=uwsgi.pid}
: ${UWSGI_PIDFILE:=/data/run/${UWSGI_SOCKET_NAME}}
: ${UWSGI_PROCESSES:=4}
: ${HTTP_PORT:=8000}
: ${HTTP_ADDRESS:=0.0.0.0}
: ${HTTP_SOCKET:=${HTTP_ADDRESS}:${HTTP_PORT}}
: ${UWSGI_MAX_REQUESTS:=4000}
echo "Running collectstatic ..."
yes yes | poetry run python manage.py collectstatic
if [ $? -ne 0 ]; then
echo "Unable to collect static files!" >&2
exit 5
fi
if [ -z "$DEBUG" -o $DEBUG != "1" ]; then
echo "Starting UWSGI server ..."
venv="$(poetry env info | head -n 6 | grep Path | awk '{print $2}')"
poetry run uwsgi \
--chdir /app \
--module django_project.wsgi:application \
--master --pidfile /data/run/uwsgi.pid \
--http-socket '0.0.0.0:8000' \
--socket /data/run/uwsgi.sock \
--processes 5 \
--harakiri 60 \
--max-requests 5000 \
--vacuum \
--venv "$venv" \
--home "$venv"
rc=$?
if [ $rc -ne 0 ]; then
echo "UWSGI Server not started" >&2
exit $rc
migrate_db() {
echo "Migrating database ..."
poetry run python manage.py migrate
if [ $? -ne 0 ]; then
echo "Unable to migrate database!" >&2
exit 5
fi
}
collectstatic() {
echo "Running collectstatic ..."
yes yes | poetry run python manage.py collectstatic
if [ $? -ne 0 ]; then
echo "Unable to collect static files!" >&2
exit 5
fi
}
createsuperuser() {
poetry run python manage.py createsuperuser "$@"
}
if [ $# -eq 0 -o "$1" = "start" ]; then
if [ -z "$DEBUG" -o $DEBUG != "1" ]; then
echo "Starting UWSGI server ..."
venv="$(poetry env info | head -n 6 | grep Path | awk '{print $2}')"
poetry run uwsgi \
--chdir /app \
--module django_project.wsgi:application \
--master --pidfile /data/run/uwsgi.pid \
--http-socket "$HTTP_SOCKET" \
--socket "$UWSGI_SOCKET" \
--processes $UWSGI_PROCESSES \
--harakiri 60 \
--max-requests $UWSGI_MAX_REQUESTS \
--buffer-size $UWSGI_BUFFER_SIZE \
--vacuum \
--venv "$venv" \
--home "$venv"
rc=$?
if [ $rc -ne 0 ]; then
echo "UWSGI Server not started" >&2
exit $rc
fi
else
echo "Starting development server ..."
poetry run python manage.py runserver $HTTP_SOCKET
rc=$?
if [ $rc -ne 0 ]; then
echo "Developemnt server was not started!" >&2
exit $rc
fi
fi
elif [ "$1" == "init" -o "$1" == "update" ]; then
migrate_db
collectstatic
elif [ "$1" == "suinit" ]; then
shift
migrate_db
collectstatic
createsuperuser "$@"
elif [ "$1" == "migrate" ]; then
migrate_db
elif [ "$1" == "collectstatic" ]; then
collectstatic
elif [ "$1" == "createsuperuser" ]; then
shift
createsuperuser "$@"
else
echo "Starting development server ..."
poetry run python manage.py runserver 0.0.0.0:8000
rc=$?
if [ $rc -ne 0 ]; then
echo "Developemnt server was not started!" >&2
exit $rc
fi
exec "$@"
fi

View File

@@ -16,7 +16,7 @@
{% block scripts %}{% endblock %}
</head>
<body class="min-vh-100 d-flex flex-column">
<div class="container-fluid">
<div class="row bg-primary pt-2">
<div clas="col-md-12 text-white ">
@@ -31,7 +31,7 @@
{% if subtitle %}
<span class="h2 text-truncate-sm">{{ subtitle }}</h2>
{% endif %}
</div>
</div>
</div>
<div class="navbar navbar-expand-lg">
<div class="collapse navbar-collapse">
@@ -66,23 +66,9 @@
</div>
<div class="row h-100 mb-4">
<div class="col-lg-3 pt-6 px-4 d-none d-lg-block" >
<ul class="list-group mt-2">
<li class="list-group-item bg-primary text-white">
<span class="list-group-badge">TinyWiki Pages</span>
</li>
<li class="list-group-item">
<a href="{% url "tinywiki:page" slug="tw-bbcode" %}">BBCode Guide</a>
</li>
<li class="list-group-item">
<a href="{% url "tinywiki:page" slug="tw-markdown" %}">Markdown Guide</a>
</li>
<li class="list-group-item">
<a href="{% url "tinywiki:page" slug="tw-license" %}">TinyWiki License</a>
</li>
<li class="list-group-item">
<a href="{% url "tinywiki:page" slug="tw-bootstrap-license" %}">Bootstrap License</a>
</li>
</ul>
{% block left_sidebar %}
{{ tinywiki_sidebar }}
{% enblock left_sidebar %}
</div>
<div class="col-lg-6">
<main>

View File

@@ -0,0 +1,10 @@
from django.http import HttpRequest
from django.utils.safestring import mark_safe
from .models import SidebarSection
def sidebar(request: HttpRequest):
sections = [
section.widget
for section in SidebarSection.objects.filter(is_visible=True).order_by('-priority'):
]
return {'tinywiki_sidebar': mark_safe("\n".join(sections))}

View File

@@ -7,13 +7,15 @@ class Migration(migrations.Migration):
('tinywiki', '0001_initial'),
]
def init_tinywiki_user(apps,schema_editor):
@staticmethod
def init_tinywiki_user(apps, schema_editor):
from django.contrib.auth import get_user_model
user = get_user_model().objects.create_user(**settings.TINYWIKI_USER_CONFIG)
def init_tinywiki_groups_and_permissions(apps,schema_editor):
get_user_model().objects.create_user(**settings.TINYWIKI_USER_CONFIG)
@staticmethod
def init_tinywiki_groups_and_permissions(apps, schema_editor):
from ..models import Page
from django.contrib.auth.models import Group,Permission
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
PERMISSIONS = [
@@ -27,7 +29,7 @@ class Migration(migrations.Migration):
'tinywiki-delete-all',
'tinywiki-delete-system',
]
GROUPS = [
('tinywiki-moderator',('tinywiki-read-all',
'tinywiki-delete-all',
@@ -45,79 +47,79 @@ class Migration(migrations.Migration):
'tinywiki-edit-all',
'tinywiki-edit-system'))
]
perm_mapping = {}
content_type = ContentType.objects.get_for_model(Page)
content_type = ContentType.objects.get_for_model(Page)
for perm in PERMISSIONS:
permission = Permission.objects.create(codename=perm,content_type=content_type)
perm_mapping[perm] = permission
for grp,perms in GROUPS:
group = Group.objects.create(name=grp)
for perm in perms:
group.permissions.add(perm_mapping[perm])
group.permissions.add(perm_mapping[perm])
def init_builtin_pages(apps,schema_editor)->None:
from ..models import Page,Image
from ..enums import WikiContentType,WikiPageStatus
from pathlib import Path
import json
page_path = Path(__file__).resolve().parent / "pages"
json_file = page_path / "pages.json"
if json_file.is_file():
with open(json_file,"rt",encoding="utf-8") as ifile:
data=json.loads(ifile.read())
for slug,spec in data.items():
filename = page_path / spec['file']
with open(filename,"rt",encoding="utf-8") as ifile:
content = ifile.read()
Page.objects.create(slug=slug,
title=spec['title'],
status_data=WikiPageStatus.from_string(spec['status']).value,
content_type_data=WikiContentType.from_string(spec['content_type']).value,
content=content)
def init_builtin_images(apps,schema_edit):
from pathlib import Path
from ..models import Image
from django.core.files.images import ImageFile
import json
image_dir = Path(__file__).resolve().parent / "images"
images_json_file = image_dir/ "images.json"
if not images_json_file.is_file():
return
with open(images_json_file,"rt",encoding="utf-8") as ifile:
images_data = json.loads(ifile.read())
for slug,_spec in images_data.items():
spec = dict(_spec)
image_basename = spec['image']
image_filename = image_dir / spec["image"]
spec['slug'] = slug
del spec['image']
img = Image(**spec)
with open(image_filename,"rb") as ifile:
img_file = ImageFile(ifile,image_basename)
img.image.save(image_basename,img_file)
img.save()
def init_user_imges(apps,schema_edit)->None:
#TODO
pass
def init_user_pages(apps,schema_edit)->None:
#TODO
pass
@@ -128,4 +130,3 @@ class Migration(migrations.Migration):
migrations.RunPython(init_builtin_images),
migrations.RunPython(init_builtin_pages),
]

View File

@@ -0,0 +1,41 @@
# Generated by Django 5.2.9 on 2025-12-23 15:57
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tinywiki', '0002_initial_data'),
]
operations = [
migrations.CreateModel(
name='SidebarSection',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=255, verbose_name='title')),
('priority', models.PositiveIntegerField(verbose_name='priority')),
('is_visible', models.BooleanField(default=True, verbose_name='is visible')),
],
),
migrations.AlterField(
model_name='page',
name='status_data',
field=models.CharField(choices=[('in_progress', 'in progress'), ('draft', 'draft'), ('published', 'published')], default='in_progress', max_length=15, verbose_name='status'),
),
migrations.CreateModel(
name='SidebarEntry',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=255, verbose_name='Title')),
('is_visible', models.BooleanField(default=True, verbose_name='is visible')),
('priority', models.PositiveIntegerField(verbose_name='Priority')),
('wiki_slug', models.CharField(blank=True, max_length=255, null=True, verbose_name='Wiki slug')),
('url', models.CharField(blank=True, max_length=512, null=True, verbose_name='Link URL')),
('widget', models.TextField(blank=True, null=True, verbose_name='Widget')),
('section', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='tinywiki.sidebarsection', verbose_name='Section')),
],
),
]

View File

@@ -0,0 +1,92 @@
# Generated by Django 5.2.9 on 2025-12-23 15:59
from django.db import migrations
class Migration(migrations.Migration):
@staticmethod
def create_tinywiki_sections(apps, schema_editor):
from tinywiki.models import SidebarSection, SidebarEntry
data = [
{
"title": "TinyWiki Pages",
"is_visible": True,
"priority": 1000,
"items": [
{
"title": "BBCode Guide",
"is_visible": True,
"priority": 100,
"wiki_slug": "tw-bbcode",
},
{
"title": "Markdown Guide",
"is_visible": True,
"priority": 90,
"wiki_slug": "tw-markdown"
},
{
"title": "TinyWiki License",
"is_visible": True,
"priority": 80,
"wiki_slug": "tw-license"
},
{
"title": "Bootstrap License",
"is_visible": True,
"priority": 70,
"wiki_slug": "tw-bootstrap-license"
},
]
},
{
"title": "TinyWiki Seiten",
"is_visible": False,
"priority": 1000,
"items": [
{
"title": "BBCode Anleitung",
"is_visible": True,
"priority": 100,
"wiki_slug": "tw-de-bbcode",
},
{
"title": "Markdown Anleitung",
"is_visible": True,
"priority": 90,
"wiki_slug": "tw-de-markdown"
},
{
"title": "TinyWiki Lizenz (EN)",
"is_visible": True,
"priority": 80,
"wiki_slug": "tw-license"
},
{
"title": "Bootstrap License (EN)",
"is_visible": True,
"priority": 70,
"wiki_slug": "tw-bootstrap-license"
},
]
},
]
for sect_spec in data:
if 'items' in sect_spec:
items = sect_spec.pop('items')
else:
items = []
section = SidebarSection.objects.create(**sect_spec)
for item_spec in items:
SidebarEntry.objects.create(section=section, **item_spec)
dependencies = [
('tinywiki', '0003_sidebarsection_alter_page_status_data_sidebarentry'),
]
operations = [
]

View File

@@ -1,12 +1,17 @@
from tabnanny import verbose
from django.db import models
from django.utils.translation import gettext_lazy as _
from django.conf import settings
from django.contrib.auth import get_user_model
from django.utils.safestring import mark_safe,SafeText
from django.contrib.auth import get_user_model
from tinywiki.enums import WIKI_CONTENT_TYPES, WIKI_PAGE_STATUS, WikiContentType, WikiPageStatus
from django.utils.safestring import mark_safe, SafeText
from django.utils.html import escape
from django.urls import reverse
from tinywiki.enums import (
WIKI_CONTENT_TYPES,
WIKI_PAGE_STATUS,
WikiContentType,
WikiPageStatus,
)
import markdown
import bbcode as _bbcode
@@ -16,6 +21,7 @@ from .parser import parse_bbcode
from . import settings
import tinywiki
def get_tinywiki_default_user():
UserModel = get_user_model()
try:
@@ -24,6 +30,7 @@ def get_tinywiki_default_user():
user = UserModel.objects.filter(is_superuser=True).order_by('pk')[0]
return user
class Page(models.Model):
slug = models.SlugField(_("slug"),
max_length=255,
@@ -48,12 +55,12 @@ class Page(models.Model):
content_type_data = models.CharField(_("content type"),
choices=[(i.value,i.str_lazy) for i in WIKI_CONTENT_TYPES],
default=WikiContentType.BBCODE.value)
content = models.TextField(_("Page content"),
null=False,
blank=False)
created_at = models.DateTimeField(_("created at"),
auto_now_add=True)
created_by = models.ForeignKey(get_user_model(),
@@ -61,7 +68,7 @@ class Page(models.Model):
verbose_name=_("created by"),
default=get_tinywiki_default_user,
related_name="tinywiki_created")
last_edited_at = models.DateTimeField(_("last edited at"),
auto_now=True)
last_edited_by = models.ForeignKey(get_user_model(),
@@ -69,40 +76,40 @@ class Page(models.Model):
verbose_name=_("last edited by"),
default=get_tinywiki_default_user,
related_name="tinywiki_last_edited")
@property
def content_type(self)->WikiContentType:
return WikiContentType.from_string(self.content_type_data)
@content_type.setter
def content_type(self,content_type:str|WikiContentType):
if isinstance(content_type,str):
def content_type(self, content_type: str | WikiContentType):
if isinstance(content_type, str):
self.content_type_data = WikiContentType.from_string(content_type).value
elif isinstance(content_type,WikiContentType):
elif isinstance(content_type, WikiContentType):
self.content_type_data = content_type.value
else:
raise TypeError("content_type")
@property
def status(self)->WikiPageStatus:
return WikiPageStatus.from_string(self.status_data)
@status.setter
def status(self,status:str|WikiPageStatus):
if isinstance(status,str):
def status(self, status: str | WikiPageStatus):
if isinstance(status, str):
self.status_data = WikiPageStatus.from_string(status).value
elif isinstance(status,WikiPageStatus):
elif isinstance(status, WikiPageStatus):
self.status_data = status.value
else:
raise TypeError("status")
@property
def html_content(self)->SafeText|str:
def html_content(self) -> SafeText | str:
if self.content_type == WikiContentType.MARKDOWN:
return mark_safe(markdown.markdown(self.content))
elif self.content_type == WikiContentType.BBCODE:
return mark_safe(parse_bbcode(self.content))
return self.content
class Image(models.Model):
models.ManyToManyField(Page, verbose_name=_(""))
slug = models.SlugField(_("slug"),
@@ -117,7 +124,7 @@ class Image(models.Model):
blank=True)
image = models.ImageField(_("image file"),
upload_to="tinywiki/img")
uploaded_by = models.ForeignKey(get_user_model(),
on_delete=models.SET_DEFAULT,
verbose_name=_("uploaded by"),
@@ -125,12 +132,86 @@ class Image(models.Model):
related_name="tinywiki_image_uploads")
uploaded_at = models.DateTimeField(_("uploaded at"),
auto_now_add=True)
@property
def description_html(self)->str:
def description_html(self) -> str:
return _bbcode.render_html(self.description)
@property
def description_html_safe(self)->SafeText:
def description_html_safe(self) -> SafeText:
return mark_safe(self.description_html)
class SidebarSection(models.Model):
title = models.CharField(_("title"),
max_length=255,
null=False,
blank=False)
priority = models.PositiveIntegerField(_("priority"))
is_visible = models.BooleanField(_("is visible"),
default=True)
@property
def widget(self) -> SafeText:
if settings.USE_BOOTSTRAP:
lines = [
"<ul class=\"list-group mt-2\">",
f"<li class=\"list-group-item bg-primary text-white\">{escape(self.title)}</li>", # noqa: E501
*[item.item for item in self.items.filter(is_visible=True).order_by('-priority')], # noqa: E501
"</ul>"
]
else:
lines = [
"<ul class=\"sidebar-section\">",
f"<li class=\"sidebar-title\">{escape(self.title)}</li>",
*[item.item for item in self.items.filter(is_visible=True).order_by('-priority')], # noqa: E501
"</ul>"
]
return mark_safe("\n".join(lines))
class SidebarEntry(models.Model):
section = models.ForeignKey(SidebarSection,
on_delete=models.CASCADE,
verbose_name=_("Section"),
related_name="items")
title = models.CharField(_("Title"),
max_length=255,
null=False,
blank=False)
is_visible = models.BooleanField(_("is visible"),
default=True)
priority = models.PositiveIntegerField(_("Priority"))
wiki_slug = models.CharField(_("Wiki slug"),
max_length=255,
null=True,
blank=True)
url = models.CharField(_("Link URL"),
max_length=512,
null=True,
blank=True)
widget = models.TextField(_("Widget"),
blank=True,
null=True)
@property
def link(self) -> SafeText:
if self.wiki_slug:
return mark_safe(f"<a href=\"{reverse('tinywiki:page', kwargs={'slug': self.wiki_slug})}\">{escape(self.title)}</a>") # noqa: E501
elif self.url:
return mark_safe(f"<a href=\"{self.url}\">{escape(self.title)}</a>") # noqa E501
return mark_safe(f"<a href=\"#\">{escape(self.title)}</a>") # noqa E501
@property
def item(self) -> SafeText:
if settings.USE_BOOTSTRAP:
if self.widget:
return mark_safe(f"<li class=\"list-group-item\">{self.widget}</li>") # noqa: E501
else:
return mark_safe(f"<li class=\"list-group-item\">{self.link}</li>") # noqa: E501
if self.widget:
return mark_safe(f"<li class=\"sidebar-item\">{self.widget}</li>")
else:
return mark_safe(f"<li class=\"sidebar-item\">{self.link}</li>")

View File

@@ -334,6 +334,7 @@ def render_table_data(tag_name:str,value,options,parent,context):
class_attr = f"class=\"{" ".join(classes)}\"" if classes else ""
return f"<td {class_attr} {" ".join(extra_attributes)}>{value}</td>"
def render_youtube_video(tag_name: str, value, options, parent, context):
if tag_name not in options:
return ""
@@ -345,8 +346,11 @@ def render_youtube_video(tag_name: str, value, options, parent, context):
if settings.USE_BOOTSTRAP:
styles = []
classes = ["w-100"]
div_classes = ["my-1"]
classes = ["embed-responsive-item", "w-100"]
div_classes = [
"embed-responsive",
"my-1",
]
div_styles = []
else:
styles = ["max-width:100%;"]
@@ -385,9 +389,9 @@ def render_youtube_video(tag_name: str, value, options, parent, context):
styles.append(f"height:{_h};")
else:
if _h.endswith('%'):
_h= _h[:-1]
_h = _h[:-1]
if _h.isdigit():
_h=int(_w)
_h = int(_w)
if _h > 100:
_h = 100
if settings.USE_BOOTSTRAP:
@@ -405,15 +409,15 @@ def render_youtube_video(tag_name: str, value, options, parent, context):
if "position" in options:
pos = options['position']
if settings.USE_BOOTSTRAP:
if pos == "left" or pos=="start":
div_classes += ["float-start","me-2"]
if pos == "left" or pos == "start":
div_classes += ["float-start", "me-2"]
#classes += ["float-start","me-2"]
elif pos == "right" or pos == "end":
div_classes += ["float-end","ms-2"]
#classes += ["float-end","ms-2"]
div_classes += ["float-end", "ms-2"]
#classes += ["float-end", "ms-2"]
elif pos == "center":
div_classes += ["mx-auto","d-block"]
#classes += ["mx-auto","d-block"]
div_classes += ["mx-auto", "d-block"]
#classes += ["mx-auto", "d-block"]
if styles:
style = f"style=\"{"".join(styles)}\""
@@ -425,6 +429,8 @@ def render_youtube_video(tag_name: str, value, options, parent, context):
else:
div_style = ""
if settings.USE_BOOTSTRAP:
return f'<div class=""{' '.join(div_classes)}" {div_style}><iframe class={" ".join(classes)}" src="https://www.youtube.com/embed/{options[tag_name]}" allowfullscreen></iframe></div>'
return f"""<div class=""{' '.join(div_classes)}" {div_style}>
<iframe class={" ".join(classes)}" src="https://www.youtube.com/embed/{options[tag_name]}?rel=0" allowfullscreen></iframe>
</div>"""
else:
return f'<div {div_style}><iframe src="https://www.youtube.com/embed/{options[tag_name]}?rel=0" allowfullscreen></iframe></div>'
return f'<div {div_style}><iframe src="https://www.youtube.com/embed/{options[tag_name]}?rel=0" allowfullscreen></iframe></div>'

View File

@@ -1,9 +1,9 @@
from django.shortcuts import render,get_object_or_404,redirect
from django.urls import reverse, reverse_lazy
from django.http import HttpRequest,HttpResponse,Http404
from django.contrib.auth.mixins import LoginRequiredMixin,UserPassesTestMixin
from django.core.exceptions import PermissionDenied
from django.shortcuts import render, get_object_or_404, redirect
from django.urls import reverse
from django.http import HttpRequest, HttpResponse, Http404
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.core.exceptions import PermissionDenied
from django.utils.translation import gettext as _
from .. import settings
@@ -12,17 +12,19 @@ from ..models import Page
from .base import View,FormView
from ..forms import PageForm,PageAdminForm,PageDeleteForm
class PageView(View):
template_name = "tinywiki/page/view.html"
bs_template_name = "tinywiki/page/bs-view.html"
@classmethod
def get_template_name(cls) -> str:
if settings.USE_BOOTSTRAP:
return cls.bs_template_name
return cls.template_name
def get_context_data(self,page,**kwargs):
def get_context_data(self, page, **kwargs):
can_edit_page = False
can_delete_page = False
user = self.request.user
@@ -39,17 +41,17 @@ class PageView(View):
or (user.pk == page.author.pk and user.has_perm('page.tinywiki-edit'))):
can_edit_page = True
if (user.has_perm('page.tinywiki-delete-all')
or (user.pk == page.author.pk and user.has_perm('page.tinywiki-delete'))):
or (user.pk == page.author.pk
and user.has_perm('page.tinywiki-delete'))):
can_delete_page = True
kwargs.update({'page':page,
'user_can_edit_wiki_page':can_edit_page,
'user_can_delete_wiki_page':can_delete_page,
'subtitle':page.title})
return super().get_context_data(**kwargs)
def get(self,request:HttpRequest,slug:str)->HttpResponse:
def get(self, request: HttpRequest, slug: str) -> HttpResponse:
try:
page = Page.objects.get(slug=slug)
except Page.DoesNotExist:
@@ -59,30 +61,31 @@ class PageView(View):
return render(request,
self.get_template_name(),
self.get_context_data(page=page))
class PageCreateView(LoginRequiredMixin,UserPassesTestMixin,FormView):
class PageCreateView(LoginRequiredMixin, UserPassesTestMixin, FormView):
template_name = "tinywiki/page/create.html"
bs_template_name = "tinywiki/page/bs-create.html"
form_class = PageForm
def test_func(self) -> bool:
if self.request.user.is_authenticated:
if self.request.user.is_staff:
return True
return self.request.user.has_perm('tinywiki.tinywiki-create')
return False
def get_template_name(self) -> str:
if settings.USE_BOOTSTRAP:
return self.bs_template_name
return self.template_name
def get(self,request):
def get(self, request):
return render(request,
self.get_template_name(),
self.get_context_data())
def get_context_data(self,**kwargs):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['create']=True
context['slug'] = self.request.GET.get('slug',None)
@@ -91,35 +94,35 @@ class PageCreateView(LoginRequiredMixin,UserPassesTestMixin,FormView):
context.setdefault("content_type_extra","")
context.setdefault("status_extra","")
context.setdefault("content_extra","")
return context
def form_invalid(self, form):
if 'slug' in form.errors:
slug_extra = "is-invalid"
else:
slug_extra = "is-valid"
if 'title' in form.errors:
title_extra = 'is-invalid'
else:
title_extra = 'is-valid'
if 'status_data' in form.errors:
status_extra = 'is-invalid'
else:
status_extra = 'is-valid'
if 'content_type_data' in form.errors:
content_type_extra = 'is-invalid'
else:
content_type_extra = 'is-valid'
if 'content' in form.errors:
content_extra = 'is-invalid'
else:
content_extra = 'is-valid'
return render(self.request,
self.get_template_name(),
self.get_context_data(
@@ -129,13 +132,13 @@ class PageCreateView(LoginRequiredMixin,UserPassesTestMixin,FormView):
content_type_extra=content_type_extra,
content_extra=content_extra,
))
def form_valid(self,form):
def form_valid(self, form):
user = self.request.user
instance = form.save(commit=False)
if (instance.slug.startswith('tw-')
and not user.is_staff and
not user.has_perm('page.tinywiki-create-system')):
if (instance.slug.startswith('tw-')
and not user.is_staff and
not user.has_perm('page.tinywiki-create-system')):
return render(self.request,
self.get_template_name(),
self.get_context_data(
@@ -145,7 +148,7 @@ class PageCreateView(LoginRequiredMixin,UserPassesTestMixin,FormView):
content_type_extra="is-valid",
content_extra="is-valid",
))
instance.author = user
instance.created_by = user
instance.last_edited_by = user
@@ -164,24 +167,24 @@ class PageCreateView(LoginRequiredMixin,UserPassesTestMixin,FormView):
content_type_extra="is-valid",
content_extra="is-valid",
))
class PageEditView(LoginRequiredMixin,UserPassesTestMixin,FormView):
template_name = "tinywiki/page/edit.html"
bs_template_name = "tinywiki/page/bs-edit.html"
form_class = PageForm
def test_func(self) -> bool:
if self.request.user.is_authenticated:
if self.request.user.is_staff:
return True
return False
def get_template_name(self) -> str:
if settings.USE_BOOTSTRAP:
return self.bs_template_name
return self.template_name
def get_context_data(self,page,**kwargs):
context = super().get_context_data(**kwargs)
context['create']=False
@@ -192,33 +195,33 @@ class PageEditView(LoginRequiredMixin,UserPassesTestMixin,FormView):
context.setdefault("status_extra","")
context.setdefault("content_extra","")
return context
def form_invalid(self, form):
if 'slug' in form.errors:
slug_extra = "is-invalid"
else:
slug_extra = "is-valid"
if 'title' in form.errors:
title_extra = 'is-invalid'
else:
title_extra = 'is-valid'
if 'status_data' in form.errors:
status_extra = 'is-invalid'
else:
status_extra = 'is-valid'
if 'content_type_data' in form.errors:
content_type_extra = 'is-invalid'
else:
content_type_extra = 'is-valid'
if 'content' in form.errors:
content_extra = 'is-invalid'
else:
content_extra = 'is-valid'
return render(self.request,
self.get_template_name(),
self.get_context_data(
@@ -229,48 +232,48 @@ class PageEditView(LoginRequiredMixin,UserPassesTestMixin,FormView):
content_type_extra=content_type_extra,
content_extra=content_extra,
))
def get(self,request,slug:str):
instance = get_object_or_404(Page,slug=slug)
user = request.user
if (instance.slug.startswith('tw-')
if (instance.slug.startswith('tw-')
and not user.is_staff
and not user.has_perm('page.tinywiki-edit-system')):
raise PermissionDenied(_("Only staff users and wiki-admins are allowed to edit TinyWiki system pages!"))
if user.pk != instance.author.pk:
if not user.is_staff and not user.has_perm("page.tinywiki-edit-all"):
raise PermissionDenied()
else:
if not user.has_perm('page.tinywiki-edit-all') or not user.has_perm('page.tinywiki-edit'):
raise PermissionDenied()
self.instance = instance
return render(request,
self.get_template_name(),
self.get_context_data(page=instance))
def post(self,request,slug:str):
instance = get_object_or_404(Page,slug=slug)
user = request.user
if (instance.slug.startswith('tw-') and not user.is_staff):
raise PermissionDenied(_("Only staff users are allowed to edit TinyWiki system pages!"))
if user.pk != instance.author.pk:
if not user.is_staff and not user.has_perm("page.tinywiki-edit-all"):
raise PermissionDenied()
else:
if not user.has_perm('page.tinywiki-edit-all') or not user.has_perm('page.tinywiki-edit'):
raise PermissionDenied()
self.instance = instance
return super().post(request)
def get_form(self):
return self.get_form_class()(instance=self.instance,**self.get_form_kwargs())
def form_valid(self,form):
user = self.request.user
instance = form.save(commit=False)
@@ -285,7 +288,7 @@ class PageEditView(LoginRequiredMixin,UserPassesTestMixin,FormView):
return render(self.request,
self.get_template_name(),
self.get_context_data(slug_invalid=True))
class PageDeleteView(LoginRequiredMixin,UserPassesTestMixin,FormView):
template_name = "tinywiki/page/delete.html"
form_class = PageDeleteForm
@@ -295,20 +298,20 @@ class PageDeleteView(LoginRequiredMixin,UserPassesTestMixin,FormView):
if self.request.user.has_perm('tinyiwki-delete-all') or self.request.user.has_perm('tinywiki-delete'):
return True
return False
def get(self,request:HttpRequest,slug:str)->HttpResponse:
instance = get_object_or_404(Page,slug=slug)
if not request.user.is_staff and not instance.author == request.user:
raise PermissionDenied()
return render(request,
self.template_name,
self.get_context_data(form=self.get_form_class()(),
page=instance))
def form_invalid(self,form:PageDeleteForm)->HttpResponse:
return redirect(reverse('tinywiki:page',kwargs={'slug':self.instance.slug}))
def form_valid(self,form:PageDeleteForm)->HttpResponse:
if self.instance.slug == form.cleaned_data['slug']:
try:
@@ -316,20 +319,20 @@ class PageDeleteView(LoginRequiredMixin,UserPassesTestMixin,FormView):
return redirect(reverse("tinywiki:home"))
except: pass
return redirect(reverse('tinywiki:page',kwargs={'slug':self.instance.slug}))
def post(self,request:HttpRequest,slug:str)->HttpResponse:
self.instance = get_object_or_404(Page,slug=slug)
if not request.user.is_staff and not instance.author == request.user:
raise PermissionDenied()
return super().post(request)
class HxPageEditView(LoginRequiredMixin,UserPassesTestMixin,FormView):
template_name = "tinywiki/page/hx-edit.html"
bs_template_name = "tinywiki/page/hx-bs-edit.html"
form_class = PageForm
def test_func(self) -> bool:
if self.request.user.is_authenticated:
if self.request.user.is_staff:
@@ -337,12 +340,12 @@ class HxPageEditView(LoginRequiredMixin,UserPassesTestMixin,FormView):
if self.request.user.has_perm('tinywiki-edit-page') or self.request.user.has_perm('tinywiki-edit-all-pages'):
return True
return False
def get_template_name(self) -> str:
if settings.USE_BOOTSTRAP:
return self.bs_template_name
return self.template_name
def get_context_data(self,**kwargs):
context = super().get_context_data(**kwargs)
context['create']=False
@@ -353,59 +356,59 @@ class HxPageEditView(LoginRequiredMixin,UserPassesTestMixin,FormView):
context.setdefault("status_extra","")
context.setdefault("content_extra","")
return context
def form_invalid(self, form):
if 'slug' in form.errors:
slug_extra = "is-invalid"
else:
slug_extra = "is-valid"
if 'title' in form.errors:
title_extra = 'is-invalid'
else:
title_extra = 'is-valid'
if 'status_data' in form.errors:
status_extra = 'is-invalid'
else:
status_extra = 'is-valid'
if 'content_type_data' in form.errors:
content_type_extra = 'is-invalid'
else:
content_type_extra = 'is-valid'
if 'content' in form.errors:
content_extra = 'is-invalid'
else:
content_extra = 'is-valid'
return render(self.request,
self.get_template_name(),
self.get_context_data(
page=self.instance,
))
def post(self,request,pk:int):
instance = get_object_or_404(Page,pk=pk)
user = request.user
if (instance.slug.startswith('tw-') and not user.is_staff):
raise PermissionDenied(_("Only staff users are allowed to edit TinyWiki system pages!"))
if user.pk != instance.author.pk:
if not user.is_staff and not user.has_perm("page.tinywiki-edit-all"):
raise PermissionDenied()
else:
if not user.has_perm('page.tinywiki-edit-all') or not user.has_perm('page.tinywiki-edit'):
raise PermissionDenied()
self.instance = instance
return super().post(request)
def get_form(self):
return self.get_form_class()(instance=self.instance,**self.get_form_kwargs())
def form_valid(self,form):
user = self.request.user
instance = form.save(commit=False)