Skip to content

Commit c39b567

Browse files
feat(windows/linux): declutter tool, Linux wizard, merge Drop, fix SendTo
- Merge "Drop"/"Multi-Drop" into a single "Drop on OpenCoperLock" (Windows fires it once per file, so it covers one or many); installer removes the old Multi-Drop key. - SendTo shortcut now targets launch.vbs directly (the most reliable form) so Explorer appends the files. - New scripts/windows-declutter.{ps1,cmd}: keyword-driven, reversible disabling of the noisy Win11 entries (Edit with Photos/Paint, Create with Designer, Ask Copilot), scanning SystemFileAssociations\image too. - New scripts/opencoperlock-linux-wizard.sh: curl-installable interactive setup (Send-to integration and/or rclone Drive mount). - Docs: feature the Linux wizard and the declutter tool.
1 parent 19219ed commit c39b567

6 files changed

Lines changed: 292 additions & 17 deletions

File tree

‎apps/web/src/app/docs/page.tsx‎

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,9 @@ curl -H "Authorization: Bearer ocl_…" -F "file=@backup.tgz" \\
403403
},
404404
{ code: 'irm https://raw.githubusercontent.com/softpython2884/OpenCoperLock/main/scripts/send-to/windows/install-windows.ps1 | iex' },
405405
{ h: L('Linux', 'Linux') },
406-
{ p: L('GNOME Files, Cinnamon (Nemo) et KDE Dolphin. Dans un terminal :', 'GNOME Files, Cinnamon (Nemo) and KDE Dolphin. In a terminal:') },
406+
{ p: L('Le plus simple : l’assistant tout-en-un. Il demande votre URL + jeton une fois, puis propose le menu clic droit (GNOME Files, Nemo, Dolphin) et/ou le montage du Drive en dossier via rclone :', 'Easiest: the all-in-one wizard. It asks your URL + token once, then offers the right-click menu (GNOME Files, Nemo, Dolphin) and/or mounting the Drive as a folder via rclone:') },
407+
{ code: 'curl -fsSL https://raw.githubusercontent.com/softpython2884/OpenCoperLock/main/scripts/opencoperlock-linux-wizard.sh | bash' },
408+
{ p: L('Ou juste l’intégration clic droit :', 'Or just the right-click integration:') },
407409
{ code: 'curl -fsSL https://raw.githubusercontent.com/softpython2884/OpenCoperLock/main/scripts/send-to/linux/install-linux.sh | bash' },
408410
{
409411
btns: [
@@ -435,10 +437,22 @@ curl -H "Authorization: Bearer ocl_…" -F "file=@backup.tgz" \\
435437
},
436438
{
437439
btns: [
438-
{ label: L('Télécharger l’outil (.cmd)', 'Download the tool (.cmd)'), href: 'https://github.com/softpython2884/OpenCoperLock/raw/main/scripts/windows-context-menu-manager.cmd' },
440+
{ label: L('Interface graphique (.cmd)', 'Graphical tool (.cmd)'), href: 'https://github.com/softpython2884/OpenCoperLock/raw/main/scripts/windows-context-menu-manager.cmd' },
439441
{ label: L('Voir le script (.ps1)', 'View the script (.ps1)'), href: 'https://github.com/softpython2884/OpenCoperLock/blob/main/scripts/windows-context-menu-manager.ps1' },
440442
],
441443
},
444+
{
445+
p: L(
446+
'Raccourci : pour dégager directement les entrées Windows 11 les plus envahissantes (Modifier avec Photos / Paint, Créer avec Designer, Demander à Copilot), cet outil dédié les désactive d’un coup (réversible) :',
447+
'Shortcut: to clear the most intrusive Windows 11 entries at once (Edit with Photos / Paint, Create with Designer, Ask Copilot), this dedicated tool disables them in one go (reversible):',
448+
),
449+
},
450+
{
451+
btns: [
452+
{ label: L('Nettoyage rapide (.cmd)', 'Quick declutter (.cmd)'), href: 'https://github.com/softpython2884/OpenCoperLock/raw/main/scripts/windows-declutter.cmd' },
453+
{ label: L('Voir le script (.ps1)', 'View the script (.ps1)'), href: 'https://github.com/softpython2884/OpenCoperLock/blob/main/scripts/windows-declutter.ps1' },
454+
],
455+
},
442456
],
443457
},
444458
{
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#!/usr/bin/env bash
2+
# OpenCoperLock - Linux setup wizard.
3+
#
4+
# One command to set up the OpenCoperLock client tools on a Linux machine:
5+
# curl -fsSL https://raw.githubusercontent.com/softpython2884/OpenCoperLock/main/scripts/opencoperlock-linux-wizard.sh | bash
6+
#
7+
# It asks for your WebDAV URL + API token once, then lets you choose to install the right-click
8+
# "Send to OpenCoperLock" integration and/or mount your Drive as a folder (via rclone). Everything
9+
# is per-user; no root needed (rclone/davfs install may need your package manager).
10+
set -euo pipefail
11+
12+
REPO_RAW="https://raw.githubusercontent.com/softpython2884/OpenCoperLock/main/scripts/send-to"
13+
data="${XDG_DATA_HOME:-$HOME/.local/share}/opencoperlock"
14+
cfgdir="${XDG_CONFIG_HOME:-$HOME/.config}/opencoperlock"
15+
mkdir -p "$data" "$cfgdir"
16+
17+
say() { printf '\033[36m%s\033[0m\n' "$*"; }
18+
ok() { printf '\033[32m%s\033[0m\n' "$*"; }
19+
warn() { printf '\033[33m%s\033[0m\n' "$*"; }
20+
21+
# When piped through `curl | bash`, stdin is the script, so read prompts from the terminal.
22+
if [ -r /dev/tty ]; then exec 3</dev/tty; else exec 3<&0; fi
23+
ask() { local p="$1" d="${2:-}" a; printf '%s' "$p" >&2; IFS= read -r a <&3 || true; printf '%s' "${a:-$d}"; }
24+
asks() { local p="$1" a; printf '%s' "$p" >&2; IFS= read -rs a <&3 || true; printf '\n' >&2; printf '%s' "$a"; }
25+
26+
say "=== OpenCoperLock - Linux setup wizard ==="
27+
echo
28+
29+
default_base="https://copper.forgenet.fr/api/dav"
30+
BASE="$(ask "WebDAV base URL [$default_base]: " "$default_base")"
31+
BASE="${BASE%/}"
32+
TOKEN="$(asks "Paste your OpenCoperLock API token (ocl_...): ")"
33+
[ -n "$TOKEN" ] || { warn "No token provided - aborting."; exit 1; }
34+
35+
# Save config (readable only by you).
36+
umask 077
37+
printf 'BASE=%q\nTOKEN=%q\n' "$BASE" "$TOKEN" > "$cfgdir/config"
38+
chmod 600 "$cfgdir/config"
39+
ok "Saved config to $cfgdir/config"
40+
41+
# Pre-create the ComputerShared space.
42+
curl -fsS -u "me:$TOKEN" -X MKCOL "$BASE/ComputerShared/" >/dev/null 2>&1 || true
43+
44+
echo
45+
say "What do you want to set up?"
46+
echo " 1) Right-click 'Send to OpenCoperLock' (file-manager integration)"
47+
echo " 2) Mount your Drive as a folder (via rclone)"
48+
echo " 3) Both"
49+
echo " 4) Nothing else (config only)"
50+
choice="$(ask "Choice [3]: " "3")"
51+
52+
install_sendto() {
53+
say "Installing the 'Send to' integration..."
54+
# Fetch the uploader + icon from the official repo.
55+
curl -fsSL "$REPO_RAW/linux/send.sh" -o "$data/send.sh" && chmod 0755 "$data/send.sh"
56+
curl -fsSL "$REPO_RAW/assets/opencoperlock.png" -o "$data/opencoperlock.png" || true
57+
58+
local done=()
59+
for fm in nautilus nemo; do
60+
if [ -d "$HOME/.local/share/$fm" ] || command -v "$fm" >/dev/null 2>&1; then
61+
mkdir -p "$HOME/.local/share/$fm/scripts"
62+
ln -sf "$data/send.sh" "$HOME/.local/share/$fm/scripts/Send to OpenCoperLock"
63+
done+=("$fm")
64+
fi
65+
done
66+
for sm in "$HOME/.local/share/kio/servicemenus" "$HOME/.local/share/kservices5/ServiceMenus"; do
67+
mkdir -p "$sm"
68+
cat > "$sm/opencoperlock.desktop" <<EOF
69+
[Desktop Entry]
70+
Type=Service
71+
MimeType=all/all;
72+
Actions=sendToOpenCoperLock;
73+
X-KDE-Priority=TopLevel
74+
75+
[Desktop Action sendToOpenCoperLock]
76+
Name=Send to OpenCoperLock
77+
Icon=$data/opencoperlock.png
78+
Exec=$data/send.sh %F
79+
EOF
80+
chmod +x "$sm/opencoperlock.desktop" 2>/dev/null || true
81+
done
82+
ok "Send-to installed for: ${done[*]:-KDE} (right-click a file; you may need: nautilus -q / nemo -q)"
83+
}
84+
85+
mount_rclone() {
86+
if ! command -v rclone >/dev/null 2>&1; then
87+
warn "rclone is not installed. Install it, then re-run this wizard and choose option 2."
88+
warn " Debian/Ubuntu: sudo apt install rclone | Fedora: sudo dnf install rclone | or: curl https://rclone.org/install.sh | sudo bash"
89+
return
90+
fi
91+
say "Configuring an rclone remote 'opencoperlock'..."
92+
rclone config delete opencoperlock >/dev/null 2>&1 || true
93+
rclone config create opencoperlock webdav url "$BASE" vendor other user me pass "$TOKEN" >/dev/null
94+
local mnt="$HOME/OpenCoperLock"
95+
mkdir -p "$mnt"
96+
ok "Remote ready. Test it: rclone lsd opencoperlock:"
97+
echo
98+
local now
99+
now="$(ask "Mount it now at $mnt ? [y/N]: " "N")"
100+
if [ "${now,,}" = "y" ] || [ "${now,,}" = "o" ]; then
101+
rclone mount opencoperlock: "$mnt" --vfs-cache-mode writes --daemon
102+
ok "Mounted at $mnt (background). Unmount with: fusermount -u \"$mnt\""
103+
else
104+
echo "Mount later with:"
105+
echo " rclone mount opencoperlock: \"$mnt\" --vfs-cache-mode writes --daemon"
106+
fi
107+
}
108+
109+
case "$choice" in
110+
1) install_sendto ;;
111+
2) mount_rclone ;;
112+
4) : ;;
113+
*) install_sendto; echo; mount_rclone ;;
114+
esac
115+
116+
echo
117+
ok "Done. Your uploads land in the 'ComputerShared' space of your Drive."

‎scripts/send-to/README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ new server code — just your existing `ocl_…` API token.
2121
- **Or** double-click **`windows/install-windows.cmd`** from a checkout.
2222
2323
Paste your WebDAV URL (e.g. `https://copper.forgenet.fr/api/dav`) and the token when asked, then
24-
pick your options (notification on/off; *Send to* menu; *Drop / Multi-Drop* right-click entries).
24+
pick your options (notification on/off; *Send to* menu; *Drop on OpenCoperLock* right-click entry).
2525
3. Done. Depending on your choices, right-click any file(s) →
2626
- **Send to → OpenCoperLock** — uploads **all** selected files in one go, or
27-
- **Drop on OpenCoperLock** / **Multi-Drop on OpenCoperLock** — top-level right-click entries.
27+
- **Drop on OpenCoperLock** — a top-level right-click entry (handles one file or several).
2828
2929
Everything runs **hidden** (a tiny `launch.vbs` starts the uploader with no PowerShell window) and
3030
shows a small tray notification when done. The installer stores your token **DPAPI-encrypted**

‎scripts/send-to/windows/install-windows.ps1‎

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ if ([string]::IsNullOrWhiteSpace($Token)) { throw "No token provided." }
6565

6666
$wantNotify = if ($Notify) { $Notify -eq 'yes' } else { Ask-YesNo "Show a small notification after each send?" $true }
6767
$wantSendTo = if ($SendTo) { $SendTo -eq 'yes' } else { Ask-YesNo "Add 'OpenCoperLock' to the 'Send to' menu (best for many files at once)?" $true }
68-
$wantContext = if ($ContextMenu) { $ContextMenu -eq 'yes' } else { Ask-YesNo "Add 'Drop / Multi-Drop on OpenCoperLock' to the right-click menu?" $true }
68+
$wantContext = if ($ContextMenu) { $ContextMenu -eq 'yes' } else { Ask-YesNo "Add 'Drop on OpenCoperLock' to the right-click menu?" $true }
6969

7070
# --- install files -------------------------------------------------------------------------------
7171
Get-Component 'windows/send.ps1' (Join-Path $dstDir 'send.ps1')
@@ -84,22 +84,23 @@ $vbs = Join-Path $dstDir 'launch.vbs'
8484
$ico = Join-Path $dstDir 'opencoperlock.ico'
8585
$wscript = Join-Path $env:SystemRoot 'System32\wscript.exe'
8686

87-
# --- "Send to" shortcut (targets wscript+launch.vbs so files pass reliably and no window shows) ---
87+
# --- "Send to" shortcut (targets launch.vbs directly so Explorer appends the files and no window
88+
# shows; this is the most reliable SendTo form) --------------------------------------------------
8889
$sendToLnk = Join-Path ([Environment]::GetFolderPath('SendTo')) 'OpenCoperLock.lnk'
8990
if ($wantSendTo) {
9091
$ws = New-Object -ComObject WScript.Shell
9192
$sc = $ws.CreateShortcut($sendToLnk)
92-
$sc.TargetPath = $wscript
93-
$sc.Arguments = '"' + $vbs + '"'
93+
$sc.TargetPath = $vbs
9494
$sc.IconLocation = "$ico,0"
9595
$sc.Description = 'Send to OpenCoperLock (ComputerShared)'
9696
$sc.WorkingDirectory = $dstDir
9797
$sc.Save()
9898
} elseif (Test-Path $sendToLnk) { Remove-Item $sendToLnk -Force }
9999

100-
# --- right-click context-menu verbs (per-user HKCU, applies to all files) -------------------------
101-
# Use the .NET registry API: the all-files key is literally named "*", which the PowerShell registry
102-
# provider would treat as a wildcard.
100+
# --- right-click context-menu verb (per-user HKCU, applies to all files) --------------------------
101+
# One "Drop on OpenCoperLock" verb: on a multi-selection Windows fires it once per file, so a single
102+
# verb handles both one file and several. Uses the .NET registry API because the all-files key is
103+
# literally named "*", which the PowerShell registry provider would treat as a wildcard.
103104
function Set-Verb($keyName, $label) {
104105
$k = [Microsoft.Win32.Registry]::CurrentUser.CreateSubKey("Software\Classes\*\shell\$keyName")
105106
$k.SetValue('MUIVerb', $label)
@@ -108,18 +109,17 @@ function Set-Verb($keyName, $label) {
108109
$c.SetValue('', '"' + $wscript + '" "' + $vbs + '" "%1"') # '' = the key's (default) value
109110
$c.Close(); $k.Close()
110111
}
112+
# Always drop the old "Multi-Drop" verb (merged into "Drop").
113+
try { [Microsoft.Win32.Registry]::CurrentUser.DeleteSubKeyTree('Software\Classes\*\shell\OpenCoperLock.MultiDrop', $false) } catch { }
111114
if ($wantContext) {
112-
Set-Verb 'OpenCoperLock.Drop' 'Drop on OpenCoperLock'
113-
Set-Verb 'OpenCoperLock.MultiDrop' 'Multi-Drop on OpenCoperLock'
115+
Set-Verb 'OpenCoperLock.Drop' 'Drop on OpenCoperLock'
114116
} else {
115-
foreach ($k in 'OpenCoperLock.Drop', 'OpenCoperLock.MultiDrop') {
116-
try { [Microsoft.Win32.Registry]::CurrentUser.DeleteSubKeyTree("Software\Classes\*\shell\$k", $false) } catch { }
117-
}
117+
try { [Microsoft.Win32.Registry]::CurrentUser.DeleteSubKeyTree('Software\Classes\*\shell\OpenCoperLock.Drop', $false) } catch { }
118118
}
119119

120120
Write-Host ""
121121
Write-Host "Installed." -ForegroundColor Green
122122
if ($wantSendTo) { Write-Host " - Right-click file(s) -> Send to -> OpenCoperLock (uploads all selected at once)." -ForegroundColor DarkGray }
123-
if ($wantContext) { Write-Host " - Right-click file(s) -> Drop / Multi-Drop on OpenCoperLock." -ForegroundColor DarkGray }
123+
if ($wantContext) { Write-Host " - Right-click file(s) -> Drop on OpenCoperLock." -ForegroundColor DarkGray }
124124
Write-Host " Uploads land in the 'ComputerShared' space. Log: $dstDir\send.log" -ForegroundColor DarkGray
125125
Write-Host " Re-run this installer any time to change settings." -ForegroundColor DarkGray

‎scripts/windows-declutter.cmd‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@echo off
2+
REM ===========================================================================
3+
REM OpenCoperLock - quick declutter for the Windows 11 right-click menu.
4+
REM Disables the noisy app entries (Edit with Photos, Create with Designer,
5+
REM Edit with Paint, Ask Copilot). Asks for Administrator, lists what it found,
6+
REM and confirms before changing anything. Fully reversible.
7+
REM ===========================================================================
8+
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0windows-declutter.ps1"
9+
echo.
10+
pause

‎scripts/windows-declutter.ps1‎

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
<#
2+
OpenCoperLock - quick declutter for the Windows 11 right-click menu.
3+
4+
Disables the noisy app entries you named (Edit with Photos, Create with Designer, Edit with Paint,
5+
Ask Copilot) - matched by keyword so it works in French or English. It scans the places these
6+
live: classic verbs and shell-extension handlers under the file/folder roots AND under
7+
SystemFileAssociations\image\... (where "Edit with Photos/Paint" hide).
8+
9+
NOTHING is deleted - verbs get a reversible `LegacyDisable` flag and shell extensions go on
10+
Windows' official "Blocked" list. Re-enable later with -Restore (same keywords) or the GUI manager.
11+
12+
Usage:
13+
windows-declutter.cmd # disable the default noisy set (asks to confirm)
14+
...ps1 -List # just show what matches, change nothing
15+
...ps1 -Keywords Photos,Paint # target your own keywords
16+
...ps1 -Restore # re-enable everything matching the keywords
17+
#>
18+
param(
19+
[string[]]$Keywords = @('Photos', 'Designer', 'Paint', 'Copilot'),
20+
[switch]$List,
21+
[switch]$Restore
22+
)
23+
$ErrorActionPreference = 'Stop'
24+
$BlockedKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Blocked'
25+
26+
function Test-Admin {
27+
$id = [Security.Principal.WindowsIdentity]::GetCurrent()
28+
(New-Object Security.Principal.WindowsPrincipal($id)).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
29+
}
30+
# Self-elevate: most of these entries are system-wide (HKLM).
31+
if (-not (Test-Admin) -and -not $List -and $PSCommandPath) {
32+
$a = @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', "`"$PSCommandPath`"", '-Keywords', ($Keywords -join ','))
33+
if ($Restore) { $a += '-Restore' }
34+
try { Start-Process powershell -Verb RunAs -ArgumentList $a; exit } catch { }
35+
}
36+
$IsAdmin = Test-Admin
37+
38+
$imgExt = @('.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.heic', '.tif', '.tiff', '.ico', '.avif')
39+
$verbRoots = @('*\shell', 'AllFilesystemObjects\shell', 'Directory\shell', 'Directory\Background\shell',
40+
'Folder\shell', 'SystemFileAssociations\image\shell', 'SystemFileAssociations\text\shell') +
41+
($imgExt | ForEach-Object { "SystemFileAssociations\$_\shell" })
42+
$shexRoots = @('*', 'AllFilesystemObjects', 'Directory', 'Directory\Background', 'Folder', 'SystemFileAssociations\image')
43+
$hives = [ordered]@{ 'HKCU' = 'HKCU:\Software\Classes'; 'HKLM' = 'HKLM:\SOFTWARE\Classes' }
44+
45+
function Matches($text) {
46+
foreach ($kw in $Keywords) { if ("$text" -match [regex]::Escape($kw)) { return $true } }
47+
return $false
48+
}
49+
50+
function Find-Candidates {
51+
$out = New-Object System.Collections.ArrayList
52+
foreach ($h in $hives.GetEnumerator()) {
53+
foreach ($vr in $verbRoots) {
54+
$base = Join-Path $h.Value $vr
55+
if (-not (Test-Path -LiteralPath $base)) { continue }
56+
foreach ($k in Get-ChildItem -LiteralPath $base -ErrorAction SilentlyContinue) {
57+
$props = Get-ItemProperty -LiteralPath $k.PSPath -ErrorAction SilentlyContinue
58+
$name = $props.MUIVerb; if (-not $name) { $name = $props.'(default)' }; if (-not $name) { $name = $k.PSChildName }
59+
if ((Matches $name) -or (Matches $k.PSChildName)) {
60+
[void]$out.Add([pscustomobject]@{ Type = 'verb'; Scope = $h.Key; Name = "$name"; Where = $vr
61+
Path = $k.PSPath; Clsid = ''; Disabled = ($props.PSObject.Properties.Name -contains 'LegacyDisable') })
62+
}
63+
}
64+
}
65+
foreach ($sr in $shexRoots) {
66+
$base = Join-Path $h.Value "$sr\shellex\ContextMenuHandlers"
67+
if (-not (Test-Path -LiteralPath $base)) { continue }
68+
foreach ($k in Get-ChildItem -LiteralPath $base -ErrorAction SilentlyContinue) {
69+
$clsid = (Get-ItemProperty -LiteralPath $k.PSPath -ErrorAction SilentlyContinue).'(default)'
70+
if (-not $clsid) { $clsid = $k.PSChildName }
71+
$friendly = $k.PSChildName
72+
if ($clsid -match '^{.+}$') {
73+
$cn = (Get-ItemProperty -LiteralPath "HKLM:\SOFTWARE\Classes\CLSID\$clsid" -ErrorAction SilentlyContinue).'(default)'
74+
if ($cn) { $friendly = $cn }
75+
}
76+
if ((Matches $friendly) -or (Matches $k.PSChildName)) {
77+
$blocked = $false
78+
if ($clsid -match '^{.+}$' -and (Test-Path -LiteralPath $BlockedKey)) {
79+
$blocked = ($null -ne (Get-ItemProperty -LiteralPath $BlockedKey -ErrorAction SilentlyContinue).$clsid)
80+
}
81+
[void]$out.Add([pscustomobject]@{ Type = 'shellex'; Scope = $h.Key; Name = "$friendly"; Where = $sr
82+
Path = $k.PSPath; Clsid = "$clsid"; Disabled = $blocked })
83+
}
84+
}
85+
}
86+
}
87+
$out | Sort-Object Name, Where -Unique
88+
}
89+
90+
function Apply($e, $disable) {
91+
if ($e.Scope -eq 'HKLM' -and -not $IsAdmin) { return "needs admin" }
92+
try {
93+
if ($e.Type -eq 'verb') {
94+
if ($disable) { New-ItemProperty -LiteralPath $e.Path -Name 'LegacyDisable' -Value '' -PropertyType String -Force | Out-Null }
95+
else { Remove-ItemProperty -LiteralPath $e.Path -Name 'LegacyDisable' -ErrorAction SilentlyContinue }
96+
} else {
97+
if ($e.Clsid -notmatch '^{.+}$') { return "no CLSID" }
98+
if (-not (Test-Path $BlockedKey)) { New-Item $BlockedKey -Force | Out-Null }
99+
if ($disable) { New-ItemProperty $BlockedKey -Name $e.Clsid -Value $e.Name -PropertyType String -Force | Out-Null }
100+
else { Remove-ItemProperty $BlockedKey -Name $e.Clsid -ErrorAction SilentlyContinue }
101+
}
102+
return $null
103+
} catch { return $_.Exception.Message }
104+
}
105+
106+
Write-Host "OpenCoperLock declutter - keywords: $($Keywords -join ', ')" -ForegroundColor Cyan
107+
$cands = @(Find-Candidates)
108+
if ($cands.Count -eq 0) { Write-Host "Nothing matched. (These entries may be part of Windows' modern menu, not the classic registry.)" -ForegroundColor Yellow; if (-not $PSCommandPath) { Read-Host "Enter to close" }; return }
109+
110+
Write-Host ""
111+
$i = 0
112+
foreach ($e in $cands) {
113+
$i++
114+
$state = if ($e.Disabled) { '[already OFF]' } else { '[on]' }
115+
Write-Host (" {0,2}. {1,-12} {2,-6} {3,-34} {4} ({5})" -f $i, $state, $e.Scope, $e.Name, $e.Where, $e.Type)
116+
}
117+
Write-Host ""
118+
119+
if ($List) { return }
120+
121+
$verb = if ($Restore) { 're-enable' } else { 'disable' }
122+
$ans = (Read-Host "$verb the $($cands.Count) matching entries above? [Y/n]").Trim().ToLower()
123+
if ($ans -eq 'n' -or $ans -eq 'no' -or $ans -eq 'non') { Write-Host "Cancelled." -ForegroundColor DarkGray; return }
124+
125+
$done = 0; $skip = 0
126+
foreach ($e in $cands) {
127+
$err = Apply $e (-not $Restore)
128+
if ($err) { Write-Host (" ! {0}: {1}" -f $e.Name, $err) -ForegroundColor Yellow; $skip++ }
129+
else { $done++ }
130+
}
131+
Write-Host ""
132+
Write-Host ("{0} {1}d, {2} skipped." -f $done, $verb, $skip) -ForegroundColor Green
133+
Write-Host "Restart Explorer to see the change: taskkill /f /im explorer.exe & start explorer.exe" -ForegroundColor DarkGray
134+
if (-not $PSCommandPath) { Read-Host "Enter to close" }

0 commit comments

Comments
 (0)