focomy 0.1.115__tar.gz → 0.1.116__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 (210) hide show
  1. {focomy-0.1.115 → focomy-0.1.116}/PKG-INFO +1 -1
  2. {focomy-0.1.115 → focomy-0.1.116}/core/admin/routes.py +32 -0
  3. {focomy-0.1.115 → focomy-0.1.116}/core/engine/routes.py +53 -0
  4. {focomy-0.1.115 → focomy-0.1.116}/core/templates/admin/entity_form.html +29 -0
  5. {focomy-0.1.115 → focomy-0.1.116}/pyproject.toml +1 -1
  6. {focomy-0.1.115 → focomy-0.1.116}/.gitignore +0 -0
  7. {focomy-0.1.115 → focomy-0.1.116}/LICENSE +0 -0
  8. {focomy-0.1.115 → focomy-0.1.116}/README.md +0 -0
  9. {focomy-0.1.115 → focomy-0.1.116}/core/__init__.py +0 -0
  10. {focomy-0.1.115 → focomy-0.1.116}/core/admin/__init__.py +0 -0
  11. {focomy-0.1.115 → focomy-0.1.116}/core/admin/url.py +0 -0
  12. {focomy-0.1.115 → focomy-0.1.116}/core/api/__init__.py +0 -0
  13. {focomy-0.1.115 → focomy-0.1.116}/core/api/auth.py +0 -0
  14. {focomy-0.1.115 → focomy-0.1.116}/core/api/comments.py +0 -0
  15. {focomy-0.1.115 → focomy-0.1.116}/core/api/entities.py +0 -0
  16. {focomy-0.1.115 → focomy-0.1.116}/core/api/forms.py +0 -0
  17. {focomy-0.1.115 → focomy-0.1.116}/core/api/media.py +0 -0
  18. {focomy-0.1.115 → focomy-0.1.116}/core/api/relations.py +0 -0
  19. {focomy-0.1.115 → focomy-0.1.116}/core/api/revisions.py +0 -0
  20. {focomy-0.1.115 → focomy-0.1.116}/core/api/schema.py +0 -0
  21. {focomy-0.1.115 → focomy-0.1.116}/core/api/search.py +0 -0
  22. {focomy-0.1.115 → focomy-0.1.116}/core/api/seo.py +0 -0
  23. {focomy-0.1.115 → focomy-0.1.116}/core/cli.py +0 -0
  24. {focomy-0.1.115 → focomy-0.1.116}/core/config.py +0 -0
  25. {focomy-0.1.115 → focomy-0.1.116}/core/content_types/__init__.py +0 -0
  26. {focomy-0.1.115 → focomy-0.1.116}/core/content_types/audit_log.yaml +0 -0
  27. {focomy-0.1.115 → focomy-0.1.116}/core/content_types/category.yaml +0 -0
  28. {focomy-0.1.115 → focomy-0.1.116}/core/content_types/channel.yaml +0 -0
  29. {focomy-0.1.115 → focomy-0.1.116}/core/content_types/comment.yaml +0 -0
  30. {focomy-0.1.115 → focomy-0.1.116}/core/content_types/edit_lock.yaml +0 -0
  31. {focomy-0.1.115 → focomy-0.1.116}/core/content_types/form.yaml +0 -0
  32. {focomy-0.1.115 → focomy-0.1.116}/core/content_types/form_submission.yaml +0 -0
  33. {focomy-0.1.115 → focomy-0.1.116}/core/content_types/menu_item.yaml +0 -0
  34. {focomy-0.1.115 → focomy-0.1.116}/core/content_types/news.yaml +0 -0
  35. {focomy-0.1.115 → focomy-0.1.116}/core/content_types/page.yaml +0 -0
  36. {focomy-0.1.115 → focomy-0.1.116}/core/content_types/plugin.yaml +0 -0
  37. {focomy-0.1.115 → focomy-0.1.116}/core/content_types/plugin_developer.yaml +0 -0
  38. {focomy-0.1.115 → focomy-0.1.116}/core/content_types/plugin_review.yaml +0 -0
  39. {focomy-0.1.115 → focomy-0.1.116}/core/content_types/post.yaml +0 -0
  40. {focomy-0.1.115 → focomy-0.1.116}/core/content_types/redirect.yaml +0 -0
  41. {focomy-0.1.115 → focomy-0.1.116}/core/content_types/scheduled_action.yaml +0 -0
  42. {focomy-0.1.115 → focomy-0.1.116}/core/content_types/series.yaml +0 -0
  43. {focomy-0.1.115 → focomy-0.1.116}/core/content_types/site_setting.yaml +0 -0
  44. {focomy-0.1.115 → focomy-0.1.116}/core/content_types/tag.yaml +0 -0
  45. {focomy-0.1.115 → focomy-0.1.116}/core/content_types/user.yaml +0 -0
  46. {focomy-0.1.115 → focomy-0.1.116}/core/content_types/widget.yaml +0 -0
  47. {focomy-0.1.115 → focomy-0.1.116}/core/content_types/workflow_history.yaml +0 -0
  48. {focomy-0.1.115 → focomy-0.1.116}/core/database.py +0 -0
  49. {focomy-0.1.115 → focomy-0.1.116}/core/engine/__init__.py +0 -0
  50. {focomy-0.1.115 → focomy-0.1.116}/core/main.py +0 -0
  51. {focomy-0.1.115 → focomy-0.1.116}/core/migrations/env.py +0 -0
  52. {focomy-0.1.115 → focomy-0.1.116}/core/migrations/script.py.mako +0 -0
  53. {focomy-0.1.115 → focomy-0.1.116}/core/migrations/versions/2038bdf6693b_add_import_jobs_table.py +0 -0
  54. {focomy-0.1.115 → focomy-0.1.116}/core/migrations/versions/3a1b2c3d4e5f_add_file_hash_to_media.py +0 -0
  55. {focomy-0.1.115 → focomy-0.1.116}/core/models/__init__.py +0 -0
  56. {focomy-0.1.115 → focomy-0.1.116}/core/models/auth.py +0 -0
  57. {focomy-0.1.115 → focomy-0.1.116}/core/models/entity.py +0 -0
  58. {focomy-0.1.115 → focomy-0.1.116}/core/models/import_job.py +0 -0
  59. {focomy-0.1.115 → focomy-0.1.116}/core/models/media.py +0 -0
  60. {focomy-0.1.115 → focomy-0.1.116}/core/models/relation.py +0 -0
  61. {focomy-0.1.115 → focomy-0.1.116}/core/models/revision.py +0 -0
  62. {focomy-0.1.115 → focomy-0.1.116}/core/plugins/__init__.py +0 -0
  63. {focomy-0.1.115 → focomy-0.1.116}/core/plugins/base.py +0 -0
  64. {focomy-0.1.115 → focomy-0.1.116}/core/plugins/hooks.py +0 -0
  65. {focomy-0.1.115 → focomy-0.1.116}/core/plugins/loader.py +0 -0
  66. {focomy-0.1.115 → focomy-0.1.116}/core/plugins/manager.py +0 -0
  67. {focomy-0.1.115 → focomy-0.1.116}/core/rate_limit.py +0 -0
  68. {focomy-0.1.115 → focomy-0.1.116}/core/relations.yaml +0 -0
  69. {focomy-0.1.115 → focomy-0.1.116}/core/scaffold/.env.template +0 -0
  70. {focomy-0.1.115 → focomy-0.1.116}/core/scaffold/.gitignore.template +0 -0
  71. {focomy-0.1.115 → focomy-0.1.116}/core/scaffold/__init__.py +0 -0
  72. {focomy-0.1.115 → focomy-0.1.116}/core/scaffold/config.yaml.template +0 -0
  73. {focomy-0.1.115 → focomy-0.1.116}/core/scaffold/themes/default/templates/archive.html +0 -0
  74. {focomy-0.1.115 → focomy-0.1.116}/core/scaffold/themes/default/templates/base.html +0 -0
  75. {focomy-0.1.115 → focomy-0.1.116}/core/scaffold/themes/default/templates/category.html +0 -0
  76. {focomy-0.1.115 → focomy-0.1.116}/core/scaffold/themes/default/templates/home.html +0 -0
  77. {focomy-0.1.115 → focomy-0.1.116}/core/scaffold/themes/default/templates/post.html +0 -0
  78. {focomy-0.1.115 → focomy-0.1.116}/core/scaffold/themes/default/templates/search.html +0 -0
  79. {focomy-0.1.115 → focomy-0.1.116}/core/scaffold/themes/default/theme.yaml +0 -0
  80. {focomy-0.1.115 → focomy-0.1.116}/core/schemas/__init__.py +0 -0
  81. {focomy-0.1.115 → focomy-0.1.116}/core/schemas/import_schema.py +0 -0
  82. {focomy-0.1.115 → focomy-0.1.116}/core/seo/__init__.py +0 -0
  83. {focomy-0.1.115 → focomy-0.1.116}/core/services/__init__.py +0 -0
  84. {focomy-0.1.115 → focomy-0.1.116}/core/services/api_auth.py +0 -0
  85. {focomy-0.1.115 → focomy-0.1.116}/core/services/assets.py +0 -0
  86. {focomy-0.1.115 → focomy-0.1.116}/core/services/audit.py +0 -0
  87. {focomy-0.1.115 → focomy-0.1.116}/core/services/auth.py +0 -0
  88. {focomy-0.1.115 → focomy-0.1.116}/core/services/block_converter.py +0 -0
  89. {focomy-0.1.115 → focomy-0.1.116}/core/services/bulk.py +0 -0
  90. {focomy-0.1.115 → focomy-0.1.116}/core/services/cache.py +0 -0
  91. {focomy-0.1.115 → focomy-0.1.116}/core/services/channel.py +0 -0
  92. {focomy-0.1.115 → focomy-0.1.116}/core/services/cleanup.py +0 -0
  93. {focomy-0.1.115 → focomy-0.1.116}/core/services/comment.py +0 -0
  94. {focomy-0.1.115 → focomy-0.1.116}/core/services/config_priority.py +0 -0
  95. {focomy-0.1.115 → focomy-0.1.116}/core/services/deployment.py +0 -0
  96. {focomy-0.1.115 → focomy-0.1.116}/core/services/edit_lock.py +0 -0
  97. {focomy-0.1.115 → focomy-0.1.116}/core/services/entity.py +0 -0
  98. {focomy-0.1.115 → focomy-0.1.116}/core/services/export.py +0 -0
  99. {focomy-0.1.115 → focomy-0.1.116}/core/services/field.py +0 -0
  100. {focomy-0.1.115 → focomy-0.1.116}/core/services/formula.py +0 -0
  101. {focomy-0.1.115 → focomy-0.1.116}/core/services/i18n.py +0 -0
  102. {focomy-0.1.115 → focomy-0.1.116}/core/services/index.py +0 -0
  103. {focomy-0.1.115 → focomy-0.1.116}/core/services/invite.py +0 -0
  104. {focomy-0.1.115 → focomy-0.1.116}/core/services/link_validator.py +0 -0
  105. {focomy-0.1.115 → focomy-0.1.116}/core/services/logging.py +0 -0
  106. {focomy-0.1.115 → focomy-0.1.116}/core/services/mail.py +0 -0
  107. {focomy-0.1.115 → focomy-0.1.116}/core/services/marketplace.py +0 -0
  108. {focomy-0.1.115 → focomy-0.1.116}/core/services/marketplace_verify.py +0 -0
  109. {focomy-0.1.115 → focomy-0.1.116}/core/services/media.py +0 -0
  110. {focomy-0.1.115 → focomy-0.1.116}/core/services/media_cleanup.py +0 -0
  111. {focomy-0.1.115 → focomy-0.1.116}/core/services/menu.py +0 -0
  112. {focomy-0.1.115 → focomy-0.1.116}/core/services/migration_helpers.py +0 -0
  113. {focomy-0.1.115 → focomy-0.1.116}/core/services/oauth.py +0 -0
  114. {focomy-0.1.115 → focomy-0.1.116}/core/services/pagination.py +0 -0
  115. {focomy-0.1.115 → focomy-0.1.116}/core/services/plugin_resolver.py +0 -0
  116. {focomy-0.1.115 → focomy-0.1.116}/core/services/plugin_sandbox.py +0 -0
  117. {focomy-0.1.115 → focomy-0.1.116}/core/services/preview.py +0 -0
  118. {focomy-0.1.115 → focomy-0.1.116}/core/services/query_optimizer.py +0 -0
  119. {focomy-0.1.115 → focomy-0.1.116}/core/services/rbac.py +0 -0
  120. {focomy-0.1.115 → focomy-0.1.116}/core/services/redirect.py +0 -0
  121. {focomy-0.1.115 → focomy-0.1.116}/core/services/relation.py +0 -0
  122. {focomy-0.1.115 → focomy-0.1.116}/core/services/revision.py +0 -0
  123. {focomy-0.1.115 → focomy-0.1.116}/core/services/routing.py +0 -0
  124. {focomy-0.1.115 → focomy-0.1.116}/core/services/sanitizer.py +0 -0
  125. {focomy-0.1.115 → focomy-0.1.116}/core/services/schedule.py +0 -0
  126. {focomy-0.1.115 → focomy-0.1.116}/core/services/search.py +0 -0
  127. {focomy-0.1.115 → focomy-0.1.116}/core/services/sentry.py +0 -0
  128. {focomy-0.1.115 → focomy-0.1.116}/core/services/seo.py +0 -0
  129. {focomy-0.1.115 → focomy-0.1.116}/core/services/settings.py +0 -0
  130. {focomy-0.1.115 → focomy-0.1.116}/core/services/spam_filter.py +0 -0
  131. {focomy-0.1.115 → focomy-0.1.116}/core/services/storage.py +0 -0
  132. {focomy-0.1.115 → focomy-0.1.116}/core/services/theme.py +0 -0
  133. {focomy-0.1.115 → focomy-0.1.116}/core/services/theme_inheritance.py +0 -0
  134. {focomy-0.1.115 → focomy-0.1.116}/core/services/thumbnail.py +0 -0
  135. {focomy-0.1.115 → focomy-0.1.116}/core/services/update.py +0 -0
  136. {focomy-0.1.115 → focomy-0.1.116}/core/services/widget.py +0 -0
  137. {focomy-0.1.115 → focomy-0.1.116}/core/services/wordpress_import/__init__.py +0 -0
  138. {focomy-0.1.115 → focomy-0.1.116}/core/services/wordpress_import/acf.py +0 -0
  139. {focomy-0.1.115 → focomy-0.1.116}/core/services/wordpress_import/analyzer.py +0 -0
  140. {focomy-0.1.115 → focomy-0.1.116}/core/services/wordpress_import/constants.py +0 -0
  141. {focomy-0.1.115 → focomy-0.1.116}/core/services/wordpress_import/content_sanitizer.py +0 -0
  142. {focomy-0.1.115 → focomy-0.1.116}/core/services/wordpress_import/diff_detector.py +0 -0
  143. {focomy-0.1.115 → focomy-0.1.116}/core/services/wordpress_import/dry_run.py +0 -0
  144. {focomy-0.1.115 → focomy-0.1.116}/core/services/wordpress_import/error_collector.py +0 -0
  145. {focomy-0.1.115 → focomy-0.1.116}/core/services/wordpress_import/id_resolver.py +0 -0
  146. {focomy-0.1.115 → focomy-0.1.116}/core/services/wordpress_import/import_service.py +0 -0
  147. {focomy-0.1.115 → focomy-0.1.116}/core/services/wordpress_import/importer.py +0 -0
  148. {focomy-0.1.115 → focomy-0.1.116}/core/services/wordpress_import/link_fixer.py +0 -0
  149. {focomy-0.1.115 → focomy-0.1.116}/core/services/wordpress_import/media.py +0 -0
  150. {focomy-0.1.115 → focomy-0.1.116}/core/services/wordpress_import/preview.py +0 -0
  151. {focomy-0.1.115 → focomy-0.1.116}/core/services/wordpress_import/redirects.py +0 -0
  152. {focomy-0.1.115 → focomy-0.1.116}/core/services/wordpress_import/rest_client.py +0 -0
  153. {focomy-0.1.115 → focomy-0.1.116}/core/services/wordpress_import/rollback.py +0 -0
  154. {focomy-0.1.115 → focomy-0.1.116}/core/services/wordpress_import/verification.py +0 -0
  155. {focomy-0.1.115 → focomy-0.1.116}/core/services/wordpress_import/wxr_parser.py +0 -0
  156. {focomy-0.1.115 → focomy-0.1.116}/core/services/workflow.py +0 -0
  157. {focomy-0.1.115 → focomy-0.1.116}/core/templates/admin/backup.html +0 -0
  158. {focomy-0.1.115 → focomy-0.1.116}/core/templates/admin/base.html +0 -0
  159. {focomy-0.1.115 → focomy-0.1.116}/core/templates/admin/comments.html +0 -0
  160. {focomy-0.1.115 → focomy-0.1.116}/core/templates/admin/components/editor.html +0 -0
  161. {focomy-0.1.115 → focomy-0.1.116}/core/templates/admin/customize.html +0 -0
  162. {focomy-0.1.115 → focomy-0.1.116}/core/templates/admin/dashboard.html +0 -0
  163. {focomy-0.1.115 → focomy-0.1.116}/core/templates/admin/entity_list.html +0 -0
  164. {focomy-0.1.115 → focomy-0.1.116}/core/templates/admin/forgot_password.html +0 -0
  165. {focomy-0.1.115 → focomy-0.1.116}/core/templates/admin/import.html +0 -0
  166. {focomy-0.1.115 → focomy-0.1.116}/core/templates/admin/link_validator.html +0 -0
  167. {focomy-0.1.115 → focomy-0.1.116}/core/templates/admin/login.html +0 -0
  168. {focomy-0.1.115 → focomy-0.1.116}/core/templates/admin/media.html +0 -0
  169. {focomy-0.1.115 → focomy-0.1.116}/core/templates/admin/menus.html +0 -0
  170. {focomy-0.1.115 → focomy-0.1.116}/core/templates/admin/plugins.html +0 -0
  171. {focomy-0.1.115 → focomy-0.1.116}/core/templates/admin/redirects.html +0 -0
  172. {focomy-0.1.115 → focomy-0.1.116}/core/templates/admin/reset_password.html +0 -0
  173. {focomy-0.1.115 → focomy-0.1.116}/core/templates/admin/settings.html +0 -0
  174. {focomy-0.1.115 → focomy-0.1.116}/core/templates/admin/sitemap.html +0 -0
  175. {focomy-0.1.115 → focomy-0.1.116}/core/templates/admin/system.html +0 -0
  176. {focomy-0.1.115 → focomy-0.1.116}/core/templates/admin/themes.html +0 -0
  177. {focomy-0.1.115 → focomy-0.1.116}/core/templates/admin/widgets.html +0 -0
  178. {focomy-0.1.115 → focomy-0.1.116}/core/themes/__init__.py +0 -0
  179. {focomy-0.1.115 → focomy-0.1.116}/core/themes/customizer.py +0 -0
  180. {focomy-0.1.115 → focomy-0.1.116}/core/themes/manager.py +0 -0
  181. {focomy-0.1.115 → focomy-0.1.116}/core/themes/marketplace.py +0 -0
  182. {focomy-0.1.115 → focomy-0.1.116}/core/utils.py +0 -0
  183. {focomy-0.1.115 → focomy-0.1.116}/static/favicon.svg +0 -0
  184. {focomy-0.1.115 → focomy-0.1.116}/themes/blog/templates/base.html +0 -0
  185. {focomy-0.1.115 → focomy-0.1.116}/themes/blog/templates/home.html +0 -0
  186. {focomy-0.1.115 → focomy-0.1.116}/themes/blog/templates/post.html +0 -0
  187. {focomy-0.1.115 → focomy-0.1.116}/themes/blog/theme.yaml +0 -0
  188. {focomy-0.1.115 → focomy-0.1.116}/themes/corporate/templates/base.html +0 -0
  189. {focomy-0.1.115 → focomy-0.1.116}/themes/corporate/templates/home.html +0 -0
  190. {focomy-0.1.115 → focomy-0.1.116}/themes/corporate/templates/page.html +0 -0
  191. {focomy-0.1.115 → focomy-0.1.116}/themes/corporate/theme.yaml +0 -0
  192. {focomy-0.1.115 → focomy-0.1.116}/themes/default/templates/404.html +0 -0
  193. {focomy-0.1.115 → focomy-0.1.116}/themes/default/templates/500.html +0 -0
  194. {focomy-0.1.115 → focomy-0.1.116}/themes/default/templates/archive.html +0 -0
  195. {focomy-0.1.115 → focomy-0.1.116}/themes/default/templates/base.html +0 -0
  196. {focomy-0.1.115 → focomy-0.1.116}/themes/default/templates/category.html +0 -0
  197. {focomy-0.1.115 → focomy-0.1.116}/themes/default/templates/channel.html +0 -0
  198. {focomy-0.1.115 → focomy-0.1.116}/themes/default/templates/form.html +0 -0
  199. {focomy-0.1.115 → focomy-0.1.116}/themes/default/templates/form_success.html +0 -0
  200. {focomy-0.1.115 → focomy-0.1.116}/themes/default/templates/home.html +0 -0
  201. {focomy-0.1.115 → focomy-0.1.116}/themes/default/templates/page.html +0 -0
  202. {focomy-0.1.115 → focomy-0.1.116}/themes/default/templates/post.html +0 -0
  203. {focomy-0.1.115 → focomy-0.1.116}/themes/default/templates/search.html +0 -0
  204. {focomy-0.1.115 → focomy-0.1.116}/themes/default/templates/series.html +0 -0
  205. {focomy-0.1.115 → focomy-0.1.116}/themes/default/theme.yaml +0 -0
  206. {focomy-0.1.115 → focomy-0.1.116}/themes/minimal/templates/base.html +0 -0
  207. {focomy-0.1.115 → focomy-0.1.116}/themes/minimal/templates/home.html +0 -0
  208. {focomy-0.1.115 → focomy-0.1.116}/themes/minimal/templates/page.html +0 -0
  209. {focomy-0.1.115 → focomy-0.1.116}/themes/minimal/templates/post.html +0 -0
  210. {focomy-0.1.115 → focomy-0.1.116}/themes/minimal/theme.yaml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: focomy
3
- Version: 0.1.115
3
+ Version: 0.1.116
4
4
  Summary: The Most Beautiful CMS - A metadata-driven, zero-duplicate-code content management system
5
5
  Project-URL: Homepage, https://github.com/focomy/focomy
6
6
  Project-URL: Documentation, https://focomy.dev/docs
@@ -2020,6 +2020,38 @@ async def preview_render(
2020
2020
  return {"html": html}
2021
2021
 
2022
2022
 
2023
+ @router.post("/api/preview/token")
2024
+ async def create_preview_token(
2025
+ request: Request,
2026
+ db: AsyncSession = Depends(get_db),
2027
+ current_user: Entity = Depends(require_admin_api),
2028
+ ):
2029
+ """Create a preview token for an entity."""
2030
+ from ..services.preview import get_preview_service
2031
+
2032
+ try:
2033
+ body = await request.json()
2034
+ except Exception:
2035
+ raise HTTPException(status_code=400, detail="Invalid JSON")
2036
+
2037
+ entity_id = body.get("entity_id")
2038
+ if not entity_id:
2039
+ raise HTTPException(status_code=400, detail="entity_id required")
2040
+
2041
+ # Verify entity exists
2042
+ entity_svc = EntityService(db)
2043
+ entity = await entity_svc.get(entity_id)
2044
+ if not entity:
2045
+ raise HTTPException(status_code=404, detail="Entity not found")
2046
+
2047
+ # Create preview token
2048
+ preview_svc = get_preview_service(db)
2049
+ token = await preview_svc.create_token(entity_id, current_user.id)
2050
+ preview_url = preview_svc.get_preview_url(token)
2051
+
2052
+ return {"token": token, "url": preview_url}
2053
+
2054
+
2023
2055
  @router.get("/system", response_class=HTMLResponse)
2024
2056
  async def system_info(
2025
2057
  request: Request,
@@ -261,6 +261,59 @@ async def sitemap_xml(
261
261
  return Response(content=xml_content, media_type="application/xml")
262
262
 
263
263
 
264
+ # === Preview Routes ===
265
+
266
+
267
+ @router.get("/preview/{token}", response_class=HTMLResponse)
268
+ async def preview_entity(
269
+ token: str,
270
+ request: Request,
271
+ db: AsyncSession = Depends(get_db),
272
+ ):
273
+ """Preview an entity with a valid token (shows draft/unpublished content)."""
274
+ from ..services.preview import get_preview_service
275
+
276
+ preview_svc = get_preview_service(db)
277
+ entity = await preview_svc.get_preview_entity(token)
278
+
279
+ if not entity:
280
+ raise HTTPException(status_code=404, detail="Preview not found or expired")
281
+
282
+ entity_svc = EntityService(db)
283
+ entity_data = entity_svc.serialize(entity)
284
+
285
+ # Get content type info
286
+ content_type = entity.type
287
+ ct = field_service.get_content_type(content_type)
288
+
289
+ # Get site URL and contexts
290
+ site_url = str(request.base_url).rstrip("/")
291
+ menus_ctx = await get_menus_context(db)
292
+ widgets_ctx = await get_widgets_context(db)
293
+ seo_ctx = await get_seo_settings(db, site_url)
294
+
295
+ # Add preview flag to context
296
+ context = {
297
+ "post": entity_data,
298
+ "entity": entity_data,
299
+ "content": entity_data,
300
+ "is_preview": True,
301
+ "content_type": ct,
302
+ **menus_ctx,
303
+ **widgets_ctx,
304
+ **seo_ctx,
305
+ }
306
+
307
+ # Determine template
308
+ template = "post.html"
309
+ if ct and ct.template:
310
+ template = ct.template
311
+
312
+ html = await render_theme(db, template, context, request=request)
313
+
314
+ return HTMLResponse(content=html)
315
+
316
+
264
317
  async def get_menus_context(db: AsyncSession) -> dict:
265
318
  """Get menus context for templates."""
266
319
  menu_svc = MenuService(db)
@@ -388,6 +388,7 @@
388
388
  <a href="{{ cancel_url }}" class="btn btn-secondary">Cancel</a>
389
389
  {% if entity %}
390
390
  <button type="button" class="btn btn-secondary" onclick="toggleRevisions()">History</button>
391
+ <button type="button" class="btn btn-secondary" onclick="openPreview()">Preview</button>
391
392
  {% endif %}
392
393
  <button type="submit" class="btn btn-primary">
393
394
  {% if entity %}Update{% else %}Create{% endif %}
@@ -1239,6 +1240,34 @@ function toggleRevisions() {
1239
1240
  }
1240
1241
  }
1241
1242
 
1243
+ // Preview in new tab
1244
+ async function openPreview() {
1245
+ const entityId = '{{ entity.id if entity else "" }}';
1246
+ if (!entityId) {
1247
+ alert('Please save the content first before previewing.');
1248
+ return;
1249
+ }
1250
+
1251
+ try {
1252
+ const response = await fetch('/admin/api/preview/token', {
1253
+ method: 'POST',
1254
+ headers: { 'Content-Type': 'application/json' },
1255
+ body: JSON.stringify({ entity_id: entityId })
1256
+ });
1257
+
1258
+ if (!response.ok) {
1259
+ const error = await response.json();
1260
+ alert('Preview error: ' + (error.detail || 'Unknown error'));
1261
+ return;
1262
+ }
1263
+
1264
+ const data = await response.json();
1265
+ window.open(data.url, '_blank');
1266
+ } catch (e) {
1267
+ alert('Preview error: ' + e.message);
1268
+ }
1269
+ }
1270
+
1242
1271
  async function loadRevisions() {
1243
1272
  const entityId = '{{ entity.id }}';
1244
1273
  const list = document.getElementById('revisions-list');
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "focomy"
7
- version = "0.1.115"
7
+ version = "0.1.116"
8
8
  description = "The Most Beautiful CMS - A metadata-driven, zero-duplicate-code content management system"
9
9
  readme = "README.md"
10
10
  license = "MIT"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes