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.
Files changed (359) hide show
  1. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/PKG-INFO +12 -3
  2. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/README.md +11 -2
  3. simple_module_pagebuilder-0.0.11/docs/api-reference.md +302 -0
  4. simple_module_pagebuilder-0.0.11/docs/architecture.md +235 -0
  5. simple_module_pagebuilder-0.0.11/docs/index.md +67 -0
  6. simple_module_pagebuilder-0.0.11/docs/operations.md +201 -0
  7. simple_module_pagebuilder-0.0.11/docs/user-guide.md +285 -0
  8. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/package.json +1 -1
  9. simple_module_pagebuilder-0.0.11/pagebuilder/components/editor/noHeaderActions.tsx +9 -0
  10. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/layoutPuckConfig.tsx +7 -4
  11. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/puckConfig.test.tsx +13 -1
  12. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/puckConfig.tsx +16 -1
  13. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/snapshots/SnapshotList.tsx +2 -1
  14. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgetCatalog.ts +7 -1
  15. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_internal/eyebrow-split-section.tsx +4 -2
  16. simple_module_pagebuilder-0.0.11/pagebuilder/components/widgets/_internal/inline-image.tsx +18 -0
  17. simple_module_pagebuilder-0.0.11/pagebuilder/components/widgets/_internal/markdown-list.tsx +46 -0
  18. simple_module_pagebuilder-0.0.11/pagebuilder/components/widgets/_internal/rich-text.test.tsx +21 -0
  19. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_internal/rich-text.tsx +40 -64
  20. simple_module_pagebuilder-0.0.11/pagebuilder/components/widgets/_internal/site-nav-context.tsx +52 -0
  21. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/faq-render.tsx +17 -13
  22. simple_module_pagebuilder-0.0.11/pagebuilder/components/widgets/markdown-widget.test.tsx +86 -0
  23. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/markdown-widget.tsx +8 -8
  24. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/quote-widget.tsx +20 -3
  25. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/section-intro-widget.tsx +5 -4
  26. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/site-footer-widget.tsx +10 -2
  27. simple_module_pagebuilder-0.0.11/pagebuilder/components/widgets/site-header-account.tsx +53 -0
  28. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/site-header-nav.tsx +11 -3
  29. simple_module_pagebuilder-0.0.11/pagebuilder/components/widgets/site-header-widget.test.tsx +101 -0
  30. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/site-header-widget.tsx +63 -26
  31. simple_module_pagebuilder-0.0.11/pagebuilder/components/widgets/site-nav-pages.test.tsx +96 -0
  32. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/conversion/blocks-to-html.test.ts +27 -0
  33. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/public_views.py +20 -5
  34. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/views.py +8 -3
  35. simple_module_pagebuilder-0.0.11/pagebuilder/locales/de.json +1392 -0
  36. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/locales/en.json +18 -1
  37. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/module.py +10 -0
  38. simple_module_pagebuilder-0.0.11/pagebuilder/og_image.py +53 -0
  39. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/pages/LayoutEditor.tsx +5 -9
  40. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/pages/MediaLibrary.tsx +2 -1
  41. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/pages/PageEditor.tsx +15 -4
  42. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/pages/PageList.tsx +6 -3
  43. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/pages/PendingReview.tsx +2 -1
  44. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/pages/PublicPage.tsx +25 -19
  45. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/service/__init__.py +3 -0
  46. simple_module_pagebuilder-0.0.11/pagebuilder/site_nav.py +69 -0
  47. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/i18n.test.ts +6 -1
  48. simple_module_pagebuilder-0.0.11/pagebuilder/utils/markdown-links.test.ts +57 -0
  49. simple_module_pagebuilder-0.0.11/pagebuilder/utils/markdown-links.ts +100 -0
  50. simple_module_pagebuilder-0.0.11/pagebuilder/utils/markdown.test.ts +153 -0
  51. simple_module_pagebuilder-0.0.11/pagebuilder/utils/markdown.ts +268 -0
  52. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pyproject.toml +12 -1
  53. simple_module_pagebuilder-0.0.11/tests/test_og_image.py +70 -0
  54. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_pages_api.py +25 -0
  55. simple_module_pagebuilder-0.0.11/tests/test_site_nav.py +144 -0
  56. simple_module_pagebuilder-0.0.9/pagebuilder/utils/markdown.ts +0 -167
  57. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/.gitignore +0 -0
  58. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/__init__.py +0 -0
  59. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/board.py +0 -0
  60. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/boot.py +0 -0
  61. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/ConfirmDialog.tsx +0 -0
  62. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/DiffSummary.tsx +0 -0
  63. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/NewPageDialog.tsx +0 -0
  64. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/NoteDialog.tsx +0 -0
  65. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/PageBoard.tsx +0 -0
  66. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/PageBoardCard.tsx +0 -0
  67. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/PageListFilters.tsx +0 -0
  68. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/PageListRow.tsx +0 -0
  69. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/ScheduledBadge.tsx +0 -0
  70. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/StatusBadge.tsx +0 -0
  71. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/TranslatePageDialog.tsx +0 -0
  72. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/blockRegistry.test.ts +0 -0
  73. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/blockRegistry.ts +0 -0
  74. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/blocks/Button.tsx +0 -0
  75. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/blocks/Columns.tsx +0 -0
  76. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/blocks/Heading.tsx +0 -0
  77. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/blocks/Image.test.tsx +0 -0
  78. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/blocks/Image.tsx +0 -0
  79. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/blocks/MediaPicker.tsx +0 -0
  80. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/blocks/Spacer.tsx +0 -0
  81. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/blocks/Text.tsx +0 -0
  82. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/editor/BlockOutline.tsx +0 -0
  83. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/editor/NarrowCanvas.tsx +0 -0
  84. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/editor/PageActions.tsx +0 -0
  85. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/editor/PageEditorToolbar.tsx +0 -0
  86. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/editor/PageInspectorDrawer.tsx +0 -0
  87. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/editor/PageSettingsPanel.tsx +0 -0
  88. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/editor/RevisionHistoryPanel.tsx +0 -0
  89. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/editor/SchedulePanel.tsx +0 -0
  90. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/editor/SeoPreview.tsx +0 -0
  91. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/editor/SeoSettingsPanel.tsx +0 -0
  92. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/editor/TranslationsPanel.tsx +0 -0
  93. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/lazyBlock.tsx +0 -0
  94. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/localizeConfig.test.ts +0 -0
  95. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/localizeConfig.ts +0 -0
  96. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/media/MediaFilters.tsx +0 -0
  97. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/media/MediaFolderSidebar.tsx +0 -0
  98. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/media/MediaGrid.tsx +0 -0
  99. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/media/MediaHeader.tsx +0 -0
  100. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/media/MediaUploadQueue.tsx +0 -0
  101. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/media/MediaUsageList.tsx +0 -0
  102. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/media/types.ts +0 -0
  103. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/migrateContent.test.ts +0 -0
  104. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/migrateContent.ts +0 -0
  105. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/snapshots/ImportPlanSummary.tsx +0 -0
  106. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/snapshots/UploadBundleDialog.tsx +0 -0
  107. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/snapshots/planSummary.test.ts +0 -0
  108. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/snapshots/planSummary.ts +0 -0
  109. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgetCatalog.test.tsx +0 -0
  110. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_internal/accent-text.tsx +0 -0
  111. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_internal/disclosure-chevron.tsx +0 -0
  112. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_internal/link-arrow.tsx +0 -0
  113. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_internal/solid-icons.tsx +0 -0
  114. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_shared/background-media.tsx +0 -0
  115. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_shared/content-media.tsx +0 -0
  116. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_shared/cta-button.tsx +0 -0
  117. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_shared/empty-placeholder.tsx +0 -0
  118. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_shared/eyebrow-text.tsx +0 -0
  119. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_shared/heading.tsx +0 -0
  120. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_shared/image-srcset.ts +0 -0
  121. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_shared/index.ts +0 -0
  122. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_shared/mask.ts +0 -0
  123. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_shared/section.tsx +0 -0
  124. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_shared/social-icon.tsx +0 -0
  125. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/_shared/text-link.tsx +0 -0
  126. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/accordion-widget.tsx +0 -0
  127. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/alert-widget.tsx +0 -0
  128. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/app-store-badges-widget.tsx +0 -0
  129. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/article-cards-render.tsx +0 -0
  130. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/article-cards-widget.tsx +0 -0
  131. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/call-to-action-render.tsx +0 -0
  132. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/call-to-action-styles.ts +0 -0
  133. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/call-to-action-widget.tsx +0 -0
  134. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/carousel-widget.tsx +0 -0
  135. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/centered-hero-widget.tsx +0 -0
  136. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/colored-action-list-widget.tsx +0 -0
  137. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/column-widget.tsx +0 -0
  138. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/contact-cards-widget.tsx +0 -0
  139. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/contact-form-render.tsx +0 -0
  140. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/contact-form-widget.tsx +0 -0
  141. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/container-widget.tsx +0 -0
  142. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/definition-list-widget.tsx +0 -0
  143. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/divider-widget.test.tsx +0 -0
  144. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/divider-widget.tsx +0 -0
  145. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/eyebrow-section-widget.tsx +0 -0
  146. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/faq-widget.tsx +0 -0
  147. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/feature-card-widget.tsx +0 -0
  148. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/feature-cards-render.tsx +0 -0
  149. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/feature-cards-styles.ts +0 -0
  150. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/feature-cards-widget.tsx +0 -0
  151. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/feature-grid-widget.tsx +0 -0
  152. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/feature-showcase-widget.tsx +0 -0
  153. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/gallery-widget.tsx +0 -0
  154. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/grid-widget.tsx +0 -0
  155. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/hero-widget-props.ts +0 -0
  156. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/hero-widget.tsx +0 -0
  157. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/html-widget.tsx +0 -0
  158. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/iframe-widget.tsx +0 -0
  159. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/leaderboard-widget.tsx +0 -0
  160. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/list-widget.tsx +0 -0
  161. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/logo-cloud-widget.tsx +0 -0
  162. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/media-object-layout.ts +0 -0
  163. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/media-object-render.tsx +0 -0
  164. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/media-object-text.tsx +0 -0
  165. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/media-object-widget.tsx +0 -0
  166. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/newsletter-widget.tsx +0 -0
  167. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/page-header-widget.tsx +0 -0
  168. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/prose-section-widget.tsx +0 -0
  169. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/row-widget.tsx +0 -0
  170. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/signup-banner-widget.tsx +0 -0
  171. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/simple-action-list-widget.tsx +0 -0
  172. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/social-banner-widget.tsx +0 -0
  173. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/stats-widget.tsx +0 -0
  174. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/steps-widget.tsx +0 -0
  175. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/story-cards-widget.tsx +0 -0
  176. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/table-widget.tsx +0 -0
  177. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/tabs-widget.tsx +0 -0
  178. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/tags-widget.tsx +0 -0
  179. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/testimonial-widget.tsx +0 -0
  180. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/timeline-layout.ts +0 -0
  181. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/timeline-render.tsx +0 -0
  182. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/timeline-widget.tsx +0 -0
  183. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/under-construction-widget.tsx +0 -0
  184. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/video-widget.tsx +0 -0
  185. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/components/widgets/welcome-widget.tsx +0 -0
  186. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/contracts/__init__.py +0 -0
  187. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/contracts/events.py +0 -0
  188. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/contracts/schemas/__init__.py +0 -0
  189. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/contracts/schemas/_layout.py +0 -0
  190. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/contracts/schemas/_media.py +0 -0
  191. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/contracts/schemas/_pages.py +0 -0
  192. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/contracts/schemas/_snapshots.py +0 -0
  193. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/conversion/blocks-to-html.ts +0 -0
  194. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/conversion/index.ts +0 -0
  195. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/conversion/types.ts +0 -0
  196. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/deps.py +0 -0
  197. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/diff.py +0 -0
  198. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/__init__.py +0 -0
  199. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/api/__init__.py +0 -0
  200. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/api/_deps.py +0 -0
  201. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/api/layout.py +0 -0
  202. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/api/media_detail.py +0 -0
  203. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/api/pages.py +0 -0
  204. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/api/revisions.py +0 -0
  205. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/api/snapshots.py +0 -0
  206. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/api/uploads.py +0 -0
  207. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/api/workflow.py +0 -0
  208. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/content_views.py +0 -0
  209. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/endpoints/seo.py +0 -0
  210. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/fields/checkbox-field.tsx +0 -0
  211. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/fields/file-picker/file-gallery.tsx +0 -0
  212. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/fields/file-picker/image-picker-field.tsx +0 -0
  213. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/fields/file-picker/index.ts +0 -0
  214. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/fields/file-picker/types.ts +0 -0
  215. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/fields/index.ts +0 -0
  216. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/fields/media-adapter.ts +0 -0
  217. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/hooks/useAutosave.ts +0 -0
  218. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/hooks/useEditorForm.ts +0 -0
  219. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/hooks/useIsNarrow.ts +0 -0
  220. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/hooks/useMediaDetail.ts +0 -0
  221. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/hooks/useMediaUploads.ts +0 -0
  222. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/hooks/usePageRevisions.ts +0 -0
  223. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/hooks/usePageSchedule.ts +0 -0
  224. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/hooks/usePageWorkflow.ts +0 -0
  225. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/hooks/usePendingDialog.ts +0 -0
  226. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/hooks/useSnapshots.ts +0 -0
  227. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/layout_service.py +0 -0
  228. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/locales.py +0 -0
  229. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/media_files.py +0 -0
  230. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/media_images.py +0 -0
  231. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/media_queries.py +0 -0
  232. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/media_service.py +0 -0
  233. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/media_usage.py +0 -0
  234. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/models/__init__.py +0 -0
  235. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/models/_base.py +0 -0
  236. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/models/_layout.py +0 -0
  237. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/models/_media.py +0 -0
  238. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/models/_page.py +0 -0
  239. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/models/_redirect.py +0 -0
  240. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/models/_snapshot.py +0 -0
  241. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/pages/ContentImportReview.tsx +0 -0
  242. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/pages/ContentSnapshots.tsx +0 -0
  243. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/pages/MediaDetail.tsx +0 -0
  244. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/pages/Trash.tsx +0 -0
  245. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/permissions.py +0 -0
  246. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/public_claims.py +0 -0
  247. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/py.typed +0 -0
  248. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/redirects.py +0 -0
  249. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/scheduler.py +0 -0
  250. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/security.py +0 -0
  251. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/service/_common.py +0 -0
  252. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/service/_revisions.py +0 -0
  253. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/service/_translations.py +0 -0
  254. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/service/_trash.py +0 -0
  255. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/service/_workflow.py +0 -0
  256. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/services.py +0 -0
  257. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/settings.py +0 -0
  258. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/snapshots/__init__.py +0 -0
  259. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/snapshots/apply.py +0 -0
  260. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/snapshots/archive.py +0 -0
  261. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/snapshots/assets.py +0 -0
  262. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/snapshots/blobs.py +0 -0
  263. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/snapshots/capture.py +0 -0
  264. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/snapshots/format.py +0 -0
  265. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/snapshots/media_match.py +0 -0
  266. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/snapshots/pages.py +0 -0
  267. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/snapshots/plan.py +0 -0
  268. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/snapshots/service.py +0 -0
  269. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/snapshots/validate.py +0 -0
  270. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/static/widgets-base.css +0 -0
  271. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/tenancy.py +0 -0
  272. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/translation/apply.ts +0 -0
  273. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/translation/extract.ts +0 -0
  274. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/translation/index.ts +0 -0
  275. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/translation/translatable-fields.ts +0 -0
  276. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/translation/translation.test.ts +0 -0
  277. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/api.ts +0 -0
  278. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/blockOutline.test.ts +0 -0
  279. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/blockOutline.ts +0 -0
  280. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/datetime.ts +0 -0
  281. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/decorative-image.ts +0 -0
  282. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/editorSnapshot.ts +0 -0
  283. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/i18n.ts +0 -0
  284. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/locale.ts +0 -0
  285. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/measure.ts +0 -0
  286. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/mediaApi.ts +0 -0
  287. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/mediaFormat.ts +0 -0
  288. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/pagesApi.ts +0 -0
  289. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/parse-list.ts +0 -0
  290. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/request.test.ts +0 -0
  291. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/request.ts +0 -0
  292. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/slugify.test.ts +0 -0
  293. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/slugify.ts +0 -0
  294. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/snapshotsApi.ts +0 -0
  295. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/types.ts +0 -0
  296. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/pagebuilder/utils/widgetUtils.ts +0 -0
  297. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/conftest.py +0 -0
  298. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/db_fixture.py +0 -0
  299. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/locale_fixture.py +0 -0
  300. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/page_helpers.py +0 -0
  301. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/pg_support.py +0 -0
  302. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/snapshot_fixture.py +0 -0
  303. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/tenant_app.py +0 -0
  304. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/tenant_fixture.py +0 -0
  305. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_admin_list_view.py +0 -0
  306. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_approval_workflow.py +0 -0
  307. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_auth_gating.py +0 -0
  308. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_csrf.py +0 -0
  309. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_draft_preview.py +0 -0
  310. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_hreflang_and_sitemap.py +0 -0
  311. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_i18n.py +0 -0
  312. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_image_processing.py +0 -0
  313. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_media_detail.py +0 -0
  314. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_media_files.py +0 -0
  315. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_media_security.py +0 -0
  316. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_media_usage_escaping.py +0 -0
  317. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_module.py +0 -0
  318. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_multilingual.py +0 -0
  319. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_page_templates.py +0 -0
  320. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_page_translations.py +0 -0
  321. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_pages_search.py +0 -0
  322. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_public_claims.py +0 -0
  323. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_public_routes.py +0 -0
  324. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_public_view_headers.py +0 -0
  325. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_public_viewer.py +0 -0
  326. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_redirects.py +0 -0
  327. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_revision_notes_and_diff.py +0 -0
  328. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_revisions_api.py +0 -0
  329. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_scheduled_publish.py +0 -0
  330. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_scheduler_stop.py +0 -0
  331. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_scheduler_tenancy.py +0 -0
  332. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_seo_meta.py +0 -0
  333. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_settings_source.py +0 -0
  334. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_site_layout.py +0 -0
  335. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_sitemap_robots.py +0 -0
  336. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshot_apply.py +0 -0
  337. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshot_apply_media.py +0 -0
  338. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshot_archive.py +0 -0
  339. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshot_assets.py +0 -0
  340. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshot_blobs.py +0 -0
  341. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshot_capture.py +0 -0
  342. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshot_media_match.py +0 -0
  343. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshot_models.py +0 -0
  344. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshot_multilingual.py +0 -0
  345. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshot_page_payload.py +0 -0
  346. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshot_plan.py +0 -0
  347. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshot_validate.py +0 -0
  348. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshot_views.py +0 -0
  349. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_snapshots_api.py +0 -0
  350. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_tenancy_blobs.py +0 -0
  351. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_tenancy_core.py +0 -0
  352. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_tenancy_isolation.py +0 -0
  353. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_tenancy_media.py +0 -0
  354. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_tenancy_public.py +0 -0
  355. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_tenancy_routes.py +0 -0
  356. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_trash.py +0 -0
  357. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_trash_preconditions.py +0 -0
  358. {simple_module_pagebuilder-0.0.9 → simple_module_pagebuilder-0.0.11}/tests/test_uploads_api.py +0 -0
  359. {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.9
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
- module can later be removed on its own with
135
- `alembic downgrade pagebuilder@base`.
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
- module can later be removed on its own with
111
- `alembic downgrade pagebuilder@base`.
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.