Jakub Zych dea0934950 feat(15-02): add Posts admin with mlmarkdown and FormatHTML
YAML-driven Posts screen stores translatable markdown and regenerates
content_html in the plugin. Publish writes without access_publish are
refused; list and form queries stay on the principal without
access_other_posts.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-06 18:41:10 +02:00

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.

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:

git submodule add git@git.golem15.com:golem15/sm-journal-plugin.git plugins/golem15/journal

Add it to the application's go.work:

use (
	.
	./plugins/golem15/journal
)

Require it in the application's go.mod with a local replace:

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:

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:

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

go test ./...

Postgres integration smokes start Docker unless -short. Missing Docker is a failure, not a skip, unless -short.

Description
No description provided
Readme 1.7 MiB
Languages
Go 75.1%
TypeScript 23%
CSS 1.4%
HTML 0.4%
Shell 0.1%