mirror of
https://git.cmoser.eu/tinytools/django-tinywiki.git
synced 2026-02-03 22:06:31 +01:00
2025.12.23 17:30:33 (cachyos.cmoser.eu)
This commit is contained in:
BIN
.media/tinywiki/img/fabian-bachli-jQAe44MEIXU-unsplash.jpg
Normal file
BIN
.media/tinywiki/img/fabian-bachli-jQAe44MEIXU-unsplash.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.1 MiB |
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -1,3 +1,5 @@
|
||||
{
|
||||
"python-envs.pythonProjects": []
|
||||
"python-envs.pythonProjects": [],
|
||||
"python-envs.defaultEnvManager": "ms-python.python:poetry",
|
||||
"python-envs.defaultPackageManager": "ms-python.python:poetry"
|
||||
}
|
||||
@@ -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
1064
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -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]
|
||||
|
||||
118
start-django.sh
118
start-django.sh
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
10
tinywiki/context_processors
Normal file
10
tinywiki/context_processors
Normal 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))}
|
||||
@@ -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),
|
||||
]
|
||||
|
||||
@@ -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')),
|
||||
],
|
||||
),
|
||||
]
|
||||
92
tinywiki/migrations/0004_sidebar.py
Normal file
92
tinywiki/migrations/0004_sidebar.py
Normal 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 = [
|
||||
]
|
||||
@@ -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>")
|
||||
|
||||
@@ -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>'
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user