|
# retoor <retoor@molodetz.nl>
|
|
|
|
from .core import TTLCache, bump_cache_version, datetime, db, get_table, sync_local_cache, timezone
|
|
from .soft_delete import soft_delete
|
|
|
|
|
|
CUSTOMIZATION_GLOBAL_SCOPE = "global"
|
|
|
|
|
|
CUSTOMIZATION_LANGS = ("css", "js")
|
|
|
|
|
|
_customizations_cache = TTLCache(ttl=300, max_size=100)
|
|
|
|
|
|
def _customization_key(owner_kind: str, owner_id: str, page_type: str) -> str:
|
|
return f"{owner_kind}\x1f{owner_id}\x1f{page_type}"
|
|
|
|
|
|
CUSTOMIZATION_PREF_COLUMNS = {
|
|
"global": "cust_disable_global",
|
|
"pagetype": "cust_disable_pagetype",
|
|
}
|
|
|
|
|
|
def get_customization_prefs(owner_kind: str, owner_id: str) -> dict:
|
|
if owner_kind != "user" or "users" not in db.tables:
|
|
return {"disable_global": False, "disable_pagetype": False}
|
|
user = db["users"].find_one(uid=owner_id)
|
|
if user is None:
|
|
return {"disable_global": False, "disable_pagetype": False}
|
|
return {
|
|
"disable_global": bool(user.get("cust_disable_global", 0)),
|
|
"disable_pagetype": bool(user.get("cust_disable_pagetype", 0)),
|
|
}
|
|
|
|
|
|
def set_customization_pref(owner_id: str, category: str, disabled: bool) -> None:
|
|
column = CUSTOMIZATION_PREF_COLUMNS.get(category)
|
|
if column is None:
|
|
raise ValueError(f"Unknown customization category: {category}")
|
|
from devplacepy.utils import clear_user_cache
|
|
|
|
get_table("users").update(
|
|
{"uid": owner_id, column: 1 if disabled else 0}, ["uid"]
|
|
)
|
|
clear_user_cache(owner_id)
|
|
bump_cache_version("customizations")
|
|
|
|
|
|
def get_custom_overrides(owner_kind: str, owner_id: str, page_type: str) -> dict:
|
|
sync_local_cache("customizations", _customizations_cache)
|
|
key = _customization_key(owner_kind, owner_id, page_type)
|
|
cached = _customizations_cache.get(key)
|
|
if cached is not None:
|
|
return cached
|
|
result = {"css": "", "js": ""}
|
|
if "user_customizations" in db.tables:
|
|
prefs = get_customization_prefs(owner_kind, owner_id)
|
|
scopes = (CUSTOMIZATION_GLOBAL_SCOPE, page_type)
|
|
rows = db["user_customizations"].find(
|
|
owner_kind=owner_kind,
|
|
owner_id=owner_id,
|
|
enabled=1,
|
|
deleted_at=None,
|
|
)
|
|
pieces: dict[str, dict[str, str]] = {lang: {} for lang in CUSTOMIZATION_LANGS}
|
|
for row in rows:
|
|
lang = row.get("lang")
|
|
scope = row.get("scope")
|
|
if lang not in pieces or scope not in scopes:
|
|
continue
|
|
if scope == CUSTOMIZATION_GLOBAL_SCOPE and prefs["disable_global"]:
|
|
continue
|
|
if scope != CUSTOMIZATION_GLOBAL_SCOPE and prefs["disable_pagetype"]:
|
|
continue
|
|
pieces[lang][scope] = row.get("code") or ""
|
|
for lang in CUSTOMIZATION_LANGS:
|
|
ordered = [pieces[lang][scope] for scope in scopes if scope in pieces[lang]]
|
|
result[lang] = "\n".join(part for part in ordered if part.strip())
|
|
_customizations_cache.set(key, result)
|
|
return result
|
|
|
|
|
|
def get_custom_override(
|
|
owner_kind: str, owner_id: str, scope: str, lang: str
|
|
) -> dict | None:
|
|
if "user_customizations" not in db.tables:
|
|
return None
|
|
return db["user_customizations"].find_one(
|
|
owner_kind=owner_kind,
|
|
owner_id=owner_id,
|
|
scope=scope,
|
|
lang=lang,
|
|
deleted_at=None,
|
|
)
|
|
|
|
|
|
def list_custom_overrides(owner_kind: str, owner_id: str) -> list:
|
|
if "user_customizations" not in db.tables:
|
|
return []
|
|
return list(
|
|
db["user_customizations"].find(
|
|
owner_kind=owner_kind, owner_id=owner_id, deleted_at=None
|
|
)
|
|
)
|
|
|
|
|
|
def set_custom_override(
|
|
owner_kind: str, owner_id: str, scope: str, lang: str, code: str
|
|
) -> dict:
|
|
from devplacepy.utils import generate_uid
|
|
|
|
table = get_table("user_customizations")
|
|
now = datetime.now(timezone.utc).isoformat()
|
|
existing = table.find_one(
|
|
owner_kind=owner_kind, owner_id=owner_id, scope=scope, lang=lang
|
|
)
|
|
if existing:
|
|
record = {
|
|
"id": existing["id"],
|
|
"code": code,
|
|
"enabled": 1,
|
|
"updated_at": now,
|
|
"deleted_at": None,
|
|
"deleted_by": None,
|
|
}
|
|
table.update(record, ["id"])
|
|
result = {**existing, **record}
|
|
else:
|
|
result = {
|
|
"uid": generate_uid(),
|
|
"owner_kind": owner_kind,
|
|
"owner_id": owner_id,
|
|
"scope": scope,
|
|
"lang": lang,
|
|
"code": code,
|
|
"enabled": 1,
|
|
"created_at": now,
|
|
"updated_at": now,
|
|
"deleted_at": None,
|
|
"deleted_by": None,
|
|
}
|
|
table.insert(result)
|
|
bump_cache_version("customizations")
|
|
return result
|
|
|
|
|
|
def delete_custom_override(
|
|
owner_kind: str,
|
|
owner_id: str,
|
|
scope: str | None = None,
|
|
lang: str | None = None,
|
|
deleted_by: str | None = None,
|
|
) -> int:
|
|
if "user_customizations" not in db.tables:
|
|
return 0
|
|
criteria: dict = {"owner_kind": owner_kind, "owner_id": owner_id}
|
|
if scope is not None:
|
|
criteria["scope"] = scope
|
|
if lang is not None:
|
|
criteria["lang"] = lang
|
|
count = soft_delete(
|
|
"user_customizations", deleted_by or f"{owner_kind}:{owner_id}", **criteria
|
|
)
|
|
bump_cache_version("customizations")
|
|
return int(count)
|