postnhost 0.0.0 → 0.1.1

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 (500) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -0
  3. data/{LICENSE.txt → LICENSE} +6 -6
  4. data/README.md +343 -15
  5. data/Rakefile +66 -6
  6. data/app/assets/builds/postnhost/application.css +2 -0
  7. data/app/assets/builds/postnhost/application.js +50190 -0
  8. data/app/assets/config/postnhost_manifest.js +2 -0
  9. data/app/assets/images/postnhost/demo.webp +0 -0
  10. data/app/assets/images/postnhost/icons/alert-circle.svg +3 -0
  11. data/app/assets/images/postnhost/icons/arrow-left-long.svg +3 -0
  12. data/app/assets/images/postnhost/icons/arrow-left.svg +3 -0
  13. data/app/assets/images/postnhost/icons/arrow-right-end-on-rectangle.svg +3 -0
  14. data/app/assets/images/postnhost/icons/arrow-right-long.svg +3 -0
  15. data/app/assets/images/postnhost/icons/arrow-right.svg +3 -0
  16. data/app/assets/images/postnhost/icons/bars.svg +3 -0
  17. data/app/assets/images/postnhost/icons/bluesky.svg +3 -0
  18. data/app/assets/images/postnhost/icons/check-circle.svg +3 -0
  19. data/app/assets/images/postnhost/icons/chevron-down-small.svg +3 -0
  20. data/app/assets/images/postnhost/icons/chevron-down.svg +3 -0
  21. data/app/assets/images/postnhost/icons/chevron-left.svg +3 -0
  22. data/app/assets/images/postnhost/icons/chevron-right.svg +3 -0
  23. data/app/assets/images/postnhost/icons/clock.svg +3 -0
  24. data/app/assets/images/postnhost/icons/close.svg +3 -0
  25. data/app/assets/images/postnhost/icons/code-bracket.svg +3 -0
  26. data/app/assets/images/postnhost/icons/document-text.svg +3 -0
  27. data/app/assets/images/postnhost/icons/drag-handle.svg +8 -0
  28. data/app/assets/images/postnhost/icons/edit.svg +3 -0
  29. data/app/assets/images/postnhost/icons/external-link.svg +3 -0
  30. data/app/assets/images/postnhost/icons/eye.svg +4 -0
  31. data/app/assets/images/postnhost/icons/facebook.svg +3 -0
  32. data/app/assets/images/postnhost/icons/folder.svg +3 -0
  33. data/app/assets/images/postnhost/icons/image.svg +3 -0
  34. data/app/assets/images/postnhost/icons/info-circle.svg +3 -0
  35. data/app/assets/images/postnhost/icons/instagram.svg +3 -0
  36. data/app/assets/images/postnhost/icons/language.svg +3 -0
  37. data/app/assets/images/postnhost/icons/layers.svg +3 -0
  38. data/app/assets/images/postnhost/icons/lightning.svg +3 -0
  39. data/app/assets/images/postnhost/icons/linkedin.svg +3 -0
  40. data/app/assets/images/postnhost/icons/logout.svg +3 -0
  41. data/app/assets/images/postnhost/icons/magnifying-glass.svg +3 -0
  42. data/app/assets/images/postnhost/icons/mastodon.svg +3 -0
  43. data/app/assets/images/postnhost/icons/menu.svg +3 -0
  44. data/app/assets/images/postnhost/icons/plus.svg +3 -0
  45. data/app/assets/images/postnhost/icons/schedule.svg +3 -0
  46. data/app/assets/images/postnhost/icons/settings.svg +4 -0
  47. data/app/assets/images/postnhost/icons/sparkles.svg +3 -0
  48. data/app/assets/images/postnhost/icons/spinner.svg +4 -0
  49. data/app/assets/images/postnhost/icons/threads.svg +3 -0
  50. data/app/assets/images/postnhost/icons/tiktok.svg +3 -0
  51. data/app/assets/images/postnhost/icons/tiptap/align-center.svg +3 -0
  52. data/app/assets/images/postnhost/icons/tiptap/align-left.svg +3 -0
  53. data/app/assets/images/postnhost/icons/tiptap/align-right.svg +3 -0
  54. data/app/assets/images/postnhost/icons/tiptap/apply-link.svg +3 -0
  55. data/app/assets/images/postnhost/icons/tiptap/blockquote.svg +3 -0
  56. data/app/assets/images/postnhost/icons/tiptap/bold.svg +3 -0
  57. data/app/assets/images/postnhost/icons/tiptap/bullet-list.svg +3 -0
  58. data/app/assets/images/postnhost/icons/tiptap/code-block.svg +3 -0
  59. data/app/assets/images/postnhost/icons/tiptap/code.svg +3 -0
  60. data/app/assets/images/postnhost/icons/tiptap/heading-2.svg +3 -0
  61. data/app/assets/images/postnhost/icons/tiptap/heading-3.svg +3 -0
  62. data/app/assets/images/postnhost/icons/tiptap/image-add.svg +3 -0
  63. data/app/assets/images/postnhost/icons/tiptap/italic.svg +3 -0
  64. data/app/assets/images/postnhost/icons/tiptap/link.svg +3 -0
  65. data/app/assets/images/postnhost/icons/tiptap/ordered-list.svg +3 -0
  66. data/app/assets/images/postnhost/icons/tiptap/redo.svg +3 -0
  67. data/app/assets/images/postnhost/icons/tiptap/remove-link.svg +3 -0
  68. data/app/assets/images/postnhost/icons/tiptap/strike.svg +3 -0
  69. data/app/assets/images/postnhost/icons/tiptap/table.svg +3 -0
  70. data/app/assets/images/postnhost/icons/tiptap/underline.svg +3 -0
  71. data/app/assets/images/postnhost/icons/tiptap/undo.svg +3 -0
  72. data/app/assets/images/postnhost/icons/tiptap/youtube.svg +4 -0
  73. data/app/assets/images/postnhost/icons/translate.svg +3 -0
  74. data/app/assets/images/postnhost/icons/trash.svg +3 -0
  75. data/app/assets/images/postnhost/icons/user.svg +4 -0
  76. data/app/assets/images/postnhost/icons/users.svg +3 -0
  77. data/app/assets/images/postnhost/icons/warning.svg +3 -0
  78. data/app/assets/images/postnhost/icons/x.svg +3 -0
  79. data/app/assets/images/postnhost/icons/youtube.svg +3 -0
  80. data/app/assets/images/postnhost/logo.webp +0 -0
  81. data/app/assets/images/postnhost/og-image.webp +0 -0
  82. data/app/assets/images/postnhost/sample-docs/08-how-to-control-suggested-articles-and-top-picks-01-discovery-controls.webp +0 -0
  83. data/app/assets/images/postnhost/sample-docs/09-all-about-authors-in-postnhost-01-author-profile.webp +0 -0
  84. data/app/assets/images/postnhost/sample-docs/09-all-about-authors-in-postnhost-02-multiple-authors.webp +0 -0
  85. data/app/assets/images/postnhost/sample-docs/11-how-to-schedule-an-article-for-publication-01-timezone.webp +0 -0
  86. data/app/assets/images/postnhost/sample-docs/11-how-to-schedule-an-article-for-publication-02-scheduled-article.webp +0 -0
  87. data/app/assets/images/postnhost/sample-docs/12-how-article-version-history-and-restores-work-01-version-history.webp +0 -0
  88. data/app/assets/images/postnhost/sample-docs/13-how-to-add-and-manage-languages-01-language-management.webp +0 -0
  89. data/app/assets/images/postnhost/sample-docs/14-how-to-translate-articles-pages-and-categories-01-translation-options.webp +0 -0
  90. data/app/assets/images/postnhost/sample-docs/14-how-to-translate-articles-pages-and-categories-02-ai-language-selection.webp +0 -0
  91. data/app/assets/images/postnhost/sample-docs/16-how-to-add-navigation-links-01-navigation-builder.webp +0 -0
  92. data/app/assets/images/postnhost/sample-docs/16-how-to-add-navigation-links-02-external-link-settings.webp +0 -0
  93. data/app/assets/images/postnhost/sample-docs/17-how-to-add-a-logo-and-default-og-image-01-assets-settings.webp +0 -0
  94. data/app/assets/images/postnhost/sample-docs/19-how-to-customize-public-text-for-each-language-01-i18n-overrides.webp +0 -0
  95. data/app/assets/images/postnhost/sample-docs/20-how-to-configure-structured-data-and-sitemap-seo-01-site-schema.webp +0 -0
  96. data/app/assets/images/postnhost/sample-docs/20-how-to-configure-structured-data-and-sitemap-seo-02-author-schema-defaults.webp +0 -0
  97. data/app/assets/images/postnhost/sample-docs/20-how-to-configure-structured-data-and-sitemap-seo-03-author-schema-advanced-fields.webp +0 -0
  98. data/app/assets/images/postnhost/sample-docs/covers/08-suggested-articles-and-top-picks.webp +0 -0
  99. data/app/assets/images/postnhost/sample-docs/covers/09-authors.webp +0 -0
  100. data/app/assets/images/postnhost/sample-docs/covers/11-scheduling.webp +0 -0
  101. data/app/assets/images/postnhost/sample-docs/covers/12-version-history.webp +0 -0
  102. data/app/assets/images/postnhost/sample-docs/covers/13-languages.webp +0 -0
  103. data/app/assets/images/postnhost/sample-docs/covers/14-translations.webp +0 -0
  104. data/app/assets/images/postnhost/sample-docs/covers/16-navigation-links.webp +0 -0
  105. data/app/assets/images/postnhost/sample-docs/covers/17-logo-and-default-og-image.webp +0 -0
  106. data/app/assets/images/postnhost/sample-docs/covers/19-public-text-for-each-language.webp +0 -0
  107. data/app/assets/images/postnhost/sample-docs/covers/20-structured-data-and-sitemap-seo.webp +0 -0
  108. data/app/assets/stylesheets/application.tailwind.css +347 -0
  109. data/app/assets/stylesheets/postnhost/application.css +15 -0
  110. data/app/assets/xsl/sitemap.xsl +64 -0
  111. data/app/controllers/concerns/postnhost/authentication.rb +59 -0
  112. data/app/controllers/concerns/postnhost/canonical_first_page_redirect.rb +14 -0
  113. data/app/controllers/concerns/postnhost/language_switcher.rb +42 -0
  114. data/app/controllers/concerns/postnhost/onboarding/access_control.rb +57 -0
  115. data/app/controllers/concerns/postnhost/onboarding/step_assignment.rb +55 -0
  116. data/app/controllers/concerns/postnhost/public_footer_data.rb +103 -0
  117. data/app/controllers/concerns/postnhost/public_http_caching.rb +35 -0
  118. data/app/controllers/concerns/postnhost/public_page_size.rb +17 -0
  119. data/app/controllers/concerns/postnhost/public_template_resolvable.rb +44 -0
  120. data/app/controllers/concerns/postnhost/settings/i18n_manageable.rb +61 -0
  121. data/app/controllers/concerns/postnhost/settings/navigation_manageable.rb +118 -0
  122. data/app/controllers/concerns/postnhost/settings/schema_manageable.rb +140 -0
  123. data/app/controllers/concerns/postnhost/users/schema_manageable.rb +119 -0
  124. data/app/controllers/postnhost/application_controller.rb +37 -0
  125. data/app/controllers/postnhost/article_variants_controller.rb +138 -0
  126. data/app/controllers/postnhost/articles/cover_images_controller.rb +35 -0
  127. data/app/controllers/postnhost/articles/images_controller.rb +28 -0
  128. data/app/controllers/postnhost/articles/translations_controller.rb +26 -0
  129. data/app/controllers/postnhost/articles_controller.rb +130 -0
  130. data/app/controllers/postnhost/categories/translations_controller.rb +24 -0
  131. data/app/controllers/postnhost/categories_controller.rb +59 -0
  132. data/app/controllers/postnhost/category_variants_controller.rb +69 -0
  133. data/app/controllers/postnhost/languages_controller.rb +66 -0
  134. data/app/controllers/postnhost/onboarding_controller.rb +70 -0
  135. data/app/controllers/postnhost/page_variants_controller.rb +136 -0
  136. data/app/controllers/postnhost/pages/images_controller.rb +28 -0
  137. data/app/controllers/postnhost/pages/translations_controller.rb +26 -0
  138. data/app/controllers/postnhost/pages_controller.rb +95 -0
  139. data/app/controllers/postnhost/public/articles_controller.rb +135 -0
  140. data/app/controllers/postnhost/public/authors_controller.rb +57 -0
  141. data/app/controllers/postnhost/public/categories_controller.rb +61 -0
  142. data/app/controllers/postnhost/public/static_pages_controller.rb +77 -0
  143. data/app/controllers/postnhost/public_controller.rb +32 -0
  144. data/app/controllers/postnhost/sessions_controller.rb +37 -0
  145. data/app/controllers/postnhost/settings/site_scripts_controller.rb +61 -0
  146. data/app/controllers/postnhost/settings_controller.rb +63 -0
  147. data/app/controllers/postnhost/sitemap_controller.rb +17 -0
  148. data/app/controllers/postnhost/templates_controller.rb +26 -0
  149. data/app/controllers/postnhost/users_controller.rb +112 -0
  150. data/app/helpers/postnhost/application_helper.rb +236 -0
  151. data/app/helpers/postnhost/article_helper.rb +41 -0
  152. data/app/helpers/postnhost/category_helper.rb +52 -0
  153. data/app/helpers/postnhost/common_css_helper.rb +97 -0
  154. data/app/helpers/postnhost/navigation_helper.rb +148 -0
  155. data/app/helpers/postnhost/pagy_helper.rb +103 -0
  156. data/app/helpers/postnhost/public_pages_helper.rb +287 -0
  157. data/app/helpers/postnhost/schema_helper.rb +102 -0
  158. data/app/helpers/postnhost/seo_helper.rb +428 -0
  159. data/app/helpers/postnhost/settings_helper.rb +79 -0
  160. data/app/javascript/application.js +3 -0
  161. data/app/javascript/controllers/ai_translation_controller.js +42 -0
  162. data/app/javascript/controllers/application.js +9 -0
  163. data/app/javascript/controllers/author_manager_controller.js +96 -0
  164. data/app/javascript/controllers/auto_save_controller.js +244 -0
  165. data/app/javascript/controllers/bulk_selection_controller.js +74 -0
  166. data/app/javascript/controllers/burger_controller.js +20 -0
  167. data/app/javascript/controllers/category_manager_controller.js +99 -0
  168. data/app/javascript/controllers/collapsible_sidebar_controller.js +87 -0
  169. data/app/javascript/controllers/cover_image_upload_controller.js +164 -0
  170. data/app/javascript/controllers/dropdown_controller.js +38 -0
  171. data/app/javascript/controllers/editor_mobile_sidebars_controller.js +36 -0
  172. data/app/javascript/controllers/file_auto_submit_controller.js +23 -0
  173. data/app/javascript/controllers/flash_message_controller.js +40 -0
  174. data/app/javascript/controllers/index.js +71 -0
  175. data/app/javascript/controllers/navigation_builder_controller.js +463 -0
  176. data/app/javascript/controllers/public_mobile_nav_controller.js +26 -0
  177. data/app/javascript/controllers/publish_guard_controller.js +47 -0
  178. data/app/javascript/controllers/reading_progress_controller.js +38 -0
  179. data/app/javascript/controllers/schedule_manager_controller.js +87 -0
  180. data/app/javascript/controllers/suggestion_manager_controller.js +99 -0
  181. data/app/javascript/controllers/template_preview_controller.js +34 -0
  182. data/app/javascript/controllers/tiptap_editor_controller.js +812 -0
  183. data/app/javascript/controllers/title_field_controller.js +17 -0
  184. data/app/javascript/helpers/auto_save_state.js +47 -0
  185. data/app/javascript/helpers/code_highlighting.js +48 -0
  186. data/app/javascript/helpers/dom_helpers.js +109 -0
  187. data/app/javascript/helpers/style_helpers.js +168 -0
  188. data/app/jobs/postnhost/application_job.rb +9 -0
  189. data/app/jobs/postnhost/scheduled_article_job.rb +13 -0
  190. data/app/jobs/postnhost/translation/article_variant_job.rb +17 -0
  191. data/app/jobs/postnhost/translation/category_variant_job.rb +17 -0
  192. data/app/jobs/postnhost/translation/page_variant_job.rb +17 -0
  193. data/app/models/concerns/postnhost/article/image_uploadable.rb +11 -0
  194. data/app/models/concerns/postnhost/excerpt_optimizable.rb +57 -0
  195. data/app/models/concerns/postnhost/inline_image_uploadable.rb +40 -0
  196. data/app/models/concerns/postnhost/page/image_uploadable.rb +11 -0
  197. data/app/models/concerns/postnhost/primary_language_guard.rb +18 -0
  198. data/app/models/concerns/postnhost/public_revision_touch.rb +15 -0
  199. data/app/models/concerns/postnhost/schema_localizable.rb +28 -0
  200. data/app/models/postnhost/application_record.rb +5 -0
  201. data/app/models/postnhost/article.rb +142 -0
  202. data/app/models/postnhost/article_author.rb +20 -0
  203. data/app/models/postnhost/article_category.rb +8 -0
  204. data/app/models/postnhost/article_suggestion.rb +19 -0
  205. data/app/models/postnhost/article_variant.rb +28 -0
  206. data/app/models/postnhost/category.rb +34 -0
  207. data/app/models/postnhost/category_variant.rb +24 -0
  208. data/app/models/postnhost/language.rb +38 -0
  209. data/app/models/postnhost/navigation.rb +76 -0
  210. data/app/models/postnhost/navigation_item.rb +100 -0
  211. data/app/models/postnhost/page.rb +63 -0
  212. data/app/models/postnhost/page_variant.rb +26 -0
  213. data/app/models/postnhost/public_site_revision.rb +24 -0
  214. data/app/models/postnhost/published_cover_image.rb +28 -0
  215. data/app/models/postnhost/setting.rb +116 -0
  216. data/app/models/postnhost/site_script.rb +11 -0
  217. data/app/models/postnhost/snapshot/article.rb +142 -0
  218. data/app/models/postnhost/snapshot/article_author.rb +11 -0
  219. data/app/models/postnhost/snapshot/article_category.rb +10 -0
  220. data/app/models/postnhost/snapshot/article_suggestion.rb +11 -0
  221. data/app/models/postnhost/snapshot/article_variant.rb +60 -0
  222. data/app/models/postnhost/snapshot/page.rb +33 -0
  223. data/app/models/postnhost/snapshot/page_variant.rb +41 -0
  224. data/app/models/postnhost/template.rb +20 -0
  225. data/app/models/postnhost/user.rb +63 -0
  226. data/app/models/postnhost/version.rb +7 -0
  227. data/app/services/postnhost/base_service.rb +17 -0
  228. data/app/services/postnhost/constraints/public_locale_constraint.rb +9 -0
  229. data/app/services/postnhost/constraints/public_route_constraint.rb +19 -0
  230. data/app/services/postnhost/onboarding/step_processor.rb +27 -0
  231. data/app/services/postnhost/onboarding/steps/base.rb +52 -0
  232. data/app/services/postnhost/onboarding/steps/choose_languages.rb +66 -0
  233. data/app/services/postnhost/onboarding/steps/complete_post_install_checklist.rb +12 -0
  234. data/app/services/postnhost/onboarding/steps/create_admin_account.rb +28 -0
  235. data/app/services/postnhost/onboarding/steps/finish_setup.rb +27 -0
  236. data/app/services/postnhost/onboarding/steps/set_site_metadata.rb +70 -0
  237. data/app/services/postnhost/public_navigation_targets.rb +67 -0
  238. data/app/services/postnhost/public_request_context.rb +101 -0
  239. data/app/services/postnhost/publishing/article_variants/publish.rb +26 -0
  240. data/app/services/postnhost/publishing/article_variants/publish_many.rb +23 -0
  241. data/app/services/postnhost/publishing/article_variants/snapshot_writer.rb +44 -0
  242. data/app/services/postnhost/publishing/article_variants/unpublish.rb +27 -0
  243. data/app/services/postnhost/publishing/article_variants/unpublish_many.rb +23 -0
  244. data/app/services/postnhost/publishing/articles/publish.rb +25 -0
  245. data/app/services/postnhost/publishing/articles/publish_scheduled.rb +42 -0
  246. data/app/services/postnhost/publishing/articles/snapshot_writer.rb +84 -0
  247. data/app/services/postnhost/publishing/articles/unpublish.rb +26 -0
  248. data/app/services/postnhost/publishing/bulk_transition.rb +63 -0
  249. data/app/services/postnhost/publishing/error.rb +12 -0
  250. data/app/services/postnhost/publishing/page_variants/publish.rb +26 -0
  251. data/app/services/postnhost/publishing/page_variants/publish_many.rb +23 -0
  252. data/app/services/postnhost/publishing/page_variants/snapshot_writer.rb +41 -0
  253. data/app/services/postnhost/publishing/page_variants/unpublish.rb +27 -0
  254. data/app/services/postnhost/publishing/page_variants/unpublish_many.rb +23 -0
  255. data/app/services/postnhost/publishing/pages/publish.rb +25 -0
  256. data/app/services/postnhost/publishing/pages/snapshot_writer.rb +38 -0
  257. data/app/services/postnhost/publishing/pages/unpublish.rb +26 -0
  258. data/app/services/postnhost/publishing/revision.rb +37 -0
  259. data/app/services/postnhost/publishing/route_validator.rb +41 -0
  260. data/app/services/postnhost/publishing/versions/restore_draft.rb +64 -0
  261. data/app/services/postnhost/service_result.rb +7 -0
  262. data/app/services/postnhost/sitemap_builder.rb +395 -0
  263. data/app/services/postnhost/translation/article_variant_translator.rb +102 -0
  264. data/app/services/postnhost/translation/category_variant_translator.rb +92 -0
  265. data/app/services/postnhost/translation/content_translator.rb +119 -0
  266. data/app/services/postnhost/translation/openai_llm_client.rb +30 -0
  267. data/app/services/postnhost/translation/page_variant_translator.rb +55 -0
  268. data/app/uploaders/base_uploader.rb +26 -0
  269. data/app/uploaders/cover_image_uploader.rb +31 -0
  270. data/app/uploaders/postnhost/avatar_uploader.rb +17 -0
  271. data/app/uploaders/postnhost/inline_article_image_uploader.rb +38 -0
  272. data/app/uploaders/postnhost/inline_page_image_uploader.rb +38 -0
  273. data/app/uploaders/postnhost/setting_asset_uploader.rb +18 -0
  274. data/app/views/layouts/postnhost/_favicon.html.erb +6 -0
  275. data/app/views/layouts/postnhost/_footer.html.erb +67 -0
  276. data/app/views/layouts/postnhost/_header.html.erb +89 -0
  277. data/app/views/layouts/postnhost/_sidebar.html.erb +72 -0
  278. data/app/views/layouts/postnhost/application.html.erb +23 -0
  279. data/app/views/layouts/postnhost/articles.html.erb +34 -0
  280. data/app/views/layouts/postnhost/product.html.erb +36 -0
  281. data/app/views/layouts/postnhost/public/templates/default.html.erb +110 -0
  282. data/app/views/layouts/postnhost/public/templates/swiss-editorial.html.erb +213 -0
  283. data/app/views/layouts/postnhost/public/templates/workspace-journal.html.erb +231 -0
  284. data/app/views/layouts/postnhost/public_header/_dashboard_sign_in.html.erb +14 -0
  285. data/app/views/layouts/postnhost/public_header/_language_switcher.html.erb +43 -0
  286. data/app/views/layouts/postnhost/public_header/_logo.html.erb +9 -0
  287. data/app/views/postnhost/article_variants/_variants_list.html.erb +82 -0
  288. data/app/views/postnhost/article_variants/edit.html.erb +48 -0
  289. data/app/views/postnhost/article_variants/index.html.erb +128 -0
  290. data/app/views/postnhost/article_variants/publish.turbo_stream.erb +3 -0
  291. data/app/views/postnhost/article_variants/unpublish.turbo_stream.erb +3 -0
  292. data/app/views/postnhost/article_variants/update.turbo_stream.erb +3 -0
  293. data/app/views/postnhost/articles/_author_manager.html.erb +38 -0
  294. data/app/views/postnhost/articles/_category_manager.html.erb +42 -0
  295. data/app/views/postnhost/articles/_cover_image_upload.html.erb +82 -0
  296. data/app/views/postnhost/articles/_suggestion_manager.html.erb +43 -0
  297. data/app/views/postnhost/articles/_version_snapshot.html.erb +10 -0
  298. data/app/views/postnhost/articles/edit.html.erb +51 -0
  299. data/app/views/postnhost/articles/index.html.erb +132 -0
  300. data/app/views/postnhost/articles/publish.turbo_stream.erb +3 -0
  301. data/app/views/postnhost/articles/unpublish.turbo_stream.erb +3 -0
  302. data/app/views/postnhost/articles/update.turbo_stream.erb +3 -0
  303. data/app/views/postnhost/articles/version_preview.html.erb +17 -0
  304. data/app/views/postnhost/articles/versions.html.erb +68 -0
  305. data/app/views/postnhost/categories/edit.html.erb +77 -0
  306. data/app/views/postnhost/categories/index.html.erb +80 -0
  307. data/app/views/postnhost/categories/new.html.erb +50 -0
  308. data/app/views/postnhost/categories/show.html.erb +99 -0
  309. data/app/views/postnhost/category_variants/_category_variants_list.html.erb +67 -0
  310. data/app/views/postnhost/category_variants/_form.html.erb +29 -0
  311. data/app/views/postnhost/category_variants/edit.html.erb +21 -0
  312. data/app/views/postnhost/category_variants/index.html.erb +82 -0
  313. data/app/views/postnhost/category_variants/new.html.erb +21 -0
  314. data/app/views/postnhost/languages/edit.html.erb +45 -0
  315. data/app/views/postnhost/languages/index.html.erb +86 -0
  316. data/app/views/postnhost/languages/new.html.erb +45 -0
  317. data/app/views/postnhost/languages/show.html.erb +156 -0
  318. data/app/views/postnhost/onboarding/_post_install_checklist.html.erb +40 -0
  319. data/app/views/postnhost/onboarding/_step_1.html.erb +42 -0
  320. data/app/views/postnhost/onboarding/_step_2.html.erb +38 -0
  321. data/app/views/postnhost/onboarding/_step_3.html.erb +62 -0
  322. data/app/views/postnhost/onboarding/_step_4.html.erb +26 -0
  323. data/app/views/postnhost/onboarding/show.html.erb +51 -0
  324. data/app/views/postnhost/page_variants/_variants_list.html.erb +82 -0
  325. data/app/views/postnhost/page_variants/edit.html.erb +50 -0
  326. data/app/views/postnhost/page_variants/index.html.erb +128 -0
  327. data/app/views/postnhost/page_variants/publish.turbo_stream.erb +3 -0
  328. data/app/views/postnhost/page_variants/unpublish.turbo_stream.erb +3 -0
  329. data/app/views/postnhost/page_variants/update.turbo_stream.erb +3 -0
  330. data/app/views/postnhost/pages/edit.html.erb +50 -0
  331. data/app/views/postnhost/pages/index.html.erb +110 -0
  332. data/app/views/postnhost/pages/publish.turbo_stream.erb +3 -0
  333. data/app/views/postnhost/pages/unpublish.turbo_stream.erb +3 -0
  334. data/app/views/postnhost/public/articles/content_for/_index.html.erb +30 -0
  335. data/app/views/postnhost/public/articles/content_for/_preview.html.erb +9 -0
  336. data/app/views/postnhost/public/articles/content_for/_show.html.erb +24 -0
  337. data/app/views/postnhost/public/authors/content_for/_show.html.erb +24 -0
  338. data/app/views/postnhost/public/categories/content_for/_index.html.erb +29 -0
  339. data/app/views/postnhost/public/static_pages/content_for/_show.html.erb +19 -0
  340. data/app/views/postnhost/public/templates/default/articles/_article_card.html.erb +37 -0
  341. data/app/views/postnhost/public/templates/default/articles/_search_form.html.erb +12 -0
  342. data/app/views/postnhost/public/templates/default/articles/_suggestions.html.erb +44 -0
  343. data/app/views/postnhost/public/templates/default/articles/_top_picks.html.erb +42 -0
  344. data/app/views/postnhost/public/templates/default/articles/index.html.erb +53 -0
  345. data/app/views/postnhost/public/templates/default/articles/preview.html.erb +102 -0
  346. data/app/views/postnhost/public/templates/default/articles/show.html.erb +111 -0
  347. data/app/views/postnhost/public/templates/default/authors/_article_card.html.erb +28 -0
  348. data/app/views/postnhost/public/templates/default/authors/_profile_block.html.erb +29 -0
  349. data/app/views/postnhost/public/templates/default/authors/show.html.erb +36 -0
  350. data/app/views/postnhost/public/templates/default/categories/_article_card.html.erb +34 -0
  351. data/app/views/postnhost/public/templates/default/categories/index.html.erb +48 -0
  352. data/app/views/postnhost/public/templates/default/shared/_article_authors.html.erb +24 -0
  353. data/app/views/postnhost/public/templates/default/shared/_author_avatar.html.erb +9 -0
  354. data/app/views/postnhost/public/templates/default/shared/_hero_cover_image.html.erb +11 -0
  355. data/app/views/postnhost/public/templates/default/shared/_pagination.html.erb +35 -0
  356. data/app/views/postnhost/public/templates/default/shared/_thumbnail_cover_image.html.erb +9 -0
  357. data/app/views/postnhost/public/templates/default/static_pages/show.html.erb +22 -0
  358. data/app/views/postnhost/public/templates/swiss-editorial/articles/_article_card.html.erb +28 -0
  359. data/app/views/postnhost/public/templates/swiss-editorial/articles/_search_form.html.erb +19 -0
  360. data/app/views/postnhost/public/templates/swiss-editorial/articles/_suggestions.html.erb +42 -0
  361. data/app/views/postnhost/public/templates/swiss-editorial/articles/_top_picks.html.erb +38 -0
  362. data/app/views/postnhost/public/templates/swiss-editorial/articles/index.html.erb +133 -0
  363. data/app/views/postnhost/public/templates/swiss-editorial/articles/preview.html.erb +42 -0
  364. data/app/views/postnhost/public/templates/swiss-editorial/articles/show.html.erb +100 -0
  365. data/app/views/postnhost/public/templates/swiss-editorial/authors/_article_card.html.erb +27 -0
  366. data/app/views/postnhost/public/templates/swiss-editorial/authors/_profile_block.html.erb +32 -0
  367. data/app/views/postnhost/public/templates/swiss-editorial/authors/show.html.erb +31 -0
  368. data/app/views/postnhost/public/templates/swiss-editorial/categories/_article_card.html.erb +28 -0
  369. data/app/views/postnhost/public/templates/swiss-editorial/categories/index.html.erb +88 -0
  370. data/app/views/postnhost/public/templates/swiss-editorial/shared/_article_authors.html.erb +26 -0
  371. data/app/views/postnhost/public/templates/swiss-editorial/shared/_author_avatar.html.erb +9 -0
  372. data/app/views/postnhost/public/templates/swiss-editorial/shared/_hero_cover_image.html.erb +11 -0
  373. data/app/views/postnhost/public/templates/swiss-editorial/shared/_pagination.html.erb +35 -0
  374. data/app/views/postnhost/public/templates/swiss-editorial/shared/_thumbnail_cover_image.html.erb +9 -0
  375. data/app/views/postnhost/public/templates/swiss-editorial/static_pages/show.html.erb +17 -0
  376. data/app/views/postnhost/public/templates/workspace-journal/articles/_article_card.html.erb +44 -0
  377. data/app/views/postnhost/public/templates/workspace-journal/articles/_search_form.html.erb +19 -0
  378. data/app/views/postnhost/public/templates/workspace-journal/articles/_suggestions.html.erb +49 -0
  379. data/app/views/postnhost/public/templates/workspace-journal/articles/_top_picks.html.erb +34 -0
  380. data/app/views/postnhost/public/templates/workspace-journal/articles/index.html.erb +69 -0
  381. data/app/views/postnhost/public/templates/workspace-journal/articles/preview.html.erb +113 -0
  382. data/app/views/postnhost/public/templates/workspace-journal/articles/show.html.erb +120 -0
  383. data/app/views/postnhost/public/templates/workspace-journal/authors/_article_card.html.erb +1 -0
  384. data/app/views/postnhost/public/templates/workspace-journal/authors/_profile_block.html.erb +32 -0
  385. data/app/views/postnhost/public/templates/workspace-journal/authors/show.html.erb +33 -0
  386. data/app/views/postnhost/public/templates/workspace-journal/categories/_article_card.html.erb +1 -0
  387. data/app/views/postnhost/public/templates/workspace-journal/categories/index.html.erb +58 -0
  388. data/app/views/postnhost/public/templates/workspace-journal/shared/_article_authors.html.erb +27 -0
  389. data/app/views/postnhost/public/templates/workspace-journal/shared/_article_card.html.erb +44 -0
  390. data/app/views/postnhost/public/templates/workspace-journal/shared/_author_avatar.html.erb +9 -0
  391. data/app/views/postnhost/public/templates/workspace-journal/shared/_category_navigation.html.erb +10 -0
  392. data/app/views/postnhost/public/templates/workspace-journal/shared/_hero_cover_image.html.erb +11 -0
  393. data/app/views/postnhost/public/templates/workspace-journal/shared/_pagination.html.erb +35 -0
  394. data/app/views/postnhost/public/templates/workspace-journal/shared/_thumbnail_cover_image.html.erb +9 -0
  395. data/app/views/postnhost/public/templates/workspace-journal/static_pages/show.html.erb +17 -0
  396. data/app/views/postnhost/sessions/new.html.erb +59 -0
  397. data/app/views/postnhost/settings/edit.html.erb +68 -0
  398. data/app/views/postnhost/settings/sections/_assets.html.erb +40 -0
  399. data/app/views/postnhost/settings/sections/_feature_flags.html.erb +43 -0
  400. data/app/views/postnhost/settings/sections/_i18n.html.erb +57 -0
  401. data/app/views/postnhost/settings/sections/_navigation.html.erb +98 -0
  402. data/app/views/postnhost/settings/sections/_schema.html.erb +253 -0
  403. data/app/views/postnhost/settings/sections/_scripts.html.erb +79 -0
  404. data/app/views/postnhost/settings/sections/_site.html.erb +41 -0
  405. data/app/views/postnhost/settings/sections/_timezone.html.erb +19 -0
  406. data/app/views/postnhost/shared/_ai_translation_modal.html.erb +85 -0
  407. data/app/views/postnhost/shared/_flash_messages.html.erb +27 -0
  408. data/app/views/postnhost/shared/_form_errors.html.erb +22 -0
  409. data/app/views/postnhost/shared/_seo_meta_tags.html.erb +70 -0
  410. data/app/views/postnhost/shared/_structured_data.html.erb +5 -0
  411. data/app/views/postnhost/shared/articles/_action_buttons.html.erb +87 -0
  412. data/app/views/postnhost/shared/articles/_cover_image_preview.html.erb +30 -0
  413. data/app/views/postnhost/shared/articles/_editor_column.html.erb +15 -0
  414. data/app/views/postnhost/shared/articles/_editor_column_preview.html.erb +33 -0
  415. data/app/views/postnhost/shared/articles/_error_messages.html.erb +23 -0
  416. data/app/views/postnhost/shared/articles/_meta_sidebar.html.erb +322 -0
  417. data/app/views/postnhost/shared/articles/_meta_sidebar_preview.html.erb +29 -0
  418. data/app/views/postnhost/shared/articles/_post_actions.html.erb +105 -0
  419. data/app/views/postnhost/shared/articles/_schedule_actions.html.erb +51 -0
  420. data/app/views/postnhost/shared/articles/_tiptap_editor.html.erb +225 -0
  421. data/app/views/postnhost/shared/articles/_title_field.html.erb +9 -0
  422. data/app/views/postnhost/shared/articles/_version_action_buttons.html.erb +35 -0
  423. data/app/views/postnhost/shared/elements/_button.html.erb +20 -0
  424. data/app/views/postnhost/shared/elements/_button_link.html.erb +24 -0
  425. data/app/views/postnhost/shared/elements/_empty_state.html.erb +22 -0
  426. data/app/views/postnhost/shared/elements/_error_messages.html.erb +28 -0
  427. data/app/views/postnhost/shared/elements/_form_card.html.erb +10 -0
  428. data/app/views/postnhost/shared/elements/_form_page_header.html.erb +21 -0
  429. data/app/views/postnhost/shared/elements/_icon_button.html.erb +17 -0
  430. data/app/views/postnhost/shared/elements/_page_header.html.erb +40 -0
  431. data/app/views/postnhost/shared/elements/_table_container.html.erb +23 -0
  432. data/app/views/postnhost/shared/pages/_action_buttons.html.erb +84 -0
  433. data/app/views/postnhost/shared/pages/_meta_sidebar.html.erb +132 -0
  434. data/app/views/postnhost/shared/pages/_post_actions.html.erb +69 -0
  435. data/app/views/postnhost/templates/edit.html.erb +83 -0
  436. data/app/views/postnhost/users/_avatar.html.erb +9 -0
  437. data/app/views/postnhost/users/_form.html.erb +160 -0
  438. data/app/views/postnhost/users/edit.html.erb +26 -0
  439. data/app/views/postnhost/users/index.html.erb +91 -0
  440. data/app/views/postnhost/users/new.html.erb +9 -0
  441. data/app/views/postnhost/users/schema_edit.html.erb +175 -0
  442. data/config/locales/de.yml +55 -0
  443. data/config/locales/en.yml +88 -0
  444. data/config/locales/es.yml +58 -0
  445. data/config/locales/fr.yml +55 -0
  446. data/config/locales/ja.yml +55 -0
  447. data/config/locales/ko.yml +55 -0
  448. data/config/locales/pl.yml +55 -0
  449. data/config/locales/pt.yml +55 -0
  450. data/config/locales/ru.yml +55 -0
  451. data/config/routes.rb +112 -0
  452. data/db/migrate/20250822213820_create_users.rb +29 -0
  453. data/db/migrate/20250822214046_create_categories.rb +15 -0
  454. data/db/migrate/20250822214106_create_languages.rb +16 -0
  455. data/db/migrate/20250822214148_create_articles.rb +34 -0
  456. data/db/migrate/20250822214217_create_article_variants.rb +24 -0
  457. data/db/migrate/20250929091137_create_article_categories.rb +12 -0
  458. data/db/migrate/20251013204200_create_versions.rb +40 -0
  459. data/db/migrate/20260106074532_create_article_suggestions.rb +13 -0
  460. data/db/migrate/20260108110506_create_category_variants.rb +15 -0
  461. data/db/migrate/20260308130000_create_settings.rb +23 -0
  462. data/db/migrate/20260323113000_create_pages.rb +19 -0
  463. data/db/migrate/20260414100000_create_article_authors.rb +14 -0
  464. data/db/migrate/20260512100000_create_templates.rb +9 -0
  465. data/db/migrate/20260520110001_create_navigations.rb +9 -0
  466. data/db/migrate/20260520110002_create_navigation_items.rb +22 -0
  467. data/db/migrate/20260528000002_create_page_variants.rb +20 -0
  468. data/db/migrate/20260709090000_create_snapshots.rb +154 -0
  469. data/db/migrate/20260726130000_create_site_scripts.rb +11 -0
  470. data/lib/generators/postnhost/install/install_generator.rb +55 -0
  471. data/lib/generators/postnhost/install/templates/carrierwave.rb +17 -0
  472. data/lib/generators/postnhost/install/templates/postnhost.rb +36 -0
  473. data/lib/generators/postnhost/locale/locale_generator.rb +36 -0
  474. data/lib/generators/postnhost/static_pages/static_pages_generator.rb +47 -0
  475. data/lib/generators/postnhost/tailwindcss/install_generator.rb +100 -0
  476. data/lib/generators/postnhost/tailwindcss/templates/host.tailwind.css +4 -0
  477. data/lib/generators/postnhost/user/user_generator.rb +36 -0
  478. data/lib/generators/postnhost/views/views_generator.rb +140 -0
  479. data/lib/postnhost/configuration.rb +64 -0
  480. data/lib/postnhost/css_build.rb +68 -0
  481. data/lib/postnhost/css_scope.rb +77 -0
  482. data/lib/postnhost/engine.rb +57 -0
  483. data/lib/postnhost/sample_data.rb +865 -0
  484. data/lib/postnhost/settings/i18n_overrides.rb +137 -0
  485. data/lib/postnhost/settings/i18n_patch.rb +18 -0
  486. data/lib/postnhost/tailwind_compiler.rb +75 -0
  487. data/lib/postnhost/tailwind_input.rb +32 -0
  488. data/lib/postnhost/version.rb +1 -3
  489. data/lib/postnhost.rb +26 -3
  490. data/lib/tasks/postnhost_snapshots.rake +77 -0
  491. data/lib/tasks/postnhost_tasks.rake +71 -0
  492. metadata +679 -21
  493. data/.github/workflows/main.yml +0 -27
  494. data/.gitignore +0 -11
  495. data/.rspec +0 -3
  496. data/.rubocop.yml +0 -8
  497. data/Gemfile +0 -13
  498. data/bin/console +0 -11
  499. data/bin/setup +0 -8
  500. data/postnhost.gemspec +0 -31
@@ -0,0 +1,55 @@
1
+ ru:
2
+ postnhost:
3
+ public:
4
+ site:
5
+ schema_site_name: "PostnHost"
6
+ blog_tagline: "Блог"
7
+ blog_subtitle: "Последние статьи, учебные материалы и истории"
8
+ blog_meta_title: "Блог — PostnHost"
9
+ blog_meta_description: "Последние статьи, руководства и истории от PostnHost."
10
+ blog_meta_title_paginated: "Страница %{page} — %{blog_meta_title}"
11
+ name_footer_copyright: "postnhost.com"
12
+
13
+ blog:
14
+ page_title: "Страница %{page} — Блог"
15
+ author_label: "Автор"
16
+ no_articles: "Пока нет статей"
17
+ no_articles_description: "Скоро появятся новые материалы."
18
+ back_to_articles: "Все статьи"
19
+ more_in_category: "Больше материалов в категории «%{category}»"
20
+ available_in_languages: "Доступно на других языках:"
21
+ browse_all_articles: "Все статьи"
22
+ read_more: "Похожие статьи"
23
+ read_article_aria_label: "Читать «%{title}»"
24
+ top_picks: "Выбор редакции"
25
+
26
+ search:
27
+ title: "Результаты поиска по запросу «%{query}»"
28
+ heading: "Результаты поиска по запросу «%{query}»"
29
+ input_placeholder: "Поиск по заголовку или содержанию"
30
+ submit: "Поиск"
31
+
32
+ category:
33
+ title: "%{category} — PostnHost"
34
+ page_title: "Страница %{page} — %{category} — PostnHost"
35
+ page_heading: "Страница %{page} — %{category}"
36
+ no_articles: "Статьи не найдены"
37
+ no_articles_description: "В этой категории пока нет опубликованных статей."
38
+
39
+ authors:
40
+ latest_from_author: "Последние статьи этого автора"
41
+ no_articles: "Пока нет статей"
42
+ no_articles_description: "Этот автор пока не опубликовал ни одной статьи."
43
+
44
+ footer:
45
+ heading: "Подвал"
46
+ explore_categories: "Статьи по категориям"
47
+ pages: "Страницы"
48
+
49
+ breadcrumbs:
50
+ home: "Главная"
51
+
52
+ pagination:
53
+ navigation: "Навигация по страницам"
54
+ previous: "Предыдущая"
55
+ next: "Следующая"
data/config/routes.rb ADDED
@@ -0,0 +1,112 @@
1
+ Postnhost::Engine.routes.draw do
2
+ # Public routes
3
+ root "public/articles#index"
4
+
5
+ # Sitemap
6
+ get "sitemap.xml", to: "sitemap#show", defaults: { format: "xml" }
7
+ get "sitemap.xsl", to: "sitemap#stylesheet", defaults: { format: "xsl" }
8
+
9
+ get "/search", to: "public/articles#search", as: :public_search
10
+
11
+ get "/authors/:slug", to: "public/authors#show", as: :public_author
12
+
13
+ # Public articles routes (non-localized)
14
+ get "/:category_slug", to: "public/categories#index", as: :public_category,
15
+ constraints: Postnhost::Constraints::PublicRouteConstraint.new(kind: :category, parameter: :category_slug)
16
+
17
+ get "/:slug", to: "public/articles#show", as: :public_article,
18
+ constraints: Postnhost::Constraints::PublicRouteConstraint.new(kind: :article, parameter: :slug)
19
+
20
+ # Localized routes
21
+ scope "/:locale", constraints: Postnhost::Constraints::PublicLocaleConstraint do
22
+ get "/", to: "public/articles#index", as: :localized_root
23
+ get "/search", to: "public/articles#search", as: :localized_public_search
24
+
25
+ get "/authors/:slug", to: "public/authors#show", as: :localized_public_author
26
+
27
+ get "/:category_slug", to: "public/categories#index", as: :localized_public_category,
28
+ constraints: Postnhost::Constraints::PublicRouteConstraint.new(kind: :category,
29
+ parameter: :category_slug)
30
+ get "/:slug", to: "public/articles#show", as: :localized_public_article,
31
+ constraints: Postnhost::Constraints::PublicRouteConstraint.new(kind: :article, parameter: :slug)
32
+ get "/:slug", to: "public/static_pages#show", as: :localized_public_static_page
33
+ end
34
+
35
+ # Admin routes
36
+ resource :onboarding, only: %i[show update], controller: "onboarding"
37
+ resource :session, only: %i[new create destroy], controller: "sessions"
38
+ resources :users, only: %i[index new create edit update destroy] do
39
+ member do
40
+ get :schema_edit
41
+ patch :schema_update
42
+ end
43
+ end
44
+
45
+ resources :articles, only: %i[index new edit update destroy] do
46
+ member do
47
+ get :versions
48
+ get "versions/:version_id/preview", to: "articles#version_preview", as: :version_preview
49
+ post :rollback
50
+ patch :publish
51
+ patch :unpublish
52
+ end
53
+
54
+ resources :images, only: %i[create], controller: "articles/images"
55
+ resource :cover_image, only: %i[create destroy], controller: "articles/cover_images"
56
+
57
+ resources :variants, only: %i[index new edit update destroy], controller: "article_variants" do
58
+ collection do
59
+ patch :bulk_publish
60
+ patch :bulk_unpublish
61
+ delete :bulk_destroy
62
+ end
63
+ member do
64
+ patch :publish
65
+ patch :unpublish
66
+ end
67
+ end
68
+
69
+ resources :translations, only: %i[new create], controller: "articles/translations"
70
+ end
71
+
72
+ resources :pages, only: %i[index new edit update destroy] do
73
+ member do
74
+ patch :publish
75
+ patch :unpublish
76
+ end
77
+
78
+ resources :images, only: %i[create], controller: "pages/images"
79
+
80
+ resources :variants, only: %i[index new edit update destroy], controller: "page_variants" do
81
+ collection do
82
+ patch :bulk_publish
83
+ patch :bulk_unpublish
84
+ delete :bulk_destroy
85
+ end
86
+ member do
87
+ patch :publish
88
+ patch :unpublish
89
+ end
90
+ end
91
+
92
+ resources :translations, only: %i[new create], controller: "pages/translations"
93
+ end
94
+
95
+ get "preview/:id", to: "public/articles#preview", as: :preview_article
96
+
97
+ resources :categories do
98
+ resources :translations, only: %i[create], controller: "categories/translations"
99
+ resources :variants, only: %i[index new create edit update destroy], controller: "category_variants"
100
+ end
101
+
102
+ resources :languages
103
+ resource :template, only: %i[edit update], controller: "templates"
104
+
105
+ resource :settings, only: %i[edit update], controller: "settings" do
106
+ resources :site_scripts, only: %i[create update destroy], controller: "settings/site_scripts"
107
+ end
108
+
109
+ # Public static pages from host app views:
110
+ # app/views/postnhost/static_pages/*.html.erb
111
+ get "/:slug", to: "public/static_pages#show", as: :public_static_page
112
+ end
@@ -0,0 +1,29 @@
1
+ class CreateUsers < ActiveRecord::Migration[7.2]
2
+ def change
3
+ create_table :postnhost_users do |t|
4
+ t.string :email, null: false, default: ""
5
+ t.string :name
6
+ t.string :slug, null: false
7
+ t.string :position
8
+ t.text :bio
9
+ t.string :avatar_file
10
+ t.string :website_url
11
+ t.string :x_url
12
+ t.string :linkedin_url
13
+ t.string :facebook_url
14
+ t.string :youtube_url
15
+ t.string :instagram_url
16
+ t.string :threads_url
17
+ t.string :tiktok_url
18
+ t.string :mastodon_url
19
+ t.string :bluesky_url
20
+ t.json :schema_profile, default: {}, null: false
21
+ t.json :schema_locale_overrides, default: {}, null: false
22
+ t.string :password_digest, null: false
23
+ t.timestamps null: false
24
+ end
25
+
26
+ add_index :postnhost_users, :email, unique: true
27
+ add_index :postnhost_users, :slug, unique: true
28
+ end
29
+ end
@@ -0,0 +1,15 @@
1
+ class CreateCategories < ActiveRecord::Migration[7.2]
2
+ def change
3
+ create_table :postnhost_categories do |t|
4
+ t.string :name
5
+ t.string :slug
6
+ t.string :meta_description
7
+ t.integer :articles_count, default: 0, null: false
8
+
9
+ t.timestamps
10
+ end
11
+
12
+ add_index :postnhost_categories, :name, unique: true
13
+ add_index :postnhost_categories, :slug, unique: true
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ class CreateLanguages < ActiveRecord::Migration[7.2]
2
+ def change
3
+ create_table :postnhost_languages do |t|
4
+ t.string :name
5
+ t.string :html_lang
6
+ t.boolean :default, default: false, null: false
7
+ t.integer :articles_count, default: 0, null: false
8
+ t.integer :article_variants_count, default: 0, null: false
9
+
10
+ t.timestamps
11
+ end
12
+
13
+ add_index :postnhost_languages, :name, unique: true
14
+ add_index :postnhost_languages, :html_lang, unique: true
15
+ end
16
+ end
@@ -0,0 +1,34 @@
1
+ class CreateArticles < ActiveRecord::Migration[7.2]
2
+ def change
3
+ create_table :postnhost_articles do |t|
4
+ t.string :title
5
+ t.string :title_tag
6
+ t.string :og_title
7
+ t.string :schema_headline
8
+ t.string :schema_article_type
9
+ t.string :meta_description
10
+ t.string :custom_excerpt
11
+ t.string :auto_excerpt
12
+ t.boolean :use_excerpt_as_meta_description, null: false, default: false
13
+ t.text :content
14
+ t.string :slug
15
+ t.string :cover_image
16
+ t.string :cover_image_alt
17
+ t.boolean :top_pick, default: false, null: false
18
+ t.datetime :scheduled_at
19
+ t.string :scheduled_job_id
20
+ t.text :publication_error
21
+ t.integer :article_variants_count, default: 0, null: false
22
+
23
+ t.references :user, null: true
24
+ t.references :language, foreign_key: { to_table: :postnhost_languages }
25
+
26
+ t.timestamps
27
+ end
28
+
29
+ add_index :postnhost_articles, :slug, unique: true
30
+ add_index :postnhost_articles, :top_pick
31
+ add_index :postnhost_articles, :scheduled_at
32
+ add_index :postnhost_articles, :scheduled_job_id
33
+ end
34
+ end
@@ -0,0 +1,24 @@
1
+ class CreateArticleVariants < ActiveRecord::Migration[7.2]
2
+ def change
3
+ create_table :postnhost_article_variants do |t|
4
+ t.string :title
5
+ t.string :title_tag
6
+ t.string :og_title
7
+ t.string :schema_headline
8
+ t.string :meta_description
9
+ t.string :custom_excerpt
10
+ t.string :auto_excerpt
11
+ t.boolean :use_excerpt_as_meta_description, null: false, default: false
12
+ t.text :content
13
+
14
+ t.boolean :generating, default: false, null: false
15
+
16
+ t.references :language, null: false, foreign_key: { to_table: :postnhost_languages }
17
+ t.references :article, null: false, foreign_key: { to_table: :postnhost_articles }
18
+
19
+ t.timestamps
20
+ end
21
+
22
+ add_index :postnhost_article_variants, %i[article_id language_id], unique: true
23
+ end
24
+ end
@@ -0,0 +1,12 @@
1
+ class CreateArticleCategories < ActiveRecord::Migration[7.2]
2
+ def change
3
+ create_table :postnhost_article_categories do |t|
4
+ t.references :article, null: false, foreign_key: { to_table: :postnhost_articles }
5
+ t.references :category, null: false, foreign_key: { to_table: :postnhost_categories }
6
+
7
+ t.timestamps
8
+ end
9
+
10
+ add_index :postnhost_article_categories, %i[article_id category_id], unique: true
11
+ end
12
+ end
@@ -0,0 +1,40 @@
1
+ # This migration creates the `postnhost_versions` table, the only schema PT requires.
2
+ # All other migrations PT provides are optional.
3
+ class CreateVersions < ActiveRecord::Migration[7.2]
4
+ # The largest text column available in all supported RDBMS is
5
+ # 1024^3 - 1 bytes, roughly one gibibyte. We specify a size
6
+ # so that MySQL will use `longtext` instead of `text`. Otherwise,
7
+ # when serializing very large objects, `text` might not be big enough.
8
+ TEXT_BYTES = 1_073_741_823
9
+
10
+ def change
11
+ create_table :postnhost_versions do |t|
12
+ # Consider using bigint type for performance if you are going to store only numeric ids.
13
+ # t.bigint :whodunnit
14
+ t.string :whodunnit
15
+
16
+ # Known issue in MySQL: fractional second precision
17
+ # -------------------------------------------------
18
+ #
19
+ # MySQL timestamp columns do not support fractional seconds unless
20
+ # defined with "fractional seconds precision". MySQL users should manually
21
+ # add fractional seconds precision to this migration, specifically, to
22
+ # the `created_at` column.
23
+ # (https://dev.mysql.com/doc/refman/5.6/en/fractional-seconds.html)
24
+ #
25
+ # MySQL users should also upgrade to at least rails 4.2, which is the first
26
+ # version of ActiveRecord with support for fractional seconds in MySQL.
27
+ # (https://github.com/rails/rails/pull/14359)
28
+ #
29
+ # MySQL users should use the following line for `created_at`
30
+ # t.datetime :created_at, limit: 6
31
+ t.datetime :created_at
32
+
33
+ t.bigint :item_id, null: false
34
+ t.string :item_type, null: false
35
+ t.string :event, null: false
36
+ t.text :object, limit: TEXT_BYTES
37
+ end
38
+ add_index :postnhost_versions, %i[item_type item_id]
39
+ end
40
+ end
@@ -0,0 +1,13 @@
1
+ class CreateArticleSuggestions < ActiveRecord::Migration[7.2]
2
+ def change
3
+ create_table :postnhost_article_suggestions do |t|
4
+ t.references :article, null: false, foreign_key: { to_table: :postnhost_articles }
5
+ t.references :suggested_article, null: false, foreign_key: { to_table: :postnhost_articles }
6
+ t.integer :position, default: 0
7
+
8
+ t.timestamps
9
+ end
10
+
11
+ add_index :postnhost_article_suggestions, %i[article_id suggested_article_id], unique: true
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ class CreateCategoryVariants < ActiveRecord::Migration[7.2]
2
+ def change
3
+ create_table :postnhost_category_variants do |t|
4
+ t.references :category, null: false, foreign_key: { to_table: :postnhost_categories }
5
+ t.references :language, null: false, foreign_key: { to_table: :postnhost_languages }
6
+ t.string :name
7
+ t.string :meta_description
8
+ t.boolean :generating, default: false, null: false
9
+
10
+ t.timestamps
11
+ end
12
+
13
+ add_index :postnhost_category_variants, %i[category_id language_id], unique: true
14
+ end
15
+ end
@@ -0,0 +1,23 @@
1
+ class CreateSettings < ActiveRecord::Migration[7.2]
2
+ def change
3
+ create_table :postnhost_settings do |t|
4
+ t.string :site_logo
5
+ t.string :og_image
6
+ t.string :site_url
7
+ t.integer :public_page_size
8
+ t.string :timezone
9
+ t.string :site_indexing, default: "index", null: false
10
+ t.json :locale_overrides, default: {}, null: false
11
+ t.json :schema_settings, default: {}, null: false
12
+ t.json :schema_locale_overrides, default: {}, null: false
13
+ t.boolean :use_auto_header_navigation, default: true, null: false
14
+ t.boolean :use_auto_footer_navigation, default: true, null: false
15
+ t.boolean :author_pages_enabled, default: true, null: false
16
+ t.boolean :show_powered_by, default: true, null: false
17
+ t.boolean :search_enabled, default: true, null: false
18
+ t.integer :lock_version, null: false, default: 0
19
+
20
+ t.timestamps
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ class CreatePages < ActiveRecord::Migration[7.2]
2
+ def change
3
+ create_table :postnhost_pages do |t|
4
+ t.references :user, null: false, foreign_key: { to_table: :postnhost_users }
5
+ t.string :title
6
+ t.string :title_tag
7
+ t.string :og_title
8
+ t.string :meta_description
9
+ t.text :content
10
+ t.string :slug
11
+ t.integer :page_variants_count, default: 0, null: false
12
+ t.references :language, null: true, foreign_key: { to_table: :postnhost_languages }
13
+
14
+ t.timestamps
15
+ end
16
+
17
+ add_index :postnhost_pages, :slug, unique: true
18
+ end
19
+ end
@@ -0,0 +1,14 @@
1
+ class CreateArticleAuthors < ActiveRecord::Migration[7.2]
2
+ def change
3
+ create_table :postnhost_article_authors do |t|
4
+ t.references :article, null: false, foreign_key: { to_table: :postnhost_articles }
5
+ t.references :user, null: false, foreign_key: { to_table: :postnhost_users }
6
+ t.integer :position, default: 0, null: false
7
+
8
+ t.timestamps
9
+ end
10
+
11
+ add_index :postnhost_article_authors, %i[article_id user_id], unique: true
12
+ add_index :postnhost_article_authors, %i[article_id position]
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ class CreateTemplates < ActiveRecord::Migration[7.2]
2
+ def change
3
+ create_table :postnhost_templates do |t|
4
+ t.string :name, null: false, default: "default"
5
+
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class CreateNavigations < ActiveRecord::Migration[7.2]
2
+ def change
3
+ create_table :postnhost_navigations do |t|
4
+ t.references :setting, null: false, foreign_key: { to_table: :postnhost_settings }, index: { unique: true }
5
+
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,22 @@
1
+ class CreateNavigationItems < ActiveRecord::Migration[7.2]
2
+ def change
3
+ create_table :postnhost_navigation_items do |t|
4
+ t.references :navigation, null: false, foreign_key: { to_table: :postnhost_navigations }
5
+ t.references :parent, foreign_key: { to_table: :postnhost_navigation_items }
6
+ t.integer :container_kind, null: false
7
+ t.integer :kind, null: false
8
+ t.integer :target_kind
9
+ t.integer :position, null: false, default: 0
10
+ t.json :label_translations, null: false, default: {}
11
+ t.bigint :target_id
12
+ t.string :target_slug
13
+ t.string :url
14
+ t.boolean :nofollow, null: false, default: false
15
+
16
+ t.timestamps
17
+ end
18
+
19
+ add_index :postnhost_navigation_items, %i[navigation_id container_kind position], name: "idx_postnhost_navigation_items_on_nav_container_position"
20
+ add_index :postnhost_navigation_items, %i[parent_id position], name: "idx_postnhost_navigation_items_on_parent_position"
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ class CreatePageVariants < ActiveRecord::Migration[7.2]
2
+ def change
3
+ create_table :postnhost_page_variants do |t|
4
+ t.string :title
5
+ t.string :title_tag
6
+ t.string :og_title
7
+ t.string :meta_description
8
+ t.text :content
9
+
10
+ t.boolean :generating, default: false, null: false
11
+
12
+ t.references :language, null: false, foreign_key: { to_table: :postnhost_languages }
13
+ t.references :page, null: false, foreign_key: { to_table: :postnhost_pages }
14
+
15
+ t.timestamps
16
+ end
17
+
18
+ add_index :postnhost_page_variants, %i[page_id language_id], unique: true
19
+ end
20
+ end
@@ -0,0 +1,154 @@
1
+ class CreateSnapshots < ActiveRecord::Migration[7.2]
2
+ def change
3
+ create_table :postnhost_article_snapshots do |t|
4
+ t.references :article, null: false, index: { unique: true },
5
+ foreign_key: { to_table: :postnhost_articles, on_delete: :cascade }
6
+ t.references :paper_trail_version, null: false, index: { unique: true },
7
+ foreign_key: { to_table: :postnhost_versions, on_delete: :restrict }
8
+ t.references :language, null: false, foreign_key: { to_table: :postnhost_languages }
9
+ t.string :title, null: false
10
+ t.string :title_tag
11
+ t.string :og_title
12
+ t.string :schema_headline
13
+ t.string :schema_article_type
14
+ t.string :meta_description
15
+ t.string :custom_excerpt
16
+ t.string :auto_excerpt
17
+ t.boolean :use_excerpt_as_meta_description, null: false, default: false
18
+ t.text :content, null: false
19
+ t.string :slug, null: false
20
+ t.string :cover_image_identifier
21
+ t.string :cover_image_alt
22
+ t.boolean :top_pick, null: false, default: false
23
+ t.datetime :published_at, null: false
24
+ t.timestamps null: false
25
+ end
26
+
27
+ add_index :postnhost_article_snapshots, :slug, unique: true
28
+ add_index :postnhost_article_snapshots, %i[language_id published_at],
29
+ name: "idx_postnhost_article_snapshots_language"
30
+ add_index :postnhost_article_snapshots, %i[top_pick language_id published_at],
31
+ name: "idx_postnhost_article_snapshots_top_picks"
32
+ add_index :postnhost_article_snapshots, :updated_at
33
+
34
+ create_table :postnhost_article_snapshot_categories do |t|
35
+ t.references :article_snapshot, null: false, index: { name: "idx_postnhost_snapshot_categories_article" },
36
+ foreign_key: { to_table: :postnhost_article_snapshots, on_delete: :cascade }
37
+ t.references :category, null: false, foreign_key: { to_table: :postnhost_categories }
38
+ t.timestamps null: false
39
+ end
40
+
41
+ add_index :postnhost_article_snapshot_categories, %i[article_snapshot_id category_id],
42
+ unique: true, name: "idx_postnhost_snapshot_categories_unique"
43
+ add_index :postnhost_article_snapshot_categories, %i[category_id article_snapshot_id],
44
+ name: "idx_postnhost_snapshot_categories_reverse"
45
+
46
+ create_table :postnhost_article_snapshot_authors do |t|
47
+ t.references :article_snapshot, null: false, index: { name: "idx_postnhost_snapshot_authors_article" },
48
+ foreign_key: { to_table: :postnhost_article_snapshots, on_delete: :cascade }
49
+ t.references :user, null: false, foreign_key: { to_table: :postnhost_users }
50
+ t.integer :position, null: false
51
+ t.timestamps null: false
52
+ end
53
+
54
+ add_index :postnhost_article_snapshot_authors, %i[article_snapshot_id user_id],
55
+ unique: true, name: "idx_postnhost_snapshot_authors_unique"
56
+ add_index :postnhost_article_snapshot_authors, %i[article_snapshot_id position],
57
+ unique: true, name: "idx_postnhost_snapshot_authors_position"
58
+ add_index :postnhost_article_snapshot_authors, %i[user_id article_snapshot_id],
59
+ name: "idx_postnhost_snapshot_authors_reverse"
60
+ add_check_constraint :postnhost_article_snapshot_authors, "position >= 0",
61
+ name: "postnhost_snapshot_author_position_nonnegative"
62
+
63
+ create_table :postnhost_article_snapshot_suggestions do |t|
64
+ t.references :article_snapshot, null: false, index: { name: "idx_postnhost_snapshot_suggestions_article" },
65
+ foreign_key: { to_table: :postnhost_article_snapshots, on_delete: :cascade }
66
+ t.references :suggested_article, null: false, index: { name: "idx_postnhost_snapshot_suggestions_target" },
67
+ foreign_key: { to_table: :postnhost_articles }
68
+ t.integer :position, null: false
69
+ t.timestamps null: false
70
+ end
71
+
72
+ add_index :postnhost_article_snapshot_suggestions, %i[article_snapshot_id suggested_article_id],
73
+ unique: true, name: "idx_postnhost_snapshot_suggestions_unique"
74
+ add_index :postnhost_article_snapshot_suggestions, %i[article_snapshot_id position],
75
+ unique: true, name: "idx_postnhost_snapshot_suggestions_position"
76
+ add_check_constraint :postnhost_article_snapshot_suggestions, "position >= 0",
77
+ name: "postnhost_snapshot_suggestion_position_nonnegative"
78
+
79
+ create_table :postnhost_article_variant_snapshots do |t|
80
+ t.references :article_variant, null: false,
81
+ index: { unique: true, name: "idx_postnhost_article_variant_snapshots_variant" },
82
+ foreign_key: { to_table: :postnhost_article_variants, on_delete: :cascade }
83
+ t.references :article, null: false, foreign_key: { to_table: :postnhost_articles }
84
+ t.references :paper_trail_version, null: false,
85
+ index: { unique: true, name: "idx_postnhost_article_variant_snapshots_version" },
86
+ foreign_key: { to_table: :postnhost_versions, on_delete: :restrict }
87
+ t.references :language, null: false, foreign_key: { to_table: :postnhost_languages }
88
+ t.string :title, null: false
89
+ t.string :title_tag
90
+ t.string :og_title
91
+ t.string :schema_headline
92
+ t.string :meta_description
93
+ t.string :custom_excerpt
94
+ t.string :auto_excerpt
95
+ t.boolean :use_excerpt_as_meta_description, null: false, default: false
96
+ t.text :content, null: false
97
+ t.datetime :published_at, null: false
98
+ t.timestamps null: false
99
+ end
100
+
101
+ add_index :postnhost_article_variant_snapshots, %i[article_id language_id], unique: true,
102
+ name: "idx_postnhost_article_variant_snapshots_unique"
103
+ add_index :postnhost_article_variant_snapshots, %i[language_id published_at],
104
+ name: "idx_postnhost_article_variant_snapshots_language"
105
+
106
+ create_table :postnhost_page_snapshots do |t|
107
+ t.references :page, null: false, index: { unique: true },
108
+ foreign_key: { to_table: :postnhost_pages, on_delete: :cascade }
109
+ t.references :paper_trail_version, null: false, index: { unique: true },
110
+ foreign_key: { to_table: :postnhost_versions, on_delete: :restrict }
111
+ t.references :language, null: false, foreign_key: { to_table: :postnhost_languages }
112
+ t.string :title, null: false
113
+ t.string :title_tag
114
+ t.string :og_title
115
+ t.string :meta_description
116
+ t.text :content, null: false
117
+ t.string :slug, null: false
118
+ t.datetime :published_at, null: false
119
+ t.timestamps null: false
120
+ end
121
+
122
+ add_index :postnhost_page_snapshots, :slug, unique: true
123
+ add_index :postnhost_page_snapshots, %i[language_id published_at], name: "idx_postnhost_page_snapshots_language"
124
+
125
+ create_table :postnhost_page_variant_snapshots do |t|
126
+ t.references :page_variant, null: false,
127
+ index: { unique: true, name: "idx_postnhost_page_variant_snapshots_variant" },
128
+ foreign_key: { to_table: :postnhost_page_variants, on_delete: :cascade }
129
+ t.references :page, null: false, foreign_key: { to_table: :postnhost_pages }
130
+ t.references :paper_trail_version, null: false,
131
+ index: { unique: true, name: "idx_postnhost_page_variant_snapshots_version" },
132
+ foreign_key: { to_table: :postnhost_versions, on_delete: :restrict }
133
+ t.references :language, null: false, foreign_key: { to_table: :postnhost_languages }
134
+ t.string :title, null: false
135
+ t.string :title_tag
136
+ t.string :og_title
137
+ t.string :meta_description
138
+ t.text :content, null: false
139
+ t.datetime :published_at, null: false
140
+ t.timestamps null: false
141
+ end
142
+
143
+ add_index :postnhost_page_variant_snapshots, %i[page_id language_id], unique: true,
144
+ name: "idx_postnhost_page_variant_snapshots_unique"
145
+ add_index :postnhost_page_variant_snapshots, %i[language_id published_at],
146
+ name: "idx_postnhost_page_variant_snapshots_language"
147
+
148
+ create_table :postnhost_public_site_revisions do |t|
149
+ t.bigint :revision, null: false, default: 0
150
+ t.integer :lock_version, null: false, default: 0
151
+ t.timestamps null: false
152
+ end
153
+ end
154
+ end