Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [Unreleased]

- **Fixed navigation page actions overriding page titles with an empty template** — `CREATE OR REPLACE NAVIGATION` wrote `FormSettings.TitleOverride` as an empty `Microflows$TextTemplate`. That is an explicit override to an empty string, not “no override”, so every newly authored page menu item added a CW0263 warning and could render without the page title. All three navigation writers now emit the Studio Pro-authored shape, an explicit null, matching the already-correct page-button and show-page writers.

## [0.20.0] - 2026-08-28

Headline: **Mendix 11.14 and Java 25, and the layer wrapped around a page — layouts, navigation profiles, mappings, translations and themes — becomes something a script can write.** A blank 11.14 project now builds, boots and runs as generated. `CREATE LAYOUT` closes the last document a page depends on that MDL could not write, so an app no longer has to start on an Atlas layout it cannot touch; a census of 327 real mapping documents turned the import/export mapping surface from a guess into a list and this release works through it; translations survive a rewrite and can be authored in bulk; and a project can carry, scaffold and switch between its own themes. Running underneath all of it is one theme: **a statement that reported success now has to have meant something.** Several of the fixes below are round-trips where `describe` printed a document mxcli could not express as one it could, and that output parsed — so the model was rebuilt, valid, and different.
Expand Down
13 changes: 13 additions & 0 deletions mdl/backend/modelsdk/navigation_icon_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,19 @@ func TestNavMenuItemBson_CarriesTheIconThrough(t *testing.T) {
}
}

// Studio Pro stores the absence of a page-title override as an explicit null.
// An empty TextTemplate is a real override to "" and raises CW0263.
func TestNavFormSettingsBson_NoTitleOverrideStaysNull(t *testing.T) {
settings := navFormSettingsBson("M.Dash")
title, present := navIconEntry(settings, "TitleOverride")
if !present {
t.Fatal("TitleOverride key missing; Studio Pro writes an explicit null")
}
if title != nil {
t.Fatalf("TitleOverride = %#v, want nil", title)
}
}

func TestMenuIconOf_NilIconYieldsNothing(t *testing.T) {
typeName, image := menuIconOf(nil)
if typeName != "" || image != "" {
Expand Down
17 changes: 3 additions & 14 deletions mdl/backend/modelsdk/navigation_write.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,9 @@ func navFormSettingsBson(formName string) bson.D {
{Key: "$Type", Value: "Forms$FormSettings"},
{Key: "Form", Value: formName},
{Key: "ParameterMappings", Value: bson.A{int32(1)}},
{Key: "TitleOverride", Value: navEmptyTextTemplate()},
// No override is an explicit null. An empty template overrides the page
// title with "" and produces CW0263 for every authored menu item (#812).
{Key: "TitleOverride", Value: nil},
}
}

Expand Down Expand Up @@ -295,16 +297,3 @@ func navMenuAction(mi types.NavMenuItemSpec) bson.D {
{Key: "$Type", Value: "Forms$NoAction"},
}
}

func navEmptyTextTemplate() bson.D {
return bson.D{
{Key: "$ID", Value: navID()},
{Key: "$Type", Value: "Microflows$TextTemplate"},
{Key: "Parameters", Value: bson.A{int32(2)}},
{Key: "Text", Value: bson.D{
{Key: "$ID", Value: navID()},
{Key: "$Type", Value: "Texts$Text"},
{Key: "Items", Value: bson.A{int32(2)}},
}},
}
}
19 changes: 3 additions & 16 deletions modelsdk/mpr/nav_patch.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,9 @@ func navpBuildFormSettingsBson(formName string) bson.D {
{Key: "$Type", Value: "Forms$FormSettings"},
{Key: "Form", Value: formName},
{Key: "ParameterMappings", Value: bson.A{int32(1)}},
{Key: "TitleOverride", Value: navpEmptyTextTemplate()},
// No override is an explicit null. An empty template overrides the page
// title with "" and produces CW0263 for every authored menu item (#812).
{Key: "TitleOverride", Value: nil},
}
}

Expand Down Expand Up @@ -320,21 +322,6 @@ func navpBuildMenuAction(mi types.NavMenuItemSpec) bson.D {
}
}

// navpEmptyTextTemplate returns an empty Microflows$TextTemplate embedded BSON document.
// Used for TitleOverride on Forms$FormSettings.
func navpEmptyTextTemplate() bson.D {
return bson.D{
{Key: "$ID", Value: idToBsonBinary(generateUUID())},
{Key: "$Type", Value: "Microflows$TextTemplate"},
{Key: "Parameters", Value: bson.A{int32(2)}},
{Key: "Text", Value: bson.D{
{Key: "$ID", Value: idToBsonBinary(generateUUID())},
{Key: "$Type", Value: "Texts$Text"},
{Key: "Items", Value: bson.A{int32(2)}},
}},
}
}

// navpSetBsonField sets a top-level field in a bson.D, adding it if not found.
func navpSetBsonField(doc bson.D, key string, value any) bson.D {
for i, elem := range doc {
Expand Down
31 changes: 31 additions & 0 deletions modelsdk/mpr/nav_patch_title_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: Apache-2.0

package mpr

import (
"testing"

"go.mongodb.org/mongo-driver/v2/bson"
)

func navpTestEntry(d bson.D, key string) (interface{}, bool) {
for _, entry := range d {
if entry.Key == key {
return entry.Value, true
}
}
return nil, false
}

// Studio Pro stores the absence of a page-title override as an explicit null.
// An empty TextTemplate is a real override to "" and raises CW0263.
func TestNavpBuildFormSettingsBson_NoTitleOverrideStaysNull(t *testing.T) {
settings := navpBuildFormSettingsBson("M.Dash")
title, present := navpTestEntry(settings, "TitleOverride")
if !present {
t.Fatal("TitleOverride key missing; Studio Pro writes an explicit null")
}
if title != nil {
t.Fatalf("TitleOverride = %#v, want nil", title)
}
}
4 changes: 3 additions & 1 deletion sdk/mpr/writer_navigation.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,9 @@ func buildFormSettingsBson(formName string) bson.D {
{Key: "$Type", Value: "Forms$FormSettings"},
{Key: "Form", Value: formName},
{Key: "ParameterMappings", Value: bson.A{int32(1)}},
{Key: "TitleOverride", Value: emptyTextTemplate()},
// No override is an explicit null. An empty template overrides the page
// title with "" and produces CW0263 for every authored menu item (#812).
{Key: "TitleOverride", Value: nil},
}
}

Expand Down
13 changes: 13 additions & 0 deletions sdk/mpr/writer_navigation_icon_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,19 @@ func TestBuildMenuItemBson_NoIconStillWritesNull(t *testing.T) {
}
}

// Studio Pro stores the absence of a page-title override as an explicit null.
// An empty TextTemplate is a real override to "" and raises CW0263.
func TestBuildFormSettingsBson_NoTitleOverrideStaysNull(t *testing.T) {
settings := buildFormSettingsBson("M.Dash")
title, present := navIconEntry(settings, "TitleOverride")
if !present {
t.Fatal("TitleOverride key missing; Studio Pro writes an explicit null")
}
if title != nil {
t.Fatalf("TitleOverride = %#v, want nil", title)
}
}

// The read side has to recognise all three variants, because a project authored
// in Studio Pro contains all three. The fixtures are the literal shapes dumped
// from ako/mxcli-ledger's navigation document.
Expand Down
Loading