From db66bd9c31bba45f179a847e2195f3b3238397b7 Mon Sep 17 00:00:00 2001 From: Christian Jorgensen Date: Fri, 18 Jul 2025 22:03:13 +0200 Subject: [PATCH 1/6] Disable all commands for change if connection is readonly --- package.json | 51 +++++++++++++++++++++++----------------------- src/instantiate.ts | 7 ++++--- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/package.json b/package.json index 94c7215ba..7d2303fa9 100644 --- a/package.json +++ b/package.json @@ -1028,13 +1028,13 @@ }, { "command": "code-for-ibmi.launchTerminalPicker", - "enablement": "code-for-ibmi:connected", + "enablement": "code-for-ibmi:connected && !code-for-ibmi:isReadonly", "title": "Launch Terminal Picker", "category": "IBM i" }, { "command": "code-for-ibmi.goToFile", - "enablement": "code-for-ibmi:connected", + "enablement": "code-for-ibmi:connected && !code-for-ibmi:isReadonly", "title": "Go to File...", "category": "IBM i", "icon": "$(go-to-file)" @@ -1162,7 +1162,7 @@ }, { "command": "code-for-ibmi.runAction", - "enablement": "code-for-ibmi:connected", + "enablement": "code-for-ibmi:connected && !code-for-ibmi:isReadonly", "title": "Run Action...", "category": "IBM i", "icon": "$(file-binary)" @@ -1259,28 +1259,28 @@ }, { "command": "code-for-ibmi.createMember", - "enablement": "code-for-ibmi:connected", + "enablement": "code-for-ibmi:connected && !code-for-ibmi:isReadonly", "title": "New Member...", "category": "IBM i", "icon": "$(new-file)" }, { "command": "code-for-ibmi.copyMember", - "enablement": "code-for-ibmi:connected", + "enablement": "code-for-ibmi:connected && !code-for-ibmi:isReadonly", "title": "Copy...", "category": "IBM i", "icon": "$(files)" }, { "command": "code-for-ibmi.updateMemberText", - "enablement": "code-for-ibmi:connected", + "enablement": "code-for-ibmi:connected && !code-for-ibmi:isReadonly", "title": "Change Description...", "category": "IBM i", "icon": "$(symbol-file)" }, { "command": "code-for-ibmi.renameQSYS", - "enablement": "code-for-ibmi:connected", + "enablement": "code-for-ibmi:connected && !code-for-ibmi:isReadonly", "title": "Rename...", "category": "IBM i", "icon": "$(files)" @@ -1293,7 +1293,7 @@ }, { "command": "code-for-ibmi.uploadAndReplaceMemberAsFile", - "enablement": "code-for-ibmi:connected", + "enablement": "code-for-ibmi:connected && !code-for-ibmi:isReadonly", "title": "Upload and Replace...", "category": "IBM i" }, @@ -1325,7 +1325,7 @@ }, { "command": "code-for-ibmi.deleteIFS", - "enablement": "code-for-ibmi:connected", + "enablement": "code-for-ibmi:connected && !code-for-ibmi:isReadonly", "title": "Delete...", "category": "IBM i" }, @@ -1337,14 +1337,14 @@ }, { "command": "code-for-ibmi.moveIFS", - "enablement": "code-for-ibmi:connected", + "enablement": "code-for-ibmi:connected && !code-for-ibmi:isReadonly", "title": "Rename/Move...", "category": "IBM i", "icon": "$(files)" }, { "command": "code-for-ibmi.copyIFS", - "enablement": "code-for-ibmi:connected", + "enablement": "code-for-ibmi:connected && !code-for-ibmi:isReadonly", "title": "Copy...", "category": "IBM i", "icon": "$(files)" @@ -1358,21 +1358,21 @@ }, { "command": "code-for-ibmi.createDirectory", - "enablement": "code-for-ibmi:connected", + "enablement": "code-for-ibmi:connected && !code-for-ibmi:isReadonly", "title": "New Directory...", "category": "IBM i", "icon": "$(new-folder)" }, { "command": "code-for-ibmi.createStreamfile", - "enablement": "code-for-ibmi:connected", + "enablement": "code-for-ibmi:connected && !code-for-ibmi:isReadonly", "title": "New File...", "category": "IBM i", "icon": "$(new-file)" }, { "command": "code-for-ibmi.uploadStreamfile", - "enablement": "code-for-ibmi:connected", + "enablement": "code-for-ibmi:connected && !code-for-ibmi:isReadonly", "title": "Upload...", "category": "IBM i", "icon": "$(cloud-upload)" @@ -1382,11 +1382,11 @@ "title": "Deploy Workspace", "category": "IBM i", "icon": "$(cloud-upload)", - "enablement": "code-for-ibmi:connected && workspaceFolderCount >= 1" + "enablement": "code-for-ibmi:connected && !code-for-ibmi:isReadonly && workspaceFolderCount >= 1" }, { "command": "code-for-ibmi.setDeployLocation", - "enablement": "code-for-ibmi:connected", + "enablement": "code-for-ibmi:connected && !code-for-ibmi:isReadonly", "title": "Set Deploy Workspace Location", "category": "IBM i", "icon": "$(cloud-upload)" @@ -1476,7 +1476,7 @@ }, { "command": "code-for-ibmi.objectBrowser.delete", - "enablement": "code-for-ibmi:connected", + "enablement": "code-for-ibmi:connected && !code-for-ibmi:isReadonly", "title": "Delete...", "category": "IBM i", "icon": "$(remove)" @@ -1537,35 +1537,35 @@ }, { "command": "code-for-ibmi.createSourceFile", - "enablement": "code-for-ibmi:connected", + "enablement": "code-for-ibmi:connected && !code-for-ibmi:isReadonly", "title": "New Source File...", "category": "IBM i", "icon": "$(new-file)" }, { "command": "code-for-ibmi.createLibrary", - "enablement": "code-for-ibmi:connected", + "enablement": "code-for-ibmi:connected && !code-for-ibmi:isReadonly", "title": "New Library...", "category": "IBM i", "icon": "$(file-directory-create)" }, { "command": "code-for-ibmi.changeObjectDesc", - "enablement": "code-for-ibmi:connected", + "enablement": "code-for-ibmi:connected && !code-for-ibmi:isReadonly", "title": "Change Description...", "category": "IBM i", "icon": "$(symbol-file)" }, { "command": "code-for-ibmi.copyObject", - "enablement": "code-for-ibmi:connected", + "enablement": "code-for-ibmi:connected && !code-for-ibmi:isReadonly", "title": "Copy...", "category": "IBM i", "icon": "$(symbol-file)" }, { "command": "code-for-ibmi.moveObject", - "enablement": "code-for-ibmi:connected", + "enablement": "code-for-ibmi:connected && !code-for-ibmi:isReadonly", "title": "Move...", "category": "IBM i", "icon": "$(symbol-file)" @@ -1599,6 +1599,7 @@ }, { "command": "code-for-ibmi.openTerminalHere", + "enablement": "code-for-ibmi:connected && !code-for-ibmi:isReadonly", "title": "Open Terminal Here", "category": "IBM i" }, @@ -1616,7 +1617,7 @@ { "command": "code-for-ibmi.edit", "title": "Edit", - "enablement": "code-for-ibmi:connected", + "enablement": "code-for-ibmi:connected && !code-for-ibmi:isReadonly", "category": "IBM i" }, { @@ -1692,7 +1693,7 @@ "title": "Reset keyboard", "category": "IBM i", "icon": "$(keyboard)", - "enablement": "code-for-ibmi:connected" + "enablement": "code-for-ibmi:connected && !code-for-ibmi:isReadonly" }, { "command": "code-for-ibmi.testing.connectWithFixture", @@ -1706,7 +1707,7 @@ "title": "Generate binder source", "category": "IBM i", "icon": "$(plus)", - "enablement": "code-for-ibmi:connected" + "enablement": "code-for-ibmi:connected && !code-for-ibmi:isReadonly" } ], "keybindings": [ diff --git a/src/instantiate.ts b/src/instantiate.ts index d21254abc..e0f61ceae 100644 --- a/src/instantiate.ts +++ b/src/instantiate.ts @@ -105,12 +105,12 @@ async function updateConnectedBar() { if (connection) { const config = connection.getConfig(); connectedBarItem.text = `$(${config.readOnlyMode ? "lock" : "settings-gear"}) ${config.name}`; - + const terminalMenuItem = config.readOnlyMode ? `` : `[$(terminal) Terminals](command:code-for-ibmi.launchTerminalPicker)`; const debugRunning = await isDebugEngineRunning(); connectedBarItem.tooltip = new vscode.MarkdownString([ `[$(settings-gear) Settings](command:code-for-ibmi.showAdditionalSettings)`, `[$(file-binary) Actions](command:code-for-ibmi.showActionsMaintenance)`, - `[$(terminal) Terminals](command:code-for-ibmi.launchTerminalPicker)`, + terminalMenuItem, debugPTFInstalled(connection) ? `[$(${debugRunning ? "bug" : "debug"}) Debugger ${((await getDebugServiceDetails(connection)).version)} (${debugRunning ? "on" : "off"})](command:ibmiDebugBrowser.focus)` : @@ -129,6 +129,8 @@ async function onConnected() { updateConnectedBar(); + vscode.commands.executeCommand(`setContext`, `code-for-ibmi:isReadonly`, config?.readOnlyMode); + // Enable the profile view if profiles exist. vscode.commands.executeCommand(`setContext`, `code-for-ibmi:hasProfiles`, (config?.connectionProfiles || []).length > 0); } @@ -154,4 +156,3 @@ async function onDisconnected() { connectedBarItem, ].forEach(barItem => barItem.hide()) } - From c195a18c5bd10d20028944efcdc42d936e350eba Mon Sep 17 00:00:00 2001 From: Christian Jorgensen Date: Fri, 18 Jul 2025 22:56:11 +0200 Subject: [PATCH 2/6] Ask for reload when readonly mode is changed --- src/webviews/settings/index.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/webviews/settings/index.ts b/src/webviews/settings/index.ts index f5974bfca..f955264ca 100644 --- a/src/webviews/settings/index.ts +++ b/src/webviews/settings/index.ts @@ -68,7 +68,7 @@ export class SettingsUI { if (serverConfig && serverConfig.codefori) { for (const field of currentSection.fields) { if (!field.id) continue; - + if (serverConfig.codefori[field.id] !== undefined) { field.readonly = true; } @@ -76,7 +76,7 @@ export class SettingsUI { } } - const restartFields = [`showDescInLibList`, `tempDir`, `debugCertDirectory`]; + const restartFields = [`readOnlyMode`, `showDescInLibList`, `tempDir`, `debugCertDirectory`]; let restart = false; const featuresTab = new Section(); @@ -204,7 +204,7 @@ export class SettingsUI { .set("Debug port", config.debugPort); debugServiceConfig.set("SEP debug port", config.debugSepPort) - + debuggerTab.addParagraph(``); debuggerTab.addCheckbox(`debugUpdateProductionFiles`, `Update production files`, `Determines whether the job being debugged can update objects in production (*PROD) libraries.`, config.debugUpdateProductionFiles) @@ -338,7 +338,7 @@ export class SettingsUI { } } - if (restartFields.some(item => data[item] && data[item] !== config[item])) { + if (restartFields.some(item => data[item] !== config[item])) { restart = true; } @@ -395,7 +395,7 @@ export class SettingsUI { .addPassword(`password`, `${vscode.l10n.t(`Password`)}${storedPassword ? ` (${vscode.l10n.t(`stored`)})` : ``}`, vscode.l10n.t("Only provide a password if you want to update an existing one or set a new one.")) .addFile(`privateKeyPath`, `${vscode.l10n.t(`Private Key`)}${privateKeyPath ? ` (${vscode.l10n.t(`Private Key`)}: ${privateKeyPath})` : ``}`, privateKeyWarning + vscode.l10n.t("Only provide a private key if you want to update from the existing one or set one.") + '
' + vscode.l10n.t("OpenSSH, RFC4716 and PPK formats are supported.")) .addHorizontalRule() - .addInput(`readyTimeout`, vscode.l10n.t(`Connection Timeout (in milliseconds)`), vscode.l10n.t(`How long to wait for the SSH handshake to complete.`), { inputType: "number", min: 1, default: stored.readyTimeout ? String(stored.readyTimeout) : "20000" }) + .addInput(`readyTimeout`, vscode.l10n.t(`Connection Timeout (in milliseconds)`), vscode.l10n.t(`How long to wait for the SSH handshake to complete.`), { inputType: "number", min: 1, default: stored.readyTimeout ? String(stored.readyTimeout) : "20000" }) .addButtons( { id: `submitButton`, label: vscode.l10n.t(`Save`), requiresValidation: true }, { id: `removeAuth`, label: vscode.l10n.t(`Remove auth methods`) } From 016c0b27c0696dd942104da5960760cc12d0b5eb Mon Sep 17 00:00:00 2001 From: Christian Jorgensen Date: Fri, 18 Jul 2025 23:12:15 +0200 Subject: [PATCH 3/6] Move readonly mode setting to Features section as first option --- src/webviews/settings/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/webviews/settings/index.ts b/src/webviews/settings/index.ts index f955264ca..889b1fec9 100644 --- a/src/webviews/settings/index.ts +++ b/src/webviews/settings/index.ts @@ -88,6 +88,8 @@ export class SettingsUI { } featuresTab + .addCheckbox(`readOnlyMode`, `Read only mode`, `When enabled, content on the server can not be changed. Requires restart when changed.`, config.readOnlyMode) + .addHorizontalRule() .addCheckbox(`quickConnect`, `Quick Connect`, `When enabled, server settings from previous connection will be used, resulting in much quicker connection. If server settings are changed, right-click the connection in Connection Browser and select Connect and Reload Server Settings to refresh the cache.`, config.quickConnect) .addCheckbox(`showDescInLibList`, `Show description of libraries in User Library List view`, `When enabled, library text and attribute will be shown in User Library List. It is recommended to also enable SQL for this.`, config.showDescInLibList) .addCheckbox(`showHiddenFiles`, `Show hidden files and directories in IFS browser.`, `When disabled, hidden files and directories (i.e. names starting with '.') will not be shown in the IFS browser, except for special config files.`, config.showHiddenFiles) @@ -154,7 +156,6 @@ export class SettingsUI { } ], `Set your Default Deployment Method. This is used when deploying from the local workspace to the server.`) .addHorizontalRule() - .addCheckbox(`readOnlyMode`, `Read only mode`, `When enabled, source members and IFS files will always be opened in read-only mode.`, config.readOnlyMode) .addInput(`protectedPaths`, `Protected paths`, `A comma separated list of libraries and/or IFS directories whose members will always be opened in read-only mode. (Example: QGPL, /home/QSECOFR, MYLIB, /QIBM)`, { default: config.protectedPaths.join(`, `) }); setFieldsReadOnly(sourceTab); From 8adc89adb03befdf9924206eeb20624d20dbe0cb Mon Sep 17 00:00:00 2001 From: Christian Jorgensen Date: Sat, 19 Jul 2025 00:08:44 +0200 Subject: [PATCH 4/6] Clarify description of readonly mode in server settings file --- schemas/settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schemas/settings.json b/schemas/settings.json index 965fd6277..9c0c35f64 100644 --- a/schemas/settings.json +++ b/schemas/settings.json @@ -7,7 +7,7 @@ "readOnlyMode": { "type": "boolean", "default": false, - "description": "Always open source members and IFS files in read-only mode." + "description": "Always open source members and IFS files in read-only mode. Content on the server can not be changed." }, "tempLibrary": { "type": "string", From e632c9423ea9da96b67edb1887a22463d3d42f73 Mon Sep 17 00:00:00 2001 From: Christian Jorgensen Date: Mon, 21 Jul 2025 21:54:41 +0200 Subject: [PATCH 5/6] Prohibit Save As when connection is in readonly mode --- src/filesystems/ifsFs.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/filesystems/ifsFs.ts b/src/filesystems/ifsFs.ts index 0f25736e5..7efeec8d2 100644 --- a/src/filesystems/ifsFs.ts +++ b/src/filesystems/ifsFs.ts @@ -70,6 +70,10 @@ export class IFSFS implements vscode.FileSystemProvider { const path = uri.path; const connection = instance.getConnection(); if (connection) { + const readonly = connection.getConfig().readOnlyMode; + if (readonly) { + throw new FileSystemError("Connection is in readonly mode"); + } const contentApi = connection.getContent(); if (!content.length) { //Coming from "Save as" this.savedAsFiles.add(path); From f7fba070f62bafe76b52889758a515ef649c3d7d Mon Sep 17 00:00:00 2001 From: worksofliam Date: Thu, 14 Aug 2025 13:20:56 -0400 Subject: [PATCH 6/6] Hide deploy button, and disable keyboard shortcut for deploy Signed-off-by: worksofliam --- package.json | 2 +- src/filesystems/local/deployment.ts | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 7d2303fa9..94e757ba8 100644 --- a/package.json +++ b/package.json @@ -1720,7 +1720,7 @@ "command": "code-for-ibmi.launchDeploy", "key": "ctrl+shift+e", "mac": "cmd+shift+e", - "when": "workspaceFolderCount >= 1 && code-for-ibmi:connected" + "when": "workspaceFolderCount >= 1 && code-for-ibmi:connected && !code-for-ibmi:isReadonly" }, { "command": "code-for-ibmi.goToFile", diff --git a/src/filesystems/local/deployment.ts b/src/filesystems/local/deployment.ts index 1a01606ea..ea2e8ef9d 100644 --- a/src/filesystems/local/deployment.ts +++ b/src/filesystems/local/deployment.ts @@ -56,8 +56,12 @@ export namespace Deployment { const storage = instance.getStorage(); if (workspaces && connection && storage) { - if (workspaces.length > 0) { + const config = connection.getConfig(); + + if (workspaces.length > 0 && !config.readOnlyMode) { button.show(); + } else { + button.hide(); } const existingPaths = storage.getDeployment();