BlueSpiceConfigManager: PluggableAuth json edits dont reactivate save button

Describe the issue / Steps to reproduce:

When i change the PluggableAuth Data object (JSON) value, the Save button does not recognize i changed it.

I always have to delete the entry and re add it as a new entry.

Expected behavior:
Recognize i changed it.

What was the error message/error log?
Did the screen show an error? Did you look at any error logs? This info will speed up the solution process.

Screenshots

If applicable, add screenshots to help explain your problem.

System info:
view system requirements

Bluespice via Docker,

BlueSpiceConfigManager 5.1.3

Thanks for reporting. We’ll try to reproduce.

Hello SaiCode,

thanks for the feedback. One further detail I would like to confirm:

did you see any error message / failed network request in the developer’s tool of your browser, when you try to save your config?

Greetings,

Hua

There are no errors. The button stays simply disabled.

Find the following gif attached:

librewolf_iHJOXYKU3s

I need to modify something else on the page for the change to be recognized.

Also perhaps improve the input there to be a Textarea or even real json editor so i dont need to modify it like this.

Hello SaiCode,

thanks for your feedback. An internal bug ticket is created to track this issue, and we will let you know when a fix is included in a future release.

Greetings,
Hua

I have the same error, and additionally, if I delete the entire config and make a new one the fields restore to the previous (deleted) config profile; in other words there is no way to actually change these fields once they’ve been saved for the first time.

1. Save button doesn’t reactivate when editing a JSON value (the original report)

KeyValueInputWidget emits change in exactly two places:

  • BlueSpiceFoundation/resources/bluespice.oojs/ui/widget/KeyValueInputWidget.js:270 — in onAddClick()
  • .../KeyValueInputWidget.js:332 — in onDeleteClick()

BlueSpiceConfigManager/resources/ui/pages/ConfigPage.js:92 is what enables the Save button, and it only listens for that change event.

Nothing connects the inputs of an existing row to it. getForm() (KeyValueInputWidget.js:111, addToWidgets at :117) stores the key/value widgets but never subscribes to
their change, and ObjectInputWidget.createInputs() (ObjectInputWidget.js:15–33) builds its sub-widgets without re-emitting change either.

So typing into “Plugin name”, “Data object (JSON)” or “Group sync settings (JSON)” on a saved row is genuinely invisible to the form — which is exactly why the button stays grey
and why the only workaround is delete + re-add (that path does go through onAddClick/onDeleteClick).

Fix direction: have ObjectInputWidget emit change when any sub-widget changes, and have getForm() connect the key and value widgets to re-emit change when
addToWidgets is set. I have not implemented or tested this one, unlike the two below.


2. Saved config is silently corrupted when re-rendered — this is the “fields restore to the previous profile” problem

This is the one I reported on 9 January, and it’s a one-character-class bug.

BlueSpiceFoundation/src/Html/OOUI/KeyObjectInputWidget.php:42, in getValueInput():

if ( $conf['type'] === 'json' ) {
    $value[$key] = is_string( $value[$key] )
        ? $value[$key]
        : FormatJson::encode( $value );   // encodes the WHOLE entry
}

It encodes $value (the entire entry) instead of $value[$key] (that field’s own value). And because the result is assigned back into $value[$key], the corruption compounds
across loop iterations — the second JSON field gets the already-corrupted first one nested inside it.

With this stored:

{"Login with Google":{"plugin":"OpenIDConnect","data":{"providerURL":"https://accounts.google.com","clientID":"…","clientsecret":"…"},"groupsyncs":[]}}

the form renders as:

Button label               => Login with Google                                    ✓
Plugin name                => OpenIDConnect                                        ✓
Data object (JSON)         => {"plugin":"OpenIDConnect","data":{…},"groupsyncs":[]} ✗ the whole entry
Group sync settings (JSON) => {"plugin":"OpenIDConnect","data":"{\"plugin\":\"…     ✗ that again, nested

Press Save from that state and the garbage is written back to bs_settings3. In our bs-config-manager log this shows up as a working config going straight to empty:

DistributionConnectorPluggableAuthConfig: {full config} -> {}

That is why the fields appear to “restore to the previous profile” and why the config can never be edited after the first save.

Fix (verified — after this, the sub-fields round-trip correctly):

--- a/src/Html/OOUI/KeyObjectInputWidget.php
+++ b/src/Html/OOUI/KeyObjectInputWidget.php
@@ -39,7 +39,7 @@
                              if ( $conf['type'] === 'json' ) {
                                      $value[$key] = is_string( $value[$key] )
                                              ? $value[$key]
-                                             : FormatJson::encode( $value );
+                                             : FormatJson::encode( $value[$key] );
                              }

After the patch:

Data object (JSON)         => {"providerURL":"https://accounts.google.com","clientID":"…","clientsecret":"…"}
Group sync settings (JSON) => []

Note this widget is generic, but PluggableAuth/Config.php is the only config definition in the distribution that uses TYPE_JSON sub-fields (EventBus/EventServices.php uses
only TYPE_TEXT/TYPE_NUMBER), so PluggableAuth is the only visible victim.


3. Saving drops you back on the first tab

After saving, the ConfigManager jumps to Administration instead of staying on Authentication, which makes a successful save look like it did nothing.

BlueSpiceConfigManager/resources/ui/panel/ConfigManager.js:134:

if ( !this.selectedPage || !configPages.includes( this.selectedPage ) ) {
if ( !this.selectedPage || !configPages.includes( this.selectedPage ) ) {

The booklet’s select handler sets this.selectedPage = item.data — a page name string — but this compares it against configPages, an array of ConfigPage objects.
The check can never match, so every store reload falls through to selectFirstSelectablePage(). The else branch calling setPage( this.selectedPage ) confirms a string was
intended.

Fix:

--- a/resources/ui/panel/ConfigManager.js
+++ b/resources/ui/panel/ConfigManager.js
@@ -132,9 +132,10 @@
      this.bookletLayout.addPages( configPages );

-     if ( !this.selectedPage || !configPages.includes( this.selectedPage ) ) {
+     const configPageNames = configPages.map( ( configPage ) => configPage.getName() );
+     if ( !this.selectedPage || !configPageNames.includes( this.selectedPage ) ) {
              this.bookletLayout.selectFirstSelectablePage();
-             this.selectedPage = this.bookletLayout.getCurrentPage();
+             this.selectedPage = this.bookletLayout.getCurrentPage().getName();
      } else {
              this.bookletLayout.setPage( this.selectedPage );
      }

One more, smaller

JsonArrayInputWidget.getValue() wraps JSON.parse in a try/catch and returns {} on failure, and the widget has no getValidity(), so it is skipped by
validateAddNewForm() entirely. A typo in “Data object (JSON)” is therefore accepted silently and saved as empty, with no error shown anywhere. That supports the earlier
suggestion in this thread for a proper JSON editor — or at minimum, validation that refuses to save unparseable input.

Also worth noting: ConfigManager.js’s save handler only checks response.error. The configmanager save task returns success: false with result.message when a record
fails validation, which the UI never surfaces — so a rejected save reports nothing at all to the user.