Skip to content

Enhancements to custom pages (BL-15948)#7766

Open
JohnThomson wants to merge 5 commits intomasterfrom
customPageImprovements
Open

Enhancements to custom pages (BL-15948)#7766
JohnThomson wants to merge 5 commits intomasterfrom
customPageImprovements

Conversation

@JohnThomson
Copy link
Contributor

@JohnThomson JohnThomson commented Mar 24, 2026

  • Add format dialog for read-only fields on custom pages
  • Remove cover-is-image book setting and migrate to custom layout
  • Keep image selected after "become background" command
  • Make hint bubbles work on custom pages

Open with Devin

This change is Reviewable

@JohnThomson JohnThomson changed the title Enhancements to custom pages Enhancements to custom pages (BL-15948) Mar 24, 2026
Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

View 3 additional findings in Devin Review.

Open in Devin Review

Comment on lines +4599 to +4611
if (
outsideFrontCover.HasClass("bloom-customLayout")
&& !string.IsNullOrEmpty(
outsideFrontCover.GetAttribute("data-custom-layout-id")
)
)
{
var onePageDom = new HtmlDom("<html><head></head><body></body></html>");
onePageDom.Body.AppendChild(
onePageDom.RawDom.ImportNode(outsideFrontCover, true)
);
bookData.SuckInDataFromEditedDom(onePageDom, BookInfo);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 MigrateToLevel14 can dereference null bookData when coverIsImage was present with data-custom-layout-id

At BookStorage.cs:4610, bookData.SuckInDataFromEditedDom(onePageDom, BookInfo) is called but bookData is a nullable parameter (default null). If the outsideFrontCover has both bloom-customLayout class (just added on line 4595) AND a data-custom-layout-id attribute, and bookData is null, this would throw a NullReferenceException. In practice, the call from Book.cs:1872 always passes _bookData, but the method signature allows null. The condition on line 4600 (outsideFrontCover.HasClass("bloom-customLayout")) is always true since line 4595 just added that class, so the only real gate is data-custom-layout-id. For a standard cover-is-image page, the xmatter template might or might not set this attribute. If it does and a caller passes null bookData, this crashes.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor Author

@JohnThomson JohnThomson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JohnThomson made 2 comments and resolved 2 discussions.
Reviewable status: 0 of 15 files reviewed, all discussions resolved.

Comment on lines +4599 to +4611
if (
outsideFrontCover.HasClass("bloom-customLayout")
&& !string.IsNullOrEmpty(
outsideFrontCover.GetAttribute("data-custom-layout-id")
)
)
{
var onePageDom = new HtmlDom("<html><head></head><body></body></html>");
onePageDom.Body.AppendChild(
onePageDom.RawDom.ImportNode(outsideFrontCover, true)
);
bookData.SuckInDataFromEditedDom(onePageDom, BookInfo);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Contributor

@StephenMcConnel StephenMcConnel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@StephenMcConnel reviewed 15 files and all commit messages.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on JohnThomson).

@JohnThomson JohnThomson force-pushed the customPageImprovements branch from 73bef05 to 23693b9 Compare March 25, 2026 18:10
Copy link
Contributor Author

@JohnThomson JohnThomson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JohnThomson made 1 comment and resolved 1 discussion.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on JohnThomson).

Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 new potential issues.

View 7 additional findings in Devin Review.

Open in Devin Review

Comment on lines +4598 to +4600
outsideFrontCover.AddClass("bloom-customLayout");

BookInfo.AppearanceSettings.PendingChangeRequiresXmatterUpdate = true;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Level 14 migration adds bloom-customLayout without data-custom-layout-id, causing it to be lost on next xmatter update

The MigrateToLevel14CoverIsImageToCustomLayout method at src/BloomExe/Book/BookStorage.cs:4598 adds bloom-customLayout class to the outside front cover but never sets data-custom-layout-id. It then sets PendingChangeRequiresXmatterUpdate = true at line 4600, which signals that the xmatter should be re-injected. When BringXmatterHtmlUpToDate runs (either triggered by the pending flag or on the next book update), the preservation code at src/BloomExe/Book/Book.cs:2548-2582 only preserves bloom-customLayout for pages that have BOTH bloom-customLayout AND data-custom-layout-id. Since the migration never sets data-custom-layout-id, the bloom-customLayout class will be stripped and the restored margin box content will be replaced with the standard xmatter template, resulting in loss of the user's custom cover content.

Xmatter preservation code that requires data-custom-layout-id

At Book.cs:2548-2582, before xmatter removal, customLayoutIds is collected from pages with both bloom-customLayout AND data-custom-layout-id. After re-injection, bloom-customLayout is only re-added to pages whose data-custom-layout-id is in that set. Without the attribute, the custom layout is lost.

Prompt for agents
In src/BloomExe/Book/BookStorage.cs, in the MigrateToLevel14CoverIsImageToCustomLayout method, after adding bloom-customLayout class at line 4598, also set data-custom-layout-id on the outsideFrontCover element. A reasonable value would be something like "customOutsideFrontCover" or derived from the page's data-xmatter-page attribute. This is required because BringXmatterHtmlUpToDate in src/BloomExe/Book/Book.cs (lines 2548-2582) only preserves bloom-customLayout for pages that also have data-custom-layout-id. Without it, the bloom-customLayout class and restored content will be lost on the next xmatter re-injection. Also reconsider whether PendingChangeRequiresXmatterUpdate should be set at all - if it triggers another BringXmatterHtmlUpToDate, the migration's work will be undone. The capture/restore mechanism only runs once (in CaptureInitialStateForMigration/RestoreStuffBeforeMigration), so a second xmatter injection would replace the restored content.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

outsideFrontCover.RemoveClass("cover-is-image");
outsideFrontCover.AddClass("bloom-customLayout");

BookInfo.AppearanceSettings.PendingChangeRequiresXmatterUpdate = true;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 PendingChangeRequiresXmatterUpdate may cause unintended second xmatter injection

At src/BloomExe/Book/BookStorage.cs:4600, PendingChangeRequiresXmatterUpdate is set to true. This flag was historically used when the coverIsImage setting changed in the UI to signal that xmatter needed re-injection. In the migration context, xmatter has already been re-injected (in EnsureUpToDateMemoryUnprotected), and the capture/restore mechanism has already run. If this flag causes another BringXmatterHtmlUpToDate call during the save path, the restored content and bloom-customLayout class would both be lost. It's worth investigating whether this flag is still consumed anywhere and whether setting it here is intentional or vestigial from the old coverIsImage flow.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants