Skip to content

Fix summernote fields#5946

Draft
TanNhatCMS wants to merge 3 commits into
Laravel-Backpack:mainfrom
TanNhatCMS:fix-summernote-fields
Draft

Fix summernote fields#5946
TanNhatCMS wants to merge 3 commits into
Laravel-Backpack:mainfrom
TanNhatCMS:fix-summernote-fields

Conversation

@TanNhatCMS

Copy link
Copy Markdown
Contributor

Pull Request Description

WHY

BEFORE – What was wrong?

  • Summernote always loaded the summernote-lite build, which is not fully compatible with Backpack themes based on Bootstrap 4 or Bootstrap 5.

  • No automatic language loading based on the application's locale.

  • No safe fallback when the locale is not supported by Summernote.

  • The image upload logic was unclear and partially duplicated.

  • Critical issue: data-ajax-upload-endpoint was previously defined inside a <script> wrapped in @bassetBlock, which caused Basset compilation errors:

    • Basset cannot reliably process Blade expressions inside @bassetBlock.
    • As a result, the upload endpoint became empty or undefined at runtime.
    • Summernote image uploads failed because JavaScript could not access a valid endpoint.

AFTER – What is happening now?

  • Summernote now automatically loads the correct build based on the active Backpack theme (lite, bs4, or bs5).

  • Automatic loading of the correct Summernote language file based on the application's locale.

  • Unsupported locales safely fall back to en-US.

  • Image upload works reliably because:

    • data-ajax-upload-endpoint is now placed directly in the HTML markup.
    • JavaScript always receives the correct upload endpoint.
  • Cleaner, more maintainable code.

  • Improved compatibility with multiple Backpack themes and multilingual setups.


HOW

Technical explanation

  • Added logic to detect the active Backpack theme and map it to the appropriate Summernote build.
  • Added internal locale → Summernote locale mapping and a list of supported languages.
  • Load Summernote JS/CSS dynamically based on theme and locale.
  • Moved data-ajax-upload-endpoint out of the @bassetBlock and into the textarea’s HTML attributes, solving Basset compilation issues.
  • Improved error handling and feedback display.
  • Kept all existing Summernote callbacks (onChange, disable, enable, etc.) intact.

Is this a breaking change?

No.
Existing Summernote fields continue working normally.
This PR only introduces improvements, better compatibility, and a fix for the upload endpoint bug.


Testing Instructions

  1. Image Upload

    • Before: fails due to undefined endpoint.
    • After: upload works correctly.
  2. Theme Compatibility

    • Test with backpack.theme-coreuiv2 (Bootstrap 4)
    • Test with backpack.theme-coreuiv4 and backpack.theme-tabler (Bootstrap 5)
    • No theme → Summernote Lite
  3. Locale Support

    • APP_LOCALE=vi → loads summernote-vi-VN.js
    • APP_LOCALE=ko → loads summernote-ko-KR.js
    • Unsupported locale → falls back to en-US
  4. Repeatable Fields

    • Ensure uploads work inside repeatable structures.
  5. Modal Rendering

    • Confirm Summernote dialogs render above Bootstrap overlays.

Improve the Summernote CRUD field: detect Backpack UI theme and load corresponding Summernote theme (bs5/bs4/lite), map app locales to Summernote locale codes and conditionally include language files, and set the editor lang option. Add explicit ajax-upload endpoint attribute and improve image upload handling (use ajax-upload-endpoint fallback, support repeatable field param names, better XHR error handling, remove previous error messages and render server validation errors as invalid-feedback). Minor options/defaults and formatting tweaks for CSS/JS asset inclusion and callbacks initialization.
Add CSS to ensure the Summernote toolbar (card-header.note-toolbar) is displayed in the editor frame. Also tidy up template indentation and formatting in the blade file and JS basset block for readability; no functional changes to the upload handling or callbacks were made.
Use PHP string concatenation for @basset calls so $summernoteTheme and $fullLocale resolve correctly, and make the bassetBlock locale-specific for per-locale JS. Add type="text/css" to the style tag, tidy up indentation/whitespace and normalize JS function spacing and event handler signatures. These changes ensure correct asset loading and improve code consistency.
@TanNhatCMS

Copy link
Copy Markdown
Contributor Author

@tabacitu Could you please review the code for me?

@pxpm

pxpm commented Mar 6, 2026

Copy link
Copy Markdown
Contributor

Hello @TanNhatCMS,

Thanks for the PR. I agree there are some improvements we can do in this field.

There is a known issue when using $variables inside @basset() blocks. You can’t really “cache” variable-based assets properly, so they only get cached at run time, which can be a problem for some deployments.

We worked on a possible solution in Basset v2, but we are not totally happy with it. It works, but the ergonomics are not great:

Basset::map($file, $file);

It’s also used to load those config files because they require run-time evaluation. We haven’t really “publicized” this feature because, like I said, we are not 100% happy with it, and we probably need to spend more time on this topic.

But the main idea is something similar to what we are doing for the elFinder manager:
https://github.com/Laravel-Backpack/FileManager/blob/8a997bad51fb60753d3b0f0648a01ab94e8fe803/src/FileManagerServiceProvider.php#L41

When you run basset:cache, Basset will also read that map and cache those assets. In this case, it will cache the FileManager file for all available languages. In the view files, you can then use those “mapped” assets like this:
https://github.com/Laravel-Backpack/FileManager/blob/8a997bad51fb60753d3b0f0648a01ab94e8fe803/resources/views/common_scripts.blade.php#L23

As you can see, the usability is not very good or polished. If we now wanted to add the same “map” for the Summernote languages, we would need to add more logic to the provider… and I don’t think that’s a good approach.

I think we probably need something similar to a “routing” system, but for assets. Something like this:

// say you create an assets.php file in your app.

// basset knows how to replace the `lang` placeholder by looping through
// the available languages in your app, but it does not know how to resolve
// `custom`. So you should be able to register "resolvers" for custom
// parameters that basset does not know about.

Basset::resolveWith('custom', function () {
    // if theme is core ui bla bla bla
    return 'bs5';
});

// now that you registered the `custom` resolver, you can register
// your "dynamic asset":
Basset::asset('my-dynamic-asset', 'https://myasset.{lang}.something.{custom}.js');

// and then use it in your views like:
@basset('my-dynamic-asset')

In the above example (and similar to what currently happens with Basset::map()), you would have all files cached for all available language/custom variations, and only the correct one would be used at runtime.

With the current state of the @basset() directive alone, when you run basset:cache and you have an asset that supports multiple languages, only the default system language will be cached. If a user visits a page using that asset but with a different locale, the asset for that language will be downloaded “on the fly”, because it does not exist on disk yet.

I hope I will have some time soon to work on this. I think it’s a very important missing feature, but it needs to be solved upstream in basset, not here in CRUD.

Sorry for the late response. Feel free to ask any questions 👍

@TanNhatCMS TanNhatCMS marked this pull request as draft March 11, 2026 16:07
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