feat: H1 conversion on the API, markdown and import paths

- the /_journal/api/v1 post writes convert default and translated content
  in the write transaction and answer an additive notices key
- journal:import-posts converts and prints the converted count
  (ImportFromDirConfig, ImportStats.H1Converted)
- a category's pillar text saved in the admin answers
  editor.h1_converted_category
- every GET /categories row reports content_h1_to_h2 (additive key)
This commit is contained in:
Jakub Zych
2026-11-04 09:51:47 +01:00
parent 305f94afd9
commit faaa9b0f55
11 changed files with 459 additions and 42 deletions

View File

@@ -13,6 +13,7 @@ import (
"unicode"
"unicode/utf8"
"git.golem15.com/golem15/sm-journal-plugin/models"
"git.golem15.com/golem15/summercms/modules/backpack"
"git.golem15.com/golem15/summercms/modules/compass"
"git.golem15.com/golem15/summercms/modules/pact"
@@ -35,6 +36,9 @@ const (
// EditorH1Converted is the notice of a save that turned H1 headings
// into H2 (Count is the number of headings).
EditorH1Converted = "golem15.journal::lang.editor.h1_converted"
// EditorH1ConvertedCategory is EditorH1Converted for a category's
// pillar text, which names the category name instead of the post title.
EditorH1ConvertedCategory = "golem15.journal::lang.editor.h1_converted_category"
)
// blogListValueRunes is how many runes of text a list column shows.
@@ -153,8 +157,9 @@ func checkUploadFolder(folder string) error {
// decodeBlogHTMLFor is the Blog Editor types' Decode: decodeBlogHTML, then,
// while the H1 setting is on (EffectiveH1ToH2 on the save transaction and
// the booted application's config), ConvertH1 on the stored HTML with the
// EditorH1Converted notice counting the converted headings. app may be nil
// the booted application's config), ConvertH1 on the stored HTML with a
// notice counting the converted headings: EditorH1ConvertedCategory on a
// category's pillar text, EditorH1Converted otherwise. app may be nil
// or answer nil; the setting is then read from the database alone.
func decodeBlogHTMLFor(app func() *backpack.App) func(context.Context, pact.FormFieldValue) (pact.FormFieldDecoded, error) {
return func(ctx context.Context, in pact.FormFieldValue) (pact.FormFieldDecoded, error) {
@@ -174,11 +179,21 @@ func decodeBlogHTMLFor(app func() *backpack.App) func(context.Context, pact.Form
return out, nil
}
out.Value = converted
out.Notices = append(out.Notices, pact.FormFieldNotice{Message: EditorH1Converted, Count: n})
out.Notices = append(out.Notices, pact.FormFieldNotice{Message: h1ConvertedNotice(in.Record), Count: n})
return out, nil
}
}
// h1ConvertedNotice is the conversion notice for the saved record: the
// category phrase for a category, the post phrase otherwise.
func h1ConvertedNotice(record any) string {
switch record.(type) {
case *models.Category, models.Category:
return EditorH1ConvertedCategory
}
return EditorH1Converted
}
// appConfig is the config of the application app answers, nil when there
// is none.
func appConfig(app func() *backpack.App) *compass.Config {