Files
django-tinywiki/tinywiki/models.py

235 lines
9.4 KiB
Python

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.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
from tinywiki.parser import bbcode
from .parser import parse_bbcode
from . import settings
import tinywiki
def get_tinywiki_default_user():
UserModel = get_user_model()
try:
user = UserModel.objects.get(**settings.TINYWIKI_USER_LOOKUP)
except UserModel.DoesNotExist:
user = UserModel.objects.filter(is_superuser=True).order_by('pk')[0]
return user
class Page(models.Model):
slug = models.SlugField(_("slug"),
max_length=255,
null=False,
blank=False,
unique=True)
title = models.CharField(_("title"),
max_length=255,
null=False,
blank=False)
author = models.ForeignKey(get_user_model(),
on_delete=models.SET_DEFAULT,
default=get_tinywiki_default_user,
verbose_name=_("author"),
related_name="tinywiki_athors")
status_data = models.CharField(_("status"),
choices=[(i.value,i.str_lazy) for i in WIKI_PAGE_STATUS],
max_length=15,
null=False,
blank=False,
default=WikiPageStatus.IN_PROGRESS.value)
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(),
on_delete=models.SET_DEFAULT,
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(),
on_delete=models.SET_DEFAULT,
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):
self.content_type_data = WikiContentType.from_string(content_type).value
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):
self.status_data = WikiPageStatus.from_string(status).value
elif isinstance(status, WikiPageStatus):
self.status_data = status.value
else:
raise TypeError("status")
@property
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"),
max_length=255)
alt = models.CharField(_("alternative text"),
max_length=511,
null=False,
blank=False)
description = models.CharField(_("description"),
max_length=1023,
null=True,
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"),
default=get_tinywiki_default_user,
related_name="tinywiki_image_uploads")
uploaded_at = models.DateTimeField(_("uploaded at"),
auto_now_add=True)
@property
def description_html(self) -> str:
return _bbcode.render_html(self.description)
@property
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>")
class BuiltinPages(models.Model):
app = models.CharField(_("app"),
max_length=255,
unique=True)
version = models.PositiveIntegerField(_("version"))
prefix = models.CharField(_("prefix"))
class BuiltinImages(models.Model):
app = models.CharField(_("app"),
max_length=255,
unique=True)
version = models.PositiveIntegerField(_("version"))
prefix = models.CharField(_("prefix"))