Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow font installation for non-admin user #161

Merged

Conversation

jonz94
Copy link
Collaborator

@jonz94 jonz94 commented Nov 27, 2021

Attempt to resolve #10

  • Install fonts into user's font directory $env:LOCALAPPDATA\Microsoft\Windows\Fonts (administrator rights are not required)

    • This feature required Windows 10 version 1809 and above (build number >= 17763)
    • Fallback to install fonts into system fonts directory $env:windir\Fonts if Windows version is before 1809 (administrator rights are required)
  • Add support for scoop global installation (e.g. scoop install -g SarasaGothic-ttc)

    • When -g is provided, the fonts will be installed into system fonts directory $env:windir\Fonts (administrator rights are required)
  • Remove sudo as dependency

PS. I write some scripts to automatically patch all the manifests, so if anything of this PR needs to be changed, it's a piece of cake ✌️

  • Usage:
    • Make sure busybox and node commands are available: scoop install busybox-lean nodejs
    • execute 0-main.ps1 on the project root: .\0-main.ps1

0-main.ps1

Get-ChildItem bucket | ForEach-Object {
    # remove all `"depends": "sudo",` part
    busybox sed -i "/depends/ d" $_.FullName

    # remove all `"",` part
    busybox sed -i '/\ \"\",$/ d' $_.FullName

    # replace all `-filter` to `-Filter`
    busybox sed -i "s/-filter/-Filter/g" $_.FullName

    # replace all `-Filter 'sarasa` to `-Filter '`
    busybox sed -i "s/-Filter 'sarasa/-Filter '/g" $_.FullName

    # replace all `\"$dir\"` to `$dir`
    busybox sed -i 's/\\\\\"\$dir\\\\\"/$dir/g' $_.FullName

    # remove all `is_admin check`
    busybox sed -i "/is_admin/ d; /Administrator\ rights\ are\ required\ to\ install/ d; /exit\ 1/ d" $_.FullName
    node "1-remove-remaining-close-bracket-of-is-admin-check.mjs" $_.FullName

    # patch
    node "2-patch.mjs" $_.FullName
}

1-remove-remaining-close-bracket-of-is-admin-check.mjs

import { resolve } from 'path';
import { readFile, writeFile } from 'fs/promises';

if (process.argv.length < 3) {
    process.exit();
}

const filename = resolve(process.argv[2]);
const content = await readFile(filename, 'utf8');
const jsonObject = JSON.parse(content);

let hasModified = false;

if (jsonObject.installer.script[0] === '}') {
    jsonObject.installer.script.shift();
    hasModified = true;
}

if (jsonObject.uninstaller.script[0] === '}') {
    jsonObject.uninstaller.script.shift();
    hasModified = true;
}

if (hasModified) {
    await writeFile(filename, JSON.stringify(jsonObject, null, 4) + '\n');
}

2-patch.mjs

import { resolve } from "path";
import { readFile, writeFile } from "fs/promises";

if (process.argv.length < 3) {
    process.exit();
}

const filename = resolve(process.argv[2]);
const content = await readFile(filename, "utf8");
const jsonObject = JSON.parse(content);

const originInstallerScript = jsonObject.installer.script;
let customInstallerSteps = [];
let indexOfMainInstallerFirstStep = 0;
let indexOfEditRegistryStep = 1;

if (originInstallerScript.length !== 4) {
    indexOfEditRegistryStep = jsonObject.installer.script.findIndex((element) =>
        element.includes("New-ItemProperty")
    );
    indexOfMainInstallerFirstStep = indexOfEditRegistryStep - 1;
    customInstallerSteps = originInstallerScript.slice(
        0,
        indexOfMainInstallerFirstStep
    );
}

const mainInstallerFirstStep =
    originInstallerScript[indexOfMainInstallerFirstStep];

const getChildItemArguments = mainInstallerFirstStep
    .substring("Get-ChildItem ".length)
    .substring(
        0,
        mainInstallerFirstStep.substring("Get-ChildItem ".length).length -
            " | ForEach-Object {".length
    );

const fontType = originInstallerScript[indexOfEditRegistryStep].includes(
    "OpenType"
)
    ? "OpenType"
    : "TrueType";

const patchedInstallerScript = [
    ...customInstallerSteps,
    '$currentBuildNumber = [int] (Get-ItemProperty "HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion").CurrentBuildNumber',
    "$windows1809BuildNumber = 17763",
    "$isPerUserFontInstallationSupported = $currentBuildNumber -ge $windows1809BuildNumber",
    "$isFontInstallationForAllUsers = $global -or !$isPerUserFontInstallationSupported",
    "if ($isFontInstallationForAllUsers -and !(is_admin)) {",
    '    error "Administrator rights are required to install $app."',
    "    exit 1",
    "}",
    '$fontInstallDir = if ($isFontInstallationForAllUsers) { "$env:windir\\Fonts" } else { "$env:LOCALAPPDATA\\Microsoft\\Windows\\Fonts" }',
    '$registryRoot = if ($isFontInstallationForAllUsers) { "HKLM" } else { "HKCU" }',
    '$registryKey = "${registryRoot}:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"',
    "New-Item $fontInstallDir -ItemType Directory -ErrorAction SilentlyContinue | Out-Null",
    `Get-ChildItem ${getChildItemArguments} | ForEach-Object {`,
    '    $value = if ($isFontInstallationForAllUsers) { $_.Name } else { "$fontInstallDir\\$($_.Name)" }',
    `    New-ItemProperty -Path $registryKey -Name $_.Name.Replace($_.Extension, ' (${fontType})') -Value $value -Force | Out-Null`,
    "    Copy-Item $_.FullName -Destination $fontInstallDir",
    "}",
];

jsonObject.installer.script = patchedInstallerScript;

const originUninstallerScript = jsonObject.uninstaller.script;
const originUninstallMessage = originUninstallerScript[4];
const patchedUninstallerScript = [
    '$currentBuildNumber = [int] (Get-ItemProperty "HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion").CurrentBuildNumber',
    "$windows1809BuildNumber = 17763",
    "$isPerUserFontInstallationSupported = $currentBuildNumber -ge $windows1809BuildNumber",
    "$isFontInstallationForAllUsers = $global -or !$isPerUserFontInstallationSupported",
    "if ($isFontInstallationForAllUsers -and !(is_admin)) {",
    '    error "Administrator rights are required to uninstall $app."',
    "    exit 1",
    "}",
    '$fontInstallDir = if ($isFontInstallationForAllUsers) { "$env:windir\\Fonts" } else { "$env:LOCALAPPDATA\\Microsoft\\Windows\\Fonts" }',
    '$registryRoot = if ($isFontInstallationForAllUsers) { "HKLM" } else { "HKCU" }',
    '$registryKey = "${registryRoot}:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"',
    `Get-ChildItem ${getChildItemArguments} | ForEach-Object {`,
    `    Remove-ItemProperty -Path $registryKey -Name $_.Name.Replace($_.Extension, ' (${fontType})') -Force -ErrorAction SilentlyContinue`,
    '    Remove-Item "$fontInstallDir\\$($_.Name)" -Force -ErrorAction SilentlyContinue',
    "}",
    originUninstallMessage,
];

jsonObject.uninstaller.script = patchedUninstallerScript;

await writeFile(filename, JSON.stringify(jsonObject, null, 4) + "\n");

@issaclin32
Copy link
Collaborator

issaclin32 commented Nov 27, 2021

Thank you so much for providing the improvement. The script works fine for me. Let's wait for @matthewjberger 's opinion and checks.

@matthewjberger
Copy link
Owner

matthewjberger commented Nov 27, 2021

LGTM, this is amazing!

@jonz94 jonz94 force-pushed the font-installation-for-non-admin-user branch from ea12cd7 to 85c5771 Compare November 27, 2021 18:07
@jonz94
Copy link
Collaborator Author

jonz94 commented Nov 27, 2021

Hi @matthewjberger,

Is everything okay or any suggestions?

Just double-check before merge this PR, because this PR is a huge update XD

@matthewjberger
Copy link
Owner

@jonz94 It looks great. :) After checking it over I didn't see any noticeable issues with the script's logic, and the logic is reused across manifests so that adds to the PR's size but not its complexity!! No worries of course if something breaks though, because we can always revert if necessary. The scripts work properly for me locally, so I don't anticipate there being any issues. I appreciate you and @issaclin32 very much for helping me maintain and improve this repo!

@jonz94
Copy link
Collaborator Author

jonz94 commented Nov 27, 2021

@matthewjberger, I appreciate that you create this project in the first place. Save me so much time to install/upgrade my favorite fonts. Now, I even more appreciate to be able to being a part of it! 😊

@jonz94 jonz94 merged commit 4e9ef12 into matthewjberger:master Nov 27, 2021
@jonz94 jonz94 deleted the font-installation-for-non-admin-user branch November 27, 2021 18:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Font installation for non-admin users
3 participants