Skip to content

Commit a000cc9

Browse files
JohnMcLearclaude
andauthored
feat(padToggle): honor runtime settings.enablePluginPadOptions flag (#9)
etherpad-lite #7698 (merged) added a feature flag — settings.enablePluginPadOptions, default false per AGENTS.MD §52 — that gates the ep_* padOptions passthrough at runtime. The helper was only checking the static PluginCapabilities flag (capability present in core) and not the runtime gate, so on a patched core where the admin hasn't opted in the helper would still render the pad-wide checkbox even though the broadcast/persist path was off. Read the flag from loadSettings's args.settings.enablePluginPadOptions and feed it into both eejsBlock_padSettings (no-op when off) and the clientVars padWideSupported flag (lets the client wire correctly). Tests cover three scenarios on top of the existing patch-not-installed case: - core unpatched (existing): no-op, capability=false - core patched + flag missing: no-op, capability=false - clientVars reports padWideSupported=false when flag is off Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 7ad59a9 commit a000cc9

3 files changed

Lines changed: 45 additions & 4 deletions

File tree

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,14 @@ const state = myToggle.init(); // reads cookie, binds checkbox
184184

185185
Parallel checkboxes in **both** the User Settings panel and the Pad Wide Settings panel — matching how native settings (sticky chat, line numbers, etc.) work. The pad-wide value rides Etherpad's existing `padoptions` broadcast/persist rail, so changes propagate to every connected client and are remembered across reloads. The pad creator can `enforceSettings` to lock the user-side checkbox for everyone.
186186

187-
Requires Etherpad with the `ep_*` padOptions passthrough patch (>= 2.7.4). On older cores the pad-wide column is hidden automatically and the user-side cookie toggle keeps working — plugins built on this helper run everywhere.
187+
Requires Etherpad with the `ep_*` padOptions passthrough patch (>= 2.7.4) AND the runtime flag `settings.enablePluginPadOptions = true` in `settings.json` (default false). When either is missing the pad-wide column is hidden automatically and the user-side cookie toggle keeps working — plugins built on this helper run everywhere.
188+
189+
```json
190+
// settings.json
191+
{
192+
"enablePluginPadOptions": true
193+
}
194+
```
188195

189196
```js
190197
const {padToggle} = require('ep_plugin_helpers');

pad-toggle-server.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,20 @@ const renderCheckbox = (settingId, l10nId, defaultLabel, idPrefix) =>
6464
const padToggleServer = (rawConfig) => {
6565
const {pluginName, settingId, l10nId, defaultLabel, defaultEnabled} = validateConfig(rawConfig);
6666
let cachedDefaultEnabled = defaultEnabled;
67+
// Etherpad >= 2.7.4 introduced settings.enablePluginPadOptions as a runtime
68+
// gate on the ep_* passthrough (default false per AGENTS.MD §52). We grab
69+
// it from loadSettings so eejsBlock_padSettings + clientVars correctly
70+
// no-op when an admin hasn't opted in, even though PluginCapabilities
71+
// reports the patch is present in the core.
72+
let runtimeFlagEnabled = false;
73+
74+
const isPadWideActive = () => padOptionsPluginPassthrough && runtimeFlagEnabled;
6775

6876
const loadSettings = async (hookName, args) => {
69-
const ps = (args && args.settings && args.settings[pluginName]) || {};
77+
const root = (args && args.settings) || {};
78+
const ps = root[pluginName] || {};
7079
if (typeof ps.defaultEnabled === 'boolean') cachedDefaultEnabled = ps.defaultEnabled;
80+
runtimeFlagEnabled = root.enablePluginPadOptions === true;
7181
};
7282

7383
const clientVars = async (hookName, ctx) => {
@@ -83,7 +93,11 @@ const padToggleServer = (rawConfig) => {
8393
ep_plugin_helpers: {
8494
padToggle: {
8595
[pluginName]: {
86-
padWideSupported: padOptionsPluginPassthrough,
96+
// True iff the running core has the patch AND the admin has
97+
// opted in via settings.enablePluginPadOptions. Client-side
98+
// init() reads this to decide whether to wire the pad-wide
99+
// checkbox, log the degradation warning, etc.
100+
padWideSupported: isPadWideActive(),
87101
settingId,
88102
l10nId,
89103
defaultEnabled: cachedDefaultEnabled,
@@ -100,7 +114,7 @@ const padToggleServer = (rawConfig) => {
100114
};
101115

102116
const eejsBlock_padSettings = (hookName, args, cb) => {
103-
if (!padOptionsPluginPassthrough) return cb();
117+
if (!isPadWideActive()) return cb();
104118
args.content += renderCheckbox(settingId, l10nId, defaultLabel, 'padsettings-');
105119
return cb();
106120
};

test/pad-toggle.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,26 @@ describe('padToggle', () => {
9494
done();
9595
});
9696
});
97+
98+
it('is a no-op when settings.enablePluginPadOptions is missing/false', async () => {
99+
// Even on a patched core, the runtime flag is opt-in (default false in
100+
// Etherpad >= 2.7.4). loadSettings without the flag set must leave
101+
// pad-wide rendering off.
102+
const t = padToggle(baseConfig());
103+
await t.loadSettings('h', {settings: {}}); // no enablePluginPadOptions
104+
const args = {content: ''};
105+
await new Promise((res) => t.eejsBlock_padSettings('h', args, res));
106+
assert.strictEqual(args.content, '');
107+
});
108+
109+
it('clientVars reports padWideSupported=false when the runtime flag is off', async () => {
110+
const t = padToggle(baseConfig());
111+
await t.loadSettings('h', {settings: {enablePluginPadOptions: false}});
112+
const cv = await t.clientVars('h', {pad: null});
113+
assert.strictEqual(
114+
cv.ep_plugin_helpers.padToggle.ep_test.padWideSupported, false,
115+
'capability flag in clientVars must reflect both core patch AND runtime flag');
116+
});
97117
});
98118

99119
describe('loadSettings', () => {

0 commit comments

Comments
 (0)