@paroicms/server
Version:
The ParoiCMS server
192 lines • 7.04 kB
JavaScript
import { getPartTypeByName, sortParts } from "@paroicms/internal-anywhere-lib";
import { parseSqliteDateTime } from "@paroicms/internal-server-lib";
import { encodeLNodeId, } from "@paroicms/public-anywhere-lib";
import { type } from "arktype";
import { mainDbSchemaName } from "../connector/db-init/db-constants.js";
const LoadPartsRowAT = type({
nodeId: "number",
relativeId: "string",
typeName: "string",
parentId: "number",
orderNum: "number|null",
listName: "string",
publishDate: "string|number|Date",
tNodeId: "number|null",
"+": "reject",
}).pipe((r) => ({
nodeId: String(r.nodeId),
relativeId: r.relativeId,
typeName: r.typeName,
parentNodeId: String(r.parentId),
orderNum: r.orderNum ?? undefined,
listName: r.listName,
publishDate: parseSqliteDateTime(r.publishDate),
inRightLanguage: r.tNodeId !== null,
}));
export async function loadPartsOf(siteContext, tracker, documentId, listType) {
const { siteSchema, cn } = siteContext;
const listNames = [...getRecursiveListNames(siteSchema, listType)];
const rows = await cn("PaNode as n")
.select([
"n.id as nodeId",
"n.relativeId",
"n.publishDate",
"n.typeName",
"n.parentId",
"p.listName",
"o.orderNum",
"l.nodeId as tNodeId",
])
.innerJoin("PaPartNode as p", "p.nodeId", "n.id")
.leftJoin("PaLNode as l", {
"l.nodeId": "n.id",
"l.language": cn.raw("?", [documentId.language]),
"l.ready": cn.raw("?", [1]),
})
.leftJoin("PaOrderedNode as o", "o.nodeId", "n.id")
.where("p.documentNodeId", documentId.nodeId)
.whereIn("p.listName", listNames)
.whereRaw("n.publishDate <= current_timestamp");
tracker.trackAccess(mainDbSchemaName, "PaPartNode", "read");
const oneLanguageItems = rows.map((row) => {
const validated = LoadPartsRowAT.assert(row);
const inRightLanguage = validated.inRightLanguage;
const commonValues = {
nodeId: validated.nodeId,
relativeId: validated.relativeId,
typeName: validated.typeName,
parentNodeId: validated.parentNodeId,
orderNum: validated.orderNum,
listName: validated.listName,
publishDate: validated.publishDate.toISOString(),
};
return inRightLanguage
? {
...commonValues,
inRightLanguage,
language: documentId.language,
}
: {
...commonValues,
inRightLanguage,
language: undefined,
};
});
const completed = await completeMissingTranslations(siteContext, tracker, oneLanguageItems);
const byParent = new Map();
for (const item of completed) {
const key = keyOfParentPartData(item);
let list = byParent.get(key);
if (!list) {
list = [];
byParent.set(key, list);
}
list.push(item);
}
return byParent;
}
export function keyOfParentPartData(item) {
return `${item.listName}:${item.parentNodeId}`;
}
export function formatListOfPartValues(siteContext, list, listType) {
sortParts(listType, list);
const numberOfTypes = new Map();
const rawItems = list.map((item, index) => {
const numberOfType = (numberOfTypes.get(item.typeName) ?? 0) + 1;
numberOfTypes.set(item.typeName, numberOfType);
return formatPartValues(siteContext, { item, index, numberOfType });
});
return rawItems;
}
function formatPartValues(siteContext, { item, index, numberOfType }) {
return {
type: item.typeName,
nodeId: item.nodeId,
language: item.language,
relativeId: item.relativeId,
number: index + 1,
numberOfType,
inRightLanguage: item.inRightLanguage,
publishDate: item.publishDate,
languageLabel: siteContext.siteSchema.languageLabels[item.language],
};
}
const MissingTranslationsRowAT = type({
typeName: "string",
nodeId: "number",
language: "string",
"+": "reject",
}).pipe((r) => ({
typeName: r.typeName,
nodeId: String(r.nodeId),
language: r.language,
}));
async function completeMissingTranslations(siteContext, tracker, list) {
const missingNodeIds = list.filter((item) => !item.inRightLanguage).map((item) => item.nodeId);
let otherLanguageItems;
if (missingNodeIds.length > 0) {
const rows = await siteContext
.cn("PaLNode as l")
.select(["l.nodeId", "l.language", "n.typeName"])
.innerJoin("PaNode as n", "n.id", "l.nodeId")
.whereIn("l.nodeId", missingNodeIds);
tracker.trackAccess(mainDbSchemaName, "PaLNode", "read");
const formatted = rows.map((row) => MissingTranslationsRowAT.assert(row));
otherLanguageItems = new Map(formatted.map((item) => [encodeLNodeId(item), item]));
}
return list.map((item) => {
if (item.inRightLanguage)
return item;
for (const language of siteContext.siteSchema.languages) {
const found = otherLanguageItems?.get(encodeLNodeId({ nodeId: item.nodeId, language }));
if (found) {
return {
typeName: item.typeName,
nodeId: item.nodeId,
relativeId: item.relativeId,
parentNodeId: item.parentNodeId,
language,
orderNum: item.orderNum,
listName: item.listName,
inRightLanguage: false,
publishDate: item.publishDate,
};
}
}
throw new Error(`missing part for item '${item.nodeId}'`);
});
}
const CountPartsRowAT = type({
cnt: "number|null",
"+": "reject",
}).pipe((data) => ({
cnt: data.cnt ?? undefined,
}));
export async function countPartsOf(siteContext, tracker, parentId, listType) {
const { listName } = listType;
const result = await siteContext
.cn("PaNode as l")
.count("* as cnt")
.innerJoin("PaPartNode as p", "p.nodeId", "l.id")
.where("l.parentId", parentId.nodeId)
.where("p.listName", listName)
.first();
tracker.trackAccess(mainDbSchemaName, "PaPartNode", "read");
return CountPartsRowAT.assert(result).cnt ?? 0;
}
function getRecursiveListNames(siteSchema, list) {
const listNames = new Set([list.listName]);
const remainingPartNames = [...list.parts];
while (true) {
const childTypeName = remainingPartNames.shift();
if (!childTypeName)
break;
const child = getPartTypeByName(siteSchema, childTypeName);
if (child.list && !listNames.has(child.list.listName)) {
listNames.add(child.list.listName);
remainingPartNames.push(...child.list.parts);
}
}
return listNames;
}
//# sourceMappingURL=parts.queries.js.map