feat(15-01): add journal en/pl phrasebooks and plugin README
Operators get UI catalogs for plugin, journal, post, category, and tag keys, plus an application-neutral README for the compiled plugin. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
113
README.md
Normal file
113
README.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# golem15.journal
|
||||
|
||||
The compiled journal plugin for SummerCMS applications: translatable posts and categories, tags, a settings singleton, and the tables a host migrate creates for a blog.
|
||||
|
||||
```go
|
||||
import _ "git.golem15.com/golem15/sm-journal-plugin"
|
||||
```
|
||||
|
||||
Module `git.golem15.com/golem15/sm-journal-plugin`, package `journal`, plugin ID `golem15.journal`.
|
||||
|
||||
## Overview
|
||||
|
||||
The plugin is a compiled SummerCMS plugin. Its `init` registers it with the framework, so an application loads it by listing it in `summer.yaml`; `summer build` then emits the blank import above into the application's `plugins.gen.go`.
|
||||
|
||||
It requires `golem15.translate`. Post and Category store default-locale values on host columns and other locales in `golem15_translate_attributes`. `MorphName` is the PHP class string (`Golem15\Journal\Models\Post`, `Golem15\Journal\Models\Category`); it is never `reflect.Type.String()`.
|
||||
|
||||
WinterCMS theme components such as `journalPost` and `journalPosts` are a later successor, not implemented here.
|
||||
|
||||
## Features
|
||||
|
||||
- **Posts.** `models.Post` maps `golem15_journal_posts`. Translatable fields are title, content, content_html, excerpt, metadata, and slug (indexed). Fillable does not include `redactor_id`, `content_html`, or `user_id`. Attachments `featured_images` and `content_images` use `system_files` under the same MorphName.
|
||||
- **Categories and tags.** `models.Category` is translatable (name, description, slug). Fillable is name, slug, code, description, parent_id; nest_* columns exist in DDL and are not fillable. `models.Tag` is not translatable. Fillable is name, slug, description. Pivots are `golem15_journal_posts_categories` and `golem15_journal_posts_tags`.
|
||||
- **Settings.** Singleton row ID=1 on `golem15_journal_settings`, not PHP `system_settings`. `search_use_typesense` defaults to false.
|
||||
- **Author slug.** Plugin migration adds `backend_users.golem15_bloghub_author_slug`.
|
||||
- **Phrasebooks.** Embedded `lang/en/lang.yaml` and `lang/pl/lang.yaml` hold plugin, journal, post, category, and tag UI strings. They are independent of model translation JSON.
|
||||
- **Schema.** Seven gormigrate steps create the six journal tables and the author-slug column. AutoMigrate is not used. Table names are `golem15_journal_*` only.
|
||||
|
||||
## Usage
|
||||
|
||||
Mount the plugin in an application as a git submodule at `plugins/golem15/journal`:
|
||||
|
||||
```sh
|
||||
git submodule add git@git.golem15.com:golem15/sm-journal-plugin.git plugins/golem15/journal
|
||||
```
|
||||
|
||||
Add it to the application's `go.work`:
|
||||
|
||||
```text
|
||||
use (
|
||||
.
|
||||
./plugins/golem15/journal
|
||||
)
|
||||
```
|
||||
|
||||
Require it in the application's `go.mod` with a local replace:
|
||||
|
||||
```text
|
||||
require git.golem15.com/golem15/sm-journal-plugin v0.0.0
|
||||
|
||||
replace git.golem15.com/golem15/sm-journal-plugin => ./plugins/golem15/journal
|
||||
```
|
||||
|
||||
List it in `summer.yaml` after translate and run `summer build`:
|
||||
|
||||
```yaml
|
||||
module: git.golem15.com/golem15/acme
|
||||
binary: acme
|
||||
plugins:
|
||||
- id: golem15.translate
|
||||
module: git.golem15.com/golem15/sm-translate-plugin
|
||||
- id: golem15.journal
|
||||
module: git.golem15.com/golem15/sm-journal-plugin
|
||||
```
|
||||
|
||||
The plugin's own `go.mod` replaces the framework with `../summercms.go` for a sibling checkout next to `summercms.go`. A host application that mounts the plugin at `plugins/golem15/journal` should replace the framework from the host `go.work`.
|
||||
|
||||
`Requires()` is exactly `golem15.translate`.
|
||||
|
||||
A blog post model already declares translatable fields:
|
||||
|
||||
```go
|
||||
func (Post) Translatable() []string {
|
||||
return []string{"title", "content", "content_html", "excerpt", "metadata", "slug"}
|
||||
}
|
||||
func (Post) TranslatableIndexes() []string { return []string{"slug"} }
|
||||
func (Post) MorphName() string { return `Golem15\Journal\Models\Post` }
|
||||
```
|
||||
|
||||
## API reference
|
||||
|
||||
| Identifier | Description |
|
||||
|------------|-------------|
|
||||
| `journal.Plugin` | Compiled plugin; `ID` is `golem15.journal`. |
|
||||
| `journal.Plugin.Requires` | Returns `golem15.translate` only. |
|
||||
| `journal.Plugin.LangFS` | Embedded English and Polish UI phrasebooks. |
|
||||
| `journal.Plugin.Migrations` | gormigrate steps registered by this plugin. |
|
||||
| `journal.Plugin.Models` | Models registered by this plugin. |
|
||||
| `models.Post` | Post row; `TableName` is `golem15_journal_posts`. |
|
||||
| `models.Post.MorphName` | PHP class string `Golem15\Journal\Models\Post`. |
|
||||
| `models.Post.Fillable` | Allow-list excluding `redactor_id`, `content_html`, and `user_id`. |
|
||||
| `models.Post.AttachRelations` | `featured_images` and `content_images` attachMany. |
|
||||
| `models.Category` | Category row; `TableName` is `golem15_journal_categories`. |
|
||||
| `models.Category.MorphName` | PHP class string `Golem15\Journal\Models\Category`. |
|
||||
| `models.Category.Fillable` | `name`, `slug`, `code`, `description`, `parent_id`. |
|
||||
| `models.Tag` | Tag row; `TableName` is `golem15_journal_tags`. |
|
||||
| `models.Tag.Fillable` | `name`, `slug`, `description`. |
|
||||
| `models.Settings` | Singleton settings row; `TableName` is `golem15_journal_settings`. |
|
||||
| `models.All` | Models registered by this plugin. |
|
||||
| `updates.All` | gormigrate steps registered by this plugin. |
|
||||
|
||||
## Dependencies
|
||||
|
||||
- SummerCMS modules: backpack, cabana, lagoon/attach, pact, party.
|
||||
- Compiled plugin: `golem15.translate`.
|
||||
- Third-party: `gorm.io/gorm`, `github.com/go-gormigrate/gormigrate/v2`. Test-only: `github.com/testcontainers/testcontainers-go`.
|
||||
|
||||
## Testing
|
||||
|
||||
```sh
|
||||
go test ./...
|
||||
```
|
||||
|
||||
Postgres integration smokes start Docker unless `-short`. Missing Docker is a failure, not a skip, unless `-short`.
|
||||
122
lang/en/lang.yaml
Normal file
122
lang/en/lang.yaml
Normal file
@@ -0,0 +1,122 @@
|
||||
plugin:
|
||||
name: Journal
|
||||
description: A robust journalging platform.
|
||||
journal:
|
||||
menu_label: Journal
|
||||
menu_description: Manage Journal Posts
|
||||
posts: Posts
|
||||
create_post: Journal post
|
||||
categories: Categories
|
||||
create_category: Journal category
|
||||
tab: Journal
|
||||
access_posts: Manage the journal posts
|
||||
access_categories: Manage the journal categories
|
||||
access_other_posts: Manage other users journal posts
|
||||
access_import_export: Allowed to import and export posts
|
||||
access_publish: Allowed to publish posts
|
||||
manage_settings: Manage journal settings
|
||||
tags: Tags
|
||||
create_tag: Journal tag
|
||||
access_tags: Manage the journal tags
|
||||
delete_confirm: Are you sure?
|
||||
chart_published: Published
|
||||
chart_drafts: Drafts
|
||||
chart_total: Total
|
||||
settings_description: Manage journal settings
|
||||
show_all_posts_label: Show all posts to backend users
|
||||
show_all_posts_comment: Display both published and unpublished posts on the frontend to backend users
|
||||
tab_general: General
|
||||
tab_search: Search
|
||||
search_use_typesense_label: Use Typesense full-text search
|
||||
search_use_typesense_comment: 'When enabled, the public posts API uses Laravel Scout + Typesense for relevance-ranked search (title weighted above content). When disabled, the standard database search is used. After enabling, run: php artisan scout:import "Golem15\Journal\Models\Post"'
|
||||
search_title_weight_label: Title weight
|
||||
search_title_weight_comment: Relevance weight for matches in the post title. Higher means title matches rank higher (default 5).
|
||||
search_excerpt_weight_label: Excerpt weight
|
||||
search_excerpt_weight_comment: Relevance weight for matches in the post excerpt (default 3).
|
||||
search_content_weight_label: Content weight
|
||||
search_content_weight_comment: Relevance weight for matches in the post body content (default 1).
|
||||
use_rich_editor_label: Use the rich editor for journal content
|
||||
use_rich_editor_comment: By default, journal posts are written using Markdown. If this is enabled, this changes the editor to an HTML based editor (WYSIWYG).
|
||||
tab_rss: RSS Feed
|
||||
rss_enabled_label: Enable RSS feed
|
||||
rss_enabled_comment: When disabled, the RSS feed component will return a 404 response.
|
||||
rss_include_content_label: Include full content
|
||||
rss_include_content_comment: Include the full HTML content of each post in the feed alongside the summary.
|
||||
rss_title_label: Feed title
|
||||
rss_title_comment: Falls back to the page title if left empty.
|
||||
rss_title_placeholder: Site name or feed title
|
||||
rss_description_label: Feed description
|
||||
rss_description_comment: Falls back to the page description if left empty.
|
||||
rss_language_label: Feed language
|
||||
rss_language_comment: Language code for the feed (e.g., en-us, de, fr).
|
||||
rss_copyright_label: Copyright notice
|
||||
rss_copyright_comment: Optional copyright notice displayed in the feed.
|
||||
rss_image_url_label: Feed image URL
|
||||
rss_image_url_comment: URL to an image/logo displayed in feed readers.
|
||||
rss_posts_per_feed_label: Posts per feed
|
||||
rss_posts_per_feed_comment: Default number of posts in the RSS feed. Can be overridden per component.
|
||||
post:
|
||||
label: Post
|
||||
label_plural: Posts
|
||||
title: Title
|
||||
title_placeholder: New post title
|
||||
content: Content
|
||||
content_html: HTML Content
|
||||
slug: Slug
|
||||
slug_placeholder: new-post-slug
|
||||
categories: Categories
|
||||
author_email: Author Email
|
||||
created: Created
|
||||
created_date: Created date
|
||||
updated: Updated
|
||||
updated_date: Updated date
|
||||
published: Published
|
||||
published_by: Published by
|
||||
preview_page: Preview Page
|
||||
preview_page_comment: The page that should be used when previewing the post
|
||||
current_user: Current user
|
||||
published_date: Published date
|
||||
published_validation: Please specify the published date
|
||||
tab_edit: Edit
|
||||
tab_categories: Categories
|
||||
categories_comment: Select categories the journal post belongs to
|
||||
categories_placeholder: There are no categories, you should create one first!
|
||||
tags_comment: Select tags for the journal post
|
||||
tags_placeholder: There are no tags, you should create one first!
|
||||
tab_manage: Manage
|
||||
published_on: Published on
|
||||
excerpt: Excerpt
|
||||
summary: Summary
|
||||
featured_images: Featured Images
|
||||
delete_confirm: Delete this post?
|
||||
delete_success: Successfully deleted those posts.
|
||||
close_confirm: The post is not saved.
|
||||
return_to_posts: Return to posts list
|
||||
posted_byline: 'Posted in :categories on :date.'
|
||||
posted_byline_no_categories: 'Posted on :date.'
|
||||
date_format: 'M d, Y'
|
||||
category:
|
||||
label: Category
|
||||
label_plural: Categories
|
||||
name: Name
|
||||
name_placeholder: New category name
|
||||
description: Description
|
||||
slug: Slug
|
||||
slug_placeholder: new-category-slug
|
||||
posts: Posts
|
||||
delete_confirm: Delete this category?
|
||||
delete_success: Successfully deleted those categories.
|
||||
return_to_categories: Return to the journal category list
|
||||
reorder: Reorder Categories
|
||||
tag:
|
||||
label: Tag
|
||||
label_plural: Tags
|
||||
name: Name
|
||||
name_placeholder: New tag name
|
||||
description: Description
|
||||
slug: Slug
|
||||
slug_placeholder: new-tag-slug
|
||||
posts: Posts
|
||||
delete_confirm: Delete this tag?
|
||||
delete_success: Successfully deleted those tags.
|
||||
return_to_tags: Return to the journal tag list
|
||||
74
lang/pl/lang.yaml
Normal file
74
lang/pl/lang.yaml
Normal file
@@ -0,0 +1,74 @@
|
||||
plugin:
|
||||
name: Journal
|
||||
description: Solidna platforma journalera
|
||||
journal:
|
||||
menu_label: Journal
|
||||
menu_description: Zarządzaj postami na journalu
|
||||
posts: Posty
|
||||
create_post: Utwórz post
|
||||
categories: Kategorie
|
||||
create_category: Utwórz kategorię
|
||||
tab: Journal
|
||||
access_posts: Zarządzaj postami
|
||||
access_categories: Zarządzaj kategoriami na journalu
|
||||
access_other_posts: Zarządzaj postami innych użytkowników
|
||||
access_import_export: Zarządzaj importowaniem i eksportowaniem postów
|
||||
access_publish: Publikuj posty
|
||||
manage_settings: Zarządzaj ustawieniami journala
|
||||
delete_confirm: Czy jesteś pewien?
|
||||
chart_published: Opublikowane
|
||||
chart_drafts: Szkice
|
||||
chart_total: Łącznie
|
||||
settings_description: Zarządzaj ustawieniami journala
|
||||
show_all_posts_label: Pokaż wszystkie posty użytkownikom backendu
|
||||
show_all_posts_comment: Wyświetl opublikowane i nieopublikowany posty na stronie dla użytkowników backendu
|
||||
tab_general: Ogólne
|
||||
tab_search: Wyszukiwanie
|
||||
search_use_typesense_label: Użyj wyszukiwania pełnotekstowego Typesense
|
||||
search_use_typesense_comment: 'Po włączeniu publiczne API postów korzysta z Laravel Scout + Typesense, aby sortować wyniki według trafności (tytuł ma większą wagę niż treść). Po wyłączeniu używane jest standardowe wyszukiwanie w bazie danych. Po włączeniu uruchom: php artisan scout:import "Golem15\Journal\Models\Post"'
|
||||
search_title_weight_label: Waga tytułu
|
||||
search_title_weight_comment: Waga trafności dla dopasowań w tytule posta. Wyższa wartość oznacza wyższą pozycję dopasowań w tytule (domyślnie 5).
|
||||
search_excerpt_weight_label: Waga zajawki
|
||||
search_excerpt_weight_comment: Waga trafności dla dopasowań w zajawce posta (domyślnie 3).
|
||||
search_content_weight_label: Waga treści
|
||||
search_content_weight_comment: Waga trafności dla dopasowań w treści posta (domyślnie 1).
|
||||
post:
|
||||
title: Tytuł
|
||||
title_placeholder: Tytuł nowego posta
|
||||
content: Zawartość
|
||||
content_html: Zawartość HTML
|
||||
slug: Alias
|
||||
slug_placeholder: alias-nowego-postu
|
||||
categories: Kategorie
|
||||
author_email: Email autora
|
||||
created: Utworzony
|
||||
created_date: Data utworzenia
|
||||
updated: Zaktualizowany
|
||||
updated_date: Data aktualizacji
|
||||
published: Opublikowany
|
||||
published_date: Data publikacji
|
||||
published_validation: Proszę określić datę publikacji
|
||||
tab_edit: Edytuj
|
||||
tab_categories: Kategorie
|
||||
categories_comment: Wybierz kategorie do których post należy
|
||||
categories_placeholder: Nie ma żadnej kategorii, powinieneś utworzyć przynajmniej jedną.
|
||||
tab_manage: Zarządzaj
|
||||
published_on: Opublikowane
|
||||
excerpt: Zalążek
|
||||
summary: Summary
|
||||
featured_images: Załączone grafiki
|
||||
delete_confirm: Czy naprawdę chcesz usunąć ten post?
|
||||
delete_success: Posty zostały pomyślnie usunięte.
|
||||
close_confirm: Ten post nie jest zapisany.
|
||||
return_to_posts: Wróć do listy postów
|
||||
category:
|
||||
name: Nazwa
|
||||
name_placeholder: Nazwa nowej kategorii
|
||||
description: Opis
|
||||
slug: Alias
|
||||
slug_placeholder: alias-nowej-kategorii
|
||||
posts: Posty
|
||||
delete_confirm: Czy naprawdę chcesz usunąć tę kategorię?
|
||||
delete_success: Kategorie zostały pomyślnie usunięte.
|
||||
return_to_categories: Wróć do listy kategorii
|
||||
reorder: Zmień kolejność kategorii
|
||||
@@ -1,6 +1,9 @@
|
||||
package journal
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"io/fs"
|
||||
|
||||
"git.golem15.com/golem15/sm-journal-plugin/models"
|
||||
"git.golem15.com/golem15/sm-journal-plugin/updates"
|
||||
"git.golem15.com/golem15/summercms/modules/backpack"
|
||||
@@ -13,8 +16,12 @@ var (
|
||||
_ party.Plugin = (*Plugin)(nil)
|
||||
_ pact.HasMigrations = (*Plugin)(nil)
|
||||
_ pact.HasModels = (*Plugin)(nil)
|
||||
_ pact.HasLang = (*Plugin)(nil)
|
||||
)
|
||||
|
||||
//go:embed lang
|
||||
var langFS embed.FS
|
||||
|
||||
// Plugin is the golem15.journal plugin.
|
||||
type Plugin struct {
|
||||
app *backpack.App
|
||||
@@ -34,6 +41,8 @@ func (p *Plugin) Boot(app *backpack.App) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Plugin) LangFS() fs.FS { return langFS }
|
||||
|
||||
func (p *Plugin) Migrations() []*gormigrate.Migration { return updates.All() }
|
||||
|
||||
func (p *Plugin) Models() []any { return models.All() }
|
||||
|
||||
Reference in New Issue
Block a user