From b62febb65c4bfb000d08de3a9895ac8be7114e0e Mon Sep 17 00:00:00 2001 From: Ethan Date: Mon, 18 May 2026 09:44:57 +0800 Subject: [PATCH 1/6] Add Success message and use in preset copy Add a Success field to the Messages struct and provide translations for en_US, zh_CN and ja_JP so the success label is localizable. Update the preset copy button handler in settings.go to use msgr.Success (localized) instead of a hardcoded "success" string and keep the color argument (ColorSuccess). This centralizes the success message for i18n and ensures consistency across UI messages. --- pagebuilder/messages.go | 4 ++++ pagebuilder/settings.go | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pagebuilder/messages.go b/pagebuilder/messages.go index d030abf69..2385a6176 100644 --- a/pagebuilder/messages.go +++ b/pagebuilder/messages.go @@ -103,6 +103,7 @@ type Messages struct { SharedContainerHasBeenUpdated string TemplateFixedAreaMessage string SharedContainerModificationWarning string + Success string } var Messages_en_US = &Messages{ @@ -201,6 +202,7 @@ var Messages_en_US = &Messages{ SharedContainerHasBeenUpdated: "The shared container on this page has been updated. You may notice differences between the preview and the live page.", TemplateFixedAreaMessage: "This container is fixed and cannot be updated", SharedContainerModificationWarning: "This is a shared container. Any modifications you make will apply to all pages that use it", + Success: "Success", } var Messages_zh_CN = &Messages{ @@ -300,6 +302,7 @@ var Messages_zh_CN = &Messages{ SharedContainerHasBeenUpdated: "此页面上的共享容器已更新。您可能会注意到预览和实时页面之间的差异。", TemplateFixedAreaMessage: "此区域由模板固定,无法编辑。", SharedContainerModificationWarning: "这是一个共享容器。您所做的任何修改都将应用于使用它的所有页面", + Success: "成功", } var Messages_ja_JP = &Messages{ @@ -397,6 +400,7 @@ var Messages_ja_JP = &Messages{ SharedContainerHasBeenUpdated: "このページの共有コンテナが更新されました。プレビューとライブページの間に違いがあるかもしれません。", TemplateFixedAreaMessage: "この領域はテンプレートによって固定されており、編集できません。", SharedContainerModificationWarning: "これは共有コンテナです。行った変更は、それを使用するすべてのページに適用されます", + Success: "成功", } type ModelsI18nModulePage struct { diff --git a/pagebuilder/settings.go b/pagebuilder/settings.go index 4253505b5..6d7f4d37a 100644 --- a/pagebuilder/settings.go +++ b/pagebuilder/settings.go @@ -146,7 +146,7 @@ transform-origin: 0 0; transform:scale(0.5);width:200%;height:200%`), h.Div( previewComp, VBtn("").Icon("mdi-content-copy").Color(ColorSecondary).Width(20).Height(20).Variant(VariantText).Size(SizeXSmall).Class("ml-1 fix-btn-icon"). - Attr("@click", fmt.Sprintf(`$event.view.window.navigator.clipboard.writeText(%s);vars.presetsMessage = { show: true, message: "success", color: %q}`, copyURL, ColorSuccess)), + Attr("@click", fmt.Sprintf(`$event.view.window.navigator.clipboard.writeText(%s);vars.presetsMessage = { show: true, message: %q, color: %q}`, copyURL, msgr.Success, ColorSuccess)), ).Class("d-inline-flex align-center py-4"), ).Class("my-10") } From 1ddaa346e31cc35106149a1a4edcc39771ed8e2e Mon Sep 17 00:00:00 2001 From: Ethan Date: Fri, 22 May 2026 10:10:18 +0800 Subject: [PATCH 2/6] Fix dialog sizing and update schedule button UI Reset listing dialog minHeight before re-locking to offsetHeight to avoid flicker and handle missing element safely (presets/listing_builder.go). Replace the VAutocomplete schedule control with a compact VBtn (icons, flat variant, density, height and class tweaks) to improve appearance and interaction, and adjust related classes/attributes (publish/version_compo.go). Also normalize dialog width string from "900px" to "900" for the listing builder configuration. --- presets/listing_builder.go | 8 +++++--- publish/version_compo.go | 16 ++++++++++------ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/presets/listing_builder.go b/presets/listing_builder.go index b48c264c5..e60e466af 100644 --- a/presets/listing_builder.go +++ b/presets/listing_builder.go @@ -372,10 +372,12 @@ func (b *ListingBuilder) openListingDialog(evCtx *web.EventContext) (r web.Event } compo.OnMounted = fmt.Sprintf(`({el}) => { - var listingDialogElem = el.ownerDocument.getElementById(%q); - if (listingDialogElem && listingDialogElem.offsetHeight > parseInt(listingDialogElem.style.minHeight || '0', 10)) { + var listingDialogElem = el.ownerDocument.getElementById(%q); + if (listingDialogElem) { + // Reset so the dialog can shrink when content gets shorter, then re-lock to prevent flicker on next reload. + listingDialogElem.style.minHeight = '0px'; listingDialogElem.style.minHeight = listingDialogElem.offsetHeight+'px'; - }; + } }`, compo.CompoID()) content := v.VCard().Attr("id", compo.CompoID()).Children( diff --git a/publish/version_compo.go b/publish/version_compo.go index 8e2db18e4..4ac4876be 100644 --- a/publish/version_compo.go +++ b/publish/version_compo.go @@ -235,12 +235,16 @@ func buildScheduleButton(obj interface{}, ctx *web.EventContext, mb *presets.Mod Query(presets.ParamID, slug). URL(mb.Info().ListingHref()).Go() if config.Top { - scheduleBtn = v.VAutocomplete().PrependInnerIcon("mdi-alarm").Density(v.DensityCompact). - Variant(v.FieldVariantSoloFilled).ModelValue(msgr.SchedulePublishTime). - BgColor(v.ColorPrimaryLighten2).Readonly(true). - Width(500).HideDetails(true). + scheduleBtn = v.VBtn(msgr.SchedulePublishTime). + PrependIcon("mdi-alarm"). + AppendIcon("mdi-menu-down"). + Variant(v.VariantFlat). + Color(v.ColorPrimaryLighten2). + Density(v.DensityCompact). + Height(36). Attr(":disabled", phraseHasPresetsDataChanged). - Attr("@click", clickEvent).Class("ml-2 text-caption page-builder-autoCmp") + Attr("@click", clickEvent). + Class("ml-2 text-caption text-none") } else { scheduleBtn = v.VBtn("").Size(v.SizeSmall).Children(v.VIcon("mdi-alarm").Size(v.SizeXLarge)).Rounded("0").Class("rounded-e ml-abs-1"). Variant(v.VariantElevated).Color(v.ColorPrimary).Width(36).Height(36). @@ -344,7 +348,7 @@ func configureVersionListDialog(db *gorm.DB, pb *Builder, b *presets.Builder, pm } lb := mb.Listing(listingFields...). - DialogWidth("900px"). + DialogWidth("900"). Title(func(evCtx *web.EventContext, _ presets.ListingStyle, _ string) (string, h.HTMLComponent, error) { msgr := i18n.MustGetModuleMessages(evCtx.R, I18nPublishKey, Messages_en_US).(*Messages) return msgr.VersionsList, nil, nil From b78acb3e8b05da221789a4d648b284b2a27ec5f7 Mon Sep 17 00:00:00 2001 From: Ethan Date: Fri, 22 May 2026 11:16:54 +0800 Subject: [PATCH 3/6] Bump github.com/qor5/x/v3 to 20260522031431 Update go.mod to require github.com/qor5/x/v3 at pseudo-version v3.2.1-0.20260522031431-46d6ad4cfc7e and regenerate go.sum to include the new module checksums. No other source changes. --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index ec4993126..a3751f52b 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( github.com/qor5/imaging v1.6.4 github.com/qor5/web v1.3.2 github.com/qor5/web/v3 v3.0.12-0.20250618085230-3764d0e521a8 - github.com/qor5/x/v3 v3.2.1-0.20260515090943-65b6f4f15c42 + github.com/qor5/x/v3 v3.2.1-0.20260522031431-46d6ad4cfc7e github.com/samber/lo v1.50.0 github.com/shurcooL/sanitized_anchor_name v1.0.0 github.com/spf13/cast v1.7.1 diff --git a/go.sum b/go.sum index 698ff968f..cbe4d8141 100644 --- a/go.sum +++ b/go.sum @@ -446,6 +446,8 @@ github.com/qor5/web/v3 v3.0.12-0.20250618085230-3764d0e521a8 h1:s3jBS5bq6VX56Gic github.com/qor5/web/v3 v3.0.12-0.20250618085230-3764d0e521a8/go.mod h1:hrhZ4nc1U+AOBrGmnUoRUPpA9fymxlAbNfGvn9TJLns= github.com/qor5/x/v3 v3.2.1-0.20260515090943-65b6f4f15c42 h1:BQUNVobg116KpSDV42Nz1vD6Ks+m8V79iutGm/ECskM= github.com/qor5/x/v3 v3.2.1-0.20260515090943-65b6f4f15c42/go.mod h1:aw3fBGpEbyrEoGLOtQdBTUAKNJ7i0RE1exHjHJSlPbs= +github.com/qor5/x/v3 v3.2.1-0.20260522031431-46d6ad4cfc7e h1:lY7f8OnU8JXmYkhSgNbqFAZ35nI45rFO2582nzBUtac= +github.com/qor5/x/v3 v3.2.1-0.20260522031431-46d6ad4cfc7e/go.mod h1:aw3fBGpEbyrEoGLOtQdBTUAKNJ7i0RE1exHjHJSlPbs= github.com/redis/go-redis/v9 v9.16.0 h1:OotgqgLSRCmzfqChbQyG1PHC3tLNR89DG4jdOERSEP4= github.com/redis/go-redis/v9 v9.16.0/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= From c11c89c22ff6c31a5aec2b63090186b25ee8c4e4 Mon Sep 17 00:00:00 2001 From: Ethan Date: Fri, 22 May 2026 11:43:15 +0800 Subject: [PATCH 4/6] fix: address DeepSource findings on pagebuilder tests - GO-W1004: set SkipDefaultTransaction:true on the test-only gorm.Config in pagebuilder_test.go; the implicit transaction wrapping is unnecessary for the plain test connection. - GO-W5003: introduce a typed modelBuilderCtxKey instead of using bare string as the context key for *ModelBuilder lookups. Updated ContextValueProvider, the publish-side reader, and the publish_test.go inline producer to use it. - GO-R1005: silence the cyclomatic-complexity finding on TestPageBuilder via //skipcq. The 62 complexity comes from ~70 inline ReqFunc closures in a table-driven test; extracting them wouldn't reduce control-flow complexity meaningfully and would obscure the test data layout. Co-Authored-By: Claude Opus 4.7 (1M context) --- example/integration/pagebuilder_test.go | 3 ++- pagebuilder/model.go | 5 ++++- pagebuilder/publish.go | 2 +- pagebuilder/publish_test.go | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/example/integration/pagebuilder_test.go b/example/integration/pagebuilder_test.go index b6fbb0ac4..db06442ea 100644 --- a/example/integration/pagebuilder_test.go +++ b/example/integration/pagebuilder_test.go @@ -42,7 +42,7 @@ func TestMain(m *testing.M) { // via SetupDatabase. The user config relies on GORM creating associations for user roles, // so the plugin must not be active on this connection. var err error - TestDB, err = gorm.Open(postgres.Open(testSuite.DSN()), &gorm.Config{}) + TestDB, err = gorm.Open(postgres.Open(testSuite.DSN()), &gorm.Config{SkipDefaultTransaction: true}) if err != nil { panic(err) } @@ -108,6 +108,7 @@ SELECT setval('container_headers_id_seq', 10, true); `, []string{"page_builder_pages", "page_builder_containers", "container_headers"})) +// skipcq: GO-R1005 — table-driven test with ~70 cases; cyclomatic complexity is driven by inline ReqFunc closures, not by control flow that benefits from extraction. func TestPageBuilder(t *testing.T) { h := admin.TestHandler(TestDB, nil) dbr, _ := TestDB.DB() diff --git a/pagebuilder/model.go b/pagebuilder/model.go index ba9f2bea0..9aa9b8145 100644 --- a/pagebuilder/model.go +++ b/pagebuilder/model.go @@ -959,8 +959,11 @@ func (b *ModelBuilder) PreviewHTML(_ context.Context, obj interface{}) (r string return } +// modelBuilderCtxKey is a typed context key used to stash a *ModelBuilder under its model name; a custom type avoids string-key collisions across packages. +type modelBuilderCtxKey string + func (b *ModelBuilder) ContextValueProvider(in context.Context) context.Context { - return context.WithValue(in, b.name, b) + return context.WithValue(in, modelBuilderCtxKey(b.name), b) } func (b *ModelBuilder) ExistedL10n() bool { diff --git a/pagebuilder/publish.go b/pagebuilder/publish.go index b5e66912d..e71db3acf 100644 --- a/pagebuilder/publish.go +++ b/pagebuilder/publish.go @@ -16,7 +16,7 @@ func (p *Page) PublishUrl(db *gorm.DB, ctx context.Context, storage oss.StorageI err error localePath string ) - builder := ctx.Value(utils.GetObjectName(p)) + builder := ctx.Value(modelBuilderCtxKey(utils.GetObjectName(p))) b, ok := builder.(*ModelBuilder) if !ok { diff --git a/pagebuilder/publish_test.go b/pagebuilder/publish_test.go index 546015539..65dfae038 100644 --- a/pagebuilder/publish_test.go +++ b/pagebuilder/publish_test.go @@ -44,7 +44,7 @@ func TestPage_PublishUrl(t *testing.T) { TestDB.First(page, 1) b := New("/", TestDB, presets.New()) r := b.Model(b.pb.Model(page)) - s := page.PublishUrl(TestDB, context.WithValue(context.Background(), r.name, r), nil) + s := page.PublishUrl(TestDB, context.WithValue(context.Background(), modelBuilderCtxKey(r.name), r), nil) if s != "/12/123/index.html" { t.Log("Error Publish Url") } From 07b7b1474509e02a58938312eb69fd23b279721f Mon Sep 17 00:00:00 2001 From: Ethan Date: Fri, 22 May 2026 12:02:54 +0800 Subject: [PATCH 5/6] fix: revert typed ctx key in pagebuilder/publish, suppress GO-W5003 via skipcq The previous attempt to fix DeepSource GO-W5003 by introducing modelBuilderCtxKey in pagebuilder/model.go broke the cross-package context-key contract: publish package (and microsite, l10n) still read context.Value with utils.GetObjectName() as a plain string, so PublishUrl's lookup returned nil and TestPageBuilderCampaign sub-tests for publish/unpublish failed in CI. Restore the plain-string key on the producer (ContextValueProvider) and reader (PublishUrl) sides and annotate the test line that DeepSource flagged with a //skipcq:GO-W5003 directive. A clean typed-key fix requires coordinated changes across pagebuilder, publish, microsite, and l10n and is out of scope for this PR. Co-Authored-By: Claude Opus 4.7 (1M context) --- pagebuilder/model.go | 5 +---- pagebuilder/publish.go | 2 +- pagebuilder/publish_test.go | 3 ++- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/pagebuilder/model.go b/pagebuilder/model.go index 9aa9b8145..ba9f2bea0 100644 --- a/pagebuilder/model.go +++ b/pagebuilder/model.go @@ -959,11 +959,8 @@ func (b *ModelBuilder) PreviewHTML(_ context.Context, obj interface{}) (r string return } -// modelBuilderCtxKey is a typed context key used to stash a *ModelBuilder under its model name; a custom type avoids string-key collisions across packages. -type modelBuilderCtxKey string - func (b *ModelBuilder) ContextValueProvider(in context.Context) context.Context { - return context.WithValue(in, modelBuilderCtxKey(b.name), b) + return context.WithValue(in, b.name, b) } func (b *ModelBuilder) ExistedL10n() bool { diff --git a/pagebuilder/publish.go b/pagebuilder/publish.go index e71db3acf..b5e66912d 100644 --- a/pagebuilder/publish.go +++ b/pagebuilder/publish.go @@ -16,7 +16,7 @@ func (p *Page) PublishUrl(db *gorm.DB, ctx context.Context, storage oss.StorageI err error localePath string ) - builder := ctx.Value(modelBuilderCtxKey(utils.GetObjectName(p))) + builder := ctx.Value(utils.GetObjectName(p)) b, ok := builder.(*ModelBuilder) if !ok { diff --git a/pagebuilder/publish_test.go b/pagebuilder/publish_test.go index 65dfae038..1fb083f74 100644 --- a/pagebuilder/publish_test.go +++ b/pagebuilder/publish_test.go @@ -44,7 +44,8 @@ func TestPage_PublishUrl(t *testing.T) { TestDB.First(page, 1) b := New("/", TestDB, presets.New()) r := b.Model(b.pb.Model(page)) - s := page.PublishUrl(TestDB, context.WithValue(context.Background(), modelBuilderCtxKey(r.name), r), nil) + // skipcq: GO-W5003 — matches the cross-package plain-string ctx-key contract shared by pagebuilder/microsite/l10n ContextValueProvider and publish readers; switching to a typed key is out of scope here. + s := page.PublishUrl(TestDB, context.WithValue(context.Background(), r.name, r), nil) if s != "/12/123/index.html" { t.Log("Error Publish Url") } From 9c1dbad65089fb03bd1c07d3ec37a4fe4a1721bc Mon Sep 17 00:00:00 2001 From: Ethan Date: Fri, 22 May 2026 12:09:58 +0800 Subject: [PATCH 6/6] chore: drop skipcq directives, leave the two non-trivial findings unsuppressed Per review feedback: don't suppress DeepSource findings with //skipcq when we can't (or won't) cleanly fix them in this PR. The two remaining findings are: - GO-R1005 (cyclomatic complexity 62 on TestPageBuilder): the function is a 72-case table-driven test with inline ReqFunc closures; extracting them to helpers would just move the complexity, not reduce it. - GO-W5003 (string as ctx key in publish_test.go:47): pagebuilder writes and publish reads via a plain-string key today; switching to a typed key needs coordinated changes across pagebuilder, publish, microsite, and l10n. Leaving these for follow-up. Co-Authored-By: Claude Opus 4.7 (1M context) --- example/integration/pagebuilder_test.go | 1 - pagebuilder/publish_test.go | 1 - 2 files changed, 2 deletions(-) diff --git a/example/integration/pagebuilder_test.go b/example/integration/pagebuilder_test.go index db06442ea..3e385c058 100644 --- a/example/integration/pagebuilder_test.go +++ b/example/integration/pagebuilder_test.go @@ -108,7 +108,6 @@ SELECT setval('container_headers_id_seq', 10, true); `, []string{"page_builder_pages", "page_builder_containers", "container_headers"})) -// skipcq: GO-R1005 — table-driven test with ~70 cases; cyclomatic complexity is driven by inline ReqFunc closures, not by control flow that benefits from extraction. func TestPageBuilder(t *testing.T) { h := admin.TestHandler(TestDB, nil) dbr, _ := TestDB.DB() diff --git a/pagebuilder/publish_test.go b/pagebuilder/publish_test.go index 1fb083f74..546015539 100644 --- a/pagebuilder/publish_test.go +++ b/pagebuilder/publish_test.go @@ -44,7 +44,6 @@ func TestPage_PublishUrl(t *testing.T) { TestDB.First(page, 1) b := New("/", TestDB, presets.New()) r := b.Model(b.pb.Model(page)) - // skipcq: GO-W5003 — matches the cross-package plain-string ctx-key contract shared by pagebuilder/microsite/l10n ContextValueProvider and publish readers; switching to a typed key is out of scope here. s := page.PublishUrl(TestDB, context.WithValue(context.Background(), r.name, r), nil) if s != "/12/123/index.html" { t.Log("Error Publish Url")