This repository has been archived on 2023-09-24. You can view files and clone it, but cannot push or open issues or pull requests.
takahe/core/models/config.py
2022-11-17 17:33:09 -07:00

179 lines
4.8 KiB
Python

from functools import partial
from typing import ClassVar
import pydantic
from django.core.files import File
from django.db import models
from django.templatetags.static import static
from django.utils.functional import classproperty
from core.uploads import upload_namer
from takahe import __version__
class UploadedImage(str):
"""
Type used to indicate a setting is an image
"""
class Config(models.Model):
"""
A configuration setting for either the server or a specific user or identity.
The possible options and their defaults are defined at the bottom of the file.
"""
key = models.CharField(max_length=500)
user = models.ForeignKey(
"users.user",
blank=True,
null=True,
related_name="configs",
on_delete=models.CASCADE,
)
identity = models.ForeignKey(
"users.identity",
blank=True,
null=True,
related_name="configs",
on_delete=models.CASCADE,
)
json = models.JSONField(blank=True, null=True)
image = models.ImageField(
blank=True,
null=True,
upload_to=partial(upload_namer, "config"),
)
class Meta:
unique_together = [
("key", "user", "identity"),
]
@classproperty
def system(cls):
cls.system = cls.load_system()
return cls.system
system: ClassVar["Config.ConfigOptions"] # type: ignore
@classmethod
def load_values(cls, options_class, filters):
"""
Loads config options and returns an object with them
"""
values = {}
for config in cls.objects.filter(**filters):
values[config.key] = config.image.url if config.image else config.json
if values[config.key] is None:
del values[config.key]
values["version"] = __version__
return options_class(**values)
@classmethod
def load_system(cls):
"""
Loads the system config options object
"""
return cls.load_values(
cls.SystemOptions,
{"identity__isnull": True, "user__isnull": True},
)
@classmethod
def load_user(cls, user):
"""
Loads a user config options object
"""
return cls.load_values(
cls.SystemOptions,
{"identity__isnull": True, "user": user},
)
@classmethod
def load_identity(cls, identity):
"""
Loads a user config options object
"""
return cls.load_values(
cls.IdentityOptions,
{"identity": identity, "user__isnull": True},
)
@classmethod
def set_value(cls, key, value, options_class, filters):
config_field = options_class.__fields__[key]
if isinstance(value, File):
if config_field.type_ is not UploadedImage:
raise ValueError(f"Cannot save file to {key} of type: {type(value)}")
cls.objects.update_or_create(
key=key,
defaults={"json": None, "image": value},
**filters,
)
elif value is None:
cls.objects.filter(key=key, **filters).delete()
else:
if not isinstance(value, config_field.type_):
raise ValueError(f"Invalid type for {key}: {type(value)}")
if value == config_field.default:
cls.objects.filter(key=key, **filters).delete()
else:
cls.objects.update_or_create(
key=key,
defaults={"json": value},
**filters,
)
@classmethod
def set_system(cls, key, value):
cls.set_value(
key,
value,
cls.SystemOptions,
{"identity__isnull": True, "user__isnull": True},
)
@classmethod
def set_user(cls, user, key, value):
cls.set_value(
key,
value,
cls.UserOptions,
{"identity__isnull": True, "user": user},
)
@classmethod
def set_identity(cls, identity, key, value):
cls.set_value(
key,
value,
cls.IdentityOptions,
{"identity": identity, "user__isnull": True},
)
class SystemOptions(pydantic.BaseModel):
version: str = __version__
site_name: str = "takahē"
highlight_color: str = "#449c8c"
site_about: str = "<h2>Welcome!</h2>\n\nThis is a community running Takahē."
site_icon: UploadedImage = static("img/icon-128.png")
site_banner: UploadedImage = static("img/fjords-banner-600.jpg")
post_length: int = 500
identity_max_age: int = 24 * 60 * 60
class UserOptions(pydantic.BaseModel):
pass
class IdentityOptions(pydantic.BaseModel):
toot_mode: bool = False