{{breadcrumbs}}
HUB / lib_cms_content.py
lib_cms_content.py
- Runtime
- Python
- Category
- CMS
- Path
- /storage/emulated/0/Projects/Management/Libraries/py/CMS/lib_cms_content.py
FILE // lib_cms_content.py
"""
Library: lib_cms_content.py
MFDB Version: 1.3.1
Format_Creator: Elton Boehnen
Status: OFFICIAL - v1.3.1
Date: 2026-05-06
"""
"""
Library: lib_cms_content.py
Family: CMS
Jurisdiction: ["PYTHON", "BEJSON_LIBRARIES"]
Status: OFFICIAL — BEJSON/Lib (v1.4)
Author: Elton Boehnen
Version: 1.3 OFFICIAL
Date: 2026-05-01
Description: Core content management library for Page and Article CRUD.
Handles dual-file BEJSON structure (Master Index + Content JSON).
Part of the Modular CMS Backend Framework.
"""
import os
import sys
import uuid
import re
from datetime import datetime
from typing import List, Dict, Optional
# Ensure local directory is in path for relative imports
LIB_DIR = os.path.dirname(os.path.abspath(__file__))
if LIB_DIR not in sys.path:
sys.path.append(LIB_DIR)
import lib_bejson_core as BEJSONCore
def _slugify(text: str) -> str:
text = text.lower()
text = re.sub(r'[^a-z0-9]+', '-', text)
return text.strip('-')
# ---------------------------------------------------------------------------
# PAGE CONTENT OPERATIONS (pages_db/<uuid>.json)
# ---------------------------------------------------------------------------
def _cms_content_init_page_file(file_path: str, title: str, html_body: str = "") -> None:
"""Initialize an individual page content file in 104db format."""
record_types = ["PageMeta", "Content"]
fields = [
{"name": "Record_Type_Parent", "type": "string"},
{"name": "meta_title", "type": "string", "Record_Type_Parent": "PageMeta"},
{"name": "meta_description", "type": "string", "Record_Type_Parent": "PageMeta"},
{"name": "html_body", "type": "string", "Record_Type_Parent": "Content"},
{"name": "markdown_body", "type": "string", "Record_Type_Parent": "Content"},
]
doc = BEJSONCore.bejson_core_create_104db(record_types, fields, [])
# Add Meta record
meta_row = ["PageMeta", title, ""]
# Pad to match field count
meta_row.extend([None] * (len(fields) - len(meta_row)))
doc = BEJSONCore.bejson_core_add_record(doc, meta_row)
# Add Content record
# Record_Type_Parent [0], meta_title [1], meta_description [2], html_body [3], markdown_body [4]
content_row = ["Content", None, None, html_body, ""]
# Pad to match field count
content_row.extend([None] * (len(fields) - len(content_row)))
doc = BEJSONCore.bejson_core_add_record(doc, content_row)
BEJSONCore.bejson_core_atomic_write(file_path, doc)
def cms_content_get_page_body(pages_dir: str, page_uuid: str) -> str:
"""Retrieve html_body from a page's individual JSON file."""
file_path = os.path.join(pages_dir, f"{page_uuid}.json")
if not os.path.exists(file_path):
return ""
doc = BEJSONCore.bejson_core_load_file(file_path)
hb_idx = BEJSONCore.bejson_core_get_field_index(doc, "html_body")
records = BEJSONCore.bejson_core_get_records_by_type(doc, "Content")
if records:
return records[0][hb_idx] or ""
return ""
# ---------------------------------------------------------------------------
# MASTER INDEX OPERATIONS (site_master.json)
# ---------------------------------------------------------------------------
def cms_content_create_page(
master_db_path: str,
pages_dir: str,
title: str,
category_ref: str = "Uncategorized",
author_ref: str = "Admin",
html_body: str = "",
featured_img: str = ""
) -> str:
"""
Create a new page: generates UUID, updates master index, creates content file.
Returns the new page_uuid.
"""
page_uuid = str(uuid.uuid4())
page_slug = _slugify(title)
today = datetime.now().strftime("%Y-%m-%d")
# 1. Update Master Index
doc = BEJSONCore.bejson_core_load_file(master_db_path)
# Map fields
fields_map = {f['name']: i for i, f in enumerate(doc["Fields"])}
new_row = [None] * len(doc["Fields"])
new_row[fields_map["Record_Type_Parent"]] = "PageRecord"
new_row[fields_map["page_uuid"]] = page_uuid
new_row[fields_map["page_title"]] = title
new_row[fields_map["page_slug"]] = page_slug
new_row[fields_map["category_ref"]] = category_ref
new_row[fields_map["item_type"]] = "page"
new_row[fields_map["created_at"]] = today
new_row[fields_map["author_ref"]] = author_ref
new_row[fields_map["featured_img"]] = featured_img
doc = BEJSONCore.bejson_core_add_record(doc, new_row)
BEJSONCore.bejson_core_atomic_write(master_db_path, doc)
# 2. Create individual content file
os.makedirs(pages_dir, exist_ok=True)
page_file = os.path.join(pages_dir, f"{page_uuid}.json")
_cms_content_init_page_file(page_file, title, html_body)
return page_uuid
def cms_content_update_page(
master_db_path: str,
pages_dir: str,
page_uuid: str,
updates: Dict
) -> None:
"""
Update page metadata in master and/or content in individual file.
'updates' can contain: title, category_ref, author_ref, featured_img, html_body.
"""
# 1. Update Master Index if needed
master_updates = {k: v for k, v in updates.items() if k in ["title", "category_ref", "author_ref", "featured_img"]}
if master_updates:
doc = BEJSONCore.bejson_core_load_file(master_db_path)
t_idx = 0
u_idx = BEJSONCore.bejson_core_get_field_index(doc, "page_uuid")
found = False
for i, row in enumerate(doc["Values"]):
if row[t_idx] == "PageRecord" and row[u_idx] == page_uuid:
for k, v in master_updates.items():
# Handle title -> page_title mapping
f_key = "page_title" if k == "title" else k
if k == "title":
row[BEJSONCore.bejson_core_get_field_index(doc, "page_slug")] = _slugify(v)
row[BEJSONCore.bejson_core_get_field_index(doc, f_key)] = v
found = True
break
if found:
BEJSONCore.bejson_core_atomic_write(master_db_path, doc)
# 2. Update Content File if needed
html_body = updates.get("html_body")
if html_body is not None or "title" in updates:
page_file = os.path.join(pages_dir, f"{page_uuid}.json")
if os.path.exists(page_file):
pdoc = BEJSONCore.bejson_core_load_file(page_file)
pt_idx = 0
if html_body is not None:
hb_idx = BEJSONCore.bejson_core_get_field_index(pdoc, "html_body")
for row in pdoc["Values"]:
if row[pt_idx] == "Content":
row[hb_idx] = html_body
if "title" in updates:
mt_idx = BEJSONCore.bejson_core_get_field_index(pdoc, "meta_title")
for row in pdoc["Values"]:
if row[pt_idx] == "PageMeta":
row[mt_idx] = updates["title"]
BEJSONCore.bejson_core_atomic_write(page_file, pdoc)
def cms_content_delete_page(master_db_path: str, pages_dir: str, page_uuid: str) -> bool:
"""Delete page from master index and remove its content file."""
# 1. Master Index
doc = BEJSONCore.bejson_core_load_file(master_db_path)
t_idx = 0
u_idx = BEJSONCore.bejson_core_get_field_index(doc, "page_uuid")
new_values = []
found = False
for row in doc["Values"]:
if row[t_idx] == "PageRecord" and row[u_idx] == page_uuid:
found = True
continue
new_values.append(row)
if found:
doc["Values"] = new_values
BEJSONCore.bejson_core_atomic_write(master_db_path, doc)
# 2. Content File
page_file = os.path.join(pages_dir, f"{page_uuid}.json")
if os.path.exists(page_file):
os.remove(page_file)
return found
def cms_content_list_pages(master_db_path: str) -> List[Dict]:
"""List all pages from master index as a list of dictionaries."""
doc = BEJSONCore.bejson_core_load_file(master_db_path)
records = BEJSONCore.bejson_core_get_records_by_type(doc, "PageRecord")
fields = BEJSONCore.bejson_core_get_fields(doc)
results = []
for row in records:
item = {}
for i, f in enumerate(fields):
item[f['name']] = row[i]
results.append(item)
return results