Book and Library¶
The Book feature provides a comprehensive system for creating structured, hierarchical documentation with automatic table of contents numbering, PDF export capabilities, and user access management. The Library content type serves as a container for organizing multiple books.
Overview¶
The Book system consists of:
Library - Container for organizing multiple books
Book - Main container with table of contents, custom styling, and PDF export
Chapter - Hierarchical content units with automatic numbering
Paragraph - Content blocks within chapters
Key features include:
Automatic table of contents generation with hierarchical numbering (1, 1.1, 1.1.1, etc.)
PDF export via WeasyPrint integration
Custom CSS styling per book
User registration and access request workflows
REST API with enhanced serialization
Content Types¶
Library¶
A container specifically designed for organizing books.
Portal Type: Library
Allowed Content Types: Book only
Book¶
The main documentation container with chapters and table of contents support.
Portal Type: Book
Allowed Content Types: Chapter only
Schema Fields (via behaviors):
custom_css(Text) - Custom CSS with variable substitutioninclude_default_css(Bool) - Include default book CSS (default: True)header_image(NamedBlobImage) - Header image for PDF export
Chapter¶
Hierarchical content container that can nest other chapters.
Portal Type: Chapter
Allowed Content Types:
Chapter- Nested chapters (subchapters)Paragraph- Content blocksTableBlock- Table contentFileListingBlock- File listingsMediaFolder- Media containersCommentBlock- Comments
Schema Fields:
hide(Bool) - Hide from table of contents (default: False)
Paragraph¶
A content block within chapters with optional TOC visibility.
Table of Contents¶
The Toc class generates hierarchical chapter numbers automatically.
Number Generation¶
Chapters receive automatic numbers based on their position in the hierarchy:
First chapter:
1Second chapter:
2First subchapter of chapter 1:
1.1Second subchapter of chapter 1:
1.2First subchapter of chapter 2:
2.1Deep nesting:
1.1.1,1.1.2,2.1.1, etc.
Usage:
from wcs.backend.book.toc import Toc
# Get chapter number
chapter = context # A Chapter object
number = Toc(chapter).number() # Returns "1.1.2" or None if hidden
TOC Visibility¶
Chapters can be hidden from the table of contents:
# Hide chapter from TOC
chapter.hide = True
chapter.reindexObject()
Hidden chapters:
Do not appear in table of contents
Return
NonefromToc(chapter).number()Their children still calculate numbers correctly (skipping the hidden parent in numbering)
Catalog Index¶
The show_in_toc index enables efficient TOC queries:
# Query chapters visible in TOC
from plone import api
catalog = api.portal.get_tool('portal_catalog')
results = catalog.searchResults(
portal_type='Chapter',
show_in_toc=True,
path='/Plone/my-book'
)
REST API¶
Enhanced Serialization¶
Books and chapters include automatic TOC number calculation in REST API responses.
Book Response:
{
"@id": "http://localhost:8080/Plone/my-book",
"@type": "Book",
"title": "User Manual",
"items": [
{
"@id": "http://localhost:8080/Plone/my-book/chapter-1",
"@type": "Chapter",
"title": "Introduction",
"number": "1"
},
{
"@id": "http://localhost:8080/Plone/my-book/chapter-2",
"@type": "Chapter",
"title": "Getting Started",
"number": "2"
}
]
}
Chapter Response:
{
"@id": "http://localhost:8080/Plone/my-book/chapter-1/subchapter-1",
"@type": "Chapter",
"title": "Installation",
"number": "1.1",
"items": [
{
"@type": "Paragraph",
"title": "Prerequisites",
"number": "1.1.1"
}
]
}
GET @book-keywords¶
Returns all unique keywords in a book with occurrence counts.
http
GET /Plone/my-book/@book-keywords HTTP/1.1
Host: localhost:8080
Accept: application/json
curl
curl -i -X GET http://localhost:8080/Plone/my-book/@book-keywords -H "Accept: application/json"
python-requests
requests.get('http://localhost:8080/Plone/my-book/@book-keywords', headers={'Accept': 'application/json'})
Response:
{
"keywords": [
{"keyword": "Baurecht", "count": 5},
{"keyword": "Eigentum", "count": 3},
{"keyword": "Vertrag", "count": 2}
],
"total": 3
}
GET @book-keyword-search¶
Searches for paragraphs containing a specific keyword within a book.
http
GET /Plone/my-book/@book-keyword-search?keyword=Baurecht HTTP/1.1
Host: localhost:8080
Accept: application/json
curl
curl -i -X GET 'http://localhost:8080/Plone/my-book/@book-keyword-search?keyword=Baurecht' -H "Accept: application/json"
python-requests
requests.get('http://localhost:8080/Plone/my-book/@book-keyword-search?keyword=Baurecht', headers={'Accept': 'application/json'})
Response:
{
"@id": "http://localhost:8080/Plone/my-book/@book-keyword-search?keyword=Baurecht",
"keyword": "Baurecht",
"items": [
{
"@id": "http://localhost:8080/Plone/my-book/chapter-1/paragraph-1",
"title": "Introduction Paragraph",
"UID": "abc123",
"parent_chapter": {
"@id": "http://localhost:8080/Plone/my-book/chapter-1",
"title": "Introduction",
"number": "1"
}
}
],
"items_total": 1
}
Pagination:
Supports b_start and b_size query parameters for pagination.
http
GET /Plone/my-book/@book-keyword-search?keyword=Baurecht&b_size=10&b_start=0 HTTP/1.1
Host: localhost:8080
Accept: application/json
curl
curl -i -X GET 'http://localhost:8080/Plone/my-book/@book-keyword-search?keyword=Baurecht&b_size=10&b_start=0' -H "Accept: application/json"
python-requests
requests.get('http://localhost:8080/Plone/my-book/@book-keyword-search?keyword=Baurecht&b_size=10&b_start=0', headers={'Accept': 'application/json'})
PDF Export¶
Books support PDF generation via WeasyPrint integration with an external PDF server.
Configuration¶
Environment variables:
PDFSERVER_URL- PDF server URL (default:http://localhost:8040)PLONE_BACKEND_HOST- Backend URL for PDF server to fetch content
PDF Server Endpoints¶
POST @pdfserver-convert
Initiates PDF conversion for the current content.
http
POST /Plone/my-book/@pdfserver-convert HTTP/1.1
Host: localhost:8080
Accept: application/json
Content-Type: application/json
curl
curl -i -X POST http://localhost:8080/Plone/my-book/@pdfserver-convert -H "Accept: application/json" -H "Content-Type: application/json"
python-requests
requests.post('http://localhost:8080/Plone/my-book/@pdfserver-convert', headers={'Accept': 'application/json', 'Content-Type': 'application/json'})
Response:
{
"uid": "conversion-job-uid",
"status": "pending"
}
GET @pdfserver-status
Check conversion status.
http
GET /Plone/my-book/@pdfserver-status?uid=conversion-job-uid HTTP/1.1
Host: localhost:8080
Accept: application/json
curl
curl -i -X GET 'http://localhost:8080/Plone/my-book/@pdfserver-status?uid=conversion-job-uid' -H "Accept: application/json"
python-requests
requests.get('http://localhost:8080/Plone/my-book/@pdfserver-status?uid=conversion-job-uid', headers={'Accept': 'application/json'})
Response:
{
"uid": "conversion-job-uid",
"status": "completed"
}
GET @@pdfserver-download
Download the generated PDF.
http
GET /Plone/my-book/@@pdfserver-download?uid=conversion-job-uid HTTP/1.1
Host: localhost:8080
curl
curl -i -X GET 'http://localhost:8080/Plone/my-book/@@pdfserver-download?uid=conversion-job-uid'
python-requests
requests.get('http://localhost:8080/Plone/my-book/@@pdfserver-download?uid=conversion-job-uid')
Response: PDF file download
WeasyPrint Views¶
The following views render content for PDF generation:
@@view_weasyprint- Main WeasyPrint template for books@@toc_weasyprint- Table of contents for PDF@@chapter_weasyprint- Chapter rendering
Custom CSS¶
Books support custom CSS with variable substitution:
/* Available variables: $portal_url, $book_url */
.book-header {
background-image: url($book_url/@@images/header_image);
}
.book-logo {
background-image: url($portal_url/++resource++images/logo.png);
}
CSS is served via:
@@custom-book.css- Custom CSS with variable substitution@@book-variables.css- CSS custom properties for dates and images
User Access Management¶
The Book feature includes a complete user registration and access request workflow.
Book Owner Behavior¶
Books can have the IBookOwner behavior for managing access:
Fields:
moderator_email- Email for access request notificationsfallback_email- Fallback email if moderator is unavailablegroup- Plone group for book access
Registration Form¶
A custom registration form (@@register) allows users to:
Create a new account
Select books to request access to
Trigger email notifications to book moderators
Browser View: @@register (available when IBackendBookLayer is active)
Request Book Access¶
Logged-in users can request access to additional books.
Browser Form: @@request-book-access
REST API:
http
POST /Plone/@request-book-access HTTP/1.1
Host: localhost:8080
Accept: application/json
Content-Type: application/json
{
"books": ["book-uid-1", "book-uid-2"]
}
curl
curl -i -X POST http://localhost:8080/Plone/@request-book-access -H "Accept: application/json" -H "Content-Type: application/json" --data-raw '{"books": ["book-uid-1", "book-uid-2"]}'
python-requests
requests.post('http://localhost:8080/Plone/@request-book-access', headers={'Accept': 'application/json', 'Content-Type': 'application/json'}, json={'books': ['book-uid-1', 'book-uid-2']})
Response:
{
"success": true
}
Approve Access¶
Book moderators can approve access requests via @@book-share:
/Plone/my-book/@@book-share?userid=john.doe
This:
Adds the user to the book’s configured group
Sends a confirmation email to the user
Email Templates¶
The following email templates are used:
new-user-access-book-request-mail- New user registration requestuser-access-granted- Access approval notificationnew-user-created-and-access-to-book-mail- Admin-created user with book access
PDF Views¶
@@pdf_view- PDF conversion interface@@pdfserver-download- PDF download
AI Ask (RAG) Configuration¶
Books can individually enable or disable the AI Ask feature. This allows administrators to control which books expose the AI Ask tab to users.
Prerequisites¶
Global RAG must be enabled via the
RAG_ENABLED=trueenvironment variableThe book must have
rag_enabledset toTrue
When both conditions are met, the “AI Ask” tab appears in the book’s navigation alongside “Table of Contents” and “Keyword Search”.
Permissions¶
The rag_enabled field requires the Manage RAG properties permission to edit. By default, only Manager and Site Administrator roles have this permission.
REST API¶
The rag_enabled field is included in the book’s REST API response:
// JavaScript example using fetch
const response = await fetch('/plone/my-book', {
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer <token>'
}
});
const book = await response.json();
console.log(book.rag_enabled); // true or false
Asking Questions¶
When enabled, the AI Ask feature allows users to ask natural language questions about the book’s content. The system searches through the book’s chapters and paragraphs using hybrid search (combining keyword and semantic matching) and generates answers using an LLM.
The @rag-ask endpoint supports a path parameter to scope questions to a specific book:
// Ask a question scoped to a specific book
const response = await fetch('/plone/@rag-ask', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
question: 'What is covered in chapter 3?',
path: '/plone/my-book'
})
});
See the RAG (Retrieval-Augmented Generation) documentation for complete details on the RAG system configuration and API.
Integration Guide¶
Querying TOC Structure¶
from plone import api
from wcs.backend.book.toc import Toc
book = api.content.get(path='/library/my-book')
catalog = api.portal.get_tool('portal_catalog')
# Get all visible chapters
chapters = catalog.searchResults(
portal_type='Chapter',
path='/'.join(book.getPhysicalPath()),
show_in_toc=True,
sort_on='getObjPositionInParent'
)
for brain in chapters:
chapter = brain.getObject()
number = Toc(chapter).number()
print(f"{number} {chapter.Title()}")
File Locations¶
Core Module:
wcs/backend/book/content.py- Content type classes (Library, Book, Chapter, Paragraph)wcs/backend/book/behaviors.py- Behaviors (IShowInToc, IBookCustomCSS, IBookConfiguration, IBookOwner)wcs/backend/book/toc.py- Table of contents generatorwcs/backend/book/utils.py- Utility functionswcs/backend/book/indexer.py- Catalog indexers (show_in_toc, book_keywords)wcs/backend/book/keyword_utils.py- Keyword extraction utilitieswcs/backend/book/configure.zcml- ZCML configuration
REST API:
wcs/backend/book/restapi.py- Serializers and services
Views:
wcs/backend/book/views.py- Browser viewswcs/backend/book/templates/- View templates
PDF Generation:
wcs/backend/book/pdfserver_client.py- PDF server clientwcs/backend/book/weasyprint/views.py- WeasyPrint viewswcs/backend/book/weasyprint/templates/- PDF templateswcs/backend/book/weasyprint/resources/- PDF CSS resources
User Management:
wcs/backend/book/register_form.py- Registration formwcs/backend/book/emails.py- Email adapterswcs/backend/book/templates/emails/- Email templates
Type Definitions:
wcs/backend/profiles/default/types/Library.xmlwcs/backend/profiles/default/types/Book.xmlwcs/backend/profiles/default/types/Chapter.xmlwcs/backend/profiles/default/types/Paragraph.xml