mirror of
https://git.cmoser.eu/tinytools/django-tinywiki.git
synced 2026-02-04 06:06:33 +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.template.context_processors.request',
|
||||||
'django.contrib.auth.context_processors.auth',
|
'django.contrib.auth.context_processors.auth',
|
||||||
'django.contrib.messages.context_processors.messages',
|
'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)
|
print("Email backend not known falling back to console!",file=sys.stderr)
|
||||||
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
||||||
|
|
||||||
|
|
||||||
|
CSP_DEFAULT_SRC = [
|
||||||
|
"'self'",
|
||||||
|
"https://youtube.com",
|
||||||
|
]
|
||||||
|
|
||||||
_secret_key = LOCAL_DIR / "secret_key.py"
|
_secret_key = LOCAL_DIR / "secret_key.py"
|
||||||
if _secret_key.is_file():
|
if _secret_key.is_file():
|
||||||
from .local import secret_key
|
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)",
|
"pillow (>=11.3.0,<12.0.0)",
|
||||||
"bbcode (>=1.1.0,<2.0.0)",
|
"bbcode (>=1.1.0,<2.0.0)",
|
||||||
"markdown (>=3.9,<4.0)",
|
"markdown (>=3.9,<4.0)",
|
||||||
|
"django-csp (>=4.0,<5.0)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
|
|||||||
@@ -1,46 +1,84 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
echo "Migrating database ..."
|
: ${UWSGI_BUFFER_SIZE:=32768}
|
||||||
poetry run python manage.py migrate
|
: ${UWSGI_SOCKET_NAME:=uwsgi.sock}
|
||||||
if [ $? -ne 0 ]; then
|
: ${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}
|
||||||
|
|
||||||
|
migrate_db() {
|
||||||
|
echo "Migrating database ..."
|
||||||
|
poetry run python manage.py migrate
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
echo "Unable to migrate database!" >&2
|
echo "Unable to migrate database!" >&2
|
||||||
exit 5
|
exit 5
|
||||||
fi
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
echo "Running collectstatic ..."
|
collectstatic() {
|
||||||
yes yes | poetry run python manage.py collectstatic
|
echo "Running collectstatic ..."
|
||||||
if [ $? -ne 0 ]; then
|
yes yes | poetry run python manage.py collectstatic
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
echo "Unable to collect static files!" >&2
|
echo "Unable to collect static files!" >&2
|
||||||
exit 5
|
exit 5
|
||||||
fi
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
if [ -z "$DEBUG" -o $DEBUG != "1" ]; then
|
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 ..."
|
echo "Starting UWSGI server ..."
|
||||||
venv="$(poetry env info | head -n 6 | grep Path | awk '{print $2}')"
|
venv="$(poetry env info | head -n 6 | grep Path | awk '{print $2}')"
|
||||||
poetry run uwsgi \
|
poetry run uwsgi \
|
||||||
--chdir /app \
|
--chdir /app \
|
||||||
--module django_project.wsgi:application \
|
--module django_project.wsgi:application \
|
||||||
--master --pidfile /data/run/uwsgi.pid \
|
--master --pidfile /data/run/uwsgi.pid \
|
||||||
--http-socket '0.0.0.0:8000' \
|
--http-socket "$HTTP_SOCKET" \
|
||||||
--socket /data/run/uwsgi.sock \
|
--socket "$UWSGI_SOCKET" \
|
||||||
--processes 5 \
|
--processes $UWSGI_PROCESSES \
|
||||||
--harakiri 60 \
|
--harakiri 60 \
|
||||||
--max-requests 5000 \
|
--max-requests $UWSGI_MAX_REQUESTS \
|
||||||
|
--buffer-size $UWSGI_BUFFER_SIZE \
|
||||||
--vacuum \
|
--vacuum \
|
||||||
--venv "$venv" \
|
--venv "$venv" \
|
||||||
--home "$venv"
|
--home "$venv"
|
||||||
|
|
||||||
rc=$?
|
rc=$?
|
||||||
if [ $rc -ne 0 ]; then
|
if [ $rc -ne 0 ]; then
|
||||||
echo "UWSGI Server not started" >&2
|
echo "UWSGI Server not started" >&2
|
||||||
exit $rc
|
exit $rc
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo "Starting development server ..."
|
echo "Starting development server ..."
|
||||||
poetry run python manage.py runserver 0.0.0.0:8000
|
poetry run python manage.py runserver $HTTP_SOCKET
|
||||||
rc=$?
|
rc=$?
|
||||||
if [ $rc -ne 0 ]; then
|
if [ $rc -ne 0 ]; then
|
||||||
echo "Developemnt server was not started!" >&2
|
echo "Developemnt server was not started!" >&2
|
||||||
exit $rc
|
exit $rc
|
||||||
fi
|
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
|
||||||
|
exec "$@"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -66,23 +66,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row h-100 mb-4">
|
<div class="row h-100 mb-4">
|
||||||
<div class="col-lg-3 pt-6 px-4 d-none d-lg-block" >
|
<div class="col-lg-3 pt-6 px-4 d-none d-lg-block" >
|
||||||
<ul class="list-group mt-2">
|
{% block left_sidebar %}
|
||||||
<li class="list-group-item bg-primary text-white">
|
{{ tinywiki_sidebar }}
|
||||||
<span class="list-group-badge">TinyWiki Pages</span>
|
{% enblock left_sidebar %}
|
||||||
</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>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
<main>
|
<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'),
|
('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
|
from django.contrib.auth import get_user_model
|
||||||
user = get_user_model().objects.create_user(**settings.TINYWIKI_USER_CONFIG)
|
get_user_model().objects.create_user(**settings.TINYWIKI_USER_CONFIG)
|
||||||
|
|
||||||
def init_tinywiki_groups_and_permissions(apps,schema_editor):
|
@staticmethod
|
||||||
|
def init_tinywiki_groups_and_permissions(apps, schema_editor):
|
||||||
from ..models import Page
|
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
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
PERMISSIONS = [
|
PERMISSIONS = [
|
||||||
@@ -128,4 +130,3 @@ class Migration(migrations.Migration):
|
|||||||
migrations.RunPython(init_builtin_images),
|
migrations.RunPython(init_builtin_images),
|
||||||
migrations.RunPython(init_builtin_pages),
|
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.db import models
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.utils.safestring import mark_safe,SafeText
|
from django.utils.safestring import mark_safe, SafeText
|
||||||
from django.contrib.auth import get_user_model
|
from django.utils.html import escape
|
||||||
|
from django.urls import reverse
|
||||||
from tinywiki.enums import WIKI_CONTENT_TYPES, WIKI_PAGE_STATUS, WikiContentType, WikiPageStatus
|
from tinywiki.enums import (
|
||||||
|
WIKI_CONTENT_TYPES,
|
||||||
|
WIKI_PAGE_STATUS,
|
||||||
|
WikiContentType,
|
||||||
|
WikiPageStatus,
|
||||||
|
)
|
||||||
import markdown
|
import markdown
|
||||||
|
|
||||||
import bbcode as _bbcode
|
import bbcode as _bbcode
|
||||||
@@ -16,6 +21,7 @@ from .parser import parse_bbcode
|
|||||||
from . import settings
|
from . import settings
|
||||||
import tinywiki
|
import tinywiki
|
||||||
|
|
||||||
|
|
||||||
def get_tinywiki_default_user():
|
def get_tinywiki_default_user():
|
||||||
UserModel = get_user_model()
|
UserModel = get_user_model()
|
||||||
try:
|
try:
|
||||||
@@ -24,6 +30,7 @@ def get_tinywiki_default_user():
|
|||||||
user = UserModel.objects.filter(is_superuser=True).order_by('pk')[0]
|
user = UserModel.objects.filter(is_superuser=True).order_by('pk')[0]
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
class Page(models.Model):
|
class Page(models.Model):
|
||||||
slug = models.SlugField(_("slug"),
|
slug = models.SlugField(_("slug"),
|
||||||
max_length=255,
|
max_length=255,
|
||||||
@@ -74,10 +81,10 @@ class Page(models.Model):
|
|||||||
def content_type(self)->WikiContentType:
|
def content_type(self)->WikiContentType:
|
||||||
return WikiContentType.from_string(self.content_type_data)
|
return WikiContentType.from_string(self.content_type_data)
|
||||||
@content_type.setter
|
@content_type.setter
|
||||||
def content_type(self,content_type:str|WikiContentType):
|
def content_type(self, content_type: str | WikiContentType):
|
||||||
if isinstance(content_type,str):
|
if isinstance(content_type, str):
|
||||||
self.content_type_data = WikiContentType.from_string(content_type).value
|
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
|
self.content_type_data = content_type.value
|
||||||
else:
|
else:
|
||||||
raise TypeError("content_type")
|
raise TypeError("content_type")
|
||||||
@@ -86,16 +93,16 @@ class Page(models.Model):
|
|||||||
def status(self)->WikiPageStatus:
|
def status(self)->WikiPageStatus:
|
||||||
return WikiPageStatus.from_string(self.status_data)
|
return WikiPageStatus.from_string(self.status_data)
|
||||||
@status.setter
|
@status.setter
|
||||||
def status(self,status:str|WikiPageStatus):
|
def status(self, status: str | WikiPageStatus):
|
||||||
if isinstance(status,str):
|
if isinstance(status, str):
|
||||||
self.status_data = WikiPageStatus.from_string(status).value
|
self.status_data = WikiPageStatus.from_string(status).value
|
||||||
elif isinstance(status,WikiPageStatus):
|
elif isinstance(status, WikiPageStatus):
|
||||||
self.status_data = status.value
|
self.status_data = status.value
|
||||||
else:
|
else:
|
||||||
raise TypeError("status")
|
raise TypeError("status")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def html_content(self)->SafeText|str:
|
def html_content(self) -> SafeText | str:
|
||||||
if self.content_type == WikiContentType.MARKDOWN:
|
if self.content_type == WikiContentType.MARKDOWN:
|
||||||
return mark_safe(markdown.markdown(self.content))
|
return mark_safe(markdown.markdown(self.content))
|
||||||
elif self.content_type == WikiContentType.BBCODE:
|
elif self.content_type == WikiContentType.BBCODE:
|
||||||
@@ -127,10 +134,84 @@ class Image(models.Model):
|
|||||||
auto_now_add=True)
|
auto_now_add=True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def description_html(self)->str:
|
def description_html(self) -> str:
|
||||||
return _bbcode.render_html(self.description)
|
return _bbcode.render_html(self.description)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def description_html_safe(self)->SafeText:
|
def description_html_safe(self) -> SafeText:
|
||||||
return mark_safe(self.description_html)
|
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 ""
|
class_attr = f"class=\"{" ".join(classes)}\"" if classes else ""
|
||||||
return f"<td {class_attr} {" ".join(extra_attributes)}>{value}</td>"
|
return f"<td {class_attr} {" ".join(extra_attributes)}>{value}</td>"
|
||||||
|
|
||||||
|
|
||||||
def render_youtube_video(tag_name: str, value, options, parent, context):
|
def render_youtube_video(tag_name: str, value, options, parent, context):
|
||||||
if tag_name not in options:
|
if tag_name not in options:
|
||||||
return ""
|
return ""
|
||||||
@@ -345,8 +346,11 @@ def render_youtube_video(tag_name: str, value, options, parent, context):
|
|||||||
|
|
||||||
if settings.USE_BOOTSTRAP:
|
if settings.USE_BOOTSTRAP:
|
||||||
styles = []
|
styles = []
|
||||||
classes = ["w-100"]
|
classes = ["embed-responsive-item", "w-100"]
|
||||||
div_classes = ["my-1"]
|
div_classes = [
|
||||||
|
"embed-responsive",
|
||||||
|
"my-1",
|
||||||
|
]
|
||||||
div_styles = []
|
div_styles = []
|
||||||
else:
|
else:
|
||||||
styles = ["max-width:100%;"]
|
styles = ["max-width:100%;"]
|
||||||
@@ -385,9 +389,9 @@ def render_youtube_video(tag_name: str, value, options, parent, context):
|
|||||||
styles.append(f"height:{_h};")
|
styles.append(f"height:{_h};")
|
||||||
else:
|
else:
|
||||||
if _h.endswith('%'):
|
if _h.endswith('%'):
|
||||||
_h= _h[:-1]
|
_h = _h[:-1]
|
||||||
if _h.isdigit():
|
if _h.isdigit():
|
||||||
_h=int(_w)
|
_h = int(_w)
|
||||||
if _h > 100:
|
if _h > 100:
|
||||||
_h = 100
|
_h = 100
|
||||||
if settings.USE_BOOTSTRAP:
|
if settings.USE_BOOTSTRAP:
|
||||||
@@ -405,15 +409,15 @@ def render_youtube_video(tag_name: str, value, options, parent, context):
|
|||||||
if "position" in options:
|
if "position" in options:
|
||||||
pos = options['position']
|
pos = options['position']
|
||||||
if settings.USE_BOOTSTRAP:
|
if settings.USE_BOOTSTRAP:
|
||||||
if pos == "left" or pos=="start":
|
if pos == "left" or pos == "start":
|
||||||
div_classes += ["float-start","me-2"]
|
div_classes += ["float-start", "me-2"]
|
||||||
#classes += ["float-start","me-2"]
|
#classes += ["float-start","me-2"]
|
||||||
elif pos == "right" or pos == "end":
|
elif pos == "right" or pos == "end":
|
||||||
div_classes += ["float-end","ms-2"]
|
div_classes += ["float-end", "ms-2"]
|
||||||
#classes += ["float-end","ms-2"]
|
#classes += ["float-end", "ms-2"]
|
||||||
elif pos == "center":
|
elif pos == "center":
|
||||||
div_classes += ["mx-auto","d-block"]
|
div_classes += ["mx-auto", "d-block"]
|
||||||
#classes += ["mx-auto","d-block"]
|
#classes += ["mx-auto", "d-block"]
|
||||||
|
|
||||||
if styles:
|
if styles:
|
||||||
style = f"style=\"{"".join(styles)}\""
|
style = f"style=\"{"".join(styles)}\""
|
||||||
@@ -425,6 +429,8 @@ def render_youtube_video(tag_name: str, value, options, parent, context):
|
|||||||
else:
|
else:
|
||||||
div_style = ""
|
div_style = ""
|
||||||
if settings.USE_BOOTSTRAP:
|
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:
|
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 django.utils.translation import gettext as _
|
||||||
|
|
||||||
from .. import settings
|
from .. import settings
|
||||||
@@ -12,6 +12,8 @@ from ..models import Page
|
|||||||
from .base import View,FormView
|
from .base import View,FormView
|
||||||
from ..forms import PageForm,PageAdminForm,PageDeleteForm
|
from ..forms import PageForm,PageAdminForm,PageDeleteForm
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PageView(View):
|
class PageView(View):
|
||||||
template_name = "tinywiki/page/view.html"
|
template_name = "tinywiki/page/view.html"
|
||||||
bs_template_name = "tinywiki/page/bs-view.html"
|
bs_template_name = "tinywiki/page/bs-view.html"
|
||||||
@@ -22,7 +24,7 @@ class PageView(View):
|
|||||||
return cls.bs_template_name
|
return cls.bs_template_name
|
||||||
return cls.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_edit_page = False
|
||||||
can_delete_page = False
|
can_delete_page = False
|
||||||
user = self.request.user
|
user = self.request.user
|
||||||
@@ -39,7 +41,8 @@ class PageView(View):
|
|||||||
or (user.pk == page.author.pk and user.has_perm('page.tinywiki-edit'))):
|
or (user.pk == page.author.pk and user.has_perm('page.tinywiki-edit'))):
|
||||||
can_edit_page = True
|
can_edit_page = True
|
||||||
if (user.has_perm('page.tinywiki-delete-all')
|
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
|
can_delete_page = True
|
||||||
|
|
||||||
kwargs.update({'page':page,
|
kwargs.update({'page':page,
|
||||||
@@ -48,8 +51,7 @@ class PageView(View):
|
|||||||
'subtitle':page.title})
|
'subtitle':page.title})
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
def get(self, request: HttpRequest, slug: str) -> HttpResponse:
|
||||||
def get(self,request:HttpRequest,slug:str)->HttpResponse:
|
|
||||||
try:
|
try:
|
||||||
page = Page.objects.get(slug=slug)
|
page = Page.objects.get(slug=slug)
|
||||||
except Page.DoesNotExist:
|
except Page.DoesNotExist:
|
||||||
@@ -60,7 +62,8 @@ class PageView(View):
|
|||||||
self.get_template_name(),
|
self.get_template_name(),
|
||||||
self.get_context_data(page=page))
|
self.get_context_data(page=page))
|
||||||
|
|
||||||
class PageCreateView(LoginRequiredMixin,UserPassesTestMixin,FormView):
|
|
||||||
|
class PageCreateView(LoginRequiredMixin, UserPassesTestMixin, FormView):
|
||||||
template_name = "tinywiki/page/create.html"
|
template_name = "tinywiki/page/create.html"
|
||||||
bs_template_name = "tinywiki/page/bs-create.html"
|
bs_template_name = "tinywiki/page/bs-create.html"
|
||||||
form_class = PageForm
|
form_class = PageForm
|
||||||
@@ -77,12 +80,12 @@ class PageCreateView(LoginRequiredMixin,UserPassesTestMixin,FormView):
|
|||||||
return self.bs_template_name
|
return self.bs_template_name
|
||||||
return self.template_name
|
return self.template_name
|
||||||
|
|
||||||
def get(self,request):
|
def get(self, request):
|
||||||
return render(request,
|
return render(request,
|
||||||
self.get_template_name(),
|
self.get_template_name(),
|
||||||
self.get_context_data())
|
self.get_context_data())
|
||||||
|
|
||||||
def get_context_data(self,**kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['create']=True
|
context['create']=True
|
||||||
context['slug'] = self.request.GET.get('slug',None)
|
context['slug'] = self.request.GET.get('slug',None)
|
||||||
@@ -130,7 +133,7 @@ class PageCreateView(LoginRequiredMixin,UserPassesTestMixin,FormView):
|
|||||||
content_extra=content_extra,
|
content_extra=content_extra,
|
||||||
))
|
))
|
||||||
|
|
||||||
def form_valid(self,form):
|
def form_valid(self, form):
|
||||||
user = self.request.user
|
user = self.request.user
|
||||||
instance = form.save(commit=False)
|
instance = form.save(commit=False)
|
||||||
if (instance.slug.startswith('tw-')
|
if (instance.slug.startswith('tw-')
|
||||||
|
|||||||
Reference in New Issue
Block a user