Skip to content

Use the bloom image gallery (BL-16444)#7982

Open
JohnThomson wants to merge 2 commits into
masterfrom
bloomImageGallery
Open

Use the bloom image gallery (BL-16444)#7982
JohnThomson wants to merge 2 commits into
masterfrom
bloomImageGallery

Conversation

@JohnThomson

@JohnThomson JohnThomson commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Devin review


This change is Reviewable

@greptile-apps

greptile-apps Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR replaces the legacy palaso ImageToolboxDialog (a WinForms-hosted AOR browser) with a new React-based ImageGalleryDialog that wraps the bloom-image-gallery npm package, adding support for Pixabay, OpenVerse, and local file picking alongside the existing Art of Reading integration.

  • Front-end: A new ImageGalleryDialog component handles image selection, GIF bypass, request-page-content delay management, and provider-key persistence. doImageCommand is refactored to dispatch paste/copy early and route "change" either to the gallery dialog or an async GIF picker (doChangeGifImage) that correctly uses wrapWithRequestPageContentDelay.
  • Back-end: Six new API endpoints are added (imageGalleryResult, pickLocalImageFile, localFilePreview, AOR collections/search/image). The old HandleChangeImage and OnChangeImage WinForms path are removed entirely, along with the MetadataEditIsFromImageToolbox callback plumbing.
  • Removed protections: The official-collection and stock-game-image read-only guards in PrepareToEditImageMetadata, and ImageIsStockGameImage itself, are deleted — this is called out in prior review threads and the team should verify how that protection is now enforced.

Important Files Changed

Filename Overview
src/BloomBrowserUI/react_components/image-gallery/ImageGalleryDialog.tsx New dialog wrapping the bloom-image-gallery component; handles key loading, local file picking, and commit flow with proper request-page-content delay management. Missing useEffect justification comment and dialog title is not localized.
src/BloomExe/web/controllers/EditingViewApi.cs Adds six new API endpoints (imageGalleryResult, pickLocalImageFile, localFilePreview, AOR collections/search/image). Path-traversal guards are present in HandleArtOfReadingSearch and HandleArtOfReadingCollectionImage. Synchronous WebClient download in HandleImageGalleryResult could block the thread on slow connections.
src/BloomBrowserUI/bookEdit/js/bloomImages.ts Restructured doImageCommand: paste/copy handled early; change now opens gallery dialog or async GIF picker. doChangeGifImage correctly uses wrapWithRequestPageContentDelay and cleans up imageId on cancel and error.
src/BloomExe/Edit/EditingView.cs Removes palaso ImageToolboxDialog integration and the official-collection / stock-game image guard in PrepareToEditImageMetadata. The guard removal is flagged in prior review threads.
src/BloomExe/Book/ImageUpdater.cs Removes ImageIsStockGameImage and the stock game image struct; this method was the BL-14610 guard for smiling-flowers.gif and sad-face.gif metadata, also flagged in prior threads.
src/BloomExe/web/controllers/CopyrightAndLicenseApi.cs Removes the image-toolbox metadata callback branch, simplifying the POST handler to always go through SaveThen/SaveImageMetadata.
src/BloomBrowserUI/bookEdit/editablePage.ts Adds changeImage and removeImageId to IPageFrameExports interface so cross-frame callers are properly typed.
src/BloomBrowserUI/bookEdit/workspaceRoot.ts Adds showImageGalleryDialog to the workspace bundle API surface; clean wiring through to the new React dialog.
DistFiles/localization/en/Bloom.xlf Adds 130+ ImageLibrary.* localization strings for the gallery UI. No existing translations were modified or IDs changed.
src/BloomExe/Properties/Settings.Designer.cs Adds ImageGalleryProviderKeys user setting for persisting API keys across sessions.

Reviews (6): Last reviewed commit: "post-PR improvements" | Re-trigger Greptile

Comment thread src/BloomBrowserUI/bookEdit/js/bloomImages.ts
Comment thread src/BloomExe/web/controllers/EditingViewApi.cs
Comment on lines 548 to 553
return null; // exception handled in ImageUpdater
}

if (ImageUpdater.ImageHasMetadata(imageBeingModified))
{
// If we have metadata with an official collectionUri
// just give a summary of the metadata
if (
ImageUpdater.ImageIsFromOfficialCollection(imageBeingModified.Metadata)
|| ImageUpdater.ImageIsStockGameImage(fileName, imageBeingModified.Metadata)
)
{
MessageBox.Show(
imageBeingModified.Metadata.GetSummaryParagraph(
new string[] { "en" },
out string _
)
);
return null;
}
}

var metadata = imageBeingModified.Metadata;
// If the license is not set, default to CC-BY.
metadata.SetupReasonableLicenseDefaultBeforeEditing();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Official-collection and stock-game image guards removed from PrepareToEditImageMetadata

The PR deletes the block that checked ImageUpdater.ImageIsFromOfficialCollection and ImageUpdater.ImageIsStockGameImage before returning metadata. Before this change, opening the Copyright/License dialog for an Art of Reading image (or for the Pixabay stock game GIFs smiling-flowers.gif/sad-face.gif from BL-14610) would display a read-only summary and return null, preventing edits. Now PrepareToEditImageMetadata returns the full editable metadata for those images, so a user can overwrite authoritative attribution data (CollectionUri, creator, license) that Bloom relies on to identify official-collection images. The ImageIsStockGameImage method is also removed entirely from the codebase, so the BL-14610 protection is fully gone. A guard equivalent to the old check (or an explanation of how that protection is now handled elsewhere) should be restored before merging.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We made a deliberate decision not to lock these credits any more

@JohnThomson JohnThomson force-pushed the bloomImageGallery branch 2 times, most recently from 738d6af to f1f9571 Compare June 19, 2026 19:17
Comment on lines +732 to +734
if (!string.IsNullOrEmpty(localPath))
{
sourceFilePath = localPath;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 security localPath copied without whitelist check in HandleImageGalleryResult

HandleLocalFilePreview correctly guards against arbitrary file reads by checking the requested path against _lastPickedLocalImagePath. HandleImageGalleryResult has no equivalent guard: any localPath value passed in the POST body is used as-is as sourceFilePath. For the GIF branch (Path.GetExtension == ".gif") the file is copied byte-for-byte into the book folder with RobustFile.Copy, so passing localPath: "C:\Users\user\private.gif" would silently exfiltrate that file into the book. The two legitimate callers (doChangeGifImage and onConfirmSelection) always supply a path that either came from HandlePickLocalImageFile or the AOR search endpoint, so server-side validation would not break normal use. A guard equivalent to the one in HandleLocalFilePreview (compare against _lastPickedLocalImagePath, or verify the path starts with ArtOfReadingBaseFolder) should be applied before accepting localPath.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I believe we dealt with this

@JohnThomson JohnThomson left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@JohnThomson made 3 comments.
Reviewable status: 0 of 16 files reviewed, 3 unresolved discussions.

Comment on lines 548 to 553
return null; // exception handled in ImageUpdater
}

if (ImageUpdater.ImageHasMetadata(imageBeingModified))
{
// If we have metadata with an official collectionUri
// just give a summary of the metadata
if (
ImageUpdater.ImageIsFromOfficialCollection(imageBeingModified.Metadata)
|| ImageUpdater.ImageIsStockGameImage(fileName, imageBeingModified.Metadata)
)
{
MessageBox.Show(
imageBeingModified.Metadata.GetSummaryParagraph(
new string[] { "en" },
out string _
)
);
return null;
}
}

var metadata = imageBeingModified.Metadata;
// If the license is not set, default to CC-BY.
metadata.SetupReasonableLicenseDefaultBeforeEditing();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We made a deliberate decision not to lock these credits any more

Comment thread src/BloomExe/web/controllers/EditingViewApi.cs
Comment on lines +732 to +734
if (!string.IsNullOrEmpty(localPath))
{
sourceFilePath = localPath;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I believe we dealt with this

@hatton hatton left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@hatton reviewed 13 files and made 3 comments.
Reviewable status: 13 of 16 files reviewed, 5 unresolved discussions (waiting on JohnThomson).


src/BloomExe/web/controllers/EditingViewApi.cs line 164 at r4 (raw file):

                false
            );
            apiHandler.RegisterEndpointHandler(

I thought that we had sort of a generic way of doing these local collections, but I could be wrong. I'm not certain that there are any local collections out there. I just want to make sure that we aren't closing the door on that without considering whether it could have been done generically.


DistFiles/localization/en/Bloom.xlf line 3295 at r4 (raw file):

        <source xml:lang="en">Training Videos</source>
        <note>ID: HelpMenu.trainingVideos</note>
      </trans-unit>

Please let's put these into various priorities. E.g. the main buttons you see are all high priority and anything related to a particular collection is low priority. You can use your judgment beyond that.


src/BloomExe/web/controllers/EditingViewApi.cs line 1037 at r4 (raw file):

        /// file directly without an extra HTTP round-trip.
        /// </summary>
        private void HandleArtOfReadingSearch(ApiRequest request)

Same as above I think we have a generic standard for these collections. See https://github.com/sillsdev/image-collection-starter

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