This script automates the creation and updating of a hierarchical Table of Contents (TOC) for a Fibery entity (in this case of database ‘Page’) and its sub entities (sub Pages), facilitating easier navigation and organization of content.
Use cases
- Documentation Management: Automates TOC generation for large knowledge bases or documentation, improving content navigation and accessibility.
- Project Overview: Creates a unified TOC for project sub-pages, simplifying progress tracking and task overview in project management.
- Educational Resources: Facilitates structured access to educational modules or courses by automatically generating a navigable TOC for each section.
Script Explanation
- Initialize and Validate: It starts by attempting to retrieve the current page entity from
args.currentEntities
. - Fetch Page Entity: The script then fetches the page entity using its ID to obtain information about its subpages, existing TOC, and name.
- Handle TOC Entity: If the page already has a TOC entity associated with it, the script fetches this entity; otherwise, it creates a new TOC entity with a default name and associates it with the page.
- Determine TOC Depth: It calculates the maximum depth of headings to be included in the TOC based on the Level field of the TOC entity, with a default depth of 3 if unspecified.
- Generate TOC Content: For each subpage of the main page, the script:
- Fetches the subpage entity to get its description, public ID, and name.
- The script generates a hierarchical TOC JSON structure that includes the subpage’s title and headings up to the specified maximum depth, using a specific indentation and formatting style.
- Update TOC Document: If the TOC entity has a description and the generated TOC content is non-empty, the script updates the TOC entity’s description document with the new TOC content formatted in JSON.
- Error Handling: Throughout the script, errors are caught and logged to the console, particularly for operations like fetching entities, creating/updating entities, and setting document content.
- Utility Functions: It includes utility functions like
createHierarchicalTOCJson
for constructing the TOC JSON structure for a subpage andgetMaxDepthFromLevelEntity
for determining the TOC depth based on the TOC entity’s Level field.
Script (v. 2024-04-05)
const fibery = context.getService('fibery');
const pageType = 'YourSpaceName/Page'; // Replace YourSpaceName
const tocType = 'YourSpaceName/TOC'; // Replace YourSpaceName
const baseUrl = 'https://YourAccountName.fibery.io/YourSpaceName/Page/'; // Replace YourAccountName and YourSpaceName
const databaseId = '8ff09230-0883-11ee-a2e3-dd72e97a05a2'; // Replace with the UUID of your Page database
async function generateTOCAndAppendForSubPages() {
const currentPageEntity = args.currentEntities[0];
let pageEntity = await fibery.getEntityById(pageType, currentPageEntity.id, ['Sub Pages', 'TOC', 'Name']);
if (!pageEntity) return;
let parentTocEntity;
if (pageEntity.TOC && pageEntity.TOC.id) {
parentTocEntity = await fibery.getEntityById(tocType, pageEntity.TOC.id, ['Description', 'Level']);
} else {
parentTocEntity = await fibery.createEntity(tocType, { 'Name': `Aggregated Table Of Contents of Subpages`, 'Page': pageEntity.id });
}
if (!parentTocEntity.Level) {
await fibery.updateEntity(tocType, parentTocEntity.id, { 'Level': 'bbed5bd0-f38c-11ee-8ba1-7bd35f7e50e0' });
parentTocEntity = await fibery.getEntityById(tocType, parentTocEntity.id, ['Description', 'Level']);
}
const maxDepth = getMaxDepthFromLevelEntity(parentTocEntity.Level ? parentTocEntity.Level.Name : '3');
let tocContent = [];
for (const subPage of pageEntity['Sub Pages']) {
const subPageEntity = await fibery.getEntityById(pageType, subPage.id, ['Description', 'Public Id', 'Name']);
if (subPageEntity && subPageEntity.Description && subPageEntity.Description.Secret) {
let descriptionContentJson = await fibery.getDocumentContent(subPageEntity.Description.Secret, 'json');
tocContent = tocContent.concat(createHierarchicalTOCJson(descriptionContentJson, subPageEntity['Public Id'], subPageEntity.Name, subPage.id, maxDepth));
}
}
if (parentTocEntity.Description && parentTocEntity.Description.Secret && tocContent.length > 0) {
await fibery.setDocumentContent(parentTocEntity.Description.Secret, JSON.stringify({ doc: { type: "doc", content: tocContent } }), 'json');
}
}
function createHierarchicalTOCJson(descriptionContentJson, publicId, pageTitle, pageId, maxDepth) {
const tocEntry = { type: "paragraph", content: [{ type: "entity", attrs: { id: pageId, text: pageTitle, typeId: databaseId, }, marks: [{ type: "strong" }] }] };
const baseIndent = " ";
const additionalIndent = " ";
let isFirstHeading = true;
descriptionContentJson.doc.content.forEach(block => {
if (block.type === 'heading' && block.attrs.level <= maxDepth) {
if (isFirstHeading) {
tocEntry.content.push({ type: "hard_break" });
isFirstHeading = false;
} else {
tocEntry.content.push({ type: "hard_break" });
}
const indent = baseIndent + additionalIndent.repeat(block.attrs.level - 1);
const bulletPoint = "• ";
const textContent = block.content.map(element => element.text || '').join('');
tocEntry.content.push({ type: "text", text: indent + bulletPoint }, { type: "text", text: textContent, marks: [{ type: "link", attrs: { href: `${baseUrl}${publicId}/anchor=${block.attrs.guid}` } }] });
}
});
return [tocEntry];
}
function getMaxDepthFromLevelEntity(levelText) {
if (!levelText) return 3;
const match = levelText.match(/\d+/);
return match ? parseInt(match[0], 10) : 3;
}
await generateTOCAndAppendForSubPages();