luci-base: fix tab handling in modal dialogues
authorPaul Donald <redacted>
Thu, 29 Jan 2026 18:02:12 +0000 (19:02 +0100)
committerPaul Donald <redacted>
Thu, 29 Jan 2026 18:02:12 +0000 (19:02 +0100)
When adding tabs in modal dialogues, the old code of simply assigning
s.tabs = this.tabs was problematic because it was an array of objects
whose behaviour would cause a traceback when s.tab is called in e.g.
a protocol handler Modal when it was reopened.

Now verify the properties do not already exist before assigning them.

This means one can define s.tab(...) in a Modal UI without a try
block (which would even then sometimes fail).

Signed-off-by: Paul Donald <redacted>
modules/luci-base/htdocs/luci-static/resources/form.js

index ae55480d788115c384431f20e8fa6b378c23cbb4..c6e84e94e4bc62e7133747f8a7e14a0fc3cf87df 100644 (file)
@@ -3339,8 +3339,26 @@ const CBITableSection = CBITypedSection.extend(/** @lends LuCI.form.TableSection
                        m.section = section_id;
                        m.readonly = parent.readonly;
 
-                       s.tabs = this.tabs;
-                       s.tab_names = this.tab_names;
+                       /* Clone tabs as both array and object. Otherwise calling renderMoreOptionsModal (reopening
+                       the same Modal multiple times) results in errors when s.tab is called in the modal. This
+                       allows Modal dialogues that declare new tabs to be opened multiple times without re-creating
+                       tabs that 'already exist'. */
+                       if (this.tabs) {
+                               s.tabs = Array.from(this.tabs);
+                               for (const key in this.tabs) {
+                                       if (Object.prototype.hasOwnProperty.call(this.tabs, key) && isNaN(Number(key))) {
+                                               s.tabs[key] = this.tabs[key];
+                                       }
+                               }
+                       } else {
+                               s.tabs = undefined;
+                       }
+
+                       if (this.tab_names) {
+                               s.tab_names = Array.isArray(this.tab_names) ? this.tab_names.slice() : Object.assign({}, this.tab_names);
+                       } else {
+                               s.tab_names = undefined;
+                       }
 
                        this.cloneOptions(this, s);
 
git clone https://git.99rst.org/PROJECT