postnhost 0.0.0 → 0.1.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/{LICENSE.txt → LICENSE} +6 -6
- data/README.md +341 -15
- data/Rakefile +38 -6
- data/app/assets/builds/postnhost/application.css +2 -0
- data/app/assets/builds/postnhost/application.js +50190 -0
- data/app/assets/config/postnhost_manifest.js +2 -0
- data/app/assets/images/postnhost/demo.webp +0 -0
- data/app/assets/images/postnhost/icons/alert-circle.svg +3 -0
- data/app/assets/images/postnhost/icons/arrow-left-long.svg +3 -0
- data/app/assets/images/postnhost/icons/arrow-left.svg +3 -0
- data/app/assets/images/postnhost/icons/arrow-right-end-on-rectangle.svg +3 -0
- data/app/assets/images/postnhost/icons/arrow-right-long.svg +3 -0
- data/app/assets/images/postnhost/icons/arrow-right.svg +3 -0
- data/app/assets/images/postnhost/icons/bars.svg +3 -0
- data/app/assets/images/postnhost/icons/bluesky.svg +3 -0
- data/app/assets/images/postnhost/icons/check-circle.svg +3 -0
- data/app/assets/images/postnhost/icons/chevron-down-small.svg +3 -0
- data/app/assets/images/postnhost/icons/chevron-down.svg +3 -0
- data/app/assets/images/postnhost/icons/chevron-left.svg +3 -0
- data/app/assets/images/postnhost/icons/chevron-right.svg +3 -0
- data/app/assets/images/postnhost/icons/clock.svg +3 -0
- data/app/assets/images/postnhost/icons/close.svg +3 -0
- data/app/assets/images/postnhost/icons/code-bracket.svg +3 -0
- data/app/assets/images/postnhost/icons/document-text.svg +3 -0
- data/app/assets/images/postnhost/icons/drag-handle.svg +8 -0
- data/app/assets/images/postnhost/icons/edit.svg +3 -0
- data/app/assets/images/postnhost/icons/external-link.svg +3 -0
- data/app/assets/images/postnhost/icons/eye.svg +4 -0
- data/app/assets/images/postnhost/icons/facebook.svg +3 -0
- data/app/assets/images/postnhost/icons/folder.svg +3 -0
- data/app/assets/images/postnhost/icons/image.svg +3 -0
- data/app/assets/images/postnhost/icons/info-circle.svg +3 -0
- data/app/assets/images/postnhost/icons/instagram.svg +3 -0
- data/app/assets/images/postnhost/icons/language.svg +3 -0
- data/app/assets/images/postnhost/icons/layers.svg +3 -0
- data/app/assets/images/postnhost/icons/lightning.svg +3 -0
- data/app/assets/images/postnhost/icons/linkedin.svg +3 -0
- data/app/assets/images/postnhost/icons/logout.svg +3 -0
- data/app/assets/images/postnhost/icons/magnifying-glass.svg +3 -0
- data/app/assets/images/postnhost/icons/mastodon.svg +3 -0
- data/app/assets/images/postnhost/icons/menu.svg +3 -0
- data/app/assets/images/postnhost/icons/plus.svg +3 -0
- data/app/assets/images/postnhost/icons/schedule.svg +3 -0
- data/app/assets/images/postnhost/icons/settings.svg +4 -0
- data/app/assets/images/postnhost/icons/sparkles.svg +3 -0
- data/app/assets/images/postnhost/icons/spinner.svg +4 -0
- data/app/assets/images/postnhost/icons/threads.svg +3 -0
- data/app/assets/images/postnhost/icons/tiktok.svg +3 -0
- data/app/assets/images/postnhost/icons/tiptap/align-center.svg +3 -0
- data/app/assets/images/postnhost/icons/tiptap/align-left.svg +3 -0
- data/app/assets/images/postnhost/icons/tiptap/align-right.svg +3 -0
- data/app/assets/images/postnhost/icons/tiptap/apply-link.svg +3 -0
- data/app/assets/images/postnhost/icons/tiptap/blockquote.svg +3 -0
- data/app/assets/images/postnhost/icons/tiptap/bold.svg +3 -0
- data/app/assets/images/postnhost/icons/tiptap/bullet-list.svg +3 -0
- data/app/assets/images/postnhost/icons/tiptap/code-block.svg +3 -0
- data/app/assets/images/postnhost/icons/tiptap/code.svg +3 -0
- data/app/assets/images/postnhost/icons/tiptap/heading-2.svg +3 -0
- data/app/assets/images/postnhost/icons/tiptap/heading-3.svg +3 -0
- data/app/assets/images/postnhost/icons/tiptap/image-add.svg +3 -0
- data/app/assets/images/postnhost/icons/tiptap/italic.svg +3 -0
- data/app/assets/images/postnhost/icons/tiptap/link.svg +3 -0
- data/app/assets/images/postnhost/icons/tiptap/ordered-list.svg +3 -0
- data/app/assets/images/postnhost/icons/tiptap/redo.svg +3 -0
- data/app/assets/images/postnhost/icons/tiptap/remove-link.svg +3 -0
- data/app/assets/images/postnhost/icons/tiptap/strike.svg +3 -0
- data/app/assets/images/postnhost/icons/tiptap/table.svg +3 -0
- data/app/assets/images/postnhost/icons/tiptap/underline.svg +3 -0
- data/app/assets/images/postnhost/icons/tiptap/undo.svg +3 -0
- data/app/assets/images/postnhost/icons/tiptap/youtube.svg +4 -0
- data/app/assets/images/postnhost/icons/translate.svg +3 -0
- data/app/assets/images/postnhost/icons/trash.svg +3 -0
- data/app/assets/images/postnhost/icons/user.svg +4 -0
- data/app/assets/images/postnhost/icons/users.svg +3 -0
- data/app/assets/images/postnhost/icons/warning.svg +3 -0
- data/app/assets/images/postnhost/icons/x.svg +3 -0
- data/app/assets/images/postnhost/icons/youtube.svg +3 -0
- data/app/assets/images/postnhost/logo.webp +0 -0
- data/app/assets/images/postnhost/og-image.webp +0 -0
- data/app/assets/images/postnhost/sample-docs/08-how-to-control-suggested-articles-and-top-picks-01-discovery-controls.webp +0 -0
- data/app/assets/images/postnhost/sample-docs/09-all-about-authors-in-postnhost-01-author-profile.webp +0 -0
- data/app/assets/images/postnhost/sample-docs/09-all-about-authors-in-postnhost-02-multiple-authors.webp +0 -0
- data/app/assets/images/postnhost/sample-docs/11-how-to-schedule-an-article-for-publication-01-timezone.webp +0 -0
- data/app/assets/images/postnhost/sample-docs/11-how-to-schedule-an-article-for-publication-02-scheduled-article.webp +0 -0
- data/app/assets/images/postnhost/sample-docs/12-how-article-version-history-and-restores-work-01-version-history.webp +0 -0
- data/app/assets/images/postnhost/sample-docs/13-how-to-add-and-manage-languages-01-language-management.webp +0 -0
- data/app/assets/images/postnhost/sample-docs/14-how-to-translate-articles-pages-and-categories-01-translation-options.webp +0 -0
- data/app/assets/images/postnhost/sample-docs/14-how-to-translate-articles-pages-and-categories-02-ai-language-selection.webp +0 -0
- data/app/assets/images/postnhost/sample-docs/16-how-to-add-navigation-links-01-navigation-builder.webp +0 -0
- data/app/assets/images/postnhost/sample-docs/16-how-to-add-navigation-links-02-external-link-settings.webp +0 -0
- data/app/assets/images/postnhost/sample-docs/17-how-to-add-a-logo-and-default-og-image-01-assets-settings.webp +0 -0
- data/app/assets/images/postnhost/sample-docs/19-how-to-customize-public-text-for-each-language-01-i18n-overrides.webp +0 -0
- data/app/assets/images/postnhost/sample-docs/20-how-to-configure-structured-data-and-sitemap-seo-01-site-schema.webp +0 -0
- data/app/assets/images/postnhost/sample-docs/20-how-to-configure-structured-data-and-sitemap-seo-02-author-schema-defaults.webp +0 -0
- data/app/assets/images/postnhost/sample-docs/20-how-to-configure-structured-data-and-sitemap-seo-03-author-schema-advanced-fields.webp +0 -0
- data/app/assets/images/postnhost/sample-docs/covers/08-suggested-articles-and-top-picks.webp +0 -0
- data/app/assets/images/postnhost/sample-docs/covers/09-authors.webp +0 -0
- data/app/assets/images/postnhost/sample-docs/covers/11-scheduling.webp +0 -0
- data/app/assets/images/postnhost/sample-docs/covers/12-version-history.webp +0 -0
- data/app/assets/images/postnhost/sample-docs/covers/13-languages.webp +0 -0
- data/app/assets/images/postnhost/sample-docs/covers/14-translations.webp +0 -0
- data/app/assets/images/postnhost/sample-docs/covers/16-navigation-links.webp +0 -0
- data/app/assets/images/postnhost/sample-docs/covers/17-logo-and-default-og-image.webp +0 -0
- data/app/assets/images/postnhost/sample-docs/covers/19-public-text-for-each-language.webp +0 -0
- data/app/assets/images/postnhost/sample-docs/covers/20-structured-data-and-sitemap-seo.webp +0 -0
- data/app/assets/stylesheets/application.tailwind.css +343 -0
- data/app/assets/stylesheets/postnhost/application.css +15 -0
- data/app/assets/xsl/sitemap.xsl +64 -0
- data/app/controllers/concerns/postnhost/authentication.rb +59 -0
- data/app/controllers/concerns/postnhost/canonical_first_page_redirect.rb +14 -0
- data/app/controllers/concerns/postnhost/language_switcher.rb +42 -0
- data/app/controllers/concerns/postnhost/onboarding/access_control.rb +57 -0
- data/app/controllers/concerns/postnhost/onboarding/step_assignment.rb +55 -0
- data/app/controllers/concerns/postnhost/public_footer_data.rb +103 -0
- data/app/controllers/concerns/postnhost/public_http_caching.rb +35 -0
- data/app/controllers/concerns/postnhost/public_page_size.rb +17 -0
- data/app/controllers/concerns/postnhost/public_template_resolvable.rb +44 -0
- data/app/controllers/concerns/postnhost/settings/i18n_manageable.rb +61 -0
- data/app/controllers/concerns/postnhost/settings/navigation_manageable.rb +118 -0
- data/app/controllers/concerns/postnhost/settings/schema_manageable.rb +140 -0
- data/app/controllers/concerns/postnhost/users/schema_manageable.rb +119 -0
- data/app/controllers/postnhost/application_controller.rb +37 -0
- data/app/controllers/postnhost/article_variants_controller.rb +138 -0
- data/app/controllers/postnhost/articles/cover_images_controller.rb +35 -0
- data/app/controllers/postnhost/articles/images_controller.rb +28 -0
- data/app/controllers/postnhost/articles/translations_controller.rb +26 -0
- data/app/controllers/postnhost/articles_controller.rb +130 -0
- data/app/controllers/postnhost/categories/translations_controller.rb +24 -0
- data/app/controllers/postnhost/categories_controller.rb +59 -0
- data/app/controllers/postnhost/category_variants_controller.rb +69 -0
- data/app/controllers/postnhost/languages_controller.rb +66 -0
- data/app/controllers/postnhost/onboarding_controller.rb +70 -0
- data/app/controllers/postnhost/page_variants_controller.rb +136 -0
- data/app/controllers/postnhost/pages/images_controller.rb +28 -0
- data/app/controllers/postnhost/pages/translations_controller.rb +26 -0
- data/app/controllers/postnhost/pages_controller.rb +95 -0
- data/app/controllers/postnhost/public/articles_controller.rb +135 -0
- data/app/controllers/postnhost/public/authors_controller.rb +57 -0
- data/app/controllers/postnhost/public/categories_controller.rb +61 -0
- data/app/controllers/postnhost/public/static_pages_controller.rb +77 -0
- data/app/controllers/postnhost/public_controller.rb +32 -0
- data/app/controllers/postnhost/sessions_controller.rb +37 -0
- data/app/controllers/postnhost/settings/site_scripts_controller.rb +61 -0
- data/app/controllers/postnhost/settings_controller.rb +63 -0
- data/app/controllers/postnhost/sitemap_controller.rb +17 -0
- data/app/controllers/postnhost/templates_controller.rb +26 -0
- data/app/controllers/postnhost/users_controller.rb +112 -0
- data/app/helpers/postnhost/application_helper.rb +247 -0
- data/app/helpers/postnhost/article_helper.rb +41 -0
- data/app/helpers/postnhost/category_helper.rb +52 -0
- data/app/helpers/postnhost/common_css_helper.rb +97 -0
- data/app/helpers/postnhost/navigation_helper.rb +148 -0
- data/app/helpers/postnhost/pagy_helper.rb +103 -0
- data/app/helpers/postnhost/public_pages_helper.rb +287 -0
- data/app/helpers/postnhost/schema_helper.rb +102 -0
- data/app/helpers/postnhost/seo_helper.rb +428 -0
- data/app/helpers/postnhost/settings_helper.rb +79 -0
- data/app/javascript/application.js +3 -0
- data/app/javascript/controllers/ai_translation_controller.js +42 -0
- data/app/javascript/controllers/application.js +9 -0
- data/app/javascript/controllers/author_manager_controller.js +96 -0
- data/app/javascript/controllers/auto_save_controller.js +244 -0
- data/app/javascript/controllers/bulk_selection_controller.js +74 -0
- data/app/javascript/controllers/burger_controller.js +20 -0
- data/app/javascript/controllers/category_manager_controller.js +99 -0
- data/app/javascript/controllers/collapsible_sidebar_controller.js +87 -0
- data/app/javascript/controllers/cover_image_upload_controller.js +164 -0
- data/app/javascript/controllers/dropdown_controller.js +38 -0
- data/app/javascript/controllers/editor_mobile_sidebars_controller.js +36 -0
- data/app/javascript/controllers/file_auto_submit_controller.js +23 -0
- data/app/javascript/controllers/flash_message_controller.js +40 -0
- data/app/javascript/controllers/index.js +71 -0
- data/app/javascript/controllers/navigation_builder_controller.js +463 -0
- data/app/javascript/controllers/public_mobile_nav_controller.js +26 -0
- data/app/javascript/controllers/publish_guard_controller.js +47 -0
- data/app/javascript/controllers/reading_progress_controller.js +38 -0
- data/app/javascript/controllers/schedule_manager_controller.js +87 -0
- data/app/javascript/controllers/suggestion_manager_controller.js +99 -0
- data/app/javascript/controllers/template_preview_controller.js +34 -0
- data/app/javascript/controllers/tiptap_editor_controller.js +812 -0
- data/app/javascript/controllers/title_field_controller.js +17 -0
- data/app/javascript/helpers/auto_save_state.js +47 -0
- data/app/javascript/helpers/code_highlighting.js +48 -0
- data/app/javascript/helpers/dom_helpers.js +109 -0
- data/app/javascript/helpers/style_helpers.js +168 -0
- data/app/jobs/postnhost/application_job.rb +9 -0
- data/app/jobs/postnhost/scheduled_article_job.rb +13 -0
- data/app/jobs/postnhost/translation/article_variant_job.rb +17 -0
- data/app/jobs/postnhost/translation/category_variant_job.rb +17 -0
- data/app/jobs/postnhost/translation/page_variant_job.rb +17 -0
- data/app/models/concerns/postnhost/article/image_uploadable.rb +11 -0
- data/app/models/concerns/postnhost/excerpt_optimizable.rb +57 -0
- data/app/models/concerns/postnhost/inline_image_uploadable.rb +40 -0
- data/app/models/concerns/postnhost/page/image_uploadable.rb +11 -0
- data/app/models/concerns/postnhost/primary_language_guard.rb +18 -0
- data/app/models/concerns/postnhost/public_revision_touch.rb +15 -0
- data/app/models/concerns/postnhost/schema_localizable.rb +28 -0
- data/app/models/postnhost/application_record.rb +5 -0
- data/app/models/postnhost/article.rb +142 -0
- data/app/models/postnhost/article_author.rb +20 -0
- data/app/models/postnhost/article_category.rb +8 -0
- data/app/models/postnhost/article_suggestion.rb +19 -0
- data/app/models/postnhost/article_variant.rb +28 -0
- data/app/models/postnhost/category.rb +34 -0
- data/app/models/postnhost/category_variant.rb +24 -0
- data/app/models/postnhost/language.rb +38 -0
- data/app/models/postnhost/navigation.rb +76 -0
- data/app/models/postnhost/navigation_item.rb +100 -0
- data/app/models/postnhost/page.rb +63 -0
- data/app/models/postnhost/page_variant.rb +26 -0
- data/app/models/postnhost/public_site_revision.rb +24 -0
- data/app/models/postnhost/published_cover_image.rb +28 -0
- data/app/models/postnhost/setting.rb +116 -0
- data/app/models/postnhost/site_script.rb +11 -0
- data/app/models/postnhost/snapshot/article.rb +142 -0
- data/app/models/postnhost/snapshot/article_author.rb +11 -0
- data/app/models/postnhost/snapshot/article_category.rb +10 -0
- data/app/models/postnhost/snapshot/article_suggestion.rb +11 -0
- data/app/models/postnhost/snapshot/article_variant.rb +60 -0
- data/app/models/postnhost/snapshot/page.rb +33 -0
- data/app/models/postnhost/snapshot/page_variant.rb +41 -0
- data/app/models/postnhost/template.rb +20 -0
- data/app/models/postnhost/user.rb +63 -0
- data/app/models/postnhost/version.rb +7 -0
- data/app/services/postnhost/base_service.rb +17 -0
- data/app/services/postnhost/constraints/public_locale_constraint.rb +9 -0
- data/app/services/postnhost/constraints/public_route_constraint.rb +19 -0
- data/app/services/postnhost/onboarding/step_processor.rb +27 -0
- data/app/services/postnhost/onboarding/steps/base.rb +52 -0
- data/app/services/postnhost/onboarding/steps/choose_languages.rb +66 -0
- data/app/services/postnhost/onboarding/steps/complete_post_install_checklist.rb +12 -0
- data/app/services/postnhost/onboarding/steps/create_admin_account.rb +28 -0
- data/app/services/postnhost/onboarding/steps/finish_setup.rb +27 -0
- data/app/services/postnhost/onboarding/steps/set_site_metadata.rb +70 -0
- data/app/services/postnhost/public_navigation_targets.rb +67 -0
- data/app/services/postnhost/public_request_context.rb +101 -0
- data/app/services/postnhost/publishing/article_variants/publish.rb +26 -0
- data/app/services/postnhost/publishing/article_variants/publish_many.rb +23 -0
- data/app/services/postnhost/publishing/article_variants/snapshot_writer.rb +44 -0
- data/app/services/postnhost/publishing/article_variants/unpublish.rb +27 -0
- data/app/services/postnhost/publishing/article_variants/unpublish_many.rb +23 -0
- data/app/services/postnhost/publishing/articles/publish.rb +25 -0
- data/app/services/postnhost/publishing/articles/publish_scheduled.rb +42 -0
- data/app/services/postnhost/publishing/articles/snapshot_writer.rb +84 -0
- data/app/services/postnhost/publishing/articles/unpublish.rb +26 -0
- data/app/services/postnhost/publishing/bulk_transition.rb +63 -0
- data/app/services/postnhost/publishing/error.rb +12 -0
- data/app/services/postnhost/publishing/page_variants/publish.rb +26 -0
- data/app/services/postnhost/publishing/page_variants/publish_many.rb +23 -0
- data/app/services/postnhost/publishing/page_variants/snapshot_writer.rb +41 -0
- data/app/services/postnhost/publishing/page_variants/unpublish.rb +27 -0
- data/app/services/postnhost/publishing/page_variants/unpublish_many.rb +23 -0
- data/app/services/postnhost/publishing/pages/publish.rb +25 -0
- data/app/services/postnhost/publishing/pages/snapshot_writer.rb +38 -0
- data/app/services/postnhost/publishing/pages/unpublish.rb +26 -0
- data/app/services/postnhost/publishing/revision.rb +37 -0
- data/app/services/postnhost/publishing/route_validator.rb +41 -0
- data/app/services/postnhost/publishing/versions/restore_draft.rb +64 -0
- data/app/services/postnhost/service_result.rb +7 -0
- data/app/services/postnhost/sitemap_builder.rb +395 -0
- data/app/services/postnhost/translation/article_variant_translator.rb +102 -0
- data/app/services/postnhost/translation/category_variant_translator.rb +92 -0
- data/app/services/postnhost/translation/content_translator.rb +119 -0
- data/app/services/postnhost/translation/openai_llm_client.rb +30 -0
- data/app/services/postnhost/translation/page_variant_translator.rb +55 -0
- data/app/uploaders/base_uploader.rb +26 -0
- data/app/uploaders/cover_image_uploader.rb +31 -0
- data/app/uploaders/postnhost/avatar_uploader.rb +17 -0
- data/app/uploaders/postnhost/inline_article_image_uploader.rb +38 -0
- data/app/uploaders/postnhost/inline_page_image_uploader.rb +38 -0
- data/app/uploaders/postnhost/setting_asset_uploader.rb +18 -0
- data/app/views/layouts/postnhost/_favicon.html.erb +6 -0
- data/app/views/layouts/postnhost/_footer.html.erb +67 -0
- data/app/views/layouts/postnhost/_header.html.erb +89 -0
- data/app/views/layouts/postnhost/_sidebar.html.erb +72 -0
- data/app/views/layouts/postnhost/application.html.erb +23 -0
- data/app/views/layouts/postnhost/articles.html.erb +34 -0
- data/app/views/layouts/postnhost/product.html.erb +36 -0
- data/app/views/layouts/postnhost/public/templates/default.html.erb +110 -0
- data/app/views/layouts/postnhost/public/templates/swiss-editorial.html.erb +213 -0
- data/app/views/layouts/postnhost/public/templates/workspace-journal.html.erb +231 -0
- data/app/views/layouts/postnhost/public_header/_dashboard_sign_in.html.erb +14 -0
- data/app/views/layouts/postnhost/public_header/_language_switcher.html.erb +43 -0
- data/app/views/layouts/postnhost/public_header/_logo.html.erb +9 -0
- data/app/views/postnhost/article_variants/_variants_list.html.erb +82 -0
- data/app/views/postnhost/article_variants/edit.html.erb +48 -0
- data/app/views/postnhost/article_variants/index.html.erb +128 -0
- data/app/views/postnhost/article_variants/publish.turbo_stream.erb +3 -0
- data/app/views/postnhost/article_variants/unpublish.turbo_stream.erb +3 -0
- data/app/views/postnhost/article_variants/update.turbo_stream.erb +3 -0
- data/app/views/postnhost/articles/_author_manager.html.erb +38 -0
- data/app/views/postnhost/articles/_category_manager.html.erb +42 -0
- data/app/views/postnhost/articles/_cover_image_upload.html.erb +82 -0
- data/app/views/postnhost/articles/_suggestion_manager.html.erb +43 -0
- data/app/views/postnhost/articles/_version_snapshot.html.erb +10 -0
- data/app/views/postnhost/articles/edit.html.erb +51 -0
- data/app/views/postnhost/articles/index.html.erb +132 -0
- data/app/views/postnhost/articles/publish.turbo_stream.erb +3 -0
- data/app/views/postnhost/articles/unpublish.turbo_stream.erb +3 -0
- data/app/views/postnhost/articles/update.turbo_stream.erb +3 -0
- data/app/views/postnhost/articles/version_preview.html.erb +17 -0
- data/app/views/postnhost/articles/versions.html.erb +68 -0
- data/app/views/postnhost/categories/edit.html.erb +77 -0
- data/app/views/postnhost/categories/index.html.erb +80 -0
- data/app/views/postnhost/categories/new.html.erb +50 -0
- data/app/views/postnhost/categories/show.html.erb +99 -0
- data/app/views/postnhost/category_variants/_category_variants_list.html.erb +67 -0
- data/app/views/postnhost/category_variants/_form.html.erb +29 -0
- data/app/views/postnhost/category_variants/edit.html.erb +21 -0
- data/app/views/postnhost/category_variants/index.html.erb +82 -0
- data/app/views/postnhost/category_variants/new.html.erb +21 -0
- data/app/views/postnhost/languages/edit.html.erb +45 -0
- data/app/views/postnhost/languages/index.html.erb +86 -0
- data/app/views/postnhost/languages/new.html.erb +45 -0
- data/app/views/postnhost/languages/show.html.erb +156 -0
- data/app/views/postnhost/onboarding/_post_install_checklist.html.erb +40 -0
- data/app/views/postnhost/onboarding/_step_1.html.erb +42 -0
- data/app/views/postnhost/onboarding/_step_2.html.erb +38 -0
- data/app/views/postnhost/onboarding/_step_3.html.erb +62 -0
- data/app/views/postnhost/onboarding/_step_4.html.erb +26 -0
- data/app/views/postnhost/onboarding/show.html.erb +51 -0
- data/app/views/postnhost/page_variants/_variants_list.html.erb +82 -0
- data/app/views/postnhost/page_variants/edit.html.erb +50 -0
- data/app/views/postnhost/page_variants/index.html.erb +128 -0
- data/app/views/postnhost/page_variants/publish.turbo_stream.erb +3 -0
- data/app/views/postnhost/page_variants/unpublish.turbo_stream.erb +3 -0
- data/app/views/postnhost/page_variants/update.turbo_stream.erb +3 -0
- data/app/views/postnhost/pages/edit.html.erb +50 -0
- data/app/views/postnhost/pages/index.html.erb +110 -0
- data/app/views/postnhost/pages/publish.turbo_stream.erb +3 -0
- data/app/views/postnhost/pages/unpublish.turbo_stream.erb +3 -0
- data/app/views/postnhost/public/articles/content_for/_index.html.erb +30 -0
- data/app/views/postnhost/public/articles/content_for/_preview.html.erb +9 -0
- data/app/views/postnhost/public/articles/content_for/_show.html.erb +24 -0
- data/app/views/postnhost/public/authors/content_for/_show.html.erb +24 -0
- data/app/views/postnhost/public/categories/content_for/_index.html.erb +29 -0
- data/app/views/postnhost/public/static_pages/content_for/_show.html.erb +19 -0
- data/app/views/postnhost/public/templates/default/articles/_article_card.html.erb +37 -0
- data/app/views/postnhost/public/templates/default/articles/_search_form.html.erb +12 -0
- data/app/views/postnhost/public/templates/default/articles/_suggestions.html.erb +44 -0
- data/app/views/postnhost/public/templates/default/articles/_top_picks.html.erb +42 -0
- data/app/views/postnhost/public/templates/default/articles/index.html.erb +53 -0
- data/app/views/postnhost/public/templates/default/articles/preview.html.erb +102 -0
- data/app/views/postnhost/public/templates/default/articles/show.html.erb +111 -0
- data/app/views/postnhost/public/templates/default/authors/_article_card.html.erb +28 -0
- data/app/views/postnhost/public/templates/default/authors/_profile_block.html.erb +29 -0
- data/app/views/postnhost/public/templates/default/authors/show.html.erb +36 -0
- data/app/views/postnhost/public/templates/default/categories/_article_card.html.erb +34 -0
- data/app/views/postnhost/public/templates/default/categories/index.html.erb +48 -0
- data/app/views/postnhost/public/templates/default/shared/_article_authors.html.erb +24 -0
- data/app/views/postnhost/public/templates/default/shared/_author_avatar.html.erb +9 -0
- data/app/views/postnhost/public/templates/default/shared/_hero_cover_image.html.erb +11 -0
- data/app/views/postnhost/public/templates/default/shared/_pagination.html.erb +35 -0
- data/app/views/postnhost/public/templates/default/shared/_thumbnail_cover_image.html.erb +9 -0
- data/app/views/postnhost/public/templates/default/static_pages/show.html.erb +22 -0
- data/app/views/postnhost/public/templates/swiss-editorial/articles/_article_card.html.erb +28 -0
- data/app/views/postnhost/public/templates/swiss-editorial/articles/_search_form.html.erb +19 -0
- data/app/views/postnhost/public/templates/swiss-editorial/articles/_suggestions.html.erb +42 -0
- data/app/views/postnhost/public/templates/swiss-editorial/articles/_top_picks.html.erb +38 -0
- data/app/views/postnhost/public/templates/swiss-editorial/articles/index.html.erb +133 -0
- data/app/views/postnhost/public/templates/swiss-editorial/articles/preview.html.erb +42 -0
- data/app/views/postnhost/public/templates/swiss-editorial/articles/show.html.erb +100 -0
- data/app/views/postnhost/public/templates/swiss-editorial/authors/_article_card.html.erb +27 -0
- data/app/views/postnhost/public/templates/swiss-editorial/authors/_profile_block.html.erb +32 -0
- data/app/views/postnhost/public/templates/swiss-editorial/authors/show.html.erb +31 -0
- data/app/views/postnhost/public/templates/swiss-editorial/categories/_article_card.html.erb +28 -0
- data/app/views/postnhost/public/templates/swiss-editorial/categories/index.html.erb +88 -0
- data/app/views/postnhost/public/templates/swiss-editorial/shared/_article_authors.html.erb +26 -0
- data/app/views/postnhost/public/templates/swiss-editorial/shared/_author_avatar.html.erb +9 -0
- data/app/views/postnhost/public/templates/swiss-editorial/shared/_hero_cover_image.html.erb +11 -0
- data/app/views/postnhost/public/templates/swiss-editorial/shared/_pagination.html.erb +35 -0
- data/app/views/postnhost/public/templates/swiss-editorial/shared/_thumbnail_cover_image.html.erb +9 -0
- data/app/views/postnhost/public/templates/swiss-editorial/static_pages/show.html.erb +17 -0
- data/app/views/postnhost/public/templates/workspace-journal/articles/_article_card.html.erb +44 -0
- data/app/views/postnhost/public/templates/workspace-journal/articles/_search_form.html.erb +19 -0
- data/app/views/postnhost/public/templates/workspace-journal/articles/_suggestions.html.erb +49 -0
- data/app/views/postnhost/public/templates/workspace-journal/articles/_top_picks.html.erb +34 -0
- data/app/views/postnhost/public/templates/workspace-journal/articles/index.html.erb +69 -0
- data/app/views/postnhost/public/templates/workspace-journal/articles/preview.html.erb +113 -0
- data/app/views/postnhost/public/templates/workspace-journal/articles/show.html.erb +120 -0
- data/app/views/postnhost/public/templates/workspace-journal/authors/_article_card.html.erb +1 -0
- data/app/views/postnhost/public/templates/workspace-journal/authors/_profile_block.html.erb +32 -0
- data/app/views/postnhost/public/templates/workspace-journal/authors/show.html.erb +33 -0
- data/app/views/postnhost/public/templates/workspace-journal/categories/_article_card.html.erb +1 -0
- data/app/views/postnhost/public/templates/workspace-journal/categories/index.html.erb +58 -0
- data/app/views/postnhost/public/templates/workspace-journal/shared/_article_authors.html.erb +27 -0
- data/app/views/postnhost/public/templates/workspace-journal/shared/_article_card.html.erb +44 -0
- data/app/views/postnhost/public/templates/workspace-journal/shared/_author_avatar.html.erb +9 -0
- data/app/views/postnhost/public/templates/workspace-journal/shared/_category_navigation.html.erb +10 -0
- data/app/views/postnhost/public/templates/workspace-journal/shared/_hero_cover_image.html.erb +11 -0
- data/app/views/postnhost/public/templates/workspace-journal/shared/_pagination.html.erb +35 -0
- data/app/views/postnhost/public/templates/workspace-journal/shared/_thumbnail_cover_image.html.erb +9 -0
- data/app/views/postnhost/public/templates/workspace-journal/static_pages/show.html.erb +17 -0
- data/app/views/postnhost/sessions/new.html.erb +59 -0
- data/app/views/postnhost/settings/edit.html.erb +68 -0
- data/app/views/postnhost/settings/sections/_assets.html.erb +40 -0
- data/app/views/postnhost/settings/sections/_feature_flags.html.erb +43 -0
- data/app/views/postnhost/settings/sections/_i18n.html.erb +57 -0
- data/app/views/postnhost/settings/sections/_navigation.html.erb +98 -0
- data/app/views/postnhost/settings/sections/_schema.html.erb +253 -0
- data/app/views/postnhost/settings/sections/_scripts.html.erb +79 -0
- data/app/views/postnhost/settings/sections/_site.html.erb +41 -0
- data/app/views/postnhost/settings/sections/_timezone.html.erb +19 -0
- data/app/views/postnhost/shared/_ai_translation_modal.html.erb +85 -0
- data/app/views/postnhost/shared/_flash_messages.html.erb +27 -0
- data/app/views/postnhost/shared/_form_errors.html.erb +22 -0
- data/app/views/postnhost/shared/_seo_meta_tags.html.erb +70 -0
- data/app/views/postnhost/shared/_structured_data.html.erb +5 -0
- data/app/views/postnhost/shared/articles/_action_buttons.html.erb +87 -0
- data/app/views/postnhost/shared/articles/_cover_image_preview.html.erb +30 -0
- data/app/views/postnhost/shared/articles/_editor_column.html.erb +15 -0
- data/app/views/postnhost/shared/articles/_editor_column_preview.html.erb +33 -0
- data/app/views/postnhost/shared/articles/_error_messages.html.erb +23 -0
- data/app/views/postnhost/shared/articles/_meta_sidebar.html.erb +322 -0
- data/app/views/postnhost/shared/articles/_meta_sidebar_preview.html.erb +29 -0
- data/app/views/postnhost/shared/articles/_post_actions.html.erb +105 -0
- data/app/views/postnhost/shared/articles/_schedule_actions.html.erb +51 -0
- data/app/views/postnhost/shared/articles/_tiptap_editor.html.erb +225 -0
- data/app/views/postnhost/shared/articles/_title_field.html.erb +9 -0
- data/app/views/postnhost/shared/articles/_version_action_buttons.html.erb +35 -0
- data/app/views/postnhost/shared/elements/_button.html.erb +20 -0
- data/app/views/postnhost/shared/elements/_button_link.html.erb +24 -0
- data/app/views/postnhost/shared/elements/_empty_state.html.erb +22 -0
- data/app/views/postnhost/shared/elements/_error_messages.html.erb +28 -0
- data/app/views/postnhost/shared/elements/_form_card.html.erb +10 -0
- data/app/views/postnhost/shared/elements/_form_page_header.html.erb +21 -0
- data/app/views/postnhost/shared/elements/_icon_button.html.erb +17 -0
- data/app/views/postnhost/shared/elements/_page_header.html.erb +40 -0
- data/app/views/postnhost/shared/elements/_table_container.html.erb +23 -0
- data/app/views/postnhost/shared/pages/_action_buttons.html.erb +84 -0
- data/app/views/postnhost/shared/pages/_meta_sidebar.html.erb +132 -0
- data/app/views/postnhost/shared/pages/_post_actions.html.erb +69 -0
- data/app/views/postnhost/templates/edit.html.erb +83 -0
- data/app/views/postnhost/users/_avatar.html.erb +9 -0
- data/app/views/postnhost/users/_form.html.erb +160 -0
- data/app/views/postnhost/users/edit.html.erb +26 -0
- data/app/views/postnhost/users/index.html.erb +91 -0
- data/app/views/postnhost/users/new.html.erb +9 -0
- data/app/views/postnhost/users/schema_edit.html.erb +175 -0
- data/config/locales/de.yml +55 -0
- data/config/locales/en.yml +88 -0
- data/config/locales/es.yml +58 -0
- data/config/locales/fr.yml +55 -0
- data/config/locales/ja.yml +55 -0
- data/config/locales/ko.yml +55 -0
- data/config/locales/pl.yml +55 -0
- data/config/locales/pt.yml +55 -0
- data/config/locales/ru.yml +55 -0
- data/config/routes.rb +112 -0
- data/db/migrate/20250822213820_create_users.rb +29 -0
- data/db/migrate/20250822214046_create_categories.rb +15 -0
- data/db/migrate/20250822214106_create_languages.rb +16 -0
- data/db/migrate/20250822214148_create_articles.rb +34 -0
- data/db/migrate/20250822214217_create_article_variants.rb +24 -0
- data/db/migrate/20250929091137_create_article_categories.rb +12 -0
- data/db/migrate/20251013204200_create_versions.rb +40 -0
- data/db/migrate/20260106074532_create_article_suggestions.rb +13 -0
- data/db/migrate/20260108110506_create_category_variants.rb +15 -0
- data/db/migrate/20260308130000_create_settings.rb +23 -0
- data/db/migrate/20260323113000_create_pages.rb +19 -0
- data/db/migrate/20260414100000_create_article_authors.rb +14 -0
- data/db/migrate/20260512100000_create_templates.rb +9 -0
- data/db/migrate/20260520110001_create_navigations.rb +9 -0
- data/db/migrate/20260520110002_create_navigation_items.rb +22 -0
- data/db/migrate/20260528000002_create_page_variants.rb +20 -0
- data/db/migrate/20260709090000_create_snapshots.rb +154 -0
- data/db/migrate/20260726130000_create_site_scripts.rb +11 -0
- data/lib/generators/postnhost/install/install_generator.rb +56 -0
- data/lib/generators/postnhost/install/templates/carrierwave.rb +17 -0
- data/lib/generators/postnhost/install/templates/postnhost.rb +36 -0
- data/lib/generators/postnhost/locale/locale_generator.rb +36 -0
- data/lib/generators/postnhost/static_pages/static_pages_generator.rb +47 -0
- data/lib/generators/postnhost/tailwindcss/install_generator.rb +97 -0
- data/lib/generators/postnhost/tailwindcss/templates/host.tailwind.css +7 -0
- data/lib/generators/postnhost/tailwindcss/templates/tailwind.config.js +8 -0
- data/lib/generators/postnhost/user/user_generator.rb +36 -0
- data/lib/generators/postnhost/views/views_generator.rb +142 -0
- data/lib/postnhost/configuration.rb +64 -0
- data/lib/postnhost/engine.rb +57 -0
- data/lib/postnhost/sample_data.rb +865 -0
- data/lib/postnhost/settings/i18n_overrides.rb +137 -0
- data/lib/postnhost/settings/i18n_patch.rb +18 -0
- data/lib/postnhost/version.rb +1 -3
- data/lib/postnhost.rb +26 -3
- data/lib/tasks/postnhost_snapshots.rake +77 -0
- data/lib/tasks/postnhost_tasks.rake +96 -0
- metadata +662 -21
- data/.github/workflows/main.yml +0 -27
- data/.gitignore +0 -11
- data/.rspec +0 -3
- data/.rubocop.yml +0 -8
- data/Gemfile +0 -13
- data/bin/console +0 -11
- data/bin/setup +0 -8
- data/postnhost.gemspec +0 -31
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f12e20da143bbf828f95af42951dcfc1bbce4e9152dd47630ea8a668b73b8a6b
|
|
4
|
+
data.tar.gz: 8ae19423f07e68ec8fef6ba7a4014751b25b658e218a939bf802157247b96c05
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c40643ed0fd03ec5bc35a717c08a50a7942f9b64df50e397ab4de7217a46090c60d3ae2f25684fb8c5629bb264a1fb4ca87ab6119a819512db90eb84d0f6b2ff
|
|
7
|
+
data.tar.gz: af8cd76fde91eaad7ac5c175721ca1b7b5a26a80caad882fcfe404ce5bd486252a57dd3681ed108fa51d90d8c500c969e05863b234c4034804502937971186db
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to PostnHost are documented in this file.
|
|
4
|
+
|
|
5
|
+
## [0.1.0] - 2026-08-29
|
|
6
|
+
|
|
7
|
+
First open source release of PostnHost.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Mountable Rails CMS engine with a built-in dashboard, onboarding, session-based authentication, and user management.
|
|
12
|
+
- Draft and snapshot-based publishing for articles, pages, and translated variants, including scheduled publishing, unpublishing, bulk transitions, and version restoration.
|
|
13
|
+
- Multilingual content, localized routes, language switching, locale-aware metadata, and optional OpenAI-assisted translations.
|
|
14
|
+
- Rich text editing, image uploads and processing, categories, multiple authors, article suggestions, navigation management, static pages, and site-wide settings.
|
|
15
|
+
- Public search, sitemaps, canonical URLs, hreflang tags, structured metadata, and HTTP and fragment caching with publication-aware invalidation.
|
|
16
|
+
- Three packaged public templates: Default, Swiss Editorial, and Workspace Journal.
|
|
17
|
+
- Generators for installation, CMS users, public view overrides, locales, static pages, and host Tailwind integration.
|
|
18
|
+
- Precompiled JavaScript and CSS assets with Propshaft and Sprockets support, requiring no frontend toolchain in host applications.
|
|
19
|
+
- Support for Ruby 3.4 and 4.0 with Rails 7.2, 8.0, and 8.1.
|
|
20
|
+
|
|
21
|
+
### Credits
|
|
22
|
+
|
|
23
|
+
Authored by Kirill Shevchenko (Ruby/Rails engineering) and Maxim Sova (technical SEO expertise).
|
|
24
|
+
|
|
25
|
+
[0.1.0]: https://github.com/postnhost/postnhost/releases/tag/v0.1.0
|
data/{LICENSE.txt → LICENSE}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2025 Kirill Shevchenko
|
|
3
|
+
Copyright (c) 2025-2026 Kirill Shevchenko and Maxim Sova
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
9
9
|
copies of the Software, and to permit persons to whom the Software is
|
|
10
10
|
furnished to do so, subject to the following conditions:
|
|
11
11
|
|
|
12
|
-
The above copyright notice and this permission notice shall be included in
|
|
13
|
-
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
14
|
|
|
15
15
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
16
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
17
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
18
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
-
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
CHANGED
|
@@ -1,39 +1,365 @@
|
|
|
1
|
-
|
|
1
|
+
<h1 align="center" style="border-bottom: none">
|
|
2
|
+
<div>
|
|
3
|
+
<a href="https://postnhost.com">
|
|
4
|
+
<img alt="PostnHost" src="app/assets/images/postnhost/logo.webp" width="200" />
|
|
5
|
+
<br>
|
|
6
|
+
</a>
|
|
7
|
+
</div>
|
|
8
|
+
</h1>
|
|
9
|
+
<h3 align="center">
|
|
10
|
+
SEO-ready multilanguage content engine for Rails.
|
|
11
|
+
</h3>
|
|
12
|
+
<p align="left">
|
|
13
|
+
PostnHost is an open source CMS engine built with Rails, Hotwire, and TailwindCSS. Write, translate, and publish articles with a rich text editor, full version history, and proper SEO that works out of the box.
|
|
14
|
+
</p>
|
|
15
|
+
<p align="left">
|
|
16
|
+
Mount this engine into an existing Rails app, or use the <a href="https://github.com/postnhost/postnhost-app">postnhost-app</a> repository for a pre-configured, self-hosted Rails application.
|
|
17
|
+
</p>
|
|
2
18
|
|
|
3
|
-
TODO: Delete this and the text below, and describe your gem
|
|
4
19
|
|
|
5
|
-
|
|
20
|
+

|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
- 📝 **Rich Text Editor** - TipTap-based WYSIWYG editor
|
|
25
|
+
- 🌍 **Multilingual** - Translatable articles, i18n support, and locale-aware SEO metadata
|
|
26
|
+
- 🖼️ **Image Management** - Optimized multisize WebP everywhere
|
|
27
|
+
- 📊 **Version History** - Published version history and rollback options
|
|
28
|
+
- 🤖 **Suggested Articles** - Manual picks plus automatic suggestions from related categories
|
|
29
|
+
- ⏰ **Post Scheduling** - Schedule publication in the chosen timezone
|
|
30
|
+
- 🌐 **Localized SEO** - Localized routes, language switcher, sitemap, and hreflang tags
|
|
31
|
+
- ⚙️ **Settings** - Manage the canonical site URL, pagination, key copy, and assets from the dashboard
|
|
32
|
+
- 🎨 **Customizable Templates** - Override any public view in your host application
|
|
33
|
+
- ⚡ **Hotwire-powered** - Fast, modern UI with Turbo Frames/Streams and Stimulus
|
|
34
|
+
- 👥 **Authors** - Multiple CMS users, author profiles, and per-article co-author bylines
|
|
35
|
+
|
|
36
|
+
## Table of contents
|
|
37
|
+
|
|
38
|
+
- [Installation](#installation)
|
|
39
|
+
- [Asset compatibility](#asset-compatibility)
|
|
40
|
+
- [Configuration](#configuration)
|
|
41
|
+
- [Customizing templates](#customizing-templates)
|
|
42
|
+
- [Replacing favicon](#replacing-favicon)
|
|
43
|
+
- [Host app i18n](#host-app-i18n-optional)
|
|
44
|
+
- [Static pages](#static-pages-terms-privacy-etc)
|
|
45
|
+
- [Routes](#routes)
|
|
46
|
+
- [Manual configuration](#manual-configuration)
|
|
47
|
+
- [Development](#development)
|
|
48
|
+
- [License](#license)
|
|
6
49
|
|
|
7
50
|
## Installation
|
|
8
51
|
|
|
9
|
-
|
|
52
|
+
### Requirements
|
|
53
|
+
|
|
54
|
+
- Ruby 3.4+
|
|
55
|
+
- libvips for image processing
|
|
56
|
+
|
|
57
|
+
Add PostnHost to your host application's Gemfile:
|
|
58
|
+
|
|
59
|
+
```ruby
|
|
60
|
+
gem "postnhost"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Run the installer:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
bundle install
|
|
67
|
+
bin/rails g postnhost:install
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
This will:
|
|
71
|
+
- Create `config/initializers/postnhost.rb` with configuration options
|
|
72
|
+
- Create `config/initializers/carrierwave.rb` (if not exists)
|
|
73
|
+
- Mount the engine in your routes
|
|
74
|
+
|
|
75
|
+
Run migrations:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
bin/rails postnhost:install:migrations
|
|
79
|
+
bin/rails db:migrate
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### First-time CMS setup
|
|
83
|
+
|
|
84
|
+
If there are no users yet, visit **`/blog/onboarding`** to create the first CMS administrator with credentials you choose. This is the default installer path; if you mount the engine somewhere else, open `<mount-path>/onboarding`. The setup flow can add optional sample content after the account is created.
|
|
85
|
+
|
|
86
|
+
Alternatively, create a CMS user interactively from the terminal:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
bin/rails g postnhost:user
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The generator prompts for the name, email, password, and password confirmation.
|
|
93
|
+
|
|
94
|
+
## Asset compatibility
|
|
95
|
+
|
|
96
|
+
PostnHost ships precompiled CSS and a prebundled JavaScript ES module. The engine serves these files through Rails asset helpers, so the host application does not need to compile PostnHost's JavaScript or install its frontend dependencies.
|
|
97
|
+
|
|
98
|
+
| Host setup | Compatible | How PostnHost integrates |
|
|
99
|
+
| --- | --- | --- |
|
|
100
|
+
| Propshaft | ✅ | The engine registers its packaged builds and images with the host asset pipeline. |
|
|
101
|
+
| Sprockets | ✅ | The engine registers its packaged builds and images for precompilation. |
|
|
102
|
+
| `importmap-rails` | ✅ | The engine bundle is loaded separately from the host import map. No pins are required. |
|
|
103
|
+
| `jsbundling-rails` with esbuild | ✅ | The engine uses its packaged bundle and does not need to be added to the host esbuild entry point. |
|
|
104
|
+
| `cssbundling-rails` or host Tailwind CSS | ✅ | The packaged engine CSS works independently. Use PostnHost host-tailwind mode only when copied views introduce new Tailwind classes. |
|
|
105
|
+
|
|
106
|
+
A host application must use Propshaft or Sprockets to serve the packaged engine assets, and either pipeline can be paired with import maps or esbuild.
|
|
107
|
+
|
|
108
|
+
## Configuration
|
|
10
109
|
|
|
11
|
-
|
|
110
|
+
### Initializer (`Postnhost.configure`)
|
|
111
|
+
|
|
112
|
+
Edit `config/initializers/postnhost.rb`:
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
Postnhost.configure do |config|
|
|
116
|
+
# Site defaults used when the matching Dashboard → Settings field is blank
|
|
117
|
+
# config.site_url = 'https://example.com'
|
|
118
|
+
# config.public_page_size = 12
|
|
119
|
+
# config.default_timezone = 'UTC'
|
|
120
|
+
|
|
121
|
+
# Optional: required only for AI-assisted translations
|
|
122
|
+
# config.openai_api_key = 'sk-...'
|
|
123
|
+
# config.openai_gpt_model = 'gpt-5.6-luna'
|
|
124
|
+
|
|
125
|
+
# Required by the generated CarrierWave initializer in production
|
|
126
|
+
# config.aws_access_key_id = 'AKIA...'
|
|
127
|
+
# config.aws_secret_access_key = '...'
|
|
128
|
+
# config.aws_region = 'us-east-1'
|
|
129
|
+
# config.aws_bucket = 'my-bucket'
|
|
130
|
+
# config.aws_endpoint_url_s3 = 'https://fly.storage.tigris.dev'
|
|
131
|
+
end
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Site URL, pagination, and timezone values are optional defaults; values saved under **Dashboard → Settings** take priority. The OpenAI settings are optional and needed only for AI translations.
|
|
135
|
+
|
|
136
|
+
The generated CarrierWave initializer uses S3-compatible storage in production. Configure the relevant AWS/S3 values above before deploying; `aws_endpoint_url_s3` is needed only for providers that use a custom endpoint. Alternatively, edit `config/initializers/carrierwave.rb` to use a different production storage backend.
|
|
137
|
+
|
|
138
|
+
Set dashboard overrides for the canonical site URL and public articles per page under **Dashboard → Settings → Site**. When both site URL sources are blank, public URLs use the incoming request origin.
|
|
139
|
+
|
|
140
|
+
### Rails credentials (alternative)
|
|
141
|
+
|
|
142
|
+
Instead of defining values in the initializer, you can add them under `postnhost:` in Rails credentials (`bin/rails credentials:edit`).
|
|
143
|
+
|
|
144
|
+
```yaml
|
|
145
|
+
postnhost:
|
|
146
|
+
# For AI translations
|
|
147
|
+
openai_access_token: ...
|
|
148
|
+
openai_gpt_model: gpt-5.6-luna
|
|
149
|
+
|
|
150
|
+
# For image uploads (S3-compatible)
|
|
151
|
+
aws_access_key_id: AKIA...
|
|
152
|
+
aws_secret_access_key: ...
|
|
153
|
+
aws_region: us-east-1
|
|
154
|
+
aws_bucket_name: my-bucket
|
|
155
|
+
aws_endpoint_url_s3: https://s3.us-east-1.amazonaws.com # or Tigris/other S3-compatible
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Customizing templates
|
|
159
|
+
|
|
160
|
+
PostnHost includes three public templates: **Default**, **Swiss Editorial**, and **Workspace Journal**. Open **Template** in the CMS, choose a template, preview it, and save the selection.
|
|
161
|
+
|
|
162
|
+
Copy views to your application for manual/vibecoding customization:
|
|
12
163
|
|
|
13
164
|
```bash
|
|
14
|
-
|
|
165
|
+
bin/rails g postnhost:views --views-scope=minimal
|
|
166
|
+
# or
|
|
167
|
+
bin/rails g postnhost:views --views-scope=full
|
|
15
168
|
```
|
|
16
169
|
|
|
17
|
-
|
|
170
|
+
Host-application copies override the packaged engine views. The CMS selection still controls which template is active.
|
|
171
|
+
|
|
172
|
+
By default, PostnHost serves packaged engine CSS (`postnhost/application.css`) for zero-setup installs.
|
|
173
|
+
If your copied views introduce new Tailwind utility classes, enable host-tailwind mode so those classes are compiled in the host app.
|
|
174
|
+
|
|
175
|
+
`--views-scope=minimal`:
|
|
176
|
+
- Copies the Default public content templates under `app/views/postnhost/public/templates/default/`
|
|
177
|
+
- Copies shared public layout partials such as the footer, favicon, logo, and language switcher
|
|
178
|
+
- Excludes internal metadata partials under `content_for/`
|
|
179
|
+
- Keeps SEO/meta internals in the engine by default
|
|
180
|
+
|
|
181
|
+
`--views-scope=full`:
|
|
182
|
+
- Copies all three public content template sets under `app/views/postnhost/public/templates/`
|
|
183
|
+
- Copies their layouts under `app/views/layouts/postnhost/public/templates/`
|
|
184
|
+
- Copies metadata, structured-data, and SEO partials
|
|
185
|
+
|
|
186
|
+
### Host Tailwind mode (for custom classes in overridden views)
|
|
187
|
+
|
|
188
|
+
Enable host-tailwind mode in your host app:
|
|
18
189
|
|
|
19
190
|
```bash
|
|
20
|
-
|
|
191
|
+
bin/rails g postnhost:tailwindcss:install
|
|
192
|
+
bin/dev
|
|
21
193
|
```
|
|
22
194
|
|
|
23
|
-
|
|
195
|
+
This generator creates:
|
|
196
|
+
|
|
197
|
+
- `postnhost.tailwind.config.js`
|
|
198
|
+
- `app/assets/stylesheets/postnhost/host.tailwind.css`
|
|
199
|
+
- `Procfile.dev` watcher entry:
|
|
200
|
+
- `postnhost_css: bundle exec rails postnhost:tailwindcss:watch`
|
|
201
|
+
- optional `package.json` scripts (if `package.json` exists):
|
|
202
|
+
- `postnhost:tailwindcss`
|
|
203
|
+
- `postnhost:tailwindcss:watch`
|
|
204
|
+
|
|
205
|
+
When host-tailwind mode files are present, PostnHost layouts automatically include `postnhost/host.css` after `postnhost/application.css`.
|
|
206
|
+
You do not need to edit layout tags.
|
|
207
|
+
|
|
208
|
+
### Robots.txt sitemap URL (required)
|
|
209
|
+
|
|
210
|
+
Update the host app `public/robots.txt` so it points to the correct PostnHost sitemap URL.
|
|
211
|
+
|
|
212
|
+
PostnHost generates `sitemap.xml` automatically from live public articles and pages.
|
|
213
|
+
|
|
214
|
+
If the engine is mounted at `/`:
|
|
215
|
+
|
|
216
|
+
```txt
|
|
217
|
+
Sitemap: https://your-domain.com/sitemap.xml
|
|
218
|
+
```
|
|
24
219
|
|
|
25
|
-
|
|
220
|
+
If the engine is mounted at `/blog`:
|
|
221
|
+
|
|
222
|
+
```txt
|
|
223
|
+
Sitemap: https://your-domain.com/blog/sitemap.xml
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Set the canonical production origin under **Dashboard → Settings → Site** or with `config.site_url` so sitemap URLs are generated with the correct host.
|
|
227
|
+
|
|
228
|
+
### Replacing favicon
|
|
229
|
+
|
|
230
|
+
PostnHost renders favicon tags from `app/views/layouts/postnhost/_favicon.html.erb` and points to root-level public paths:
|
|
231
|
+
|
|
232
|
+
- `/favicon.ico`
|
|
233
|
+
- `/icon.svg`
|
|
234
|
+
- `/apple-touch-icon.png`
|
|
235
|
+
- `/site.webmanifest`
|
|
236
|
+
|
|
237
|
+
Because these are absolute paths, they are served from the **host app** `public/` directory.
|
|
238
|
+
Replace the files in your host app:
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
# From host app root
|
|
242
|
+
cp /path/to/your/favicon.ico public/favicon.ico
|
|
243
|
+
cp /path/to/your/icon.svg public/icon.svg
|
|
244
|
+
cp /path/to/your/apple-touch-icon.png public/apple-touch-icon.png
|
|
245
|
+
cp /path/to/your/site.webmanifest public/site.webmanifest
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
If you need different markup or paths, run either `postnhost:views` command from the template-customization section, then edit:
|
|
249
|
+
|
|
250
|
+
- `app/views/layouts/postnhost/_favicon.html.erb`
|
|
251
|
+
|
|
252
|
+
### Host app i18n (optional)
|
|
253
|
+
|
|
254
|
+
PostnHost ships with locale files for: `en`, `fr`, `de`, `ja`, `ko`, `pt`, `pl`, `es`, `ru`.
|
|
255
|
+
|
|
256
|
+
You can use PostnHost without extra i18n setup for a single-locale site. **If you use multiple locales** (localized routes, language switcher, or custom `config/locales/*.yml` files), add this to `config/application.rb`:
|
|
257
|
+
|
|
258
|
+
```ruby
|
|
259
|
+
config.i18n.default_locale = :en
|
|
260
|
+
config.i18n.available_locales = %i[en fr de ja ko pt pl es ru]
|
|
261
|
+
config.i18n.fallbacks = [:en]
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**Required:** `config.i18n.fallbacks = [:en]` (or a map that ultimately falls back to a default language).
|
|
265
|
+
|
|
266
|
+
If you want to add more languages, add the locale code to `available_locales` and add `config/locales/<code>.yml`. To scaffold English strings into your host app:
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
bin/rails g postnhost:locale it
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Then translate the values under `postnhost.public` as needed.
|
|
273
|
+
|
|
274
|
+
### Static pages (Terms, Privacy, etc.)
|
|
275
|
+
|
|
276
|
+
You can define static public pages directly in your host app under:
|
|
277
|
+
|
|
278
|
+
- `app/views/postnhost/static_pages/*.html.erb`
|
|
279
|
+
|
|
280
|
+
You can generate starter templates:
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
bin/rails g postnhost:static_pages terms privacy about
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Examples:
|
|
287
|
+
|
|
288
|
+
- `app/views/postnhost/static_pages/terms.html.erb` → `/blog/terms`
|
|
289
|
+
- `app/views/postnhost/static_pages/privacy.html.erb` → `/blog/privacy`
|
|
290
|
+
- `app/views/postnhost/static_pages/about.html.erb` → `/blog/about`
|
|
291
|
+
|
|
292
|
+
Link helpers:
|
|
293
|
+
|
|
294
|
+
```erb
|
|
295
|
+
<%= link_to "Terms", postnhost.public_static_page_path(slug: "terms") %>
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## Routes
|
|
299
|
+
|
|
300
|
+
The engine mounts at `/blog` by default. You can change this in `config/routes.rb`:
|
|
301
|
+
|
|
302
|
+
```ruby
|
|
303
|
+
mount Postnhost::Engine, at: "/blog"
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Available Public Routes
|
|
307
|
+
|
|
308
|
+
- `GET /` - Blog index
|
|
309
|
+
- `GET /:locale` - Blog index (localized)
|
|
310
|
+
- `GET /search` - Article search
|
|
311
|
+
- `GET /:locale/search` - Article search (localized)
|
|
312
|
+
- `GET /authors/:slug` - Author show (when author pages are enabled)
|
|
313
|
+
- `GET /:locale/authors/:slug` - Author show (localized, when author pages are enabled)
|
|
314
|
+
- `GET /:category_slug` - Category articles
|
|
315
|
+
- `GET /:locale/:category_slug` - Category articles (localized)
|
|
316
|
+
- `GET /:slug` - Article show or static page
|
|
317
|
+
- `GET /:locale/:slug` - Article show or static page (localized)
|
|
318
|
+
- `GET /preview/:id` - Article preview
|
|
319
|
+
- `GET /sitemap.xml` - XML sitemap
|
|
320
|
+
- `GET /sitemap.xsl` - Sitemap stylesheet
|
|
321
|
+
|
|
322
|
+
## Manual configuration
|
|
323
|
+
|
|
324
|
+
Some parts must still be configured manually in code:
|
|
325
|
+
|
|
326
|
+
- Favicons
|
|
327
|
+
- Robots.txt
|
|
328
|
+
- Error pages 404/500 etc.
|
|
26
329
|
|
|
27
330
|
## Development
|
|
28
331
|
|
|
29
|
-
|
|
332
|
+
For engine development, use Ruby 4.0.6 and Node.js 24+ as listed in `mise.toml`. Install them with mise, or another version manager, then run:
|
|
333
|
+
|
|
334
|
+
```bash
|
|
335
|
+
cd postnhost
|
|
336
|
+
bundle install
|
|
337
|
+
yarn install
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Run both JS and CSS watchers in one command:
|
|
341
|
+
|
|
342
|
+
```bash
|
|
343
|
+
yarn dev
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Running tests
|
|
30
347
|
|
|
31
|
-
|
|
348
|
+
From the engine root:
|
|
32
349
|
|
|
33
|
-
|
|
350
|
+
```bash
|
|
351
|
+
bundle install
|
|
352
|
+
yarn install
|
|
353
|
+
bundle exec rake prepare_test_db
|
|
354
|
+
bundle exec rspec
|
|
355
|
+
```
|
|
34
356
|
|
|
35
|
-
|
|
357
|
+
System specs run with Selenium headless by default. Use visible Chrome when needed:
|
|
358
|
+
|
|
359
|
+
```bash
|
|
360
|
+
SYSTEM_TESTS_BROWSER=1 bundle exec rspec spec/system
|
|
361
|
+
```
|
|
36
362
|
|
|
37
363
|
## License
|
|
38
364
|
|
|
39
|
-
The gem is available as open source under the terms of the [MIT License](
|
|
365
|
+
The gem is available as open source under the terms of the [MIT License](LICENSE).
|
data/Rakefile
CHANGED
|
@@ -1,12 +1,44 @@
|
|
|
1
|
-
|
|
1
|
+
require "bundler/setup"
|
|
2
|
+
require "fileutils"
|
|
2
3
|
|
|
3
4
|
require "bundler/gem_tasks"
|
|
4
|
-
require "rspec/core/rake_task"
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
# Load dummy app so the Rails `environment` task exists (required by prepare_test_db).
|
|
7
|
+
require_relative "spec/dummy/config/application"
|
|
8
|
+
Rails.application.load_tasks
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
desc "Prepare dummy app test database"
|
|
11
|
+
task prepare_test_db: :environment do
|
|
12
|
+
dummy_dir = File.expand_path("spec/dummy", __dir__)
|
|
13
|
+
engine_migrations_dir = File.expand_path("db/migrate", __dir__)
|
|
14
|
+
dummy_migrations_dir = File.join(dummy_dir, "db", "migrate")
|
|
15
|
+
dummy_test_db = File.join(dummy_dir, "storage", "test.sqlite3")
|
|
16
|
+
dummy_schema = File.join(dummy_dir, "db", "schema.rb")
|
|
9
17
|
|
|
10
|
-
|
|
18
|
+
FileUtils.mkdir_p(dummy_migrations_dir)
|
|
19
|
+
FileUtils.rm_f(Dir[File.join(dummy_migrations_dir, "*.rb")])
|
|
20
|
+
Dir[File.join(engine_migrations_dir, "*.rb")].each do |migration_file|
|
|
21
|
+
FileUtils.cp(migration_file, dummy_migrations_dir)
|
|
22
|
+
end
|
|
23
|
+
FileUtils.rm_f(dummy_test_db)
|
|
24
|
+
FileUtils.rm_f(dummy_schema)
|
|
11
25
|
|
|
12
|
-
|
|
26
|
+
system(
|
|
27
|
+
{ "RAILS_ENV" => "test" },
|
|
28
|
+
"cd #{dummy_dir} && bundle exec rails db:create db:migrate",
|
|
29
|
+
out: $stdout,
|
|
30
|
+
err: $stderr
|
|
31
|
+
) ||
|
|
32
|
+
raise("Failed to prepare test database")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
begin
|
|
36
|
+
require "rspec/core/rake_task"
|
|
37
|
+
|
|
38
|
+
desc "Run engine specs"
|
|
39
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
40
|
+
task spec: :prepare_test_db
|
|
41
|
+
task default: :spec
|
|
42
|
+
rescue LoadError
|
|
43
|
+
# rspec-rails is not available in minimal environments.
|
|
44
|
+
end
|