TroubleChute Logo
< Go Back

TroubleChute Script Hub

Currently viewing: PowerShell/Modules/Get-TCHTPath.psm1


NOTE: The following code may be out-of-date compared to GitHub, but is all pulled from GitHub every hour or so.

When shortlinks are used (filename as subdomain), code used by PowerShell and other interactions with this site is served from GitHub.

(Right-click -> Download the button below to save the file)

Last modified: 2024-06-19T23:33:48.000+02:00

Get-TCHTPath.psm1
# Copyright (C) 2023 TroubleChute (Wesley Pyburn)
# Licensed under the GNU General Public License v3.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.gnu.org/licenses/gpl-3.0.en.html
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#    
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#    
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see .
#
# ----------------------------------------
# This function gets and/or sets the TCHT install path.
# ----------------------------------------

function Get-TCHTPathSaved() {
    $os = [System.Environment]::OSVersion.Platform.ToString()

    switch ($os) {
        "Win32NT" {
            $registryPath = "HKCU:\Software\TCHT"
            if (Test-Path $registryPath) {
                return (Get-ItemProperty $registryPath).Path
            } else {
                return ""
            }
            break
        }
        "Unix" {
            # If gsettings installed:
            if (command -v gsettings) {
                Write-Host "gsettings is installed"
                
                $gsettingsValue = $(gsettings get tc.ht path 2> $null)
                if ($LASTEXITCODE -eq 0) {
                    return $gsettingsValue.Trim("`"'")
                }
            }

            return ""
            break
        }
        default {
            throw "Unsupported operating system."
        }
    }
}


function Get-TCHTPathFromUser() {
    $os = [System.Environment]::OSVersion.Platform.ToString()

    switch ($os) {
        "Win32NT" {
            if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
                Write-Host "This script needs to be run as an administrator.`nProcess can try to continue, but will likely fail. Press Enter to continue..." -ForegroundColor Red
                Read-Host
            }

            $path = "C:\TCHT"
            break
        }
        "Unix" {
            $uname = $(uname)
            if ($uname -eq "Darwin") {
                $path = Join-Path (Resolve-Path "~/Documents") "TCHT"
            } else {
                $path = "/home/TCHT"
            }
            break
        }
        default {
            throw "Unsupported operating system."
        }
    }


    # Ask the user where to install
    $installLocation = (Read-Host @"
Pick where to install:
1. (default) $path
2. Current folder: $((Get-Location).Path)
or Enter a custom path
"@).Trim()

    $firstLoop = $True
    if ($installLocation -in "1", "", " ") {
        $installLocation = $path
        if (!(Test-Path $installLocation -PathType Container)) {
            Write-Host "Folder created: $installLocation"
            New-Item -ItemType Directory -Path $installLocation | Out-Null
        }        
    }
    elseif ($installLocation -eq "2") {
        $installLocation = (Get-Location).Path
        if (!(Test-Path $installLocation -PathType Container)) {
            Write-Host "Folder created: $installLocation"
            New-Item -ItemType Directory -Path $installLocation | Out-Null
        }
    }
    # Else, a custom path entered. Check the path exists and prompt about spaces.
    do {
        if (-not $firstLoop) {
            $installLocation = (Read-Host "Please enter a custom path").Trim()
            $firstLoop = $False
        } else {
            if (!(Test-Path $installLocation -PathType Container)) {
                $createFolder = (Read-Host "The folder $installLocation does not exist. Do you want to create it? (Y/N)").Trim()
                if ($createFolder -eq "Y" -or $createFolder -eq "y") {
                    Write-Host "Folder created: $installLocation"
                    New-Item -ItemType Directory -Path $installLocation | Out-Null
                }
            }            
        }
        if ($installLocation.Contains(" ")) {
            $proceedAnyway = (Read-Host "Using a path with a space can result in things not working properly. Enter another path or type Y to use the current path: $installPath").Trim()
        }
    } while ($installLocation.Contains(" ") -and $proceedAnyway -notin 'Y', 'y')

    Write-Host "Saving path..."
    Set-TCHTPath -Path $installLocation

    Write-Host "Installing this, and future TC.HT programs to: $installLocation"
    return $installLocation
}


function Install-GSettings {
    $os = [System.Environment]::OSVersion.Platform.ToString()

    switch ($os) {
        "Win32NT" {
            Write-Host "You only need GSettings on Mac or Linux."
            return
        }
        "Unix" {
            if (which apt-get) {
                # Ubuntu, Debian, Raspbian, Kali, etc.
                apt-get update
                apt-get install -y gsettings-ubuntu-schemas
                if (command -v gsettings) { return }
            }
            if (which dnf) {
                # Fedora, RedHat, CentOS, etc.
                dnf install -y gsettings-desktop-schemas
                if (command -v gsettings) { return }
            }
            if (which yum) {
                # CentOS, RedHat, etc.
                yum install -y gsettings-desktop-schemas
                if (command -v gsettings) { return }
            }
            if (which apk) {
                # Alpine, etc.
                apk update
                apk add glib-dev
                apk add gsettings-desktop-schemas
                if (command -v gsettings) { return }
            }
            if (which pacman) {
                # Pacman
                pacman -S glib2
                if (command -v gsettings) { return }
            }
            
            Write-Error "Could not find a package manager to install gsettings."
            break
        }
        default {
            throw "Unsupported operating system."
        }
    }
}


function Set-TCHTPath() {
    param (
        [Parameter(Mandatory=$true)]
        [string]$Path
    )

    $os = [System.Environment]::OSVersion.Platform.ToString()

    switch ($os) {
        "Win32NT" {
            $registryPath = "HKCU:\Software\TCHT"
            if (Test-Path $registryPath) {
                Set-ItemProperty $registryPath -Name Path -Value $path
            } else {
                New-Item -Path $registryPath -Force | Out-Null
                New-ItemProperty -Path $registryPath -Name Path -Value $path -PropertyType String | Out-Null
            }
            break
        }
        "Unix" {
            if (command -v gsettings) {
                Write-Host "gsettings installed."
                if (!(command -v glib-compile-schemas)) {
                    Write-Host "glib not installed. Installing..."
                    brew install glib
                }

                # Install schema
                @"


  
    
      '$HOME/Documents/TCHT'
      Path for TCHT
      Path where TCHT files are located.
    
  

"@ | Set-Content -Path ./tc.ht.gschema.xml
                
                $SchemaDir = "$HOME/.local/share/glib-2.0/schemas"
                if (-not (Test-Path -Path $SchemaDir)) {
                    New-Item -ItemType Directory -Path $SchemaDir -Force | Out-Null
                }

                glib-compile-schemas . --strict --targetdir=$HOME/.local/share/glib-2.0/schemas
                $Env:GSETTINGS_SCHEMA_DIR="$HOME/.local/share/glib-2.0/schemas"
                
                Write-Host "Saving gsettings path as /tc.ht/path, $path"
                gsettings set tc.ht path "$HOME/Documents/TCHT"
            } else {
                # If not gsettings installed:
                if (! $(command -v gsettings)) {
                    Write-Host "gsettings not installed. Installing..."
                    Install-GSettings
                }

                Write-Host "Saving gsettings path as /tc.ht/path, $path"
                # If gsettings installed:
                gsettings write "/tcht/path" "$path"
            }
            break
        }
        default {
            throw "Unsupported operating system."
        }
    }

}

# From: https://stackoverflow.com/a/818054
# Tests to see if path is a symlink
function Test-ReparsePoint([string]$path) {
    $file = Get-Item $path -Force -ea SilentlyContinue
    return [bool]($file.Attributes -band [IO.FileAttributes]::ReparsePoint)
  }
  
function Get-TCHTPath() {
    param (
        [Parameter()]
        [string]$Subfolder = ""
    )
    $path = Get-TCHTPathSaved

    if ($path -eq "") {
        $path = Get-TCHTPathFromUser
    } else {
        # Ask the user if they want to install this software here, or in another location
        $clearScreenFound = [bool](Get-Command Clear-ConsoleScreen -erroraction silentlycontinue)
        if (!$clearScreenFound) {
            iex (irm Import-RemoteFunction.tc.ht)
            Import-RemoteFunction("Get-GeneralFuncs.tc.ht")
        }

        if ($Subfolder -ne "") {
            Write-Host "$Subfolder will install to $path." -ForegroundColor Cyan
        } else {
            Write-Host "The program will install to $path." -ForegroundColor Cyan
        }
        do {
            Write-Host -ForegroundColor Cyan -NoNewline "`n`nDo you want to install it somewhere else? (y/n) [Default: n]: "
            $installElsewhere = (Read-Host).Trim()
        } while ($installElsewhere -notin "Y", "y", "N", "n", "")
        
        if (($installElsewhere -in "Y", "y") -and !("" -eq $installElsewhere)) {
            Clear-ConsoleScreen
            Write-Host "The install script choses a folder, and installs programs inside of it. This is what you're setting." -ForegroundColor Cyan
            Write-Host "The default is C:\TCHT"
            $chosenPath = ""
            do {
                Write-Host -ForegroundColor Cyan -NoNewline "`n`nEnter a path to install this (and possibly other programs) to: "
                $chosenPath = (Read-Host).Trim()
            } while ($chosenPath -eq "")

            # For some reason POWERSHELL LOVES CHANGING THE TYPE OF THIS TO AN OBJECT
            $returnValue = "$chosenPath"

            # If doesn't exist, create the path
            if (!(Test-Path -Path $chosenPath)) {
                New-Item -ItemType Directory -Path $chosenPath | Out-Null
            }
            
            # If $Subfolder is set
            if ($Subfolder -ne "") {
                $originalPath = Join-Path -Path $path -ChildPath $Subfolder
                $chosenPathSubfolder = Join-Path -Path $chosenPath -ChildPath $Subfolder
                
                # If the $originalPath exists:
                if (Test-Path -Path $originalPath) {
                    $existsAsSymlink = Test-ReparsePoint -path $originalPath

                    # If $existsAsSymlink: Expand symlink path to real path
                    if ($existsAsSymlink) {
                        $symlinkPath = $originalPath
                        $originalPath = (Get-Item $originalPath).Target

                        # Remove existing, now wrong, symlink:
                        Remove-Item -Path $symlinkPath | Out-Null
                    }

                    Write-Host "Moving existing files from $originalPath to $chosenPathSubfolder..." -ForegroundColor Yellow
                    Move-Item -Path $originalPath -Destination $chosenPath -Force
                }
            }

            # Case insensitive compare paths
            if (!($path -ieq $chosenPath)) {
                do {
                    Clear-ConsoleScreen
                    Write-Host -ForegroundColor Cyan -NoNewline "Would you like to install all future TCHT programs to this path? ($chosenPath) (y/n): "
                    $changeDefault = (Read-Host).Trim()
                } while ($changeDefault -notin "Y", "y", "N", "n")
    
                # Set default if chosen
                if ($changeDefault -in "Y", "y") {
                    Write-Host "`nCalculating existing folder size..."
                    $getTotalFolderSize = [bool](Get-Command Get-FolderSize -erroraction silentlycontinue)
                    if (!$getTotalFolderSize) {
                        iex (irm Import-RemoteFunction.tc.ht)
                        Import-RemoteFunction("Get-GeneralFuncs.tc.ht")
                    }
                    $existingFolderSize = Get-FolderSize -Path $path
                    
                    do {
                        Clear-ConsoleScreen
                        Write-Host -ForegroundColor Cyan -NoNewline "Do you want to move existing programs installed to ($path) to ($chosenPath)? Total size: $existingFolderSize (y/n): "
                        $moveExisting = (Read-Host).Trim()
                    } while ($moveExisting -notin "Y", "y", "N", "n")
                    
                    if ($moveExisting -in "Y", "y") {
                        # Foreach folder in $path
                        # Move to $chosenPath
                        # Create symlink in $path to $chosenPath
                        ForEach-Object -InputObject (Get-ChildItem -Path $path -Directory) -Process {
                            $folderName = $_.Name
                            $folderPath = Join-Path -Path $path -ChildPath $folderName
                            $folderPathChosen = Join-Path -Path $chosenPath -ChildPath $folderName
    
                            # If $folderPathChosen exists
                            if (Test-Path -Path $folderPathChosen) {
                                do {
                                    try {
                                        Remove-Item $folderPathChosen -Recurse -Force -ErrorAction Stop
                                    }
                                    catch {
                                        if ($_.Exception.Message.Contains("being used by another process")) {
                                            Write-Verbose "File locked, trying again in 5 seconds"
                                            Start-Sleep -Seconds 5
                                        }
                                        else {
                                            Write-Error $_.Exception.Message
                                            break
                                        }
                                    }
                                } until (!(Test-Path -Path $folderPathChosen))
                            }
    
                            Write-Host "`nMoving $folderPath to $folderPathChosen..." -ForegroundColor Yellow
                            Move-Item -Path $folderPath -Destination $chosenPath -Force
    
                            Write-Host "Creating symlink from $folderPath to $folderPathChosen..." -ForegroundColor Yellow
                            New-Item -ItemType SymbolicLink -Path $folderPath -Target $folderPathChosen -Force | Out-Null
                        }
    
                        Write-Host "Done moving!" -ForegroundColor Green
                        Write-Host "The files have physically moved, but will still 'appear' to exist in the original path - This is so shortcuts still work." -ForegroundColor Cyan
                        Write-Host "Continuing in 5 seconds... Scroll up to continue reading!" -ForegroundColor Yellow
                        Start-Sleep -s 5
                    } else {
                        # Else, just symlink all the existing folders in the new folder
                        # And copy any symlinks that may exist in the old folder, so no symlinks are symlinked.
                        ForEach-Object -InputObject (Get-ChildItem -Path $path -Directory) -Process {
                            $folderName = $_.Name
                            $folderPath = Join-Path -Path $path -ChildPath $folderName
    
                            $existsAsSymlink = Test-ReparsePoint -path $originalPath
                            # If $existsAsSymlink: Expand symlink path to real path
                            if ($existsAsSymlink) {
                                $folderPath = (Get-Item $folderPath).Target
                            }
    
                            $folderPathChosen = Join-Path -Path $chosenPath -ChildPath $folderName
    
                            Write-Host "`nCreating symlink from $folderPath to $folderPathChosen..." -ForegroundColor Yellow
                            New-Item -ItemType SymbolicLink -Path $folderPath -Target $folderPathChosen -Force | Out-Null
                        }
                    }
    
                    Set-TCHTPath -Path $chosenPath
                }
            }

            return $returnValue
        } else {
            if ($Subfolder -ne ""){
                # Wants to install to default location, but may be moving everything back on update/reinstall
                # So, if it exists in another location (and is symlinked currently), move it and create a symlink in the old install path.
                $returnValue = "$path"
        
                $folderPath = Join-Path -Path $path -ChildPath $Subfolder
                $existsAsSymlink = Test-ReparsePoint -path $folderPath
                # If $existsAsSymlink: Expand symlink path to real path
                if ($existsAsSymlink) {    
                    $symlinkPath = $folderPath
                    $folderPath = (Get-Item $folderPath).Target
                    Remove-Item $symlinkPath -Recurse -Force | Out-Null
    
                    Write-Host "Moving existing files from $folderPath to $path..." -ForegroundColor Yellow
                    Move-Item -Path $folderPath -Destination $path -Force
        
                    Write-Host "`nCreating symlink from $folderPath to $symlinkPath..." -ForegroundColor Yellow
                    New-Item -ItemType SymbolicLink -Path $folderPath -Target $symlinkPath -Force | Out-Null
                }
            }
        }
    }

    if (!$returnValue) {
        $returnValue = "$path"
    }

    # We'll create $TCHT if it doesn't already exist:
    if (!(Test-Path -Path $returnValue)) {
        New-Item -ItemType Directory -Path $returnValue | Out-Null
    }
    return $returnValue
}

<#
.SYNOPSIS
When a TCHT program is NOT being installed to the default path, then a symlink in the default path is created.
Returns whether is symlink or not.

.PARAMETER Path
Path to the new chosen install path (not including the program name)

.PARAMETER Subfolder
The programs name to be appended to Path
#>
function Sync-ProgramFolder() {
    param(
        [parameter(Mandatory=$true)][string]$ChosenPath,
        [parameter(Mandatory=$true)][string]$Subfolder
    )

    $clearScreenFound = [bool](Get-Command Clear-ConsoleScreen -erroraction silentlycontinue)
    if (!$clearScreenFound) {
        iex (irm Import-RemoteFunction.tc.ht)
        Import-RemoteFunction("Get-GeneralFuncs.tc.ht")
    }

    $savedPath = Get-TCHTPathSaved

    if (!($ChosenPath -eq $savedPath)) {
        # User has chosen to install this in another directory
        $actualInstallPath = Join-Path -Path $ChosenPath -ChildPath $Subfolder
        $symlinkPath = Join-Path -Path $savedPath -ChildPath $Subfolder

        # If actualInstallPath doesn't exist, create it
        if (!(Test-Path -Path $actualInstallPath)) {
            New-Item -ItemType Directory -Path $actualInstallPath | Out-Null
        }

        # If savedPath doesn't exist, create it
        if (!(Test-Path -Path $savedPath)) {
            New-Item -ItemType Directory -Path $savedPath | Out-Null
        }

        # Create a symlink to the subfolder in savedPath
        New-Item -ItemType SymbolicLink -Path $symlinkPath -Target $actualInstallPath | Out-Null

        Clear-ConsoleScreen
        Write-Host "The installer has created a link between your default ($savedPath) and where it is actually installed ($actualInstallPath)" -ForegroundColor Cyan
        Write-Host "`n`nEven though the files appear to be on $symlinkPath, they take up no space, and are actually on $actualInstallPath"
        Write-Host "This is performed so shortcuts still work, easy management, etc."
        Write-Host "Interactions between the symlink (`"shortcut`") will appear in the actual location they are installed to. New files, changes, etc.`n`n"

        Write-Host "Continuing in 5 seconds... Scroll up to continue reading!" -ForegroundColor Yellow
        Start-Sleep -s 5
        return $true
    }

    return $false
}

TCNO TechNobo / TroubleChute © Wesley Pyburn (TroubleChute)
Support Me Privacy Policy Cookies Policy Terms of Service Contact