ElvUI.psm1
|
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidLongLines', '', Justification = 'Contains long links.')] [CmdletBinding()] param() $baseName = [System.IO.Path]::GetFileNameWithoutExtension($PSCommandPath) $script:PSModuleInfo = Import-PowerShellDataFile -Path "$PSScriptRoot\$baseName.psd1" $script:PSModuleInfo | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } $scriptName = $script:PSModuleInfo.Name Write-Debug "[$scriptName] - Importing module" #region Data importer Write-Debug "[$scriptName] - [data] - Processing folder" $dataFolder = (Join-Path $PSScriptRoot 'data') Write-Debug "[$scriptName] - [data] - [$dataFolder]" Get-ChildItem -Path "$dataFolder" -Recurse -Force -Include '*.psd1' -ErrorAction SilentlyContinue | ForEach-Object { Write-Debug "[$scriptName] - [data] - [$($_.BaseName)] - Importing" New-Variable -Name $_.BaseName -Value (Import-PowerShellDataFile -Path $_.FullName) -Force Write-Debug "[$scriptName] - [data] - [$($_.BaseName)] - Done" } Write-Debug "[$scriptName] - [data] - Done" #endregion Data importer #region [functions] - [private] Write-Debug "[$scriptName] - [functions] - [private] - Processing folder" #region [functions] - [private] - [Get-TukuiAddon] Write-Debug "[$scriptName] - [functions] - [private] - [Get-TukuiAddon] - Importing" function Get-TukuiAddon { <# .SYNOPSIS Fetches addon info from the Tukui API. .DESCRIPTION Returns metadata for a Tukui-hosted addon including version, download URL, description, supported patches, and more. When called without parameters, returns all available addons. .EXAMPLE Get-TukuiAddon -Name elvui Returns metadata for ElvUI. .EXAMPLE Get-TukuiAddon Returns all available Tukui addons. .OUTPUTS PSCustomObject #> [CmdletBinding()] param( # The slug name of the addon to retrieve. Omit to return all addons. [Parameter()] [ValidateSet('elvui', 'tukui')] [string] $Name ) if ($Name) { $url = "https://api.tukui.org/v1/addon/$Name" } else { $url = 'https://api.tukui.org/v1/addons' } $response = Invoke-RestMethod -Uri $url foreach ($addon in @($response)) { [PSCustomObject]@{ Id = $addon.id Slug = $addon.slug Name = $addon.name Author = $addon.author Version = $addon.version DownloadUrl = $addon.url ChangelogUrl = $addon.changelog_url TicketUrl = $addon.ticket_url GitUrl = $addon.git_url Patches = $addon.patch LastUpdate = $addon.last_update WebUrl = $addon.web_url DonateUrl = $addon.donate_url Description = $addon.small_desc ScreenshotUrl = $addon.screenshot_url GalleryUrls = $addon.gallery_url LogoUrl = $addon.logo_url LogoSquareUrl = $addon.logo_square_url Directories = $addon.directories } } } Write-Debug "[$scriptName] - [functions] - [private] - [Get-TukuiAddon] - Done" #endregion [functions] - [private] - [Get-TukuiAddon] #region [functions] - [private] - [Get-TukuiInstalledVersion] Write-Debug "[$scriptName] - [functions] - [private] - [Get-TukuiInstalledVersion] - Importing" function Get-TukuiInstalledVersion { <# .SYNOPSIS Gets the currently installed version of a Tukui addon from its .toc file. .DESCRIPTION Reads the .toc file for the specified addon in the AddOns folder and extracts the version string. Returns $null if the addon is not installed. .EXAMPLE Get-TukuiInstalledVersion -AddOnsPath 'C:\...\AddOns' -Name elvui Returns the installed ElvUI version string, or $null if not found. .OUTPUTS System.String or $null if not installed. #> [CmdletBinding()] param( # The full path to the WoW AddOns directory. [Parameter(Mandatory)] [string] $AddOnsPath, # The slug name of the addon to check. [Parameter(Mandatory)] [ValidateSet('elvui', 'tukui')] [string] $Name ) $addonFolder = switch ($Name) { 'elvui' { 'ElvUI' } 'tukui' { 'Tukui' } } $tocCandidates = @( (Join-Path -Path $AddOnsPath -ChildPath $addonFolder | Join-Path -ChildPath "${addonFolder}_Mainline.toc") (Join-Path -Path $AddOnsPath -ChildPath $addonFolder | Join-Path -ChildPath "$addonFolder.toc") ) $tocPath = $tocCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1 if (-not $tocPath) { return $null } $tocContent = Get-Content -Path $tocPath -Raw if ($tocContent -match '(?m)^## Version:\s*(.+)$') { return $Matches[1].Trim().TrimStart('v') } return $null } Write-Debug "[$scriptName] - [functions] - [private] - [Get-TukuiInstalledVersion] - Done" #endregion [functions] - [private] - [Get-TukuiInstalledVersion] #region [functions] - [private] - [Get-WoWAddOnsPath] Write-Debug "[$scriptName] - [functions] - [private] - [Get-WoWAddOnsPath] - Importing" function Get-WoWAddOnsPath { <# .SYNOPSIS Resolves the WoW AddOns folder path for a given flavor. .DESCRIPTION Constructs and validates the full path to the World of Warcraft AddOns directory based on the installation path and game flavor. .EXAMPLE Get-WoWAddOnsPath Returns the default retail AddOns path: C:\Program Files (x86)\World of Warcraft\_retail_\Interface\AddOns .EXAMPLE Get-WoWAddOnsPath -WoWPath 'D:\Games\World of Warcraft' -Flavor '_classic_' Returns the classic AddOns path under a custom installation directory. .OUTPUTS System.String #> [CmdletBinding()] param( # Path to the World of Warcraft installation folder. [Parameter()] [string] $WoWPath = 'C:\Program Files (x86)\World of Warcraft', # WoW game flavor to target. [Parameter()] [ValidateSet('_retail_', '_classic_', '_classic_era_')] [string] $Flavor = '_retail_' ) $addOnsPath = Join-Path -Path $WoWPath -ChildPath $Flavor | Join-Path -ChildPath 'Interface' | Join-Path -ChildPath 'AddOns' if (-not (Test-Path $addOnsPath)) { throw "AddOns folder not found: $addOnsPath" } $addOnsPath } Write-Debug "[$scriptName] - [functions] - [private] - [Get-WoWAddOnsPath] - Done" #endregion [functions] - [private] - [Get-WoWAddOnsPath] #region [functions] - [private] - [Install-TukuiAddon] Write-Debug "[$scriptName] - [functions] - [private] - [Install-TukuiAddon] - Importing" function Install-TukuiAddon { <# .SYNOPSIS Downloads and installs a Tukui addon to the WoW AddOns folder. .DESCRIPTION Downloads the ZIP archive from the Tukui API, extracts it, removes old addon folders, and copies the new ones into place. Uses a temporary directory that is cleaned up automatically. .EXAMPLE $addon = Get-TukuiAddon -Name elvui Install-TukuiAddon -AddOnsPath 'C:\...\AddOns' -Addon $addon Downloads and installs ElvUI to the specified AddOns directory. #> [CmdletBinding()] param( # The full path to the WoW AddOns directory. [Parameter(Mandatory)] [string] $AddOnsPath, # The addon object returned by Get-TukuiAddon containing metadata and download URL. [Parameter(Mandatory)] [PSCustomObject] $Addon ) $tempDir = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath "Tukui_$($Addon.Slug)_Update_$([guid]::NewGuid().ToString('N'))" try { if (Test-Path $tempDir) { Remove-Item $tempDir -Recurse -Force } $null = New-Item -ItemType Directory -Path $tempDir # Download $zipPath = Join-Path -Path $tempDir -ChildPath "$($Addon.Slug)-$($Addon.Version).zip" Write-Verbose "Downloading $($Addon.Name) $($Addon.Version) ..." Invoke-WebRequest -Uri $Addon.DownloadUrl -OutFile $zipPath # Extract $extractPath = Join-Path -Path $tempDir -ChildPath 'extracted' Write-Verbose 'Extracting...' Expand-Archive -Path $zipPath -DestinationPath $extractPath -Force # Remove old addon folders matching the known directory names foreach ($dir in $Addon.Directories) { $oldPath = Join-Path -Path $AddOnsPath -ChildPath $dir if (Test-Path $oldPath) { Write-Verbose " Removing $dir" Remove-Item $oldPath -Recurse -Force } } # Copy new folders $extractedFolders = Get-ChildItem -Path $extractPath -Directory foreach ($folder in $extractedFolders) { $destination = Join-Path -Path $AddOnsPath -ChildPath $folder.Name Write-Verbose " Installing $($folder.Name)" Copy-Item -Path $folder.FullName -Destination $destination -Recurse -Force } Write-Verbose "$($Addon.Name) $($Addon.Version) installed successfully!" } finally { if (Test-Path $tempDir) { Remove-Item $tempDir -Recurse -Force } } } Write-Debug "[$scriptName] - [functions] - [private] - [Install-TukuiAddon] - Done" #endregion [functions] - [private] - [Install-TukuiAddon] Write-Debug "[$scriptName] - [functions] - [private] - Done" #endregion [functions] - [private] #region [functions] - [public] Write-Debug "[$scriptName] - [functions] - [public] - Processing folder" #region [functions] - [public] - [Install-ElvUI] Write-Debug "[$scriptName] - [functions] - [public] - [Install-ElvUI] - Importing" function Install-ElvUI { <# .SYNOPSIS Downloads and installs ElvUI to the WoW AddOns folder. .DESCRIPTION Fetches the latest ElvUI release from the Tukui API, downloads the ZIP archive, and installs it to the World of Warcraft AddOns directory. Any existing ElvUI folders are removed before the new version is copied into place. .EXAMPLE Install-ElvUI Installs ElvUI to the default retail WoW AddOns folder. .EXAMPLE Install-ElvUI -WoWPath 'D:\Games\World of Warcraft' Installs ElvUI using a custom WoW installation path. .EXAMPLE Install-ElvUI -Flavor '_classic_' Installs ElvUI to the Classic WoW AddOns folder. #> [CmdletBinding(SupportsShouldProcess)] param( # Path to the World of Warcraft installation folder. [Parameter()] [string] $WoWPath = 'C:\Program Files (x86)\World of Warcraft', # WoW game flavor to target. [Parameter()] [ValidateSet('_retail_', '_classic_', '_classic_era_')] [string] $Flavor = '_retail_' ) $addOnsPath = Get-WoWAddOnsPath -WoWPath $WoWPath -Flavor $Flavor $addon = Get-TukuiAddon -Name elvui if ($PSCmdlet.ShouldProcess($addOnsPath, "Install $($addon.Name) $($addon.Version)")) { Write-Verbose "Installing $($addon.Name) $($addon.Version) ..." Install-TukuiAddon -AddOnsPath $addOnsPath -Addon $addon } } Write-Debug "[$scriptName] - [functions] - [public] - [Install-ElvUI] - Done" #endregion [functions] - [public] - [Install-ElvUI] #region [functions] - [public] - [Update-ElvUI] Write-Debug "[$scriptName] - [functions] - [public] - [Update-ElvUI] - Importing" function Update-ElvUI { <# .SYNOPSIS Updates the ElvUI addon to the latest version. .DESCRIPTION Checks the installed ElvUI version against the latest available version from the Tukui API. If an update is available (or -Force is used), downloads and installs the new version. If ElvUI is not installed, performs a fresh install. .EXAMPLE Update-ElvUI Updates ElvUI in the default retail WoW installation. .EXAMPLE Update-ElvUI -WoWPath 'D:\Games\World of Warcraft' Updates ElvUI using a custom WoW installation path. .EXAMPLE Update-ElvUI -Flavor '_classic_' Updates ElvUI in the Classic WoW AddOns folder. .EXAMPLE Update-ElvUI -Force Reinstalls ElvUI even if the installed version matches the latest. #> [CmdletBinding(SupportsShouldProcess)] param( # Path to the World of Warcraft installation folder. [Parameter()] [string] $WoWPath = 'C:\Program Files (x86)\World of Warcraft', # WoW game flavor to target. [Parameter()] [ValidateSet('_retail_', '_classic_', '_classic_era_')] [string] $Flavor = '_retail_', # Force reinstall even if the installed version matches the latest. [Parameter()] [switch] $Force ) $addOnsPath = Get-WoWAddOnsPath -WoWPath $WoWPath -Flavor $Flavor $installedVersion = Get-TukuiInstalledVersion -AddOnsPath $addOnsPath -Name elvui if ($installedVersion) { Write-Verbose "Installed version: $installedVersion" } else { Write-Verbose 'No existing ElvUI installation detected. Installing fresh.' } $addon = Get-TukuiAddon -Name elvui Write-Verbose "Latest ElvUI version: $($addon.Version)" if ($installedVersion -eq $addon.Version -and -not $Force) { Write-Verbose 'ElvUI is already up to date. Use -Force to reinstall.' return } if ($installedVersion -eq $addon.Version) { Write-Verbose "Forcing reinstall of $($addon.Version) ..." } elseif ($installedVersion) { Write-Verbose "Updating from $installedVersion to $($addon.Version) ..." } if ($PSCmdlet.ShouldProcess($addOnsPath, "Install $($addon.Name) $($addon.Version)")) { Install-TukuiAddon -AddOnsPath $addOnsPath -Addon $addon } } Write-Debug "[$scriptName] - [functions] - [public] - [Update-ElvUI] - Done" #endregion [functions] - [public] - [Update-ElvUI] Write-Debug "[$scriptName] - [functions] - [public] - Done" #endregion [functions] - [public] #region Member exporter $exports = @{ Alias = '*' Cmdlet = '' Function = @( 'Install-ElvUI' 'Update-ElvUI' ) Variable = '' } Export-ModuleMember @exports #endregion Member exporter |