simple-module-pagebuilder 0.0.9__tar.gz → 0.0.11__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/PKG-INFO +12 -3
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/README.md +11 -2
- simple_module_pagebuilder-0.0.11/docs/api-reference.md +302 -0
- simple_module_pagebuilder-0.0.11/docs/architecture.md +235 -0
- simple_module_pagebuilder-0.0.11/docs/index.md +67 -0
- simple_module_pagebuilder-0.0.11/docs/operations.md +201 -0
- simple_module_pagebuilder-0.0.11/docs/user-guide.md +285 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/package.json +1 -1
- simple_module_pagebuilder-0.0.11/pagebuilder/components/editor/noHeaderActions.tsx +9 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/layoutPuckConfig.tsx +7 -4
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/puckConfig.test.tsx +13 -1
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/puckConfig.tsx +16 -1
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/snapshots/SnapshotList.tsx +2 -1
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgetCatalog.ts +7 -1
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_internal/eyebrow-split-section.tsx +4 -2
- simple_module_pagebuilder-0.0.11/pagebuilder/components/widgets/_internal/inline-image.tsx +18 -0
- simple_module_pagebuilder-0.0.11/pagebuilder/components/widgets/_internal/markdown-list.tsx +46 -0
- simple_module_pagebuilder-0.0.11/pagebuilder/components/widgets/_internal/rich-text.test.tsx +21 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_internal/rich-text.tsx +40 -64
- simple_module_pagebuilder-0.0.11/pagebuilder/components/widgets/_internal/site-nav-context.tsx +52 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/faq-render.tsx +17 -13
- simple_module_pagebuilder-0.0.11/pagebuilder/components/widgets/markdown-widget.test.tsx +86 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/markdown-widget.tsx +8 -8
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/quote-widget.tsx +20 -3
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/section-intro-widget.tsx +5 -4
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/site-footer-widget.tsx +10 -2
- simple_module_pagebuilder-0.0.11/pagebuilder/components/widgets/site-header-account.tsx +53 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/site-header-nav.tsx +11 -3
- simple_module_pagebuilder-0.0.11/pagebuilder/components/widgets/site-header-widget.test.tsx +101 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/site-header-widget.tsx +63 -26
- simple_module_pagebuilder-0.0.11/pagebuilder/components/widgets/site-nav-pages.test.tsx +96 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/conversion/blocks-to-html.test.ts +27 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/public_views.py +20 -5
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/views.py +8 -3
- simple_module_pagebuilder-0.0.11/pagebuilder/locales/de.json +1392 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/locales/en.json +18 -1
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/module.py +10 -0
- simple_module_pagebuilder-0.0.11/pagebuilder/og_image.py +53 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/pages/LayoutEditor.tsx +5 -9
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/pages/MediaLibrary.tsx +2 -1
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/pages/PageEditor.tsx +15 -4
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/pages/PageList.tsx +6 -3
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/pages/PendingReview.tsx +2 -1
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/pages/PublicPage.tsx +25 -19
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/service/__init__.py +3 -0
- simple_module_pagebuilder-0.0.11/pagebuilder/site_nav.py +69 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/i18n.test.ts +6 -1
- simple_module_pagebuilder-0.0.11/pagebuilder/utils/markdown-links.test.ts +57 -0
- simple_module_pagebuilder-0.0.11/pagebuilder/utils/markdown-links.ts +100 -0
- simple_module_pagebuilder-0.0.11/pagebuilder/utils/markdown.test.ts +153 -0
- simple_module_pagebuilder-0.0.11/pagebuilder/utils/markdown.ts +268 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pyproject.toml +12 -1
- simple_module_pagebuilder-0.0.11/tests/test_og_image.py +70 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_pages_api.py +25 -0
- simple_module_pagebuilder-0.0.11/tests/test_site_nav.py +144 -0
- simple_module_pagebuilder-0.0.9/pagebuilder/utils/markdown.ts +0 -167
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/.gitignore +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/__init__.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/board.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/boot.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/ConfirmDialog.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/DiffSummary.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/NewPageDialog.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/NoteDialog.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/PageBoard.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/PageBoardCard.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/PageListFilters.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/PageListRow.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/ScheduledBadge.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/StatusBadge.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/TranslatePageDialog.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/blockRegistry.test.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/blockRegistry.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/blocks/Button.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/blocks/Columns.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/blocks/Heading.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/blocks/Image.test.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/blocks/Image.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/blocks/MediaPicker.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/blocks/Spacer.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/blocks/Text.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/editor/BlockOutline.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/editor/NarrowCanvas.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/editor/PageActions.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/editor/PageEditorToolbar.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/editor/PageInspectorDrawer.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/editor/PageSettingsPanel.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/editor/RevisionHistoryPanel.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/editor/SchedulePanel.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/editor/SeoPreview.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/editor/SeoSettingsPanel.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/editor/TranslationsPanel.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/lazyBlock.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/localizeConfig.test.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/localizeConfig.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/media/MediaFilters.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/media/MediaFolderSidebar.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/media/MediaGrid.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/media/MediaHeader.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/media/MediaUploadQueue.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/media/MediaUsageList.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/media/types.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/migrateContent.test.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/migrateContent.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/snapshots/ImportPlanSummary.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/snapshots/UploadBundleDialog.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/snapshots/planSummary.test.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/snapshots/planSummary.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgetCatalog.test.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_internal/accent-text.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_internal/disclosure-chevron.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_internal/link-arrow.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_internal/solid-icons.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_shared/background-media.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_shared/content-media.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_shared/cta-button.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_shared/empty-placeholder.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_shared/eyebrow-text.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_shared/heading.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_shared/image-srcset.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_shared/index.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_shared/mask.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_shared/section.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_shared/social-icon.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_shared/text-link.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/accordion-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/alert-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/app-store-badges-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/article-cards-render.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/article-cards-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/call-to-action-render.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/call-to-action-styles.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/call-to-action-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/carousel-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/centered-hero-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/colored-action-list-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/column-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/contact-cards-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/contact-form-render.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/contact-form-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/container-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/definition-list-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/divider-widget.test.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/divider-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/eyebrow-section-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/faq-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/feature-card-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/feature-cards-render.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/feature-cards-styles.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/feature-cards-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/feature-grid-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/feature-showcase-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/gallery-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/grid-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/hero-widget-props.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/hero-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/html-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/iframe-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/leaderboard-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/list-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/logo-cloud-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/media-object-layout.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/media-object-render.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/media-object-text.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/media-object-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/newsletter-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/page-header-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/prose-section-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/row-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/signup-banner-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/simple-action-list-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/social-banner-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/stats-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/steps-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/story-cards-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/table-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/tabs-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/tags-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/testimonial-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/timeline-layout.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/timeline-render.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/timeline-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/under-construction-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/video-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/welcome-widget.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/contracts/__init__.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/contracts/events.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/contracts/schemas/__init__.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/contracts/schemas/_layout.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/contracts/schemas/_media.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/contracts/schemas/_pages.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/contracts/schemas/_snapshots.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/conversion/blocks-to-html.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/conversion/index.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/conversion/types.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/deps.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/diff.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/__init__.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/api/__init__.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/api/_deps.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/api/layout.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/api/media_detail.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/api/pages.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/api/revisions.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/api/snapshots.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/api/uploads.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/api/workflow.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/content_views.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/seo.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/fields/checkbox-field.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/fields/file-picker/file-gallery.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/fields/file-picker/image-picker-field.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/fields/file-picker/index.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/fields/file-picker/types.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/fields/index.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/fields/media-adapter.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/hooks/useAutosave.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/hooks/useEditorForm.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/hooks/useIsNarrow.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/hooks/useMediaDetail.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/hooks/useMediaUploads.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/hooks/usePageRevisions.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/hooks/usePageSchedule.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/hooks/usePageWorkflow.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/hooks/usePendingDialog.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/hooks/useSnapshots.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/layout_service.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/locales.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/media_files.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/media_images.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/media_queries.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/media_service.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/media_usage.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/models/__init__.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/models/_base.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/models/_layout.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/models/_media.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/models/_page.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/models/_redirect.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/models/_snapshot.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/pages/ContentImportReview.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/pages/ContentSnapshots.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/pages/MediaDetail.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/pages/Trash.tsx +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/permissions.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/public_claims.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/py.typed +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/redirects.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/scheduler.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/security.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/service/_common.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/service/_revisions.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/service/_translations.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/service/_trash.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/service/_workflow.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/services.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/settings.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/snapshots/__init__.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/snapshots/apply.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/snapshots/archive.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/snapshots/assets.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/snapshots/blobs.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/snapshots/capture.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/snapshots/format.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/snapshots/media_match.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/snapshots/pages.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/snapshots/plan.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/snapshots/service.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/snapshots/validate.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/static/widgets-base.css +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/tenancy.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/translation/apply.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/translation/extract.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/translation/index.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/translation/translatable-fields.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/translation/translation.test.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/api.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/blockOutline.test.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/blockOutline.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/datetime.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/decorative-image.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/editorSnapshot.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/i18n.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/locale.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/measure.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/mediaApi.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/mediaFormat.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/pagesApi.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/parse-list.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/request.test.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/request.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/slugify.test.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/slugify.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/snapshotsApi.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/types.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/widgetUtils.ts +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/conftest.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/db_fixture.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/locale_fixture.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/page_helpers.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/pg_support.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/snapshot_fixture.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/tenant_app.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/tenant_fixture.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_admin_list_view.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_approval_workflow.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_auth_gating.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_csrf.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_draft_preview.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_hreflang_and_sitemap.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_i18n.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_image_processing.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_media_detail.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_media_files.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_media_security.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_media_usage_escaping.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_module.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_multilingual.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_page_templates.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_page_translations.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_pages_search.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_public_claims.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_public_routes.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_public_view_headers.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_public_viewer.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_redirects.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_revision_notes_and_diff.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_revisions_api.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_scheduled_publish.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_scheduler_stop.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_scheduler_tenancy.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_seo_meta.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_settings_source.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_site_layout.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_sitemap_robots.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshot_apply.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshot_apply_media.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshot_archive.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshot_assets.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshot_blobs.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshot_capture.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshot_media_match.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshot_models.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshot_multilingual.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshot_page_payload.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshot_plan.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshot_validate.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshot_views.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshots_api.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_tenancy_blobs.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_tenancy_core.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_tenancy_isolation.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_tenancy_media.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_tenancy_public.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_tenancy_routes.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_trash.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_trash_preconditions.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_uploads_api.py +0 -0
- {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tsconfig.json +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: simple_module_pagebuilder
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.11
|
|
4
4
|
Summary: Drag-and-drop visual page builder for simple_module apps
|
|
5
5
|
Project-URL: Repository, https://github.com/antosubash/simple_module_python_modules
|
|
6
6
|
Author-email: Anto Subash <antosubash@live.com>
|
|
@@ -50,6 +50,13 @@ at `/p/{slug}`.
|
|
|
50
50
|
- **Content snapshots** — capture the whole site, download or upload it as a
|
|
51
51
|
`.zip`, and restore behind an approval gate. See below.
|
|
52
52
|
|
|
53
|
+
**Full documentation** lives in [`docs/`](docs/index.md):
|
|
54
|
+
[user guide](docs/user-guide.md) (the editor, workflow, layout, media and
|
|
55
|
+
snapshots screens), [API reference](docs/api-reference.md) (every
|
|
56
|
+
`/api/pagebuilder` route, public claims, errors), [operations](docs/operations.md)
|
|
57
|
+
(install, settings, languages, tenancy, troubleshooting) and
|
|
58
|
+
[architecture](docs/architecture.md).
|
|
59
|
+
|
|
53
60
|
## Installation
|
|
54
61
|
|
|
55
62
|
```bash
|
|
@@ -131,8 +138,10 @@ make migrate
|
|
|
131
138
|
```
|
|
132
139
|
|
|
133
140
|
Add `branch_labels = ("pagebuilder",)` to that first generated revision so the
|
|
134
|
-
|
|
135
|
-
`
|
|
141
|
+
revision can be named. That does **not** make `alembic downgrade
|
|
142
|
+
pagebuilder@base` remove pagebuilder alone: autogenerate chains every revision
|
|
143
|
+
off the current head, so the command walks everything beneath it
|
|
144
|
+
(antosubash/simple_module_python#333).
|
|
136
145
|
|
|
137
146
|
## Permissions
|
|
138
147
|
|
|
@@ -26,6 +26,13 @@ at `/p/{slug}`.
|
|
|
26
26
|
- **Content snapshots** — capture the whole site, download or upload it as a
|
|
27
27
|
`.zip`, and restore behind an approval gate. See below.
|
|
28
28
|
|
|
29
|
+
**Full documentation** lives in [`docs/`](docs/index.md):
|
|
30
|
+
[user guide](docs/user-guide.md) (the editor, workflow, layout, media and
|
|
31
|
+
snapshots screens), [API reference](docs/api-reference.md) (every
|
|
32
|
+
`/api/pagebuilder` route, public claims, errors), [operations](docs/operations.md)
|
|
33
|
+
(install, settings, languages, tenancy, troubleshooting) and
|
|
34
|
+
[architecture](docs/architecture.md).
|
|
35
|
+
|
|
29
36
|
## Installation
|
|
30
37
|
|
|
31
38
|
```bash
|
|
@@ -107,8 +114,10 @@ make migrate
|
|
|
107
114
|
```
|
|
108
115
|
|
|
109
116
|
Add `branch_labels = ("pagebuilder",)` to that first generated revision so the
|
|
110
|
-
|
|
111
|
-
`
|
|
117
|
+
revision can be named. That does **not** make `alembic downgrade
|
|
118
|
+
pagebuilder@base` remove pagebuilder alone: autogenerate chains every revision
|
|
119
|
+
off the current head, so the command walks everything beneath it
|
|
120
|
+
(antosubash/simple_module_python#333).
|
|
112
121
|
|
|
113
122
|
## Permissions
|
|
114
123
|
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
# PageBuilder API reference
|
|
2
|
+
|
|
3
|
+
All admin routes are JSON under `/api/pagebuilder`. Every one of them runs three
|
|
4
|
+
router-level dependencies, in this order:
|
|
5
|
+
|
|
6
|
+
1. **Authentication** — `401 Not authenticated` without a signed-in user, unless
|
|
7
|
+
the `requires_auth` setting is off (read per request).
|
|
8
|
+
2. **Tenant binding** (`tenancy.bind_admin`) — single-tenant host: tenant
|
|
9
|
+
`"default"`; multi-tenant host: the tenant the framework resolved, or
|
|
10
|
+
`403 tenant_required`.
|
|
11
|
+
3. **CSRF** — every `POST`/`PUT`/`PATCH`/`DELETE` needs the session token in an
|
|
12
|
+
`X-CSRF-Token` header, else `403 Invalid or missing CSRF token` (when
|
|
13
|
+
`csrf_protect` is on). The admin views expose it as the Inertia shared prop
|
|
14
|
+
`csrf.token` and in a JS-readable cookie (`pagebuilder_csrf` by default).
|
|
15
|
+
|
|
16
|
+
"Permission" below is the extra `RequiresPermission` check on top; **auth** means
|
|
17
|
+
any signed-in user. Errors are `{"detail": "<message>"}`; the table is at the end.
|
|
18
|
+
|
|
19
|
+
## Pages
|
|
20
|
+
|
|
21
|
+
### `GET /api/pagebuilder/pages` — auth
|
|
22
|
+
|
|
23
|
+
Query: `search` (title or slug, case-insensitive, wildcards literal), `status`
|
|
24
|
+
(`draft` | `submitted_for_review` | `published`; empty = no filter), `locale`,
|
|
25
|
+
`limit` (1–200, default **unbounded**), `offset`. Newest first, trashed pages
|
|
26
|
+
excluded.
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{"items": [PageRead, …], "total": 42}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`PageRead`:
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{"id": 7, "slug": "about", "locale": "en", "translation_group": "9f…",
|
|
36
|
+
"title": "About", "status": "published", "has_published": true,
|
|
37
|
+
"meta_title": null, "meta_description": "…", "og_image": null,
|
|
38
|
+
"canonical_url": null, "index_in_search": true, "rejection_note": null,
|
|
39
|
+
"publish_at": null, "unpublish_at": null, "parent_id": null,
|
|
40
|
+
"is_template": false, "show_in_header_nav": true, "show_in_footer": false,
|
|
41
|
+
"deleted_at": null, "created_at": "…", "updated_at": "…"}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Literal list routes
|
|
45
|
+
|
|
46
|
+
| Route | Permission | Returns |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| `GET /pages/trash` | `pagebuilder.edit` | trashed pages, most recently binned first |
|
|
49
|
+
| `GET /pages/templates` | auth | pages with `is_template`, by title |
|
|
50
|
+
| `GET /pages/pending` | `pagebuilder.approve` | pages in `submitted_for_review` |
|
|
51
|
+
|
|
52
|
+
All three return `{"items": [PageRead], "total": n}`.
|
|
53
|
+
|
|
54
|
+
### `GET /api/pagebuilder/locales` — auth
|
|
55
|
+
|
|
56
|
+
`{"locales": ["en", "de"], "default": "en"}` — the configured content languages.
|
|
57
|
+
|
|
58
|
+
### `POST /api/pagebuilder/pages` — `pagebuilder.edit` → 201
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{"title": "About", "slug": "about", "locale": "de",
|
|
62
|
+
"meta_title": null, "meta_description": null, "og_image": null,
|
|
63
|
+
"canonical_url": null, "index_in_search": true, "json_ld": null,
|
|
64
|
+
"show_in_header_nav": false, "show_in_footer": false,
|
|
65
|
+
"draft_data": {}, "publish_at": null, "unpublish_at": null,
|
|
66
|
+
"parent_id": null, "is_template": false, "copy_from_page_id": null}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Only `title` (1–300) and `slug` (`^[a-z0-9][a-z0-9-]*$`, ≤200) are required.
|
|
70
|
+
`locale` omitted means the default content locale; an unconfigured one is 422.
|
|
71
|
+
`copy_from_page_id` seeds `draft_data` from that page's draft and is not stored.
|
|
72
|
+
The page starts as a `draft` in a translation group of its own. Returns
|
|
73
|
+
`PageDetail`. Slug taken in that language → `409 Slug already in use in <locale>`.
|
|
74
|
+
|
|
75
|
+
### `GET /api/pagebuilder/pages/{id}` — auth
|
|
76
|
+
|
|
77
|
+
`PageDetail` = `PageRead` plus:
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{"draft_data": {…}, "published_data": {…} | null, "json_ld": {…} | null,
|
|
81
|
+
"translations": [{"id": 7, "locale": "en", "slug": "about", "title": "About",
|
|
82
|
+
"status": "published", "trashed": false}, …]}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`translations` lists every page in the group, this one and trashed ones included,
|
|
86
|
+
ordered by locale. A trashed page is 404 here.
|
|
87
|
+
|
|
88
|
+
### `PUT /api/pagebuilder/pages/{id}` — `pagebuilder.edit`
|
|
89
|
+
|
|
90
|
+
Partial update: any of `title`, `slug`, `meta_title`, `meta_description`,
|
|
91
|
+
`show_in_header_nav`, `show_in_footer`, `og_image`, `canonical_url`,
|
|
92
|
+
`index_in_search`, `json_ld`, `draft_data`, `parent_id`, `is_template`. There is
|
|
93
|
+
no `locale`: a page's language cannot change. A slug change records a redirect
|
|
94
|
+
from the old slug (same locale). Returns `PageDetail`.
|
|
95
|
+
|
|
96
|
+
### `POST /api/pagebuilder/pages/{id}/translations` — `pagebuilder.edit` → 201
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{"locale": "de", "slug": null, "title": null, "copy_content": true}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Creates a draft sibling in the same `translation_group` and returns **its**
|
|
103
|
+
`PageDetail`. `slug` defaults to the source's (or `-2`, `-3`… if taken in that
|
|
104
|
+
language); `title` to the source's. Inherits meta description, social image,
|
|
105
|
+
indexing, template flag and nav flags; never `canonical_url`; `parent_id` is the
|
|
106
|
+
parent's counterpart in that language, or none. Errors: 422 unconfigured locale;
|
|
107
|
+
409 `This page is already in <locale>.`; 409 `A <locale> translation already
|
|
108
|
+
exists.` with header `X-Existing-Page-Id`; 409 slug taken.
|
|
109
|
+
|
|
110
|
+
### Trash
|
|
111
|
+
|
|
112
|
+
| Route | Permission | Effect |
|
|
113
|
+
|---|---|---|
|
|
114
|
+
| `DELETE /pages/{id}` | `pagebuilder.edit` | 204. Moves to trash (`deleted_at`). Publishes nothing. |
|
|
115
|
+
| `POST /pages/{id}/restore` | `pagebuilder.edit` | `PageDetail`, status `draft`, `published_data` kept. 404 unless trashed. |
|
|
116
|
+
| `DELETE /pages/{id}/purge` | `pagebuilder.edit` | 204. Removes the row, revisions and redirects, un-parents children, commits, then publishes `PageDeleted`. 404 unless trashed. |
|
|
117
|
+
|
|
118
|
+
## Workflow
|
|
119
|
+
|
|
120
|
+
Each returns `PageRead`. Bodies marked *note* take `{"note": "…"}` (≤2000,
|
|
121
|
+
optional; send `{}` or no body).
|
|
122
|
+
|
|
123
|
+
| Route | Permission | Body | Transition |
|
|
124
|
+
|---|---|---|---|
|
|
125
|
+
| `POST /pages/{id}/publish` | `pagebuilder.publish` | note | any → `published`; draft copied to published; clears `publish_at` and `rejection_note` |
|
|
126
|
+
| `POST /pages/{id}/unpublish` | `pagebuilder.publish` | note | → `draft`; clears `unpublish_at` |
|
|
127
|
+
| `POST /pages/{id}/submit` | `pagebuilder.edit` | note | `draft` → `submitted_for_review`, else 409 |
|
|
128
|
+
| `POST /pages/{id}/approve` | `pagebuilder.approve` | note | `submitted_for_review` → `published`, else 409 |
|
|
129
|
+
| `POST /pages/{id}/reject` | `pagebuilder.approve` | `{"note": "…"}` required, 1–2000 | `submitted_for_review` → `draft`, sets `rejection_note`, else 409 |
|
|
130
|
+
| `POST /pages/{id}/schedule` | `pagebuilder.publish` | `{"publish_at"?, "unpublish_at"?}` | sets timestamps only |
|
|
131
|
+
|
|
132
|
+
Every transition except `schedule` writes a revision with event `publish`,
|
|
133
|
+
`unpublish`, `submit`, `approve` or `reject`. For `schedule`, an absent key leaves
|
|
134
|
+
that field alone, `null` clears it, `{}` is a no-op; naive datetimes are UTC; both
|
|
135
|
+
set with `publish_at >= unpublish_at` is
|
|
136
|
+
`422 publish_at must be strictly before unpublish_at`.
|
|
137
|
+
|
|
138
|
+
## Revisions
|
|
139
|
+
|
|
140
|
+
| Route | Permission | Returns |
|
|
141
|
+
|---|---|---|
|
|
142
|
+
| `GET /pages/{id}/revisions` | auth | `{"items": [PageRevisionRead]}`, newest first |
|
|
143
|
+
| `GET /pages/{id}/revisions/{rev}` | auth | `PageRevisionRead` + `data` |
|
|
144
|
+
| `GET /pages/{id}/revisions/{before}/diff/{after}` | auth | `RevisionDiffResponse` |
|
|
145
|
+
| `POST /pages/{id}/revisions/{rev}/restore` | `pagebuilder.edit` | `PageDetail`; copies title, meta description, social image and blocks into the draft |
|
|
146
|
+
|
|
147
|
+
`PageRevisionRead`: `id`, `page_id`, `title`, `meta_description`, `og_image`,
|
|
148
|
+
`event`, `note`, `created_at`, `created_by`. The diff names direction
|
|
149
|
+
explicitly because a restored revision can have a higher id and older content:
|
|
150
|
+
|
|
151
|
+
```json
|
|
152
|
+
{"before_id": 3, "after_id": 5,
|
|
153
|
+
"metadata": {"title": {"before": "A", "after": "B"}},
|
|
154
|
+
"blocks": {"added": [{"id": "…", "type": "Hero", "fields": []}],
|
|
155
|
+
"changed": [{"id": "…", "type": "Text", "fields": ["text"]}],
|
|
156
|
+
"removed": []}}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
A revision that belongs to another page is 404.
|
|
160
|
+
|
|
161
|
+
## Site layout
|
|
162
|
+
|
|
163
|
+
| Route | Permission | Body / returns |
|
|
164
|
+
|---|---|---|
|
|
165
|
+
| `GET /layout` | auth | `LayoutDetail` (`id`, `header_data`, `footer_data`, timestamps); created empty on first read |
|
|
166
|
+
| `PUT /layout` | `pagebuilder.edit` | `{"header_data"?, "footer_data"?, "note"?}`; `null`/absent leaves a slot alone. An unchanged save writes no revision. |
|
|
167
|
+
| `GET /layout/revisions` | auth | `{"items": [{"id", "layout_id", "note", "created_at", "created_by"}]}` |
|
|
168
|
+
| `GET /layout/revisions/{id}` | auth | revision + `header_data`, `footer_data` |
|
|
169
|
+
| `POST /layout/revisions/{id}/restore` | `pagebuilder.edit` | `LayoutDetail` |
|
|
170
|
+
|
|
171
|
+
## Media
|
|
172
|
+
|
|
173
|
+
### `GET /api/pagebuilder/uploads` — auth
|
|
174
|
+
|
|
175
|
+
Query: `search`, `content_type`, `folder` (absent = all, empty = unfiled, else
|
|
176
|
+
exact path), `min_size_bytes`, `max_size_bytes`, `cursor`, `offset`, `limit`
|
|
177
|
+
(1–200, default 60). Newest first. `offset` takes precedence over `cursor` and
|
|
178
|
+
adds `total`.
|
|
179
|
+
|
|
180
|
+
```json
|
|
181
|
+
{"items": [{"id": 4, "filename": "3f…a1.jpg", "original_filename": "hero.jpg",
|
|
182
|
+
"content_type": "image/jpeg", "size_bytes": 482113,
|
|
183
|
+
"url": "/media/pagebuilder/default/3f…a1.jpg", "width": 2400,
|
|
184
|
+
"height": 1600, "folder": "marketing/heros", "alt_text": "",
|
|
185
|
+
"caption": "", "credit": "",
|
|
186
|
+
"variants": {"w640": {"filename": "…", "url": "…", "content_type": "image/webp",
|
|
187
|
+
"width": 640, "height": 427, "size_bytes": 31022}},
|
|
188
|
+
"created_at": "…"}],
|
|
189
|
+
"next_cursor": 3, "folders": ["marketing/heros"], "total": null}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### `POST /api/pagebuilder/uploads` — `pagebuilder.edit` → 201
|
|
193
|
+
|
|
194
|
+
Multipart: `file`, optional `folder`. Returns `MediaAssetRead`. The declared type
|
|
195
|
+
must be in `media_allowed_content_types` and match the sniffed magic bytes (415
|
|
196
|
+
otherwise); over `media_max_bytes` is 413. Stored under a random name with the
|
|
197
|
+
sniffed extension; WebP variants generated for each configured width below the
|
|
198
|
+
original's.
|
|
199
|
+
|
|
200
|
+
### One asset — all `pagebuilder.edit`
|
|
201
|
+
|
|
202
|
+
| Route | Effect |
|
|
203
|
+
|---|---|
|
|
204
|
+
| `GET /uploads/{id}` | `{"asset": MediaAssetRead, "used_in": [{"page_id", "title", "slug", "status", "draft_only"}], "used_in_total": n}` (first 20 pages) |
|
|
205
|
+
| `PUT /uploads/{id}` | `{"alt_text"?, "caption"?, "credit"?}` (≤500/500/200); returns the same detail shape |
|
|
206
|
+
| `DELETE /uploads/{id}/checked` | 204, or `409 Still used on N page(s): …. Detach it there first.` |
|
|
207
|
+
| `DELETE /uploads/{id}` | 204, unconditionally: removes the row, file and variants |
|
|
208
|
+
|
|
209
|
+
Usage counts non-trashed pages whose draft or published data contains the
|
|
210
|
+
asset's URL (current or pre-tenancy flat URL).
|
|
211
|
+
|
|
212
|
+
## Snapshots and imports
|
|
213
|
+
|
|
214
|
+
| Route | Permission | Body / returns |
|
|
215
|
+
|---|---|---|
|
|
216
|
+
| `GET /snapshots` | auth | `{"items": [SnapshotRead]}` |
|
|
217
|
+
| `POST /snapshots` | `pagebuilder.publish` | `{"note"?}` → 201 `SnapshotRead` |
|
|
218
|
+
| `GET /snapshots/{id}/download` | `pagebuilder.publish` | `application/zip`, `content-snapshot-{id}.zip` |
|
|
219
|
+
| `POST /snapshots/upload` | `pagebuilder.publish` | multipart `file` → 201 `SnapshotRead`; 413 over `snapshot_max_upload_bytes`; 422 invalid bundle |
|
|
220
|
+
| `DELETE /snapshots/{id}` | `pagebuilder.publish` | 204 |
|
|
221
|
+
| `POST /snapshots/{id}/restore` | `pagebuilder.publish` | 201 `PendingImportRead`; writes nothing to the site; 409 if one is pending |
|
|
222
|
+
| `GET /imports/pending` | auth | `PendingImportRead` or `null` |
|
|
223
|
+
| `POST /imports/{id}/approve` | `pagebuilder.approve` | `ImportApplyResponse` |
|
|
224
|
+
| `POST /imports/{id}/reject` | `pagebuilder.approve` | `{"note"?}` → `PendingImportRead` |
|
|
225
|
+
|
|
226
|
+
`SnapshotRead`: `id`, `note`, `source` (`manual` | `upload` | `pre_restore`),
|
|
227
|
+
`format_version`, `manifest`, `size_bytes`, `created_at`, `created_by`.
|
|
228
|
+
`PendingImportRead`: `id`, `snapshot_id`, `plan`, `status` (`pending` |
|
|
229
|
+
`approved` | `rejected`), `note`, `created_*`, `decided_at`, `decided_by`.
|
|
230
|
+
`ImportApplyResponse`: `pages_created`, `pages_updated`, `media_added`,
|
|
231
|
+
`redirects`, `redirects_dropped` (slugs). Deciding an import twice is 409
|
|
232
|
+
`Import already decided`. Self-approval is allowed.
|
|
233
|
+
|
|
234
|
+
## Public routes
|
|
235
|
+
|
|
236
|
+
Anonymous, `GET`/`HEAD` only, mounted at startup from the settings:
|
|
237
|
+
|
|
238
|
+
| Route | Notes |
|
|
239
|
+
|---|---|
|
|
240
|
+
| `{public_route_prefix}/{slug}` | default content locale, e.g. `/p/about` |
|
|
241
|
+
| `/{locale}{public_route_prefix}/{slug}` | each other content locale, e.g. `/de/p/about` |
|
|
242
|
+
| `/{default}{public_route_prefix}/{slug}` | multilingual sites only: 301 to the unprefixed URL |
|
|
243
|
+
| `/sitemap.xml` | when `sitemap_enabled` |
|
|
244
|
+
| `/robots.txt` | when `robots_enabled` |
|
|
245
|
+
| `{media_url_prefix}/{tenant}/{file}` | uploaded media, `max-age=31536000, immutable` |
|
|
246
|
+
|
|
247
|
+
An old slug answers 301 to the page's current address. A claimed slug (below)
|
|
248
|
+
answers 404 here. See [operations.md](operations.md#public-pages-caching-and-csp)
|
|
249
|
+
for headers.
|
|
250
|
+
|
|
251
|
+
## For other modules
|
|
252
|
+
|
|
253
|
+
**Contracts.** DTOs are exported from `pagebuilder.contracts`; anything else in
|
|
254
|
+
the package may change without a major version.
|
|
255
|
+
|
|
256
|
+
**`PageDeleted(page_id, slug)`** (`pagebuilder.contracts.events`) is published
|
|
257
|
+
on the event bus when a page is *purged* — not when it is trashed, since a
|
|
258
|
+
restore would otherwise come back without what other modules dropped. A module
|
|
259
|
+
that keys rows to a page id must handle it: there is no cross-module foreign key,
|
|
260
|
+
and SQLite reuses ids.
|
|
261
|
+
|
|
262
|
+
**Public claims** (`pagebuilder.public_claims`). A module that presents a page at
|
|
263
|
+
its own address registers a claim from its startup hook:
|
|
264
|
+
|
|
265
|
+
```python
|
|
266
|
+
from pagebuilder import public_claims
|
|
267
|
+
|
|
268
|
+
async def claim(db, slugs, locale) -> dict[str, str]:
|
|
269
|
+
# return {slug: public_path} for the slugs in `locale` this module serves
|
|
270
|
+
...
|
|
271
|
+
|
|
272
|
+
public_claims.register(claim)
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Claims are asked in bulk, one locale at a time. A claimed slug is then refused
|
|
276
|
+
(404) by `/p/{slug}`; the sitemap, `hreflang` alternates, nav links and old-slug
|
|
277
|
+
redirects all point at the claimed path instead. On the same slug the earliest
|
|
278
|
+
registered claim wins. `public_claims.reset()` clears them (tests).
|
|
279
|
+
|
|
280
|
+
To serve the claimed page, call
|
|
281
|
+
`pagebuilder.endpoints.public_views.render_public_page(slug, request, inertia,
|
|
282
|
+
db, settings, url_prefix=…, locale=…)` from a router that depends on
|
|
283
|
+
`tenancy.bind_public`: it applies the same ETag, cache, CSP, canonical,
|
|
284
|
+
`hreflang`, nav and redirect handling.
|
|
285
|
+
|
|
286
|
+
## Errors
|
|
287
|
+
|
|
288
|
+
| Status | Detail | When |
|
|
289
|
+
|---|---|---|
|
|
290
|
+
| 401 | `Not authenticated` | no user and `requires_auth` on |
|
|
291
|
+
| 403 | `tenant_required` | multi-tenant host, admin request with no tenant |
|
|
292
|
+
| 403 | `Invalid or missing CSRF token` | mutating request without the header |
|
|
293
|
+
| 403 | (framework) | missing permission |
|
|
294
|
+
| 404 | `Page not found` / `Revision not found` / `Asset not found` / `Snapshot not found` / `Import not found` | unknown id, trashed page, other tenant's row |
|
|
295
|
+
| 409 | `Slug already in use in <locale>` | create, update |
|
|
296
|
+
| 409 | `Cannot submit/approve/reject a page in '<status>' status` | workflow |
|
|
297
|
+
| 409 | translation exists / page already in that locale | translations |
|
|
298
|
+
| 409 | `Still used on …` | checked media delete |
|
|
299
|
+
| 409 | `An import is already awaiting approval` / `Import already decided` | imports |
|
|
300
|
+
| 413 | `File exceeds N bytes` / `Bundle exceeds N bytes` | uploads |
|
|
301
|
+
| 415 | content-type not allowed, or sniffed type mismatch | media upload |
|
|
302
|
+
| 422 | validation; unconfigured locale; schedule order; unsupported bundle format | various |
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# PageBuilder architecture
|
|
2
|
+
|
|
3
|
+
Design records for the larger changes are under `docs/superpowers/specs/` at the
|
|
4
|
+
repo root (for example `2026-10-05-pagebuilder-tenancy-design.md`). This page
|
|
5
|
+
describes the code as it is.
|
|
6
|
+
|
|
7
|
+
## Boundaries
|
|
8
|
+
|
|
9
|
+
Pagebuilder depends on the framework (`simple_module_core`, `_db`, `_hosting`)
|
|
10
|
+
and on the framework's **Settings** module (`depends_on=["Settings"]`), nothing
|
|
11
|
+
else. Other modules reach it only through:
|
|
12
|
+
|
|
13
|
+
| Seam | Used for |
|
|
14
|
+
|---|---|
|
|
15
|
+
| `pagebuilder.contracts` | DTOs; everything else is internal |
|
|
16
|
+
| `PageDeleted` event | dropping rows keyed to a purged page |
|
|
17
|
+
| `pagebuilder.public_claims` | serving a page at another module's address |
|
|
18
|
+
| `render_public_page` | rendering a claimed page with the viewer's headers |
|
|
19
|
+
| `registerPuckBlocks` (frontend) | adding blocks to the palette |
|
|
20
|
+
|
|
21
|
+
A module that only *might* have pagebuilder installed (news does this) imports it
|
|
22
|
+
lazily behind a guard.
|
|
23
|
+
|
|
24
|
+
## Data
|
|
25
|
+
|
|
26
|
+
| Table | Unique | Holds |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| `pagebuilder_pages` | `(tenant_id, locale, slug)`, `(tenant_id, translation_group, locale)` | title, `draft_data`, `published_data` (Puck JSON), status, SEO fields, nav flags, `parent_id`, `is_template`, schedule, `rejection_note`, `deleted_at` |
|
|
29
|
+
| `pagebuilder_page_revisions` | — | one row per transition: event, note, title, meta description, og image, `data` |
|
|
30
|
+
| `pagebuilder_page_redirects` | per tenant + locale + `from_slug` | old slug → `page_id` |
|
|
31
|
+
| `pagebuilder_layout` | one row per tenant (lazily created) | `header_data`, `footer_data` |
|
|
32
|
+
| `pagebuilder_layout_revisions` | — | one row per changed save |
|
|
33
|
+
| `pagebuilder_media` | per tenant + filename | file name, original name, type, size, dimensions, folder, alt/caption/credit, `variants` |
|
|
34
|
+
| `pagebuilder_snapshots`, `pagebuilder_snapshot_media`, `pagebuilder_pending_imports` | one pending import per tenant | snapshot manifests, the media each references, staged restores and their plans |
|
|
35
|
+
|
|
36
|
+
Every table carries `MultiTenantMixin`; most carry `AuditMixin`
|
|
37
|
+
(`created_by`/`updated_by`). `translation_group` is a generated key, not a
|
|
38
|
+
pointer to an "original": translations are siblings, so deleting one orphans
|
|
39
|
+
nothing. `parent_id` is breadcrumb-only and never part of the URL.
|
|
40
|
+
`published_data` is kept on unpublish and restore, which is what
|
|
41
|
+
`has_published` reports.
|
|
42
|
+
|
|
43
|
+
## Tenancy (#38)
|
|
44
|
+
|
|
45
|
+
The framework filters `MultiTenantMixin` tables only while a tenant is bound; with
|
|
46
|
+
none bound a read is **unfiltered**. So every entry point binds one itself and
|
|
47
|
+
never relies on one happening to be set (`pagebuilder/tenancy.py`):
|
|
48
|
+
|
|
49
|
+
| Entry point | Binding |
|
|
50
|
+
|---|---|
|
|
51
|
+
| Admin API and views | `bind_admin` router dependency: `"default"` single-tenant; resolved tenant or 403 `tenant_required` multi-tenant |
|
|
52
|
+
| Public viewer, sitemap | `bind_public`: `"default"`, or resolved tenant, or the same 404 an unknown slug gets |
|
|
53
|
+
| Media files | `MediaFiles` maps the request to `<media_root>/<tenant>/` |
|
|
54
|
+
| Scheduler | scans due tenants under `all_tenants()`, then one session per tenant under `tenant_context` |
|
|
55
|
+
|
|
56
|
+
`bind_admin`/`bind_public` must precede `get_db` so the commit runs while the
|
|
57
|
+
tenant is still bound; router-level dependencies guarantee that. The mode is
|
|
58
|
+
detected once, in `on_startup`, from the built middleware stack
|
|
59
|
+
(`tenancy.configure`), and a host fixed to a non-`default` tenant is refused. A
|
|
60
|
+
`text()` statement on a pagebuilder table would need its own `tenant_id`
|
|
61
|
+
predicate (there are none).
|
|
62
|
+
|
|
63
|
+
## Boot order and settings
|
|
64
|
+
|
|
65
|
+
Settings are DB-backed (`settings.PagebuilderSettings`, registered with
|
|
66
|
+
`register_module_settings`; `settings_customise_sources` returns init kwargs
|
|
67
|
+
only). The host hydrates them at **lifespan start**, after app construction. So:
|
|
68
|
+
|
|
69
|
+
- `register_routes` reads no setting; the admin auth and CSRF dependencies read
|
|
70
|
+
`app.state.pagebuilder.settings` per request.
|
|
71
|
+
- `register_public_routes` does nothing. `on_startup` (`boot.py`) exempts the
|
|
72
|
+
public prefixes (one per content locale, trailing `/` so `/p` never exempts
|
|
73
|
+
`/pagebuilder/`), mounts one `locale_router` per locale, the default-locale
|
|
74
|
+
alias, the SEO router and the media mount.
|
|
75
|
+
- `CsrfCookieMiddleware` is always installed and checks `csrf_protect` per
|
|
76
|
+
request.
|
|
77
|
+
|
|
78
|
+
Any field read in `on_startup` is marked `json_schema_extra=_RESTART`. Adding a
|
|
79
|
+
field means adding it to the class; there is no env var to add.
|
|
80
|
+
|
|
81
|
+
## Status machine (`service/_workflow.py`)
|
|
82
|
+
|
|
83
|
+
| From | Action | To | Revision | Side effects |
|
|
84
|
+
|---|---|---|---|---|
|
|
85
|
+
| any | publish | `published` | `publish` | draft → published, clear `publish_at`, `rejection_note` |
|
|
86
|
+
| any | unpublish | `draft` | `unpublish` | clear `unpublish_at` |
|
|
87
|
+
| `draft` | submit | `submitted_for_review` | `submit` | clear `rejection_note` |
|
|
88
|
+
| `submitted_for_review` | approve | `published` | `approve` | draft → published |
|
|
89
|
+
| `submitted_for_review` | reject | `draft` | `reject` | set `rejection_note` |
|
|
90
|
+
| `draft` + `publish_at` due | scheduler | `published` | `publish` ("Auto-published on schedule") | |
|
|
91
|
+
| `published` + `unpublish_at` due | scheduler | `draft` | `unpublish` | |
|
|
92
|
+
|
|
93
|
+
Anything else is 409. Saving (`PUT`) and restoring a revision write no revision;
|
|
94
|
+
a revision always snapshots `draft_data` after the transition. Trash is
|
|
95
|
+
orthogonal (`service/_trash.py`): `deleted_at` hides the row from every query via
|
|
96
|
+
`NOT_TRASHED`, restore sets status `draft`, and **purge** is the one method that
|
|
97
|
+
commits itself — it must commit before publishing `PageDeleted`, because a
|
|
98
|
+
subscriber's own session would otherwise hit SQLite's lock and the bus swallows
|
|
99
|
+
handler errors. Purge clears revisions and redirects and nulls children's
|
|
100
|
+
`parent_id` explicitly (SQLite does not enforce FKs and reuses ids).
|
|
101
|
+
|
|
102
|
+
## The public viewer (`endpoints/public_views.py`)
|
|
103
|
+
|
|
104
|
+
```mermaid
|
|
105
|
+
sequenceDiagram
|
|
106
|
+
participant V as Visitor
|
|
107
|
+
participant R as locale_router(locale)
|
|
108
|
+
participant C as public_claims
|
|
109
|
+
participant DB as pages / redirects / layout
|
|
110
|
+
V->>R: GET /de/p/about
|
|
111
|
+
R->>R: bind_public (tenant or 404)
|
|
112
|
+
R->>C: claimed_url(about, de)? → 404 if claimed
|
|
113
|
+
R->>DB: published page (slug, locale)
|
|
114
|
+
alt not found
|
|
115
|
+
R->>DB: redirects.resolve(slug, locale) → 301 to current (or claimed) URL, else 404
|
|
116
|
+
end
|
|
117
|
+
R->>DB: layout + site_nav(locale)
|
|
118
|
+
R->>R: ETag(page, layout, variant, nav) → 304 on match
|
|
119
|
+
R-->>V: Inertia PublicPage + Cache-Control, CSP, Content-Language, Vary
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
- **Title** is `meta_title` stripped, else `title`; **canonical** is
|
|
123
|
+
`canonical_url` or `public_base_url` + path; **alternates** are the group's
|
|
124
|
+
*published* pages (each resolved through claims), plus `x-default` = the
|
|
125
|
+
default locale, omitted when fewer than two.
|
|
126
|
+
- **`site_nav`** (`site_nav.py`) returns published, non-trashed pages in the
|
|
127
|
+
page's locale with either flag, ordered by `lower(title)`, hrefs through
|
|
128
|
+
claims. The SiteHeader/SiteFooter widgets append them after the layout's links,
|
|
129
|
+
skipping hrefs the layout already has (trailing slash ignored, dropdown
|
|
130
|
+
children included) — `appendPages` in
|
|
131
|
+
`components/widgets/_internal/site-nav-context.tsx`. Order is the layout's.
|
|
132
|
+
- **Preview** (`/pagebuilder/{id}/preview`) renders `draft_data` through the same
|
|
133
|
+
`PublicPage` component with `index_in_search=False` and no alternates, so the
|
|
134
|
+
preview cannot disagree with the live renderer.
|
|
135
|
+
- **Sitemap** uses a column-only query (no block JSON), asks claims once per
|
|
136
|
+
locale, and adds `xhtml:link` alternates per translation group.
|
|
137
|
+
|
|
138
|
+
Redirects (`redirects.py`) are locale-scoped. `record` first deletes any redirect
|
|
139
|
+
*from* the new slug (no loops) and from the old one (re-renames move it);
|
|
140
|
+
`resolve` returns the *page's* current slug, so chains collapse to one hop.
|
|
141
|
+
|
|
142
|
+
## Media
|
|
143
|
+
|
|
144
|
+
`media_service.upload` checks the declared type against the allowlist, sniffs
|
|
145
|
+
magic bytes, requires the two to agree, names the file `<uuid><sniffed ext>`,
|
|
146
|
+
streams to disk under the size cap, then generates WebP variants in a thread
|
|
147
|
+
(`media_images.process_image`). Usage (`media_usage.find`) searches draft and
|
|
148
|
+
published JSON for the asset's URL — both the per-tenant URL and the pre-#38 flat
|
|
149
|
+
one — ignoring trashed pages.
|
|
150
|
+
|
|
151
|
+
## Snapshots (`snapshots/`)
|
|
152
|
+
|
|
153
|
+
- **Capture** writes pages (keyed by `(locale, slug)`), redirects and the layout
|
|
154
|
+
as JSON documents, media URLs rewritten to `asset://<name>` sentinels, media
|
|
155
|
+
bytes into a per-tenant content-addressed blob store shared across snapshots.
|
|
156
|
+
`FORMAT_VERSION` is 2; versions 1 and 2 are readable (v1 pages are read as the
|
|
157
|
+
default locale).
|
|
158
|
+
- **Upload** validates the zip at the boundary (`archive.py`, `validate.py`):
|
|
159
|
+
entry names stay inside the destination, the manifest declares a readable
|
|
160
|
+
format, every blob hashes to its name, the expanded size stays under
|
|
161
|
+
`snapshot_max_extracted_bytes`.
|
|
162
|
+
- **Restore** computes a plan (`plan.py`) and parks it as a `PendingImport`.
|
|
163
|
+
**Approve** takes a `pre_restore` snapshot, then `apply.apply_bundle`: media
|
|
164
|
+
matched by digest, pages upserted by `(locale, slug)` (a trashed match is
|
|
165
|
+
revived, not duplicated), parents and redirects resolved by slug in a second
|
|
166
|
+
pass, the layout saved through `LayoutService`. No page revisions are written;
|
|
167
|
+
the `pre_restore` snapshot is the before-state. Nothing is deleted.
|
|
168
|
+
|
|
169
|
+
## Frontend
|
|
170
|
+
|
|
171
|
+
- **Pages.** `pagebuilder/pages/*.tsx` are Inertia pages, registered by
|
|
172
|
+
`import.meta.glob`: the file name *is* the page name (`PageBuilder/PageList`).
|
|
173
|
+
- **Block palette.** `components/widgetCatalog.ts` lists the built-ins and their
|
|
174
|
+
categories; `puckConfig.tsx` merges in `registerPuckBlocks` registrations
|
|
175
|
+
(`{blocks, category?, layout?}`) and is memoised on the registry version.
|
|
176
|
+
Built-in names are declared so a module cannot shadow one (a duplicate name
|
|
177
|
+
throws: names are the stored `type`). `layoutPuckConfig.tsx` is a curated
|
|
178
|
+
allowlist with **Site chrome**; blocks once offered there stay registered but
|
|
179
|
+
hidden so saved layouts keep rendering. Heavy blocks use `lazyBlock` so their
|
|
180
|
+
dependencies stay out of the entry chunk.
|
|
181
|
+
- **Stored data** goes through `migrateContent` on every load (editor, public
|
|
182
|
+
page, layout) to upgrade older Puck shapes.
|
|
183
|
+
- **Markdown** lives in `utils/markdown.ts` (blocks, lists, emphasis, HTML export)
|
|
184
|
+
and `utils/markdown-links.ts` (links and images with the scheme allowlist);
|
|
185
|
+
`_internal/rich-text.tsx` renders them for widget fields.
|
|
186
|
+
- **i18n.** Every editor string is in `locales/en.json`, namespace
|
|
187
|
+
`pagebuilder`. `utils/i18n.ts` derives the `keys` tree from that JSON at
|
|
188
|
+
compile time, so `tsc` rejects an undefined key. Block configs are module-scope
|
|
189
|
+
constants and cannot call hooks, so their labels hold keys and
|
|
190
|
+
`localizeConfig.ts` resolves the assembled config (including `arrayFields` /
|
|
191
|
+
`objectFields`) inside `PageEditor` and `LayoutEditor`. `defaultProps` stay
|
|
192
|
+
literal: they are seed content saved into the document.
|
|
193
|
+
- **Library helpers.** `conversion/` (`blocksToHtml`) and `translation/`
|
|
194
|
+
(extract and re-apply translatable strings) are pure functions with no storage.
|
|
195
|
+
|
|
196
|
+
## Rules for contributors
|
|
197
|
+
|
|
198
|
+
- **300-line cap** on every `.py`, `.ts` and `.tsx`, enforced by
|
|
199
|
+
`scripts/check_file_size.py` (`make lint`). Nothing under `modules/` is exempt;
|
|
200
|
+
that is why `service/`, `models/`, `endpoints/api/` and `contracts/schemas/`
|
|
201
|
+
are packages of mixins and sub-routers.
|
|
202
|
+
- **Never add a file under `pagebuilder/pages/` unless it is a real Inertia
|
|
203
|
+
page.** A stray `.tsx` there silently registers a page. Extractions go to
|
|
204
|
+
`components/`, `hooks/` or `utils/`.
|
|
205
|
+
- **Every slug lookup, redirect and claim takes a locale.** One that does not is
|
|
206
|
+
how `/de/p/x` starts serving the English page.
|
|
207
|
+
- **Every new entry point binds a tenant** (`bind_admin`, `bind_public`, or
|
|
208
|
+
`tenant_context` outside a request). Code in another module reading these
|
|
209
|
+
tables pins `tenant_id` itself.
|
|
210
|
+
- **No env vars.** New settings go on `PagebuilderSettings`; boot-time ones are
|
|
211
|
+
read from `app.state.pagebuilder.settings` in `on_startup` and marked restart.
|
|
212
|
+
- **No migrations in the module.** Hosts autogenerate against the models.
|
|
213
|
+
- **Literal API paths before `/pages/{page_id}`**, or `trash` parses as an id
|
|
214
|
+
(422). Same for `/uploads/{id}/checked`, which is why `media_detail` is
|
|
215
|
+
included before `uploads`.
|
|
216
|
+
- New UI strings go in `locales/en.json`; `utils/i18n.test.ts` rejects unused
|
|
217
|
+
entries and unfilled placeholders.
|
|
218
|
+
|
|
219
|
+
## File map
|
|
220
|
+
|
|
221
|
+
| Path | Role |
|
|
222
|
+
|---|---|
|
|
223
|
+
| `module.py`, `boot.py` | registration hooks; startup wiring |
|
|
224
|
+
| `settings.py`, `locales.py` | DB-backed settings; content-locale helpers |
|
|
225
|
+
| `tenancy.py`, `security.py`, `permissions.py` | tenant binding; auth/CSRF; permissions and roles |
|
|
226
|
+
| `models/`, `contracts/` | tables; public DTOs and `PageDeleted` |
|
|
227
|
+
| `service/` | `PagesService` (CRUD, workflow, revisions, trash, translations) |
|
|
228
|
+
| `layout_service.py`, `site_nav.py`, `redirects.py`, `public_claims.py` | layout; nav flags; old slugs; claims |
|
|
229
|
+
| `media_*.py` | upload, images, queries, files, usage |
|
|
230
|
+
| `scheduler.py` | per-tenant publish/unpublish/purge loop |
|
|
231
|
+
| `snapshots/` | capture, archive, validate, plan, apply |
|
|
232
|
+
| `endpoints/api/` | JSON API, one file per resource |
|
|
233
|
+
| `endpoints/views.py`, `content_views.py` | Inertia admin views |
|
|
234
|
+
| `endpoints/public_views.py`, `seo.py` | public viewer; sitemap and robots |
|
|
235
|
+
| `pages/`, `components/`, `hooks/`, `utils/` | frontend |
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# PageBuilder documentation
|
|
2
|
+
|
|
3
|
+
`simple_module_pagebuilder` is the content module of a SimpleModule site. An
|
|
4
|
+
editor composes **pages** from a palette of blocks in a drag-and-drop editor
|
|
5
|
+
([Puck](https://puckeditor.com)); each page has a draft, a published copy, a
|
|
6
|
+
revision history and an approval workflow, and is served to anonymous visitors
|
|
7
|
+
at `/p/{slug}` inside a shared **site layout** (header and footer). Around that
|
|
8
|
+
sit a **media library** with server-generated thumbnails, per-page SEO
|
|
9
|
+
(canonical, noindex, JSON-LD, `hreflang`, a sitemap), multilingual content, and
|
|
10
|
+
**snapshots** that export the whole site as a `.zip` and restore it behind an
|
|
11
|
+
approval gate.
|
|
12
|
+
|
|
13
|
+
Start with the document that matches what you are trying to do.
|
|
14
|
+
|
|
15
|
+
## Who should read what
|
|
16
|
+
|
|
17
|
+
| You are | Read |
|
|
18
|
+
|---|---|
|
|
19
|
+
| An editor, publisher or approver writing and publishing pages | [user-guide.md](user-guide.md) |
|
|
20
|
+
| A developer calling the pagebuilder API, or presenting pages from another module | [api-reference.md](api-reference.md) |
|
|
21
|
+
| An operator installing it, configuring languages and the public site, or answering "why does this page 404?" | [operations.md](operations.md) |
|
|
22
|
+
| A developer changing the module or adding blocks | [architecture.md](architecture.md) |
|
|
23
|
+
|
|
24
|
+
### [user-guide.md](user-guide.md) — for editors, publishers and approvers
|
|
25
|
+
|
|
26
|
+
The screens under **Site** in the sidebar (Pages, Site layout, Media library,
|
|
27
|
+
Import / Export), using the exact UI labels: a page's lifecycle from **New
|
|
28
|
+
page** to **Delete forever**, the approval workflow, scheduling, translations,
|
|
29
|
+
slugs and the redirects a rename leaves, the SEO tab, the header/footer nav
|
|
30
|
+
flags, the media library, snapshots, a tour of the block palette by category,
|
|
31
|
+
and what the Markdown block understands.
|
|
32
|
+
|
|
33
|
+
### [api-reference.md](api-reference.md) — for integrators
|
|
34
|
+
|
|
35
|
+
Every route under `/api/pagebuilder`: method, permission, request and response
|
|
36
|
+
shapes, and the error table. Also the public routes, the `PageDeleted` event,
|
|
37
|
+
the public-claims seam another module uses to serve a page at its own address,
|
|
38
|
+
and `render_public_page`.
|
|
39
|
+
|
|
40
|
+
### [operations.md](operations.md) — for operators
|
|
41
|
+
|
|
42
|
+
Installing and migrating, every setting (all database-backed, none read from the
|
|
43
|
+
environment, several needing a restart), turning on more languages, tenancy on a
|
|
44
|
+
single- and multi-tenant host, caching, ETags and the Content-Security-Policy on
|
|
45
|
+
public pages, the scheduler, storage on disk, and a troubleshooting table that
|
|
46
|
+
starts from the symptom.
|
|
47
|
+
|
|
48
|
+
### [architecture.md](architecture.md) — for maintainers
|
|
49
|
+
|
|
50
|
+
The tables, how a request binds a tenant, why boot-time settings are read in
|
|
51
|
+
`on_startup`, the status machine as a table, how the public viewer assembles a
|
|
52
|
+
page (claims, redirects, nav, ETag), how snapshots are captured and applied, the
|
|
53
|
+
frontend's block registry and i18n, and the contributor rules: the 300-line cap
|
|
54
|
+
and what may live under `pagebuilder/pages/`.
|
|
55
|
+
|
|
56
|
+
## The module in one paragraph
|
|
57
|
+
|
|
58
|
+
A page is one row with a `draft_data` and a `published_data` Puck document.
|
|
59
|
+
Editing writes the draft; **Publish** (or **Approve** after **Submit for
|
|
60
|
+
review**) copies the draft to the published copy and writes a revision. A page
|
|
61
|
+
is written in exactly one language for its lifetime; a translation is a sibling
|
|
62
|
+
page sharing a `translation_group`, with its own slug, draft and workflow. Slugs
|
|
63
|
+
are unique per tenant and language, and a rename records a redirect from the old
|
|
64
|
+
slug. Deleting moves a page to the trash for 30 days; purging removes it and
|
|
65
|
+
announces `PageDeleted`. Every table is tenant-owned, and every entry point binds
|
|
66
|
+
a tenant before it reads anything. Settings live in the database and are edited
|
|
67
|
+
on the Settings screen; the ones that decide routes need a restart.
|