diff --git a/.gitattributes b/.gitattributes index 06ec582cb..fb2ccc79f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1 @@ -src/wingetui/choco-cli/** linguist-vendored -src/wingetui/ExternalLibraries/** linguist-vendored \ No newline at end of file +src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/** linguist-vendored \ No newline at end of file diff --git a/.github/workflows/dotnet-desktop.yml b/.github/workflows/dotnet-desktop.yml index 4d34a1217..26cfa365b 100644 --- a/.github/workflows/dotnet-desktop.yml +++ b/.github/workflows/dotnet-desktop.yml @@ -1,11 +1,10 @@ -name: Build +name: Test and Build on: pull_request: branches: [ "main" ] workflow_dispatch: - jobs: build: @@ -13,7 +12,7 @@ jobs: # https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on steps: - - name: Checkout + - name: Checkout the repository uses: actions/checkout@v4 with: fetch-depth: 0 @@ -24,28 +23,17 @@ jobs: with: dotnet-version: 8.0.x - - name: Install winget + - name: Install WinGet uses: Cyberboss/install-winget@v1 - - # Add MSBuild to the PATH: https://github.com/microsoft/setup-msbuild - - name: Setup MSBuild.exe - uses: microsoft/setup-msbuild@v2.0.0 - - # Execute all unit tests in the solution - # - name: Execute unit tests - # run: dotnet test - - # Restore the application to populate the obj folder with RuntimeIdentifiers - - - name: Setup App Sdk + - name: Run Tests run: | - cd InstallerExtras - ./appsdk.exe + cd src + dotnet test -v q --nologo cd .. - - name: build and run + - name: Build solution run: | - mkdir out - msbuild src/UniGetUI.sln /noLogo /property:Configuration=Release /property:Platform=x64 /restore:True /interactive:False /property:OutputPath=.\out - exit $LASTEXITCODE + cd src + dotnet publish -v q UniGetUI.sln /noLogo /restore:True /interactive:False + exit $LASTEXITCODE \ No newline at end of file diff --git a/.gitignore b/.gitignore index b45c8f383..3f049dfa3 100644 --- a/.gitignore +++ b/.gitignore @@ -32,9 +32,20 @@ src/packages/ src/UniGetUI/bin src/UniGetUI/obj - src/WindowsPackageManager.Interop/bin src/WindowsPackageManager.Interop/obj +src/WindowsPackageManager.Interop/out +src/WindowsPackageManager.Interop/outpublish + +src/UniGetU*/bin +src/UniGetU*/obj +src/UniGetU*/out +src/UniGetU*/outpublish + +src/ExternalLibraries.*/bin +src/ExternalLibraries.*/obj +src/ExternalLibraries.*/out +src/ExternalLibraries.*/outpublish src/.vs src/.vscode @@ -65,3 +76,5 @@ src/UniGetUI/choco-cli/redirects/cup.exe.ignore src/UniGetUI/choco-cli/bin/ssh-copy-id.exe src/UniGetUI/choco-cli/extensions/chocolatey-visualstudio/ src/UniGetUI/choco-cli/extensions/chocolatey-dotnetfx/ + +*.user diff --git a/WingetUI.iss b/WingetUI.iss index c05a66952..316d6ff47 100644 --- a/WingetUI.iss +++ b/WingetUI.iss @@ -1,7 +1,7 @@ ; Script generated by the Inno Setup Script Wizard. ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! -#define MyAppVersion "3.1.0-beta" +#define MyAppVersion "3.1.0-alpha" #define MyAppName "UniGetUI (formerly WingetUI)" #define MyAppPublisher "Martí Climent" #define MyAppURL "https://github.com/marticliment/WingetUI" diff --git a/readme.md b/readme.md index 576e20f85..3dd231cec 100644 --- a/readme.md +++ b/readme.md @@ -126,7 +126,7 @@ To translate WingetUI to other languages or to update an old translation, please |   English - English | 100% | [marticliment](https://github.com/marticliment), [ppvnf](https://github.com/ppvnf) | |   Spanish - Castellano | 100% | [apazga](https://github.com/apazga), [dalbitresb12](https://github.com/dalbitresb12), [evaneliasyoung](https://github.com/evaneliasyoung), [guplem](https://github.com/guplem), [JMoreno97](https://github.com/JMoreno97), [marticliment](https://github.com/marticliment), [rubnium](https://github.com/rubnium), [uKER](https://github.com/uKER) | |   Persian - فارسی‎ | 66% | [Imorate](https://github.com/Imorate), [itsarian](https://github.com/itsarian), [Mahdi-Hazrati](https://github.com/Mahdi-Hazrati), [smsi2001](https://github.com/smsi2001) | -|   Finnish - Suomi | 43% | [simakuutio](https://github.com/simakuutio) | +|   Finnish - Suomi | 50% | [simakuutio](https://github.com/simakuutio) | |   French - Français | 100% | BreatFR, Evans Costa, [PikPakPik](https://github.com/PikPakPik), Rémi Guerrero, [W1L7dev](https://github.com/W1L7dev) | |   Hindi - हिंदी | 56% | [atharva_xoxo](https://github.com/atharva_xoxo), [satanarious](https://github.com/satanarious) | |   Croatian - Hrvatski | 60% | Stjepan Treger | @@ -156,7 +156,7 @@ To translate WingetUI to other languages or to update an old translation, please |   Simplified Chinese (China) | 100% | Aaron Liu, adfnekc, [arthurfsy2](https://github.com/arthurfsy2), [bai0012](https://github.com/bai0012), BUGP Association, ciaran, CnYeSheng, Cololi, [FloatStream](https://github.com/FloatStream), [SpaceTimee](https://github.com/SpaceTimee), Yisme | |   Traditional Chinese (Taiwan) | 100% | Aaron Liu, CnYeSheng, Cololi, [Henryliu880922](https://github.com/Henryliu880922), [yrctw](https://github.com/yrctw) | -Last updated: Fri May 17 00:11:37 2024 +Last updated: Sun May 19 14:36:17 2024 diff --git a/scripts/Languages/LangData.py b/scripts/Languages/LangData.py index 40580c7fd..540c76ac7 100644 --- a/scripts/Languages/LangData.py +++ b/scripts/Languages/LangData.py @@ -9,29 +9,29 @@ import os import json -if os.path.exists("../src/UniGetUI/Assets/Data/Contributors.list"): - f = open("../src/UniGetUI/Assets/Data/Contributors.list", "r", encoding="utf-8") +if os.path.exists("../src/UniGetUI.Core.Data/Assets/Data/Contributors.list"): + f = open("../src/UniGetUI.Core.Data/Assets/Data/Contributors.list", "r", encoding="utf-8") contributors = f.readlines() else: print("No contributors file!") contributors = [] -if os.path.exists("../src/UniGetUI/Assets/Data/Translators.json"): - f = open("../src/UniGetUI/Assets/Data/Translators.json", "r", encoding="utf-8") +if os.path.exists("../src/UniGetUI.Core.LanguageEngine/Assets/Data/Translators.json"): + f = open("../src/UniGetUI.Core.LanguageEngine/Assets/Data/Translators.json", "r", encoding="utf-8") languageCredits = json.load(f) else: print("No translators file!") languageCredits = {} -if os.path.exists("../src/UniGetUI/Assets/Data/TranslatedPercentages.json"): - f = open("../src/UniGetUI/Assets/Data/TranslatedPercentages.json", "r", encoding="utf-8") +if os.path.exists("../src/UniGetUI.Core.LanguageEngine/Assets/Data/TranslatedPercentages.json"): + f = open("../src/UniGetUI.Core.LanguageEngine/Assets/Data/TranslatedPercentages.json", "r", encoding="utf-8") untranslatedPercentage = json.load(f) else: print("No translated percent file!") untranslatedPercentage = {} -if os.path.exists("../src/UniGetUI/Assets/Data/LanguagesReference.json"): - f = open("../src/UniGetUI/Assets/Data/LanguagesReference.json", "r", encoding="utf-8") +if os.path.exists("../src/UniGetUI.Core.LanguageEngine/Assets/Data/LanguagesReference.json"): + f = open("../src/UniGetUI.Core.LanguageEngine/Assets/Data/LanguagesReference.json", "r", encoding="utf-8") languageReference = json.load(f) else: print("No translated percent file!") @@ -88,7 +88,7 @@ def getMarkdownSupportLangs(): dir = os.path.dirname(__file__) for lang, langName in languageReference.items(): - if (not os.path.exists(f"{dir}/../../src/UniGetUI/Assets/Languages/lang_{lang}.json")): + if (not os.path.exists(f"{dir}/../../src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_{lang}.json")): continue perc = untranslatedPercentage[lang] if (lang in untranslatedPercentage) else "100%" diff --git a/scripts/apply_versions.py b/scripts/apply_versions.py index a5ab0cf11..1c5a8d7fd 100644 --- a/scripts/apply_versions.py +++ b/scripts/apply_versions.py @@ -24,14 +24,15 @@ def fileReplaceLinesWith(filename: str, list: dict[str, str], encoding="utf-8"): f.write(data) f.truncate() - fileReplaceLinesWith("src/UniGetUI/Core/Data/Core.cs", { + fileReplaceLinesWith("src/UniGetUI.Core.Data/Core.cs", { " public static string VersionName = ": f" \"{versionName}\"; // Do not modify this line, use file scripts/apply_versions.py\n", " public static double VersionNumber = ": f" {versionCode}; // Do not modify this line, use file scripts/apply_versions.py\n", }, encoding="utf-8-sig") - fileReplaceLinesWith("src/UniGetUI/UniGetUI.csproj", { + fileReplaceLinesWith("src/Solution.props", { "\t": f"{versionISS}\n", "\t": f"{versionName}\n", + "\t": f"{versionName}\n", }, encoding="utf-8-sig") fileReplaceLinesWith("WingetUI.iss", { diff --git a/scripts/download_translations.py b/scripts/download_translations.py index 7e7d6e7e9..69dd7ae68 100644 --- a/scripts/download_translations.py +++ b/scripts/download_translations.py @@ -29,7 +29,7 @@ print("nocommit") print(sys.argv[1]) - os.chdir(os.path.normpath(os.path.join(root_dir, "src/UniGetUI/Assets/Languages"))) + os.chdir(os.path.normpath(os.path.join(root_dir, "src/UniGetUI.Core.LanguageEngine/Assets/Languages"))) print() print("-------------------------------------------------------") @@ -133,13 +133,13 @@ # languageCredits = {json.dumps(langCredits, indent=2, ensure_ascii=False)} # """ - with open(os.path.join(root_dir, "src/UniGetUI/Assets/Data/Translators.json"), "w", encoding="utf-8") as f: + with open(os.path.join(root_dir, "src/UniGetUI.Core.LanguageEngine/Assets/Data/Translators.json"), "w", encoding="utf-8") as f: f.write(json.dumps(langCredits, indent=2, ensure_ascii=False)) - with open(os.path.join(root_dir, "src/UniGetUI/Assets/Data/TranslatedPercentages.json"), "w", encoding="utf-8") as f: + with open(os.path.join(root_dir, "src/UniGetUI.Core.LanguageEngine/Assets/Data/TranslatedPercentages.json"), "w", encoding="utf-8") as f: f.write(json.dumps(langPerc, indent=2, ensure_ascii=False)) - # translations_filepath = os.path.normpath(os.path.join(root_dir, "UniGetUI/Core/Data/Translations.py")) + # translations_filepath = os.path.normpath(os.path.join(root_dir, "UniGetUI.Core.LanguageEngine/Core/Data/Translations.py")) # with open(translations_filepath, "w", encoding="utf-8") as f: # f.write(outputString.strip()) diff --git a/scripts/generate_json_from_excel.py b/scripts/generate_json_from_excel.py index 4f22952c2..a02e41ab6 100644 --- a/scripts/generate_json_from_excel.py +++ b/scripts/generate_json_from_excel.py @@ -22,7 +22,7 @@ try: workbook = xlrd.open_workbook('screenshot_database.xlsx') except: - os.system("python -m pip install xlrd==1.2.0") + os.system("python -m pip install xlrd==1.0.0") import xlrd workbook = xlrd.open_workbook('screenshot_database.xlsx') diff --git a/scripts/get_contributors.py b/scripts/get_contributors.py index 439be58f1..22540893d 100644 --- a/scripts/get_contributors.py +++ b/scripts/get_contributors.py @@ -35,7 +35,7 @@ # contributorsInfo = {json.dumps(contributorsInfo, indent=2, ensure_ascii=False)} # """ -contributors_filepath = os.path.normpath(os.path.join(root_dir, "src/UniGetUI/Assets/Data/Contributors.list")) +contributors_filepath = os.path.normpath(os.path.join(root_dir, "src/UniGetUI.Core.Data/Assets/Data/Contributors.list")) with open(contributors_filepath, "w", encoding="utf-8") as f: f.writelines(contributors) diff --git a/src/ExternalLibraries.Clipboard/ExternalLibraries.Clipboard.csproj b/src/ExternalLibraries.Clipboard/ExternalLibraries.Clipboard.csproj new file mode 100644 index 000000000..293f74341 --- /dev/null +++ b/src/ExternalLibraries.Clipboard/ExternalLibraries.Clipboard.csproj @@ -0,0 +1,10 @@ + + + + net8.0 + enable + enable + AnyCPU;x64;ARM64 + + + diff --git a/src/UniGetUI/ExternalLibraries/Clipboard.cs b/src/ExternalLibraries.Clipboard/WindowsClipboard.cs similarity index 91% rename from src/UniGetUI/ExternalLibraries/Clipboard.cs rename to src/ExternalLibraries.Clipboard/WindowsClipboard.cs index 94e43e574..69773aa0a 100644 --- a/src/UniGetUI/ExternalLibraries/Clipboard.cs +++ b/src/ExternalLibraries.Clipboard/WindowsClipboard.cs @@ -1,25 +1,10 @@ -using System; -using System.ComponentModel; +using System.ComponentModel; using System.Runtime.InteropServices; using System.Text; -using System.Threading; -namespace UniGetUI.ExternalLibraries.Clipboard +namespace ExternalLibraries.Clipboard { - public class Main - { - public static string? GetText() - { - return WindowsClipboard.GetText(); - } - - public static void SetText(string text) - { - WindowsClipboard.SetText(text); - } - } - - static class WindowsClipboard + public static class WindowsClipboard { const uint cfUnicodeText = 13; diff --git a/src/UniGetUI/ExternalLibraries/Pickers/Classes/FileOpenDialogRCW.cs b/src/ExternalLibraries.FilePickers/Classes/FileOpenDialogRCW.cs similarity index 75% rename from src/UniGetUI/ExternalLibraries/Pickers/Classes/FileOpenDialogRCW.cs rename to src/ExternalLibraries.FilePickers/Classes/FileOpenDialogRCW.cs index 40797179c..b54f8c31b 100644 --- a/src/UniGetUI/ExternalLibraries/Pickers/Classes/FileOpenDialogRCW.cs +++ b/src/ExternalLibraries.FilePickers/Classes/FileOpenDialogRCW.cs @@ -1,7 +1,7 @@ using System.Runtime.InteropServices; -using UniGetUI.ExternalLibraries.Pickers.Guids; +using ExternalLibraries.Pickers.Guids; -namespace UniGetUI.ExternalLibraries.Pickers.Classes; +namespace ExternalLibraries.Pickers.Classes; // --------------------------------------------------- // .NET classes representing runtime callable wrappers diff --git a/src/UniGetUI/ExternalLibraries/Pickers/Classes/FileSaveDialogRCW.cs b/src/ExternalLibraries.FilePickers/Classes/FileSaveDialogRCW.cs similarity index 75% rename from src/UniGetUI/ExternalLibraries/Pickers/Classes/FileSaveDialogRCW.cs rename to src/ExternalLibraries.FilePickers/Classes/FileSaveDialogRCW.cs index 3f5f07e8b..96a69092f 100644 --- a/src/UniGetUI/ExternalLibraries/Pickers/Classes/FileSaveDialogRCW.cs +++ b/src/ExternalLibraries.FilePickers/Classes/FileSaveDialogRCW.cs @@ -1,7 +1,7 @@ using System.Runtime.InteropServices; -using UniGetUI.ExternalLibraries.Pickers.Guids; +using ExternalLibraries.Pickers.Guids; -namespace UniGetUI.ExternalLibraries.Pickers.Classes; +namespace ExternalLibraries.Pickers.Classes; // --------------------------------------------------- // .NET classes representing runtime callable wrappers diff --git a/src/UniGetUI/ExternalLibraries/Pickers/Classes/Helper.cs b/src/ExternalLibraries.FilePickers/Classes/Helper.cs similarity index 70% rename from src/UniGetUI/ExternalLibraries/Pickers/Classes/Helper.cs rename to src/ExternalLibraries.FilePickers/Classes/Helper.cs index 0b64472c6..e71cbf16f 100644 --- a/src/UniGetUI/ExternalLibraries/Pickers/Classes/Helper.cs +++ b/src/ExternalLibraries.FilePickers/Classes/Helper.cs @@ -1,11 +1,11 @@ -using UniGetUI.ExternalLibraries.Pickers.Enums; -using UniGetUI.ExternalLibraries.Pickers.Interfaces; -using UniGetUI.ExternalLibraries.Pickers.Structures; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; +using ExternalLibraries.Pickers.Enums; +using ExternalLibraries.Pickers.Interfaces; +using ExternalLibraries.Pickers.Structures; -namespace UniGetUI.ExternalLibraries.Pickers.Classes; +namespace ExternalLibraries.Pickers.Classes; internal static class Helper { @@ -18,7 +18,7 @@ internal static class Helper /// Path to selected file, folder or empty string. internal static string ShowOpen(nint windowHandle, FOS fos, List? typeFilters = null) { - var dialog = new FileOpenDialog(); + FileOpenDialog dialog = new(); try { dialog.SetOptions(fos); @@ -26,7 +26,7 @@ internal static string ShowOpen(nint windowHandle, FOS fos, List? typeFi if (typeFilters != null) { typeFilters.Insert(0, string.Join("; ", typeFilters)); - var filterSpecs = typeFilters.Select(f => new COMDLG_FILTERSPEC(f)).ToArray(); + COMDLG_FILTERSPEC[] filterSpecs = typeFilters.Select(f => new COMDLG_FILTERSPEC(f)).ToArray(); dialog.SetFileTypes((uint)filterSpecs.Length, filterSpecs); } @@ -34,8 +34,8 @@ internal static string ShowOpen(nint windowHandle, FOS fos, List? typeFi if (dialog.Show(windowHandle) != 0) return string.Empty; - dialog.GetResult(out var item); - item.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out var path); + dialog.GetResult(out IShellItem item); + item.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out string path); return path; } finally @@ -48,14 +48,14 @@ internal static string ShowOpen(nint windowHandle, FOS fos, List? typeFi internal static string ShowSave(nint windowHandle, FOS fos, List? typeFilters = null, string name = "") { - var dialog = new FileSaveDialog(); + FileSaveDialog dialog = new(); try { dialog.SetOptions(fos); if (typeFilters != null) { - var filterSpecs = typeFilters.Select(f => new COMDLG_FILTERSPEC(f)).ToArray(); + COMDLG_FILTERSPEC[] filterSpecs = typeFilters.Select(f => new COMDLG_FILTERSPEC(f)).ToArray(); dialog.SetFileTypes((uint)filterSpecs.Length, filterSpecs); } @@ -66,8 +66,8 @@ internal static string ShowSave(nint windowHandle, FOS fos, List? typeFi if (dialog.Show(windowHandle) != 0) return string.Empty; - dialog.GetResult(out var item); - item.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out var path); + dialog.GetResult(out IShellItem item); + item.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out string path); return path; } finally diff --git a/src/UniGetUI/ExternalLibraries/Pickers/Enums/FDAP.cs b/src/ExternalLibraries.FilePickers/Enums/FDAP.cs similarity index 75% rename from src/UniGetUI/ExternalLibraries/Pickers/Enums/FDAP.cs rename to src/ExternalLibraries.FilePickers/Enums/FDAP.cs index c19ea4a40..c6347e932 100644 --- a/src/UniGetUI/ExternalLibraries/Pickers/Enums/FDAP.cs +++ b/src/ExternalLibraries.FilePickers/Enums/FDAP.cs @@ -1,4 +1,4 @@ -namespace UniGetUI.ExternalLibraries.Pickers.Enums; +namespace ExternalLibraries.Pickers.Enums; // https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/ne-shobjidl_core-fdap internal enum FDAP diff --git a/src/UniGetUI/ExternalLibraries/Pickers/Enums/FDE_OVERWRITE_RESPONSE.cs b/src/ExternalLibraries.FilePickers/Enums/FDE_OVERWRITE_RESPONSE.cs similarity index 81% rename from src/UniGetUI/ExternalLibraries/Pickers/Enums/FDE_OVERWRITE_RESPONSE.cs rename to src/ExternalLibraries.FilePickers/Enums/FDE_OVERWRITE_RESPONSE.cs index 80aeb4b3c..011f2bdaa 100644 --- a/src/UniGetUI/ExternalLibraries/Pickers/Enums/FDE_OVERWRITE_RESPONSE.cs +++ b/src/ExternalLibraries.FilePickers/Enums/FDE_OVERWRITE_RESPONSE.cs @@ -1,4 +1,4 @@ -namespace UniGetUI.ExternalLibraries.Pickers.Enums; +namespace ExternalLibraries.Pickers.Enums; // https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/ne-shobjidl_core-fde_overwrite_response internal enum FDE_OVERWRITE_RESPONSE diff --git a/src/UniGetUI/ExternalLibraries/Pickers/Enums/FDE_SHAREVIOLATION_RESPONSE.cs b/src/ExternalLibraries.FilePickers/Enums/FDE_SHAREVIOLATION_RESPONSE.cs similarity index 82% rename from src/UniGetUI/ExternalLibraries/Pickers/Enums/FDE_SHAREVIOLATION_RESPONSE.cs rename to src/ExternalLibraries.FilePickers/Enums/FDE_SHAREVIOLATION_RESPONSE.cs index 723eb0f42..71fb59290 100644 --- a/src/UniGetUI/ExternalLibraries/Pickers/Enums/FDE_SHAREVIOLATION_RESPONSE.cs +++ b/src/ExternalLibraries.FilePickers/Enums/FDE_SHAREVIOLATION_RESPONSE.cs @@ -1,4 +1,4 @@ -namespace UniGetUI.ExternalLibraries.Pickers.Enums; +namespace ExternalLibraries.Pickers.Enums; // https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/ne-shobjidl_core-fde_shareviolation_response internal enum FDE_SHAREVIOLATION_RESPONSE diff --git a/src/UniGetUI/ExternalLibraries/Pickers/Enums/FOS.cs b/src/ExternalLibraries.FilePickers/Enums/FOS.cs similarity index 95% rename from src/UniGetUI/ExternalLibraries/Pickers/Enums/FOS.cs rename to src/ExternalLibraries.FilePickers/Enums/FOS.cs index a3eaa6cb6..abfb4aaee 100644 --- a/src/UniGetUI/ExternalLibraries/Pickers/Enums/FOS.cs +++ b/src/ExternalLibraries.FilePickers/Enums/FOS.cs @@ -1,6 +1,6 @@ using System; -namespace UniGetUI.ExternalLibraries.Pickers.Enums; +namespace ExternalLibraries.Pickers.Enums; [Flags] // https://learn.microsoft.com/ru-ru/windows/win32/api/shobjidl_core/ne-shobjidl_core-_fileopendialogoptions diff --git a/src/UniGetUI/ExternalLibraries/Pickers/Enums/HRESULT.cs b/src/ExternalLibraries.FilePickers/Enums/HRESULT.cs similarity index 75% rename from src/UniGetUI/ExternalLibraries/Pickers/Enums/HRESULT.cs rename to src/ExternalLibraries.FilePickers/Enums/HRESULT.cs index c2fd533d6..2bbc83dbf 100644 --- a/src/UniGetUI/ExternalLibraries/Pickers/Enums/HRESULT.cs +++ b/src/ExternalLibraries.FilePickers/Enums/HRESULT.cs @@ -1,4 +1,4 @@ -namespace UniGetUI.ExternalLibraries.Pickers.Enums; +namespace ExternalLibraries.Pickers.Enums; internal enum HRESULT : long { diff --git a/src/UniGetUI/ExternalLibraries/Pickers/Enums/SIATTRIBFLAGS.cs b/src/ExternalLibraries.FilePickers/Enums/SIATTRIBFLAGS.cs similarity index 88% rename from src/UniGetUI/ExternalLibraries/Pickers/Enums/SIATTRIBFLAGS.cs rename to src/ExternalLibraries.FilePickers/Enums/SIATTRIBFLAGS.cs index bc65788af..7b5493018 100644 --- a/src/UniGetUI/ExternalLibraries/Pickers/Enums/SIATTRIBFLAGS.cs +++ b/src/ExternalLibraries.FilePickers/Enums/SIATTRIBFLAGS.cs @@ -1,4 +1,4 @@ -namespace UniGetUI.ExternalLibraries.Pickers.Enums; +namespace ExternalLibraries.Pickers.Enums; // https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ishellitemarray-getattributes internal enum SIATTRIBFLAGS diff --git a/src/UniGetUI/ExternalLibraries/Pickers/Enums/SIGDN.cs b/src/ExternalLibraries.FilePickers/Enums/SIGDN.cs similarity index 93% rename from src/UniGetUI/ExternalLibraries/Pickers/Enums/SIGDN.cs rename to src/ExternalLibraries.FilePickers/Enums/SIGDN.cs index 0d3590941..0b2456d0d 100644 --- a/src/UniGetUI/ExternalLibraries/Pickers/Enums/SIGDN.cs +++ b/src/ExternalLibraries.FilePickers/Enums/SIGDN.cs @@ -1,4 +1,4 @@ -namespace UniGetUI.ExternalLibraries.Pickers.Enums; +namespace ExternalLibraries.Pickers.Enums; // https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/ne-shobjidl_core-sigdn internal enum SIGDN : uint diff --git a/src/ExternalLibraries.FilePickers/ExternalLibraries.FilePickers.csproj b/src/ExternalLibraries.FilePickers/ExternalLibraries.FilePickers.csproj new file mode 100644 index 000000000..8d819499e --- /dev/null +++ b/src/ExternalLibraries.FilePickers/ExternalLibraries.FilePickers.csproj @@ -0,0 +1,10 @@ + + + + net8.0 + enable + enable + AnyCPU;ARM64;x64 + + + diff --git a/src/UniGetUI/ExternalLibraries/Pickers/FileOpenPicker.cs b/src/ExternalLibraries.FilePickers/FileOpenPicker.cs similarity index 84% rename from src/UniGetUI/ExternalLibraries/Pickers/FileOpenPicker.cs rename to src/ExternalLibraries.FilePickers/FileOpenPicker.cs index 624820504..766104a39 100644 --- a/src/UniGetUI/ExternalLibraries/Pickers/FileOpenPicker.cs +++ b/src/ExternalLibraries.FilePickers/FileOpenPicker.cs @@ -1,9 +1,9 @@ -using UniGetUI.ExternalLibraries.Pickers.Classes; -using UniGetUI.ExternalLibraries.Pickers.Enums; -using System; +using System; using System.Collections.Generic; +using ExternalLibraries.Pickers.Classes; +using ExternalLibraries.Pickers.Enums; -namespace UniGetUI.ExternalLibraries.Pickers; +namespace ExternalLibraries.Pickers; /// /// Class responsible for file pick dialog. diff --git a/src/UniGetUI/ExternalLibraries/Pickers/FileSavePicker.cs b/src/ExternalLibraries.FilePickers/FileSavePicker.cs similarity index 86% rename from src/UniGetUI/ExternalLibraries/Pickers/FileSavePicker.cs rename to src/ExternalLibraries.FilePickers/FileSavePicker.cs index 4a36bc0be..77cde3134 100644 --- a/src/UniGetUI/ExternalLibraries/Pickers/FileSavePicker.cs +++ b/src/ExternalLibraries.FilePickers/FileSavePicker.cs @@ -1,9 +1,9 @@ -using UniGetUI.ExternalLibraries.Pickers.Classes; -using UniGetUI.ExternalLibraries.Pickers.Enums; -using System; +using System; using System.Collections.Generic; +using ExternalLibraries.Pickers.Classes; +using ExternalLibraries.Pickers.Enums; -namespace UniGetUI.ExternalLibraries.Pickers; +namespace ExternalLibraries.Pickers; /// /// Class responsible for file pick dialog. diff --git a/src/UniGetUI/ExternalLibraries/Pickers/FolderPicker.cs b/src/ExternalLibraries.FilePickers/FolderPicker.cs similarity index 82% rename from src/UniGetUI/ExternalLibraries/Pickers/FolderPicker.cs rename to src/ExternalLibraries.FilePickers/FolderPicker.cs index b2a3a5ae8..bfd302ab9 100644 --- a/src/UniGetUI/ExternalLibraries/Pickers/FolderPicker.cs +++ b/src/ExternalLibraries.FilePickers/FolderPicker.cs @@ -1,8 +1,8 @@ -using UniGetUI.ExternalLibraries.Pickers.Classes; -using UniGetUI.ExternalLibraries.Pickers.Enums; -using System; +using System; +using ExternalLibraries.Pickers.Classes; +using ExternalLibraries.Pickers.Enums; -namespace UniGetUI.ExternalLibraries.Pickers; +namespace ExternalLibraries.Pickers; /// /// Class responsible for folder pick dialog. diff --git a/src/UniGetUI/ExternalLibraries/Pickers/Guids/CLSIDGuid.cs b/src/ExternalLibraries.FilePickers/Guids/CLSIDGuid.cs similarity index 86% rename from src/UniGetUI/ExternalLibraries/Pickers/Guids/CLSIDGuid.cs rename to src/ExternalLibraries.FilePickers/Guids/CLSIDGuid.cs index cce14d002..228f4ed35 100644 --- a/src/UniGetUI/ExternalLibraries/Pickers/Guids/CLSIDGuid.cs +++ b/src/ExternalLibraries.FilePickers/Guids/CLSIDGuid.cs @@ -1,4 +1,4 @@ -namespace UniGetUI.ExternalLibraries.Pickers.Guids; +namespace ExternalLibraries.Pickers.Guids; internal static class CLSIDGuid { diff --git a/src/UniGetUI/ExternalLibraries/Pickers/Guids/IIDGuid.cs b/src/ExternalLibraries.FilePickers/Guids/IIDGuid.cs similarity index 95% rename from src/UniGetUI/ExternalLibraries/Pickers/Guids/IIDGuid.cs rename to src/ExternalLibraries.FilePickers/Guids/IIDGuid.cs index 35d78f596..a0f187807 100644 --- a/src/UniGetUI/ExternalLibraries/Pickers/Guids/IIDGuid.cs +++ b/src/ExternalLibraries.FilePickers/Guids/IIDGuid.cs @@ -1,4 +1,4 @@ -namespace UniGetUI.ExternalLibraries.Pickers.Guids; +namespace ExternalLibraries.Pickers.Guids; internal static class IIDGuid { diff --git a/src/UniGetUI/ExternalLibraries/Pickers/Guids/KFIDGuid.cs b/src/ExternalLibraries.FilePickers/Guids/KFIDGuid.cs similarity index 86% rename from src/UniGetUI/ExternalLibraries/Pickers/Guids/KFIDGuid.cs rename to src/ExternalLibraries.FilePickers/Guids/KFIDGuid.cs index 991e7dcae..b51f3740d 100644 --- a/src/UniGetUI/ExternalLibraries/Pickers/Guids/KFIDGuid.cs +++ b/src/ExternalLibraries.FilePickers/Guids/KFIDGuid.cs @@ -1,4 +1,4 @@ -namespace UniGetUI.ExternalLibraries.Pickers.Guids; +namespace ExternalLibraries.Pickers.Guids; internal static class KFIDGuid { diff --git a/src/UniGetUI/ExternalLibraries/Pickers/Interfaces/FileOpenDialog.cs b/src/ExternalLibraries.FilePickers/Interfaces/FileOpenDialog.cs similarity index 75% rename from src/UniGetUI/ExternalLibraries/Pickers/Interfaces/FileOpenDialog.cs rename to src/ExternalLibraries.FilePickers/Interfaces/FileOpenDialog.cs index bd6ef1fcc..9cddb1024 100644 --- a/src/UniGetUI/ExternalLibraries/Pickers/Interfaces/FileOpenDialog.cs +++ b/src/ExternalLibraries.FilePickers/Interfaces/FileOpenDialog.cs @@ -1,8 +1,8 @@ using System.Runtime.InteropServices; -using UniGetUI.ExternalLibraries.Pickers.Classes; -using UniGetUI.ExternalLibraries.Pickers.Guids; +using ExternalLibraries.Pickers.Classes; +using ExternalLibraries.Pickers.Guids; -namespace UniGetUI.ExternalLibraries.Pickers.Interfaces; +namespace ExternalLibraries.Pickers.Interfaces; // --------------------------------------------------------- // Coclass interfaces - designed to "look like" the object diff --git a/src/UniGetUI/ExternalLibraries/Pickers/Interfaces/FileSaveDialog.cs b/src/ExternalLibraries.FilePickers/Interfaces/FileSaveDialog.cs similarity index 75% rename from src/UniGetUI/ExternalLibraries/Pickers/Interfaces/FileSaveDialog.cs rename to src/ExternalLibraries.FilePickers/Interfaces/FileSaveDialog.cs index ed9ffcd5d..2f76d7e80 100644 --- a/src/UniGetUI/ExternalLibraries/Pickers/Interfaces/FileSaveDialog.cs +++ b/src/ExternalLibraries.FilePickers/Interfaces/FileSaveDialog.cs @@ -1,8 +1,8 @@ using System.Runtime.InteropServices; -using UniGetUI.ExternalLibraries.Pickers.Classes; -using UniGetUI.ExternalLibraries.Pickers.Guids; +using ExternalLibraries.Pickers.Classes; +using ExternalLibraries.Pickers.Guids; -namespace UniGetUI.ExternalLibraries.Pickers.Interfaces; +namespace ExternalLibraries.Pickers.Interfaces; // --------------------------------------------------------- // Coclass interfaces - designed to "look like" the object diff --git a/src/UniGetUI/ExternalLibraries/Pickers/Interfaces/IFileDialog.cs b/src/ExternalLibraries.FilePickers/Interfaces/IFileDialog.cs similarity index 95% rename from src/UniGetUI/ExternalLibraries/Pickers/Interfaces/IFileDialog.cs rename to src/ExternalLibraries.FilePickers/Interfaces/IFileDialog.cs index da70b4d81..0f353f9a5 100644 --- a/src/UniGetUI/ExternalLibraries/Pickers/Interfaces/IFileDialog.cs +++ b/src/ExternalLibraries.FilePickers/Interfaces/IFileDialog.cs @@ -1,11 +1,11 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using UniGetUI.ExternalLibraries.Pickers.Enums; -using UniGetUI.ExternalLibraries.Pickers.Guids; -using UniGetUI.ExternalLibraries.Pickers.Structures; +using ExternalLibraries.Pickers.Enums; +using ExternalLibraries.Pickers.Guids; +using ExternalLibraries.Pickers.Structures; -namespace UniGetUI.ExternalLibraries.Pickers.Interfaces; +namespace ExternalLibraries.Pickers.Interfaces; [ComImport(), Guid(IIDGuid.IFileDialog), diff --git a/src/UniGetUI/ExternalLibraries/Pickers/Interfaces/IFileDialogEvents.cs b/src/ExternalLibraries.FilePickers/Interfaces/IFileDialogEvents.cs similarity index 92% rename from src/UniGetUI/ExternalLibraries/Pickers/Interfaces/IFileDialogEvents.cs rename to src/ExternalLibraries.FilePickers/Interfaces/IFileDialogEvents.cs index 18ab25d5e..732a52d25 100644 --- a/src/UniGetUI/ExternalLibraries/Pickers/Interfaces/IFileDialogEvents.cs +++ b/src/ExternalLibraries.FilePickers/Interfaces/IFileDialogEvents.cs @@ -1,9 +1,9 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using UniGetUI.ExternalLibraries.Pickers.Enums; -using UniGetUI.ExternalLibraries.Pickers.Guids; +using ExternalLibraries.Pickers.Enums; +using ExternalLibraries.Pickers.Guids; -namespace UniGetUI.ExternalLibraries.Pickers.Interfaces; +namespace ExternalLibraries.Pickers.Interfaces; [ComImport, Guid(IIDGuid.IFileDialogEvents), diff --git a/src/UniGetUI/ExternalLibraries/Pickers/Interfaces/IFileOpenDialog.cs b/src/ExternalLibraries.FilePickers/Interfaces/IFileOpenDialog.cs similarity index 91% rename from src/UniGetUI/ExternalLibraries/Pickers/Interfaces/IFileOpenDialog.cs rename to src/ExternalLibraries.FilePickers/Interfaces/IFileOpenDialog.cs index 83941f903..61f666d28 100644 --- a/src/UniGetUI/ExternalLibraries/Pickers/Interfaces/IFileOpenDialog.cs +++ b/src/ExternalLibraries.FilePickers/Interfaces/IFileOpenDialog.cs @@ -1,9 +1,9 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using UniGetUI.ExternalLibraries.Pickers.Guids; -using UniGetUI.ExternalLibraries.Pickers.Structures; +using ExternalLibraries.Pickers.Guids; +using ExternalLibraries.Pickers.Structures; -namespace UniGetUI.ExternalLibraries.Pickers.Interfaces; +namespace ExternalLibraries.Pickers.Interfaces; [ComImport(), Guid(IIDGuid.IFileOpenDialog), diff --git a/src/UniGetUI/ExternalLibraries/Pickers/Interfaces/IModalWindow.cs b/src/ExternalLibraries.FilePickers/Interfaces/IModalWindow.cs similarity index 77% rename from src/UniGetUI/ExternalLibraries/Pickers/Interfaces/IModalWindow.cs rename to src/ExternalLibraries.FilePickers/Interfaces/IModalWindow.cs index 319f062fc..5745b0e79 100644 --- a/src/UniGetUI/ExternalLibraries/Pickers/Interfaces/IModalWindow.cs +++ b/src/ExternalLibraries.FilePickers/Interfaces/IModalWindow.cs @@ -1,9 +1,9 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using UniGetUI.ExternalLibraries.Pickers.Guids; +using ExternalLibraries.Pickers.Guids; -namespace UniGetUI.ExternalLibraries.Pickers.Interfaces; +namespace ExternalLibraries.Pickers.Interfaces; [ComImport(), Guid(IIDGuid.IModalWindow), diff --git a/src/UniGetUI/ExternalLibraries/Pickers/Interfaces/IShellItem.cs b/src/ExternalLibraries.FilePickers/Interfaces/IShellItem.cs similarity index 88% rename from src/UniGetUI/ExternalLibraries/Pickers/Interfaces/IShellItem.cs rename to src/ExternalLibraries.FilePickers/Interfaces/IShellItem.cs index 1431c0301..64ccf5604 100644 --- a/src/UniGetUI/ExternalLibraries/Pickers/Interfaces/IShellItem.cs +++ b/src/ExternalLibraries.FilePickers/Interfaces/IShellItem.cs @@ -1,10 +1,10 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using UniGetUI.ExternalLibraries.Pickers.Enums; -using UniGetUI.ExternalLibraries.Pickers.Guids; +using ExternalLibraries.Pickers.Enums; +using ExternalLibraries.Pickers.Guids; -namespace UniGetUI.ExternalLibraries.Pickers.Interfaces; +namespace ExternalLibraries.Pickers.Interfaces; [ComImport, Guid(IIDGuid.IShellItem), diff --git a/src/UniGetUI/ExternalLibraries/Pickers/Interfaces/IShellItemArray.cs b/src/ExternalLibraries.FilePickers/Interfaces/IShellItemArray.cs similarity index 88% rename from src/UniGetUI/ExternalLibraries/Pickers/Interfaces/IShellItemArray.cs rename to src/ExternalLibraries.FilePickers/Interfaces/IShellItemArray.cs index e0752e782..038492d43 100644 --- a/src/UniGetUI/ExternalLibraries/Pickers/Interfaces/IShellItemArray.cs +++ b/src/ExternalLibraries.FilePickers/Interfaces/IShellItemArray.cs @@ -1,11 +1,11 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using UniGetUI.ExternalLibraries.Pickers.Enums; -using UniGetUI.ExternalLibraries.Pickers.Guids; -using UniGetUI.ExternalLibraries.Pickers.Structures; +using ExternalLibraries.Pickers.Enums; +using ExternalLibraries.Pickers.Guids; +using ExternalLibraries.Pickers.Structures; -namespace UniGetUI.ExternalLibraries.Pickers.Interfaces; +namespace ExternalLibraries.Pickers.Interfaces; [ComImport, Guid(IIDGuid.IShellItemArray), diff --git a/src/UniGetUI/ExternalLibraries/Pickers/Structures/COMDLG_FILTERSPEC.cs b/src/ExternalLibraries.FilePickers/Structures/COMDLG_FILTERSPEC.cs similarity index 89% rename from src/UniGetUI/ExternalLibraries/Pickers/Structures/COMDLG_FILTERSPEC.cs rename to src/ExternalLibraries.FilePickers/Structures/COMDLG_FILTERSPEC.cs index 464f62dc9..2fa3b3110 100644 --- a/src/UniGetUI/ExternalLibraries/Pickers/Structures/COMDLG_FILTERSPEC.cs +++ b/src/ExternalLibraries.FilePickers/Structures/COMDLG_FILTERSPEC.cs @@ -1,6 +1,6 @@ using System.Runtime.InteropServices; -namespace UniGetUI.ExternalLibraries.Pickers.Structures; +namespace ExternalLibraries.Pickers.Structures; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 4)] internal struct COMDLG_FILTERSPEC diff --git a/src/UniGetUI/ExternalLibraries/Pickers/Structures/PROPERTYKEY.cs b/src/ExternalLibraries.FilePickers/Structures/PROPERTYKEY.cs similarity index 76% rename from src/UniGetUI/ExternalLibraries/Pickers/Structures/PROPERTYKEY.cs rename to src/ExternalLibraries.FilePickers/Structures/PROPERTYKEY.cs index 4ca3d18e0..a062a7ef2 100644 --- a/src/UniGetUI/ExternalLibraries/Pickers/Structures/PROPERTYKEY.cs +++ b/src/ExternalLibraries.FilePickers/Structures/PROPERTYKEY.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.InteropServices; -namespace UniGetUI.ExternalLibraries.Pickers.Structures; +namespace ExternalLibraries.Pickers.Structures; [StructLayout(LayoutKind.Sequential, Pack = 4)] internal struct PROPERTYKEY diff --git a/src/Solution.props b/src/Solution.props new file mode 100644 index 000000000..c0fafdd9d --- /dev/null +++ b/src/Solution.props @@ -0,0 +1,23 @@ + + + net8.0-windows10.0.19041.0 + enable + win-x64;win-arm64 + win-$(Platform) + ARM64;x64 + 10.0.19041.0 + 10.0.19041.0 + win-x64;win-arm64 + 8.0.204 + true + true + 3.1.0.0 + 3.1.0-alpha + UniGetUI + Martí Climent and the contributors + Martí Climent + 3.1.0-alpha + 2024, Martí Climent + enable + + diff --git a/src/Tools/obj/UniGetUI.Core.Tools.csproj.nuget.dgspec.json b/src/Tools/obj/UniGetUI.Core.Tools.csproj.nuget.dgspec.json new file mode 100644 index 000000000..440e12339 --- /dev/null +++ b/src/Tools/obj/UniGetUI.Core.Tools.csproj.nuget.dgspec.json @@ -0,0 +1,435 @@ +{ + "format": 1, + "restore": { + "C:\\SomePrograms\\WingetUI-Store\\src\\Tools\\UniGetUI.Core.Tools.csproj": {} + }, + "projects": { + "C:\\SomePrograms\\WingetUI-Store\\src\\Tools\\UniGetUI.Core.Tools.csproj": { + "version": "1.0.0", + "restore": { + "projectUniqueName": "C:\\SomePrograms\\WingetUI-Store\\src\\Tools\\UniGetUI.Core.Tools.csproj", + "projectName": "UniGetUI.Core.Tools", + "projectPath": "C:\\SomePrograms\\WingetUI-Store\\src\\Tools\\UniGetUI.Core.Tools.csproj", + "packagesPath": "C:\\Users\\marti\\.nuget\\packages\\", + "outputPath": "C:\\SomePrograms\\WingetUI-Store\\src\\Tools\\obj\\", + "projectStyle": "PackageReference", + "fallbackFolders": [ + "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages" + ], + "configFilePaths": [ + "C:\\Users\\marti\\AppData\\Roaming\\NuGet\\NuGet.Config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" + ], + "originalTargetFrameworks": [ + "net8.0" + ], + "sources": { + "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, + "c:\\program files\\dotnet\\library-packs": {}, + "https://api.nuget.org/v3/index.json": {} + }, + "frameworks": { + "net8.0": { + "targetAlias": "net8.0", + "projectReferences": { + "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Data\\UniGetUI.Core.Data.csproj": { + "projectPath": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Data\\UniGetUI.Core.Data.csproj" + }, + "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.LanguageEngine\\UniGetUI.Core.Language.csproj": { + "projectPath": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.LanguageEngine\\UniGetUI.Core.Language.csproj" + }, + "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Logger\\UniGetUI.Core.Logging.csproj": { + "projectPath": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Logger\\UniGetUI.Core.Logging.csproj" + }, + "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Settings\\UniGetUI.Core.Settings.csproj": { + "projectPath": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Settings\\UniGetUI.Core.Settings.csproj" + } + } + } + }, + "warningProperties": { + "warnAsError": [ + "NU1605" + ] + }, + "restoreAuditProperties": { + "enableAudit": "true", + "auditLevel": "low", + "auditMode": "direct" + } + }, + "frameworks": { + "net8.0": { + "targetAlias": "net8.0", + "imports": [ + "net461", + "net462", + "net47", + "net471", + "net472", + "net48", + "net481" + ], + "assetTargetFallback": true, + "warn": true, + "frameworkReferences": { + "Microsoft.NETCore.App": { + "privateAssets": "all" + } + }, + "runtimeIdentifierGraphPath": "c:\\program files\\dotnet\\sdk\\8.0.300-preview.24203.14/PortableRuntimeIdentifierGraph.json" + } + } + }, + "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Data\\UniGetUI.Core.Data.csproj": { + "version": "1.0.0", + "restore": { + "projectUniqueName": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Data\\UniGetUI.Core.Data.csproj", + "projectName": "UniGetUI.Core.Data", + "projectPath": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Data\\UniGetUI.Core.Data.csproj", + "packagesPath": "C:\\Users\\marti\\.nuget\\packages\\", + "outputPath": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Data\\obj\\", + "projectStyle": "PackageReference", + "fallbackFolders": [ + "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages" + ], + "configFilePaths": [ + "C:\\Users\\marti\\AppData\\Roaming\\NuGet\\NuGet.Config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" + ], + "originalTargetFrameworks": [ + "net8.0" + ], + "sources": { + "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, + "c:\\program files\\dotnet\\library-packs": {}, + "https://api.nuget.org/v3/index.json": {} + }, + "frameworks": { + "net8.0": { + "targetAlias": "net8.0", + "projectReferences": { + "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Logger\\UniGetUI.Core.Logging.csproj": { + "projectPath": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Logger\\UniGetUI.Core.Logging.csproj" + } + } + } + }, + "warningProperties": { + "warnAsError": [ + "NU1605" + ] + }, + "restoreAuditProperties": { + "enableAudit": "true", + "auditLevel": "low", + "auditMode": "direct" + } + }, + "frameworks": { + "net8.0": { + "targetAlias": "net8.0", + "imports": [ + "net461", + "net462", + "net47", + "net471", + "net472", + "net48", + "net481" + ], + "assetTargetFallback": true, + "warn": true, + "frameworkReferences": { + "Microsoft.NETCore.App": { + "privateAssets": "all" + } + }, + "runtimeIdentifierGraphPath": "c:\\program files\\dotnet\\sdk\\8.0.300-preview.24203.14/PortableRuntimeIdentifierGraph.json" + } + } + }, + "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Enums\\UniGetUI.PackageEngine.Enums.csproj": { + "version": "1.0.0", + "restore": { + "projectUniqueName": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Enums\\UniGetUI.PackageEngine.Enums.csproj", + "projectName": "UniGetUI.PackageEngine.Enums", + "projectPath": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Enums\\UniGetUI.PackageEngine.Enums.csproj", + "packagesPath": "C:\\Users\\marti\\.nuget\\packages\\", + "outputPath": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Enums\\obj\\", + "projectStyle": "PackageReference", + "fallbackFolders": [ + "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages" + ], + "configFilePaths": [ + "C:\\Users\\marti\\AppData\\Roaming\\NuGet\\NuGet.Config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" + ], + "originalTargetFrameworks": [ + "net8.0" + ], + "sources": { + "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, + "c:\\program files\\dotnet\\library-packs": {}, + "https://api.nuget.org/v3/index.json": {} + }, + "frameworks": { + "net8.0": { + "targetAlias": "net8.0", + "projectReferences": {} + } + }, + "warningProperties": { + "warnAsError": [ + "NU1605" + ] + }, + "restoreAuditProperties": { + "enableAudit": "true", + "auditLevel": "low", + "auditMode": "direct" + } + }, + "frameworks": { + "net8.0": { + "targetAlias": "net8.0", + "imports": [ + "net461", + "net462", + "net47", + "net471", + "net472", + "net48", + "net481" + ], + "assetTargetFallback": true, + "warn": true, + "frameworkReferences": { + "Microsoft.NETCore.App": { + "privateAssets": "all" + } + }, + "runtimeIdentifierGraphPath": "c:\\program files\\dotnet\\sdk\\8.0.300-preview.24203.14/PortableRuntimeIdentifierGraph.json" + } + } + }, + "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.LanguageEngine\\UniGetUI.Core.Language.csproj": { + "version": "1.0.0", + "restore": { + "projectUniqueName": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.LanguageEngine\\UniGetUI.Core.Language.csproj", + "projectName": "UniGetUI.Core.Language", + "projectPath": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.LanguageEngine\\UniGetUI.Core.Language.csproj", + "packagesPath": "C:\\Users\\marti\\.nuget\\packages\\", + "outputPath": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.LanguageEngine\\obj\\", + "projectStyle": "PackageReference", + "fallbackFolders": [ + "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages" + ], + "configFilePaths": [ + "C:\\Users\\marti\\AppData\\Roaming\\NuGet\\NuGet.Config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" + ], + "originalTargetFrameworks": [ + "net8.0" + ], + "sources": { + "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, + "c:\\program files\\dotnet\\library-packs": {}, + "https://api.nuget.org/v3/index.json": {} + }, + "frameworks": { + "net8.0": { + "targetAlias": "net8.0", + "projectReferences": { + "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Data\\UniGetUI.Core.Data.csproj": { + "projectPath": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Data\\UniGetUI.Core.Data.csproj" + }, + "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Enums\\UniGetUI.PackageEngine.Enums.csproj": { + "projectPath": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Enums\\UniGetUI.PackageEngine.Enums.csproj" + }, + "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Logger\\UniGetUI.Core.Logging.csproj": { + "projectPath": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Logger\\UniGetUI.Core.Logging.csproj" + }, + "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Settings\\UniGetUI.Core.Settings.csproj": { + "projectPath": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Settings\\UniGetUI.Core.Settings.csproj" + } + } + } + }, + "warningProperties": { + "warnAsError": [ + "NU1605" + ] + }, + "restoreAuditProperties": { + "enableAudit": "true", + "auditLevel": "low", + "auditMode": "direct" + } + }, + "frameworks": { + "net8.0": { + "targetAlias": "net8.0", + "imports": [ + "net461", + "net462", + "net47", + "net471", + "net472", + "net48", + "net481" + ], + "assetTargetFallback": true, + "warn": true, + "frameworkReferences": { + "Microsoft.NETCore.App": { + "privateAssets": "all" + } + }, + "runtimeIdentifierGraphPath": "c:\\program files\\dotnet\\sdk\\8.0.300-preview.24203.14/PortableRuntimeIdentifierGraph.json" + } + } + }, + "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Logger\\UniGetUI.Core.Logging.csproj": { + "version": "1.0.0", + "restore": { + "projectUniqueName": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Logger\\UniGetUI.Core.Logging.csproj", + "projectName": "UniGetUI.Core.Logging", + "projectPath": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Logger\\UniGetUI.Core.Logging.csproj", + "packagesPath": "C:\\Users\\marti\\.nuget\\packages\\", + "outputPath": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Logger\\obj\\", + "projectStyle": "PackageReference", + "fallbackFolders": [ + "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages" + ], + "configFilePaths": [ + "C:\\Users\\marti\\AppData\\Roaming\\NuGet\\NuGet.Config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" + ], + "originalTargetFrameworks": [ + "net8.0" + ], + "sources": { + "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, + "c:\\program files\\dotnet\\library-packs": {}, + "https://api.nuget.org/v3/index.json": {} + }, + "frameworks": { + "net8.0": { + "targetAlias": "net8.0", + "projectReferences": {} + } + }, + "warningProperties": { + "warnAsError": [ + "NU1605" + ] + }, + "restoreAuditProperties": { + "enableAudit": "true", + "auditLevel": "low", + "auditMode": "direct" + } + }, + "frameworks": { + "net8.0": { + "targetAlias": "net8.0", + "imports": [ + "net461", + "net462", + "net47", + "net471", + "net472", + "net48", + "net481" + ], + "assetTargetFallback": true, + "warn": true, + "frameworkReferences": { + "Microsoft.NETCore.App": { + "privateAssets": "all" + } + }, + "runtimeIdentifierGraphPath": "c:\\program files\\dotnet\\sdk\\8.0.300-preview.24203.14/PortableRuntimeIdentifierGraph.json" + } + } + }, + "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Settings\\UniGetUI.Core.Settings.csproj": { + "version": "1.0.0", + "restore": { + "projectUniqueName": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Settings\\UniGetUI.Core.Settings.csproj", + "projectName": "UniGetUI.Core.Settings", + "projectPath": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Settings\\UniGetUI.Core.Settings.csproj", + "packagesPath": "C:\\Users\\marti\\.nuget\\packages\\", + "outputPath": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Settings\\obj\\", + "projectStyle": "PackageReference", + "fallbackFolders": [ + "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages" + ], + "configFilePaths": [ + "C:\\Users\\marti\\AppData\\Roaming\\NuGet\\NuGet.Config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" + ], + "originalTargetFrameworks": [ + "net8.0" + ], + "sources": { + "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, + "c:\\program files\\dotnet\\library-packs": {}, + "https://api.nuget.org/v3/index.json": {} + }, + "frameworks": { + "net8.0": { + "targetAlias": "net8.0", + "projectReferences": { + "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Data\\UniGetUI.Core.Data.csproj": { + "projectPath": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Data\\UniGetUI.Core.Data.csproj" + }, + "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Logger\\UniGetUI.Core.Logging.csproj": { + "projectPath": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Logger\\UniGetUI.Core.Logging.csproj" + } + } + } + }, + "warningProperties": { + "warnAsError": [ + "NU1605" + ] + }, + "restoreAuditProperties": { + "enableAudit": "true", + "auditLevel": "low", + "auditMode": "direct" + } + }, + "frameworks": { + "net8.0": { + "targetAlias": "net8.0", + "imports": [ + "net461", + "net462", + "net47", + "net471", + "net472", + "net48", + "net481" + ], + "assetTargetFallback": true, + "warn": true, + "frameworkReferences": { + "Microsoft.NETCore.App": { + "privateAssets": "all" + } + }, + "runtimeIdentifierGraphPath": "c:\\program files\\dotnet\\sdk\\8.0.300-preview.24203.14/PortableRuntimeIdentifierGraph.json" + } + } + } + } +} \ No newline at end of file diff --git a/src/Tools/obj/UniGetUI.Core.Tools.csproj.nuget.g.props b/src/Tools/obj/UniGetUI.Core.Tools.csproj.nuget.g.props new file mode 100644 index 000000000..e169dd7ba --- /dev/null +++ b/src/Tools/obj/UniGetUI.Core.Tools.csproj.nuget.g.props @@ -0,0 +1,16 @@ + + + + True + NuGet + $(MSBuildThisFileDirectory)project.assets.json + $(UserProfile)\.nuget\packages\ + C:\Users\marti\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages + PackageReference + 6.10.0 + + + + + + \ No newline at end of file diff --git a/src/Tools/obj/UniGetUI.Core.Tools.csproj.nuget.g.targets b/src/Tools/obj/UniGetUI.Core.Tools.csproj.nuget.g.targets new file mode 100644 index 000000000..3dc06ef3c --- /dev/null +++ b/src/Tools/obj/UniGetUI.Core.Tools.csproj.nuget.g.targets @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/Tools/obj/project.assets.json b/src/Tools/obj/project.assets.json new file mode 100644 index 000000000..95f8bed7c --- /dev/null +++ b/src/Tools/obj/project.assets.json @@ -0,0 +1,187 @@ +{ + "version": 3, + "targets": { + "net8.0": { + "UniGetUI.Core.Data/1.0.0": { + "type": "project", + "framework": ".NETCoreApp,Version=v8.0", + "dependencies": { + "UniGetUI.Core.Logging": "1.0.0" + }, + "compile": { + "bin/placeholder/UniGetUI.Core.Data.dll": {} + }, + "runtime": { + "bin/placeholder/UniGetUI.Core.Data.dll": {} + } + }, + "UniGetUI.Core.Language/1.0.0": { + "type": "project", + "framework": ".NETCoreApp,Version=v8.0", + "dependencies": { + "UniGetUI.Core.Data": "1.0.0", + "UniGetUI.Core.Logging": "1.0.0", + "UniGetUI.Core.Settings": "1.0.0", + "UniGetUI.PackageEngine.Enums": "1.0.0" + }, + "compile": { + "bin/placeholder/UniGetUI.Core.Language.dll": {} + }, + "runtime": { + "bin/placeholder/UniGetUI.Core.Language.dll": {} + } + }, + "UniGetUI.Core.Logging/1.0.0": { + "type": "project", + "framework": ".NETCoreApp,Version=v8.0", + "compile": { + "bin/placeholder/UniGetUI.Core.Logging.dll": {} + }, + "runtime": { + "bin/placeholder/UniGetUI.Core.Logging.dll": {} + } + }, + "UniGetUI.Core.Settings/1.0.0": { + "type": "project", + "framework": ".NETCoreApp,Version=v8.0", + "dependencies": { + "UniGetUI.Core.Data": "1.0.0", + "UniGetUI.Core.Logging": "1.0.0" + }, + "compile": { + "bin/placeholder/UniGetUI.Core.Settings.dll": {} + }, + "runtime": { + "bin/placeholder/UniGetUI.Core.Settings.dll": {} + } + }, + "UniGetUI.PackageEngine.Enums/1.0.0": { + "type": "project", + "framework": ".NETCoreApp,Version=v8.0", + "compile": { + "bin/placeholder/UniGetUI.PackageEngine.Enums.dll": {} + }, + "runtime": { + "bin/placeholder/UniGetUI.PackageEngine.Enums.dll": {} + } + } + } + }, + "libraries": { + "UniGetUI.Core.Data/1.0.0": { + "type": "project", + "path": "../UniGetUI.Core.Data/UniGetUI.Core.Data.csproj", + "msbuildProject": "../UniGetUI.Core.Data/UniGetUI.Core.Data.csproj" + }, + "UniGetUI.Core.Language/1.0.0": { + "type": "project", + "path": "../UniGetUI.Core.LanguageEngine/UniGetUI.Core.Language.csproj", + "msbuildProject": "../UniGetUI.Core.LanguageEngine/UniGetUI.Core.Language.csproj" + }, + "UniGetUI.Core.Logging/1.0.0": { + "type": "project", + "path": "../UniGetUI.Core.Logger/UniGetUI.Core.Logging.csproj", + "msbuildProject": "../UniGetUI.Core.Logger/UniGetUI.Core.Logging.csproj" + }, + "UniGetUI.Core.Settings/1.0.0": { + "type": "project", + "path": "../UniGetUI.Core.Settings/UniGetUI.Core.Settings.csproj", + "msbuildProject": "../UniGetUI.Core.Settings/UniGetUI.Core.Settings.csproj" + }, + "UniGetUI.PackageEngine.Enums/1.0.0": { + "type": "project", + "path": "../UniGetUI.Core.Enums/UniGetUI.PackageEngine.Enums.csproj", + "msbuildProject": "../UniGetUI.Core.Enums/UniGetUI.PackageEngine.Enums.csproj" + } + }, + "projectFileDependencyGroups": { + "net8.0": [ + "UniGetUI.Core.Data >= 1.0.0", + "UniGetUI.Core.Language >= 1.0.0", + "UniGetUI.Core.Logging >= 1.0.0", + "UniGetUI.Core.Settings >= 1.0.0" + ] + }, + "packageFolders": { + "C:\\Users\\marti\\.nuget\\packages\\": {}, + "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages": {} + }, + "project": { + "version": "1.0.0", + "restore": { + "projectUniqueName": "C:\\SomePrograms\\WingetUI-Store\\src\\Tools\\UniGetUI.Core.Tools.csproj", + "projectName": "UniGetUI.Core.Tools", + "projectPath": "C:\\SomePrograms\\WingetUI-Store\\src\\Tools\\UniGetUI.Core.Tools.csproj", + "packagesPath": "C:\\Users\\marti\\.nuget\\packages\\", + "outputPath": "C:\\SomePrograms\\WingetUI-Store\\src\\Tools\\obj\\", + "projectStyle": "PackageReference", + "fallbackFolders": [ + "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages" + ], + "configFilePaths": [ + "C:\\Users\\marti\\AppData\\Roaming\\NuGet\\NuGet.Config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" + ], + "originalTargetFrameworks": [ + "net8.0" + ], + "sources": { + "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, + "c:\\program files\\dotnet\\library-packs": {}, + "https://api.nuget.org/v3/index.json": {} + }, + "frameworks": { + "net8.0": { + "targetAlias": "net8.0", + "projectReferences": { + "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Data\\UniGetUI.Core.Data.csproj": { + "projectPath": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Data\\UniGetUI.Core.Data.csproj" + }, + "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.LanguageEngine\\UniGetUI.Core.Language.csproj": { + "projectPath": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.LanguageEngine\\UniGetUI.Core.Language.csproj" + }, + "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Logger\\UniGetUI.Core.Logging.csproj": { + "projectPath": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Logger\\UniGetUI.Core.Logging.csproj" + }, + "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Settings\\UniGetUI.Core.Settings.csproj": { + "projectPath": "C:\\SomePrograms\\WingetUI-Store\\src\\UniGetUI.Core.Settings\\UniGetUI.Core.Settings.csproj" + } + } + } + }, + "warningProperties": { + "warnAsError": [ + "NU1605" + ] + }, + "restoreAuditProperties": { + "enableAudit": "true", + "auditLevel": "low", + "auditMode": "direct" + } + }, + "frameworks": { + "net8.0": { + "targetAlias": "net8.0", + "imports": [ + "net461", + "net462", + "net47", + "net471", + "net472", + "net48", + "net481" + ], + "assetTargetFallback": true, + "warn": true, + "frameworkReferences": { + "Microsoft.NETCore.App": { + "privateAssets": "all" + } + }, + "runtimeIdentifierGraphPath": "c:\\program files\\dotnet\\sdk\\8.0.300-preview.24203.14/PortableRuntimeIdentifierGraph.json" + } + } + } +} \ No newline at end of file diff --git a/src/Tools/obj/project.nuget.cache b/src/Tools/obj/project.nuget.cache new file mode 100644 index 000000000..b0c82d585 --- /dev/null +++ b/src/Tools/obj/project.nuget.cache @@ -0,0 +1,8 @@ +{ + "version": 2, + "dgSpecHash": "9/dpZuVlARI=", + "success": true, + "projectFilePath": "C:\\SomePrograms\\WingetUI-Store\\src\\Tools\\UniGetUI.Core.Tools.csproj", + "expectedPackageFiles": [], + "logs": [] +} \ No newline at end of file diff --git a/src/UniGetUI.Core.Classes.Tests/PersonTests.cs b/src/UniGetUI.Core.Classes.Tests/PersonTests.cs new file mode 100644 index 000000000..4c9782c67 --- /dev/null +++ b/src/UniGetUI.Core.Classes.Tests/PersonTests.cs @@ -0,0 +1,32 @@ +using Xunit; + +namespace UniGetUI.Core.Classes.Tests +{ + public class PersonTests + { + [Theory] + [InlineData("Bernat-Miquel Guimerà", "https://github.com/BernatMiquelG.png", "https://github.com/BernatMiquelG")] + [InlineData("Bernat-Miquel Guimerà", "https://github.com/BernatMiquelG.png", null)] + [InlineData("Bernat-Miquel Guimerà", null, "https://github.com/BernatMiquelG")] + [InlineData("Bernat-Miquel Guimerà", null, null)] + public void TestPerson(string name, string? profilePicture, string? gitHubUrl) + { + + //arrange + Person actual = new(Name: name, + ProfilePicture: profilePicture is null ? null : new Uri(profilePicture), + GitHubUrl: gitHubUrl is null ? null : new Uri(gitHubUrl)); + + //Assert + if (string.IsNullOrEmpty(profilePicture)) + Assert.False(actual.HasPicture); + else + Assert.True(actual.HasPicture); + + if (string.IsNullOrEmpty(gitHubUrl)) + Assert.False(actual.HasGitHubProfile); + else + Assert.True(actual.HasGitHubProfile); + } + } +} diff --git a/src/UniGetUI.Core.Classes.Tests/SingletonBaseTest.cs b/src/UniGetUI.Core.Classes.Tests/SingletonBaseTest.cs new file mode 100644 index 000000000..591e135fe --- /dev/null +++ b/src/UniGetUI.Core.Classes.Tests/SingletonBaseTest.cs @@ -0,0 +1,37 @@ +using Xunit; + +namespace UniGetUI.Core.Classes.Tests +{ + public class SingletonBaseTest + { + + private class InheritedClass1 : SingletonBase + { + public int Attribute1 { get; set; } = 0; + } + + private class InheritedClass2 : SingletonBase + { + public int Attribute1 { get; set; } = 0; + } + + + [Fact] + public void TestSingletonClass() + { + var Type1Instance1 = InheritedClass1.Instance; + Type1Instance1.Attribute1 = 1; + + var Type1Instance2 = InheritedClass1.Instance; + Type1Instance2.Attribute1 = 3; + + Assert.Equal(Type1Instance1.Attribute1, Type1Instance2.Attribute1); + Assert.Equal(Type1Instance1, Type1Instance2); + + var Type2Instance1 = new InheritedClass2(); + Type2Instance1.Attribute1 = 2; + + Assert.NotEqual(Type1Instance1.Attribute1, Type2Instance1.Attribute1); + } + } +} diff --git a/src/UniGetUI.Core.Classes.Tests/SortableObservableCollectionTests.cs b/src/UniGetUI.Core.Classes.Tests/SortableObservableCollectionTests.cs new file mode 100644 index 000000000..5a7ec4e3e --- /dev/null +++ b/src/UniGetUI.Core.Classes.Tests/SortableObservableCollectionTests.cs @@ -0,0 +1,35 @@ +using Xunit; + +namespace UniGetUI.Core.Classes.Tests +{ + public class SortableObservableCollectionTests + { + [Fact] + public void TestSortableCollection() + { + int EventTriggeredCount = 0; + + var SortableCollection = new SortableObservableCollection(); + SortableCollection.CollectionChanged += (s, e) => { EventTriggeredCount++; }; + SortableCollection.SortingSelector = (s) => { return s; }; + SortableCollection.Add(1); + SortableCollection.Add(3); + SortableCollection.Add(4); + + SortableCollection.BlockSorting = true; + SortableCollection.Add(5); + SortableCollection.Add(2); + SortableCollection.BlockSorting = false; + + + SortableCollection.Sort(); + + Assert.Equal(7, EventTriggeredCount); + Assert.Equal(1, SortableCollection[0]); + Assert.Equal(2, SortableCollection[1]); + Assert.Equal(3, SortableCollection[2]); + Assert.Equal(4, SortableCollection[3]); + Assert.Equal(5, SortableCollection[4]); + } + } +} \ No newline at end of file diff --git a/src/UniGetUI.Core.Classes.Tests/UniGetUI.Core.Classes.Tests.csproj b/src/UniGetUI.Core.Classes.Tests/UniGetUI.Core.Classes.Tests.csproj new file mode 100644 index 000000000..fd1a09986 --- /dev/null +++ b/src/UniGetUI.Core.Classes.Tests/UniGetUI.Core.Classes.Tests.csproj @@ -0,0 +1,33 @@ + + + + + enable + false + true + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + diff --git a/src/UniGetUI.Core.Classes/Person.cs b/src/UniGetUI.Core.Classes/Person.cs new file mode 100644 index 000000000..8668b15e3 --- /dev/null +++ b/src/UniGetUI.Core.Classes/Person.cs @@ -0,0 +1,22 @@ +namespace UniGetUI.Core.Classes +{ + public readonly struct Person + { + public readonly string Name; + public readonly Uri? ProfilePicture; + public readonly Uri? GitHubUrl; + public readonly bool HasPicture; + public readonly bool HasGitHubProfile; + public readonly string Language; + + public Person(string Name, Uri? ProfilePicture = null, Uri? GitHubUrl = null, string Language = "") + { + this.Name = Name; + this.ProfilePicture = ProfilePicture; + this.GitHubUrl = GitHubUrl; + this.HasPicture = ProfilePicture is not null; + this.HasGitHubProfile = GitHubUrl is not null; + this.Language = Language; + } + } +} diff --git a/src/UniGetUI.Core.Classes/SingletonBase.cs b/src/UniGetUI.Core.Classes/SingletonBase.cs new file mode 100644 index 000000000..d17cc337b --- /dev/null +++ b/src/UniGetUI.Core.Classes/SingletonBase.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UniGetUI.Core.Classes +{ + public abstract class SingletonBase where T : SingletonBase + { + private static readonly Lazy Lazy = + new(() => (Activator.CreateInstance(typeof(T), true) as T)!); + + public static T Instance => Lazy.Value; + + protected SingletonBase() { } + } +} diff --git a/src/UniGetUI/Core/Classes.cs b/src/UniGetUI.Core.Classes/SortableObservableCollection.cs similarity index 72% rename from src/UniGetUI/Core/Classes.cs rename to src/UniGetUI.Core.Classes/SortableObservableCollection.cs index 76b8c1c5a..3fc0e61c9 100644 --- a/src/UniGetUI/Core/Classes.cs +++ b/src/UniGetUI.Core.Classes/SortableObservableCollection.cs @@ -1,14 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; +using System.Collections.ObjectModel; using System.Collections.Specialized; -using System.Linq; -namespace UniGetUI.Core +namespace UniGetUI.Core.Classes { public class SortableObservableCollection : ObservableCollection { - public Func SortingSelector { get; set; } + public Func? SortingSelector { get; set; } public bool Descending { get; set; } public bool BlockSorting { get; set; } = false; protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) @@ -22,12 +19,15 @@ protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) return; Sort(); } - } + public void Sort() { BlockSorting = true; + if (SortingSelector == null) + throw new Exception("SortableObservableCollection.SortingSelector must not be null when sorting a function"); + List sorted = Descending ? this.OrderByDescending(SortingSelector).ToList() : this.OrderBy(SortingSelector).ToList(); foreach (T item in sorted) { @@ -37,12 +37,4 @@ public void Sort() base.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } } - - public abstract class SingletonBase where T : SingletonBase - { - private static readonly Lazy Lazy = - new(() => (Activator.CreateInstance(typeof(T), true) as T)!); - - public static T Instance => Lazy.Value; - } } diff --git a/src/UniGetUI.Core.Classes/UniGetUI.Core.Classes.csproj b/src/UniGetUI.Core.Classes/UniGetUI.Core.Classes.csproj new file mode 100644 index 000000000..d82d51cab --- /dev/null +++ b/src/UniGetUI.Core.Classes/UniGetUI.Core.Classes.csproj @@ -0,0 +1,8 @@ + + + + + + enable + + diff --git a/src/UniGetUI.Core.Data.Tests/ContributorsTests.cs b/src/UniGetUI.Core.Data.Tests/ContributorsTests.cs new file mode 100644 index 000000000..9fd992549 --- /dev/null +++ b/src/UniGetUI.Core.Data.Tests/ContributorsTests.cs @@ -0,0 +1,12 @@ +namespace UniGetUI.Core.Data.Tests +{ + public class ContributorsTests + { + + [Fact] + public void CheckIfContributorListIsEmpty() + { + Assert.NotEmpty(ContributorsData.Contributors); + } + } +} diff --git a/src/UniGetUI.Core.Data.Tests/CoreTests.cs b/src/UniGetUI.Core.Data.Tests/CoreTests.cs new file mode 100644 index 000000000..8e199959e --- /dev/null +++ b/src/UniGetUI.Core.Data.Tests/CoreTests.cs @@ -0,0 +1,42 @@ +namespace UniGetUI.Core.Data.Tests +{ + public class CoreTests + { + public static object[][] Data => + [ + [CoreData.UniGetUIDataDirectory], + [CoreData.UniGetUIInstallationOptionsDirectory ], + [CoreData.UniGetUICacheDirectory_Data ], + [CoreData.UniGetUICacheDirectory_Icons ], + [CoreData.UniGetUICacheDirectory_Lang ], + [CoreData.UniGetUI_DefaultBackupDirectory ] + ]; + + [Theory] + [MemberData(nameof(Data))] + public void CheckDirectoryAttributes(string directory) + { + Assert.True(Directory.Exists(directory), $"Directory ${directory} does not exist, but it should have been created automatically"); + } + + [Fact] + public void CheckOtherAttributes() + { + Assert.NotEmpty(CoreData.VersionName); + Assert.NotEqual(0, CoreData.VersionNumber); + Assert.True(File.Exists(CoreData.IgnoredUpdatesDatabaseFile), "The Ignored Updates database file does not exist, but it should have been created automatically."); + + var notif_1 = CoreData.VolatileNotificationIdCounter; + var notif_2 = CoreData.VolatileNotificationIdCounter; + Assert.NotEqual(notif_1, notif_2); + + var notif_3 = CoreData.UpdatesAvailableNotificationId; + var notif_4 = CoreData.UpdatesAvailableNotificationId; + Assert.True(notif_3 == notif_4, "The UpdatesAvailableNotificationId must be always the same"); + Assert.NotEqual(0, CoreData.UpdatesAvailableNotificationId); + + Assert.True(Directory.Exists(CoreData.UniGetUIExecutableDirectory), "Directory where the executable is located does not exist"); + Assert.True(File.Exists(CoreData.UniGetUIExecutableFile), "The executable file does not exist"); + } + } +} diff --git a/src/UniGetUI.Core.Data.Tests/LicensesTest.cs b/src/UniGetUI.Core.Data.Tests/LicensesTest.cs new file mode 100644 index 000000000..3f17a1bae --- /dev/null +++ b/src/UniGetUI.Core.Data.Tests/LicensesTest.cs @@ -0,0 +1,30 @@ +namespace UniGetUI.Core.Data.Tests +{ + public class LicensesTest + { + [Fact] + public void EnsureLicenseUrlsExist() + { + List MissingUrls = new(); + + foreach (string library in LicenseData.LicenseNames.Keys) + if(!LicenseData.LicenseURLs.ContainsKey(library)) + MissingUrls.Add(library); + + Assert.True(MissingUrls.Count == 0, "The list of missing licenses is not empty: " + MissingUrls.ToArray().ToString()); + } + + [Fact] + public void EnsureHomepageUrlsExist() + { + List MissingUrls = new(); + + foreach (string library in LicenseData.LicenseNames.Keys) + if (!LicenseData.HomepageUrls.ContainsKey(library)) + MissingUrls.Add(library); + + Assert.True(MissingUrls.Count == 0, "The list of missing licenses is not empty: " + MissingUrls.ToArray().ToString()); + } + + } +} \ No newline at end of file diff --git a/src/UniGetUI.Core.Data.Tests/UniGetUI.Core.Data.Tests.csproj b/src/UniGetUI.Core.Data.Tests/UniGetUI.Core.Data.Tests.csproj new file mode 100644 index 000000000..6e0ca21a7 --- /dev/null +++ b/src/UniGetUI.Core.Data.Tests/UniGetUI.Core.Data.Tests.csproj @@ -0,0 +1,33 @@ + + + + + + enable + false + true + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + diff --git a/src/UniGetUI/Assets/Data/Contributors.list b/src/UniGetUI.Core.Data/Assets/Data/Contributors.list similarity index 100% rename from src/UniGetUI/Assets/Data/Contributors.list rename to src/UniGetUI.Core.Data/Assets/Data/Contributors.list diff --git a/src/UniGetUI/Core/Data/Contributors.cs b/src/UniGetUI.Core.Data/Contributors.cs similarity index 79% rename from src/UniGetUI/Core/Data/Contributors.cs rename to src/UniGetUI.Core.Data/Contributors.cs index 6bfae5e95..cd274d57b 100644 --- a/src/UniGetUI/Core/Data/Contributors.cs +++ b/src/UniGetUI.Core.Data/Contributors.cs @@ -1,7 +1,4 @@ -using System.IO; -using System.Linq; - -namespace UniGetUI.Core.Data +namespace UniGetUI.Core.Data { public static class ContributorsData { diff --git a/src/UniGetUI.Core.Data/Core.cs b/src/UniGetUI.Core.Data/Core.cs new file mode 100644 index 000000000..d0b796877 --- /dev/null +++ b/src/UniGetUI.Core.Data/Core.cs @@ -0,0 +1,195 @@ +using System.Reflection.Metadata.Ecma335; +using UniGetUI.Core.Logging; +using Windows.Storage.Search; +using Windows.System.Diagnostics; + +namespace UniGetUI.Core.Data +{ + public static class CoreData + { + public static string VersionName = "3.1.0-alpha"; // Do not modify this line, use file scripts/apply_versions.py + public static double VersionNumber = 3.099; // Do not modify this line, use file scripts/apply_versions.py + + /// + /// The directory where all the user data is stored. The directory is automatically created if it does not exist. + /// + public static string UniGetUIDataDirectory + { + get + { + string old_path = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".wingetui"); + string new_path = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "UniGetUI"); + return GetNewDataDirectoryOrMoveOld(old_path, new_path); + } + } + + /// + /// The directory where the installation options are stored. The directory is automatically created if it does not exist. + /// + public static string UniGetUIInstallationOptionsDirectory + { + get + { + var path = Path.Join(UniGetUIDataDirectory, "InstallationOptions"); + if(!Directory.Exists(path)) Directory.CreateDirectory(path); + return path; + } + } + + /// + /// The directory where the metadata cache is stored. The directory is automatically created if it does not exist. + /// + public static string UniGetUICacheDirectory_Data + { + get + { + string old_path = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "WingetUI", "CachedData"); + string new_path = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "UniGetUI", "CachedMetadata"); + return GetNewDataDirectoryOrMoveOld(old_path, new_path); + } + } + + /// + /// The directory where the cached icons and screenshots are saved. The directory is automatically created if it does not exist. + /// + public static string UniGetUICacheDirectory_Icons + { + get + { + string old_path = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "WingetUI", "CachedIcons"); + string new_path = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "UniGetUI", "CachedMedia"); + return GetNewDataDirectoryOrMoveOld(old_path, new_path); + } + } + + /// + /// The directory where the cached language files are stored. The directory is automatically created if it does not exist. + /// + public static string UniGetUICacheDirectory_Lang + { + get + { + string old_dir = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "WingetUI", "CachedLangFiles"); + string new_dir = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "UniGetUI", "CachedLanguageFiles"); + return GetNewDataDirectoryOrMoveOld(old_dir, new_dir); + } + } + + /// + /// The directory where package backups will be saved by default. + /// + public static string UniGetUI_DefaultBackupDirectory + { + get + { + string old_dir = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "WingetUI"); + string new_dir = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "UniGetUI"); + return GetNewDataDirectoryOrMoveOld(old_dir, new_dir); + } + } + + /// + /// The file where the screenshot metadata is stored. If the file does not exist, it will be created automatically. + /// + public static string IgnoredUpdatesDatabaseFile + { + get + { + // Calling the UniGetUIDataDirectory will create the directory if it does not exist + string file_path = Path.Join(UniGetUIDataDirectory, "IgnoredPackageUpdates.json"); + if (!File.Exists(file_path)) + File.WriteAllText(file_path, "{}"); + return file_path; + } + } + public static bool IsDaemon = false; + + public static string ManagerLogs = ""; + + + private static int __volatile_notification_id_counter = 1235; + + /// + /// A self-incremented value to generate random notification IDs + /// + public static int VolatileNotificationIdCounter + { + get => __volatile_notification_id_counter++; + } + + /// + /// The ID of the notification that is used to inform the user that updates are available + /// + public static int UpdatesAvailableNotificationId + { + get => 1234; + } + + /// + /// A path pointing to the location where the app is installed + /// + public static string UniGetUIExecutableDirectory + { + get { + string? dir = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); + if (dir != null) + return dir; + else + Logger.Error("System.Reflection.Assembly.GetExecutingAssembly().Location returned an empty path"); + return Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "UiGetUI"); + } + } + + /// + /// A path pointing to the executable file of the app + /// + public static string UniGetUIExecutableFile + { + get { + string? filename = System.Reflection.Assembly.GetExecutingAssembly().Location; + if (filename != null) + return filename; + else + Logger.Error("System.Reflection.Assembly.GetExecutingAssembly().Location returned an empty path"); + return Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "UiGetUI", "UniGetUI.exe"); + } + } + + + /// + /// This method will return the most appropriate data directory. + /// If the new directory exists, it will be used. + /// If the new directory does not exist, but the old directory does, it will be moved to the new location, and the new location will be used. + /// If none exist, the new directory will be created. + /// + /// The old/legacy directory + /// The new directory + /// The path to an existing, valid directory + private static string GetNewDataDirectoryOrMoveOld(string old_path, string new_path) + { + if (Directory.Exists(new_path)) + return new_path; + else if (Directory.Exists(old_path)) + { + try + { + Directory.Move(old_path, new_path); + return new_path; + } + catch (Exception e) + { + Logger.Error("Cannot move old data directory to new location. Directory to move: " + old_path + ". Destination: " + new_path); + Logger.Error(e); + return old_path; + } + } + else + { + Logger.Debug("Creating non-existing data directory at: " + new_path); + Directory.CreateDirectory(new_path); + return new_path; + } + } + + } +} diff --git a/src/UniGetUI/Core/Data/Licenses.cs b/src/UniGetUI.Core.Data/Licenses.cs similarity index 98% rename from src/UniGetUI/Core/Data/Licenses.cs rename to src/UniGetUI.Core.Data/Licenses.cs index ac0f357e2..2ced3e0d4 100644 --- a/src/UniGetUI/Core/Data/Licenses.cs +++ b/src/UniGetUI.Core.Data/Licenses.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; - -namespace UniGetUI.Core.Data +namespace UniGetUI.Core.Data { public static class LicenseData { diff --git a/src/UniGetUI.Core.Data/UniGetUI.Core.Data.csproj b/src/UniGetUI.Core.Data/UniGetUI.Core.Data.csproj new file mode 100644 index 000000000..6a0f6a73a --- /dev/null +++ b/src/UniGetUI.Core.Data/UniGetUI.Core.Data.csproj @@ -0,0 +1,18 @@ + + + + + + enable + + + + + + + + PreserveNewest + + + + diff --git a/src/UniGetUI.Core.IconEngine.Tests/IconStoreTests.cs b/src/UniGetUI.Core.IconEngine.Tests/IconStoreTests.cs new file mode 100644 index 000000000..e8494f24e --- /dev/null +++ b/src/UniGetUI.Core.IconEngine.Tests/IconStoreTests.cs @@ -0,0 +1,48 @@ +namespace UniGetUI.Core.IconEngine.Tests +{ + public class IconStoreTests + { + IconDatabase iconStore = new(); + + [Fact] + public async Task LoadIconsAndScreenshotsDatabaseTest() + { + // Serializable is implicitly tested when calling the method + // LoadIconAndScreenshotsDatabase() + + await iconStore.LoadIconAndScreenshotsDatabaseAsync(); + + var iconCount = iconStore.GetIconCount(); + Assert.NotEqual(0, iconCount.PackagesWithIconCount); + Assert.NotEqual(0, iconCount.PackagesWithScreenshotCount); + Assert.NotEqual(0, iconCount.TotalScreenshotCount); + } + + [Fact] + public async Task TestGetExistingIconAndImagesAsync() + { + await iconStore.LoadIconAndScreenshotsDatabaseAsync(); + + var icon = iconStore.GetIconUrlForId("__test_entry_DO_NOT_EDIT_PLEASE"); + Assert.Equal("https://this.is.a.test/url/used_for/automated_unit_testing.png", icon); + + var screenshots = iconStore.GetScreenshotsUrlForId("__test_entry_DO_NOT_EDIT_PLEASE"); + Assert.Equal(3, screenshots.Length); + Assert.Equal("https://image_number.com/1.png", screenshots[0]); + Assert.Equal("https://image_number.com/2.png", screenshots[1]); + Assert.Equal("https://image_number.com/3.png", screenshots[2]); + } + + [Fact] + public async Task TestGetNonExistingIconAndImagesAsync() + { + await iconStore.LoadIconAndScreenshotsDatabaseAsync(); + + var nonexistent_icon = iconStore.GetIconUrlForId("__test_entry_THIS_ICON_DOES_NOT_EXTST"); + Assert.Empty(nonexistent_icon); + + var nonexistent_screenshots = iconStore.GetScreenshotsUrlForId("__test_entry_THIS_ICON_DOES_NOT_EXTST"); + Assert.Empty(nonexistent_screenshots); + } + } +} \ No newline at end of file diff --git a/src/UniGetUI.Core.IconEngine.Tests/UniGetUI.Core.IconEngine.Tests.csproj b/src/UniGetUI.Core.IconEngine.Tests/UniGetUI.Core.IconEngine.Tests.csproj new file mode 100644 index 000000000..52d9d66bd --- /dev/null +++ b/src/UniGetUI.Core.IconEngine.Tests/UniGetUI.Core.IconEngine.Tests.csproj @@ -0,0 +1,32 @@ + + + + + + enable + false + true + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + diff --git a/src/UniGetUI.Core.IconStore/IconCacheEngine.cs b/src/UniGetUI.Core.IconStore/IconCacheEngine.cs new file mode 100644 index 000000000..8cc7f5297 --- /dev/null +++ b/src/UniGetUI.Core.IconStore/IconCacheEngine.cs @@ -0,0 +1,186 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection.PortableExecutable; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; +using UniGetUI.Core.Data; +using UniGetUI.Core.Logging; +using UniGetUI.Core.Tools; +using Windows.Foundation.Collections; + +namespace UniGetUI.Core.IconEngine +{ + public enum CachedIconVerificationMethod + { + None, + Sha256Checksum, + FileSize, + PackageVersion, + UriSource + } + + public readonly struct CacheableIcon + { + public readonly Uri Url; + public readonly byte[] Sha256 = {}; + public readonly string Version = ""; + public readonly long Size = -1; + public readonly CachedIconVerificationMethod VerificationMethod; + + public CacheableIcon(Uri uri, byte[] Sha256) + { + Url = uri; + this.Sha256 = Sha256; + VerificationMethod = CachedIconVerificationMethod.Sha256Checksum; + } + + public CacheableIcon(Uri uri, string version) + { + Url = uri; + this.Version = version; + VerificationMethod = CachedIconVerificationMethod.PackageVersion; + } + + public CacheableIcon(Uri uri, long size) + { + Url = uri; + this.Size = size; + VerificationMethod = CachedIconVerificationMethod.FileSize; + } + + public CacheableIcon(Uri icon) + { + Url = icon; + VerificationMethod = CachedIconVerificationMethod.UriSource; + } + } + + public static class IconCacheEngine + { + /// + /// Returns the path to the icon file, downloading it if necessary + /// + /// a CacheableIcon object representing the object + /// The name of the PackageManager + /// the Id of the package + /// A path to a local icon file + public static async Task DownloadIconOrCache(CacheableIcon? _icon, string ManagerName, string PackageId) + { + if (_icon is null) return ""; + CacheableIcon icon = _icon ?? new CacheableIcon(); + + var extension = icon.Url.AbsolutePath.Substring(icon.Url.AbsolutePath.LastIndexOf('.'))[1..]; + + if (extension.Length > 6) + { + Logger.Warn($"Extension {extension} for file url {icon.Url} seems to be invalid, defaulting to .png"); + extension = "png"; + } + + var FilePath = Path.Join(CoreData.UniGetUICacheDirectory_Icons, ManagerName, $"{PackageId}.{extension}"); + var VersionPath = Path.Join(CoreData.UniGetUICacheDirectory_Icons, ManagerName, $"{PackageId}.{extension}.version"); + var UriPath = Path.Join(CoreData.UniGetUICacheDirectory_Icons, ManagerName, $"{PackageId}.{extension}.uri"); + var FileDirectory = Path.Join(CoreData.UniGetUICacheDirectory_Icons, ManagerName); + if (!Directory.Exists(FileDirectory)) + Directory.CreateDirectory(FileDirectory); + + var FileExists = File.Exists(FilePath); + bool IsFileValid = false; + if(FileExists) + switch (icon.VerificationMethod) + { + case CachedIconVerificationMethod.FileSize: + try + { + long size = await CoreTools.GetFileSizeAsyncAsLong(icon.Url); + IsFileValid = (size == icon.Size); + } catch (Exception e) + { + Logger.Warn($"Failed to verify icon file size for {icon.Url} through FileSize with error {e.Message}"); + } + break; + + case CachedIconVerificationMethod.Sha256Checksum: + try + { + byte[] hash = await CalculateFileHashAsync(FilePath); + IsFileValid = (hash == icon.Sha256); + } + catch (Exception e) + { + Logger.Warn($"Failed to verify icon file size for {icon.Url} through Sha256 with error {e.Message}"); + } + break; + + case CachedIconVerificationMethod.PackageVersion: + try + { + if(File.Exists(VersionPath)) + { + var localVersion = File.ReadAllText(VersionPath); + IsFileValid = (localVersion == icon.Version); + } + } + catch (Exception e) + { + Logger.Warn($"Failed to verify icon file size for {icon.Url} through PackageVersion with error {e.Message}"); + } + break; + + case CachedIconVerificationMethod.UriSource: + try + { + if (File.Exists(UriPath)) + { + var localVersion = File.ReadAllText(UriPath); + IsFileValid = (localVersion == icon.Url.ToString()); + } + } + catch (Exception e) + { + Logger.Warn($"Failed to verify icon file size for {icon.Url} through UriSource with error {e.Message}"); + } + break; + + default: + Logger.ImportantInfo($"Icon {icon.Url} for package {PackageId} on manager {ManagerName} does not use a valid cache verification method"); + IsFileValid = true; + break; + } + + Logger.Debug($"Icon for package {PackageId} on manager {ManagerName} with Uri={icon.Url} has been determined to be {(IsFileValid? "VALID": "INVALID")} through verification method {icon.VerificationMethod}"); + + if(!IsFileValid) + using (var client = new HttpClient()) + { + HttpResponseMessage response = await client.GetAsync(icon.Url); + response.EnsureSuccessStatusCode(); + using (var stream = await response.Content.ReadAsStreamAsync()) + using(FileStream fileStream = File.Create(FilePath)) + await stream.CopyToAsync(fileStream); + if (icon.VerificationMethod == CachedIconVerificationMethod.PackageVersion) + await File.WriteAllTextAsync(VersionPath, icon.Version); + + if (icon.VerificationMethod == CachedIconVerificationMethod.UriSource) + await File.WriteAllTextAsync(UriPath, icon.Url.ToString()); + } + + Logger.Info($"Icon for package {PackageId} stored on {FilePath}"); + return FilePath; + } + + + private static async Task CalculateFileHashAsync(string filePath) + { + using (var stream = File.OpenRead(filePath)) + using (var sha256 = SHA256.Create()) + return await sha256.ComputeHashAsync(stream); + } + } +} diff --git a/src/UniGetUI.Core.IconStore/IconDatabase.cs b/src/UniGetUI.Core.IconStore/IconDatabase.cs new file mode 100644 index 000000000..c8bad1e86 --- /dev/null +++ b/src/UniGetUI.Core.IconStore/IconDatabase.cs @@ -0,0 +1,127 @@ +using System.ComponentModel; +using System.Diagnostics; +using System.Text.Json; +using UniGetUI.Core.Data; +using UniGetUI.Core.Logging; +using UniGetUI.Core.SettingsEngine; + +namespace UniGetUI.Core.IconEngine +{ + public class IconDatabase + { + public struct IconCount + { + public int PackagesWithIconCount = 0; + public int TotalScreenshotCount = 0; + public int PackagesWithScreenshotCount = 0; + public IconCount() { } + } + + private static IconDatabase? __instance = null; + + public static IconDatabase Instance + { + get + { + if(__instance == null) + { + Logger.Error("IconStore.Instance was not initialized, creating an empty instance."); + InitializeInstance(); + return Instance; + } + return __instance; + } + } + + public static void InitializeInstance() + { + __instance = new(); + } + + /// + /// The icon and screenshot database + /// + private Dictionary IconDatabaseData = new(); + private IconCount __icon_count = new(); + + /// + /// Tis class represents the structure of the icon and screenshot database. It is used to deserialize the JSON data. + /// + + + /// + /// Download the icon and screenshots database to a local file, and load it into memory + /// + /// + public async void LoadIconAndScreenshotsDatabase() + { + await LoadIconAndScreenshotsDatabaseAsync(); + } + + public async Task LoadIconAndScreenshotsDatabaseAsync() + { + string IconsAndScreenshotsFile = Path.Join(CoreData.UniGetUICacheDirectory_Data, "Icon Database.json"); + try + { + Uri DownloadUrl = new("https://raw.githubusercontent.com/marticliment/WingetUI/main/WebBasedData/screenshot-database-v2.json"); + if (Settings.Get("IconDataBaseURL")) + DownloadUrl = new Uri(Settings.GetValue("IconDataBaseURL")); + + using (HttpClient client = new()) + { + string fileContents = await client.GetStringAsync(DownloadUrl); + await File.WriteAllTextAsync(IconsAndScreenshotsFile, fileContents); + } + + Logger.ImportantInfo("Downloaded new icons and screenshots successfully!"); + + } + catch (Exception e) + { + Logger.Warn("Failed to download icons and screenshots"); + Logger.Warn(e); + } + + + if (!File.Exists(IconsAndScreenshotsFile)) + { + Logger.Error("Icon Database file not found"); + return; + } + + try + { + IconScreenshotDatabase_v2 JsonData = JsonSerializer.Deserialize(await File.ReadAllTextAsync(IconsAndScreenshotsFile)); + if (JsonData.icons_and_screenshots != null) + IconDatabaseData = JsonData.icons_and_screenshots; + __icon_count = new IconCount() + { + PackagesWithIconCount = JsonData.package_count.packages_with_icon, + PackagesWithScreenshotCount = JsonData.package_count.packages_with_screenshot, + TotalScreenshotCount = JsonData.package_count.total_screenshots, + }; + } + catch (Exception ex) + { + Logger.Error("Failed to load icon and screenshot database"); + Logger.Error(ex); + } + } + + public string GetIconUrlForId(string id) + { + return IconDatabaseData.ContainsKey(id) ? IconDatabaseData[id].icon : ""; + } + + public string[] GetScreenshotsUrlForId(string id) + { + return IconDatabaseData.ContainsKey(id) ? IconDatabaseData[id].images.ToArray() : []; + } + + public IconCount GetIconCount() + { + return __icon_count; + } + + } +} diff --git a/src/UniGetUI.Core.IconStore/Serializable.cs b/src/UniGetUI.Core.IconStore/Serializable.cs new file mode 100644 index 000000000..543fa263c --- /dev/null +++ b/src/UniGetUI.Core.IconStore/Serializable.cs @@ -0,0 +1,22 @@ +namespace UniGetUI.Core.IconEngine +{ + internal struct IconScreenshotDatabase_v2 + { + public struct PackageCount + { + public int total { get; set; } + public int done { get; set; } + public int packages_with_icon { get; set; } + public int packages_with_screenshot { get; set; } + public int total_screenshots { get; set; } + } + public struct PackageIconAndScreenshots + { + public string icon { get; set; } + public List images { get; set; } + } + + public PackageCount package_count { get; set; } + public Dictionary icons_and_screenshots { get; set; } + } +} diff --git a/src/UniGetUI.Core.IconStore/UniGetUI.Core.IconEngine.csproj b/src/UniGetUI.Core.IconStore/UniGetUI.Core.IconEngine.csproj new file mode 100644 index 000000000..2bc2b1dfb --- /dev/null +++ b/src/UniGetUI.Core.IconStore/UniGetUI.Core.IconEngine.csproj @@ -0,0 +1,16 @@ + + + + + + enable + + + + + + + + + + diff --git a/src/UniGetUI.Core.Language.Tests/LanguageDataTests.cs b/src/UniGetUI.Core.Language.Tests/LanguageDataTests.cs new file mode 100644 index 000000000..622aa2e1f --- /dev/null +++ b/src/UniGetUI.Core.Language.Tests/LanguageDataTests.cs @@ -0,0 +1,48 @@ +using System.Text.Json.Nodes; +using UniGetUI.Core.Classes; + +namespace UniGetUI.Core.Language.Tests +{ + public class LanguageDataTests + { + public static object[][] Translators => + LanguageData.TranslatorsList.Select(x => new object[] { x }).ToArray(); + + public static object[][] LanguageReferences => + LanguageData.LanguageReference.Select(x => new object[] { x.Key, x.Value }).ToArray(); + + [Fact] + public void TranslatorsListNotEmptyTest() + => Assert.NotEmpty(LanguageData.TranslatorsList); + + [Theory] + [MemberData(nameof(Translators))] + public void TranslatorsListTest(Person translator) + => Assert.NotEmpty(translator.Name); + + + [Fact] + public void LanguageReferenceNotEmptyTest() + { + Assert.NotEmpty(LanguageData.LanguageReference); + } + + [Theory] + [MemberData(nameof(LanguageReferences))] + public void LanguageReferenceTest(string key, string value) + { + Assert.False(value.Contains("NoNameLang_"), $"The language with key {key} has no assigned name"); + } + + [Fact] + public void TranslatedPercentageNotEmptyTests() + { + var TranslatedPercent = LanguageData.TranslationPercentages; + foreach (var key in TranslatedPercent.Keys) + { + Assert.True(LanguageData.LanguageReference.ContainsKey(key), $"The language key {key} was not found on LanguageReference"); + Assert.False(LanguageData.TranslationPercentages[key].Contains("404%"), $"Somehow the key {key} has no value"); + } + } + } +} \ No newline at end of file diff --git a/src/UniGetUI.Core.Language.Tests/LanguageEngineTests.cs b/src/UniGetUI.Core.Language.Tests/LanguageEngineTests.cs new file mode 100644 index 000000000..667202706 --- /dev/null +++ b/src/UniGetUI.Core.Language.Tests/LanguageEngineTests.cs @@ -0,0 +1,80 @@ +using UniGetUI.Core.Data; +using UniGetUI.PackageEngine.Enums; + +namespace UniGetUI.Core.Language.Tests +{ + public class LanguageEngineTests + { + [Theory] + [InlineData("ca", "Fes una còpia de seguretat dels paquets instal·lats")] + [InlineData("es", "Respaldar paquetes instalados")] + public void TestLoadingLanguage(string language, string translation) + { + var engine = new LanguageEngine(); + + engine.LoadLanguage(language); + Assert.Equal(translation, engine.Translate("Backup installed packages")); + } + + [Fact] + public void TestLoadingLanguageForNonExistentKey() + { + //arrange + var engine = new LanguageEngine(); + engine.LoadLanguage("es"); + //act + var NONEXISTENT_KEY = "This is a nonexistent key thay should be returned as-is"; + //assert + Assert.Equal(NONEXISTENT_KEY, engine.Translate(NONEXISTENT_KEY)); + } + + [Theory] + [InlineData("en", "UniGetUI Log", "UniGetUI (formerly WingetUI)")] + [InlineData("ca", "Registre del UniGetUI", "UniGetUI (abans WingetUI)")] + public void TestUniGetUIRefactoring(string language, string uniGetUILogTranslation, string uniGetUITranslation) + { + var engine = new LanguageEngine(); + + engine.LoadLanguage(language); + Assert.Equal(uniGetUILogTranslation, engine.Translate("WingetUI Log")); + Assert.Equal(uniGetUITranslation, engine.Translate("WingetUI")); + } + + + [Fact] + public void LocalFallbackTest() + { + var engine = new LanguageEngine(); + engine.LoadLanguage("random-nonexistent-language"); + Assert.Equal("en", engine.Translate("locale")); + } + + [Fact] + public void TestStaticallyLoadedLanguages() + { + var engine = new LanguageEngine(); + engine.LoadLanguage("ca"); + engine.LoadStaticTranslation(); + Assert.Equal("Usuari | Local", CommonTranslations.ScopeNames[PackageScope.Local]); + Assert.Equal("Màquina | Global", CommonTranslations.ScopeNames[PackageScope.Global]); + + Assert.Equal(PackageScope.Global, CommonTranslations.InvertedScopeNames["Màquina | Global"]); + Assert.Equal(PackageScope.Local, CommonTranslations.InvertedScopeNames["Usuari | Local"]); + } + + [Fact] + public async Task TestDownloadUpdatedTranslationsAsync() + { + var expected_file = Path.Join(CoreData.UniGetUICacheDirectory_Lang, "lang_ca.json"); + if (File.Exists(expected_file)) + File.Delete(expected_file); + + var engine = new LanguageEngine(); + engine.LoadLanguage("ca"); + await engine.DownloadUpdatedLanguageFile("ca"); + + Assert.True(File.Exists(expected_file), "The updated file was not created"); + File.Delete(expected_file); + } + } +} diff --git a/src/UniGetUI.Core.Language.Tests/UniGetUI.Core.LanguageEngine.Tests.csproj b/src/UniGetUI.Core.Language.Tests/UniGetUI.Core.LanguageEngine.Tests.csproj new file mode 100644 index 000000000..14d35bf21 --- /dev/null +++ b/src/UniGetUI.Core.Language.Tests/UniGetUI.Core.LanguageEngine.Tests.csproj @@ -0,0 +1,33 @@ + + + + + + enable + false + true + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + diff --git a/src/UniGetUI/Assets/Data/LanguagesReference.json b/src/UniGetUI.Core.LanguageEngine/Assets/Data/LanguagesReference.json similarity index 98% rename from src/UniGetUI/Assets/Data/LanguagesReference.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Data/LanguagesReference.json index 986a87737..bb4d68654 100644 --- a/src/UniGetUI/Assets/Data/LanguagesReference.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Data/LanguagesReference.json @@ -31,6 +31,7 @@ "ro": "Romanian - Română", "ru": "Russian - Русский", "sr": "Serbian - Srpski", + "sq": "Albanian - Shqip", "si": "Sinhala - සිංහල", "sl": "Slovene - Slovenščina", "sv": "Swedish - Svenska", diff --git a/src/UniGetUI/Assets/Data/TranslatedPercentages.json b/src/UniGetUI.Core.LanguageEngine/Assets/Data/TranslatedPercentages.json similarity index 56% rename from src/UniGetUI/Assets/Data/TranslatedPercentages.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Data/TranslatedPercentages.json index 106c284ba..0c4cb7286 100644 --- a/src/UniGetUI/Assets/Data/TranslatedPercentages.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Data/TranslatedPercentages.json @@ -1,22 +1,34 @@ { - "ar": "80%", + "ar": "79%", "bg": "63%", "bn": "87%", + "cs": "99%", "da": "79%", + "de": "99%", "el": "99%", "fa": "66%", - "fi": "50%", + "fi": "49%", + "fr": "99%", "he": "50%", "hi": "56%", "hr": "60%", + "hu": "99%", "id": "88%", + "it": "99%", "ja": "98%", "ko": "88%", "mk": "66%", + "nb": "99%", + "nl": "99%", + "nn": "99%", "pl": "99%", "pt_BR": "99%", + "pt_PT": "99%", + "ro": "99%", "ru": "99%", "si": "6%", + "sl": "99%", + "sq": "99%", "sr": "70%", "sv": "0%", "tg": "15%", @@ -24,5 +36,7 @@ "tr": "99%", "ua": "59%", "ur": "0%", - "vi": "99%" + "vi": "99%", + "zh_CN": "99%", + "zh_TW": "99%" } \ No newline at end of file diff --git a/src/UniGetUI/Assets/Data/Translators.json b/src/UniGetUI.Core.LanguageEngine/Assets/Data/Translators.json similarity index 100% rename from src/UniGetUI/Assets/Data/Translators.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Data/Translators.json diff --git a/src/UniGetUI/Assets/Languages/lang_ar.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_ar.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_ar.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_ar.json index 30e1b8255..758f514ba 100644 --- a/src/UniGetUI/Assets/Languages/lang_ar.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_ar.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "تلقائياً قم بحفظ قائمة لكل حُزمِك المثبتة لاستعادتها بسهولة.", "Automatically save a list of your installed packages on your computer." : "تلقائياً قم بحفظ قائمة لكل حُزمِك المثبتة على جهازك.", "Autostart WingetUI in the notifications area" : "تشغيل WingetUI تلقائياً في منطقة الإشعارات", + "Available Updates" : null, "Available updates: {0}" : "التحديثات المتاحة: {0}", "Available updates: {0}, not finished yet..." : "التحديثات المتاحة: {0}, لم يتم الانتهاء بعد...", "Backup" : "النسخ الإحتياطي", diff --git a/src/UniGetUI/Assets/Languages/lang_bg.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_bg.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_bg.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_bg.json index 88f1bc8ee..021f39477 100644 --- a/src/UniGetUI/Assets/Languages/lang_bg.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_bg.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : null, "Automatically save a list of your installed packages on your computer." : "Автоматично запазване на списъка на Вашите инсталирани пакети на Вашия компютър.", "Autostart WingetUI in the notifications area" : "Автоматично стартиране на WingetUI в зоната за известия", + "Available Updates" : null, "Available updates: {0}" : "Налични актуализации: {0}", "Available updates: {0}, not finished yet..." : "Налични актуализации: {0}, още не сме готови...", "Backup" : null, diff --git a/src/UniGetUI/Assets/Languages/lang_bn.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_bn.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_bn.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_bn.json index ee3426d89..033559b8f 100644 --- a/src/UniGetUI/Assets/Languages/lang_bn.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_bn.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "সহজেই পুনরুদ্ধার করতে আপনার সমস্ত ইনস্টল করা প্যাকেজগুলির একটি তালিকা স্বয়ংক্রিয়ভাবে সংরক্ষণ করুন।", "Automatically save a list of your installed packages on your computer." : "আপনার কম্পিউটারে আপনার ইনস্টল করা প্যাকেজগুলির একটি তালিকা স্বয়ংক্রিয়ভাবে সংরক্ষণ করুন।", "Autostart WingetUI in the notifications area" : "বিজ্ঞপ্তি এলাকায় WingetUI অটোস্টার্ট করুন", + "Available Updates" : null, "Available updates: {0}" : "উপলভ্য আপডেটগুলিঃ {0}", "Available updates: {0}, not finished yet..." : "উপলব্ধ আপডেটগুলি: {0}, এখনো শেষ হয়নি...", "Backup" : "ব্যাকআপ", diff --git a/src/UniGetUI/Assets/Languages/lang_ca.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_ca.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_ca.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_ca.json index 07958f4b2..f63a335dc 100644 --- a/src/UniGetUI/Assets/Languages/lang_ca.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_ca.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "Desa automàticament una llista de tots els paquets instal·lats per a restaurar-los fàcilment.", "Automatically save a list of your installed packages on your computer." : "Desa una llista dels paquets instal·lats al vostre ordinador de forma automàtica", "Autostart WingetUI in the notifications area" : "Inicia el WingetUI a l'àrea de notificacions", + "Available Updates" : "Actualitzacions disponibles", "Available updates: {0}" : "Actualitzacions disponibles: {0}", "Available updates: {0}, not finished yet..." : "Actualitzacions disponibles: {0}, encara no hem acabat...", "Backup" : "Fes la còpia", diff --git a/src/UniGetUI/Assets/Languages/lang_cs.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_cs.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_cs.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_cs.json index 4f2888346..3448d9921 100644 --- a/src/UniGetUI/Assets/Languages/lang_cs.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_cs.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "Automatické uložení seznamu všech nainstalovaných balíčků pro jejich snadné obnovení.", "Automatically save a list of your installed packages on your computer." : "Automatické uložení seznamu nainstalovaných balíčků do počítače.", "Autostart WingetUI in the notifications area" : "Automatické spuštění WingetUI do notifikační oblasti", + "Available Updates" : null, "Available updates: {0}" : "Dostupných aktualizací: {0}", "Available updates: {0}, not finished yet..." : "Dostupných aktualizací: {0}, ještě nedokončeno...", "Backup" : "Zálohovat", diff --git a/src/UniGetUI/Assets/Languages/lang_da.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_da.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_da.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_da.json index fcbc73ba4..0d452aa24 100644 --- a/src/UniGetUI/Assets/Languages/lang_da.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_da.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "Gem automatisk en liste af dine installerede pakker til genoprettelse.", "Automatically save a list of your installed packages on your computer." : "Gem automatisk en liste af dine installerede pakker på din computer.", "Autostart WingetUI in the notifications area" : "Start WingetUI automatisk i notifikations området", + "Available Updates" : null, "Available updates: {0}" : "Tilgængelige opdateringer: {0}", "Available updates: {0}, not finished yet..." : "Tilgængelige opdateringer: {0}, endnu ikke færdiggjorte...", "Backup" : "Backup", diff --git a/src/UniGetUI/Assets/Languages/lang_de.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_de.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_de.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_de.json index f4380dc77..06b6036a8 100644 --- a/src/UniGetUI/Assets/Languages/lang_de.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_de.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "Liste von alle auf dem Computer installierten Paketen automatisch speichern", "Automatically save a list of your installed packages on your computer." : "Liste von auf dem Computer installierten Paketen automatisch speichern", "Autostart WingetUI in the notifications area" : "WingetUI automatisch im Infobereich starten", + "Available Updates" : null, "Available updates: {0}" : "Verfügbare Updates: {0}", "Available updates: {0}, not finished yet..." : "Verfügbare Updates: {0}, noch nicht abgeschlossen...", "Backup" : "Sichern", diff --git a/src/UniGetUI/Assets/Languages/lang_el.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_el.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_el.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_el.json index ce5b6af2d..1982d4d3b 100644 --- a/src/UniGetUI/Assets/Languages/lang_el.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_el.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "Αυτόματη αποθήκευση μιας λίστας με όλα τα εγκατεστημένα πακέτα σας για εύκολη επαναφορά.", "Automatically save a list of your installed packages on your computer." : "Αυτόματη αποθήκευση μιας λίστας με τα εγκατεστημένα πακέτα στον υπολογιστή σας.", "Autostart WingetUI in the notifications area" : "Αυτόματη εκκίνηση του WingetUI στην περιοχή ειδοποιήσεων", + "Available Updates" : null, "Available updates: {0}" : "Διαθέσιμες ενημερώσεις: {0}", "Available updates: {0}, not finished yet..." : "Διαθέσιμες ενημερώσεις: {0}, δεν έχει ολοκληρωθεί ακόμα...", "Backup" : "Δημιουργία αντιγράφου ασφαλείας", diff --git a/src/UniGetUI/Assets/Languages/lang_en.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_en.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_en.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_en.json index 675207044..3f7c62147 100644 --- a/src/UniGetUI/Assets/Languages/lang_en.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_en.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : null, "Automatically save a list of your installed packages on your computer." : null, "Autostart WingetUI in the notifications area" : null, + "Available Updates" : null, "Available updates: {0}" : null, "Available updates: {0}, not finished yet..." : null, "Backup" : null, diff --git a/src/UniGetUI/Assets/Languages/lang_es.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_es.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_es.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_es.json index 8cea74976..a1fa5752c 100644 --- a/src/UniGetUI/Assets/Languages/lang_es.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_es.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "Salvar automáticamente una lista de todos tus paquetes instalados para fácilmente restaurarlos.", "Automatically save a list of your installed packages on your computer." : "Salvar automáticamente una lista de tus paquetes instalados en tu computador.", "Autostart WingetUI in the notifications area" : "Iniciar WingetUI automáticamente en el área de notificaciones", + "Available Updates" : "Actualizaciones disponibles", "Available updates: {0}" : "Actualizaciones disponibles: {0}", "Available updates: {0}, not finished yet..." : "Actualizaciones disponibles: {0}, no ha acabado todavía...", "Backup" : "Respaldar", diff --git a/src/UniGetUI/Assets/Languages/lang_fa.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_fa.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_fa.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_fa.json index 98268fa2e..1216496fb 100644 --- a/src/UniGetUI/Assets/Languages/lang_fa.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_fa.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : null, "Automatically save a list of your installed packages on your computer." : null, "Autostart WingetUI in the notifications area" : "WingetUI را به صورت خودکار در قسمت اعلان ها راه اندازی کنید\n", + "Available Updates" : null, "Available updates: {0}" : "به روز رسانی های موجود: {0}", "Available updates: {0}, not finished yet..." : "به روز رسانی های موجود: {0}، هنوز تمام نشده است...\n", "Backup" : "پشتیبان گیری", diff --git a/src/UniGetUI/Assets/Languages/lang_fi.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_fi.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_fi.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_fi.json index df9dd9704..bf9683f30 100644 --- a/src/UniGetUI/Assets/Languages/lang_fi.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_fi.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "Tallenna automaattisesti luettelo kaikista asennetuista paketeista, jotta voit palauttaa ne helposti.", "Automatically save a list of your installed packages on your computer." : "Tallenna automaattisesti luettelo asennetuista paketeista tietokoneellesi.", "Autostart WingetUI in the notifications area" : "Käynnistä WingetUI automaattisesti ilmoitusalueella", + "Available Updates" : null, "Available updates: {0}" : "Saatavilla olevat päivitykset: {0},", "Available updates: {0}, not finished yet..." : "Saatavilla olevat päivitykset: {0}, ei vielä valmis...", "Backup" : "Varmuuskopioi", diff --git a/src/UniGetUI/Assets/Languages/lang_fr.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_fr.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_fr.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_fr.json index aaf33d263..3fe3f4339 100644 --- a/src/UniGetUI/Assets/Languages/lang_fr.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_fr.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "Enregistrer automatiquement une liste de tous les paquets installés pour les restaurer facilement.", "Automatically save a list of your installed packages on your computer." : "Enregistrer automatiquement une liste de vos paquets installés sur votre ordinateur.", "Autostart WingetUI in the notifications area" : "Démarrer automatiquement WingetUI dans la zone de notification", + "Available Updates" : null, "Available updates: {0}" : "Mises à jour disponibles : {0}", "Available updates: {0}, not finished yet..." : "Mises à jour disponibles : {0}, pas encore terminé...", "Backup" : "Sauvegarder", diff --git a/src/UniGetUI/Assets/Languages/lang_he.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_he.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_he.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_he.json index ad7cad576..745b1247f 100644 --- a/src/UniGetUI/Assets/Languages/lang_he.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_he.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : null, "Automatically save a list of your installed packages on your computer." : null, "Autostart WingetUI in the notifications area" : "הפעל אוטומטית את WingetUI במרכז ההודעות", + "Available Updates" : null, "Available updates: {0}" : "עדכונים זמינים: {0}", "Available updates: {0}, not finished yet..." : "עדכונים זמינים: {0}, טרם הושלמו...", "Backup" : null, diff --git a/src/UniGetUI/Assets/Languages/lang_hi.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_hi.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_hi.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_hi.json index b3128b2d3..db4d0e35d 100644 --- a/src/UniGetUI/Assets/Languages/lang_hi.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_hi.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : null, "Automatically save a list of your installed packages on your computer." : null, "Autostart WingetUI in the notifications area" : "सूचना क्षेत्र में WingetUI ऑटोस्टार्ट करें", + "Available Updates" : null, "Available updates: {0}" : "उपलब्ध अपडेट: {0}", "Available updates: {0}, not finished yet..." : "\nउपलब्ध अपडेट: {0}, अभी तक समाप्त नहीं हुआ...", "Backup" : null, diff --git a/src/UniGetUI/Assets/Languages/lang_hr.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_hr.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_hr.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_hr.json index 7638663fa..029e83c47 100644 --- a/src/UniGetUI/Assets/Languages/lang_hr.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_hr.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : null, "Automatically save a list of your installed packages on your computer." : null, "Autostart WingetUI in the notifications area" : "Automatski pokrenite WingetUI u području obavijesti", + "Available Updates" : null, "Available updates: {0}" : "Dostupna ažuriranja: {0}", "Available updates: {0}, not finished yet..." : "Dostupna ažuriranja: {0}, još nisu završena...", "Backup" : null, diff --git a/src/UniGetUI/Assets/Languages/lang_hu.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_hu.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_hu.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_hu.json index cdf382c7d..a504ccca3 100644 --- a/src/UniGetUI/Assets/Languages/lang_hu.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_hu.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "Autom. elmenti az összes telepített csomag listáját, hogy könnyen visszaállíthassa azokat.", "Automatically save a list of your installed packages on your computer." : "Automatikusan elmenti a telepített csomagok listáját a számítógépen.", "Autostart WingetUI in the notifications area" : "A WingetUI auto. indítása az értesítési területen", + "Available Updates" : null, "Available updates: {0}" : "Elérhető frissítések: {0}", "Available updates: {0}, not finished yet..." : "Elérhető frissítések: {0}, még nincs kész...", "Backup" : "Biztonsági mentés", diff --git a/src/UniGetUI/Assets/Languages/lang_id.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_id.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_id.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_id.json index 41e458cfc..826064431 100644 --- a/src/UniGetUI/Assets/Languages/lang_id.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_id.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "Secara otomatis menyimpan daftar semua paket yang Anda instal untuk memulihkannya dengan mudah.", "Automatically save a list of your installed packages on your computer." : "Secara otomatis menyimpan daftar paket yang Anda instal di komputer Anda.", "Autostart WingetUI in the notifications area" : "Mulai otomatis WingetUI di bagian notifikasi", + "Available Updates" : null, "Available updates: {0}" : "Pembaruan tersedia: {0}", "Available updates: {0}, not finished yet..." : "Pembaruan tersedia: {0}, belum selesai...\n", "Backup" : "Cadangkan", diff --git a/src/UniGetUI/Assets/Languages/lang_it.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_it.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_it.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_it.json index 35946afdd..5fece446d 100644 --- a/src/UniGetUI/Assets/Languages/lang_it.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_it.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "Salva automaticamente un elenco di tutti i pacchetti installati per ripristinarli facilmente.", "Automatically save a list of your installed packages on your computer." : "Salva automaticamente l'elenco dei pacchetti installati sul computer.", "Autostart WingetUI in the notifications area" : "Avvia automaticamente WingetUI nell'area delle notifiche", + "Available Updates" : null, "Available updates: {0}" : "Aggiornamenti disponibili: {0}", "Available updates: {0}, not finished yet..." : "Aggiornamenti disponibili: {0}, ma non ho ancora terminato il controllo...", "Backup" : "Backup", diff --git a/src/UniGetUI/Assets/Languages/lang_ja.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_ja.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_ja.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_ja.json index 095c80264..6ac9c612b 100644 --- a/src/UniGetUI/Assets/Languages/lang_ja.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_ja.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "インストール済みパッケージの一覧を自動的に保存し、簡単に復元できるようにします。", "Automatically save a list of your installed packages on your computer." : "インストール済みパッケージの一覧を自動的にコンピューターに保存する", "Autostart WingetUI in the notifications area" : "WingetUIを通知領域で自動起動する", + "Available Updates" : null, "Available updates: {0}" : "利用可能なアップデート: {0}", "Available updates: {0}, not finished yet..." : "利用可能なアップデート: {0}、進行中...", "Backup" : "バックアップ", diff --git a/src/UniGetUI/Assets/Languages/lang_ko.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_ko.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_ko.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_ko.json index 62e3c3a90..6181295a4 100644 --- a/src/UniGetUI/Assets/Languages/lang_ko.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_ko.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "설치된 모든 패키지 목록을 자동으로 저장하여 쉽게 복원할 수 있습니다.", "Automatically save a list of your installed packages on your computer." : "설치된 패키지 목록을 컴퓨터에 자동으로 저장합니다.", "Autostart WingetUI in the notifications area" : "알림 영역에서 WingetUI 자동 시작", + "Available Updates" : null, "Available updates: {0}" : "사용 가능한 업데이트: {0}", "Available updates: {0}, not finished yet..." : "사용 가능한 업데이트: {0}, 진행 중...", "Backup" : "백업", diff --git a/src/UniGetUI/Assets/Languages/lang_mk.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_mk.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_mk.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_mk.json index cedc53bce..9a66545d4 100644 --- a/src/UniGetUI/Assets/Languages/lang_mk.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_mk.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "Автоматски зачувајте листа со сите ваши инсталирани пакети за лесно да ги вратите.", "Automatically save a list of your installed packages on your computer." : "Автоматски зачувајте листа со инсталираните пакети на вашиот компјутер.", "Autostart WingetUI in the notifications area" : "Автоматско стартување на WingetUI во областа за известувања", + "Available Updates" : null, "Available updates: {0}" : "Достапни ажурирања: {0}", "Available updates: {0}, not finished yet..." : "Достапни ажурирања: {0}, сè уште не е завршено...", "Backup" : null, diff --git a/src/UniGetUI/Assets/Languages/lang_nb.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_nb.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_nb.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_nb.json index 46cc43ca7..a6e71a64d 100644 --- a/src/UniGetUI/Assets/Languages/lang_nb.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_nb.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "Lagre en liste over alle dine installerte pakker automatisk for å forenkle gjenoppretting av dem.", "Automatically save a list of your installed packages on your computer." : "Lagre en liste over dine installerte pakker på datamaskinen din automatisk.", "Autostart WingetUI in the notifications area" : "Start WingetUI automatisk i varselfeltet", + "Available Updates" : null, "Available updates: {0}" : "Tilgjengelige oppdateringer: {0}", "Available updates: {0}, not finished yet..." : "Tilgjengelige oppdateringer: {0}, jobber fortsatt...", "Backup" : "Ta backup", diff --git a/src/UniGetUI/Assets/Languages/lang_nl.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_nl.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_nl.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_nl.json index 85f4376eb..16a1785a1 100644 --- a/src/UniGetUI/Assets/Languages/lang_nl.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_nl.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "Automatisch een lijst opslaan van al je geïnstalleerde pakketten om ze gemakkelijk te herstellen.", "Automatically save a list of your installed packages on your computer." : "Automatisch een lijst opslaan met de op je computer geïnstalleerde pakketten.", "Autostart WingetUI in the notifications area" : "WingetUI automatisch starten in het systeemvak", + "Available Updates" : null, "Available updates: {0}" : "Beschikbare updates: {0}", "Available updates: {0}, not finished yet..." : "Beschikbare updates: {0}, bijna klaar…", "Backup" : "Back-up", diff --git a/src/UniGetUI/Assets/Languages/lang_nn.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_nn.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_nn.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_nn.json index 7e08754c8..cbbbc484d 100644 --- a/src/UniGetUI/Assets/Languages/lang_nn.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_nn.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "Lagre ei liste over alle dine installerte pakkar automatisk for å forenkle gjenoppretting av dei.", "Automatically save a list of your installed packages on your computer." : "Lagre ei liste over dine installerte pakkar på datamaskina di automatisk", "Autostart WingetUI in the notifications area" : "Start WingetUI automatisk i varslingsfeltet", + "Available Updates" : null, "Available updates: {0}" : "Tilgjengelege oppdateringar: {0}", "Available updates: {0}, not finished yet..." : "Tilgjengelege oppdateringar: {0}, jobbar framleis...", "Backup" : "Ta backup", diff --git a/src/UniGetUI/Assets/Languages/lang_pl.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_pl.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_pl.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_pl.json index ee5a8ed6d..2492e202b 100644 --- a/src/UniGetUI/Assets/Languages/lang_pl.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_pl.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "Automatyczne zapisywanie listy wszystkich zainstalowanych pakietów w celu ich łatwego przywrócenia.", "Automatically save a list of your installed packages on your computer." : "Automatycznie zapisz listę zainstalowanych pakietów na komputerze.", "Autostart WingetUI in the notifications area" : "Autostart WingetUI w obszarze powiadomień", + "Available Updates" : null, "Available updates: {0}" : "Dostępne aktualizacje: {0}", "Available updates: {0}, not finished yet..." : "Dostępne aktualizacje: {0}, jeszcze szukam...", "Backup" : "Zrób kopię", diff --git a/src/UniGetUI/Assets/Languages/lang_pt_BR.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_pt_BR.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_pt_BR.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_pt_BR.json index 0ce98465a..620a61bcd 100644 --- a/src/UniGetUI/Assets/Languages/lang_pt_BR.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_pt_BR.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "Salve automaticamente uma lista de todos os seus pacotes instalados para restaurá-los facilmente.", "Automatically save a list of your installed packages on your computer." : "Salvar automaticamente uma lista dos pacotes instalados no seu computador.", "Autostart WingetUI in the notifications area" : "Iniciar o WingetUI na área de notificações", + "Available Updates" : null, "Available updates: {0}" : "Atualizações disponíveis: {0} ", "Available updates: {0}, not finished yet..." : "Atualizações disponíveis: {0}, ainda não terminou...", "Backup" : "Backup", diff --git a/src/UniGetUI/Assets/Languages/lang_pt_PT.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_pt_PT.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_pt_PT.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_pt_PT.json index ad02b7001..f248b7411 100644 --- a/src/UniGetUI/Assets/Languages/lang_pt_PT.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_pt_PT.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "Guardar automaticamente uma lista de todos os pacotes instalados para ser mas fácil reinstalá-los.", "Automatically save a list of your installed packages on your computer." : "Guardar automaticamente uma lista dos pacotes instalados no teu computador.", "Autostart WingetUI in the notifications area" : "Iniciar automaticamente o WingetUI para a área de notificações", + "Available Updates" : null, "Available updates: {0}" : "Atualizações disponíveis: {0} ", "Available updates: {0}, not finished yet..." : "Atualizações disponíveis: {0}, ainda não terminou...", "Backup" : "Cópia de segurança", diff --git a/src/UniGetUI/Assets/Languages/lang_ro.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_ro.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_ro.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_ro.json index 5aab22e19..b7cf3efd0 100644 --- a/src/UniGetUI/Assets/Languages/lang_ro.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_ro.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "Salvează automat o listă a tuturor pachetelor instalate pentru a putea să le restaurezi cu ușurință", "Automatically save a list of your installed packages on your computer." : "Salvează automat o listă a pachetelor instalate pe calculatorul tău.", "Autostart WingetUI in the notifications area" : "Pornește automat WingetUI în zona de notificări", + "Available Updates" : null, "Available updates: {0}" : "Actualizări disponibile: {0}", "Available updates: {0}, not finished yet..." : "Actualizări disponibile: {0}, căutare în curs...", "Backup" : "Fă o copie de siguranță", diff --git a/src/UniGetUI/Assets/Languages/lang_ru.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_ru.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_ru.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_ru.json index a10fd9bcf..d1d9b9297 100644 --- a/src/UniGetUI/Assets/Languages/lang_ru.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_ru.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "Автоматически сохраняйте список всех установленных вами пакетов, чтобы легко восстановить их.", "Automatically save a list of your installed packages on your computer." : "Автоматическое сохранение списка установленных пакетов на вашем компьютере.", "Autostart WingetUI in the notifications area" : "Автозапуск WingetUI в области уведомлений", + "Available Updates" : null, "Available updates: {0}" : "Доступно обновлений: {0}", "Available updates: {0}, not finished yet..." : "Доступно обновлений: {0}, еще не завершено...", "Backup" : "Сделать копию", diff --git a/src/UniGetUI/Assets/Languages/lang_si.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_si.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_si.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_si.json index 68a70e61a..85dad01f5 100644 --- a/src/UniGetUI/Assets/Languages/lang_si.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_si.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : null, "Automatically save a list of your installed packages on your computer." : null, "Autostart WingetUI in the notifications area" : null, + "Available Updates" : null, "Available updates: {0}" : null, "Available updates: {0}, not finished yet..." : null, "Backup" : null, diff --git a/src/UniGetUI/Assets/Languages/lang_sl.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_sl.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_sl.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_sl.json index 9863ae79f..bf1b41093 100644 --- a/src/UniGetUI/Assets/Languages/lang_sl.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_sl.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "Samodejno shranite seznam vseh nameščenih paketov, da jih preprosto obnovite.", "Automatically save a list of your installed packages on your computer." : "Samodejno shranite seznam svojih nameščenih paketov v svoj računalnik.", "Autostart WingetUI in the notifications area" : "Samodejni zagon WingetUI v območje za obvestila", + "Available Updates" : null, "Available updates: {0}" : "Razpoložljive posodobitve: {0}", "Available updates: {0}, not finished yet..." : "Razpoložljive posodobitve: {0}, še nedokončane...", "Backup" : "Varnostna kopija", diff --git a/src/UniGetUI/Assets/Languages/lang_sq.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_sq.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_sq.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_sq.json index b72b5da78..4ddc9befe 100644 --- a/src/UniGetUI/Assets/Languages/lang_sq.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_sq.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "Ruaj automatikisht një listë të të gjitha paketave tuaja të instaluara për t'i rikthyer ato lehtësisht.", "Automatically save a list of your installed packages on your computer." : "Ruaj automatikisht një listë të paketave tuaja të instaluara në kompjuterin tënd.", "Autostart WingetUI in the notifications area" : "Nis automatikisht WingetUI në hapësirën e njoftimeve", + "Available Updates" : null, "Available updates: {0}" : "Përditësimet e ofruara: {0}", "Available updates: {0}, not finished yet..." : "Përditësimet e ofruara: {0}, nuk ka përfunduar ende...", "Backup" : "Bëj një kopje rezervë", diff --git a/src/UniGetUI/Assets/Languages/lang_sr.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_sr.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_sr.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_sr.json index fb3d83b10..9d3ef0c72 100644 --- a/src/UniGetUI/Assets/Languages/lang_sr.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_sr.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "Аутоматски сачувај листу свих инсталираних пакета због лакшег враћања.", "Automatically save a list of your installed packages on your computer." : "Аутоматски сачувај листу инсталираних пакета на рачунару.", "Autostart WingetUI in the notifications area" : "Аутоматски покрени WingetUI међу нотификацијама", + "Available Updates" : null, "Available updates: {0}" : "Доступне надоградње: {0}", "Available updates: {0}, not finished yet..." : "Доспутне надоградње: {0}, није још завршено...", "Backup" : null, diff --git a/src/UniGetUI/Assets/Languages/lang_sv.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_sv.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_sv.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_sv.json index 4132720ad..67b23261e 100644 --- a/src/UniGetUI/Assets/Languages/lang_sv.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_sv.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : null, "Automatically save a list of your installed packages on your computer." : null, "Autostart WingetUI in the notifications area" : null, + "Available Updates" : null, "Available updates: {0}" : null, "Available updates: {0}, not finished yet..." : null, "Backup" : null, diff --git a/src/UniGetUI/Assets/Languages/lang_tg.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_tg.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_tg.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_tg.json index bdff7e1f5..fb05f73a4 100644 --- a/src/UniGetUI/Assets/Languages/lang_tg.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_tg.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : null, "Automatically save a list of your installed packages on your computer." : null, "Autostart WingetUI in the notifications area" : "Laging buksan ang WingetUI sa lugar ng mga abiso", + "Available Updates" : null, "Available updates: {0}" : "Mga maaaring i-update: {0}", "Available updates: {0}, not finished yet..." : "Mga maaaring i-update: {0}, hindi pa nakakatapos ang iba...", "Backup" : null, diff --git a/src/UniGetUI/Assets/Languages/lang_th.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_th.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_th.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_th.json index 025979136..5081faf4d 100644 --- a/src/UniGetUI/Assets/Languages/lang_th.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_th.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "บันทึกรายการแพ็กเกจทั้งหมดที่ติดตั้งไว้โดยอัตโนมัติเพื่อทำให้การกู้คืนง่ายขึ้น", "Automatically save a list of your installed packages on your computer." : "บันทึกรายการแพ็กเกจที่ติดตั้งบนคอมพิวเตอร์ของคุณโดยอัตโนมัติ", "Autostart WingetUI in the notifications area" : "เริ่ม WingetUI อัตโนมัติในถาดการแจ้งเตือน", + "Available Updates" : null, "Available updates: {0}" : "อัปเดตที่พบ: {0}", "Available updates: {0}, not finished yet..." : "อัปเดตที่พบ: {0} และยังไม่เสร็จ...", "Backup" : "สำรองข้อมูล", diff --git a/src/UniGetUI/Assets/Languages/lang_tr.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_tr.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_tr.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_tr.json index cffbf3bcc..f3998b59c 100644 --- a/src/UniGetUI/Assets/Languages/lang_tr.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_tr.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "Kolayca geri yüklemek için yüklü tüm paketlerinizin bir listesini otomatik olarak kaydedin.", "Automatically save a list of your installed packages on your computer." : "Yüklü paketlerinizin bir listesini otomatik olarak bilgisayarınıza kaydedin.", "Autostart WingetUI in the notifications area" : "WingetUI'yi bildirim alanında otomatik başlat", + "Available Updates" : null, "Available updates: {0}" : "Mevcut güncellemeler : {0}", "Available updates: {0}, not finished yet..." : "Mevcut güncellemeler : {0}, henüz bitmedi...", "Backup" : "Yedekle", diff --git a/src/UniGetUI/Assets/Languages/lang_ua.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_ua.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_ua.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_ua.json index 0f5dba1ee..7b5671bc4 100644 --- a/src/UniGetUI/Assets/Languages/lang_ua.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_ua.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : null, "Automatically save a list of your installed packages on your computer." : null, "Autostart WingetUI in the notifications area" : "Автозапуск WingetUI в області сповіщень", + "Available Updates" : null, "Available updates: {0}" : "Доступні оновлення: {0}", "Available updates: {0}, not finished yet..." : "Доступні оновлення: {0}, ще не завершено...", "Backup" : null, diff --git a/src/UniGetUI/Assets/Languages/lang_ur.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_ur.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_ur.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_ur.json index 0759cd34d..7384d081b 100644 --- a/src/UniGetUI/Assets/Languages/lang_ur.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_ur.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : null, "Automatically save a list of your installed packages on your computer." : null, "Autostart WingetUI in the notifications area" : null, + "Available Updates" : null, "Available updates: {0}" : null, "Available updates: {0}, not finished yet..." : null, "Backup" : null, diff --git a/src/UniGetUI/Assets/Languages/lang_vi.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_vi.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_vi.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_vi.json index b2b6e9fbe..5a5ef11ef 100644 --- a/src/UniGetUI/Assets/Languages/lang_vi.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_vi.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "Tự động lưu một danh sách tất cả các gói bạn đã cài đặt để dễ dàng khôi phục chúng sau này.", "Automatically save a list of your installed packages on your computer." : "Tự động lưu danh sách những gói đã cài đặt trên máy tính của bạn.", "Autostart WingetUI in the notifications area" : "Tự động chạy WingetUI trong khu vực thông báo", + "Available Updates" : null, "Available updates: {0}" : "Cập nhật khả dụng: {0}", "Available updates: {0}, not finished yet..." : "Cập nhật khả dụng: {0}, chưa hoàn tất...", "Backup" : "Sao lưu", diff --git a/src/UniGetUI/Assets/Languages/lang_zh_CN.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_zh_CN.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_zh_CN.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_zh_CN.json index 239d4aba2..4d0e1cfd5 100644 --- a/src/UniGetUI/Assets/Languages/lang_zh_CN.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_zh_CN.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "自动保存所有已安装软件的列表以便于恢复它们。", "Automatically save a list of your installed packages on your computer." : "在您的计算机上自动保存所有已安装软件包的列表。", "Autostart WingetUI in the notifications area" : "开机自动启动 WingetUI 并最小化到任务栏", + "Available Updates" : null, "Available updates: {0}" : "可用更新:{0}", "Available updates: {0}, not finished yet..." : "可用更新:{0},仍在进行中...", "Backup" : "备份", diff --git a/src/UniGetUI/Assets/Languages/lang_zh_TW.json b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_zh_TW.json similarity index 99% rename from src/UniGetUI/Assets/Languages/lang_zh_TW.json rename to src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_zh_TW.json index c6eb3b2f4..158d0e876 100644 --- a/src/UniGetUI/Assets/Languages/lang_zh_TW.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Languages/lang_zh_TW.json @@ -59,6 +59,7 @@ "Automatically save a list of all your installed packages to easily restore them." : "自動儲存已安裝的套件清單以便輕鬆還原它們。", "Automatically save a list of your installed packages on your computer." : "將已安裝的套件清單自動儲存到您的電腦。", "Autostart WingetUI in the notifications area" : "開機時啟動 WingetUI 並顯示系統匣圖示", + "Available Updates" : null, "Available updates: {0}" : "{0} 個可用的更新", "Available updates: {0}, not finished yet..." : "已發現 {0} 個可用的更新,仍在繼續進行中...", "Backup" : "備份", diff --git a/src/UniGetUI.Core.LanguageEngine/LanguageData.cs b/src/UniGetUI.Core.LanguageEngine/LanguageData.cs new file mode 100644 index 000000000..f05952ea4 --- /dev/null +++ b/src/UniGetUI.Core.LanguageEngine/LanguageData.cs @@ -0,0 +1,154 @@ +using System; +using System.Runtime.InteropServices; +using System.Text.Json.Nodes; +using UniGetUI.Core.Data; +using UniGetUI.Core.Logging; +using UniGetUI.PackageEngine.Enums; +using UniGetUI.Core.Classes; +using System.Collections.ObjectModel; + +namespace UniGetUI.Core.Language +{ + public static class LanguageData + { + /// + /// Returns + /// + private static Person[]? __translators_list; + + public static Person[] TranslatorsList + { + get + { + if (__translators_list == null) + __translators_list = LoadLanguageTranslatorList(); + + return __translators_list; + } + } + + private static ReadOnlyDictionary? __language_reference; + public static ReadOnlyDictionary LanguageReference + { + get { + if (__language_reference == null) + __language_reference = LoadLanguageReference(); + return __language_reference; + } + } + + private static ReadOnlyDictionary? __translation_percentages; + public static ReadOnlyDictionary TranslationPercentages + { + get + { + if (__translation_percentages == null) + __translation_percentages = LoadTranslationPercentages(); + return __translation_percentages; + } + } + + private static ReadOnlyDictionary LoadTranslationPercentages() + { + JsonObject? val = JsonObject.Parse(File.ReadAllText(Path.Join(CoreData.UniGetUIExecutableDirectory, "Assets", "Data", "TranslatedPercentages.json"))) as JsonObject; + if (val != null) + return new(val.ToDictionary(x => x.Key, x => (x.Value ?? ("404%" + x.Key)).ToString())); + else + return new(new Dictionary()); + } + + private static ReadOnlyDictionary LoadLanguageReference() + { + JsonObject? val = JsonObject.Parse(File.ReadAllText(Path.Join(CoreData.UniGetUIExecutableDirectory, "Assets", "Data", "LanguagesReference.json"))) as JsonObject; + if (val != null) + return new(val.ToDictionary(x => x.Key, x => (x.Value ?? ("NoNameLang_" + x.Key)).ToString())); + else + return new(new Dictionary()); + } + + private static Person[] LoadLanguageTranslatorList() + { + var JsonContents = File.ReadAllText(Path.Join(CoreData.UniGetUIExecutableDirectory, "Assets", "Data", "Translators.json")); + JsonObject? TranslatorsInfo = JsonNode.Parse(JsonContents) as JsonObject; + + if (TranslatorsInfo == null) return []; + + List result = new(); + foreach (var langKey in TranslatorsInfo) + { + if (!LanguageReference.ContainsKey(langKey.Key)) + { + Logger.Warn($"Language {langKey.Key} not in list, maybe has not been added yet?"); + continue; + } + + JsonArray TranslatorsForLang = (langKey.Value ?? new JsonArray()).AsArray(); + bool LangShown = false; + foreach (JsonNode? translator in TranslatorsForLang) + { + if (translator is null) continue; + + Uri? url = null; + if (translator["link"] != null && translator["link"]?.ToString() != "") + url = new Uri((translator["link"] ?? "").ToString()); + + Person person = new( + Name: (url != null ? "@" : "") + (translator["name"] ?? "").ToString(), + ProfilePicture: url != null ? new Uri(url.ToString() + ".png") : null, + GitHubUrl: url, + Language: !LangShown ? LanguageData.LanguageReference[langKey.Key] : "" + ); + LangShown = true; + result.Add(person); + } + } + + return result.ToArray(); + } + } + + public static class CommonTranslations + { + public static readonly Dictionary ArchNames = new() + { + { Architecture.X64, "x64" }, + { Architecture.X86, "x86" }, + { Architecture.Arm64, "arm64" }, + { Architecture.Arm, "arm32" }, + }; + + public static readonly Dictionary InvertedArchNames = new() + { + { "x64", Architecture.X64 }, + { "x86", Architecture.X86 }, + { "arm64", Architecture.Arm64 }, + { "arm32", Architecture.Arm }, + }; + + public static readonly Dictionary ScopeNames = new() + { + { PackageScope.Global, "Machine | Global" }, + { PackageScope.Local, "User | Local" }, + }; + + public static readonly Dictionary InvertedScopeNames = new() + { + { "Machine | Global", PackageScope.Global }, + { "User | Local", PackageScope.Local }, + }; + + public static readonly Dictionary ScopeNames_NonLang = new() + { + { PackageScope.Global, "mac" + + "hine" }, + { PackageScope.Local, "user" }, + }; + + public static readonly Dictionary InvertedScopeNames_NonLang = new() + { + { "machine", PackageScope.Global }, + { "user", PackageScope.Local }, + }; + } +} + diff --git a/src/UniGetUI.Core.LanguageEngine/LanguageEngine.cs b/src/UniGetUI.Core.LanguageEngine/LanguageEngine.cs new file mode 100644 index 000000000..bccf0a302 --- /dev/null +++ b/src/UniGetUI.Core.LanguageEngine/LanguageEngine.cs @@ -0,0 +1,152 @@ +using System.Text.Json.Nodes; +using UniGetUI.Core.Data; +using UniGetUI.Core.Logging; +using UniGetUI.Core.SettingsEngine; +using UniGetUI.PackageEngine.Enums; + +namespace UniGetUI.Core.Language +{ + public class LanguageEngine + { + private Dictionary MainLangDict = new(); + + public LanguageEngine(string ForceLanguage = "") + { + string LangName = Settings.GetValue("PreferredLanguage"); + if (LangName == "default" || LangName == "") + { + LangName = System.Globalization.CultureInfo.CurrentCulture.ToString().Replace("-", "_"); + } + LoadLanguage((ForceLanguage != "")? ForceLanguage: LangName); + } + + /// + /// Loads the specified language into the current instance + /// + /// the language code + public void LoadLanguage(string lang) + { + if (LanguageData.LanguageReference.ContainsKey(lang)) + { + MainLangDict = LoadLanguageFile(lang); + MainLangDict.TryAdd("locale", lang); + } + else if (LanguageData.LanguageReference.ContainsKey(lang[0..2])) + { + MainLangDict = LoadLanguageFile(lang[0..2]); + MainLangDict.TryAdd("locale", lang[0..2]); + } + else + { + MainLangDict = LoadLanguageFile("en"); + MainLangDict.TryAdd("locale", "en"); + } + LoadStaticTranslation(); + Logger.Info("Loaded language locale: " + MainLangDict["locale"]); + } + + public Dictionary LoadLanguageFile(string LangKey, bool ForceBundled = false) + { + try + { + Dictionary LangDict = new(); + string LangFileToLoad = Path.Join(CoreData.UniGetUICacheDirectory_Lang, "lang_" + LangKey + ".json"); + + if (!File.Exists(LangFileToLoad) || Settings.Get("DisableLangAutoUpdater")) + { + ForceBundled = true; + } + + if (ForceBundled) + { + LangFileToLoad = Path.Join(CoreData.UniGetUIExecutableDirectory, "Assets", "Languages", "lang_" + LangKey + ".json"); + } + + var __LangDict = (JsonNode.Parse(File.ReadAllText(LangFileToLoad)) as JsonObject)?.ToDictionary(x => x.Key, x => x.Value != null ? x.Value.ToString() : ""); + + if (__LangDict != null) + LangDict = __LangDict; + else + Logger.Error($"Deserialization of language file {LangFileToLoad} resulted in a null object"); + + if (!Settings.Get("DisableLangAutoUpdater")) + _ = DownloadUpdatedLanguageFile(LangKey); + + return LangDict; + } + catch (Exception e) + { + Logger.Error($"LoadLanguageFile Failed for LangKey={LangKey}, ForceBundled={ForceBundled}"); + Logger.Error(e); + return new Dictionary(); + } + } + + /// + /// Downloads and saves an updated version of the translations for the specified language. + /// + /// The Id of the language to download + /// Use the new or the old Url (should not be used manually) + /// + public async Task DownloadUpdatedLanguageFile(string LangKey, bool UseOldUrl = false) + { + try + { + Uri NewFile = new("https://raw.githubusercontent.com/marticliment/WingetUI/main/src/" + (UseOldUrl ? "wingetui" : "UniGetUI") + "/Assets/Languages/lang_" + LangKey + ".json"); + + HttpClient client = new(); + string fileContents = await client.GetStringAsync(NewFile); + + if (!Directory.Exists(CoreData.UniGetUICacheDirectory_Lang)) + Directory.CreateDirectory(CoreData.UniGetUICacheDirectory_Lang); + + File.WriteAllText(Path.Join(CoreData.UniGetUICacheDirectory_Lang, "lang_" + LangKey + ".json"), fileContents); + + Logger.ImportantInfo("Lang files were updated successfully from GitHub"); + } + catch (Exception e) + { + if (e is HttpRequestException && !UseOldUrl) + await DownloadUpdatedLanguageFile(LangKey, true); + else + { + Logger.Warn("Could not download updated translations from GitHub"); + Logger.Warn(e); + } + } + } + + public void LoadStaticTranslation() + { + CommonTranslations.ScopeNames[PackageScope.Local] = Translate("User | Local"); + CommonTranslations.ScopeNames[PackageScope.Global] = Translate("Machine | Global"); + + CommonTranslations.InvertedScopeNames.Clear(); + CommonTranslations.InvertedScopeNames.Add(Translate("Machine | Global"), PackageScope.Global); + CommonTranslations.InvertedScopeNames.Add(Translate("User | Local"), PackageScope.Local); + } + + public string Translate(string key) + { + if (key == "WingetUI") + { + if (MainLangDict.ContainsKey("formerly WingetUI") && MainLangDict["formerly WingetUI"] != "") + return "UniGetUI (" + MainLangDict["formerly WingetUI"] + ")"; + return "UniGetUI (formerly WingetUI)"; + } + else if (key == "Formerly known as WingetUI") + { + if (MainLangDict.ContainsKey(key)) + return MainLangDict[key]; + return key; + } + + if (key == null || key == "") + return ""; + else if (MainLangDict.ContainsKey(key) && MainLangDict[key] != "") + return MainLangDict[key].Replace("WingetUI", "UniGetUI"); + else + return key.Replace("WingetUI", "UniGetUI"); + } + } +} diff --git a/src/UniGetUI.Core.LanguageEngine/UniGetUI.Core.LanguageEngine.csproj b/src/UniGetUI.Core.LanguageEngine/UniGetUI.Core.LanguageEngine.csproj new file mode 100644 index 000000000..5f08487d6 --- /dev/null +++ b/src/UniGetUI.Core.LanguageEngine/UniGetUI.Core.LanguageEngine.csproj @@ -0,0 +1,86 @@ + + + + + + enable + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + + + + + + + + + PreserveNewest + + + + diff --git a/src/UniGetUI.Core.Logger/LogEntry.cs b/src/UniGetUI.Core.Logger/LogEntry.cs new file mode 100644 index 000000000..25dba2fff --- /dev/null +++ b/src/UniGetUI.Core.Logger/LogEntry.cs @@ -0,0 +1,26 @@ +namespace UniGetUI.Core.Logging +{ + public readonly struct LogEntry + { + public enum SeverityLevel + { + Debug, + Info, + Success, + Warning, + Error, + } + + public readonly DateTime Time { get; } + public readonly String Content { get; } + public readonly SeverityLevel Severity { get; } + + public LogEntry(string content, SeverityLevel severity) + { + Time = DateTime.Now; + Content = content; + Severity = severity; + } + + } +} diff --git a/src/UniGetUI.Core.Logger/Logger.cs b/src/UniGetUI.Core.Logger/Logger.cs new file mode 100644 index 000000000..27b8ee172 --- /dev/null +++ b/src/UniGetUI.Core.Logger/Logger.cs @@ -0,0 +1,78 @@ +using Diagnostics = System.Diagnostics; + +namespace UniGetUI.Core.Logging +{ + public static class Logger + { + private static readonly List LogContents = new(); + + // String parameter log functions + public static void ImportantInfo(string s) + { + Diagnostics.Debug.WriteLine(s); + LogContents.Add(new LogEntry(s, LogEntry.SeverityLevel.Success)); + } + + public static void Debug(string s) + { + Diagnostics.Debug.WriteLine(s); + LogContents.Add(new LogEntry(s, LogEntry.SeverityLevel.Debug)); + } + + public static void Info(string s) + { + Diagnostics.Debug.WriteLine(s); + LogContents.Add(new LogEntry(s, LogEntry.SeverityLevel.Info)); + } + + public static void Warn(string s) + { + Diagnostics.Debug.WriteLine(s); + LogContents.Add(new LogEntry(s, LogEntry.SeverityLevel.Warning)); + } + + public static void Error(string s) + { + Diagnostics.Debug.WriteLine(s); + LogContents.Add(new LogEntry(s, LogEntry.SeverityLevel.Error)); + } + + + // Exception parameter log functions + public static void ImportantInfo(Exception e) + { + Diagnostics.Debug.WriteLine(e.ToString()); + LogContents.Add(new LogEntry(e.ToString() , LogEntry.SeverityLevel.Success)); + } + + public static void Debug(Exception e) + { + Diagnostics.Debug.WriteLine(e.ToString()); + LogContents.Add(new LogEntry(e.ToString(), LogEntry.SeverityLevel.Debug)); + } + + public static void Info(Exception e) + { + Diagnostics.Debug.WriteLine(e.ToString()); + LogContents.Add(new LogEntry(e.ToString(), LogEntry.SeverityLevel.Info)); + } + + public static void Warn(Exception e) + { + Diagnostics.Debug.WriteLine(e.ToString()); + LogContents.Add(new LogEntry(e.ToString(), LogEntry.SeverityLevel.Warning)); + } + + public static void Error(Exception e) + { + Diagnostics.Debug.WriteLine(e.ToString()); + LogContents.Add(new LogEntry(e.ToString(), LogEntry.SeverityLevel.Error)); + } + + + public static LogEntry[] GetLogs() + { + return LogContents.ToArray(); + } + } +} diff --git a/src/UniGetUI.Core.Logger/UniGetUI.Core.Logging.csproj b/src/UniGetUI.Core.Logger/UniGetUI.Core.Logging.csproj new file mode 100644 index 000000000..1897680a2 --- /dev/null +++ b/src/UniGetUI.Core.Logger/UniGetUI.Core.Logging.csproj @@ -0,0 +1,9 @@ + + + + + + enable + + + diff --git a/src/UniGetUI.Core.Logging.Tests/LogEntryTests.cs b/src/UniGetUI.Core.Logging.Tests/LogEntryTests.cs new file mode 100644 index 000000000..ae29196c7 --- /dev/null +++ b/src/UniGetUI.Core.Logging.Tests/LogEntryTests.cs @@ -0,0 +1,37 @@ +namespace UniGetUI.Core.Logging.Tests +{ + public class LogEntryTests + { + [Fact] + public async Task TestLogEntry() + { + var startTime = DateTime.Now; + + await Task.Delay(100); + + var logEntry1 = new LogEntry("Hello World", LogEntry.SeverityLevel.Info); + await Task.Delay(50); + var logEntry2 = new LogEntry("Hello World 2", LogEntry.SeverityLevel.Debug); + await Task.Delay(50); + var logEntry3 = new LogEntry("Hello World 3", LogEntry.SeverityLevel.Error); + + await Task.Delay(100); + + var endTime = DateTime.Now; + + Assert.Equal("Hello World", logEntry1.Content); + Assert.Equal("Hello World 2", logEntry2.Content); + Assert.Equal("Hello World 3", logEntry3.Content); + + Assert.Equal(LogEntry.SeverityLevel.Info, logEntry1.Severity); + Assert.Equal(LogEntry.SeverityLevel.Debug, logEntry2.Severity); + Assert.Equal(LogEntry.SeverityLevel.Error, logEntry3.Severity); + + Assert.True(logEntry1.Time > startTime && logEntry1.Time < endTime); + Assert.True(logEntry2.Time > startTime && logEntry2.Time < endTime); + Assert.True(logEntry3.Time > startTime && logEntry3.Time < endTime); + Assert.True(logEntry1.Time < logEntry2.Time); + Assert.True(logEntry2.Time < logEntry3.Time); + } + } +} \ No newline at end of file diff --git a/src/UniGetUI.Core.Logging.Tests/LoggerTests.cs b/src/UniGetUI.Core.Logging.Tests/LoggerTests.cs new file mode 100644 index 000000000..4eb16996c --- /dev/null +++ b/src/UniGetUI.Core.Logging.Tests/LoggerTests.cs @@ -0,0 +1,34 @@ +namespace UniGetUI.Core.Logging.Tests +{ + public class LoggerTests + { + [Fact] + public void TestLogger() + { + var startTime = DateTime.Now; + Logger.Info("Hello World"); + Logger.Debug("Hello World 2"); + Logger.Error("Hello World 3"); + Logger.Warn(new Exception("Test exception")); + + var endTime = DateTime.Now; + + var logs = Logger.GetLogs(); + + Assert.Equal("Hello World", logs[0].Content); + Assert.Equal("Hello World 2", logs[1].Content); + Assert.Equal("Hello World 3", logs[2].Content); + Assert.Equal("System.Exception: Test exception", logs[3].Content); + + Assert.Equal(LogEntry.SeverityLevel.Info, logs[0].Severity); + Assert.Equal(LogEntry.SeverityLevel.Debug, logs[1].Severity); + Assert.Equal(LogEntry.SeverityLevel.Error, logs[2].Severity); + Assert.Equal(LogEntry.SeverityLevel.Warning, logs[3].Severity); + + Assert.True(logs[0].Time > startTime && logs[0].Time < endTime); + Assert.True(logs[1].Time > startTime && logs[1].Time < endTime); + Assert.True(logs[2].Time > startTime && logs[2].Time < endTime); + Assert.True(logs[3].Time > startTime && logs[3].Time < endTime); + } + } +} diff --git a/src/UniGetUI.Core.Logging.Tests/UniGetUI.Core.Logging.Tests.csproj b/src/UniGetUI.Core.Logging.Tests/UniGetUI.Core.Logging.Tests.csproj new file mode 100644 index 000000000..95886c86c --- /dev/null +++ b/src/UniGetUI.Core.Logging.Tests/UniGetUI.Core.Logging.Tests.csproj @@ -0,0 +1,33 @@ + + + + + + enable + false + true + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + diff --git a/src/UniGetUI.Core.Settings.Tests/SettingsTest.cs b/src/UniGetUI.Core.Settings.Tests/SettingsTest.cs new file mode 100644 index 000000000..7bb3884a8 --- /dev/null +++ b/src/UniGetUI.Core.Settings.Tests/SettingsTest.cs @@ -0,0 +1,68 @@ +using UniGetUI.Core.Data; +using UniGetUI.Core.SettingsEngine; + +namespace UniGetUI.Core.SettingsEgine.Tests +{ + public class SettingsTest + { + [Theory] + [InlineData("TestSetting1", true, false, false, true)] + [InlineData("TestSetting2", true, false, false, false)] + [InlineData("Test.Settings_with", true, false, true, true)] + [InlineData("TestSettingEntry With A Space", false, true, false, false)] + [InlineData("ª", false, false, false, false)] + [InlineData("VeryVeryLongTestSettingEntrySoTheClassCanReallyBeStressedOut", true, false, true, true)] + public void TestBooleanSettings(string SettingName, bool st1, bool st2, bool st3, bool st4) + { + Settings.Set(SettingName, st1); + Assert.Equal(st1, Settings.Get(SettingName)); + Assert.Equal(st1, File.Exists(Path.Join(CoreData.UniGetUIDataDirectory, SettingName))); + + Settings.Set(SettingName, st2); + Assert.Equal(st2, Settings.Get(SettingName)); + Assert.Equal(st2, File.Exists(Path.Join(CoreData.UniGetUIDataDirectory, SettingName))); + + Settings.Set(SettingName, st3); + Assert.Equal(st3, Settings.Get(SettingName)); + Assert.Equal(st3, File.Exists(Path.Join(CoreData.UniGetUIDataDirectory, SettingName))); + + Settings.Set(SettingName, st4); + Assert.Equal(st4, Settings.Get(SettingName)); + Assert.Equal(st4, File.Exists(Path.Join(CoreData.UniGetUIDataDirectory, SettingName))); + + Settings.Set(SettingName, false); // Cleanup + Assert.False(Settings.Get(SettingName)); + Assert.False(File.Exists(Path.Join(CoreData.UniGetUIDataDirectory, SettingName))); + } + + [Theory] + [InlineData("TestSetting1", "RandomFirstValue", "RandomSecondValue", "", "RandomThirdValue")] + [InlineData("ktjgshdfsd", "", "", "", "RandomThirdValue")] + [InlineData("ª", "RandomFirstValue", " ", "\t", "RandomThirdValue")] + [InlineData("TestSettingEntry With A Space", "RandomFirstValue", "", "", "")] + [InlineData("VeryVeryLongTestSettingEntrySoTheClassCanReallyBeStressedOut", "\x00\x01\x02\u0003", "", "", "RandomThirdValue")] + public void TestValueSettings(string SettingName, string st1, string st2, string st3, string st4) + { + Settings.SetValue(SettingName, st1); + Assert.Equal(st1, Settings.GetValue(SettingName)); + Assert.Equal((st1 != ""), File.Exists(Path.Join(CoreData.UniGetUIDataDirectory, SettingName))); + + Settings.SetValue(SettingName, st2); + Assert.Equal(st2, Settings.GetValue(SettingName)); + Assert.Equal((st2 != ""), File.Exists(Path.Join(CoreData.UniGetUIDataDirectory, SettingName))); + + Settings.SetValue(SettingName, st3); + Assert.Equal(st3, Settings.GetValue(SettingName)); + Assert.Equal((st3 != ""), File.Exists(Path.Join(CoreData.UniGetUIDataDirectory, SettingName))); + + Settings.SetValue(SettingName, st4); + Assert.Equal(st4, Settings.GetValue(SettingName)); + Assert.Equal((st4 != ""), File.Exists(Path.Join(CoreData.UniGetUIDataDirectory, SettingName))); + + Settings.Set(SettingName, false); // Cleanup + Assert.False(Settings.Get(SettingName)); + Assert.Equal("", Settings.GetValue(SettingName)); + Assert.False(File.Exists(Path.Join(CoreData.UniGetUIDataDirectory, SettingName))); + } + } +} \ No newline at end of file diff --git a/src/UniGetUI.Core.Settings.Tests/UniGetUI.Core.Settings.Tests.csproj b/src/UniGetUI.Core.Settings.Tests/UniGetUI.Core.Settings.Tests.csproj new file mode 100644 index 000000000..bd8b5a416 --- /dev/null +++ b/src/UniGetUI.Core.Settings.Tests/UniGetUI.Core.Settings.Tests.csproj @@ -0,0 +1,26 @@ + + + + + + enable + false + true + + + + + + + + + + + + + + + + + + diff --git a/src/UniGetUI.Core.Settings/SettingsEngine.cs b/src/UniGetUI.Core.Settings/SettingsEngine.cs new file mode 100644 index 000000000..328b6d5fc --- /dev/null +++ b/src/UniGetUI.Core.Settings/SettingsEngine.cs @@ -0,0 +1,58 @@ +using UniGetUI.Core.Data; +using UniGetUI.Core.Logging; + +namespace UniGetUI.Core.SettingsEngine +{ + public static class Settings + { + public static bool Get(string setting, bool invert = false) + { + return File.Exists(Path.Join(CoreData.UniGetUIDataDirectory, setting)) ^ invert; + } + + public static void Set(string setting, bool value) + { + try + { + if (value) + { + if (!File.Exists(Path.Join(CoreData.UniGetUIDataDirectory, setting))) + File.WriteAllText(Path.Join(CoreData.UniGetUIDataDirectory, setting), ""); + } + else + { + if (File.Exists(Path.Join(CoreData.UniGetUIDataDirectory, setting))) + File.Delete(Path.Join(CoreData.UniGetUIDataDirectory, setting)); + } + } + catch (Exception e) + { + Logger.Error($"CANNOT SET SETTING FOR setting={setting} enabled={value}"); + Logger.Error(e); + } + } + + public static string GetValue(string setting) + { + if (!File.Exists(Path.Join(CoreData.UniGetUIDataDirectory, setting))) + return ""; + return File.ReadAllText(Path.Join(CoreData.UniGetUIDataDirectory, setting)); + } + + public static void SetValue(string setting, string value) + { + try + { + if (value == "") + Set(setting, false); + else + File.WriteAllText(Path.Join(CoreData.UniGetUIDataDirectory, setting), value); + } + catch (Exception e) + { + Logger.Error($"CANNOT SET SETTING VALUE FOR setting={setting} enabled={value}"); + Logger.Error(e); + } + } + } +} diff --git a/src/UniGetUI.Core.Settings/UniGetUI.Core.Settings.csproj b/src/UniGetUI.Core.Settings/UniGetUI.Core.Settings.csproj new file mode 100644 index 000000000..da1d54a30 --- /dev/null +++ b/src/UniGetUI.Core.Settings/UniGetUI.Core.Settings.csproj @@ -0,0 +1,13 @@ + + + + + enable + + + + + + + + diff --git a/src/UniGetUI.Core.Structs/Person.cs b/src/UniGetUI.Core.Structs/Person.cs new file mode 100644 index 000000000..813cc6dac --- /dev/null +++ b/src/UniGetUI.Core.Structs/Person.cs @@ -0,0 +1,24 @@ +using System.Collections.Specialized; + +namespace UniGetUI.Core.Structs +{ + public readonly struct Person + { + public readonly string Name; + public readonly Uri? ProfilePicture; + public readonly Uri? GitHubUrl; + public readonly bool HasPicture; + public readonly bool HasGitHubProfile; + public readonly string Language; + + public Person(string Name, Uri? ProfilePicture = null, Uri? GitHubUrl = null, string Language = "") + { + this.Name = Name; + this.ProfilePicture = ProfilePicture; + this.HasPicture = ProfilePicture != null; + this.GitHubUrl = GitHubUrl; + this.HasPicture = GitHubUrl != null; + this.Language = Language; + } + } +} diff --git a/src/UniGetUI.Core.Structs/UniGetUI.Core.Structs.csproj b/src/UniGetUI.Core.Structs/UniGetUI.Core.Structs.csproj new file mode 100644 index 000000000..fa71b7ae6 --- /dev/null +++ b/src/UniGetUI.Core.Structs/UniGetUI.Core.Structs.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/src/UniGetUI.Core.Tools.Tests/ToolsTests.cs b/src/UniGetUI.Core.Tools.Tests/ToolsTests.cs new file mode 100644 index 000000000..8bf379d41 --- /dev/null +++ b/src/UniGetUI.Core.Tools.Tests/ToolsTests.cs @@ -0,0 +1,120 @@ +using System.Diagnostics; +using UniGetUI.Core.Language; + +namespace UniGetUI.Core.Tools.Tests +{ + public class ToolsTests + { + [Theory] + [InlineData("NonExistentString", false)] + [InlineData(" ", false)] + [InlineData("", false)] + [InlineData("@@", false)] + [InlineData("0 packages found", true)] + [InlineData("Add packages or open an existing bundle", true)] + public void TranslateFunctionTester(string textEntry, bool TranslationExists) + { + var langEngine = new LanguageEngine("fr"); + CoreTools.ReloadLanguageEngineInstance("fr"); + + Assert.Equal(CoreTools.Translate(textEntry), langEngine.Translate(textEntry)); + + if (TranslationExists) + Assert.NotEqual(CoreTools.Translate(textEntry), textEntry); + else + Assert.Equal(CoreTools.Translate(textEntry), textEntry); + + Assert.Equal(CoreTools.AutoTranslated(textEntry), textEntry); + } + + [Fact] + public async Task TestWhichFunctionForExistingFile() + { + var result = await CoreTools.Which("cmd.exe"); + Assert.True(result.Item1); + Assert.True(File.Exists(result.Item2)); + } + + [Fact] + public async Task TestWhichFunctionForNonExistingFile() + { + var result = await CoreTools.Which("nonexistentfile.exe"); + Assert.False(result.Item1); + Assert.Equal("", result.Item2); + } + + [Theory] + [InlineData("7zip19.00-helpEr", "7zip19.00 HelpEr")] + [InlineData("packagename", "Packagename")] + [InlineData("Packagename", "Packagename")] + [InlineData("PackageName", "PackageName")] + [InlineData("pacKagENaMe", "PacKagENaMe")] + [InlineData("PACKAGENAME", "PACKAGENAME")] + [InlineData("package-Name", "Package Name")] + [InlineData("pub-name/pkg-version_2.00.portable", "Pkg Version 2.00")] + [InlineData("!helloWorld", "!helloWorld")] + [InlineData("@stylistic/eslint-plugin", "Eslint Plugin")] + [InlineData("Flask-RESTful", "Flask RESTful")] + public void TestFormatAsName(string id, string name) + { + Assert.Equal(name, CoreTools.FormatAsName(id)); + } + + [Theory] + [InlineData(89)] + [InlineData(33)] + [InlineData(558)] + [InlineData(12)] + [InlineData(0)] + [InlineData(1)] + [InlineData(64)] + public void TestRandomStringGenerator(int length) + { + var string1 = CoreTools.RandomString(length); + var string2 = CoreTools.RandomString(length); + var string3 = CoreTools.RandomString(length); + Assert.Equal(string1.Length, length); + Assert.Equal(string2.Length, length); + Assert.Equal(string3.Length, length); + + // Zero-lenghted strings are always equal + // One-lenghted strings are likely to be equal + if (length > 1) + { + Assert.NotEqual(string1, string2); + Assert.NotEqual(string2, string3); + Assert.NotEqual(string1, string3); + } + + foreach (string s in new string[] { string1, string2, string3}) + foreach (char c in s) + Assert.True("abcdefghijklmnopqrstuvwxyz0123456789".Contains(c)); + } + + [Theory] + [InlineData("", 0)] + [InlineData("https://invalid.url.com/this/is/an/invalid.php?file=to_test&if=the&code_returns=zero", 0)] + [InlineData("https://www.marticliment.com/wingetui/wingetui_size_test.txt", 460)] + public async Task TestFileSizeLoader(string uri, long expectedSize) + { + var size = await CoreTools.GetFileSizeAsync(uri != ""? new Uri(uri): null); + Assert.Equal(expectedSize / 1048576, size); + } + + [Theory] + [InlineData("1000.0", 1000.0, 0.0)] + [InlineData("2.4", 2.4, 0.001)] + [InlineData("33a.12-beta", 33.12, 0.0)] + [InlineData("0", 0.0, 0.0)] + [InlineData("", -1, 0.0)] + [InlineData("dfgfdsgdfg", -1, 0.0)] + [InlineData("-12", 12.0, 0.0)] + [InlineData("4.0.0.1.0", 4.001, 0.01)] + [InlineData("2024.30.04.1223", 2024.30041223, 0.0)] + [InlineData("0.0", 0.0, 0.0)] + public void TestGetVersionStringAsFloat(string version, double expected, double tolerance) + { + Assert.Equal(expected, CoreTools.GetVersionStringAsFloat(version), tolerance); + } + } +} \ No newline at end of file diff --git a/src/UniGetUI.Core.Tools.Tests/UniGetUI.Core.Tools.Tests.csproj b/src/UniGetUI.Core.Tools.Tests/UniGetUI.Core.Tools.Tests.csproj new file mode 100644 index 000000000..c74d66cc4 --- /dev/null +++ b/src/UniGetUI.Core.Tools.Tests/UniGetUI.Core.Tools.Tests.csproj @@ -0,0 +1,26 @@ + + + + + + enable + false + true + + + + + + + + + + + + + + + + + + diff --git a/src/UniGetUI.Core.Tools/Tools.cs b/src/UniGetUI.Core.Tools/Tools.cs new file mode 100644 index 000000000..a45ccabc7 --- /dev/null +++ b/src/UniGetUI.Core.Tools/Tools.cs @@ -0,0 +1,299 @@ +using System; +using System.Diagnostics; +using System.Globalization; +using System.Net; +using System.Security.Principal; +using UniGetUI.Core.Data; +using UniGetUI.Core.Language; +using UniGetUI.Core.Logging; + +namespace UniGetUI.Core.Tools +{ + public static class CoreTools + { + + public static readonly HttpClientHandler HttpClientConfig = new HttpClientHandler() + { + AutomaticDecompression = DecompressionMethods.All + }; + + private static LanguageEngine? LanguageEngine; + + /// + /// Translate a string to the current language + /// + /// The string to translate + /// The translated string if available, the original string otherwise + public static string Translate(string text) + { + if(LanguageEngine == null) LanguageEngine = new LanguageEngine(); + return LanguageEngine.Translate(text); + } + + public static void ReloadLanguageEngineInstance(string ForceLanguage = "") + { + LanguageEngine = new LanguageEngine(ForceLanguage); + } + + /// + /// Dummy function to capture the strings that need to be translated but the translation is handled by a custom widget + /// + /// + /// + public static string AutoTranslated(string text) + { + return text; + } + + /// + /// Launches the self executable on a new process and kills the current process + /// + public static void RelaunchProcess() + { + Logger.Debug("Launching process: " + Environment.GetCommandLineArgs()[0].Replace(".dll", ".exe")); + Process.Start(Environment.GetCommandLineArgs()[0].Replace(".dll", ".exe")); + Logger.Warn("About to kill process"); + Environment.Exit(0); + } + + /// + /// Finds an executable in path and returns its location + /// + /// The executable alias to find + /// A tuple containing: a boolean hat represents wether the path was found or not; the path to the file if found. + public static async Task> Which(string command) + { + Process process = new() + { + StartInfo = new ProcessStartInfo() + { + FileName = "cmd.exe", + Arguments = "/C where " + command, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true + } + }; + process.Start(); + string? line = await process.StandardOutput.ReadLineAsync(); + string output; + if (line == null) + output = ""; + else + output = line.Trim(); + await process.WaitForExitAsync(); + if (process.ExitCode != 0 || output == "") + return new Tuple(false, ""); + else + return new Tuple(File.Exists(output), output); + } + + /// + /// Formats a given package id as a name, capitalizing words and replacing separators with spaces + /// + /// A string containing the Id of a package + /// The formatted string + public static string FormatAsName(string name) + { + name = name.Replace(".install", "").Replace(".portable", "").Replace("-", " ").Replace("_", " ").Split("/")[^1]; + string newName = ""; + for (int i = 0; i < name.Length; i++) + { + if (i == 0 || name[i - 1] == ' ') + newName += name[i].ToString().ToUpper(); + else + newName += name[i]; + } + return newName; + } + + /// + /// Generates a random string composed of alphanumeric characters and numbers + /// + /// The length of the string + /// A string + public static string RandomString(int length) + { + Random random = new(); + const string pool = "abcdefghijklmnopqrstuvwxyz0123456789"; + IEnumerable chars = Enumerable.Range(0, length) + .Select(x => pool[random.Next(0, pool.Length)]); + return new string(chars.ToArray()); + } + + public static void ReportFatalException(Exception e) + { + string LangName = "Unknown"; + try + { + LangName = Translate("langName"); + } + catch { } + + string Error_String = $@" + OS: {Environment.OSVersion.Platform} + Version: {Environment.OSVersion.VersionString} + OS Architecture: {Environment.Is64BitOperatingSystem} + APP Architecture: {Environment.Is64BitProcess} + Language: {LangName} + APP Version: {CoreData.VersionName} + Executable: {Environment.ProcessPath} + +Crash Message: {e.Message} + +Crash Traceback: +{e.StackTrace}"; + + Console.WriteLine(Error_String); + + + string ErrorBody = "https://www.marticliment.com/error-report/?appName=UniGetUI^&errorBody=" + Uri.EscapeDataString(Error_String.Replace("\n", "{l}")); + + Console.WriteLine(ErrorBody); + + using System.Diagnostics.Process cmd = new(); + cmd.StartInfo.FileName = "cmd.exe"; + cmd.StartInfo.RedirectStandardInput = true; + cmd.StartInfo.RedirectStandardOutput = false; + cmd.StartInfo.CreateNoWindow = true; + cmd.StartInfo.UseShellExecute = false; + cmd.Start(); + cmd.StandardInput.WriteLine("start " + ErrorBody); + cmd.StandardInput.WriteLine("exit"); + cmd.WaitForExit(); + Environment.Exit(1); + + } + + /// + /// Launches a .bat or .cmd file for the given filename + /// + /// The path of the batch file + /// The title of the window + /// Whether the batch file should be launched elevated or not + public static async void LaunchBatchFile(string path, string WindowTitle = "", bool RunAsAdmin = false) + { + Process p = new(); + p.StartInfo.FileName = "cmd.exe"; + p.StartInfo.Arguments = "/C start \"" + WindowTitle + "\" \"" + path + "\""; + p.StartInfo.UseShellExecute = true; + p.StartInfo.CreateNoWindow = true; + p.StartInfo.Verb = RunAsAdmin ? "runas" : ""; + p.Start(); + await p.WaitForExitAsync(); + } + + /// + /// Checks whether the current process has administrator privileges + /// + /// True if the process has administrator privileges + public static bool IsAdministrator() + { + try + { + return (new WindowsPrincipal(WindowsIdentity.GetCurrent())) + .IsInRole(WindowsBuiltInRole.Administrator); + } + catch (Exception e) + { + Logger.Warn("Could not check if user is administrator"); + Logger.Warn(e); + return false; + } + } + + /// + /// Returns the size (in MB) of the file at the given URL + /// + /// a valid Uri object containing a URL to a file + /// a double representing the size in MBs, 0 if the process fails + public static async Task GetFileSizeAsync(Uri? url) + { + return await GetFileSizeAsyncAsLong(url) / 1048576; + } + + public static async Task GetFileSizeAsyncAsLong(Uri? url) + { + if(url == null) + return 0; + + try + { +#pragma warning disable SYSLIB0014 // Type or member is obsolete + WebRequest req = WebRequest.Create(url); +#pragma warning restore SYSLIB0014 // Type or member is obsolete + req.Method = "HEAD"; + WebResponse resp = await req.GetResponseAsync(); + long ContentLength; + if (long.TryParse(resp.Headers.Get("Content-Length"), out ContentLength)) + { + return ContentLength; + } + + } + catch (Exception e) + { + Logger.Warn($"Could not load file size for url={url}"); + Logger.Warn(e); + } + return 0; + } + + public static double GetVersionStringAsFloat(string Version) + { + try + { + string _ver = ""; + bool _dotAdded = false; + foreach (char _char in Version) + { + if (char.IsDigit(_char)) + _ver += _char; + else if (_char == '.') + { + if (!_dotAdded) + { + _ver += _char; + _dotAdded = true; + } + } + } + double res = -1; + if (_ver != "" && _ver != ".") + try + { + var val = double.Parse(_ver, CultureInfo.InvariantCulture); + return val; + } + catch { } + return res; + } + catch + { + Logger.Warn($"Failed to parse version {Version} to float"); + return -1; + } + } + + /// + /// Returns the query that can be safely passed as a command-line parameter + /// + /// The query to make safe + /// The safe version of the query + public static string EnsureSafeQueryString(string query) + { + return query.Replace(";", string.Empty) + .Replace("&", string.Empty) + .Replace("|", string.Empty) + .Replace(">", string.Empty) + .Replace("<", string.Empty) + .Replace("%", string.Empty) + .Replace("\"", string.Empty) + .Replace("'", string.Empty) + .Replace("\\", string.Empty) + .Replace("`", string.Empty); + } + } +} + diff --git a/src/UniGetUI.Core.Tools/UniGetUI.Core.Tools.csproj b/src/UniGetUI.Core.Tools/UniGetUI.Core.Tools.csproj new file mode 100644 index 000000000..13c8e6fab --- /dev/null +++ b/src/UniGetUI.Core.Tools/UniGetUI.Core.Tools.csproj @@ -0,0 +1,15 @@ + + + + + enable + + + + + + + + + + diff --git a/src/UniGetUI.Interface.Enums/Enums.cs b/src/UniGetUI.Interface.Enums/Enums.cs new file mode 100644 index 000000000..b1dfc85c7 --- /dev/null +++ b/src/UniGetUI.Interface.Enums/Enums.cs @@ -0,0 +1,16 @@ +namespace UniGetUI.Interface.Enums +{ + /// + /// Represents the visual status of a package on a list + /// + public enum PackageTag + { + Default, + AlreadyInstalled, + IsUpgradable, + Pinned, + OnQueue, + BeingProcessed, + Failed + } +} diff --git a/src/UniGetUI.Interface.Enums/UniGetUI.Interface.Enums.csproj b/src/UniGetUI.Interface.Enums/UniGetUI.Interface.Enums.csproj new file mode 100644 index 000000000..293f74341 --- /dev/null +++ b/src/UniGetUI.Interface.Enums/UniGetUI.Interface.Enums.csproj @@ -0,0 +1,10 @@ + + + + net8.0 + enable + enable + AnyCPU;x64;ARM64 + + + diff --git a/src/UniGetUI.PackageEngine.Enums/Enums.cs b/src/UniGetUI.PackageEngine.Enums/Enums.cs new file mode 100644 index 000000000..6c3a40535 --- /dev/null +++ b/src/UniGetUI.PackageEngine.Enums/Enums.cs @@ -0,0 +1,53 @@ +namespace UniGetUI.PackageEngine.Enums +{ + /// + /// Represents the installation scope of a package + /// + public enum PackageScope + { + Global = 1, + Machine = 1, + Local = 0, + User = 0, + } + + public enum DeserializedPackageStatus + { + ManagerNotFound, + ManagerNotEnabled, + ManagerNotReady, + SourceNotFound, + IsAvailable + } + + public enum BundleFormatType + { + JSON, + YAML, + XML + } + + + public enum OperationVeredict + { + Succeeded, + Failed, + AutoRetry, + } + public enum OperationStatus + { + Pending, + Running, + Succeeded, + Failed, + Cancelled + } + + public enum OperationType + { + Install, + Update, + Uninstall, + None + } +} diff --git a/src/UniGetUI.PackageEngine.Enums/UniGetUI.PackageEngine.Enums.csproj b/src/UniGetUI.PackageEngine.Enums/UniGetUI.PackageEngine.Enums.csproj new file mode 100644 index 000000000..ba2ecaa37 --- /dev/null +++ b/src/UniGetUI.PackageEngine.Enums/UniGetUI.PackageEngine.Enums.csproj @@ -0,0 +1,8 @@ + + + + + enable + + + diff --git a/src/UniGetUI.PackageEngine.Managers.Chocolatey/Chocolatey.cs b/src/UniGetUI.PackageEngine.Managers.Chocolatey/Chocolatey.cs new file mode 100644 index 000000000..dc6ae467c --- /dev/null +++ b/src/UniGetUI.PackageEngine.Managers.Chocolatey/Chocolatey.cs @@ -0,0 +1,339 @@ +using System.Diagnostics; +using System.Runtime.InteropServices; +using UniGetUI.Core.Data; +using UniGetUI.Core.Logging; +using UniGetUI.Core.SettingsEngine; +using UniGetUI.Core.Tools; +using UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers; +using UniGetUI.PackageEngine.Enums; +using UniGetUI.PackageEngine.ManagerClasses.Manager; +using UniGetUI.PackageEngine.Managers.PowerShellManager; +using UniGetUI.PackageEngine.PackageClasses; + +namespace UniGetUI.PackageEngine.Managers.ChocolateyManager +{ + public class Chocolatey : BaseNuGet + { + new public static string[] FALSE_PACKAGE_NAMES = new string[] { "" }; + new public static string[] FALSE_PACKAGE_IDS = new string[] { "Directory", "", "Did", "Features?", "Validation", "-", "being", "It", "Error", "L'accs", "Maximum", "This", "Output is package name ", "operable", "Invalid" }; + new public static string[] FALSE_PACKAGE_VERSIONS = new string[] { "", "Did", "Features?", "Validation", "-", "being", "It", "Error", "L'accs", "Maximum", "This", "packages", "current version", "installed version", "is", "program", "validations", "argument", "no" }; + + public Chocolatey() : base() + { + Capabilities = new ManagerCapabilities() + { + CanRunAsAdmin = true, + CanSkipIntegrityChecks = true, + CanRunInteractively = true, + SupportsCustomVersions = true, + SupportsCustomArchitectures = true, + SupportedCustomArchitectures = new Architecture[] { Architecture.X86 }, + SupportsPreRelease = true, + SupportsCustomSources = true, + SupportsCustomPackageIcons = true, + Sources = new ManagerSource.Capabilities() + { + KnowsPackageCount = false, + KnowsUpdateDate = false, + } + }; + + Properties = new ManagerProperties() + { + Name = "Chocolatey", + Description = CoreTools.Translate("The classic package manager for windows. You'll find everything there.
Contains: General Software"), + IconId = "choco", + ColorIconId = "choco_color", + ExecutableFriendlyName = "choco.exe", + InstallVerb = "install", + UninstallVerb = "uninstall", + UpdateVerb = "upgrade", + ExecutableCallArgs = "", + KnownSources = [new ManagerSource(this, "community", new Uri("https://community.chocolatey.org/api/v2/"))], + DefaultSource = new ManagerSource(this, "community", new Uri("https://community.chocolatey.org/api/v2/")), + + }; + + SourceProvider = new ChocolateySourceProvider(this); + + } + + protected override async Task GetAvailableUpdates_UnSafe() + { + Process p = new(); + p.StartInfo = new ProcessStartInfo() + { + FileName = Status.ExecutablePath, + Arguments = Properties.ExecutableCallArgs + " outdated", + RedirectStandardOutput = true, + RedirectStandardError = true, + RedirectStandardInput = true, + UseShellExecute = false, + CreateNoWindow = true, + StandardOutputEncoding = System.Text.Encoding.UTF8 + }; + + p.Start(); + string? line; + string output = ""; + List Packages = new(); + while ((line = await p.StandardOutput.ReadLineAsync()) != null) + { + output += line + "\n"; + if (!line.StartsWith("Chocolatey")) + { + string[] elements = line.Split('|'); + for (int i = 0; i < elements.Length; i++) elements[i] = elements[i].Trim(); + + if (elements.Length <= 2) + continue; + + if (FALSE_PACKAGE_IDS.Contains(elements[0]) || FALSE_PACKAGE_VERSIONS.Contains(elements[1]) || elements[1] == elements[2]) + continue; + + Packages.Add(new UpgradablePackage(CoreTools.FormatAsName(elements[0]), elements[0], elements[1], elements[2], DefaultSource, this)); + } + } + + output += await p.StandardError.ReadToEndAsync(); + LogOperation(p, output); + + await p.WaitForExitAsync(); + + return Packages.ToArray(); + } + + protected override async Task GetInstalledPackages_UnSafe() + { + Process p = new(); + p.StartInfo = new ProcessStartInfo() + { + FileName = Status.ExecutablePath, + Arguments = Properties.ExecutableCallArgs + " list", + RedirectStandardOutput = true, + RedirectStandardError = true, + RedirectStandardInput = true, + UseShellExecute = false, + CreateNoWindow = true, + StandardOutputEncoding = System.Text.Encoding.UTF8 + }; + + p.Start(); + string? line; + string output = ""; + List Packages = new(); + while ((line = await p.StandardOutput.ReadLineAsync()) != null) + { + output += line + "\n"; + if (!line.StartsWith("Chocolatey")) + { + string[] elements = line.Split(' '); + for (int i = 0; i < elements.Length; i++) elements[i] = elements[i].Trim(); + + if (elements.Length <= 1) + continue; + + if (FALSE_PACKAGE_IDS.Contains(elements[0]) || FALSE_PACKAGE_VERSIONS.Contains(elements[1])) + continue; + + Packages.Add(new Package(CoreTools.FormatAsName(elements[0]), elements[0], elements[1], DefaultSource, this)); + } + } + + output += await p.StandardError.ReadToEndAsync(); + LogOperation(p, output); + + await p.WaitForExitAsync(); + + return Packages.ToArray(); + } + public override OperationVeredict GetInstallOperationVeredict(Package package, InstallationOptions options, int ReturnCode, string[] Output) + { + string output_string = string.Join("\n", Output); + + if (ReturnCode == 1641 || ReturnCode == 0) + return OperationVeredict.Succeeded; + else if (ReturnCode == 3010) + return OperationVeredict.Succeeded; // TODO: Restart required + else if ((output_string.Contains("Run as administrator") || output_string.Contains("The requested operation requires elevation") || output_string.Contains("ERROR: Exception calling \"CreateDirectory\" with \"1\" argument(s): \"Access to the path")) && !options.RunAsAdministrator) + { + options.RunAsAdministrator = true; + return OperationVeredict.AutoRetry; + } + return OperationVeredict.Failed; + } + + public override OperationVeredict GetUpdateOperationVeredict(Package package, InstallationOptions options, int ReturnCode, string[] Output) + { + return GetInstallOperationVeredict(package, options, ReturnCode, Output); + } + + public override OperationVeredict GetUninstallOperationVeredict(Package package, InstallationOptions options, int ReturnCode, string[] Output) + { + string output_string = string.Join("\n", Output); + + if (ReturnCode == 1641 || ReturnCode == 1614 || ReturnCode == 1605 || ReturnCode == 0) + return OperationVeredict.Succeeded; + else if (ReturnCode == 3010) + return OperationVeredict.Succeeded; // TODO: Restart required + else if ((output_string.Contains("Run as administrator") || output_string.Contains("The requested operation requires elevation")) && !options.RunAsAdministrator) + { + options.RunAsAdministrator = true; + return OperationVeredict.AutoRetry; + } + return OperationVeredict.Failed; + } + public override string[] GetInstallParameters(Package package, InstallationOptions options) + { + List parameters = GetUninstallParameters(package, options).ToList(); + parameters[0] = Properties.InstallVerb; + parameters.Add("--no-progress"); + + if (options.Architecture == System.Runtime.InteropServices.Architecture.X86) + parameters.Add("--forcex86"); + + if (options.PreRelease) + parameters.Add("--prerelease"); + + if (options.SkipHashCheck) + parameters.AddRange(new string[] { "--ignore-checksums", "--force" }); + + if (options.Version != "") + parameters.AddRange(new string[] { "--version=" + options.Version, "--allow-downgrade" }); + + return parameters.ToArray(); + } + public override string[] GetUpdateParameters(Package package, InstallationOptions options) + { + string[] parameters = GetInstallParameters(package, options); + parameters[0] = Properties.UpdateVerb; + return parameters; + } + + public override string[] GetUninstallParameters(Package package, InstallationOptions options) + { + List parameters = new() { Properties.UninstallVerb, package.Id, "-y" }; + + if (options.CustomParameters != null) + parameters.AddRange(options.CustomParameters); + + if (options.InteractiveInstallation) + parameters.Add("--notsilent"); + + return parameters.ToArray(); + } + + protected override async Task LoadManager() + { + ManagerStatus status = new(); + + string old_choco_path = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Programs\\WingetUI\\choco-cli"); + string new_choco_path = Path.Join(CoreData.UniGetUIDataDirectory, "Chocolatey"); + + if (Directory.Exists(old_choco_path)) + try + { + Logger.Info("Moving Bundled Chocolatey from old path to new path..."); + + if (!Directory.Exists(new_choco_path)) + Directory.CreateDirectory(new_choco_path); + + foreach (string old_subdir in Directory.GetDirectories(old_choco_path, "*", SearchOption.AllDirectories)) + { + string new_subdir = old_subdir.Replace(old_choco_path, new_choco_path); + if (!Directory.Exists(new_subdir)) + { + Logger.Debug("New directory: " + new_subdir); + await Task.Run(() => Directory.CreateDirectory(new_subdir)); + } + else + Logger.Debug("Directory " + new_subdir + " already exists"); + } + + foreach (string old_file in Directory.GetFiles(old_choco_path, "*", SearchOption.AllDirectories)) + { + string new_file = old_file.Replace(old_choco_path, new_choco_path); + if (!File.Exists(new_file)) + { + Logger.Info("Copying " + old_file); + await Task.Run(() => File.Move(old_file, new_file)); + } + else + { + Logger.Debug("File " + new_file + " already exists."); + File.Delete(old_file); + } + } + + foreach (string old_subdir in Directory.GetDirectories(old_choco_path, "*", SearchOption.AllDirectories).Reverse()) + { + if (!Directory.EnumerateFiles(old_subdir).Any() && !Directory.EnumerateDirectories(old_subdir).Any()) + { + Logger.Debug("Deleting old empty subdirectory " + old_subdir); + Directory.Delete(old_subdir); + } + } + + if (!Directory.EnumerateFiles(old_choco_path).Any() && !Directory.EnumerateDirectories(old_choco_path).Any()) + { + Logger.Info("Deleting old Chocolatey directory " + old_choco_path); + Directory.Delete(old_choco_path); + } + + + } + catch (Exception e) + { + Logger.Error("An error occurred while migrating chocolatey"); + Logger.Error(e); + } + + if (Settings.Get("UseSystemChocolatey")) + status.ExecutablePath = (await CoreTools.Which("choco.exe")).Item2; + else if (File.Exists(Path.Join(new_choco_path, "choco.exe"))) + status.ExecutablePath = Path.Join(new_choco_path, "choco.exe"); + else + status.ExecutablePath = Path.Join(CoreData.UniGetUIExecutableDirectory, "choco-cli\\choco.exe"); + + status.Found = File.Exists(status.ExecutablePath); + + if (!status.Found) + return status; + + Process process = new() + { + StartInfo = new ProcessStartInfo() + { + FileName = status.ExecutablePath, + Arguments = "--version", + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true, + StandardOutputEncoding = System.Text.Encoding.UTF8 + } + }; + process.Start(); + status.Version = (await process.StandardOutput.ReadToEndAsync()).Trim(); + + // If the user is running bundled chocolatey and chocolatey is not in path, add chocolatey to path + if (/*Settings.Get("ShownWelcomeWizard") && */!Settings.Get("UseSystemChocolatey") && !File.Exists(@"C:\ProgramData\Chocolatey\bin\choco.exe")) + { + string? path = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.User); + if (!path?.Contains(status.ExecutablePath.Replace("\\choco.exe", "\\bin")) ?? false) + { + Logger.Info("Adding chocolatey to path since it was not on path."); + Environment.SetEnvironmentVariable("PATH", $"{status.ExecutablePath.Replace("\\choco.exe", "\\bin")};{path}", EnvironmentVariableTarget.User); + Environment.SetEnvironmentVariable("chocolateyinstall", Path.GetDirectoryName(status.ExecutablePath), EnvironmentVariableTarget.User); + } + } + Environment.SetEnvironmentVariable("chocolateyinstall", Path.GetDirectoryName(status.ExecutablePath), EnvironmentVariableTarget.Process); + + + if (status.Found && IsEnabled()) + await RefreshPackageIndexes(); + + return status; + } + } +} diff --git a/src/UniGetUI.PackageEngine.Managers.Chocolatey/ChocolateySourceProvider.cs b/src/UniGetUI.PackageEngine.Managers.Chocolatey/ChocolateySourceProvider.cs new file mode 100644 index 000000000..5453c27fe --- /dev/null +++ b/src/UniGetUI.PackageEngine.Managers.Chocolatey/ChocolateySourceProvider.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UniGetUI.Core.Logging; +using UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers; +using UniGetUI.PackageEngine.Classes.Manager.Providers; +using UniGetUI.PackageEngine.Enums; +using UniGetUI.PackageEngine.ManagerClasses.Manager; + +namespace UniGetUI.PackageEngine.Managers.ChocolateyManager +{ + internal class ChocolateySourceProvider : BaseSourceProvider + { + public ChocolateySourceProvider(Chocolatey manager) : base(manager) { } + + public override string[] GetAddSourceParameters(ManagerSource source) + { + return new string[] { "source", "add", "--name", source.Name, "--source", source.Url.ToString(), "-y" }; + } + + public override string[] GetRemoveSourceParameters(ManagerSource source) + { + return new string[] { "source", "remove", "--name", source.Name, "-y" }; + } + + public override OperationVeredict GetAddSourceOperationVeredict(ManagerSource source, int ReturnCode, string[] Output) + { + return ReturnCode == 0 ? OperationVeredict.Succeeded : OperationVeredict.Failed; + } + + public override OperationVeredict GetRemoveSourceOperationVeredict(ManagerSource source, int ReturnCode, string[] Output) + { + return ReturnCode == 0 ? OperationVeredict.Succeeded : OperationVeredict.Failed; + } + + protected override async Task GetSources_UnSafe() + { + List sources = new(); + + Process process = new(); + ProcessStartInfo startInfo = new() + { + FileName = Manager.Status.ExecutablePath, + Arguments = Manager.Properties.ExecutableCallArgs + " source list", + RedirectStandardOutput = true, + RedirectStandardError = true, + RedirectStandardInput = true, + UseShellExecute = false, + CreateNoWindow = true, + StandardOutputEncoding = System.Text.Encoding.UTF8 + }; + + process.StartInfo = startInfo; + process.Start(); + + + string output = ""; + string? line; + while ((line = await process.StandardOutput.ReadLineAsync()) != null) + { + output += line + "\n"; + try + { + if (string.IsNullOrEmpty(line)) + continue; + + if (line.Contains(" - ") && line.Contains(" | ")) + { + string[] parts = line.Trim().Split('|')[0].Trim().Split(" - "); + if (parts[1].Trim() == "https://community.chocolatey.org/api/v2/") + sources.Add(new ManagerSource(Manager, "community", new Uri("https://community.chocolatey.org/api/v2/"))); + else + sources.Add(new ManagerSource(Manager, parts[0].Trim(), new Uri(parts[1].Trim()))); + } + } + catch (Exception e) + { + Logger.Error(e); + } + } + + output += await process.StandardError.ReadToEndAsync(); + Manager.LogOperation(process, output); + + await process.WaitForExitAsync(); + return sources.ToArray(); + } + } +} diff --git a/src/UniGetUI.PackageEngine.Managers.Chocolatey/UniGetUI.PackageEngine.Managers.Chocolatey.csproj b/src/UniGetUI.PackageEngine.Managers.Chocolatey/UniGetUI.PackageEngine.Managers.Chocolatey.csproj new file mode 100644 index 000000000..f9fcd239f --- /dev/null +++ b/src/UniGetUI.PackageEngine.Managers.Chocolatey/UniGetUI.PackageEngine.Managers.Chocolatey.csproj @@ -0,0 +1,253 @@ + + + + + enable + + + + + + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + diff --git a/src/UniGetUI/choco-cli/CREDITS.txt b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/CREDITS.txt similarity index 99% rename from src/UniGetUI/choco-cli/CREDITS.txt rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/CREDITS.txt index fb81ac8f1..154f19ee8 100644 --- a/src/UniGetUI/choco-cli/CREDITS.txt +++ b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/CREDITS.txt @@ -383,9 +383,9 @@ Chocolatey uses [Checksum](https://github.com/chocolatey/checksum) to determine ``` -### log4net @ 2.0.12 +### Logger.Log4net @ 2.0.12 -Chocolatey uses [log4net](http://logging.apache.org/log4net/) for logging. +Chocolatey uses [log4net](http://logging.apache.org/log4net/) for Logger.Logging. [License terms](http://logging.apache.org/log4net/license.html): ```txt diff --git a/src/UniGetUI/choco-cli/LICENSE.txt b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/LICENSE.txt similarity index 100% rename from src/UniGetUI/choco-cli/LICENSE.txt rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/LICENSE.txt diff --git a/src/UniGetUI/choco-cli/bin/RefreshEnv.cmd b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/bin/RefreshEnv.cmd similarity index 100% rename from src/UniGetUI/choco-cli/bin/RefreshEnv.cmd rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/bin/RefreshEnv.cmd diff --git a/src/UniGetUI/choco-cli/bin/_processed.txt b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/bin/_processed.txt similarity index 100% rename from src/UniGetUI/choco-cli/bin/_processed.txt rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/bin/_processed.txt diff --git a/src/UniGetUI/choco-cli/bin/choco.exe b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/bin/choco.exe similarity index 100% rename from src/UniGetUI/choco-cli/bin/choco.exe rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/bin/choco.exe diff --git a/src/UniGetUI/choco-cli/bin/cinst.exe b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/bin/cinst.exe similarity index 100% rename from src/UniGetUI/choco-cli/bin/cinst.exe rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/bin/cinst.exe diff --git a/src/UniGetUI/choco-cli/bin/clist.exe b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/bin/clist.exe similarity index 100% rename from src/UniGetUI/choco-cli/bin/clist.exe rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/bin/clist.exe diff --git a/src/UniGetUI/choco-cli/bin/cpush.exe b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/bin/cpush.exe similarity index 100% rename from src/UniGetUI/choco-cli/bin/cpush.exe rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/bin/cpush.exe diff --git a/src/UniGetUI/choco-cli/bin/cuninst.exe b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/bin/cuninst.exe similarity index 100% rename from src/UniGetUI/choco-cli/bin/cuninst.exe rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/bin/cuninst.exe diff --git a/src/UniGetUI/choco-cli/bin/cup.exe b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/bin/cup.exe similarity index 100% rename from src/UniGetUI/choco-cli/bin/cup.exe rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/bin/cup.exe diff --git a/src/UniGetUI/choco-cli/choco.exe b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/choco.exe similarity index 100% rename from src/UniGetUI/choco-cli/choco.exe rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/choco.exe diff --git a/src/UniGetUI/choco-cli/choco.exe.ignore b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/choco.exe.ignore similarity index 100% rename from src/UniGetUI/choco-cli/choco.exe.ignore rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/choco.exe.ignore diff --git a/src/UniGetUI/choco-cli/choco.exe.manifest b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/choco.exe.manifest similarity index 100% rename from src/UniGetUI/choco-cli/choco.exe.manifest rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/choco.exe.manifest diff --git a/src/UniGetUI/choco-cli/config/chocolatey.config b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/config/chocolatey.config similarity index 96% rename from src/UniGetUI/choco-cli/config/chocolatey.config rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/config/chocolatey.config index 25f9bc3e7..1518c070c 100644 --- a/src/UniGetUI/choco-cli/config/chocolatey.config +++ b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/config/chocolatey.config @@ -26,7 +26,7 @@ - + @@ -41,8 +41,8 @@ - - + + diff --git a/src/UniGetUI/choco-cli/config/chocolatey.config.backup b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/config/chocolatey.config.backup similarity index 96% rename from src/UniGetUI/choco-cli/config/chocolatey.config.backup rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/config/chocolatey.config.backup index 13c5a774c..3b7c1eebb 100644 --- a/src/UniGetUI/choco-cli/config/chocolatey.config.backup +++ b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/config/chocolatey.config.backup @@ -26,7 +26,7 @@ - + @@ -41,8 +41,8 @@ - - + + diff --git a/src/UniGetUI/choco-cli/helpers/ChocolateyTabExpansion.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/ChocolateyTabExpansion.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/ChocolateyTabExpansion.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/ChocolateyTabExpansion.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/chocolateyInstaller.psm1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/chocolateyInstaller.psm1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/chocolateyInstaller.psm1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/chocolateyInstaller.psm1 diff --git a/src/UniGetUI/choco-cli/helpers/chocolateyProfile.psm1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/chocolateyProfile.psm1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/chocolateyProfile.psm1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/chocolateyProfile.psm1 diff --git a/src/UniGetUI/choco-cli/helpers/chocolateyScriptRunner.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/chocolateyScriptRunner.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/chocolateyScriptRunner.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/chocolateyScriptRunner.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Format-FileSize.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Format-FileSize.ps1 similarity index 99% rename from src/UniGetUI/choco-cli/helpers/functions/Format-FileSize.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Format-FileSize.ps1 index 0b604ceed..a4721bce8 100644 --- a/src/UniGetUI/choco-cli/helpers/functions/Format-FileSize.ps1 +++ b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Format-FileSize.ps1 @@ -49,7 +49,7 @@ Get-WebFile [parameter(ValueFromRemainingArguments = $true)][Object[]] $ignoredArguments ) - # Do not log function call, it interrupts the single line download progress output. + # Do not Logger.Log function call, it interrupts the single line download progress output. Foreach ($unit in @('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB')) { If ($size -lt 1024) { diff --git a/src/UniGetUI/choco-cli/helpers/functions/Get-CheckSumValid.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-CheckSumValid.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Get-CheckSumValid.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-CheckSumValid.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Get-ChocolateyConfigValue.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-ChocolateyConfigValue.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Get-ChocolateyConfigValue.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-ChocolateyConfigValue.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Get-ChocolateyPath.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-ChocolateyPath.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Get-ChocolateyPath.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-ChocolateyPath.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Get-ChocolateyUnzip.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-ChocolateyUnzip.ps1 similarity index 99% rename from src/UniGetUI/choco-cli/helpers/functions/Get-ChocolateyUnzip.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-ChocolateyUnzip.ps1 index 0d45b52d8..d5f0b3452 100644 --- a/src/UniGetUI/choco-cli/helpers/functions/Get-ChocolateyUnzip.ps1 +++ b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-ChocolateyUnzip.ps1 @@ -70,11 +70,11 @@ OPTIONAL - This is a specific directory within zip file to extract. The folder and its contents will be extracted to the destination. .PARAMETER PackageName -OPTIONAL - This will facilitate logging unzip activity for subsequent +OPTIONAL - This will facilitate Logger.Logging unzip activity for subsequent uninstalls .PARAMETER DisableLogging -OPTIONAL - This disables logging of the extracted items. It speeds up +OPTIONAL - This disables Logger.Logging of the extracted items. It speeds up extraction of archives with many files. Usage of this parameter will prevent Uninstall-ChocolateyZipPackage diff --git a/src/UniGetUI/choco-cli/helpers/functions/Get-ChocolateyWebFile.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-ChocolateyWebFile.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Get-ChocolateyWebFile.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-ChocolateyWebFile.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Get-EnvironmentVariable.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-EnvironmentVariable.ps1 similarity index 99% rename from src/UniGetUI/choco-cli/helpers/functions/Get-EnvironmentVariable.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-EnvironmentVariable.ps1 index 697897ec4..2ac4ab498 100644 --- a/src/UniGetUI/choco-cli/helpers/functions/Get-EnvironmentVariable.ps1 +++ b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-EnvironmentVariable.ps1 @@ -64,7 +64,7 @@ Set-EnvironmentVariable [parameter(ValueFromRemainingArguments = $true)][Object[]] $ignoredArguments ) - # Do not log function call, it may expose variable names + # Do not Logger.Log function call, it may expose variable names ## Called from chocolateysetup.psm1 - wrap any Write-Host in try/catch [string] $MACHINE_ENVIRONMENT_REGISTRY_KEY_NAME = "SYSTEM\CurrentControlSet\Control\Session Manager\Environment\"; diff --git a/src/UniGetUI/choco-cli/helpers/functions/Get-EnvironmentVariableNames.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-EnvironmentVariableNames.ps1 similarity index 99% rename from src/UniGetUI/choco-cli/helpers/functions/Get-EnvironmentVariableNames.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-EnvironmentVariableNames.ps1 index dd5f7fa54..55b51ed7b 100644 --- a/src/UniGetUI/choco-cli/helpers/functions/Get-EnvironmentVariableNames.ps1 +++ b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-EnvironmentVariableNames.ps1 @@ -47,7 +47,7 @@ Get-EnvironmentVariable Set-EnvironmentVariable #> - # Do not log function call + # Do not Logger.Log function call # HKCU:\Environment may not exist in all Windows OSes (such as Server Core). switch ($Scope) { diff --git a/src/UniGetUI/choco-cli/helpers/functions/Get-FtpFile.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-FtpFile.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Get-FtpFile.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-FtpFile.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Get-OSArchitectureWidth.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-OSArchitectureWidth.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Get-OSArchitectureWidth.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-OSArchitectureWidth.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Get-PackageParameters.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-PackageParameters.ps1 similarity index 99% rename from src/UniGetUI/choco-cli/helpers/functions/Get-PackageParameters.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-PackageParameters.ps1 index ff6b96f11..157f514f8 100644 --- a/src/UniGetUI/choco-cli/helpers/functions/Get-PackageParameters.ps1 +++ b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-PackageParameters.ps1 @@ -139,7 +139,7 @@ Install-ChocolateyZipPackage Write-Debug 'Parsing $env:ChocolateyPackageParameters and $env:ChocolateyPackageParametersSensitive for parameters' $paramStrings = @("$env:ChocolateyPackageParameters", "$env:ChocolateyPackageParametersSensitive") if ($env:ChocolateyPackageParametersSensitive) { - Write-Debug "Sensitive parameters detected, no logging of parameters." + Write-Debug "Sensitive parameters detected, no Logger.Logging of parameters." $loggingAllowed = $false } } diff --git a/src/UniGetUI/choco-cli/helpers/functions/Get-ToolsLocation.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-ToolsLocation.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Get-ToolsLocation.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-ToolsLocation.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Get-UACEnabled.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-UACEnabled.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Get-UACEnabled.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-UACEnabled.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Get-UninstallRegistryKey.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-UninstallRegistryKey.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Get-UninstallRegistryKey.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-UninstallRegistryKey.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Get-VirusCheckValid.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-VirusCheckValid.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Get-VirusCheckValid.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-VirusCheckValid.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Get-WebFile.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-WebFile.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Get-WebFile.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-WebFile.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Get-WebFileName.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-WebFileName.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Get-WebFileName.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-WebFileName.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Get-WebHeaders.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-WebHeaders.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Get-WebHeaders.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Get-WebHeaders.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Install-BinFile.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-BinFile.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Install-BinFile.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-BinFile.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Install-ChocolateyEnvironmentVariable.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-ChocolateyEnvironmentVariable.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Install-ChocolateyEnvironmentVariable.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-ChocolateyEnvironmentVariable.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Install-ChocolateyExplorerMenuItem.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-ChocolateyExplorerMenuItem.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Install-ChocolateyExplorerMenuItem.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-ChocolateyExplorerMenuItem.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Install-ChocolateyFileAssociation.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-ChocolateyFileAssociation.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Install-ChocolateyFileAssociation.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-ChocolateyFileAssociation.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Install-ChocolateyInstallPackage.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-ChocolateyInstallPackage.ps1 similarity index 99% rename from src/UniGetUI/choco-cli/helpers/functions/Install-ChocolateyInstallPackage.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-ChocolateyInstallPackage.ps1 index 8884e408a..db4865215 100644 --- a/src/UniGetUI/choco-cli/helpers/functions/Install-ChocolateyInstallPackage.ps1 +++ b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-ChocolateyInstallPackage.ps1 @@ -312,7 +312,7 @@ Pro / Business supports a single, ubiquitous install directory option. } try { - # make sure any logging folder exists + # make sure any Logger.Logging folder exists $pattern = "(?:['`"])([a-zA-Z]\:\\[^'`"]+)(?:[`"'])|([a-zA-Z]\:\\[\S]+)" $silentArgs, $additionalInstallArgs | ForEach-Object { Select-String $pattern -input $_ -AllMatches } | diff --git a/src/UniGetUI/choco-cli/helpers/functions/Install-ChocolateyPackage.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-ChocolateyPackage.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Install-ChocolateyPackage.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-ChocolateyPackage.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Install-ChocolateyPath.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-ChocolateyPath.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Install-ChocolateyPath.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-ChocolateyPath.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Install-ChocolateyPinnedTaskBarItem.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-ChocolateyPinnedTaskBarItem.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Install-ChocolateyPinnedTaskBarItem.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-ChocolateyPinnedTaskBarItem.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Install-ChocolateyPowershellCommand.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-ChocolateyPowershellCommand.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Install-ChocolateyPowershellCommand.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-ChocolateyPowershellCommand.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Install-ChocolateyShortcut.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-ChocolateyShortcut.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Install-ChocolateyShortcut.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-ChocolateyShortcut.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Install-ChocolateyVsixPackage.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-ChocolateyVsixPackage.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Install-ChocolateyVsixPackage.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-ChocolateyVsixPackage.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Install-ChocolateyZipPackage.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-ChocolateyZipPackage.ps1 similarity index 99% rename from src/UniGetUI/choco-cli/helpers/functions/Install-ChocolateyZipPackage.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-ChocolateyZipPackage.ps1 index 19fcfa3c7..9ecddf583 100644 --- a/src/UniGetUI/choco-cli/helpers/functions/Install-ChocolateyZipPackage.ps1 +++ b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-ChocolateyZipPackage.ps1 @@ -152,7 +152,7 @@ This parameter provides compatibility, but should not be used directly and not with the community package repository until January 2018. .PARAMETER DisableLogging -OPTIONAL - This disables logging of the extracted items. It speeds up +OPTIONAL - This disables Logger.Logging of the extracted items. It speeds up extraction of archives with many files. Usage of this parameter will prevent Uninstall-ChocolateyZipPackage diff --git a/src/UniGetUI/choco-cli/helpers/functions/Install-Vsix.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-Vsix.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Install-Vsix.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Install-Vsix.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Set-EnvironmentVariable.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Set-EnvironmentVariable.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Set-EnvironmentVariable.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Set-EnvironmentVariable.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Set-PowerShellExitCode.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Set-PowerShellExitCode.ps1 similarity index 99% rename from src/UniGetUI/choco-cli/helpers/functions/Set-PowerShellExitCode.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Set-PowerShellExitCode.ps1 index 44276ca15..18e9c28bb 100644 --- a/src/UniGetUI/choco-cli/helpers/functions/Set-PowerShellExitCode.ps1 +++ b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Set-PowerShellExitCode.ps1 @@ -46,7 +46,7 @@ Set-PowerShellExitCode 3010 [parameter(ValueFromRemainingArguments = $true)][Object[]] $ignoredArguments ) - # Do not log function call - can mess things up + # Do not Logger.Log function call - can mess things up if ($exitCode -eq $null -or $exitCode -eq '') { Write-Debug '$exitCode was passed null' diff --git a/src/UniGetUI/choco-cli/helpers/functions/Start-ChocolateyProcessAsAdmin.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Start-ChocolateyProcessAsAdmin.ps1 similarity index 97% rename from src/UniGetUI/choco-cli/helpers/functions/Start-ChocolateyProcessAsAdmin.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Start-ChocolateyProcessAsAdmin.ps1 index 07a472156..bbaac81f9 100644 --- a/src/UniGetUI/choco-cli/helpers/functions/Start-ChocolateyProcessAsAdmin.ps1 +++ b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Start-ChocolateyProcessAsAdmin.ps1 @@ -66,7 +66,7 @@ The working directory for the running process. Defaults to `$env:TEMP` for default. .PARAMETER SensitiveStatements -Arguments to pass to `ExeToRun` that are not logged. +Arguments to pass to `ExeToRun` that are not Logger.Logged. Note that only licensed versions of Chocolatey provide a way to pass those values completely through without having them in the install @@ -326,7 +326,7 @@ $dbMessagePrepend [`"$exeToRun`" $wrappedStatements]. This may take a while, dep $exitErrorMessage = 'User (you) cancelled the installation.'; break } 1603 { - $exitErrorMessage = "Generic MSI Error. This is a local environment error, not an issue with a package or the MSI itself - it could mean a pending reboot is necessary prior to install or something else (like the same version is already installed). Please see MSI log if available. If not, try again adding `'--install-arguments=`"`'/l*v c:\$($env:chocolateyPackageName)_msi_install.log`'`"`'. Then search the MSI Log for `"Return Value 3`" and look above that for the error."; break + $exitErrorMessage = "Generic MSI Error. This is a local environment error, not an issue with a package or the MSI itself - it could mean a pending reboot is necessary prior to install or something else (like the same version is already installed). Please see MSI Logger.Log if available. If not, try again adding `'--install-arguments=`"`'/l*v c:\$($env:chocolateyPackageName)_msi_install.log`'`"`'. Then search the MSI Logger.Log for `"Return Value 3`" and look above that for the error."; break } 1618 { $exitErrorMessage = 'Another installation currently in progress. Try again later.'; break @@ -338,7 +338,7 @@ $dbMessagePrepend [`"$exeToRun`" $wrappedStatements]. This may take a while, dep $exitErrorMessage = 'MSI could not be opened - it is possibly corrupt or not an MSI at all. If it was downloaded and the MSI is less than 30K, try opening it in an editor like Notepad++ as it is likely HTML.' + $errorMessageAddendum; break } 1622 { - $exitErrorMessage = 'Something is wrong with the install log location specified. Please fix this in the package silent arguments (or in install arguments you specified). The directory specified as part of the log file path must exist for an MSI to be able to log to that directory.' + $errorMessageAddendum; break + $exitErrorMessage = 'Something is wrong with the install Logger.Log location specified. Please fix this in the package silent arguments (or in install arguments you specified). The directory specified as part of the Logger.Log file path must exist for an MSI to be able to Logger.Log to that directory.' + $errorMessageAddendum; break } 1623 { $exitErrorMessage = 'This MSI has a language that is not supported by your system. Contact package maintainer(s) if there is an install available in your language and you would like it added to the packaging.'; break @@ -371,7 +371,7 @@ $dbMessagePrepend [`"$exeToRun`" $wrappedStatements]. This may take a while, dep Write-Warning $exitErrorMessage } else { - $errorMessageSpecific = 'See log for possible error messages.' + $errorMessageSpecific = 'See Logger.Log for possible error messages.' } if ($validExitCodes -notcontains $exitCode) { diff --git a/src/UniGetUI/choco-cli/helpers/functions/Test-ProcessAdminRights.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Test-ProcessAdminRights.ps1 similarity index 99% rename from src/UniGetUI/choco-cli/helpers/functions/Test-ProcessAdminRights.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Test-ProcessAdminRights.ps1 index fd857c002..1f84655f0 100644 --- a/src/UniGetUI/choco-cli/helpers/functions/Test-ProcessAdminRights.ps1 +++ b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Test-ProcessAdminRights.ps1 @@ -36,7 +36,7 @@ None System.Boolean #> - # do not log function call + # do not Logger.Log function call ## Called from chocolateysetup.psm1 - wrap any Write-Host in try/catch $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent([Security.Principal.TokenAccessLevels]'Query,Duplicate')) diff --git a/src/UniGetUI/choco-cli/helpers/functions/UnInstall-ChocolateyZipPackage.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/UnInstall-ChocolateyZipPackage.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/UnInstall-ChocolateyZipPackage.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/UnInstall-ChocolateyZipPackage.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Uninstall-BinFile.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Uninstall-BinFile.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Uninstall-BinFile.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Uninstall-BinFile.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Uninstall-ChocolateyEnvironmentVariable.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Uninstall-ChocolateyEnvironmentVariable.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Uninstall-ChocolateyEnvironmentVariable.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Uninstall-ChocolateyEnvironmentVariable.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Uninstall-ChocolateyPackage.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Uninstall-ChocolateyPackage.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Uninstall-ChocolateyPackage.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Uninstall-ChocolateyPackage.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Update-SessionEnvironment.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Update-SessionEnvironment.ps1 similarity index 100% rename from src/UniGetUI/choco-cli/helpers/functions/Update-SessionEnvironment.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Update-SessionEnvironment.ps1 diff --git a/src/UniGetUI/choco-cli/helpers/functions/Write-FunctionCallLogMessage.ps1 b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Write-FunctionCallLogMessage.ps1 similarity index 99% rename from src/UniGetUI/choco-cli/helpers/functions/Write-FunctionCallLogMessage.ps1 rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Write-FunctionCallLogMessage.ps1 index e03853498..4ed5e011f 100644 --- a/src/UniGetUI/choco-cli/helpers/functions/Write-FunctionCallLogMessage.ps1 +++ b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/helpers/functions/Write-FunctionCallLogMessage.ps1 @@ -52,7 +52,7 @@ Write-FunctionCallLogMessage -Invocation $MyInvocation -Parameters $PSBoundParam [parameter(ValueFromRemainingArguments = $true)][Object[]] $ignoredArguments ) - # do not log function call - recursion? + # do not Logger.Log function call - recursion? $argumentsPassed = '' foreach ($param in $parameters.GetEnumerator()) { diff --git a/src/UniGetUI/choco-cli/redirects/RefreshEnv.cmd b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/redirects/RefreshEnv.cmd similarity index 100% rename from src/UniGetUI/choco-cli/redirects/RefreshEnv.cmd rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/redirects/RefreshEnv.cmd diff --git a/src/UniGetUI/choco-cli/redirects/choco.exe b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/redirects/choco.exe similarity index 100% rename from src/UniGetUI/choco-cli/redirects/choco.exe rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/redirects/choco.exe diff --git a/src/UniGetUI/choco-cli/redirects/choco.exe.ignore b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/redirects/choco.exe.ignore similarity index 100% rename from src/UniGetUI/choco-cli/redirects/choco.exe.ignore rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/redirects/choco.exe.ignore diff --git a/src/UniGetUI/choco-cli/tools/7z.dll b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/tools/7z.dll similarity index 100% rename from src/UniGetUI/choco-cli/tools/7z.dll rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/tools/7z.dll diff --git a/src/UniGetUI/choco-cli/tools/7z.dll.manifest b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/tools/7z.dll.manifest similarity index 100% rename from src/UniGetUI/choco-cli/tools/7z.dll.manifest rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/tools/7z.dll.manifest diff --git a/src/UniGetUI/choco-cli/tools/7z.exe b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/tools/7z.exe similarity index 100% rename from src/UniGetUI/choco-cli/tools/7z.exe rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/tools/7z.exe diff --git a/src/UniGetUI/choco-cli/tools/7z.exe.ignore b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/tools/7z.exe.ignore similarity index 100% rename from src/UniGetUI/choco-cli/tools/7z.exe.ignore rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/tools/7z.exe.ignore diff --git a/src/UniGetUI/choco-cli/tools/7z.exe.manifest b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/tools/7z.exe.manifest similarity index 100% rename from src/UniGetUI/choco-cli/tools/7z.exe.manifest rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/tools/7z.exe.manifest diff --git a/src/UniGetUI/choco-cli/tools/7zip.license.txt b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/tools/7zip.license.txt similarity index 100% rename from src/UniGetUI/choco-cli/tools/7zip.license.txt rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/tools/7zip.license.txt diff --git a/src/UniGetUI/choco-cli/tools/checksum.exe b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/tools/checksum.exe similarity index 100% rename from src/UniGetUI/choco-cli/tools/checksum.exe rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/tools/checksum.exe diff --git a/src/UniGetUI/choco-cli/tools/checksum.exe.config b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/tools/checksum.exe.config similarity index 100% rename from src/UniGetUI/choco-cli/tools/checksum.exe.config rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/tools/checksum.exe.config diff --git a/src/UniGetUI/choco-cli/tools/checksum.exe.ignore b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/tools/checksum.exe.ignore similarity index 100% rename from src/UniGetUI/choco-cli/tools/checksum.exe.ignore rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/tools/checksum.exe.ignore diff --git a/src/UniGetUI/choco-cli/tools/checksum.license.txt b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/tools/checksum.license.txt similarity index 100% rename from src/UniGetUI/choco-cli/tools/checksum.license.txt rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/tools/checksum.license.txt diff --git a/src/UniGetUI/choco-cli/tools/shimgen.exe b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/tools/shimgen.exe similarity index 100% rename from src/UniGetUI/choco-cli/tools/shimgen.exe rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/tools/shimgen.exe diff --git a/src/UniGetUI/choco-cli/tools/shimgen.exe.ignore b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/tools/shimgen.exe.ignore similarity index 100% rename from src/UniGetUI/choco-cli/tools/shimgen.exe.ignore rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/tools/shimgen.exe.ignore diff --git a/src/UniGetUI/choco-cli/tools/shimgen.license.txt b/src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/tools/shimgen.license.txt similarity index 100% rename from src/UniGetUI/choco-cli/tools/shimgen.license.txt rename to src/UniGetUI.PackageEngine.Managers.Chocolatey/choco-cli/tools/shimgen.license.txt diff --git a/src/UniGetUI/PackageEngine/Managers/Dotnet.cs b/src/UniGetUI.PackageEngine.Managers.Dotnet/DotNet.cs similarity index 56% rename from src/UniGetUI/PackageEngine/Managers/Dotnet.cs rename to src/UniGetUI.PackageEngine.Managers.Dotnet/DotNet.cs index 037cbf1cf..06c1f1091 100644 --- a/src/UniGetUI/PackageEngine/Managers/Dotnet.cs +++ b/src/UniGetUI.PackageEngine.Managers.Dotnet/DotNet.cs @@ -1,74 +1,66 @@ -using UniGetUI.PackageEngine.Classes; -using UniGetUI.PackageEngine.Operations; -using UniGetUI.Core; -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; -using System.Net; using System.Net.Http; using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Threading.Tasks; - -namespace UniGetUI.PackageEngine.Managers +using UniGetUI.Core; +using UniGetUI.PackageEngine.Classes; +using UniGetUI.PackageEngine.Enums; +using UniGetUI.Core.Logging; +using UniGetUI.Core.Tools; +using UniGetUI.PackageEngine.Managers; +using UniGetUI.PackageEngine.ManagerClasses.Manager; +using UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers; +using UniGetUI.PackageEngine.PackageClasses; +using UniGetUI.PackageEngine.Managers.PowerShellManager; + +namespace UniGetUI.PackageEngine.Managers.DotNetManager { - public class Dotnet : PackageManager + public class DotNet : BaseNuGet { new public static string[] FALSE_PACKAGE_NAMES = new string[] { "" }; new public static string[] FALSE_PACKAGE_IDS = new string[] { "" }; new public static string[] FALSE_PACKAGE_VERSIONS = new string[] { "" }; - protected override async Task FindPackages_UnSafe(string query) - { - Process p = new(); - p.StartInfo = new ProcessStartInfo() + public DotNet() : base() + { + Capabilities = new ManagerCapabilities() { - FileName = Status.ExecutablePath, - Arguments = Properties.ExecutableCallArgs + " search \"" + query + "\"", - RedirectStandardOutput = true, - RedirectStandardError = true, - RedirectStandardInput = true, - UseShellExecute = false, - CreateNoWindow = true, - StandardOutputEncoding = System.Text.Encoding.UTF8 + CanRunAsAdmin = true, + SupportsCustomScopes = true, + SupportsCustomArchitectures = true, + SupportedCustomArchitectures = new Architecture[] { Architecture.X86, Architecture.X64, Architecture.Arm64, Architecture.Arm }, + SupportsPreRelease = true, + SupportsCustomLocations = true, + SupportsCustomPackageIcons = true, + SupportsCustomVersions = true, }; - p.Start(); - string line; - List Packages = new(); - bool DashesPassed = false; - string output = ""; - while ((line = await p.StandardOutput.ReadLineAsync()) != null) + Properties = new ManagerProperties() { - output += line + "\n"; - if (!DashesPassed) - { - if (line.Contains("-----")) - DashesPassed = true; - } - else - { - string[] elements = Regex.Replace(line, " {2,}", " ").Split(' '); - if (elements.Length >= 2) - Packages.Add(new Package(Tools.FormatAsName(elements[0]), elements[0], elements[1], MainSource, this, PackageScope.Global)); - // Dotnet tool packages are installed globally by default, hence the Global flag - } - } - - output += await p.StandardError.ReadToEndAsync(); - AppTools.LogManagerOperation(this, p, output); - - await p.WaitForExitAsync(); - - return Packages.ToArray(); + Name = ".NET Tool", + Description = CoreTools.Translate("A repository full of tools and executables designed with Microsoft's .NET ecosystem in mind.
Contains: .NET related tools and scripts"), + IconId = "dotnet", + ColorIconId = "dotnet_color", + ExecutableFriendlyName = "dotnet tool", + InstallVerb = "install", + UninstallVerb = "uninstall", + UpdateVerb = "update", + ExecutableCallArgs = "tool", + DefaultSource = new ManagerSource(this, "nuget.org", new Uri("https://www.nuget.org/api/v2")), + KnownSources = [new ManagerSource(this, "nuget.org", new Uri("https://www.nuget.org/api/v2"))], + }; } protected override async Task GetAvailableUpdates_UnSafe() { - string path = await Tools.Which("dotnet-tools-outdated.exe"); - if (!File.Exists(path)) + var which_res = await CoreTools.Which("dotnet-tools-outdated.exe"); + string path = which_res.Item2; + if (!which_res.Item1) { Process proc = new() { @@ -101,7 +93,7 @@ protected override async Task GetAvailableUpdates_UnSafe() p.Start(); - string line; + string? line; bool DashesPassed = false; List Packages = new(); string output = ""; @@ -123,11 +115,11 @@ protected override async Task GetAvailableUpdates_UnSafe() if (FALSE_PACKAGE_IDS.Contains(elements[0]) || FALSE_PACKAGE_VERSIONS.Contains(elements[1])) continue; - Packages.Add(new UpgradablePackage(Tools.FormatAsName(elements[0]), elements[0], elements[1], elements[2], MainSource, this, PackageScope.Global)); + Packages.Add(new UpgradablePackage(CoreTools.FormatAsName(elements[0]), elements[0], elements[1], elements[2], DefaultSource, this, PackageScope.Global)); } } output += await p.StandardError.ReadToEndAsync(); - AppTools.LogManagerOperation(this, p, output); + LogOperation(p, output); return Packages.ToArray(); } @@ -150,7 +142,7 @@ protected override async Task GetInstalledPackages_UnSafe() p.Start(); - string line; + string? line; bool DashesPassed = false; List Packages = new(); while ((line = await p.StandardOutput.ReadLineAsync()) != null) @@ -170,7 +162,7 @@ protected override async Task GetInstalledPackages_UnSafe() if (FALSE_PACKAGE_IDS.Contains(elements[0]) || FALSE_PACKAGE_VERSIONS.Contains(elements[1])) continue; - Packages.Add(new Package(Tools.FormatAsName(elements[0]), elements[0], elements[1], MainSource, this, PackageScope.User)); + Packages.Add(new Package(CoreTools.FormatAsName(elements[0]), elements[0], elements[1], DefaultSource, this, PackageScope.User)); } } @@ -210,11 +202,11 @@ protected override async Task GetInstalledPackages_UnSafe() if (FALSE_PACKAGE_IDS.Contains(elements[0]) || FALSE_PACKAGE_VERSIONS.Contains(elements[1])) continue; - Packages.Add(new Package(Tools.FormatAsName(elements[0]), elements[0], elements[1], MainSource, this, PackageScope.Global)); + Packages.Add(new Package(CoreTools.FormatAsName(elements[0]), elements[0], elements[1], DefaultSource, this, PackageScope.Global)); } } output += await p.StandardError.ReadToEndAsync(); - AppTools.LogManagerOperation(this, p, output); + LogOperation(p, output); return Packages.ToArray(); } @@ -272,131 +264,13 @@ public override string[] GetUninstallParameters(Package package, InstallationOpt return parameters.ToArray(); } - public override ManagerSource GetMainSource() - { - return new ManagerSource(this, "nuget.org", new Uri("https://www.nuget.org/")); - } - - public override async Task GetPackageDetails_UnSafe(Package package) - { - PackageDetails details = new(package); - - try - { - details.ManifestUrl = new Uri("https://www.nuget.org/packages/" + package.Id); - string url = $"http://www.nuget.org/api/v2/Packages(Id='{package.Id}',Version='')"; - - using (HttpClient client = new()) - { - string apiContents = await client.GetStringAsync(url); - - details.InstallerUrl = new Uri($"https://globalcdn.nuget.org/packages/{package.Id}.{package.Version}.nupkg"); - details.InstallerType = Tools.Translate("NuPkg (zipped manifest)"); - details.InstallerSize = await Tools.GetFileSizeAsync(details.InstallerUrl); - - - foreach (Match match in Regex.Matches(apiContents, @"[^<>]+<\/name>")) - { - details.Author = match.Value.Replace("", "").Replace("", ""); - details.Publisher = match.Value.Replace("", "").Replace("", ""); - break; - } - - foreach (Match match in Regex.Matches(apiContents, @"[^<>]+<\/d:Description>")) - { - details.Description = match.Value.Replace("", "").Replace("", ""); - break; - } - - foreach (Match match in Regex.Matches(apiContents, @"[^<>]+<\/updated>")) - { - details.UpdateDate = match.Value.Replace("", "").Replace("", ""); - break; - } - - foreach (Match match in Regex.Matches(apiContents, @"[^<>]+<\/d:ProjectUrl>")) - { - details.HomepageUrl = new Uri(match.Value.Replace("", "").Replace("", "")); - break; - } - - foreach (Match match in Regex.Matches(apiContents, @"[^<>]+<\/d:LicenseUrl>")) - { - details.LicenseUrl = new Uri(match.Value.Replace("", "").Replace("", "")); - break; - } - - foreach (Match match in Regex.Matches(apiContents, @"[^<>]+<\/d:PackageHash>")) - { - details.InstallerHash = match.Value.Replace("", "").Replace("", ""); - break; - } - - foreach (Match match in Regex.Matches(apiContents, @"[^<>]+<\/d:ReleaseNotes>")) - { - details.ReleaseNotes = match.Value.Replace("", "").Replace("", ""); - break; - } - - foreach (Match match in Regex.Matches(apiContents, @"[^<>]+<\/d:LicenseNames>")) - { - details.License = match.Value.Replace("", "").Replace("", ""); - break; - } - } - return details; - } - catch (Exception e) - { - AppTools.Log(e); - return details; - } - } - - -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously - public override async Task RefreshPackageIndexes() - { - // .NET Tool does not support manual source refreshing - } - - protected override ManagerCapabilities GetCapabilities() - { - return new ManagerCapabilities() - { - CanRunAsAdmin = true, - SupportsCustomScopes = true, - SupportsCustomArchitectures = true, - SupportedCustomArchitectures = new Architecture[] { Architecture.X86, Architecture.X64, Architecture.Arm64, Architecture.Arm }, - SupportsPreRelease = true, - SupportsCustomLocations = true - }; - } - - protected override ManagerProperties GetProperties() - { - ManagerProperties properties = new() - { - Name = ".NET Tool", - Description = Tools.Translate("A repository full of tools and executables designed with Microsoft's .NET ecosystem in mind.
Contains: .NET related tools and scripts"), - IconId = "dotnet", - ColorIconId = "dotnet_color", - ExecutableFriendlyName = "dotnet tool", - InstallVerb = "install", - UninstallVerb = "uninstall", - UpdateVerb = "update", - ExecutableCallArgs = "tool", - - }; - return properties; - } - protected override async Task LoadManager() { ManagerStatus status = new(); - status.ExecutablePath = await Tools.Which("dotnet.exe"); - status.Found = File.Exists(status.ExecutablePath); + var which_res = await CoreTools.Which("dotnet.exe"); + status.ExecutablePath = which_res.Item2; + status.Found = which_res.Item1; if (!status.Found) return status; @@ -422,12 +296,5 @@ protected override async Task LoadManager() return status; } - - protected override async Task GetPackageVersions_Unsafe(Package package) - { - await Task.Delay(0); - AppTools.Log("Manager " + Name + " does not support version retrieving, this function should have never been called"); - return new string[0]; - } } } diff --git a/src/UniGetUI.PackageEngine.Managers.Dotnet/UniGetUI.PackageEngine.Managers.Dotnet.csproj b/src/UniGetUI.PackageEngine.Managers.Dotnet/UniGetUI.PackageEngine.Managers.Dotnet.csproj new file mode 100644 index 000000000..942b61f2f --- /dev/null +++ b/src/UniGetUI.PackageEngine.Managers.Dotnet/UniGetUI.PackageEngine.Managers.Dotnet.csproj @@ -0,0 +1,23 @@ + + + + + + enable + + + + + + + + + + + + + + + + + diff --git a/src/UniGetUI.PackageEngine.Managers.Generic.NuGet/BaseNuGet.cs b/src/UniGetUI.PackageEngine.Managers.Generic.NuGet/BaseNuGet.cs new file mode 100644 index 000000000..77bc603c4 --- /dev/null +++ b/src/UniGetUI.PackageEngine.Managers.Generic.NuGet/BaseNuGet.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using UniGetUI.Core; +using UniGetUI.PackageEngine.Classes; +using UniGetUI.PackageEngine.Enums; +using UniGetUI.Core.Logging; +using UniGetUI.Core.Tools; +using UniGetUI.PackageEngine.ManagerClasses.Manager; +using UniGetUI.PackageEngine.PackageClasses; +using UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers; +using System.Net; +using System.Web; + +namespace UniGetUI.PackageEngine.Managers.PowerShellManager +{ + public abstract class BaseNuGet : PackageManager + { + new public static string[] FALSE_PACKAGE_NAMES = new string[] { "" }; + new public static string[] FALSE_PACKAGE_IDS = new string[] { "" }; + new public static string[] FALSE_PACKAGE_VERSIONS = new string[] { "" }; + + public BaseNuGet() : base() + { + PackageDetailsProvider = new BaseNuGetDetailsProvider(this); + } + + public sealed override async Task InitializeAsync() + { + if(PackageDetailsProvider is not BaseNuGetDetailsProvider) + throw new Exception("NuGet-based package managers must not reassign the PackageDetailsProvider property"); + + if (!Capabilities.SupportsCustomVersions) + throw new Exception("NuGet-based package managers must support custom versions"); + if (!Capabilities.SupportsCustomPackageIcons) + throw new Exception("NuGet-based package managers must support custom versions"); + + await base.InitializeAsync(); + } + + private struct SearchResult + { + public string version; + public double version_float; + public string id; + } + + protected sealed override async Task FindPackages_UnSafe(string query) + { + Logger.Error($"Using new NuGet search engine for manager {Name}"); + List Packages = new(); + + ManagerSource[] sources; + if (Capabilities.SupportsCustomSources) + sources = await GetSources(); + else + sources = new ManagerSource[] { Properties.DefaultSource }; + + foreach(var source in sources) + { + Uri SearchUrl = new Uri($"{source.Url}/Search()?searchTerm=%27{HttpUtility.UrlEncode(query)}%27&targetFramework=%27%27&includePrerelease=false"); + Logger.Debug($"Begin package search with url={SearchUrl} on manager {Name}"); ; + HttpClientHandler handler = new HttpClientHandler() + { + AutomaticDecompression = DecompressionMethods.All + }; + + using (HttpClient client = new HttpClient(handler)) + { + var response = await client.GetAsync(SearchUrl); + + if (!response.IsSuccessStatusCode) + { + Logger.Warn($"Failed to fetch api at Url={SearchUrl} with status code {response.StatusCode}"); + continue; + } + + string SearchResults = await response.Content.ReadAsStringAsync(); + MatchCollection matches = Regex.Matches(SearchResults, "([\\s\\S]*?)<\\/entry>"); + + Dictionary AlreadyProcessedPackages = new(); + + foreach (Match match in matches) + { + if (!match.Success) continue; + + string id = Regex.Match(match.Value, "Id='([^<>']+)'").Groups[1].Value; + string version = Regex.Match(match.Value, "Version='([^<>']+)'").Groups[1].Value; + double float_version = CoreTools.GetVersionStringAsFloat(version); + Match title = Regex.Match(match.Value, "([^<>]+)<\\/title>"); + + if (AlreadyProcessedPackages.ContainsKey(id) && AlreadyProcessedPackages[id].version_float >= float_version) + continue; + + AlreadyProcessedPackages[id] = new SearchResult() { id = id, version = version, version_float = float_version }; + } + foreach(var package in AlreadyProcessedPackages.Values) + Packages.Add(new Package(CoreTools.FormatAsName(package.id), package.id, package.version, source, this)); + + } + } + + return Packages.ToArray(); + } + + } + +} diff --git a/src/UniGetUI.PackageEngine.Managers.Generic.NuGet/BaseNuGetDetailsProvider.cs b/src/UniGetUI.PackageEngine.Managers.Generic.NuGet/BaseNuGetDetailsProvider.cs new file mode 100644 index 000000000..543d75c00 --- /dev/null +++ b/src/UniGetUI.PackageEngine.Managers.Generic.NuGet/BaseNuGetDetailsProvider.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Xml.Linq; +using UniGetUI.Core.IconEngine; +using UniGetUI.Core.Logging; +using UniGetUI.Core.Tools; +using UniGetUI.PackageEngine.Classes.Manager.BaseProviders; +using UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers; +using UniGetUI.PackageEngine.ManagerClasses.Manager; +using UniGetUI.PackageEngine.Managers.Generic.NuGet.Internal; +using UniGetUI.PackageEngine.PackageClasses; +using static UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers.ManagerSource; + +namespace UniGetUI.PackageEngine.Managers.PowerShellManager +{ + internal class BaseNuGetDetailsProvider : BasePackageDetailsProvider + { + public BaseNuGetDetailsProvider(BaseNuGet manager) : base(manager) { } + + protected override async Task GetPackageDetails_Unsafe(Package package) + { + PackageDetails details = new(package); + try + { + details.ManifestUrl = PackageManifestLoader.GetPackageManifestUrl(package); + string? PackageManifestContents = await PackageManifestLoader.GetPackageManifestContent(package); + if (PackageManifestContents == null) + { + Logger.Warn($"No manifest content could be loaded for package {package.Id} on manager {package.Manager.Name}, returning empty PackageDetails"); + return details; + } + + // details.InstallerUrl = new Uri($"https://globalcdn.nuget.org/packages/{package.Id}.{package.Version}.nupkg"); + details.InstallerUrl = PackageManifestLoader.GetPackageNuGetPackageUrl(package); + details.InstallerType = CoreTools.Translate("NuPkg (zipped manifest)"); + details.InstallerSize = await CoreTools.GetFileSizeAsync(details.InstallerUrl); + + foreach (Match match in Regex.Matches(PackageManifestContents, @"[^<>]+<\/name>")) + { + details.Author = match.Value.Replace("", "").Replace("", ""); + details.Publisher = match.Value.Replace("", "").Replace("", ""); + break; + } + + foreach (Match match in Regex.Matches(PackageManifestContents, @"[^<>]+<\/d:Description>")) + { + details.Description = match.Value.Replace("", "").Replace("", ""); + break; + } + + foreach (Match match in Regex.Matches(PackageManifestContents, @"[^<>]+<\/updated>")) + { + details.UpdateDate = match.Value.Replace("", "").Replace("", ""); + break; + } + + foreach (Match match in Regex.Matches(PackageManifestContents, @"[^<>]+<\/d:ProjectUrl>")) + { + details.HomepageUrl = new Uri(match.Value.Replace("", "").Replace("", "")); + break; + } + + foreach (Match match in Regex.Matches(PackageManifestContents, @"[^<>]+<\/d:LicenseUrl>")) + { + details.LicenseUrl = new Uri(match.Value.Replace("", "").Replace("", "")); + break; + } + + foreach (Match match in Regex.Matches(PackageManifestContents, @"[^<>]+<\/d:PackageHash>")) + { + details.InstallerHash = match.Value.Replace("", "").Replace("", ""); + break; + } + + foreach (Match match in Regex.Matches(PackageManifestContents, @"[^<>]+<\/d:ReleaseNotes>")) + { + details.ReleaseNotes = match.Value.Replace("", "").Replace("", ""); + break; + } + + foreach (Match match in Regex.Matches(PackageManifestContents, @"[^<>]+<\/d:LicenseNames>")) + { + details.License = match.Value.Replace("", "").Replace("", ""); + break; + } + + return details; + } + catch (Exception e) + { + Logger.Error(e); + return details; + } + } + + protected override async Task GetPackageIcon_Unsafe(Package package) + { + var PackageManifestContent = await PackageManifestLoader.GetPackageManifestContent(package); + if (PackageManifestContent == null) + { + Logger.Warn($"No manifest content could be loaded for package {package.Id} on manager {package.Manager.Name}"); + return null; + } + + var possibleIconUrl = Regex.Match(PackageManifestContent, "<(?:d\\:)?IconUrl>(.*)<(?:\\/d:)?IconUrl>"); + + if (!possibleIconUrl.Success) + { + Logger.Warn($"No Icon URL could be parsed on the manifest Url={PackageManifestLoader.GetPackageManifestUrl(package).ToString()}"); + return null; + } + + Logger.Debug($"A native icon with Url={possibleIconUrl.Groups[1].Value} was found"); + return new CacheableIcon(new Uri(possibleIconUrl.Groups[1].Value), package.Version); + } + + protected override Task GetPackageScreenshots_Unsafe(Package package) + { + throw new NotImplementedException(); + } + + protected override async Task GetPackageVersions_Unsafe(Package package) + { + Uri SearchUrl = new Uri($"{package.Source.Url}/FindPackagesById()?id='{package.Id}'"); + Logger.Debug($"Begin package version search with url={SearchUrl} on manager {Manager.Name}"); ; + HttpClientHandler handler = new HttpClientHandler() + { + AutomaticDecompression = DecompressionMethods.All + }; + + using (HttpClient client = new HttpClient(handler)) + { + var response = await client.GetAsync(SearchUrl); + + if (!response.IsSuccessStatusCode) + { + Logger.Warn($"Failed to fetch api at Url={SearchUrl} with status code {response.StatusCode} to load versions"); + return []; + } + + string SearchResults = await response.Content.ReadAsStringAsync(); + MatchCollection matches = Regex.Matches(SearchResults, "Version='([^<>']+)'"); + + List results = new List(); + HashSet alreadyProcessed = new HashSet(); + + foreach (Match match in matches) + if(!alreadyProcessed.Contains(match.Groups[1].Value) && match.Success) + { + results.Add(match.Groups[1].Value); + alreadyProcessed.Add(match.Groups[1].Value); + } + + results.Reverse(); + return results.ToArray(); + + } + } + } +} diff --git a/src/UniGetUI.PackageEngine.Managers.Generic.NuGet/Internal/PackageManifestLoader.cs b/src/UniGetUI.PackageEngine.Managers.Generic.NuGet/Internal/PackageManifestLoader.cs new file mode 100644 index 000000000..62d2d329f --- /dev/null +++ b/src/UniGetUI.PackageEngine.Managers.Generic.NuGet/Internal/PackageManifestLoader.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using UniGetUI.Core.Logging; +using UniGetUI.PackageEngine.PackageClasses; + +namespace UniGetUI.PackageEngine.Managers.Generic.NuGet.Internal +{ + internal static class PackageManifestLoader + { + private static Dictionary __manifest_cache = new(); + + /// + /// Returns the URL to the manifest of a NuGet-based package + /// + /// A valid Package object + /// A Uri object + public static Uri GetPackageManifestUrl(Package package) + { + return new Uri($"{package.Source.Url}/Packages(Id='{package.Id}',Version='{package.Version}')"); + } + + /// + /// Returns the URL to the NuPk file + /// + /// A valid Package object + /// A Uri object + public static Uri GetPackageNuGetPackageUrl(Package package) + { + return new Uri($"{package.Source.Url}/Packages/{package.Id}.{package.Version}.nupkg"); + } + + /// + /// Returns the contents of the manifest of a NuGet-based package + /// + /// The package for which to obtain the manifest + /// A string containing the contents of the manifest + public static async Task GetPackageManifestContent(Package package) + { + string? PackageManifestContent = ""; + string PackageManifestUrl = GetPackageManifestUrl(package).ToString(); + if (__manifest_cache.ContainsKey(PackageManifestUrl)) + { + Logger.Debug($"Loading cached NuGet manifest for package {package.Id} on manager {package.Manager.Name}"); + return __manifest_cache[PackageManifestUrl]; + } + + try + { + HttpClientHandler handler = new HttpClientHandler() + { + AutomaticDecompression = DecompressionMethods.All + }; + + using (HttpClient client = new HttpClient(handler)) + { + var response = await client.GetAsync(PackageManifestUrl); + if (!response.IsSuccessStatusCode && package.Version.EndsWith(".0")) + response = await client.GetAsync(new Uri(PackageManifestUrl.ToString().Replace(".0')", "')"))); + + if (!response.IsSuccessStatusCode) + { + Logger.Warn($"Failed to download the {package.Manager.Name} manifest at Url={PackageManifestUrl.ToString()} with status code {response.StatusCode}"); + return null; + } + + PackageManifestContent = await response.Content.ReadAsStringAsync(); + } + __manifest_cache[PackageManifestUrl] = PackageManifestContent; + return PackageManifestContent; + } + catch (Exception e) + { + Logger.Warn($"Failed to download the {package.Manager.Name} manifest at Url={PackageManifestUrl.ToString()}"); + Logger.Warn(e); + return null; + } + } + } +} diff --git a/src/UniGetUI.PackageEngine.Managers.Generic.NuGet/UniGetUI.PackageEngine.Managers.Generic.NuGet.csproj b/src/UniGetUI.PackageEngine.Managers.Generic.NuGet/UniGetUI.PackageEngine.Managers.Generic.NuGet.csproj new file mode 100644 index 000000000..981d1e543 --- /dev/null +++ b/src/UniGetUI.PackageEngine.Managers.Generic.NuGet/UniGetUI.PackageEngine.Managers.Generic.NuGet.csproj @@ -0,0 +1,14 @@ + + + + + + enable + + + + + + + + diff --git a/src/UniGetUI/PackageEngine/Managers/Npm.cs b/src/UniGetUI.PackageEngine.Managers.Npm/Npm.cs similarity index 57% rename from src/UniGetUI/PackageEngine/Managers/Npm.cs rename to src/UniGetUI.PackageEngine.Managers.Npm/Npm.cs index 1a03ed637..69e183b75 100644 --- a/src/UniGetUI/PackageEngine/Managers/Npm.cs +++ b/src/UniGetUI.PackageEngine.Managers.Npm/Npm.cs @@ -1,21 +1,47 @@ -using UniGetUI.PackageEngine.Classes; -using UniGetUI.PackageEngine.Operations; -using UniGetUI.Core; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Net; -using System.Threading.Tasks; - -namespace UniGetUI.PackageEngine.Managers +using System.Diagnostics; +using UniGetUI.Core.Logging; +using UniGetUI.Core.Tools; +using UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers; +using UniGetUI.PackageEngine.Enums; +using UniGetUI.PackageEngine.ManagerClasses.Manager; +using UniGetUI.PackageEngine.PackageClasses; + +namespace UniGetUI.PackageEngine.Managers.NpmManager { public class Npm : PackageManager { new public static string[] FALSE_PACKAGE_NAMES = new string[] { "" }; new public static string[] FALSE_PACKAGE_IDS = new string[] { "" }; new public static string[] FALSE_PACKAGE_VERSIONS = new string[] { "" }; + + public Npm() : base() + { + Capabilities = new ManagerCapabilities() + { + CanRunAsAdmin = true, + SupportsCustomVersions = true, + SupportsCustomScopes = true, + }; + + Properties = new ManagerProperties() + { + Name = "Npm", + Description = CoreTools.Translate("Node JS's package manager. Full of libraries and other utilities that orbit the javascript world
Contains: Node javascript libraries and other related utilities"), + IconId = "node", + ColorIconId = "node_color", + ExecutableFriendlyName = "npm", + InstallVerb = "install", + UninstallVerb = "uninstall", + UpdateVerb = "install", + ExecutableCallArgs = " -NoProfile -ExecutionPolicy Bypass -Command npm", + DefaultSource = new ManagerSource(this, "npm", new Uri("https://www.npmjs.com/")), + KnownSources = [new ManagerSource(this, "npm", new Uri("https://www.npmjs.com/"))], + + }; + + PackageDetailsProvider = new NpmPackageDetailsProvider(this); + } + protected override async Task FindPackages_UnSafe(string query) { Process p = new(); @@ -33,7 +59,7 @@ protected override async Task FindPackages_UnSafe(string query) }; p.Start(); - string line; + string? line; List Packages = new(); bool HeaderPassed = false; string output = ""; @@ -47,7 +73,7 @@ protected override async Task FindPackages_UnSafe(string query) { string[] elements = line.Split('\t'); if (elements.Length >= 5) - Packages.Add(new Package(Tools.FormatAsName(elements[0]), elements[0], elements[4], MainSource, this)); + Packages.Add(new Package(CoreTools.FormatAsName(elements[0]), elements[0], elements[4], DefaultSource, this)); } } @@ -73,7 +99,7 @@ protected override async Task GetAvailableUpdates_UnSafe() }; p.Start(); - string line; + string? line; List Packages = new(); string output = ""; while ((line = await p.StandardOutput.ReadLineAsync()) != null) @@ -82,7 +108,7 @@ protected override async Task GetAvailableUpdates_UnSafe() string[] elements = line.Split(':'); if (elements.Length >= 4) { - Packages.Add(new UpgradablePackage(Tools.FormatAsName(elements[2].Split('@')[0]), elements[2].Split('@')[0], elements[3].Split('@')[^1], elements[2].Split('@')[^1], MainSource, this)); + Packages.Add(new UpgradablePackage(CoreTools.FormatAsName(elements[2].Split('@')[0]), elements[2].Split('@')[0], elements[3].Split('@')[^1], elements[2].Split('@')[^1], DefaultSource, this)); } } @@ -114,12 +140,12 @@ protected override async Task GetAvailableUpdates_UnSafe() if (elements[3][0] == '@') elements[3] = "%" + elements[3][1..]; - Packages.Add(new UpgradablePackage(Tools.FormatAsName(elements[2].Split('@')[0]).Replace('%', '@'), elements[2].Split('@')[0].Replace('%', '@'), elements[3].Split('@')[^1].Replace('%', '@'), elements[2].Split('@')[^1].Replace('%', '@'), MainSource, this, PackageScope.Global)); + Packages.Add(new UpgradablePackage(CoreTools.FormatAsName(elements[2].Split('@')[0]).Replace('%', '@'), elements[2].Split('@')[0].Replace('%', '@'), elements[3].Split('@')[^1].Replace('%', '@'), elements[2].Split('@')[^1].Replace('%', '@'), DefaultSource, this, PackageScope.Global)); } } output += await p.StandardError.ReadToEndAsync(); - AppTools.LogManagerOperation(this, p, output); + LogOperation(p, output); await p.WaitForExitAsync(); @@ -143,7 +169,7 @@ protected override async Task GetInstalledPackages_UnSafe() }; p.Start(); - string line; + string? line; List Packages = new(); string output = ""; while ((line = await p.StandardOutput.ReadLineAsync()) != null) @@ -152,7 +178,7 @@ protected override async Task GetInstalledPackages_UnSafe() if (line.Contains("--") || line.Contains("├─") || line.Contains("└─")) { string[] elements = line[4..].Split('@'); - Packages.Add(new Package(Tools.FormatAsName(elements[0]), elements[0], elements[1], MainSource, this)); + Packages.Add(new Package(CoreTools.FormatAsName(elements[0]), elements[0], elements[1], DefaultSource, this)); } } @@ -178,12 +204,12 @@ protected override async Task GetInstalledPackages_UnSafe() { line = line.Replace("- @", "- %"); string[] elements = line[4..].Split('@'); - Packages.Add(new Package(Tools.FormatAsName(elements[0].Replace('%', '@')), elements[0].Replace('%', '@'), elements[1], MainSource, this, PackageScope.Global)); + Packages.Add(new Package(CoreTools.FormatAsName(elements[0].Replace('%', '@')), elements[0].Replace('%', '@'), elements[1], DefaultSource, this, PackageScope.Global)); } } output += await p.StandardError.ReadToEndAsync(); - AppTools.LogManagerOperation(this, p, output); + LogOperation(p, output); await p.WaitForExitAsync(); return Packages.ToArray(); @@ -235,142 +261,13 @@ public override string[] GetUninstallParameters(Package package, InstallationOpt return parameters.ToArray(); } - public override ManagerSource GetMainSource() - { - return new ManagerSource(this, "npm", new Uri("https://www.npmjs.com/")); - } - - public override async Task GetPackageDetails_UnSafe(Package package) - { - PackageDetails details = new(package); - try - { - details.InstallerType = "Tarball"; - details.ManifestUrl = new Uri($"https://www.npmjs.com/package/{package.Id}"); - details.ReleaseNotesUrl = new Uri($"https://www.npmjs.com/package/{package.Id}?activeTab=versions"); - - using (Process p = new()) - { - p.StartInfo = new ProcessStartInfo() - { - FileName = Status.ExecutablePath, - Arguments = Properties.ExecutableCallArgs + " info " + package.Id, - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - RedirectStandardInput = true, - CreateNoWindow = true, - WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), - StandardOutputEncoding = System.Text.Encoding.UTF8 - }; - - p.Start(); - - List output = new(); - string line; - while ((line = await p.StandardOutput.ReadLineAsync()) != null) - { - output.Add(line); - } - - int lineNo = 0; - bool ReadingMaintainer = false; - foreach (string outLine in output) - { - try - { - lineNo++; - if (lineNo == 2) - { - details.License = outLine.Split("|")[1]; - } - else if (lineNo == 3) - { - details.Description = outLine.Trim(); - } - else if (lineNo == 4) - { - details.HomepageUrl = new Uri(outLine.Trim()); - } - else if (outLine.StartsWith(".tarball")) - { - details.InstallerUrl = new Uri(outLine.Replace(".tarball: ", "").Trim()); - details.InstallerSize = await Tools.GetFileSizeAsync(details.InstallerUrl); - } - else if (outLine.StartsWith(".integrity")) - { - details.InstallerHash = outLine.Replace(".integrity: sha512-", "").Replace("==", "").Trim(); - } - else if (outLine.StartsWith("maintainers:")) - { - ReadingMaintainer = true; - } - else if (ReadingMaintainer) - { - ReadingMaintainer = false; - details.Author = outLine.Replace("-", "").Split('<')[0].Trim(); - } - else if (outLine.StartsWith("published")) - { - details.Publisher = outLine.Split("by").Last().Split('<')[0].Trim(); - details.UpdateDate = outLine.Replace("published", "").Split("by")[0].Trim(); - } - } - catch (Exception e) - { - AppTools.Log(e); - } - } - } - } - catch (Exception e) - { - AppTools.Log(e); - } - - return details; - } - -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously - public override async Task RefreshPackageIndexes() - { - // Npm does not support manual source refreshing - } - - protected override ManagerCapabilities GetCapabilities() - { - return new ManagerCapabilities() - { - CanRunAsAdmin = true, - SupportsCustomVersions = true, - SupportsCustomScopes = true, - }; - } - - protected override ManagerProperties GetProperties() - { - ManagerProperties properties = new() - { - Name = "Npm", - Description = Tools.Translate("Node JS's package manager. Full of libraries and other utilities that orbit the javascript world
Contains: Node javascript libraries and other related utilities"), - IconId = "node", - ColorIconId = "node_color", - ExecutableFriendlyName = "npm", - InstallVerb = "install", - UninstallVerb = "uninstall", - UpdateVerb = "install", - ExecutableCallArgs = " -NoProfile -ExecutionPolicy Bypass -Command npm", - - }; - return properties; - } protected override async Task LoadManager() { ManagerStatus status = new(); status.ExecutablePath = Path.Join(Environment.SystemDirectory, "windowspowershell\\v1.0\\powershell.exe"); - status.Found = File.Exists(await Tools.Which("npm")); + status.Found = (await CoreTools.Which("npm")).Item1; if (!status.Found) return status; @@ -400,36 +297,6 @@ protected override async Task LoadManager() return status; } - protected override async Task GetPackageVersions_Unsafe(Package package) - { - Process p = new() - { - StartInfo = new ProcessStartInfo() - { - FileName = Status.ExecutablePath, - Arguments = Properties.ExecutableCallArgs + " show " + package.Id + " versions --json", - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - RedirectStandardInput = true, - CreateNoWindow = true, - WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), - StandardOutputEncoding = System.Text.Encoding.UTF8 - } - }; - - string line; - List versions = new(); - - p.Start(); - - while ((line = await p.StandardOutput.ReadLineAsync()) != null) - { - if (line.Contains("\"")) - versions.Add(line.Trim().TrimStart('"').TrimEnd(',').TrimEnd('"')); - } - - return versions.ToArray(); - } + } } diff --git a/src/UniGetUI.PackageEngine.Managers.Npm/NpmPackageDetailsProvider.cs b/src/UniGetUI.PackageEngine.Managers.Npm/NpmPackageDetailsProvider.cs new file mode 100644 index 000000000..38ef9f807 --- /dev/null +++ b/src/UniGetUI.PackageEngine.Managers.Npm/NpmPackageDetailsProvider.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UniGetUI.Core.IconEngine; +using UniGetUI.Core.Logging; +using UniGetUI.Core.Tools; +using UniGetUI.PackageEngine.Classes.Manager.BaseProviders; +using UniGetUI.PackageEngine.ManagerClasses.Manager; +using UniGetUI.PackageEngine.PackageClasses; + +namespace UniGetUI.PackageEngine.Managers.NpmManager +{ + internal class NpmPackageDetailsProvider : BasePackageDetailsProvider + { + public NpmPackageDetailsProvider(Npm manager) : base(manager) { } + + protected override async Task GetPackageDetails_Unsafe(Package package) + { + PackageDetails details = new(package); + try + { + details.InstallerType = "Tarball"; + details.ManifestUrl = new Uri($"https://www.npmjs.com/package/{package.Id}"); + details.ReleaseNotesUrl = new Uri($"https://www.npmjs.com/package/{package.Id}?activeTab=versions"); + + using (Process p = new()) + { + p.StartInfo = new ProcessStartInfo() + { + FileName = Manager.Status.ExecutablePath, + Arguments = Manager.Properties.ExecutableCallArgs + " info " + package.Id, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + RedirectStandardInput = true, + CreateNoWindow = true, + WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), + StandardOutputEncoding = System.Text.Encoding.UTF8 + }; + + p.Start(); + + List output = new(); + string? line; + while ((line = await p.StandardOutput.ReadLineAsync()) != null) + { + output.Add(line); + } + + int lineNo = 0; + bool ReadingMaintainer = false; + foreach (string outLine in output) + { + try + { + lineNo++; + if (lineNo == 2) + { + details.License = outLine.Split("|")[1]; + } + else if (lineNo == 3) + { + details.Description = outLine.Trim(); + } + else if (lineNo == 4) + { + details.HomepageUrl = new Uri(outLine.Trim()); + } + else if (outLine.StartsWith(".tarball")) + { + details.InstallerUrl = new Uri(outLine.Replace(".tarball: ", "").Trim()); + details.InstallerSize = await CoreTools.GetFileSizeAsync(details.InstallerUrl); + } + else if (outLine.StartsWith(".integrity")) + { + details.InstallerHash = outLine.Replace(".integrity: sha512-", "").Replace("==", "").Trim(); + } + else if (outLine.StartsWith("maintainers:")) + { + ReadingMaintainer = true; + } + else if (ReadingMaintainer) + { + ReadingMaintainer = false; + details.Author = outLine.Replace("-", "").Split('<')[0].Trim(); + } + else if (outLine.StartsWith("published")) + { + details.Publisher = outLine.Split("by").Last().Split('<')[0].Trim(); + details.UpdateDate = outLine.Replace("published", "").Split("by")[0].Trim(); + } + } + catch (Exception e) + { + Logger.Warn(e); + } + } + } + } + catch (Exception e) + { + Logger.Error(e); + } + + return details; + } + + protected override Task GetPackageIcon_Unsafe(Package package) + { + throw new NotImplementedException(); + } + + protected override Task GetPackageScreenshots_Unsafe(Package package) + { + throw new NotImplementedException(); + } + + protected override async Task GetPackageVersions_Unsafe(Package package) + { + Process p = new() + { + StartInfo = new ProcessStartInfo() + { + FileName = Manager.Status.ExecutablePath, + Arguments = Manager.Properties.ExecutableCallArgs + " show " + package.Id + " versions --json", + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + RedirectStandardInput = true, + CreateNoWindow = true, + WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), + StandardOutputEncoding = System.Text.Encoding.UTF8 + } + }; + + string? line; + List versions = new(); + + p.Start(); + + while ((line = await p.StandardOutput.ReadLineAsync()) != null) + { + if (line.Contains("\"")) + versions.Add(line.Trim().TrimStart('"').TrimEnd(',').TrimEnd('"')); + } + + return versions.ToArray(); + } + } +} diff --git a/src/UniGetUI.PackageEngine.Managers.Npm/UniGetUI.PackageEngine.Managers.Npm.csproj b/src/UniGetUI.PackageEngine.Managers.Npm/UniGetUI.PackageEngine.Managers.Npm.csproj new file mode 100644 index 000000000..cfc05a17d --- /dev/null +++ b/src/UniGetUI.PackageEngine.Managers.Npm/UniGetUI.PackageEngine.Managers.Npm.csproj @@ -0,0 +1,21 @@ + + + + + enable + + + + + + + + + + + + + + + + diff --git a/src/UniGetUI/PackageEngine/Managers/Pip.cs b/src/UniGetUI.PackageEngine.Managers.Pip/Pip.cs similarity index 56% rename from src/UniGetUI/PackageEngine/Managers/Pip.cs rename to src/UniGetUI.PackageEngine.Managers.Pip/Pip.cs index f33cc34e9..28f27684f 100644 --- a/src/UniGetUI/PackageEngine/Managers/Pip.cs +++ b/src/UniGetUI.PackageEngine.Managers.Pip/Pip.cs @@ -1,30 +1,57 @@ -using UniGetUI.PackageEngine.Classes; -using UniGetUI.PackageEngine.Operations; -using UniGetUI.Core; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Http; +using System.Diagnostics; using System.Text.Json.Nodes; using System.Text.RegularExpressions; -using System.Threading.Tasks; - -namespace UniGetUI.PackageEngine.Managers +using UniGetUI.Core.Logging; +using UniGetUI.Core.Tools; +using UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers; +using UniGetUI.PackageEngine.Enums; +using UniGetUI.PackageEngine.ManagerClasses.Manager; +using UniGetUI.PackageEngine.PackageClasses; + +namespace UniGetUI.PackageEngine.Managers.PipManager { public class Pip : PackageManager { new public static string[] FALSE_PACKAGE_NAMES = new string[] { "", "WARNING:", "[notice]", "Package", "DEPRECATION:" }; new public static string[] FALSE_PACKAGE_IDS = new string[] { "", "WARNING:", "[notice]", "Package", "DEPRECATION:" }; new public static string[] FALSE_PACKAGE_VERSIONS = new string[] { "", "Ignoring", "invalid" }; + + public Pip() : base() + { + Capabilities = new ManagerCapabilities() + { + CanRunAsAdmin = true, + SupportsCustomVersions = true, + SupportsCustomScopes = true, + SupportsPreRelease = true, + }; + + Properties= new ManagerProperties() + { + Name = "Pip", + Description = CoreTools.Translate("Python's library manager. Full of python libraries and other python-related utilities
Contains: Python libraries and related utilities"), + IconId = "python", + ColorIconId = "pip_color", + ExecutableFriendlyName = "pip", + InstallVerb = "install", + UninstallVerb = "uninstall", + UpdateVerb = "install --upgrade", + ExecutableCallArgs = " -m pip", + DefaultSource = new ManagerSource(this, "pip", new Uri("https://pypi.org/")), + KnownSources = [new ManagerSource(this, "pip", new Uri("https://pypi.org/"))], + + }; + + PackageDetailsProvider = new PipPackageDetailsProvider(this); + } + protected override async Task FindPackages_UnSafe(string query) { List Packages = new(); - string path = await Tools.Which("parse_pip_search"); - if (!File.Exists(path)) + var which_res = await CoreTools.Which("parse_pip_search"); + string path = which_res.Item2; + if (!which_res.Item1) { Process proc = new() { @@ -57,7 +84,7 @@ protected override async Task FindPackages_UnSafe(string query) p.Start(); - string line; + string? line; bool DashesPassed = false; string output = ""; while ((line = await p.StandardOutput.ReadLineAsync()) != null) @@ -78,11 +105,11 @@ protected override async Task FindPackages_UnSafe(string query) if (FALSE_PACKAGE_IDS.Contains(elements[0]) || FALSE_PACKAGE_VERSIONS.Contains(elements[1])) continue; - Packages.Add(new Package(Tools.FormatAsName(elements[0]), elements[0], elements[1], MainSource, this, scope: PackageScope.Global)); + Packages.Add(new Package(Core.Tools.CoreTools.FormatAsName(elements[0]), elements[0], elements[1], DefaultSource, this, scope: PackageScope.Global)); } } output += await p.StandardError.ReadToEndAsync(); - AppTools.LogManagerOperation(this, p, output); + LogOperation(p, output); return Packages.ToArray(); } @@ -104,7 +131,7 @@ protected override async Task GetAvailableUpdates_UnSafe() p.Start(); - string line; + string? line; bool DashesPassed = false; List Packages = new(); string output = ""; @@ -126,11 +153,11 @@ protected override async Task GetAvailableUpdates_UnSafe() if (FALSE_PACKAGE_IDS.Contains(elements[0]) || FALSE_PACKAGE_VERSIONS.Contains(elements[1])) continue; - Packages.Add(new UpgradablePackage(Tools.FormatAsName(elements[0]), elements[0], elements[1], elements[2], MainSource, this, scope: PackageScope.Global)); + Packages.Add(new UpgradablePackage(Core.Tools.CoreTools.FormatAsName(elements[0]), elements[0], elements[1], elements[2], DefaultSource, this, scope: PackageScope.Global)); } } output += await p.StandardError.ReadToEndAsync(); - AppTools.LogManagerOperation(this, p, output); + LogOperation(p, output); return Packages.ToArray(); } @@ -153,7 +180,7 @@ protected override async Task GetInstalledPackages_UnSafe() p.Start(); - string line; + string? line; bool DashesPassed = false; List Packages = new(); string output = ""; @@ -175,11 +202,11 @@ protected override async Task GetInstalledPackages_UnSafe() if (FALSE_PACKAGE_IDS.Contains(elements[0]) || FALSE_PACKAGE_VERSIONS.Contains(elements[1])) continue; - Packages.Add(new Package(Tools.FormatAsName(elements[0]), elements[0], elements[1], MainSource, this, scope: PackageScope.Global)); + Packages.Add(new Package(CoreTools.FormatAsName(elements[0]), elements[0], elements[1], DefaultSource, this, scope: PackageScope.Global)); } } output += await p.StandardError.ReadToEndAsync(); - AppTools.LogManagerOperation(this, p, output); + LogOperation(p, output); return Packages.ToArray(); } @@ -241,145 +268,14 @@ public override string[] GetUninstallParameters(Package package, InstallationOpt return parameters.ToArray(); } - public override ManagerSource GetMainSource() - { - return new ManagerSource(this, "pip", new Uri("https://pypi.org/")); - } - - public override async Task GetPackageDetails_UnSafe(Package package) - { - PackageDetails details = new(package); - - AppTools.Log("Getting package details for " + package.Id); - - string JsonString; - HttpClient client = new(); - JsonString = await client.GetStringAsync($"https://pypi.org/pypi/{package.Id}/json"); - - JsonObject RawInfo = JsonObject.Parse(JsonString) as JsonObject; - - try - { - if ((RawInfo["info"] as JsonObject).ContainsKey("author")) - details.Author = (RawInfo["info"] as JsonObject)["author"].ToString(); - } - catch (Exception ex) { AppTools.Log("Can't load author: " + ex); } - - try - { - if ((RawInfo["info"] as JsonObject).ContainsKey("home_page")) - details.HomepageUrl = new Uri((RawInfo["info"] as JsonObject)["home_page"].ToString()); - } - catch (Exception ex) { AppTools.Log("Can't load home_page: " + ex); } - try - { - if ((RawInfo["info"] as JsonObject).ContainsKey("package_url")) - details.ManifestUrl = new Uri((RawInfo["info"] as JsonObject)["package_url"].ToString()); - } - catch (Exception ex) { AppTools.Log("Can't load package_url: " + ex); } - try - { - if ((RawInfo["info"] as JsonObject).ContainsKey("summary")) - details.Description = (RawInfo["info"] as JsonObject)["summary"].ToString(); - } - catch (Exception ex) { AppTools.Log("Can't load summary: " + ex); } - try - { - if ((RawInfo["info"] as JsonObject).ContainsKey("license")) - details.License = (RawInfo["info"] as JsonObject)["license"].ToString(); - } - catch (Exception ex) { AppTools.Log("Can't load license: " + ex); } - try - { - if ((RawInfo["info"] as JsonObject).ContainsKey("maintainer")) - details.Publisher = (RawInfo["info"] as JsonObject)["maintainer"].ToString(); - } - catch (Exception ex) { AppTools.Log("Can't load maintainer: " + ex); } - try - { - if ((RawInfo["info"] as JsonObject).ContainsKey("classifiers") && (RawInfo["info"] as JsonObject)["classifiers"] is JsonArray) - { - List Tags = new(); - foreach (string line in (RawInfo["info"] as JsonObject)["classifiers"] as JsonArray) - if (line.Contains("License ::")) - details.License = line.Split("::")[^1].Trim(); - else if (line.Contains("Topic ::")) - if (!Tags.Contains(line.Split("::")[^1].Trim())) - Tags.Add(line.Split("::")[^1].Trim()); - details.Tags = Tags.ToArray(); - } - } - catch (Exception ex) { AppTools.Log("Can't load classifiers: " + ex); } - - try - { - JsonObject? url = null; - if (RawInfo.ContainsKey("url")) - - url = RawInfo["url"] as JsonObject; - else if (RawInfo.ContainsKey("urls")) - url = (RawInfo["urls"] as JsonArray)[0] as JsonObject; - - if (url != null) - { - if (url.ContainsKey("digests") && (url["digests"] as JsonObject).ContainsKey("sha256")) - { - details.InstallerHash = url["digests"]["sha256"].ToString(); - } - if (url.ContainsKey("url")) - { - details.InstallerType = url["url"].ToString().Split('.')[^1].Replace("whl", "Wheel"); - details.InstallerUrl = new Uri(url["url"].ToString()); - details.InstallerSize = await Tools.GetFileSizeAsync(details.InstallerUrl); - } - } - } - catch (Exception ex) { AppTools.Log("Can't load installer data: " + ex); } - - return details; - } - -#pragma warning disable CS1998 - public override async Task RefreshPackageIndexes() - { - // Pip does not support manual source refreshing - } - - protected override ManagerCapabilities GetCapabilities() - { - return new ManagerCapabilities() - { - CanRunAsAdmin = true, - SupportsCustomVersions = true, - SupportsCustomScopes = true, - SupportsPreRelease = true, - }; - } - - protected override ManagerProperties GetProperties() - { - ManagerProperties properties = new() - { - Name = "Pip", - Description = Tools.Translate("Python's library manager. Full of python libraries and other python-related utilities
Contains: Python libraries and related utilities"), - IconId = "python", - ColorIconId = "pip_color", - ExecutableFriendlyName = "pip", - InstallVerb = "install", - UninstallVerb = "uninstall", - UpdateVerb = "install --upgrade", - ExecutableCallArgs = " -m pip", - - }; - return properties; - } protected override async Task LoadManager() { ManagerStatus status = new(); - status.ExecutablePath = await Tools.Which("python.exe"); - status.Found = File.Exists(status.ExecutablePath); + var which_res = await CoreTools.Which("python.exe"); + status.ExecutablePath = which_res.Item2; + status.Found = which_res.Item1; if (!status.Found) return status; @@ -405,39 +301,6 @@ protected override async Task LoadManager() return status; } - - protected override async Task GetPackageVersions_Unsafe(Package package) - { - Process p = new() - { - StartInfo = new ProcessStartInfo() - { - FileName = Status.ExecutablePath, - Arguments = Properties.ExecutableCallArgs + " index versions " + package.Id, - RedirectStandardOutput = true, - RedirectStandardInput = true, - RedirectStandardError = true, - UseShellExecute = false, - CreateNoWindow = true, - StandardOutputEncoding = System.Text.Encoding.UTF8 - } - }; - - p.Start(); - - string line; - string[] result = new string[0]; - string output = ""; - while ((line = await p.StandardOutput.ReadLineAsync()) != null) - { - output += line + "\n"; - if (line.Contains("Available versions:")) - result = line.Replace("Available versions:", "").Trim().Split(", "); - } - - output += await p.StandardError.ReadToEndAsync(); - AppTools.LogManagerOperation(this, p, output); - return result; - } } } + diff --git a/src/UniGetUI.PackageEngine.Managers.Pip/PipPackageDetailsProvider.cs b/src/UniGetUI.PackageEngine.Managers.Pip/PipPackageDetailsProvider.cs new file mode 100644 index 000000000..5868f40f9 --- /dev/null +++ b/src/UniGetUI.PackageEngine.Managers.Pip/PipPackageDetailsProvider.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Text.Json.Nodes; +using System.Threading.Tasks; +using UniGetUI.Core.IconEngine; +using UniGetUI.Core.Logging; +using UniGetUI.Core.Tools; +using UniGetUI.PackageEngine.Classes.Manager.BaseProviders; +using UniGetUI.PackageEngine.ManagerClasses.Manager; +using UniGetUI.PackageEngine.PackageClasses; + +namespace UniGetUI.PackageEngine.Managers.PipManager +{ + internal class PipPackageDetailsProvider : BasePackageDetailsProvider + { + public PipPackageDetailsProvider(Pip manager) : base(manager) { } + + protected override async Task GetPackageDetails_Unsafe(Package package) + { + PackageDetails details = new(package); + + + string JsonString; + HttpClient client = new(); + JsonString = await client.GetStringAsync($"https://pypi.org/pypi/{package.Id}/json"); + + JsonObject? RawInfo = JsonObject.Parse(JsonString) as JsonObject; + + if(RawInfo == null) + { + Logger.Error($"Can't load package info on manager {Manager.Name}, JsonObject? RawInfo was null"); + return details; + } + + try + { + if ((RawInfo["info"] as JsonObject)?.ContainsKey("author") ?? false) + details.Author = (RawInfo["info"] as JsonObject)?["author"]?.ToString() ?? ""; + } + catch (Exception ex) { Logger.Debug("[Pip] Can't load author: " + ex); } + + try + { + if ((RawInfo["info"] as JsonObject)?.ContainsKey("home_page") ?? false) + details.HomepageUrl = new Uri((RawInfo["info"] as JsonObject)?["home_page"]?.ToString() ?? ""); + } + catch (Exception ex) { Logger.Debug("[Pip] Can't load home_page: " + ex); } + try + { + if ((RawInfo["info"] as JsonObject)?.ContainsKey("package_url") ?? false) + details.ManifestUrl = new Uri((RawInfo["info"] as JsonObject)?["package_url"]?.ToString() ?? ""); + } + catch (Exception ex) { Logger.Debug("[Pip] Can't load package_url: " + ex); } + try + { + if ((RawInfo["info"] as JsonObject)?.ContainsKey("summary") ?? false) + details.Description = (RawInfo["info"] as JsonObject)?["summary"]?.ToString() ?? ""; + } + catch (Exception ex) { Logger.Debug("[Pip] Can't load summary: " + ex); } + try + { + if ((RawInfo["info"] as JsonObject)?.ContainsKey("license") ?? false) + details.License = (RawInfo["info"] as JsonObject)?["license"]?.ToString() ?? ""; + } + catch (Exception ex) { Logger.Debug("[Pip] Can't load license: " + ex); } + try + { + if ((RawInfo["info"] as JsonObject)?.ContainsKey("maintainer") ?? false) + details.Publisher = (RawInfo["info"] as JsonObject)?["maintainer"]?.ToString() ?? ""; + } + catch (Exception ex) { Logger.Debug("[Pip] Can't load maintainer: " + ex); } + try + { + if (((RawInfo["info"] as JsonObject)?.ContainsKey("classifiers") ?? false) + && ((RawInfo["info"] as JsonObject)?["classifiers"] is JsonArray)) + { + List Tags = new(); + foreach (string? line in (RawInfo["info"] as JsonObject)?["classifiers"] as JsonArray ?? new()) + if (line?.Contains("License ::") ?? false) + details.License = line.Split("::")[^1].Trim(); + else if (line?.Contains("Topic ::") ?? false) + if (!Tags.Contains(line.Split("::")[^1].Trim())) + Tags.Add(line.Split("::")[^1].Trim()); + details.Tags = Tags.ToArray(); + } + } + catch (Exception ex) { Logger.Debug("[Pip] Can't load classifiers: " + ex); } + + try + { + JsonObject? url = null; + if (RawInfo.ContainsKey("url")) + + url = RawInfo["url"] as JsonObject; + else if (RawInfo.ContainsKey("urls")) + url = (RawInfo["urls"] as JsonArray)?[0] as JsonObject; + + if (url != null) + { + if (url.ContainsKey("digests") && ((url["digests"] as JsonObject)?.ContainsKey("sha256") ?? false)) + { + details.InstallerHash = url["digests"]?["sha256"]?.ToString() ?? ""; + } + if (url.ContainsKey("url")) + { + details.InstallerType = url["url"]?.ToString().Split('.')[^1].Replace("whl", "Wheel") ?? ""; + details.InstallerUrl = url["url"] != null? new Uri(url["url"]?.ToString() ?? ""): null; + details.InstallerSize = await CoreTools.GetFileSizeAsync(details.InstallerUrl); + } + } + } + catch (Exception ex) { Logger.Debug("Can't load installer data: " + ex); } + + return details; + } + + protected override Task GetPackageIcon_Unsafe(Package package) + { + throw new NotImplementedException(); + } + + protected override Task GetPackageScreenshots_Unsafe(Package package) + { + throw new NotImplementedException(); + } + + protected override async Task GetPackageVersions_Unsafe(Package package) + { + Process p = new() + { + StartInfo = new ProcessStartInfo() + { + FileName = Manager.Status.ExecutablePath, + Arguments = Manager.Properties.ExecutableCallArgs + " index versions " + package.Id, + RedirectStandardOutput = true, + RedirectStandardInput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, + StandardOutputEncoding = System.Text.Encoding.UTF8 + } + }; + + p.Start(); + + string? line; + string[] result = new string[0]; + string output = ""; + while ((line = await p.StandardOutput.ReadLineAsync()) != null) + { + output += line + "\n"; + if (line.Contains("Available versions:")) + result = line.Replace("Available versions:", "").Trim().Split(", "); + } + + output += await p.StandardError.ReadToEndAsync(); + Manager.LogOperation(p, output); + return result; + } + } +} diff --git a/src/UniGetUI.PackageEngine.Managers.Pip/UniGetUI.PackageEngine.Managers.Pip.csproj b/src/UniGetUI.PackageEngine.Managers.Pip/UniGetUI.PackageEngine.Managers.Pip.csproj new file mode 100644 index 000000000..0d96aa278 --- /dev/null +++ b/src/UniGetUI.PackageEngine.Managers.Pip/UniGetUI.PackageEngine.Managers.Pip.csproj @@ -0,0 +1,21 @@ + + + + + + enable + + + + + + + + + + + + + + + diff --git a/src/UniGetUI.PackageEngine.Managers.PowerShell/PowerShell.cs b/src/UniGetUI.PackageEngine.Managers.PowerShell/PowerShell.cs new file mode 100644 index 000000000..81b22f395 --- /dev/null +++ b/src/UniGetUI.PackageEngine.Managers.PowerShell/PowerShell.cs @@ -0,0 +1,274 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using UniGetUI.Core; +using UniGetUI.PackageEngine.Classes; +using UniGetUI.PackageEngine.Enums; +using UniGetUI.Core.Logging; +using UniGetUI.Core.Tools; +using UniGetUI.PackageEngine.ManagerClasses.Manager; +using UniGetUI.PackageEngine.PackageClasses; +using UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers; + +namespace UniGetUI.PackageEngine.Managers.PowerShellManager +{ + public class PowerShell : BaseNuGet + { + new public static string[] FALSE_PACKAGE_NAMES = new string[] { "" }; + new public static string[] FALSE_PACKAGE_IDS = new string[] { "" }; + new public static string[] FALSE_PACKAGE_VERSIONS = new string[] { "" }; + + public PowerShell() : base() + { + Capabilities = new ManagerCapabilities() + { + CanRunAsAdmin = true, + CanSkipIntegrityChecks = true, + SupportsCustomVersions = true, + SupportsCustomScopes = true, + SupportsCustomSources = true, + SupportsPreRelease = true, + SupportsCustomPackageIcons = true, + Sources = new ManagerSource.Capabilities() + { + KnowsPackageCount = false, + KnowsUpdateDate = false, + } + }; + + Properties = new ManagerProperties() + { + Name = "PowerShell", + Description = CoreTools.Translate("PowerShell's package manager. Find libraries and scripts to expand PowerShell capabilities
Contains: Modules, Scripts, Cmdlets"), + IconId = "powershell", + ColorIconId = "powershell_color", + ExecutableFriendlyName = "powershell.exe", + InstallVerb = "Install-Module", + UninstallVerb = "Uninstall-Module", + UpdateVerb = "Update-Module", + ExecutableCallArgs = " -NoProfile -Command", + KnownSources = [new(this, "PSGallery", new Uri("https://www.powershellgallery.com/api/v2")), + new(this, "PoshTestGallery", new Uri("https://www.poshtestgallery.com/api/v2"))], + DefaultSource = new ManagerSource(this, "PSGallery", new Uri("https://www.powershellgallery.com/api/v2")), + }; + + SourceProvider = new PowerShellSourceProvider(this); + } + protected override async Task GetAvailableUpdates_UnSafe() + { + Process p = new(); + p.StartInfo = new ProcessStartInfo() + { + FileName = Status.ExecutablePath, + Arguments = "", + RedirectStandardOutput = true, + RedirectStandardError = true, + RedirectStandardInput = true, + UseShellExecute = false, + CreateNoWindow = true, + StandardOutputEncoding = System.Text.Encoding.UTF8 + }; + + p.Start(); + + await p.StandardInput.WriteLineAsync(@" + function Test-GalleryModuleUpdate { + param ( + [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [string] $Name, + [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [version] $Version, + [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [string] $Repository, + [switch] $NeedUpdateOnly + ) + process { + $URLs = @{} + @(Get-PSRepository).ForEach({$URLs[$_.Name] = $_.SourceLocation}) + $page = Invoke-WebRequest -Uri ($URLs[$Repository] + ""/package/$Name"") -UseBasicParsing -Maximum 0 -ea Ignore + [version]$latest = Split-Path -Path ($page.Headers.Location -replace ""$Name."" -replace "".nupkg"") -Leaf + $needsupdate = $Latest -gt $Version + if ($needsupdate) { + Write-Output($Name + ""|"" + $Version.ToString() + ""|"" + $Latest.ToString() + ""|"" + $Repository) + } + } + } + Get-InstalledModule | Test-GalleryModuleUpdate + + exit + "); // do NOT remove the trailing endline + string? line; + List Packages = new(); + string output = ""; + while ((line = await p.StandardOutput.ReadLineAsync()) != null) + { + output += line + "\n"; + if (line.StartsWith(">>")) + continue; + + string[] elements = line.Split('|'); + if (elements.Length < 4) + continue; + + for (int i = 0; i < elements.Length; i++) elements[i] = elements[i].Trim(); + + if (elements[1] + ".0" == elements[2] || elements[1] + ".0.0" == elements[2]) + continue; + + Packages.Add(new UpgradablePackage(Core.Tools.CoreTools.FormatAsName(elements[0]), elements[0], elements[1], elements[2], GetSourceOrDefault(elements[3]), this)); + } + + output += await p.StandardError.ReadToEndAsync(); + LogOperation(p, output); + await p.WaitForExitAsync(); + + return Packages.ToArray(); + } + + protected override async Task GetInstalledPackages_UnSafe() + { + Process p = new(); + p.StartInfo = new ProcessStartInfo() + { + FileName = Status.ExecutablePath, + Arguments = Properties.ExecutableCallArgs + " Get-InstalledModule", + RedirectStandardOutput = true, + RedirectStandardError = true, + RedirectStandardInput = true, + UseShellExecute = false, + CreateNoWindow = true, + StandardOutputEncoding = System.Text.Encoding.UTF8 + }; + + p.Start(); + string? line; + List Packages = new(); + bool DashesPassed = false; + string output = ""; + while ((line = await p.StandardOutput.ReadLineAsync()) != null) + { + output += line + "\n"; + if (!DashesPassed) + { + if (line.Contains("-----")) + DashesPassed = true; + } + else + { + string[] elements = Regex.Replace(line, " {2,}", " ").Split(' '); + if (elements.Length < 3) + continue; + + for (int i = 0; i < elements.Length; i++) elements[i] = elements[i].Trim(); + + Packages.Add(new Package(CoreTools.FormatAsName(elements[1]), elements[1], elements[0], GetSourceOrDefault(elements[2]), this)); + } + } + + output += await p.StandardError.ReadToEndAsync(); + LogOperation(p, output); + await p.WaitForExitAsync(); + + return Packages.ToArray(); + } + + public override OperationVeredict GetInstallOperationVeredict(Package package, InstallationOptions options, int ReturnCode, string[] Output) + { + return GetUninstallOperationVeredict(package, options, ReturnCode, Output); + } + + public override OperationVeredict GetUpdateOperationVeredict(Package package, InstallationOptions options, int ReturnCode, string[] Output) + { + return GetUninstallOperationVeredict(package, options, ReturnCode, Output); + } + + public override OperationVeredict GetUninstallOperationVeredict(Package package, InstallationOptions options, int ReturnCode, string[] Output) + { + string output_string = string.Join("\n", Output); + + if (output_string.Contains("AdminPrivilegesAreRequired") && !options.RunAsAdministrator) + { + options.RunAsAdministrator = true; + return OperationVeredict.AutoRetry; + } + + return ReturnCode == 0 ? OperationVeredict.Succeeded : OperationVeredict.Failed; + } + public override string[] GetInstallParameters(Package package, InstallationOptions options) + { + List parameters = GetUpdateParameters(package, options).ToList(); + parameters[0] = Properties.InstallVerb; + + parameters.AddRange(new string[] { "-AllowClobber" }); + if (package.Scope == PackageScope.Global) + parameters.AddRange(new string[] { "-Scope", "AllUsers" }); + else + parameters.AddRange(new string[] { "-Scope", "CurrentUser" }); + + if (options.Version != "") + parameters.AddRange(new string[] { "-RequiredVersion", options.Version }); + + return parameters.ToArray(); + + } + public override string[] GetUpdateParameters(Package package, InstallationOptions options) + { + List parameters = GetUninstallParameters(package, options).ToList(); + parameters[0] = Properties.UpdateVerb; + + if (options.PreRelease) + parameters.Add("-AllowPrerelease"); + + if (options.SkipHashCheck) + parameters.Add("-SkipPublisherCheck"); + + return parameters.ToArray(); + } + + public override string[] GetUninstallParameters(Package package, InstallationOptions options) + { + List parameters = new() { Properties.UninstallVerb, "-Name", package.Id, "-Confirm:$false", "-Force" }; + + if (options.CustomParameters != null) + parameters.AddRange(options.CustomParameters); + + return parameters.ToArray(); + } + + protected override async Task LoadManager() + { + ManagerStatus status = new() + { + ExecutablePath = Path.Join(Environment.SystemDirectory, "windowspowershell\\v1.0\\powershell.exe") + }; + status.Found = File.Exists(status.ExecutablePath); + + if (!status.Found) + return status; + + Process process = new() + { + StartInfo = new ProcessStartInfo() + { + FileName = status.ExecutablePath, + Arguments = Properties.ExecutableCallArgs + " \"echo $PSVersionTable\"", + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true, + StandardOutputEncoding = System.Text.Encoding.UTF8 + } + }; + process.Start(); + status.Version = (await process.StandardOutput.ReadToEndAsync()).Trim(); + + if (status.Found && IsEnabled()) + await RefreshPackageIndexes(); + + return status; + } + + } + +} diff --git a/src/UniGetUI.PackageEngine.Managers.PowerShell/PowerShellSourceProvider.cs b/src/UniGetUI.PackageEngine.Managers.PowerShell/PowerShellSourceProvider.cs new file mode 100644 index 000000000..a501d9de4 --- /dev/null +++ b/src/UniGetUI.PackageEngine.Managers.PowerShell/PowerShellSourceProvider.cs @@ -0,0 +1,91 @@ +using System.Diagnostics; +using System.Text.RegularExpressions; +using UniGetUI.Core.Logging; +using UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers; +using UniGetUI.PackageEngine.Classes.Manager.Providers; +using UniGetUI.PackageEngine.Enums; +using UniGetUI.PackageEngine.ManagerClasses.Manager; + +namespace UniGetUI.PackageEngine.Managers.PowerShellManager +{ + internal class PowerShellSourceProvider : BaseSourceProvider + { + public PowerShellSourceProvider(PowerShell manager) : base(manager) { } + + public override string[] GetAddSourceParameters(ManagerSource source) + { + if (source.Url.ToString() == "https://www.powershellgallery.com/api/v2") + return new string[] { "Register-PSRepository", "-Default" }; + return new string[] { "Register-PSRepository", "-Name", source.Name, "-SourceLocation", source.Url.ToString() }; + } + + public override string[] GetRemoveSourceParameters(ManagerSource source) + { + return new string[] { "Unregister-PSRepository", "-Name", source.Name }; + } + + public override OperationVeredict GetAddSourceOperationVeredict(ManagerSource source, int ReturnCode, string[] Output) + { + return ReturnCode == 0 ? OperationVeredict.Succeeded : OperationVeredict.Failed; + } + + public override OperationVeredict GetRemoveSourceOperationVeredict(ManagerSource source, int ReturnCode, string[] Output) + { + return ReturnCode == 0 ? OperationVeredict.Succeeded : OperationVeredict.Failed; + } + + protected override async Task GetSources_UnSafe() + { + List sources = new(); + + Process process = new(); + ProcessStartInfo startInfo = new() + { + FileName = Manager.Status.ExecutablePath, + Arguments = Manager.Properties.ExecutableCallArgs + " Get-PSRepository", + RedirectStandardOutput = true, + RedirectStandardError = true, + RedirectStandardInput = true, + UseShellExecute = false, + CreateNoWindow = true, + StandardOutputEncoding = System.Text.Encoding.UTF8 + }; + + process.StartInfo = startInfo; + process.Start(); + + bool dashesPassed = false; + string? line; + string output = ""; + while ((line = await process.StandardOutput.ReadLineAsync()) != null) + { + output += line + "\n"; + try + { + if (string.IsNullOrEmpty(line)) + continue; + + if (!dashesPassed) + { + if (line.Contains("---")) + dashesPassed = true; + } + else + { + string[] parts = Regex.Replace(line.Trim(), " {2,}", " ").Split(' '); + if (parts.Length >= 3) + sources.Add(new ManagerSource(Manager, parts[0].Trim(), new Uri(parts[2].Trim()))); + } + } + catch (Exception e) + { + Logger.Warn(e); + } + } + output += await process.StandardError.ReadToEndAsync(); + Manager.LogOperation(process, output); + await process.WaitForExitAsync(); + return sources.ToArray(); + } + } +} diff --git a/src/UniGetUI.PackageEngine.Managers.PowerShell/UniGetUI.PackageEngine.Managers.PowerShell.csproj b/src/UniGetUI.PackageEngine.Managers.PowerShell/UniGetUI.PackageEngine.Managers.PowerShell.csproj new file mode 100644 index 000000000..942b61f2f --- /dev/null +++ b/src/UniGetUI.PackageEngine.Managers.PowerShell/UniGetUI.PackageEngine.Managers.PowerShell.csproj @@ -0,0 +1,23 @@ + + + + + + enable + + + + + + + + + + + + + + + + + diff --git a/src/UniGetUI.PackageEngine.Managers.Scoop/Scoop.cs b/src/UniGetUI.PackageEngine.Managers.Scoop/Scoop.cs new file mode 100644 index 000000000..1ebac5931 --- /dev/null +++ b/src/UniGetUI.PackageEngine.Managers.Scoop/Scoop.cs @@ -0,0 +1,431 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text.Json.Nodes; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using UniGetUI.Core; +using UniGetUI.PackageEngine.Classes; +using UniGetUI.PackageEngine.Enums; +using UniGetUI.Core.Logging; +using UniGetUI.Core.Tools; +using UniGetUI.Core.SettingsEngine; +using UniGetUI.PackageEngine.PackageClasses; +using UniGetUI.PackageEngine.ManagerClasses.Manager; +using UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers; + +namespace UniGetUI.PackageEngine.Managers.ScoopManager +{ + + public class Scoop : PackageManager + { + new public static string[] FALSE_PACKAGE_NAMES = new string[] { "" }; + new public static string[] FALSE_PACKAGE_IDS = new string[] { "No" }; + new public static string[] FALSE_PACKAGE_VERSIONS = new string[] { "Matches" }; + + public Scoop(): base() + { + Capabilities = new ManagerCapabilities() + { + CanRunAsAdmin = true, + CanSkipIntegrityChecks = true, + CanRemoveDataOnUninstall = true, + SupportsCustomArchitectures = true, + SupportedCustomArchitectures = new Architecture[] { Architecture.X86, Architecture.X64, Architecture.Arm64 }, + SupportsCustomScopes = true, + SupportsCustomSources = true, + Sources = new ManagerSource.Capabilities() + { + KnowsPackageCount = true, + KnowsUpdateDate = true + } + }; + + Properties = new ManagerProperties() + { + Name = "Scoop", + Description = CoreTools.Translate("Great repository of unknown but useful utilities and other interesting packages.
Contains: Utilities, Command-line programs, General Software (extras bucket required)"), + IconId = "scoop", + ColorIconId = "scoop_color", + ExecutableCallArgs = " -NoProfile -ExecutionPolicy Bypass -Command scoop", + ExecutableFriendlyName = "scoop", + InstallVerb = "install", + UpdateVerb = "update", + UninstallVerb = "uninstall", + KnownSources = [new(this, "main", new Uri("https://github.com/ScoopInstaller/Main")), + new(this, "extras", new Uri("https://github.com/ScoopInstaller/Extras")), + new(this, "versions", new Uri("https://github.com/ScoopInstaller/Versions")), + new(this, "nirsoft", new Uri("https://github.com/kodybrown/scoop-nirsoft")), + new(this, "sysinternals", new Uri("https://github.com/niheaven/scoop-sysinternals")), + new(this, "php", new Uri("https://github.com/ScoopInstaller/PHP")), + new(this, "nerd-fonts", new Uri("https://github.com/matthewjberger/scoop-nerd-fonts")), + new(this, "nonportable", new Uri("https://github.com/ScoopInstaller/Nonportable")), + new(this, "java", new Uri("https://github.com/ScoopInstaller/Java")), + new(this, "games", new Uri("https://github.com/Calinou/scoop-games"))], + DefaultSource = new(this, "main", new Uri("https://github.com/ScoopInstaller/Main")), + }; + + SourceProvider = new ScoopSourceProvider(this); + PackageDetailsProvider = new ScoopPackageDetailsProvider(this); + } + + protected override async Task FindPackages_UnSafe(string query) + { + List Packages = new(); + + var which_res = await CoreTools.Which("scoop-search.exe"); + string path = which_res.Item2; + if (!which_res.Item1) + { + Process proc = new() + { + StartInfo = new ProcessStartInfo() + { + FileName = Status.ExecutablePath, + Arguments = Properties.ExecutableCallArgs + " install main/scoop-search", + UseShellExecute = true, + CreateNoWindow = true + } + }; + proc.Start(); + await proc.WaitForExitAsync(); + path = "scoop-search.exe"; + } + + Process p = new() + { + StartInfo = new ProcessStartInfo() + { + FileName = path, + Arguments = query, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, + StandardOutputEncoding = System.Text.Encoding.UTF8 + } + }; + + p.Start(); + + string? line; + ManagerSource source = Properties.DefaultSource; + string output = ""; + while ((line = await p.StandardOutput.ReadLineAsync()) != null) + { + output += line + "\n"; + if (line.StartsWith("'")) + { + string sourceName = line.Split(" ")[0].Replace("'", ""); + source = GetSourceOrDefault(sourceName); + } + else if (line.Trim() != "") + { + string[] elements = line.Trim().Split(" "); + if (elements.Length < 2) + continue; + + for (int i = 0; i < elements.Length; i++) elements[i] = elements[i].Trim(); + + if (FALSE_PACKAGE_IDS.Contains(elements[0]) || FALSE_PACKAGE_VERSIONS.Contains(elements[1])) + continue; + + Packages.Add(new Package(Core.Tools.CoreTools.FormatAsName(elements[0]), elements[0], elements[1].Replace("(", "").Replace(")", ""), source, this)); + } + } + output += await p.StandardError.ReadToEndAsync(); + LogOperation(p, output); + return Packages.ToArray(); + } + + protected override async Task GetAvailableUpdates_UnSafe() + { + Dictionary InstalledPackages = new(); + foreach (Package InstalledPackage in await GetInstalledPackages()) + { + if (!InstalledPackages.ContainsKey(InstalledPackage.Id + "." + InstalledPackage.Version)) + InstalledPackages.Add(InstalledPackage.Id + "." + InstalledPackage.Version, InstalledPackage); + } + + List Packages = new(); + + Process p = new() + { + StartInfo = new ProcessStartInfo() + { + FileName = Status.ExecutablePath, + Arguments = Properties.ExecutableCallArgs + " status", + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, + StandardOutputEncoding = System.Text.Encoding.UTF8 + } + }; + + p.Start(); + + string? line; + bool DashesPassed = false; + string output = ""; + while ((line = await p.StandardOutput.ReadLineAsync()) != null) + { + output += line + "\n"; + if (!DashesPassed) + { + if (line.Contains("---")) + DashesPassed = true; + } + else if (line.Trim() != "") + { + string[] elements = Regex.Replace(line, " {2,}", " ").Trim().Split(" "); + if (elements.Length < 3) + continue; + + for (int i = 0; i < elements.Length; i++) elements[i] = elements[i].Trim(); + + if (FALSE_PACKAGE_IDS.Contains(elements[0]) || FALSE_PACKAGE_VERSIONS.Contains(elements[1])) + continue; + + if (!InstalledPackages.ContainsKey(elements[0] + "." + elements[1])) + { + Logger.Warn("Upgradable scoop package not listed on installed packages - id=" + elements[0]); + continue; + } + + Packages.Add(new UpgradablePackage(Core.Tools.CoreTools.FormatAsName(elements[0]), elements[0], elements[1], elements[2], InstalledPackages[elements[0] + "." + elements[1]].Source, this, InstalledPackages[elements[0] + "." + elements[1]].Scope)); + } + } + output += await p.StandardError.ReadToEndAsync(); + LogOperation(p, output); + return Packages.ToArray(); + } + + protected override async Task GetInstalledPackages_UnSafe() + { + List Packages = new(); + + Process p = new() + { + StartInfo = new ProcessStartInfo() + { + FileName = Status.ExecutablePath, + Arguments = Properties.ExecutableCallArgs + " list", + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, + StandardOutputEncoding = System.Text.Encoding.UTF8 + } + }; + + p.Start(); + + string? line; + bool DashesPassed = false; + string output = ""; + while ((line = await p.StandardOutput.ReadLineAsync()) != null) + { + output += line + "\n"; + if (!DashesPassed) + { + if (line.Contains("---")) + DashesPassed = true; + } + else if (line.Trim() != "") + { + string[] elements = Regex.Replace(line, " {2,}", " ").Trim().Split(" "); + if (elements.Length < 3) + continue; + + for (int i = 0; i < elements.Length; i++) elements[i] = elements[i].Trim(); + + if (FALSE_PACKAGE_IDS.Contains(elements[0]) || FALSE_PACKAGE_VERSIONS.Contains(elements[1])) + continue; + + PackageScope scope = PackageScope.User; + if (line.Contains("Global install")) + scope = PackageScope.Global; + + Packages.Add(new Package(Core.Tools.CoreTools.FormatAsName(elements[0]), elements[0], elements[1], GetSourceOrDefault(elements[2]), this, scope)); + } + } + output += await p.StandardError.ReadToEndAsync(); + LogOperation(p, output); + return Packages.ToArray(); + } + + + public override OperationVeredict GetUninstallOperationVeredict(Package package, InstallationOptions options, int ReturnCode, string[] Output) + { + string output_string = string.Join("\n", Output); + if ((output_string.Contains("Try again with the --global (or -g) flag instead") && package.Scope == PackageScope.Local)) + { + package.Scope = PackageScope.Global; + return OperationVeredict.AutoRetry; + } + if (output_string.Contains("requires admin rights") || output_string.Contains("requires administrator rights") || output_string.Contains("you need admin rights to install global apps")) + { + options.RunAsAdministrator = true; + return OperationVeredict.AutoRetry; + } + if (output_string.Contains("was uninstalled")) + return OperationVeredict.Succeeded; + return OperationVeredict.Failed; + } + public override OperationVeredict GetInstallOperationVeredict(Package package, InstallationOptions options, int ReturnCode, string[] Output) + { + string output_string = string.Join("\n", Output); + if ((output_string.Contains("Try again with the --global (or -g) flag instead") && package.Scope == PackageScope.Local)) + { + package.Scope = PackageScope.Global; + return OperationVeredict.AutoRetry; + } + if (output_string.Contains("requires admin rights") || output_string.Contains("requires administrator rights") || output_string.Contains("you need admin rights to install global apps")) + { + options.RunAsAdministrator = true; + return OperationVeredict.AutoRetry; + } + if (output_string.Contains("ERROR")) + return OperationVeredict.Failed; + return OperationVeredict.Succeeded; + } + public override OperationVeredict GetUpdateOperationVeredict(Package package, InstallationOptions options, int ReturnCode, string[] Output) + { + return GetInstallOperationVeredict(package, options, ReturnCode, Output); + } + + public override string[] GetUninstallParameters(Package package, InstallationOptions options) + { + List parameters = new(); + + parameters.Add(Properties.UninstallVerb); + parameters.Add(package.Source.Name + "/" + package.Id); + + if (package.Scope == PackageScope.Global) + parameters.Add("--global"); + + if (options.CustomParameters != null) + parameters.AddRange(options.CustomParameters); + + if (options.RemoveDataOnUninstall) + parameters.Add("--purge"); + + return parameters.ToArray(); + } + public override string[] GetInstallParameters(Package package, InstallationOptions options) + { + string[] parameters = GetUpdateParameters(package, options); + parameters[0] = Properties.InstallVerb; + return parameters; + } + public override string[] GetUpdateParameters(Package package, InstallationOptions options) + { + List parameters = GetUninstallParameters(package, options).ToList(); + parameters[0] = Properties.UpdateVerb; + + parameters.Remove("--purge"); + + switch (options.Architecture) + { + case null: + break; + case Architecture.X64: + parameters.Add("--arch"); + parameters.Add("64bit"); + break; + case Architecture.X86: + parameters.Add("--arch"); + parameters.Add("32bit"); + break; + case Architecture.Arm64: + parameters.Add("--arch"); + parameters.Add("arm64"); + break; + } + + if (options.SkipHashCheck) + { + parameters.Add("--skip"); + } + + return parameters.ToArray(); + } + + public override async Task RefreshPackageIndexes() + { + Process process = new(); + ProcessStartInfo StartInfo = new() + { + FileName = Properties.ExecutableFriendlyName, + Arguments = Properties.ExecutableCallArgs + " update" + }; + process.StartInfo = StartInfo; + process.Start(); + await process.WaitForExitAsync(); + } + + protected override async Task LoadManager() + { + ManagerStatus status = new() + { + ExecutablePath = Path.Join(Environment.SystemDirectory, "windowspowershell\\v1.0\\powershell.exe") + }; + + Process process = new() + { + StartInfo = new ProcessStartInfo() + { + FileName = status.ExecutablePath, + Arguments = Properties.ExecutableCallArgs + " --version", + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true, + StandardOutputEncoding = System.Text.Encoding.UTF8 + } + }; + process.Start(); + status.Version = (await process.StandardOutput.ReadToEndAsync()).Trim(); + status.Found = process.ExitCode == 0; + + + if (status.Found && IsEnabled()) + _ = RefreshPackageIndexes(); + + + Status = status; // Wee need this for the RunCleanup method to get the executable path + if (status.Found && IsEnabled() && Settings.Get("EnableScoopCleanup")) + RunCleanup(); + + return status; + } + + private async void RunCleanup() + { + Logger.Info("Starting scoop cleanup..."); + foreach (string command in new string[] { " cache rm *", " cleanup --all --cache", " cleanup --all --global --cache" }) + { + Process p = new() + { + StartInfo = new ProcessStartInfo() + { + FileName = Status.ExecutablePath, + Arguments = Properties.ExecutableCallArgs + " " + command, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true, + StandardOutputEncoding = System.Text.Encoding.UTF8 + } + }; + p.Start(); + await p.WaitForExitAsync(); + } + Logger.Info("Scoop cleanup finished!"); + } + } +} \ No newline at end of file diff --git a/src/UniGetUI.PackageEngine.Managers.Scoop/ScoopPackageDetailsProvider.cs b/src/UniGetUI.PackageEngine.Managers.Scoop/ScoopPackageDetailsProvider.cs new file mode 100644 index 000000000..9c098a181 --- /dev/null +++ b/src/UniGetUI.PackageEngine.Managers.Scoop/ScoopPackageDetailsProvider.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Text.Json.Nodes; +using System.Threading.Tasks; +using System.Xml.Linq; +using UniGetUI.Core.IconEngine; +using UniGetUI.Core.Logging; +using UniGetUI.Core.Tools; +using UniGetUI.PackageEngine.Classes.Manager.BaseProviders; +using UniGetUI.PackageEngine.ManagerClasses.Manager; +using UniGetUI.PackageEngine.PackageClasses; + +namespace UniGetUI.PackageEngine.Managers.ScoopManager +{ + internal class ScoopPackageDetailsProvider : BasePackageDetailsProvider + { + public ScoopPackageDetailsProvider(Scoop manager) : base(manager) { } + + protected override async Task GetPackageDetails_Unsafe(Package package) + { + PackageDetails details = new(package); + + if (package.Source.Url != null) + try + { + details.ManifestUrl = new Uri(package.Source.Url.ToString() + "/blob/master/bucket/" + package.Id + ".json"); + } + catch (Exception ex) + { + Logger.Error("Cannot load package manifest URL"); + Logger.Error(ex); + } + + Process p = new(); + p.StartInfo = new ProcessStartInfo() + { + FileName = Manager.Status.ExecutablePath, + Arguments = Manager.Properties.ExecutableCallArgs + " cat " + package.Source.Name + "/" + package.Id, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, + StandardOutputEncoding = System.Text.Encoding.UTF8, + }; + + p.Start(); + string JsonString = await p.StandardOutput.ReadToEndAsync(); + + JsonObject? RawInfo = JsonObject.Parse(JsonString) as JsonObject; + + if(RawInfo == null) + { + throw new Exception("Deserialized RawInfo was null"); + } + + try + { + if (RawInfo.ContainsKey("description") && (RawInfo["description"] is JsonArray)) + { + details.Description = ""; + foreach (JsonNode? note in RawInfo["description"] as JsonArray ?? new()) + details.Description += note?.ToString() + "\n"; + details.Description = details.Description.Replace("\n\n", "\n").Trim(); + } + else if (RawInfo.ContainsKey("description")) + details.Description = RawInfo["description"]?.ToString() ?? ""; + } + catch (Exception ex) { Logger.Debug("[Scoop] Can't load description: " + ex); } + + try + { + if (RawInfo.ContainsKey("innosetup")) + details.InstallerType = "Inno Setup (" + CoreTools.Translate("extracted") + ")"; + else + details.InstallerType = CoreTools.Translate("Scoop package"); + } + catch (Exception ex) { Logger.Debug("[Scoop] Can't load installer type: " + ex); } + + try + { + if (RawInfo.ContainsKey("homepage")) + { + details.HomepageUrl = RawInfo["homepage"] != null? new Uri(RawInfo["homepage"]?.ToString() ?? ""): null; + if (details.HomepageUrl?.ToString().Contains("https://github.com/") ?? false) + details.Author = details.HomepageUrl.ToString().Replace("https://github.com/", "").Split("/")[0]; + else + details.Author = details.HomepageUrl?.Host.Split(".")[^2] ?? ""; + } + } + catch (Exception ex) { Logger.Debug("[Scoop] Can't load homepage: " + ex); } + + try + { + if (RawInfo.ContainsKey("notes") && (RawInfo["notes"] is JsonArray)) + { + details.ReleaseNotes = ""; + foreach (JsonNode? note in RawInfo["notes"] as JsonArray ?? new()) + details.ReleaseNotes += note?.ToString() + "\n"; + details.ReleaseNotes = details.ReleaseNotes.Replace("\n\n", "\n").Trim(); + } + else if (RawInfo.ContainsKey("notes")) + details.ReleaseNotes = RawInfo["notes"]?.ToString() ?? ""; + } + catch (Exception ex) { Logger.Debug("[Scoop] Can't load notes: " + ex); } + + try + { + if (RawInfo.ContainsKey("license")) + { + if (RawInfo["license"] is not JsonValue) + { + details.License = RawInfo["license"]?["identifier"]?.ToString() ?? ""; + details.LicenseUrl = RawInfo["license"]?["url"] != null? new Uri(RawInfo["license"]?["url"]?.ToString() ?? ""): null; + } + else + details.License = RawInfo["license"]?.ToString() ?? ""; + } + } + catch (Exception ex) { Logger.Debug("[Scoop] Can't load license: " + ex); } + + try + { + if (RawInfo.ContainsKey("url") && RawInfo.ContainsKey("hash")) + { + if (RawInfo["url"] is JsonArray) + details.InstallerUrl = RawInfo["url"] != null? new Uri(RawInfo["url"]?[0]?.ToString() ?? ""): null; + else + details.InstallerUrl = RawInfo["url"] != null? new Uri(RawInfo["url"]?.ToString() ?? ""): null; + + if (RawInfo["hash"] is JsonArray) + details.InstallerHash = RawInfo["hash"]?[0]?.ToString() ?? ""; + else + details.InstallerHash = RawInfo["hash"]?.ToString() ?? ""; + } + else if (RawInfo.ContainsKey("architecture")) + { + string FirstArch = (RawInfo["architecture"] as JsonObject)?.ElementAt(0).Key ?? ""; + details.InstallerHash = RawInfo["architecture"]?[FirstArch]?["hash"]?.ToString() ?? ""; + details.InstallerUrl = RawInfo["architecture"]?[FirstArch]?["url"] != null? + new Uri(RawInfo["architecture"]?[FirstArch]?["url"]?.ToString() ?? ""): null; + } + + details.InstallerSize = await CoreTools.GetFileSizeAsync(details.InstallerUrl); + } + catch (Exception ex) { Logger.Debug("[Scoop] Can't load installer URL: " + ex); } + + try + { + if (RawInfo.ContainsKey("checkver") && RawInfo["checkver"] is JsonObject && ((RawInfo["checkver"] as JsonObject)?.ContainsKey("url") ?? false)) + details.ReleaseNotesUrl = RawInfo["checkver"]?["url"] != null? new Uri(RawInfo["checkver"]?["url"]?.ToString() ?? ""): null; + } + catch (Exception ex) { Logger.Debug("[Scoop] Can't load notes URL: " + ex); } + + return details; + + } + + protected override Task GetPackageIcon_Unsafe(Package package) + { + throw new NotImplementedException(); + } + + protected override Task GetPackageScreenshots_Unsafe(Package package) + { + throw new NotImplementedException(); + } + +#pragma warning disable + protected override async Task GetPackageVersions_Unsafe(Package package) + { + throw new Exception("Scoop does not support custom package versions"); + } + } +#pragma warning enable +} diff --git a/src/UniGetUI.PackageEngine.Managers.Scoop/ScoopSourceProvider.cs b/src/UniGetUI.PackageEngine.Managers.Scoop/ScoopSourceProvider.cs new file mode 100644 index 000000000..fa17cb335 --- /dev/null +++ b/src/UniGetUI.PackageEngine.Managers.Scoop/ScoopSourceProvider.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using UniGetUI.Core.Logging; +using UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers; +using UniGetUI.PackageEngine.Classes.Manager.Providers; +using UniGetUI.PackageEngine.Enums; +using UniGetUI.PackageEngine.ManagerClasses.Manager; + +namespace UniGetUI.PackageEngine.Managers.ScoopManager +{ + internal class ScoopSourceProvider : BaseSourceProvider + { + public ScoopSourceProvider(Scoop manager) : base(manager) { } + + public override OperationVeredict GetAddSourceOperationVeredict(ManagerSource source, int ReturnCode, string[] Output) + { + return ReturnCode == 0 ? OperationVeredict.Succeeded : OperationVeredict.Failed; + } + + public override string[] GetAddSourceParameters(ManagerSource source) + { + return new string[] { "bucket", "add", source.Name, source.Url.ToString() }; + } + + public override OperationVeredict GetRemoveSourceOperationVeredict(ManagerSource source, int ReturnCode, string[] Output) + { + return ReturnCode == 0 ? OperationVeredict.Succeeded : OperationVeredict.Failed; + } + + public override string[] GetRemoveSourceParameters(ManagerSource source) + { + return new string[] { "bucket", "rm", source.Name }; + } + + protected override async Task GetSources_UnSafe() + { + using (Process process = new()) + { + process.StartInfo.FileName = Manager.Status.ExecutablePath; + process.StartInfo.Arguments = Manager.Properties.ExecutableCallArgs + " bucket list"; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + process.StartInfo.RedirectStandardInput = true; + process.StartInfo.UseShellExecute = false; + process.StartInfo.CreateNoWindow = true; + process.StartInfo.StandardInputEncoding = System.Text.Encoding.UTF8; + process.StartInfo.StandardOutputEncoding = System.Text.Encoding.UTF8; + + List sources = new(); + + process.Start(); + + string _output = ""; + bool DashesPassed = false; + + string? line; + while ((line = await process.StandardOutput.ReadLineAsync()) != null) + { + _output += line + "\n"; + try + { + if (!DashesPassed) + { + if (line.Contains("---")) + DashesPassed = true; + } + else if (line.Trim() != "") + { + string[] elements = Regex.Replace(line.Replace("AM", "").Replace("PM", "").Trim(), " {2,}", " ").Split(' '); + if (elements.Length >= 5) + { + if (!elements[1].Contains("https://")) + elements[1] = "https://scoop.sh/"; // If the URI is invalid, we'll use the main website + sources.Add(new ManagerSource(Manager, elements[0].Trim(), new Uri(elements[1].Trim()), int.Parse(elements[4].Trim()), elements[2].Trim() + " " + elements[3].Trim())); + } + } + } + catch (Exception e) + { + Logger.Warn(e); + } + } + _output += await process.StandardError.ReadToEndAsync(); + Manager.LogOperation(process, _output); + + await process.WaitForExitAsync(); + + + return sources.ToArray(); + } + } + } +} diff --git a/src/UniGetUI.PackageEngine.Managers.Scoop/UniGetUI.PackageEngine.Managers.Scoop.csproj b/src/UniGetUI.PackageEngine.Managers.Scoop/UniGetUI.PackageEngine.Managers.Scoop.csproj new file mode 100644 index 000000000..1446938aa --- /dev/null +++ b/src/UniGetUI.PackageEngine.Managers.Scoop/UniGetUI.PackageEngine.Managers.Scoop.csproj @@ -0,0 +1,22 @@ + + + + + + enable + + + + + + + + + + + + + + + + diff --git a/src/UniGetUI.PackageEngine.Managers.WinGet/UniGetUI.PackageEngine.Managers.WinGet.csproj b/src/UniGetUI.PackageEngine.Managers.WinGet/UniGetUI.PackageEngine.Managers.WinGet.csproj new file mode 100644 index 000000000..24f61f4f4 --- /dev/null +++ b/src/UniGetUI.PackageEngine.Managers.WinGet/UniGetUI.PackageEngine.Managers.WinGet.csproj @@ -0,0 +1,75 @@ + + + + + + enable + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + diff --git a/src/UniGetUI/PackageEngine/Managers/Winget.cs b/src/UniGetUI.PackageEngine.Managers.WinGet/WinGet.cs similarity index 69% rename from src/UniGetUI/PackageEngine/Managers/Winget.cs rename to src/UniGetUI.PackageEngine.Managers.WinGet/WinGet.cs index 6ea4f067d..2a408544c 100644 --- a/src/UniGetUI/PackageEngine/Managers/Winget.cs +++ b/src/UniGetUI.PackageEngine.Managers.WinGet/WinGet.cs @@ -1,46 +1,96 @@ -using UniGetUI.Core.Data; -using UniGetUI.PackageEngine.Classes; -using UniGetUI.PackageEngine.Operations; -using UniGetUI.Core; using System; using System.Collections.Generic; -using System.ComponentModel.Design; using System.Diagnostics; using System.IO; using System.Linq; -using System.Net; using System.Runtime.InteropServices; -using System.Text.RegularExpressions; using System.Threading.Tasks; -using Windows.Services.TargetedContent; +using UniGetUI.Core; +using UniGetUI.Core.Data; +using UniGetUI.PackageEngine.Classes; +using UniGetUI.PackageEngine.Enums; +using UniGetUI.Core.Logging; +using UniGetUI.Core.Tools; +using UniGetUI.PackageEngine.ManagerClasses.Manager; +using UniGetUI.PackageEngine.PackageClasses; +using UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers; +using Windows.Graphics.Display; -namespace UniGetUI.PackageEngine.Managers +namespace UniGetUI.PackageEngine.Managers.WingetManager { - public class Winget : PackageManagerWithSources + public class WinGet : PackageManager { new public static string[] FALSE_PACKAGE_NAMES = new string[] { "", "e(s)", "have", "the", "Id" }; new public static string[] FALSE_PACKAGE_IDS = new string[] { "", "e(s)", "have", "an", "'winget", "pin'", "have", "an", "Version" }; new public static string[] FALSE_PACKAGE_VERSIONS = new string[] { "", "have", "an", "'winget", "pin'", "have", "an", "Version" }; - private static LocalPcSource LocalPcSource { get; } = new LocalPcSource(); - private static AndroidSubsystemSource AndroidSubsystemSource { get; } = new AndroidSubsystemSource(); - private static SteamSource SteamSource { get; } = new SteamSource(); - private static UbisoftConnectSource UbisoftConnectSource { get; } = new UbisoftConnectSource(); - private static GOGSource GOGSource { get; } = new GOGSource(); - private static MicrosoftStoreSource MicrosoftStoreSource { get; } = new MicrosoftStoreSource(); - - private IWinGetPackageHelper WinGetHelper; - + private LocalWingetSource LocalPcSource { get; set; } + private LocalWingetSource AndroidSubsystemSource { get; set; } + private LocalWingetSource SteamSource { get; set; } + private LocalWingetSource UbisoftConnectSource { get; set; } + private LocalWingetSource GOGSource { get; set; } + private LocalWingetSource MicrosoftStoreSource { get; set; } + + public WinGet(): base() + { + Capabilities = new ManagerCapabilities() + { + CanRunAsAdmin = true, + CanSkipIntegrityChecks = true, + CanRunInteractively = true, + SupportsCustomVersions = true, + SupportsCustomArchitectures = true, + SupportedCustomArchitectures = new Architecture[] { Architecture.X86, Architecture.X64, Architecture.Arm64 }, + SupportsCustomScopes = true, + SupportsCustomLocations = true, + SupportsCustomSources = true, + SupportsCustomPackageIcons = true, + SupportsCustomPackageScreenshots = true, + Sources = new ManagerSource.Capabilities() + { + KnowsPackageCount = false, + KnowsUpdateDate = true, + MustBeInstalledAsAdmin = true, + } + }; + + Properties = new ManagerProperties() + { + Name = "Winget", + Description = CoreTools.Translate("Microsoft's official package manager. Full of well-known and verified packages
Contains: General Software, Microsoft Store apps"), + IconId = "winget", + ColorIconId = "winget_color", + ExecutableFriendlyName = "winget.exe", + InstallVerb = "install", + UninstallVerb = "uninstall", + UpdateVerb = "update", + ExecutableCallArgs = "", + KnownSources = [ new(this, "winget", new Uri("https://cdn.winget.microsoft.com/cache")), + new(this, "msstore", new Uri("https://storeedgefd.dsx.mp.microsoft.com/v9.0")) ], + DefaultSource = new(this, "winget", new Uri("https://cdn.winget.microsoft.com/cache")) + }; + + SourceProvider = new WinGetSourceProvider(this); + PackageDetailsProvider = new WinGetPackageDetailsProvider(this); + + LocalPcSource = new LocalWingetSource(this, CoreTools.Translate("Local PC"), "localpc"); + AndroidSubsystemSource = new(this, CoreTools.Translate("Android Subsystem"), "android"); + SteamSource = new(this, "Steam", "steam"); + UbisoftConnectSource = new(this, "Ubisoft Connect", "uplay"); + GOGSource = new(this, "GOG", "gog"); + MicrosoftStoreSource = new(this, "Microsoft Store", "msstore"); + } + protected override async Task FindPackages_UnSafe(string query) { - return await WinGetHelper.FindPackages_UnSafe(this, query); + return await WinGetHelper.Instance.FindPackages_UnSafe(this, query); } protected override async Task GetAvailableUpdates_UnSafe() { - var Packages = new List(); + List Packages = new(); - Process p = new Process(); + Process p = new(); p.StartInfo = new ProcessStartInfo() { FileName = "powershell.exe", @@ -85,7 +135,7 @@ function Print-WinGetPackage { "); - string line; + string? line; string output = ""; while ((line = await p.StandardOutput.ReadLineAsync()) != null) { @@ -97,13 +147,13 @@ function Print-WinGetPackage { if (elements.Length < 5) continue; - ManagerSource source = SourceFactory.GetSourceOrDefault(elements[4]); + ManagerSource source = GetSourceOrDefault(elements[4]); Packages.Add(new UpgradablePackage(elements[0][1..], elements[1], elements[2], elements[3], source, this)); } output += await p.StandardError.ReadToEndAsync(); - AppTools.LogManagerOperation(this, p, output); + LogOperation(p, output); await p.WaitForExitAsync(); return Packages.ToArray(); @@ -111,9 +161,9 @@ function Print-WinGetPackage { protected override async Task GetInstalledPackages_UnSafe() { - var Packages = new List(); + List Packages = new(); - Process p = new Process(); + Process p = new(); p.StartInfo = new ProcessStartInfo() { FileName = "powershell.exe", @@ -153,7 +203,7 @@ function Print-WinGetPackage { exit "); - string line; + string? line; string output = ""; while ((line = await p.StandardOutput.ReadLineAsync()) != null) { @@ -167,7 +217,7 @@ function Print-WinGetPackage { ManagerSource source; if (elements[3] != "") - source = SourceFactory.GetSourceOrDefault(elements[3]); + source = GetSourceOrDefault(elements[3]); else source = GetLocalSource(elements[1]); @@ -175,7 +225,7 @@ function Print-WinGetPackage { } output += await p.StandardError.ReadToEndAsync(); - AppTools.LogManagerOperation(this, p, output); + LogOperation(p, output); await p.WaitForExitAsync(); return Packages.ToArray(); @@ -217,7 +267,8 @@ private ManagerSource GetLocalSource(string id) } catch (Exception ex) { - AppTools.Log(ex); + Logger.Warn($"Could not parse local source for package {id}"); + Logger.Warn(ex); return LocalPcSource; } } @@ -272,7 +323,9 @@ public override string[] GetUpdateParameters(Package package, InstallationOption public override string[] GetUninstallParameters(Package package, InstallationOptions options) { List parameters = new() { Properties.UninstallVerb }; - parameters.AddRange(new string[] { "--id", package.Id, "--exact", "--source", package.Source.Name }); + parameters.AddRange(new string[] { "--id", package.Id, "--exact" }); + if(!package.Source.IsVirtualManager) + parameters.AddRange(new string[] { "--source", package.Source.Name }); parameters.Add("--accept-source-agreements"); @@ -309,14 +362,14 @@ public override OperationVeredict GetInstallOperationVeredict(Package package, I if (output_string.Contains("No applicable upgrade found") || output_string.Contains("No newer package versions are available from the configured sources")) return OperationVeredict.Succeeded; - if(output_string.Contains("winget settings --enable InstallerHashOverride")) + if (output_string.Contains("winget settings --enable InstallerHashOverride")) { - AppTools.Log("Enabling skip hash ckeck for winget..."); - Process p = new Process() + Logger.Info("Enabling skip hash ckeck for winget..."); + Process p = new() { StartInfo = new ProcessStartInfo() { - FileName = CoreData.GSudoPath, + // FileName = AppTools.GSudoPath, Arguments = Status.ExecutablePath + " " + Properties.ExecutableCallArgs + " settings --enable InstallerHashOverride", UseShellExecute = false, RedirectStandardOutput = true, @@ -351,84 +404,18 @@ public override OperationVeredict GetUninstallOperationVeredict(Package package, return ReturnCode == 0 ? OperationVeredict.Succeeded : OperationVeredict.Failed; } - - public override ManagerSource GetMainSource() - { - return new ManagerSource(this, "winget", new Uri("https://cdn.winget.microsoft.com/cache")); - } - - protected override async Task GetPackageVersions_Unsafe(Package package) - { - return await WinGetHelper.GetPackageVersions_Unsafe(this, package); - } - - public override async Task GetPackageDetails_UnSafe(Package package) - { - return await WinGetHelper.GetPackageDetails_UnSafe(this, package); - } - - protected override async Task GetSources_UnSafe() - { - return await WinGetHelper.GetSources_UnSafe(this); - } - - public override async Task RefreshPackageIndexes() - { - await Task.Delay(0); - // As of WinGet 1.6, WinGet does handle updating package indexes automatically - } - - protected override ManagerCapabilities GetCapabilities() - { - return new ManagerCapabilities() - { - CanRunAsAdmin = true, - CanSkipIntegrityChecks = true, - CanRunInteractively = true, - SupportsCustomVersions = true, - SupportsCustomArchitectures = true, - SupportedCustomArchitectures = new Architecture[] { Architecture.X86, Architecture.X64, Architecture.Arm64 }, - SupportsCustomScopes = true, - SupportsCustomLocations = true, - SupportsCustomSources = true, - Sources = new ManagerSource.Capabilities() - { - KnowsPackageCount = false, - KnowsUpdateDate = true, - MustBeInstalledAsAdmin = true, - } - }; - } - - protected override ManagerProperties GetProperties() - { - ManagerProperties properties = new() - { - Name = "Winget", - Description = Tools.Translate("Microsoft's official package manager. Full of well-known and verified packages
Contains: General Software, Microsoft Store apps"), - IconId = "winget", - ColorIconId = "winget_color", - ExecutableFriendlyName = "winget.exe", - InstallVerb = "install", - UninstallVerb = "uninstall", - UpdateVerb = "update", - ExecutableCallArgs = "", - }; - return properties; - } - protected override async Task LoadManager() { ManagerStatus status = new(); - - status.ExecutablePath = await Tools.Which("winget.exe"); - status.Found = File.Exists(status.ExecutablePath); + var which_res = await CoreTools.Which("winget.exe"); + status.ExecutablePath = which_res.Item2; + status.Found = which_res.Item1; if (!status.Found) return status; - var process = new Process() + Process process = new() { StartInfo = new ProcessStartInfo() { @@ -444,16 +431,15 @@ protected override async Task LoadManager() process.Start(); status.Version = "Naive WinGet CLI Version: " + (await process.StandardOutput.ReadToEndAsync()).Trim(); - try { - await Task.Run(() => WinGetHelper = new NativeWinGetHelper()); + await Task.Run(() => WinGetHelper.Instance = new NativeWinGetHelper()); status.Version += "\nUsing Native WinGet helper (COM Api)"; } catch (Exception ex) { - AppTools.Log("Cannot create native WinGet instance due to error: " + ex.ToString()); - WinGetHelper = new BundledWinGetHelper(); + Logger.Error("Cannot create native WinGet instance due to error: " + ex.ToString()); + WinGetHelper.Instance = new BundledWinGetHelper(); status.Version += "\nUsing bundled WinGet helper (CLI parsing)"; } @@ -463,100 +449,28 @@ protected override async Task LoadManager() return status; } - - public override ManagerSource[] GetKnownSources() - { - return new ManagerSource[] - { - new(this, "winget", new Uri("https://cdn.winget.microsoft.com/cache")), - new(this, "msstore", new Uri("https://storeedgefd.dsx.mp.microsoft.com/v9.0")), - }; - } - - public override string[] GetAddSourceParameters(ManagerSource source) - { - return new string[] { "source", "add", "--name", source.Name, "--arg", source.Url.ToString(), "--accept-source-agreements", "--disable-interactivity" }; - } - - public override string[] GetRemoveSourceParameters(ManagerSource source) - { - return new string[] { "source", "remove", "--name", source.Name, "--disable-interactivity" }; - } - - public override OperationVeredict GetAddSourceOperationVeredict(ManagerSource source, int ReturnCode, string[] Output) - { - return ReturnCode == 0 ? OperationVeredict.Succeeded : OperationVeredict.Failed; - } - - public override OperationVeredict GetRemoveSourceOperationVeredict(ManagerSource source, int ReturnCode, string[] Output) - { - return ReturnCode == 0 ? OperationVeredict.Succeeded : OperationVeredict.Failed; - } } - public class LocalPcSource : ManagerSource + internal class LocalWingetSource: ManagerSource { - public override string IconId { get { return "localpc"; } } - public LocalPcSource() : base(Winget.Tools.App.Winget, Winget.Tools.Translate("Local PC"), new Uri("https://microsoft.com/local-pc-source")) - { IsVirtualManager = true; } - public override string ToString() - { - return Winget.Tools.Translate("Local PC"); - } - } + private string name; + private string __icon_id; + public override string IconId { get { return __icon_id; } } - public class AndroidSubsystemSource : ManagerSource - { - public override string IconId { get { return "android"; } } - public AndroidSubsystemSource() : base(Winget.Tools.App.Winget, Winget.Tools.Translate("Android Subsystem"), new Uri("https://microsoft.com/local-pc-source")) - { IsVirtualManager = true; } - public override string ToString() + public LocalWingetSource(WinGet manager, string name, string iconId) + : base(manager, name, new Uri("https://microsoft.com/local-pc-source"), isVirtualManager: true) { - return Winget.Tools.Translate("Android Subsystem"); + this.name = name; + __icon_id = iconId; } - } - public class SteamSource : ManagerSource - { - public override string IconId { get { return "steam"; } } - public SteamSource() : base(Winget.Tools.App.Winget, "Steam", new Uri("https://microsoft.com/local-pc-source")) - { IsVirtualManager = true; } public override string ToString() { - return "Steam"; + return name; } } +} + - public class UbisoftConnectSource : ManagerSource - { - public override string IconId { get { return "uplay"; } } - public UbisoftConnectSource() : base(Winget.Tools.App.Winget, "Ubisoft Connect", new Uri("https://microsoft.com/local-pc-source")) - { IsVirtualManager = true; } - public override string ToString() - { - return "Ubisoft Connect"; - } - } - public class GOGSource : ManagerSource - { - public override string IconId { get { return "gog"; } } - public GOGSource() : base(Winget.Tools.App.Winget, "GOG", new Uri("https://microsoft.com/gog-source")) - { IsVirtualManager = true; } - public override string ToString() - { - return "GOG"; - } - } - public class MicrosoftStoreSource : ManagerSource - { - public override string IconId { get { return "msstore"; } } - public MicrosoftStoreSource() : base(Winget.Tools.App.Winget, "Microsoft Store", new Uri("https://microsoft.com/microsoft-store-source")) - { IsVirtualManager = true; } - public override string ToString() - { - return "Microsoft Store"; - } - } -} diff --git a/src/UniGetUI/PackageEngine/Managers/WinGetHelpers.cs b/src/UniGetUI.PackageEngine.Managers.WinGet/WinGetHelpers.cs similarity index 78% rename from src/UniGetUI/PackageEngine/Managers/WinGetHelpers.cs rename to src/UniGetUI.PackageEngine.Managers.WinGet/WinGetHelpers.cs index 668150357..ec0b85e1d 100644 --- a/src/UniGetUI/PackageEngine/Managers/WinGetHelpers.cs +++ b/src/UniGetUI.PackageEngine.Managers.WinGet/WinGetHelpers.cs @@ -1,80 +1,103 @@ -using System; +using Microsoft.Management.Deployment; +using System; using System.Collections.Generic; +using System.Diagnostics; +using System.IO; using System.Linq; -using System.Text; +using System.Text.RegularExpressions; using System.Threading.Tasks; using UniGetUI.Core; +using UniGetUI.Core.Data; using UniGetUI.PackageEngine.Classes; using WindowsPackageManager.Interop; +using UniGetUI.Core.Logging; using Deployment = Microsoft.Management.Deployment; -using Microsoft.Management.Deployment; -using System.Diagnostics; -using UniGetUI.Core.Data; -using System.IO; -using System.ComponentModel; -using YamlDotNet.Core.Tokens; -using System.Text.RegularExpressions; +using UniGetUI.Core.Tools; +using UniGetUI.PackageEngine.PackageClasses; +using UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers; -namespace UniGetUI.PackageEngine.Managers +namespace UniGetUI.PackageEngine.Managers.WingetManager { + internal static class WinGetHelper + { + private static IWinGetPackageHelper? __helper; + public static IWinGetPackageHelper Instance + { + get + { + if (__helper == null) + { + __helper = new BundledWinGetHelper(); + } + return __helper; + } + + set + { + __helper = value; + } + } + } + internal interface IWinGetPackageHelper { - public Task FindPackages_UnSafe(Winget ManagerInstance, string query); - public Task GetSources_UnSafe(Winget ManagerInstance); - public Task GetPackageVersions_Unsafe(Winget ManagerInstance, Package package); - public Task GetPackageDetails_UnSafe(Winget ManagerInstance, Package package); + public Task FindPackages_UnSafe(WinGet ManagerInstance, string query); + public Task GetSources_UnSafe(WinGet ManagerInstance); + public Task GetPackageVersions_Unsafe(WinGet ManagerInstance, Package package); + public Task GetPackageDetails_UnSafe(WinGet ManagerInstance, Package package); } - internal class NativeWinGetHelper: IWinGetPackageHelper + internal class NativeWinGetHelper : IWinGetPackageHelper { - WindowsPackageManagerStandardFactory Factory; - Deployment.PackageManager WinGetManager; + public WindowsPackageManagerStandardFactory Factory; + public Deployment.PackageManager WinGetManager; - public NativeWinGetHelper() { - if (Winget.Tools.IsAdministrator()) - AppTools.Log("[WARNING] Running elevated, WinGet class registration is likely to fail"); + public NativeWinGetHelper() + { + if (CoreTools.IsAdministrator()) + Logger.Info("Running elevated, WinGet class registration is likely to fail"); Factory = new WindowsPackageManagerStandardFactory(); WinGetManager = Factory.CreatePackageManager(); } - public async Task FindPackages_UnSafe(Winget ManagerInstance, string query) + public async Task FindPackages_UnSafe(WinGet ManagerInstance, string query) { List Packages = new(); - var PackageFilters = Factory.CreateFindPackagesOptions(); + FindPackagesOptions PackageFilters = Factory.CreateFindPackagesOptions(); // Name filter - var FilterName = Factory.CreatePackageMatchFilter(); + PackageMatchFilter FilterName = Factory.CreatePackageMatchFilter(); FilterName.Field = Deployment.PackageMatchField.Name; FilterName.Value = query; FilterName.Option = Deployment.PackageFieldMatchOption.ContainsCaseInsensitive; PackageFilters.Filters.Add(FilterName); // Id filter - var FilterId = Factory.CreatePackageMatchFilter(); + PackageMatchFilter FilterId = Factory.CreatePackageMatchFilter(); FilterId.Field = Deployment.PackageMatchField.Name; FilterId.Value = query; FilterId.Option = Deployment.PackageFieldMatchOption.ContainsCaseInsensitive; PackageFilters.Filters.Add(FilterId); // Load catalogs - var AvailableCatalogs = WinGetManager.GetPackageCatalogs(); + IReadOnlyList AvailableCatalogs = WinGetManager.GetPackageCatalogs(); Dictionary> FindPackageTasks = new(); // Spawn Tasks to find packages on catalogs - foreach (var CatalogReference in AvailableCatalogs.ToArray()) + foreach (PackageCatalogReference CatalogReference in AvailableCatalogs.ToArray()) { // Connect to catalog CatalogReference.AcceptSourceAgreements = true; - var result = await CatalogReference.ConnectAsync(); + ConnectResult result = await CatalogReference.ConnectAsync(); if (result.Status == Deployment.ConnectResultStatus.Ok) { try { // Create task and spawn it - var task = new Task(() => result.PackageCatalog.FindPackages(PackageFilters)); + Task task = new(() => result.PackageCatalog.FindPackages(PackageFilters)); task.Start(); // Add task to list @@ -85,28 +108,28 @@ public async Task FindPackages_UnSafe(Winget ManagerInstance, string } catch (Exception e) { - AppTools.Log("WinGet: Catalog " + CatalogReference.Info.Name + " failed to spawn FindPackages task."); - AppTools.Log(e); + Logger.Error("WinGet: Catalog " + CatalogReference.Info.Name + " failed to spawn FindPackages task."); + Logger.Error(e); } } else { - AppTools.Log("WinGet: Catalog " + CatalogReference.Info.Name + " failed to connect."); + Logger.Error("WinGet: Catalog " + CatalogReference.Info.Name + " failed to connect."); } } // Wait for tasks completion await Task.WhenAll(FindPackageTasks.Values.ToArray()); - foreach (var CatalogTaskPair in FindPackageTasks) + foreach (KeyValuePair> CatalogTaskPair in FindPackageTasks) { try { // Get the source for the catalog - ManagerSource source = ManagerInstance.SourceFactory.GetSourceOrDefault(CatalogTaskPair.Key.Info.Name); + ManagerSource source = ManagerInstance.GetSourceOrDefault(CatalogTaskPair.Key.Info.Name); - var FoundPackages = CatalogTaskPair.Value.Result; - foreach (var package in FoundPackages.Matches.ToArray()) + FindPackagesResult FoundPackages = CatalogTaskPair.Value.Result; + foreach (MatchResult package in FoundPackages.Matches.ToArray()) { // Create the Package item and add it to the list Packages.Add(new Package( @@ -120,72 +143,72 @@ public async Task FindPackages_UnSafe(Winget ManagerInstance, string } catch (Exception e) { - AppTools.Log("WinGet: Catalog " + CatalogTaskPair.Key.Info.Name + " failed to get available packages."); - AppTools.Log(e); + Logger.Error("WinGet: Catalog " + CatalogTaskPair.Key.Info.Name + " failed to get available packages."); + Logger.Error(e); } } return Packages.ToArray(); } - public async Task GetSources_UnSafe(Winget ManagerInstance) + public async Task GetSources_UnSafe(WinGet ManagerInstance) { List sources = new(); - foreach (var catalog in await Task.Run(() => WinGetManager.GetPackageCatalogs().ToArray())) + foreach (PackageCatalogReference catalog in await Task.Run(() => WinGetManager.GetPackageCatalogs().ToArray())) try { sources.Add(new ManagerSource(ManagerInstance, catalog.Info.Name, new Uri(catalog.Info.Argument), updateDate: catalog.Info.LastUpdateTime.ToString())); } catch (Exception e) { - AppTools.Log(e); + Logger.Error(e); } return sources.ToArray(); } - public async Task GetPackageVersions_Unsafe(Winget ManagerInstance, Package package) + public async Task GetPackageVersions_Unsafe(WinGet ManagerInstance, Package package) { // Find the native package for the given Package object - var Catalog = WinGetManager.GetPackageCatalogByName(package.Source.Name); + PackageCatalogReference Catalog = WinGetManager.GetPackageCatalogByName(package.Source.Name); if (Catalog == null) { - AppTools.Log("Failed to get catalog " + package.Source.Name + ". Is the package local?"); + Logger.Error("Failed to get catalog " + package.Source.Name + ". Is the package local?"); return []; } // Connect to catalog Catalog.AcceptSourceAgreements = true; - var ConnectResult = await Task.Run(() => Catalog.Connect()); + ConnectResult ConnectResult = await Task.Run(() => Catalog.Connect()); if (ConnectResult.Status != Deployment.ConnectResultStatus.Ok) { - AppTools.Log("Failed to connect to catalog " + package.Source.Name); + Logger.Error("Failed to connect to catalog " + package.Source.Name); return []; } // Match only the exact same Id - var packageMatchFilter = Factory.CreateFindPackagesOptions(); - var filters = Factory.CreatePackageMatchFilter(); + FindPackagesOptions packageMatchFilter = Factory.CreateFindPackagesOptions(); + PackageMatchFilter filters = Factory.CreatePackageMatchFilter(); filters.Field = Deployment.PackageMatchField.Id; filters.Value = package.Id; filters.Option = Deployment.PackageFieldMatchOption.Equals; packageMatchFilter.Filters.Add(filters); packageMatchFilter.ResultLimit = 1; - var SearchResult = Task.Run(() => ConnectResult.PackageCatalog.FindPackages(packageMatchFilter)); + Task SearchResult = Task.Run(() => ConnectResult.PackageCatalog.FindPackages(packageMatchFilter)); if (SearchResult.Result == null || SearchResult.Result.Matches == null || SearchResult.Result.Matches.Count() == 0) { - AppTools.Log("WinGet: Failed to find package " + package.Id + " in catalog " + package.Source.Name); + Logger.Error("WinGet: Failed to find package " + package.Id + " in catalog " + package.Source.Name); return []; } // Get the Native Package - var NativePackage = SearchResult.Result.Matches.First().CatalogPackage; + CatalogPackage NativePackage = SearchResult.Result.Matches.First().CatalogPackage; return NativePackage.AvailableVersions.Select(x => x.Version).ToArray(); } - public async Task GetPackageDetails_UnSafe(Winget ManagerInstance, Package package) + public async Task GetPackageDetails_UnSafe(WinGet ManagerInstance, Package package) { PackageDetails details = new(package); @@ -199,43 +222,43 @@ public async Task GetPackageDetails_UnSafe(Winget ManagerInstanc details.ManifestUrl = new Uri("https://apps.microsoft.com/detail/" + package.Id); // Find the native package for the given Package object - var Catalog = WinGetManager.GetPackageCatalogByName(package.Source.Name); + PackageCatalogReference Catalog = WinGetManager.GetPackageCatalogByName(package.Source.Name); if (Catalog == null) { - AppTools.Log("Failed to get catalog " + package.Source.Name + ". Is the package local?"); + Logger.Error("Failed to get catalog " + package.Source.Name + ". Is the package local?"); return details; } // Connect to catalog Catalog.AcceptSourceAgreements = true; - var ConnectResult = await Task.Run(() => Catalog.Connect()); + ConnectResult ConnectResult = await Task.Run(() => Catalog.Connect()); if (ConnectResult.Status != Deployment.ConnectResultStatus.Ok) { - AppTools.Log("Failed to connect to catalog " + package.Source.Name); + Logger.Error("Failed to connect to catalog " + package.Source.Name); return details; } // Match only the exact same Id - var packageMatchFilter = Factory.CreateFindPackagesOptions(); - var filters = Factory.CreatePackageMatchFilter(); + FindPackagesOptions packageMatchFilter = Factory.CreateFindPackagesOptions(); + PackageMatchFilter filters = Factory.CreatePackageMatchFilter(); filters.Field = Deployment.PackageMatchField.Id; filters.Value = package.Id; filters.Option = Deployment.PackageFieldMatchOption.Equals; packageMatchFilter.Filters.Add(filters); packageMatchFilter.ResultLimit = 1; - var SearchResult = Task.Run(() => ConnectResult.PackageCatalog.FindPackages(packageMatchFilter)); + Task SearchResult = Task.Run(() => ConnectResult.PackageCatalog.FindPackages(packageMatchFilter)); if (SearchResult.Result == null || SearchResult.Result.Matches == null || SearchResult.Result.Matches.Count() == 0) { - AppTools.Log("WinGet: Failed to find package " + package.Id + " in catalog " + package.Source.Name); + Logger.Error("WinGet: Failed to find package " + package.Id + " in catalog " + package.Source.Name); return details; } // Get the Native Package - var NativePackage = SearchResult.Result.Matches.First().CatalogPackage; + CatalogPackage NativePackage = SearchResult.Result.Matches.First().CatalogPackage; // Extract data from NativeDetails - var NativeDetails = NativePackage.DefaultInstallVersion.GetCatalogPackageMetadata(Windows.System.UserProfile.GlobalizationPreferences.Languages[0]); + CatalogPackageMetadata NativeDetails = NativePackage.DefaultInstallVersion.GetCatalogPackageMetadata(Windows.System.UserProfile.GlobalizationPreferences.Languages[0]); if (NativeDetails.Author != "") details.Author = NativeDetails.Author; @@ -264,7 +287,6 @@ public async Task GetPackageDetails_UnSafe(Winget ManagerInstanc if (NativeDetails.Tags != null) details.Tags = NativeDetails.Tags.ToArray(); - Debug.WriteLine("Starting manual fetch"); // There is no way yet to retrieve installer URLs right now so this part will be console-parsed. // TODO: Replace this code with native code when available on the COM api @@ -272,7 +294,7 @@ public async Task GetPackageDetails_UnSafe(Winget ManagerInstanc List output = new(); ProcessStartInfo startInfo = new() { - FileName = Path.Join(CoreData.UniGetUIExecutableDirectory, "PackageEngine", "Managers", "winget-cli_x64", "winget.exe"), + FileName = Path.Join(CoreData.UniGetUIExecutableDirectory, "winget-cli_x64", "winget.exe"), Arguments = ManagerInstance.Properties.ExecutableCallArgs + " show --id " + package.Id + " --exact --disable-interactivity --accept-source-agreements --source " + package.Source.Name, RedirectStandardOutput = true, RedirectStandardError = true, @@ -285,7 +307,7 @@ public async Task GetPackageDetails_UnSafe(Winget ManagerInstanc process.Start(); // Retrieve the output - string _line; + string? _line; while ((_line = await process.StandardOutput.ReadLineAsync()) != null) if (_line.Trim() != "") output.Add(_line); @@ -295,7 +317,6 @@ public async Task GetPackageDetails_UnSafe(Winget ManagerInstanc { try { - AppTools.Log(__line); string line = __line.Trim(); if (line.Contains("Installer SHA256:")) details.InstallerHash = line.Split(":")[1].Trim(); @@ -303,7 +324,7 @@ public async Task GetPackageDetails_UnSafe(Winget ManagerInstanc else if (line.Contains("Installer Url:")) { details.InstallerUrl = new Uri(line.Replace("Installer Url:", "").Trim()); - details.InstallerSize = await Winget.Tools.GetFileSizeAsync(details.InstallerUrl); + details.InstallerSize = await CoreTools.GetFileSizeAsync(details.InstallerUrl); } else if (line.Contains("Release Date:")) details.UpdateDate = line.Split(":")[1].Trim(); @@ -313,8 +334,8 @@ public async Task GetPackageDetails_UnSafe(Winget ManagerInstanc } catch (Exception e) { - AppTools.Log("Error occurred while parsing line value=\"" + __line + "\""); - AppTools.Log(e.Message); + Logger.Warn("Error occurred while parsing line value=\"" + __line + "\""); + Logger.Warn(e.Message); } } @@ -327,15 +348,16 @@ internal class BundledWinGetHelper : IWinGetPackageHelper { private string WinGetBundledPath; - public BundledWinGetHelper() { + public BundledWinGetHelper() + { WinGetBundledPath = Path.Join(CoreData.UniGetUIExecutableDirectory, "PackageEngine", "Managers", "winget-cli_x64", "winget.exe"); } - public async Task FindPackages_UnSafe(Winget ManagerInstance, string query) + public async Task FindPackages_UnSafe(WinGet ManagerInstance, string query) { - var Packages = new List(); + List Packages = new(); - Process p = new Process(); + Process p = new(); p.StartInfo = new ProcessStartInfo() { FileName = "powershell.exe", @@ -377,11 +399,10 @@ function Print-WinGetPackage { "); - string line; + string? line; string output = ""; while ((line = await p.StandardOutput.ReadLineAsync()) != null) { - Debug.WriteLine(line); output += line + "\n"; if (!line.StartsWith("#")) continue; // The PowerShell script appends a '#' to the beginning of each line to identify the output @@ -390,20 +411,20 @@ function Print-WinGetPackage { if (elements.Length < 4) continue; - ManagerSource source = ManagerInstance.SourceFactory.GetSourceOrDefault(elements[3]); + ManagerSource source = ManagerInstance.GetSourceOrDefault(elements[3]); Packages.Add(new Package(elements[0][1..], elements[1], elements[2], source, ManagerInstance)); } output += await p.StandardError.ReadToEndAsync(); - AppTools.LogManagerOperation(ManagerInstance, p, output); + // AppTools.LogManagerOperation(ManagerInstance, p, output); await p.WaitForExitAsync(); return Packages.ToArray(); } - public async Task GetPackageDetails_UnSafe(Winget ManagerInstance, Package package) + public async Task GetPackageDetails_UnSafe(WinGet ManagerInstance, Package package) { PackageDetails details = new(package); @@ -442,13 +463,12 @@ public async Task GetPackageDetails_UnSafe(Winget ManagerInstanc process.StartInfo = startInfo; process.Start(); - string _line; + string? _line; while ((_line = await process.StandardOutput.ReadLineAsync()) != null) if (_line.Trim() != "") { output.Add(_line); - AppTools.Log(_line); - if (_line.Contains("The value provided for the `locale` argument is invalid") || _line.Contains("No applicable installer found; see logs for more details.")) + if (_line.Contains("The value provided for the `locale` argument is invalid") || _line.Contains("No applicable installer found; see Logger.Logs for more details.")) { LocaleFound = false; break; @@ -459,7 +479,7 @@ public async Task GetPackageDetails_UnSafe(Winget ManagerInstanc if (!LocaleFound) { output.Clear(); - AppTools.Log("Winget could not found culture data for package Id=" + package.Id + " and Culture=" + System.Globalization.CultureInfo.CurrentCulture.ToString() + ". Trying to get data for en-US"); + Logger.Info("Winget could not found culture data for package Id=" + package.Id + " and Culture=" + System.Globalization.CultureInfo.CurrentCulture.ToString() + ". Trying to get data for en-US"); process = new Process(); LocaleFound = true; startInfo = new() @@ -480,8 +500,7 @@ public async Task GetPackageDetails_UnSafe(Winget ManagerInstanc if (_line.Trim() != "") { output.Add(_line); - AppTools.Log(_line); - if (_line.Contains("The value provided for the `locale` argument is invalid") || _line.Contains("No applicable installer found; see logs for more details.")) + if (_line.Contains("The value provided for the `locale` argument is invalid") || _line.Contains("No applicable installer found; see Logger.Logs for more details.")) { LocaleFound = false; break; @@ -493,7 +512,7 @@ public async Task GetPackageDetails_UnSafe(Winget ManagerInstanc if (!LocaleFound) { output.Clear(); - AppTools.Log("Winget could not found culture data for package Id=" + package.Id + " and Culture=en-US. Loading default"); + Logger.Info("Winget could not found culture data for package Id=" + package.Id + " and Culture=en-US. Loading default"); LocaleFound = true; process = new Process(); startInfo = new() @@ -514,7 +533,6 @@ public async Task GetPackageDetails_UnSafe(Winget ManagerInstanc if (_line.Trim() != "") { output.Add(_line); - AppTools.Log(_line); } } @@ -568,7 +586,7 @@ public async Task GetPackageDetails_UnSafe(Winget ManagerInstanc else if (line.Contains("Installer Url:")) { details.InstallerUrl = new Uri(line.Replace("Installer Url:", "").Trim()); - details.InstallerSize = await Winget.Tools.GetFileSizeAsync(details.InstallerUrl); + details.InstallerSize = await CoreTools.GetFileSizeAsync(details.InstallerUrl); } else if (line.Contains("Release Date:")) details.UpdateDate = line.Split(":")[1].Trim(); @@ -597,16 +615,16 @@ public async Task GetPackageDetails_UnSafe(Winget ManagerInstanc } catch (Exception e) { - AppTools.Log("Error occurred while parsing line value=\"" + _line + "\""); - AppTools.Log(e.Message); + Logger.Warn("Error occurred while parsing line value=\"" + _line + "\""); + Logger.Warn(e.Message); } } return details; } - public async Task GetPackageVersions_Unsafe(Winget ManagerInstance, Package package) - { + public async Task GetPackageVersions_Unsafe(WinGet ManagerInstance, Package package) + { Process p = new() { StartInfo = new ProcessStartInfo() @@ -624,7 +642,7 @@ public async Task GetPackageVersions_Unsafe(Winget ManagerInstance, Pa p.Start(); - string line; + string? line; List versions = new(); bool DashesPassed = false; string output = ""; @@ -640,11 +658,11 @@ public async Task GetPackageVersions_Unsafe(Winget ManagerInstance, Pa versions.Add(line.Trim()); } output += await p.StandardError.ReadToEndAsync(); - AppTools.LogManagerOperation(ManagerInstance, p, output); + // AppTools.LogManagerOperation(ManagerInstance, p, output); return versions.ToArray(); } - public async Task GetSources_UnSafe(Winget ManagerInstance) + public async Task GetSources_UnSafe(WinGet ManagerInstance) { List sources = new(); @@ -666,7 +684,7 @@ public async Task GetSources_UnSafe(Winget ManagerInstance) bool dashesPassed = false; string output = ""; - string line; + string? line; while ((line = await process.StandardOutput.ReadLineAsync()) != null) { output += line + "\n"; @@ -683,18 +701,18 @@ public async Task GetSources_UnSafe(Winget ManagerInstance) else { string[] parts = Regex.Replace(line.Trim(), " {2,}", " ").Split(' '); - if(parts.Length > 1) + if (parts.Length > 1) sources.Add(new ManagerSource(ManagerInstance, parts[0].Trim(), new Uri(parts[1].Trim()))); } } catch (Exception e) { - AppTools.Log(e); + Logger.Warn(e); } } output += await process.StandardError.ReadToEndAsync(); - AppTools.LogManagerOperation(ManagerInstance, process, output); + // AppTools.LogManagerOperation(ManagerInstance, process, output); await process.WaitForExitAsync(); return sources.ToArray(); diff --git a/src/UniGetUI.PackageEngine.Managers.WinGet/WinGetPackageDetailsProvider.cs b/src/UniGetUI.PackageEngine.Managers.WinGet/WinGetPackageDetailsProvider.cs new file mode 100644 index 000000000..cdb801b29 --- /dev/null +++ b/src/UniGetUI.PackageEngine.Managers.WinGet/WinGetPackageDetailsProvider.cs @@ -0,0 +1,245 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UniGetUI.Core.Data; +using UniGetUI.Core.Logging; +using UniGetUI.Core.Tools; +using UniGetUI.PackageEngine.Classes.Manager.BaseProviders; +using UniGetUI.PackageEngine.ManagerClasses.Manager; +using UniGetUIManagers = UniGetUI.PackageEngine.ManagerClasses.Manager; +using UniGetUI.PackageEngine.Managers.WingetManager; +using UniGetUI.PackageEngine.PackageClasses; +using Microsoft.Management.Deployment; +using UniGetUI.Core.IconEngine; +using System.Net.Http.Json; +using System.Text.RegularExpressions; +using System.Runtime.InteropServices; +using System.Net; +using System.Globalization; + +namespace UniGetUI.PackageEngine.Managers.WingetManager +{ + internal class WinGetPackageDetailsProvider : BasePackageDetailsProvider + { + private static Dictionary __msstore_package_manifests = new(); + + struct MicrosoftStoreProductType + { + public string productIds { get; set; } + } + + public WinGetPackageDetailsProvider(WinGet manager) : base(manager) { } + + protected override async Task GetPackageVersions_Unsafe(Package package) + { + return await WinGetHelper.Instance.GetPackageVersions_Unsafe((WinGet)Manager, package); + } + + protected override async Task GetPackageDetails_Unsafe(Package package) + { + return await WinGetHelper.Instance.GetPackageDetails_UnSafe((WinGet)Manager, package); + } + + protected override async Task GetPackageIcon_Unsafe(Package package) + { + + if(package.Source.Name == "msstore") + return await GetMicrosoftStorePackageIcon(package); + + Logger.Warn("Non-MSStore WinGet Native Icons have been forcefully disabled on code"); + return null; + // return GetWinGetPackageIcon(package); + } + + protected override async Task GetPackageScreenshots_Unsafe(Package package) + { + if (package.Source.Name != "msstore") + return []; + + var ResponseContent = await GetMicrosoftStorePackageManifest(package); + if (ResponseContent == null) + return []; + + var IconArray = Regex.Match(ResponseContent, "(?:\"|')Images(?:\"|'): ?\\[([^\\]]+)\\]"); + if (!IconArray.Success) + { + Logger.Warn($"Could not parse Images array from Microsoft Store response"); + return []; + } + + List FoundIcons = new(); + + foreach (Match ImageEntry in Regex.Matches(IconArray.Groups[1].Value, "{([^}]+)}")) + { + + if (!ImageEntry.Success) + continue; + + var ImagePurpose = Regex.Match(ImageEntry.Groups[1].Value, "(?:\"|')ImagePurpose(?:\"|'): ?(?:\"|')([^'\"]+)(?:\"|')"); + if (!ImagePurpose.Success || ImagePurpose.Groups[1].Value != "Screenshot") + continue; + + var ImageUrl = Regex.Match(ImageEntry.Groups[1].Value, "(?:\"|')Uri(?:\"|'): ?(?:\"|')([^'\"]+)(?:\"|')"); + if (!ImageUrl.Success) + continue; + + FoundIcons.Add(new Uri("https:" + ImageUrl.Groups[1].Value)); + } + + return FoundIcons.ToArray(); + } + + + private async Task GetMicrosoftStorePackageManifest(Package package) + { + if(__msstore_package_manifests.ContainsKey(package.Id)) + return __msstore_package_manifests[package.Id]; + + string CountryCode = CultureInfo.CurrentCulture.Name.Split("-")[^1]; + string Locale = CultureInfo.CurrentCulture.Name; + string url = $"https://storeedgefd.dsx.mp.microsoft.com/v8.0/sdk/products?market={CountryCode}&locale={Locale}&deviceFamily=Windows.Desktop"; + +#pragma warning disable SYSLIB0014 + var httpRequest = (HttpWebRequest)WebRequest.Create(url); +#pragma warning restore SYSLIB0014 + + httpRequest.Method = "POST"; + httpRequest.ContentType = "application/json"; + + var data = "{\"productIds\": \"" + package.Id.ToLower() + "\"}"; + + using (var streamWriter = new StreamWriter(httpRequest.GetRequestStream())) + { + streamWriter.Write(data); + } + + var httpResponse = (HttpWebResponse)await httpRequest.GetResponseAsync(); + string result; + using (var streamReader = new StreamReader(httpResponse.GetResponseStream())) + { + result = streamReader.ReadToEnd(); + } + + Logger.Debug("Microsoft Store API call status code: " + httpResponse.StatusCode); + + if(result != "" && httpResponse.StatusCode == HttpStatusCode.OK) __msstore_package_manifests[package.Id] = result; + return result; + } + + private async Task GetMicrosoftStorePackageIcon(Package package) + { + var ResponseContent = await GetMicrosoftStorePackageManifest(package); + if (ResponseContent == null) + return null; + + var IconArray = Regex.Match(ResponseContent, "(?:\"|')Images(?:\"|'): ?\\[([^\\]]+)\\]"); + if (!IconArray.Success) + { + Logger.Warn($"Could not parse Images array from Microsoft Store response"); + return null; + } + + Dictionary FoundIcons = new(); + + foreach (Match ImageEntry in Regex.Matches(IconArray.Groups[1].Value, "{([^}]+)}")) + { + var CurrentImage = ImageEntry.Groups[1].Value; + + if (!ImageEntry.Success) + continue; + + var ImagePurpose = Regex.Match(CurrentImage, "(?:\"|')ImagePurpose(?:\"|'): ?(?:\"|')([^'\"]+)(?:\"|')"); + if (!ImagePurpose.Success || ImagePurpose.Groups[1].Value != "Tile") + continue; + + var ImageUrl = Regex.Match(CurrentImage, "(?:\"|')Uri(?:\"|'): ?(?:\"|')([^'\"]+)(?:\"|')"); + var ImageSize = Regex.Match(CurrentImage, "(?:\"|')Height(?:\"|'): ?([^,]+)"); + + if (!ImageUrl.Success || !ImageSize.Success) + continue; + + FoundIcons[int.Parse(ImageSize.Groups[1].Value)] = ImageUrl.Groups[1].Value; + } + + if (FoundIcons.Count == 0) + { + Logger.Warn($"No Logo image found for package {package.Id} in Microsoft Store response"); + return null; + } + + Logger.Debug("Choosing icon with size " + FoundIcons.Keys.Max() + " for package " + package.Id + " from Microsoft Store"); + + string uri = "https:" + FoundIcons[FoundIcons.Keys.Max()]; + + return new CacheableIcon(new Uri(uri)); + } + + + private async Task GetWinGetPackageIcon(Package package) + { // TODO: Need to work on retrieving WinGet icons + + if (WinGetHelper.Instance is not NativeWinGetHelper) + { + Logger.Warn("WinGet will not attempt to load icon since the helper is using bundled WinGet"); + return null; + } + + var WinGetManager = ((NativeWinGetHelper)WinGetHelper.Instance).WinGetManager; + var Factory = ((NativeWinGetHelper)WinGetHelper.Instance).Factory; + + // Find the native package for the given Package object + PackageCatalogReference Catalog = WinGetManager.GetPackageCatalogByName(package.Source.Name); + if (Catalog == null) + { + Logger.Error("[WINGET COM] Failed to get catalog " + package.Source.Name + ". Is the package local?"); + return null; + } + + // Connect to catalog + Catalog.AcceptSourceAgreements = true; + ConnectResult ConnectResult = await Task.Run(() => Catalog.Connect()); + // ConnectResult ConnectResult = await Catalog.ConnectAsync(); + if (ConnectResult.Status != ConnectResultStatus.Ok) + { + Logger.Error("[WINGET COM] Failed to connect to catalog " + package.Source.Name); + return null; + } + + // Match only the exact same Id + FindPackagesOptions packageMatchFilter = Factory.CreateFindPackagesOptions(); + PackageMatchFilter filters = Factory.CreatePackageMatchFilter(); + filters.Field = PackageMatchField.Id; + filters.Value = package.Id; + filters.Option = PackageFieldMatchOption.Equals; + packageMatchFilter.Filters.Add(filters); + packageMatchFilter.ResultLimit = 1; + Task SearchResult = Task.Run(() => ConnectResult.PackageCatalog.FindPackages(packageMatchFilter)); + + if (SearchResult.Result == null || SearchResult.Result.Matches == null || SearchResult.Result.Matches.Count() == 0) + { + Logger.Error("[WINGET COM] Failed to find package " + package.Id + " in catalog " + package.Source.Name); + return null; + } + + // Get the Native Package + CatalogPackage NativePackage = SearchResult.Result.Matches.First().CatalogPackage; + + // Extract data from NativeDetails + CatalogPackageMetadata NativeDetails = await Task.Run(() => NativePackage.DefaultInstallVersion.GetCatalogPackageMetadata()); + + CacheableIcon? Icon = null; + + foreach (var icon in NativeDetails.Icons.ToArray()) + { + Icon = new CacheableIcon(new Uri(icon.Url), icon.Sha256); + Logger.Debug($"Found WinGet native icon for {package.Id} with URL={icon.Url}"); + } + + return Icon; + } + + } +} diff --git a/src/UniGetUI.PackageEngine.Managers.WinGet/WinGetSourceProvider.cs b/src/UniGetUI.PackageEngine.Managers.WinGet/WinGetSourceProvider.cs new file mode 100644 index 000000000..9bd1600e2 --- /dev/null +++ b/src/UniGetUI.PackageEngine.Managers.WinGet/WinGetSourceProvider.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers; +using UniGetUI.PackageEngine.Classes.Manager.Providers; +using UniGetUI.PackageEngine.Enums; +using UniGetUI.PackageEngine.ManagerClasses.Manager; + +namespace UniGetUI.PackageEngine.Managers.WingetManager +{ + internal class WinGetSourceProvider : BaseSourceProvider + { + public WinGetSourceProvider(WinGet manager) : base(manager) { } + + public override string[] GetAddSourceParameters(ManagerSource source) + { + return new string[] { "source", "add", "--name", source.Name, "--arg", source.Url.ToString(), "--accept-source-agreements", "--disable-interactivity" }; + } + + public override string[] GetRemoveSourceParameters(ManagerSource source) + { + return new string[] { "source", "remove", "--name", source.Name, "--disable-interactivity" }; + } + + public override OperationVeredict GetAddSourceOperationVeredict(ManagerSource source, int ReturnCode, string[] Output) + { + return ReturnCode == 0 ? OperationVeredict.Succeeded : OperationVeredict.Failed; + } + + public override OperationVeredict GetRemoveSourceOperationVeredict(ManagerSource source, int ReturnCode, string[] Output) + { + return ReturnCode == 0 ? OperationVeredict.Succeeded : OperationVeredict.Failed; + } + + protected override async Task GetSources_UnSafe() + { + if (Manager is WinGet manager) + return await WinGetHelper.Instance.GetSources_UnSafe(manager); + else + throw new Exception("WinGetSourceProvider.GetSources_UnSafe: Manager is supposed to be WinGet"); + } + } +} diff --git a/src/UniGetUI/PackageEngine/Managers/winget-cli_x64/AppInstallerBackgroundTasks.dll b/src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/AppInstallerBackgroundTasks.dll similarity index 100% rename from src/UniGetUI/PackageEngine/Managers/winget-cli_x64/AppInstallerBackgroundTasks.dll rename to src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/AppInstallerBackgroundTasks.dll diff --git a/src/UniGetUI/PackageEngine/Managers/winget-cli_x64/Microsoft.Management.Configuration.dll b/src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/Microsoft.Management.Configuration.dll similarity index 100% rename from src/UniGetUI/PackageEngine/Managers/winget-cli_x64/Microsoft.Management.Configuration.dll rename to src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/Microsoft.Management.Configuration.dll diff --git a/src/UniGetUI/PackageEngine/Managers/winget-cli_x64/Microsoft.Web.WebView2.Core.dll b/src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/Microsoft.Web.WebView2.Core.dll similarity index 100% rename from src/UniGetUI/PackageEngine/Managers/winget-cli_x64/Microsoft.Web.WebView2.Core.dll rename to src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/Microsoft.Web.WebView2.Core.dll diff --git a/src/UniGetUI/PackageEngine/Managers/winget-cli_x64/WindowsPackageManager.dll b/src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/WindowsPackageManager.dll similarity index 100% rename from src/UniGetUI/PackageEngine/Managers/winget-cli_x64/WindowsPackageManager.dll rename to src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/WindowsPackageManager.dll diff --git a/src/UniGetUI/PackageEngine/Managers/winget-cli_x64/WindowsPackageManagerServer.exe b/src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/WindowsPackageManagerServer.exe similarity index 100% rename from src/UniGetUI/PackageEngine/Managers/winget-cli_x64/WindowsPackageManagerServer.exe rename to src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/WindowsPackageManagerServer.exe diff --git a/src/UniGetUI/PackageEngine/Managers/winget-cli_x64/concrt140_app.dll b/src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/concrt140_app.dll similarity index 100% rename from src/UniGetUI/PackageEngine/Managers/winget-cli_x64/concrt140_app.dll rename to src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/concrt140_app.dll diff --git a/src/UniGetUI/PackageEngine/Managers/winget-cli_x64/msvcp140_1_app.dll b/src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/msvcp140_1_app.dll similarity index 100% rename from src/UniGetUI/PackageEngine/Managers/winget-cli_x64/msvcp140_1_app.dll rename to src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/msvcp140_1_app.dll diff --git a/src/UniGetUI/PackageEngine/Managers/winget-cli_x64/msvcp140_2_app.dll b/src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/msvcp140_2_app.dll similarity index 100% rename from src/UniGetUI/PackageEngine/Managers/winget-cli_x64/msvcp140_2_app.dll rename to src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/msvcp140_2_app.dll diff --git a/src/UniGetUI/PackageEngine/Managers/winget-cli_x64/msvcp140_app.dll b/src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/msvcp140_app.dll similarity index 100% rename from src/UniGetUI/PackageEngine/Managers/winget-cli_x64/msvcp140_app.dll rename to src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/msvcp140_app.dll diff --git a/src/UniGetUI/PackageEngine/Managers/winget-cli_x64/resources.pri b/src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/resources.pri similarity index 100% rename from src/UniGetUI/PackageEngine/Managers/winget-cli_x64/resources.pri rename to src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/resources.pri diff --git a/src/UniGetUI/PackageEngine/Managers/winget-cli_x64/vcamp140_app.dll b/src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/vcamp140_app.dll similarity index 100% rename from src/UniGetUI/PackageEngine/Managers/winget-cli_x64/vcamp140_app.dll rename to src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/vcamp140_app.dll diff --git a/src/UniGetUI/PackageEngine/Managers/winget-cli_x64/vccorlib140_app.dll b/src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/vccorlib140_app.dll similarity index 100% rename from src/UniGetUI/PackageEngine/Managers/winget-cli_x64/vccorlib140_app.dll rename to src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/vccorlib140_app.dll diff --git a/src/UniGetUI/PackageEngine/Managers/winget-cli_x64/vcomp140_app.dll b/src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/vcomp140_app.dll similarity index 100% rename from src/UniGetUI/PackageEngine/Managers/winget-cli_x64/vcomp140_app.dll rename to src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/vcomp140_app.dll diff --git a/src/UniGetUI/PackageEngine/Managers/winget-cli_x64/vcruntime140_1_app.dll b/src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/vcruntime140_1_app.dll similarity index 100% rename from src/UniGetUI/PackageEngine/Managers/winget-cli_x64/vcruntime140_1_app.dll rename to src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/vcruntime140_1_app.dll diff --git a/src/UniGetUI/PackageEngine/Managers/winget-cli_x64/vcruntime140_app.dll b/src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/vcruntime140_app.dll similarity index 100% rename from src/UniGetUI/PackageEngine/Managers/winget-cli_x64/vcruntime140_app.dll rename to src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/vcruntime140_app.dll diff --git a/src/UniGetUI/PackageEngine/Managers/winget-cli_x64/winget.exe b/src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/winget.exe similarity index 100% rename from src/UniGetUI/PackageEngine/Managers/winget-cli_x64/winget.exe rename to src/UniGetUI.PackageEngine.Managers.WinGet/winget-cli_x64/winget.exe diff --git a/src/UniGetUI.PackageEngine.Package/InstallationOptions.cs b/src/UniGetUI.PackageEngine.Package/InstallationOptions.cs new file mode 100644 index 000000000..f18f6fc53 --- /dev/null +++ b/src/UniGetUI.PackageEngine.Package/InstallationOptions.cs @@ -0,0 +1,241 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using UniGetUI.Core.Data; +using UniGetUI.Core.Language; +using UniGetUI.Core.Logging; +using UniGetUI.PackageEngine.Enums; +using UniGetUI.PackageEngine.Serializable; + +namespace UniGetUI.PackageEngine.PackageClasses +{ + + /// + /// This class represents the options in which a package must be installed, updated or uninstalled. + /// + public class InstallationOptions + { + public bool SkipHashCheck { get; set; } = false; + public bool InteractiveInstallation { get; set; } = false; + public bool RunAsAdministrator { get; set; } = false; + public string Version { get; set; } = ""; + public Architecture? Architecture { get; set; } = null; + public PackageScope? InstallationScope { get; set; } = null; + public List CustomParameters { get; set; } = new List(); + public bool RemoveDataOnUninstall { get; set; } = false; + public bool PreRelease { get; set; } = false; + public string CustomInstallLocation { get; set; } = ""; + + public Package Package { get; } + + private string _saveFileName = "Unknown.Unknown.InstallationOptions"; + + /// + /// Construct a new InstallationOptions object for a given package. The options will be + /// loaded from disk unless the reset parameter is set to true, in which case the options + /// will be the default ones. + /// + /// + /// + public InstallationOptions(Package package, bool reset = false) + { + Package = package; + _saveFileName = Package.Manager.Name.Replace(" ", "").Replace(".", "") + "." + Package.Id; + if (!reset) + { + LoadOptionsFromDisk(); + } + } + + /// + /// Returns a new InstallationOptions object from a given package. The options will be + /// loaded from the disk asynchronously. + /// + /// + /// + public static async Task FromPackageAsync(Package package) + { + InstallationOptions options = new(package, reset: true); + await options.LoadOptionsFromDiskAsync(); + return options; + } + + /// + /// Overload of the constructor that accepts an UpgradablePackage object. + /// + /// + /// + public InstallationOptions(UpgradablePackage package, bool reset = false) : this((Package)package, reset) + { } + + /// + /// Returns a new InstallationOptions object from a given SerializableInstallationOptions_v1 and a package. + /// + /// + /// + /// + public static InstallationOptions FromSerialized(SerializableInstallationOptions_v1 options, Package package) + { + InstallationOptions opt = new(package, reset: true); + opt.FromSerialized(options); + return opt; + } + + /// + /// Loads and applies the options from the given SerializableInstallationOptions_v1 object to the current object. + /// + /// + public void FromSerialized(SerializableInstallationOptions_v1 options) + { + SkipHashCheck = options.SkipHashCheck; + InteractiveInstallation = options.InteractiveInstallation; + RunAsAdministrator = options.RunAsAdministrator; + CustomInstallLocation = options.CustomInstallLocation; + Version = options.Version; + PreRelease = options.PreRelease; + if (options.Architecture != "" && CommonTranslations.InvertedArchNames.ContainsKey(options.Architecture)) + Architecture = CommonTranslations.InvertedArchNames[options.Architecture]; + if (options.InstallationScope != "" && CommonTranslations.InvertedScopeNames_NonLang.ContainsKey(options.InstallationScope)) + InstallationScope = CommonTranslations.InvertedScopeNames_NonLang[options.InstallationScope]; + CustomParameters = options.CustomParameters; + } + + /// + /// Returns a SerializableInstallationOptions_v1 object containing the options of the current instance. + /// + /// + public SerializableInstallationOptions_v1 Serialized() + { + SerializableInstallationOptions_v1 options = new(); + options.SkipHashCheck = SkipHashCheck; + options.InteractiveInstallation = InteractiveInstallation; + options.RunAsAdministrator = RunAsAdministrator; + options.CustomInstallLocation = CustomInstallLocation; + options.PreRelease = PreRelease; + options.Version = Version; + if (Architecture != null) + options.Architecture = CommonTranslations.ArchNames[Architecture.Value]; + if (InstallationScope != null) + options.InstallationScope = CommonTranslations.ScopeNames_NonLang[InstallationScope.Value]; + options.CustomParameters = CustomParameters; + return options; + } + + private FileInfo GetPackageOptionsFile() + { + string optionsFileName = Package.Manager.Name + "." + Package.Id + ".json"; + return new FileInfo(Path.Join(CoreData.UniGetUIInstallationOptionsDirectory, optionsFileName)); + } + + /// + /// Saves the current options to disk. + /// + public void SaveOptionsToDisk() + { + try + { + FileInfo optionsFile = GetPackageOptionsFile(); + if (optionsFile.Directory?.Exists == false) + optionsFile.Directory.Create(); + + using FileStream outputStream = optionsFile.OpenWrite(); + JsonSerializer.Serialize(outputStream, Serialized()); + } + catch (Exception ex) + { + Logger.Log(ex); + } + } + + /// + /// Saves the current options to disk, asynchronously. + /// + public async Task SaveOptionsToDiskAsync() + { + try + { + FileInfo optionsFile = GetPackageOptionsFile(); + if (optionsFile.Directory?.Exists == false) + optionsFile.Directory.Create(); + + await using FileStream outputStream = optionsFile.OpenWrite(); + await JsonSerializer.SerializeAsync(outputStream, Serialized()); + } + catch (Exception ex) + { + Logger.Log(ex); + } + } + + /// + /// Loads the options from disk, asynchronously. + /// + public void LoadOptionsFromDisk() + { + try + { + FileInfo optionsFile = GetPackageOptionsFile(); + if (!optionsFile.Exists) + return; + + using FileStream inputStream = optionsFile.OpenRead(); + SerializableInstallationOptions_v1 options = JsonSerializer.Deserialize(inputStream); + FromSerialized(options); + } + catch (Exception e) + { + Logger.Log(e); + } + } + + /// + /// Loads the options from disk. + /// + public async Task LoadOptionsFromDiskAsync() + { + FileInfo optionsFile = GetPackageOptionsFile(); + try + { + if (!optionsFile.Exists) + return; + + + await using FileStream inputStream = optionsFile.OpenRead(); + SerializableInstallationOptions_v1 options = await JsonSerializer.DeserializeAsync(inputStream); + FromSerialized(options); + } + catch (JsonException) + { + Logger.Log("An error occurred while parsing package " + optionsFile + ". The file will be overwritten"); + await File.WriteAllTextAsync(optionsFile.FullName, "{}"); + } + catch (Exception e) + { + Logger.Log("Loading installation options for file " + optionsFile + " have failed: "); + Logger.Log(e); + } + } + + /// + /// Returns a string representation of the current options. + /// + /// + public override string ToString() + { + string customparams = CustomParameters != null ? string.Join(",", CustomParameters) : "[]"; + return $""; + } + } +} diff --git a/src/UniGetUI.PackageEngine.Package/Package.cs b/src/UniGetUI.PackageEngine.Package/Package.cs new file mode 100644 index 000000000..e7579547b --- /dev/null +++ b/src/UniGetUI.PackageEngine.Package/Package.cs @@ -0,0 +1,413 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Threading.Tasks; +using UniGetUI.Core.Data; +using UniGetUI.Core.Tools; +using UniGetUI.Interface.Enums; +using UniGetUI.Core.Logging; +using UniGetUI.Core.IconEngine; +using UniGetUI.PackageEngine.Enums; +using UniGetUI.PackageEngine.ManagerClasses.Manager; + +namespace UniGetUI.PackageEngine.PackageClasses +{ + public class Package : INotifyPropertyChanged + { + // Internal properties + private bool __is_checked = false; + public event PropertyChangedEventHandler? PropertyChanged; + private string __listed_icon_id; + private string __name_tooltip; + private PackageTag __tag; + private float __opacity; + private bool __show_icon_highlight; + private string __hash; + + public PackageTag Tag + { + get { return __tag; } + + set + { + __tag = value; + switch (__tag) + { + case PackageTag.Default: + ListedIconId = "install"; + ListIconShowHighlight = false; + ListedOpacity = 1; + ListedNameTooltip = Name; + break; + + case PackageTag.AlreadyInstalled: + ListedIconId = "installed"; + ListIconShowHighlight = true; + ListedOpacity = 1; + ListedNameTooltip = CoreTools.Translate("This package is already installed") + " - " + Name; + break; + + case PackageTag.IsUpgradable: + ListedIconId = "update"; + ListIconShowHighlight = true; + ListedOpacity = 1; + ListedNameTooltip = CoreTools.Translate("This package can be updated") + " - " + Name; + break; + + case PackageTag.Pinned: + ListedIconId = "pin_fill"; + ListIconShowHighlight = false; + ListedOpacity = 1; + ListedNameTooltip = CoreTools.Translate("Updates for this package are ignored") + " - " + Name; + break; + + case PackageTag.OnQueue: + ListedIconId = "sandclock"; + ListIconShowHighlight = false; + ListedOpacity = .5F; + ListedNameTooltip = CoreTools.Translate("This package is on the queue") + " - " + Name; + break; + + case PackageTag.BeingProcessed: + ListedIconId = "gears"; + ListIconShowHighlight = false; + ListedOpacity = .5F; + ListedNameTooltip = CoreTools.Translate("This package is being processed") + " - " + Name; + break; + + case PackageTag.Failed: + ListedIconId = "stop"; + ListIconShowHighlight = true; + ListedOpacity = 1; + ListedNameTooltip = CoreTools.Translate("An error occurred while processing this package") + " - " + Name; + break; + } + } + } + + // Public properties + public bool ListIconShowHighlight + { + get { return __show_icon_highlight; } + set { __show_icon_highlight = value; OnPropertyChanged(); } + } + + public bool IsChecked + { + get { return __is_checked; } + set { __is_checked = value; OnPropertyChanged(); } + } + + public string ListedIconId + { + set { __listed_icon_id = value; OnPropertyChanged(); } + get { return __listed_icon_id; } + } + + public string ListedNameTooltip + { + get { return __name_tooltip; } + set { __name_tooltip = value; OnPropertyChanged(); } + } + + public float ListedOpacity + { + get { return __opacity; } + set { __opacity = value; OnPropertyChanged(); } + } + + public string IsCheckedAsString { get { return IsChecked ? "True" : "False"; } } + public string Name { get; } + public string Id { get; set; } + public string Version { get; } + public float VersionAsFloat { get; } + public ManagerSource Source { get; set; } + public PackageManager Manager { get; } + public string UniqueId { get; } + public string NewVersion { get; set; } + public virtual bool IsUpgradable { get; } = false; + public PackageScope Scope { get; set; } + public string SourceAsString + { + get + { + if (Source != null) return Source.ToString(); + else return ""; + } + } + + /// + /// Constuct a package with a given name, id, version, source and manager, and an optional scope. + /// + /// + /// + /// + /// + /// + /// + public Package(string name, string id, string version, ManagerSource source, PackageManager manager, PackageScope scope = PackageScope.Local) + { + Name = name; + Id = id; + Version = version; + Source = source; + Manager = manager; + Scope = scope; + UniqueId = $"{Manager.Properties.Name}\\{Id}\\{Version}"; + NewVersion = ""; + VersionAsFloat = GetFloatVersion(); + Tag = PackageTag.Default; + __hash = Manager.Name + "\\" + Source.Name + "\\" + Id; + } + + public string GetHash() + { + return __hash; + } + + + /// + /// Internal method + /// + /// + /// + public override bool Equals(object? obj) + { + if (!(obj is Package)) + return false; + else + return (obj as Package)?.GetHash() == GetHash(); + } + + /// + /// Load the package's normalized icon id, + /// + /// a string with the package's normalized icon id + public string GetIconId() + { + string iconId = Id.ToLower(); + if (Manager.Name == "Winget") + iconId = string.Join('.', iconId.Split(".")[1..]); + else if (Manager.Name == "Chocolatey") + iconId = iconId.Replace(".install", "").Replace(".portable", ""); + else if (Manager.Name == "Scoop") + iconId = iconId.Replace(".app", ""); + return iconId; + } + + /// + /// Get the package's icon url. If the package has no icon, a fallback image is returned. + /// + /// An always-valid URI object + public Uri GetIconUrl() + { + string iconId = GetIconId(); + if (IconDatabase.Instance.GetIconUrlForId(iconId) != "") + return new Uri(IconDatabase.Instance.GetIconUrlForId(iconId)); + + return new Uri("ms-appx:///Assets/Images/package_color.png"); + } + + /// + /// Returns a float representation of the package's version for comparison purposes. + /// + /// A float value. Returns 0.0F if the version could not be parsed + public float GetFloatVersion() + { + string _ver = ""; + bool _dotAdded = false; + foreach (char _char in Version) + { + if (char.IsDigit(_char)) + _ver += _char; + else if (_char == '.') + { + if (!_dotAdded) + { + _ver += _char; + _dotAdded = true; + } + } + } + float res = 0.0F; + if (_ver != "" && _ver != ".") + try + { + return float.Parse(_ver); + } + catch { } + return res; + } + + /// + /// Adds the package to the ignored updates list. If no version is provided, all updates are ignored. + /// Calling this method will override older ignored updates. + /// + /// + /// + public async Task AddToIgnoredUpdatesAsync(string version = "*") + { + try + { + string IgnoredId = $"{Manager.Properties.Name.ToLower()}\\{Id}"; + JsonObject IgnoredUpdatesJson = JsonNode.Parse(await File.ReadAllTextAsync(CoreData.IgnoredUpdatesDatabaseFile)) as JsonObject; + if (IgnoredUpdatesJson.ContainsKey(IgnoredId)) + IgnoredUpdatesJson.Remove(IgnoredId); + IgnoredUpdatesJson.Add(IgnoredId, version); + await File.WriteAllTextAsync(CoreData.IgnoredUpdatesDatabaseFile, IgnoredUpdatesJson.ToString()); + // FIXME: Tools.App.MainWindow.NavigationPage.UpdatesPage.RemoveCorrespondingPackages(this); + + GetInstalledPackage()?.SetTag(PackageTag.Pinned); + } + catch (Exception ex) + { + Logger.Log(ex); + } + } + + /// + /// Removes the package from the ignored updates list, either if it is ignored for all updates or for a specific version only. + /// + /// + public async Task RemoveFromIgnoredUpdatesAsync() + { + try + { + + string IgnoredId = $"{Manager.Properties.Name.ToLower()}\\{Id}"; + JsonObject IgnoredUpdatesJson = JsonNode.Parse(await File.ReadAllTextAsync(CoreData.IgnoredUpdatesDatabaseFile)) as JsonObject; + if (IgnoredUpdatesJson.ContainsKey(IgnoredId)) + { + IgnoredUpdatesJson.Remove(IgnoredId); + await File.WriteAllTextAsync(CoreData.IgnoredUpdatesDatabaseFile, IgnoredUpdatesJson.ToString()); + } + + GetInstalledPackage()?.SetTag(PackageTag.Default); + } + catch (Exception ex) + { + Logger.Log(ex); + } + } + + /// + /// Returns true if the package's updates are ignored. If the version parameter + /// is passed it will be checked if that version is ignored. Please note that if + /// all updates are ignored, calling this method with a specific version will + /// still return true, although the passed version is not explicitly ignored. + /// + /// + /// + public async Task HasUpdatesIgnoredAsync(string Version = "*") + { + try + { + string IgnoredId = $"{Manager.Properties.Name.ToLower()}\\{Id}"; + JsonObject IgnoredUpdatesJson = JsonNode.Parse(await File.ReadAllTextAsync(CoreData.IgnoredUpdatesDatabaseFile)) as JsonObject; + if (IgnoredUpdatesJson.ContainsKey(IgnoredId) && (IgnoredUpdatesJson[IgnoredId].ToString() == "*" || IgnoredUpdatesJson[IgnoredId].ToString() == Version)) + return true; + else + return false; + } + catch (Exception ex) + { + Logger.Log(ex); + return false; + } + + } + + /// + /// Returns (as a string) the version for which a package has been ignored. When no versions + /// are ignored, an empty string will be returned; and when all versions are ignored an asterisk + /// will be returned. + /// + /// + public async Task GetIgnoredUpdatesVersionAsync() + { + try + { + string IgnoredId = $"{Manager.Properties.Name.ToLower()}\\{Id}"; + JsonObject IgnoredUpdatesJson = JsonNode.Parse(await File.ReadAllTextAsync(CoreData.IgnoredUpdatesDatabaseFile)) as JsonObject; + if (IgnoredUpdatesJson.ContainsKey(IgnoredId)) + return IgnoredUpdatesJson[IgnoredId].ToString(); + else + return ""; + } + catch (Exception ex) + { + Logger.Log(ex); + return ""; + } + } + + /// + /// Internal method to raise the PropertyChanged event. + /// + /// + protected void OnPropertyChanged([CallerMemberName] string name = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); + } + + /// + /// Returns the corresponding installed Package object. Will return null if not applicable + /// + /// a Package object if found, null if not + public Package? GetInstalledPackage() + { + throw new NotImplementedException(); + /* foreach (Package package in Tools.App.MainWindow.NavigationPage.InstalledPage.Packages) + if (package.Equals(this)) + return package; + return null; + */ + } + + /// + /// Returns the corresponding available Package object. Will return null if not applicable + /// + /// a Package object if found, null if not + public Package? GetAvailablePackage() + { + throw new NotImplementedException(); + /* + * foreach (Package package in Tools.App.MainWindow.NavigationPage.DiscoverPage.Packages) + if (package.Equals(this)) + return package; + return null; + */ + } + + /// + /// Returns the corresponding upgradable Package object. Will return null if not applicable + /// + /// a Package object if found, null if not + public Package? GetUpgradablePackage() + { + throw new NotImplementedException(); + /* + foreach (UpgradablePackage package in Tools.App.MainWindow.NavigationPage.UpdatesPage.Packages) + if (package.Equals(this)) + return package; + return null; + */ + } + + /// + /// Sets the package tag. You may as well use the Tag property. + /// This function is used for compatibility with the ? operator + /// + /// + public void SetTag(PackageTag tag) + { + Tag = tag; + } + + } +} diff --git a/src/UniGetUI.PackageEngine.Package/PackageDetails.cs b/src/UniGetUI.PackageEngine.Package/PackageDetails.cs new file mode 100644 index 000000000..76ea02fd9 --- /dev/null +++ b/src/UniGetUI.PackageEngine.Package/PackageDetails.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UniGetUI.PackageEngine.PackageClasses +{ + /// + /// The properties of a given package. + /// + public class PackageDetails + { + public Package Package { get; } + public string Name { get; } + public string Id { get; } + public string Version { get; } + public string NewVersion { get; } + public ManagerSource Source { get; } + public string Description { get; set; } = ""; + public string Publisher { get; set; } = ""; + public string Author { get; set; } = ""; + public Uri HomepageUrl { get; set; } = null; + public string License { get; set; } = ""; + public Uri LicenseUrl { get; set; } = null; + public Uri InstallerUrl { get; set; } = null; + public string InstallerHash { get; set; } = ""; + public string InstallerType { get; set; } = ""; + public double InstallerSize { get; set; } = 0; // In Megabytes + public Uri ManifestUrl { get; set; } = null; + public string UpdateDate { get; set; } = ""; + public string ReleaseNotes { get; set; } = ""; + public Uri ReleaseNotesUrl { get; set; } = null; + public string[] Tags { get; set; } = new string[0]; + + public PackageDetails(Package package) + { + Package = package; + Name = package.Name; + Id = package.Id; + Version = package.Version; + Source = package.Source; + if (package is UpgradablePackage) + NewVersion = ((UpgradablePackage)package).NewVersion; + else + NewVersion = ""; + } + } + +} diff --git a/src/UniGetUI.PackageEngine.Package/UniGetUI.PackageEngine.PackageClasses.csproj b/src/UniGetUI.PackageEngine.Package/UniGetUI.PackageEngine.PackageClasses.csproj new file mode 100644 index 000000000..5444dc4e7 --- /dev/null +++ b/src/UniGetUI.PackageEngine.Package/UniGetUI.PackageEngine.PackageClasses.csproj @@ -0,0 +1,22 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + + + diff --git a/src/UniGetUI.PackageEngine.Package/UpgradablePackage.cs b/src/UniGetUI.PackageEngine.Package/UpgradablePackage.cs new file mode 100644 index 000000000..f64e1634e --- /dev/null +++ b/src/UniGetUI.PackageEngine.Package/UpgradablePackage.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UniGetUI.PackageEngine.PackageClasses +{ + + + public class UpgradablePackage : Package + { + // Public properties + public float NewVersionAsFloat { get; } + public override bool IsUpgradable { get; } = true; + + private string __hash; + + /// + /// Creates an UpgradablePackage object representing a package that can be upgraded; given its name, id, installed version, new version, source and manager, and an optional scope. + /// + /// + /// + /// + /// + /// + /// + /// + public UpgradablePackage(string name, string id, string installed_version, string new_version, ManagerSource source, PackageManager manager, PackageScope scope = PackageScope.Local) : base(name, id, installed_version, source, manager, scope) + { + NewVersion = new_version; + IsChecked = true; + NewVersionAsFloat = GetFloatNewVersion(); + + __hash = Manager.Name + "\\" + Source.Name + "\\" + Id + "\\" + Version + "->" + NewVersion; + } + public new string GetHash() + { + return __hash; + } + + /// + /// Returns a float value representing the new new version of the package, for comparison purposes. + /// + /// + public float GetFloatNewVersion() + { + string _ver = ""; + bool _dotAdded = false; + foreach (char _char in NewVersion) + { + if (char.IsDigit(_char)) + _ver += _char; + else if (_char == '.') + { + if (!_dotAdded) + { + _ver += _char; + _dotAdded = true; + } + } + } + float res = 0.0F; + if (_ver != "" && _ver != ".") + try + { + return float.Parse(_ver); + } + catch (Exception) + { + } + return res; + } + + /// + /// This version will check if the new version of the package is already present + /// on the InstalledPackages list, to prevent already installed updates from being updated again. + /// + /// + public bool NewVersionIsInstalled() + { + foreach (Package package in Tools.App.MainWindow.NavigationPage.InstalledPage.Packages) + if (package.Manager == Manager && package.Id == Id && package.Version == NewVersion && package.Source.Name == Source.Name) + return true; + return false; + } + } + +} diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/BaseProviders/BasePackageDetailsProvider.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/BaseProviders/BasePackageDetailsProvider.cs new file mode 100644 index 000000000..770006023 --- /dev/null +++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/BaseProviders/BasePackageDetailsProvider.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UniGetUI.Core.Data; +using UniGetUI.Core.IconEngine; +using UniGetUI.Core.Logging; +using UniGetUI.PackageEngine.Classes.Manager.Interfaces; +using UniGetUI.PackageEngine.ManagerClasses.Manager; +using UniGetUI.PackageEngine.PackageClasses; + +namespace UniGetUI.PackageEngine.Classes.Manager.BaseProviders +{ + public abstract class BasePackageDetailsProvider : IPackageDetailsProvider where T : PackageManager + { + protected T Manager; + + public BasePackageDetailsProvider(T manager) + { + Manager = manager; + } + + public async Task GetPackageDetails(Package package) + { + return await GetPackageDetails_Unsafe(package); + } + + public async Task GetPackageVersions(Package package) + { + if (Manager.Capabilities.SupportsCustomVersions) + { + var result = await GetPackageVersions_Unsafe(package); + Logger.Debug($"Found {result.Length} versions for package Id={package.Id} on manager {Manager.Name}"); + return result; + } + else + { + Logger.Warn($"Manager {Manager.Name} does not support version retrieving, this method should have not been called"); + return []; + } + } + + public async Task GetPackageIconUrl(Package package) + { + CacheableIcon? Icon = null; + if (Manager.Capabilities.SupportsCustomPackageIcons) + { + Icon = await GetPackageIcon_Unsafe(package); + if(Icon == null) Logger.Debug($"Manager {Manager.Name} did not find a native icon for {package.Id}"); + } + else + Logger.Debug($"Manager {Manager.Name} does not support native icons"); + + if (Icon == null) + { + var url = IconDatabase.Instance.GetIconUrlForId(package.GetIconId()); + if(url != "") Icon = new CacheableIcon(new Uri(url), package.Version); + } + + if (Icon == null) + { + Logger.Warn($"Icon for package {package.Id} was not found, returning default icon"); + return null; + } + else + { + Logger.Info($"Loaded icon with URL={Icon.ToString()} for package Id={package.Id}"); + } + return Icon; + } + + public async Task GetPackageScreenshotsUrl(Package package) + { + Uri[] URIs = []; + + if (Manager.Capabilities.SupportsCustomPackageScreenshots) + URIs = await GetPackageScreenshots_Unsafe(package); + else + Logger.Debug($"Manager {Manager.Name} does not support native screenshots"); + + if(URIs.Length == 0){ + var UrlArray = IconDatabase.Instance.GetScreenshotsUrlForId(package.Id); + List UriList = new(); + foreach (var url in UrlArray) if (url != "") UriList.Add(new Uri(url)); + URIs = UriList.ToArray(); + } + Logger.Info($"Found {URIs.Length} screenshots for package Id={package.Id}"); + return URIs; + } + + protected abstract Task GetPackageDetails_Unsafe(Package package); + protected abstract Task GetPackageVersions_Unsafe(Package package); + protected abstract Task GetPackageIcon_Unsafe(Package package); + protected abstract Task GetPackageScreenshots_Unsafe(Package package); + } +} diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/BaseProviders/BaseSourceProvider.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/BaseProviders/BaseSourceProvider.cs new file mode 100644 index 000000000..e5d7b7f84 --- /dev/null +++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/BaseProviders/BaseSourceProvider.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; +using UniGetUI.Core.Logging; +using UniGetUI.PackageEngine.Classes.Manager.Interfaces; +using UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers; +using UniGetUI.PackageEngine.Enums; +using UniGetUI.PackageEngine.ManagerClasses.Manager; + +namespace UniGetUI.PackageEngine.Classes.Manager.Providers +{ + public abstract class BaseSourceProvider : ISourceProvider where T : PackageManager + { + public readonly ManagerSourceFactory SourceFactory; + protected T Manager; + + public BaseSourceProvider(T manager) + { + Manager = manager; + SourceFactory = new(manager); + } + + public abstract string[] GetAddSourceParameters(ManagerSource source); + public abstract string[] GetRemoveSourceParameters(ManagerSource source); + public abstract OperationVeredict GetAddSourceOperationVeredict(ManagerSource source, int ReturnCode, string[] Output); + public abstract OperationVeredict GetRemoveSourceOperationVeredict(ManagerSource source, int ReturnCode, string[] Output); + + /// + /// Loads the sources for the manager. This method SHOULD NOT handle exceptions + /// + /// + protected abstract Task GetSources_UnSafe(); + public virtual async Task GetSources() + { + ManagerSource[] sources = await GetSources_UnSafe(); + SourceFactory.Reset(); + + foreach (ManagerSource source in sources) + SourceFactory.AddSource(source); + + return sources; + } + } +} diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Classes/ManagerCapabilities.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Classes/ManagerCapabilities.cs new file mode 100644 index 000000000..e4f2e9e39 --- /dev/null +++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Classes/ManagerCapabilities.cs @@ -0,0 +1,39 @@ +using System.Runtime.InteropServices; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers; + +namespace UniGetUI.PackageEngine.ManagerClasses.Manager +{ + public struct ManagerCapabilities + { + public bool IsDummy = false; + public bool CanRunAsAdmin = false; + public bool CanSkipIntegrityChecks = false; + public bool CanRunInteractively = false; + public bool CanRemoveDataOnUninstall = false; + public bool SupportsCustomVersions = false; + public bool SupportsCustomArchitectures = false; + public Architecture[] SupportedCustomArchitectures = new Architecture[0]; + public bool SupportsCustomScopes = false; + public bool SupportsPreRelease = false; + public bool SupportsCustomLocations = false; + public bool SupportsCustomSources = false; + public bool SupportsCustomPackageIcons = false; + public bool SupportsCustomPackageScreenshots = false; + public ManagerSource.Capabilities Sources { get; set; } + public ManagerCapabilities() + { + Sources = new ManagerSource.Capabilities(); + } + + public ManagerCapabilities(bool IsDummy) + { + Sources = new ManagerSource.Capabilities(); + this.IsDummy = IsDummy; + } + } +} diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Classes/ManagerProperties.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Classes/ManagerProperties.cs new file mode 100644 index 000000000..a213f1d6f --- /dev/null +++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Classes/ManagerProperties.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers; + +namespace UniGetUI.PackageEngine.ManagerClasses.Manager +{ + public class ManagerProperties + { + public bool IsDummy = false; + public string Name { get; set; } = "Unset"; + public string Description { get; set; } = "Unset"; + public string IconId { get; set; } = "Unset"; + public string ColorIconId { get; set; } = "Unset"; + public string ExecutableCallArgs { get; set; } = "Unset"; + public string ExecutableFriendlyName { get; set; } = "Unset"; + public string InstallVerb { get; set; } = "Unset"; + public string UpdateVerb { get; set; } = "Unset"; + public string UninstallVerb { get; set; } = "Unset"; + public ManagerSource[] KnownSources { get; set; } = []; + public ManagerSource DefaultSource { get; set; } +#pragma warning disable CS8618 + public ManagerProperties() { } + public ManagerProperties(bool IsDummy) { this.IsDummy = IsDummy; } +#pragma warning restore CS8618 + } +} diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Classes/ManagerSource.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Classes/ManagerSource.cs new file mode 100644 index 000000000..c126fc69c --- /dev/null +++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Classes/ManagerSource.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UniGetUI.PackageEngine.ManagerClasses.Manager; + +namespace UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers +{ + public class ManagerSource + { + public virtual string IconId { get { return Manager.Properties.IconId; } } + public readonly bool IsVirtualManager = false; + public struct Capabilities + { + public bool KnowsUpdateDate { get; set; } = false; + public bool KnowsPackageCount { get; set; } = false; + public bool MustBeInstalledAsAdmin { get; set; } = false; + public Capabilities() + { } + } + + public PackageManager Manager { get; } + public string Name { get; } + public Uri Url { get; private set; } + public int? PackageCount { get; } + public string UpdateDate { get; } + + public ManagerSource(PackageManager manager, string name, Uri url, int? packageCount = 0, string updateDate = "", bool isVirtualManager = false) + { + IsVirtualManager = isVirtualManager; + Manager = manager; + Name = name; + Url = url; + if (manager.Capabilities.Sources.KnowsPackageCount) + PackageCount = packageCount; + + UpdateDate = updateDate; + } + + public override string ToString() + { + if (Manager.Capabilities.SupportsCustomSources) + return Manager.Name + ": " + Name; + else + return Manager.Name; + } + + /// + /// Replaces the current URL with the new one. Must be used only when a placeholder URL is used. + /// + /// + public void ReplaceUrl(Uri newUrl) + { + Url = newUrl; + } + } +} diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Classes/ManagerSourceFactory.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Classes/ManagerSourceFactory.cs new file mode 100644 index 000000000..62bd9bb5c --- /dev/null +++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Classes/ManagerSourceFactory.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UniGetUI.PackageEngine.ManagerClasses.Manager; + +namespace UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers +{ + public class ManagerSourceFactory + { + private PackageManager __manager; + private Dictionary __reference; + private Uri __default_uri = new("https://marticliment.com/unigetui/"); + + public ManagerSourceFactory(PackageManager manager) + { + __reference = new(); + __manager = manager; + } + + public void Reset() + { + __reference.Clear(); + } + + /// + /// Returns the existing source for the given name, or creates a new one if it does not exist. + /// + /// The name of the source + /// A valid ManagerSource + public ManagerSource GetSourceOrDefault(string name) + { + ManagerSource? source; + if (__reference.TryGetValue(name, out source) && source != null) + { + return source; + } + + ManagerSource new_source = new(__manager, name, __default_uri); + __reference.Add(name, new_source); + return new_source; + } + + /// + /// Returns the existing source for the given name, or null if it does not exist. + /// + /// + /// + public ManagerSource? GetSourceIfExists(string name) + { + ManagerSource? source; + if (__reference.TryGetValue(name, out source)) + { + return source; + } + return null; + } + + public void AddSource(ManagerSource source) + { + if (!__reference.TryAdd(source.Name, source)) + { + ManagerSource existing_source = __reference[source.Name]; + if (existing_source.Url == __default_uri) + existing_source.ReplaceUrl(source.Url); + } + } + + public ManagerSource[] GetAvailableSources() + { + return __reference.Values.ToArray(); + } + } +} diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Classes/ManagerStatus.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Classes/ManagerStatus.cs new file mode 100644 index 000000000..718b68805 --- /dev/null +++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Classes/ManagerStatus.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers +{ + public class ManagerStatus + { + public string Version = ""; + public bool Found = false; + public string ExecutablePath = ""; + public ManagerStatus() + { } + + } +} diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Interfaces/IPackageDetailsProvider.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Interfaces/IPackageDetailsProvider.cs new file mode 100644 index 000000000..b3aa60750 --- /dev/null +++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Interfaces/IPackageDetailsProvider.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; +using UniGetUI.Core.IconEngine; +using UniGetUI.Core.Logging; +using UniGetUI.PackageEngine.PackageClasses; + +namespace UniGetUI.PackageEngine.Classes.Manager.Interfaces +{ + internal interface IPackageDetailsProvider + { + /// + /// Returns a PackageDetails object that represents the details for the given Package object. + /// This method is fail-safe and will return a valid but empty PackageDetails object with the package + /// id if an error occurs. + /// + /// + /// A PackageDetails object + public abstract Task GetPackageDetails(Package package); + + + /// + /// Returns the available versions to install for the given package. + /// If the manager does not support listing the versions, an empty array will be returned. + /// This method is fail-safe and will return an empty array if an error occurs. + /// + /// The package from which to load its versions + /// An array of stings containing the found versions, an empty array if none. + public abstract Task GetPackageVersions(Package package); + + /// + /// Returns an Uri pointing to the icon of this package. + /// The uri may be either a ms-appx:/// url or a http(s):// protocol url + /// + /// The package from which to load the icon + /// A full path to a valid icon file + public abstract Task GetPackageIconUrl(Package package); + + + /// + /// Returns the URLs to the screenshots (if any) of this package. + /// + /// The package from which to load the screenshots + /// An array with valid URIs to the screenshots + public abstract Task GetPackageScreenshotsUrl(Package package); + } +} diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Interfaces/ISourceProvider.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Interfaces/ISourceProvider.cs new file mode 100644 index 000000000..fd5b49c1f --- /dev/null +++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Interfaces/ISourceProvider.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers; +using UniGetUI.PackageEngine.Enums; + +namespace UniGetUI.PackageEngine.Classes.Manager.Interfaces +{ + internal interface ISourceProvider + { + /// + /// Returns the command-line parameters required to add the given source to the manager. + /// + /// The source to add + /// An array containing the parameters to pass to the manager executable + public abstract string[] GetAddSourceParameters(ManagerSource source); + + /// + /// Returns the command-line parameters required to remove the given source from the manager. + /// + /// The source to remove + /// An array containing the parameters to pass to the manager executable + public abstract string[] GetRemoveSourceParameters(ManagerSource source); + + /// + /// Checks the result of attempting to add a source + /// + /// The added (or not) source + /// The returncode of the operation + /// the command-line output of the operation + /// An OperationVeredict value + public abstract OperationVeredict GetAddSourceOperationVeredict(ManagerSource source, int ReturnCode, string[] Output); + + /// + /// Checks the result of attempting to remove a source + /// + /// The removed (or not) source + /// The returncode of the operation + /// the command-line output of the operation + /// An OperationVeredict value + public abstract OperationVeredict GetRemoveSourceOperationVeredict(ManagerSource source, int ReturnCode, string[] Output); + + /// + /// Returns the available sources + /// + /// An array of ManagerSource objects + public abstract Task GetSources(); + } +} diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/PackageManager.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/PackageManager.cs new file mode 100644 index 000000000..11143cddf --- /dev/null +++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/PackageManager.cs @@ -0,0 +1,466 @@ +using System.ComponentModel; +using System.Diagnostics; +using System.Text.RegularExpressions; +using UniGetUI.Core.Classes; +using UniGetUI.Core.Data; +using UniGetUI.Core.IconEngine; +using UniGetUI.Core.Logging; +using UniGetUI.Core.SettingsEngine; +using UniGetUI.PackageEngine.Classes.Manager.BaseProviders; +using UniGetUI.PackageEngine.Classes.Manager.Interfaces; +using UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers; +using UniGetUI.PackageEngine.Classes.Manager.Providers; +using UniGetUI.PackageEngine.Classes.Packages; +using UniGetUI.PackageEngine.Enums; +using UniGetUI.PackageEngine.PackageClasses; + +namespace UniGetUI.PackageEngine.ManagerClasses.Manager +{ + public abstract class PackageManager : SingletonBase, ISourceProvider, IPackageDetailsProvider + { + public ManagerProperties Properties { get; set; } = new(IsDummy: true); + public ManagerCapabilities Capabilities { get; set; } = new(IsDummy: true); + public ManagerStatus Status { get; set; } = new() { Found = false }; + public string Name { get; set; } = "Unset"; + public ManagerSource DefaultSource { get; set; } + public static string[] FALSE_PACKAGE_NAMES = new string[] { "" }; + public static string[] FALSE_PACKAGE_IDS = new string[] { "" }; + public static string[] FALSE_PACKAGE_VERSIONS = new string[] { "" }; + public bool ManagerReady { get; set; } = false; + + public BaseSourceProvider? SourceProvider; + public BasePackageDetailsProvider? PackageDetailsProvider; + private bool __base_constructor_called = false; + + public PackageManager() + { + DefaultSource = Properties.DefaultSource; + Name = Properties.Name; + __base_constructor_called = true; + } + + + /// + /// Initializes the Package Manager (asynchronously). Must be run before using any other method of the manager. + /// + /// + public virtual async Task InitializeAsync() + { + // BEGIN integrity check + if (!__base_constructor_called) + throw new Exception($"The Manager {Properties.Name} has not called the base constructor."); + else if (Capabilities.IsDummy) + throw new Exception($"The current instance of PackageManager with name ${Properties.Name} does not have a valid Capabilities object"); + else if (Properties.IsDummy) + throw new Exception($"The current instance of PackageManager with name ${Properties.Name} does not have a valid Properties object"); + else if (Capabilities.SupportsCustomSources && SourceProvider == null) + throw new Exception($"Manager {Name} has been declared as SupportsCustomSources but has no helper associated with it"); + // END integrity check + + DefaultSource = Properties.DefaultSource; + Name = Properties.Name; + try + { + Status = await LoadManager(); + + + if (SourceProvider != null && Status.Found) + { + + Task SourcesTask = GetSources(); + Task winner = await Task.WhenAny( + SourcesTask, + Task.Delay(10000)); + if (winner == SourcesTask) + { + ManagerReady = true; + } + else + { + ManagerReady = true; + Logger.Warn(Name + " sources took too long to load, using known sources as default"); + } + } + ManagerReady = true; + + string LogData = "▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄" + + "\n█▀▀▀▀▀▀▀▀▀▀▀▀▀ MANAGER LOADED ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀" + + "\n█ Name: " + Name + + "\n█ Enabled: " + IsEnabled().ToString() + + (IsEnabled() ? + "\n█ Found: " + Status.Found.ToString() + + (Status.Found ? + "\n█ Fancye exe name: " + Properties.ExecutableFriendlyName + + "\n█ Executable path: " + Status.ExecutablePath + + "\n█ Call arguments: " + Properties.ExecutableCallArgs + + "\n█ Version: \n" + "█ " + Status.Version.Replace("\n", "\n█ ") + : + "\n█ THE MANAGER WAS NOT FOUND. PERHAPS IT IS NOT " + + "\n█ INSTALLED OR IT HAS BEEN MISCONFIGURED " + ) + : + "\n█ THE MANAGER IS DISABLED" + ) + + "\n▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀"; + + Logger.Info(LogData); + } + catch (Exception e) + { + ManagerReady = true; // We need this to unblock the main thread + Logger.Error("Could not initialize Package Manager " + Name); + Logger.Error(e); + } + } + + + /// + /// Returns a ManagerStatus object representing the current status of the package manager. This method runs asynchronously. + /// + /// + protected abstract Task LoadManager(); + + /// + /// Returns true if the manager is enabled, false otherwise + /// + /// + public bool IsEnabled() + { + return !Settings.Get("Disable" + Name); + } + + /// + /// Returns an array of Package objects that the manager lists for the given query. Depending on the manager, the list may + /// also include similar results. This method is fail-safe and will return an empty array if an error occurs. + /// + /// + /// + public async Task FindPackages(string query) + { + try + { + Package[] packages = await FindPackages_UnSafe(query); + for (int i = 0; i < packages.Length; i++) + { + packages[i] = PackageFactory.GetAvailablePackageIfRepeated(packages[i]); + } + Logger.Info($"Found {packages.Length} available packages from {Name} with the query {query}"); + return packages; + } + catch (Exception e) + { + Logger.Error("Error finding packages on manager " + Name + " with query " + query); + Logger.Error(e); + return []; + } + } + + /// + /// Returns an array of UpgradablePackage objects that represent the available updates reported by the manager. + /// This method is fail-safe and will return an empty array if an error occurs. + /// + /// + /// + public async Task GetAvailableUpdates() + { + try + { + Package[] packages = await GetAvailableUpdates_UnSafe(); + for (int i = 0; i < packages.Length; i++) + packages[i] = PackageFactory.GetUpgradablePackageIfRepeated(packages[i]); + Logger.Info($"Found {packages.Length} available updates from {Name}"); + return packages; + } + catch (Exception e) + { + Logger.Error("Error finding updates on manager " + Name); + Logger.Error(e); + return []; + } + } + + /// + /// Returns an array of Package objects that represent the installed reported by the manager. + /// This method is fail-safe and will return an empty array if an error occurs. + /// + /// + public async Task GetInstalledPackages() + { + try + { + Package[] packages = await GetInstalledPackages_UnSafe(); + for (int i = 0; i < packages.Length; i++) + packages[i] = PackageFactory.GetInstalledPackageIfRepeated(packages[i]); + Logger.Info($"Found {packages.Length} installed packages from {Name}"); + return packages; + } + catch (Exception e) + { + Logger.Error("Error finding installed packages on manager " + Name); + Logger.Error(e); + return []; + } + } + + + /// + /// Returns the available packages to install for the given query. + /// Each manager MUST implement this method. + /// + /// The query string to search for + /// An array of Package objects + protected abstract Task FindPackages_UnSafe(string query); + + /// + /// Returns the available updates reported by the manager. + /// Each manager MUST implement this method. + /// + /// An array of UpgradablePackage objects + protected abstract Task GetAvailableUpdates_UnSafe(); + + /// + /// Returns an array of Package objects containing the installed packages reported by the manager. + /// Each manager MUST implement this method. + /// + /// An array of Package objects + protected abstract Task GetInstalledPackages_UnSafe(); + + + /// + /// Returns the command-line parameters to install the given package. + /// Each manager MUST implement this method. + /// + /// The Package going to be installed + /// The options in which it is going to be installed + /// An array of strings containing the parameters without the manager executable file + public abstract string[] GetInstallParameters(Package package, InstallationOptions options); + + + /// + /// Returns the command-line parameters to update the given package. + /// Each manager MUST implement this method. + /// + /// The Package going to be updated + /// The options in which it is going to be updated + /// An array of strings containing the parameters without the manager executable file + public abstract string[] GetUpdateParameters(Package package, InstallationOptions options); + + /// + /// Returns the command-line parameters to uninstall the given package. + /// Each manager MUST implement this method. + /// + /// The Package going to be uninstalled + /// The options in which it is going to be uninstalled + /// An array of strings containing the parameters without the manager executable file + public abstract string[] GetUninstallParameters(Package package, InstallationOptions options); + + /// + /// Decides and returns the verdict of the install operation. + /// Each manager MUST implement this method. + /// + /// The package that was installed + /// The options with which the package was installed. They may be modified if the returned value is OperationVeredict.AutoRetry + /// The exit code of the process + /// the output of the process + /// An OperationVeredict value representing the result of the installation + public abstract OperationVeredict GetInstallOperationVeredict(Package package, InstallationOptions options, int ReturnCode, string[] Output); + + + /// + /// Decides and returns the verdict of the update operation. + /// Each manager MUST implement this method. + /// + /// The package that was updated + /// The options with which the package was updated. They may be modified if the returned value is OperationVeredict.AutoRetry + /// The exit code of the process + /// the output of the process + /// An OperationVeredict value representing the result of the update + public abstract OperationVeredict GetUpdateOperationVeredict(Package package, InstallationOptions options, int ReturnCode, string[] Output); + + /// + /// Decides and returns the verdict of the uninstall operation. + /// Each manager MUST implement this method. + /// + /// The package that was uninstalled + /// The options with which the package was uninstalled. They may be modified if the returned value is OperationVeredict.AutoRetry + /// The exit code of the process + /// the output of the process + /// An OperationVeredict value representing the result of the uninstall + public abstract OperationVeredict GetUninstallOperationVeredict(Package package, InstallationOptions options, int ReturnCode, string[] Output); + + /// + /// Refreshes the Package Manager sources/indexes + /// Each manager MUST implement this method. + /// + /// +#pragma warning disable CS1998 + public virtual async Task RefreshPackageIndexes() + { + Logger.Debug($"Manager {Name} has not implemented RefreshPackageIndexes"); + } +#pragma warning restore CS1998 + + // BEGIN SOURCE-RELATED METHODS + + /// + /// Will check if the Manager supports custom sources, and throw an exception if not + /// + /// + /// + private void AssertSourceCompatibility(string MethodName) + { + if (!Capabilities.SupportsCustomSources) + throw new Exception($"Manager {Name} does not support custom sources but yet {MethodName} method was called.\n {Environment.StackTrace}"); + else if (SourceProvider == null) + throw new Exception($"Manager {Name} does support custom sources but yet the source helper is null"); + } +#pragma warning disable CS8602 + public ManagerSource GetSourceOrDefault(string SourceName) + { + AssertSourceCompatibility("GetSourceFromName"); + return SourceProvider.SourceFactory.GetSourceOrDefault(SourceName); + } + public ManagerSource? GetSourceIfExists(string SourceName) + { + AssertSourceCompatibility("GetSourceIfExists"); + return SourceProvider.SourceFactory.GetSourceIfExists(SourceName); + } + public string[] GetAddSourceParameters(ManagerSource source) + { + AssertSourceCompatibility("GetAddSourceParameters"); + return SourceProvider.GetAddSourceParameters(source); + } + public string[] GetRemoveSourceParameters(ManagerSource source) + { + AssertSourceCompatibility("GetRemoveSourceParameters"); + return SourceProvider.GetRemoveSourceParameters(source); + } + public OperationVeredict GetAddSourceOperationVeredict(ManagerSource source, int ReturnCode, string[] Output) + { + AssertSourceCompatibility("GetAddSourceOperationVeredict"); + return SourceProvider.GetAddSourceOperationVeredict(source, ReturnCode, Output); + } + public OperationVeredict GetRemoveSourceOperationVeredict(ManagerSource source, int ReturnCode, string[] Output) + { + AssertSourceCompatibility("GetRemoveSourceOperationVeredict"); + return SourceProvider.GetRemoveSourceOperationVeredict(source, ReturnCode, Output); + } + public virtual async Task GetSources() + { + try + { + AssertSourceCompatibility("GetSources"); + var result = await SourceProvider.GetSources(); + Logger.Debug($"Loaded {result.Length} sources for manager {Name}"); + return result; + } + catch (Exception e) + { + Logger.Error($"Error finding sources for manager " + Name); + Logger.Error(e); + return []; + } + } +#pragma warning restore CS8602 + // END SOURCE-RELATED METHODS + + + + + + + + + // BEGIN PACKAGEDEAILS-RELATED METHODS + private void AssertPackageDetailsCompatibility(string MethodName) + { + if (PackageDetailsProvider == null) + throw new Exception($"Manager {Name} does not have a valid PackageDetailsProvider helper"); + } +#pragma warning disable CS8602 + public async Task GetPackageDetails(Package package) + { + try + { + AssertPackageDetailsCompatibility("GetPackageDetails"); + var details = await PackageDetailsProvider.GetPackageDetails(package); + Logger.Info($"Loaded details for package {package.Id} on manager {Name}"); + return details; + } + catch (Exception e) + { + Logger.Error("Error finding installed packages on manager " + Name); + Logger.Error(e); + return new PackageDetails(package); + } + } + + public async Task GetPackageVersions(Package package) + { + try + { + AssertPackageDetailsCompatibility("GetPackageVersions"); + if (package.Manager.Capabilities.SupportsCustomVersions) + return await PackageDetailsProvider.GetPackageVersions(package); + else + return []; + } + catch (Exception e) + { + Logger.Error($"Error finding available package versions for package {package.Id} on manager " + Name); + Logger.Error(e); + return []; + } + } + + public async Task GetPackageIconUrl(Package package) + { + try + { + AssertPackageDetailsCompatibility("GetPackageIcon"); + return await PackageDetailsProvider.GetPackageIconUrl(package); + } + catch (Exception e) + { + Logger.Error($"Error when loading the package icon for the package {package.Id} on manager " + Name); + Logger.Error(e); + return null; + } + } + + public async Task GetPackageScreenshotsUrl(Package package) + { + try + { + AssertPackageDetailsCompatibility("GetPackageScreenshots"); + return await PackageDetailsProvider.GetPackageScreenshotsUrl(package); + } + catch (Exception e) + { + Logger.Error($"Error when loading the package icon for the package {package.Id} on manager " + Name); + Logger.Error(e); + return []; + } + } +#pragma warning restore CS8602 + // END PACKAGEDETAILS-RELATED METHODS + + + + public void LogOperation(Process process, string output) + { + output = Regex.Replace(output, "\n.{0,6}\n", "\n"); + CoreData.ManagerLogs += $"\n▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄"; + CoreData.ManagerLogs += $"\n█▀▀▀▀▀▀▀▀▀ [{DateTime.Now}] {this.Name} ▀▀▀▀▀▀▀▀▀▀▀"; + CoreData.ManagerLogs += $"\n█ Executable: {process.StartInfo.FileName}"; + CoreData.ManagerLogs += $"\n█ Arguments: {process.StartInfo.Arguments}"; + CoreData.ManagerLogs += "\n"; + CoreData.ManagerLogs += output; + CoreData.ManagerLogs += "\n"; + CoreData.ManagerLogs += $"[{DateTime.Now}] Exit Code: {process.ExitCode}"; + CoreData.ManagerLogs += "\n"; + CoreData.ManagerLogs += "\n"; + } + + } +} diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/InstallationOptions.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/InstallationOptions.cs new file mode 100644 index 000000000..9b4e6561f --- /dev/null +++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/InstallationOptions.cs @@ -0,0 +1,253 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using UniGetUI.Core.Data; +using UniGetUI.Core.Language; +using UniGetUI.Core.Logging; +using UniGetUI.PackageEngine.Enums; +using UniGetUI.PackageEngine.Serializable; + +namespace UniGetUI.PackageEngine.PackageClasses +{ + + /// + /// This class represents the options in which a package must be installed, updated or uninstalled. + /// + public class InstallationOptions + { + public bool SkipHashCheck { get; set; } = false; + public bool InteractiveInstallation { get; set; } = false; + public bool RunAsAdministrator { get; set; } = false; + public string Version { get; set; } = ""; + public Architecture? Architecture { get; set; } = null; + public PackageScope? InstallationScope { get; set; } = null; + public List CustomParameters { get; set; } = new List(); + public bool RemoveDataOnUninstall { get; set; } = false; + public bool PreRelease { get; set; } = false; + public string CustomInstallLocation { get; set; } = ""; + + public Package Package { get; } + + private string _saveFileName = "Unknown.Unknown.InstallationOptions"; + + /// + /// Construct a new InstallationOptions object for a given package. The options will be + /// loaded from disk unless the reset parameter is set to true, in which case the options + /// will be the default ones. + /// + /// + /// + public InstallationOptions(Package package, bool reset = false) + { + Package = package; + _saveFileName = Package.Manager.Name.Replace(" ", "").Replace(".", "") + "." + Package.Id; + if (!reset) + { + LoadOptionsFromDisk(); + } + } + + /// + /// Returns a new InstallationOptions object from a given package. The options will be + /// loaded from the disk asynchronously. + /// + /// + /// + public static async Task FromPackageAsync(Package package) + { + InstallationOptions options = new(package, reset: true); + await options.LoadOptionsFromDiskAsync(); + return options; + } + + /// + /// Overload of the constructor that accepts an UpgradablePackage object. + /// + /// + /// + public InstallationOptions(UpgradablePackage package, bool reset = false) : this((Package)package, reset) + { } + + /// + /// Returns a new InstallationOptions object from a given SerializableInstallationOptions_v1 and a package. + /// + /// + /// + /// + public static InstallationOptions FromSerialized(SerializableInstallationOptions_v1 options, Package package) + { + InstallationOptions opt = new(package, reset: true); + opt.FromSerialized(options); + return opt; + } + + /// + /// Loads and applies the options from the given SerializableInstallationOptions_v1 object to the current object. + /// + /// + public void FromSerialized(SerializableInstallationOptions_v1 options) + { + SkipHashCheck = options.SkipHashCheck; + InteractiveInstallation = options.InteractiveInstallation; + RunAsAdministrator = options.RunAsAdministrator; + CustomInstallLocation = options.CustomInstallLocation; + Version = options.Version; + PreRelease = options.PreRelease; + if (options.Architecture != "" && CommonTranslations.InvertedArchNames.ContainsKey(options.Architecture)) + Architecture = CommonTranslations.InvertedArchNames[options.Architecture]; + if (options.InstallationScope != "" && CommonTranslations.InvertedScopeNames_NonLang.ContainsKey(options.InstallationScope)) + InstallationScope = CommonTranslations.InvertedScopeNames_NonLang[options.InstallationScope]; + CustomParameters = options.CustomParameters; + } + + /// + /// Returns a SerializableInstallationOptions_v1 object containing the options of the current instance. + /// + /// + public SerializableInstallationOptions_v1 Serialized() + { + SerializableInstallationOptions_v1 options = new(); + options.SkipHashCheck = SkipHashCheck; + options.InteractiveInstallation = InteractiveInstallation; + options.RunAsAdministrator = RunAsAdministrator; + options.CustomInstallLocation = CustomInstallLocation; + options.PreRelease = PreRelease; + options.Version = Version; + if (Architecture != null) + options.Architecture = CommonTranslations.ArchNames[Architecture.Value]; + if (InstallationScope != null) + options.InstallationScope = CommonTranslations.ScopeNames_NonLang[InstallationScope.Value]; + options.CustomParameters = CustomParameters; + return options; + } + + private FileInfo GetPackageOptionsFile() + { + string optionsFileName = Package.Manager.Name + "." + Package.Id + ".json"; + return new FileInfo(Path.Join(CoreData.UniGetUIInstallationOptionsDirectory, optionsFileName)); + } + + /// + /// Saves the current options to disk. + /// + public void SaveOptionsToDisk() + { + try + { + FileInfo optionsFile = GetPackageOptionsFile(); + if (optionsFile.Directory?.Exists == false) + optionsFile.Directory.Create(); + + using FileStream outputStream = optionsFile.OpenWrite(); + JsonSerializer.Serialize(outputStream, Serialized()); + } + catch (Exception ex) + { + Logger.Error($"Could not save {Package.Id} options to disk"); + Logger.Error(ex); + } + } + + /// + /// Saves the current options to disk, asynchronously. + /// + public async Task SaveOptionsToDiskAsync() + { + try + { + FileInfo optionsFile = GetPackageOptionsFile(); + if (optionsFile.Directory?.Exists == false) + optionsFile.Directory.Create(); + + await using FileStream outputStream = optionsFile.OpenWrite(); + await JsonSerializer.SerializeAsync(outputStream, Serialized()); + } + catch (Exception ex) + { + Logger.Error($"[ASYNC] Could not save {Package.Id} options to disk"); + Logger.Error(ex); + } + } + + /// + /// Loads the options from disk, asynchronously. + /// + public void LoadOptionsFromDisk() + { + try + { + FileInfo optionsFile = GetPackageOptionsFile(); + if (!optionsFile.Exists) + return; + + using FileStream inputStream = optionsFile.OpenRead(); + SerializableInstallationOptions_v1? options = JsonSerializer.Deserialize(inputStream); + + if (options == null) + throw new Exception("Deserialized options cannot be null."); + + FromSerialized(options); + } + catch (Exception e) + { + Logger.Error($"Could not load {Package.Id} options from disk"); + Logger.Error(e); + } + } + + /// + /// Loads the options from disk. + /// + public async Task LoadOptionsFromDiskAsync() + { + FileInfo optionsFile = GetPackageOptionsFile(); + try + { + if (!optionsFile.Exists) + return; + + + await using FileStream inputStream = optionsFile.OpenRead(); + SerializableInstallationOptions_v1? options = await JsonSerializer.DeserializeAsync(inputStream); + + if (options == null) + throw new Exception("Deserialized options cannot be null!"); + + FromSerialized(options); + Logger.Debug($"InstallationOptions loaded successfully from disk for package {Package.Id}"); + } + catch (JsonException) + { + Logger.Warn("An error occurred while parsing package " + optionsFile + ". The file will be overwritten"); + await File.WriteAllTextAsync(optionsFile.FullName, "{}"); + } + catch (Exception e) + { + Logger.Error("Loading installation options for file " + optionsFile + " have failed: "); + Logger.Error(e); + } + } + + /// + /// Returns a string representation of the current options. + /// + /// + public override string ToString() + { + string customparams = CustomParameters != null ? string.Join(",", CustomParameters) : "[]"; + return $""; + } + } +} diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Package.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Package.cs new file mode 100644 index 000000000..341b4a055 --- /dev/null +++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Package.cs @@ -0,0 +1,449 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Threading.Tasks; +using UniGetUI.Core.Data; +using UniGetUI.Core.Tools; +using UniGetUI.Interface.Enums; +using UniGetUI.Core.Logging; +using UniGetUI.Core.IconEngine; +using UniGetUI.PackageEngine.Enums; +using UniGetUI.PackageEngine.ManagerClasses.Manager; +using UniGetUI.PackageEngine.Classes.Packages; +using UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers; +using Windows.UI.Composition; + +namespace UniGetUI.PackageEngine.PackageClasses +{ + public class Package : INotifyPropertyChanged + { + // Internal properties + private bool __is_checked = false; + public event PropertyChangedEventHandler? PropertyChanged; + private string __listed_icon_id = ""; + private string __name_tooltip = ""; + private PackageTag __tag; + private float __opacity = 1; + private bool __show_icon_highlight = false; + private string __hash = ""; + + public int NewVersionLabelWidth { get { return IsUpgradable? 125: 0; } } + public int NewVersionIconWidth { get { return IsUpgradable? 24: 0; } } + + public PackageTag Tag + { + get { return __tag; } + + set + { + __tag = value; + switch (__tag) + { + case PackageTag.Default: + ListedIconId = "install"; + ListIconShowHighlight = false; + ListedOpacity = 1; + ListedNameTooltip = Name; + break; + + case PackageTag.AlreadyInstalled: + ListedIconId = "installed"; + ListIconShowHighlight = true; + ListedOpacity = 1; + ListedNameTooltip = CoreTools.Translate("This package is already installed") + " - " + Name; + break; + + case PackageTag.IsUpgradable: + ListedIconId = "update"; + ListIconShowHighlight = true; + ListedOpacity = 1; + ListedNameTooltip = CoreTools.Translate("This package can be updated") + " - " + Name; + break; + + case PackageTag.Pinned: + ListedIconId = "pin_fill"; + ListIconShowHighlight = false; + ListedOpacity = 1; + ListedNameTooltip = CoreTools.Translate("Updates for this package are ignored") + " - " + Name; + break; + + case PackageTag.OnQueue: + ListedIconId = "sandclock"; + ListIconShowHighlight = false; + ListedOpacity = .5F; + ListedNameTooltip = CoreTools.Translate("This package is on the queue") + " - " + Name; + break; + + case PackageTag.BeingProcessed: + ListedIconId = "gears"; + ListIconShowHighlight = false; + ListedOpacity = .5F; + ListedNameTooltip = CoreTools.Translate("This package is being processed") + " - " + Name; + break; + + case PackageTag.Failed: + ListedIconId = "stop"; + ListIconShowHighlight = true; + ListedOpacity = 1; + ListedNameTooltip = CoreTools.Translate("An error occurred while processing this package") + " - " + Name; + break; + } + } + } + + // Public properties + public bool ListIconShowHighlight + { + get { return __show_icon_highlight; } + set { __show_icon_highlight = value; OnPropertyChanged(); } + } + + public bool IsChecked + { + get { return __is_checked; } + set { __is_checked = value; OnPropertyChanged(); } + } + + public string ListedIconId + { + set { __listed_icon_id = value; OnPropertyChanged(); } + get { return __listed_icon_id; } + } + + public string ListedNameTooltip + { + get { return __name_tooltip; } + set { __name_tooltip = value; OnPropertyChanged(); } + } + + public float ListedOpacity + { + get { return __opacity; } + set { __opacity = value; OnPropertyChanged(); } + } + + public string IsCheckedAsString { get { return IsChecked ? "True" : "False"; } } + public string Name { get; } + public string Id { get; set; } + public string Version { get; } + public float VersionAsFloat { get; } + public ManagerSource Source { get; set; } + public PackageManager Manager { get; } + public string UniqueId { get; } + public string NewVersion { get; set; } + public virtual bool IsUpgradable { get; } = false; + public PackageScope Scope { get; set; } + public string SourceAsString + { + get + { + if (Source != null) return Source.ToString(); + else return ""; + } + } + + /// + /// Constuct a package with a given name, id, version, source and manager, and an optional scope. + /// + /// + /// + /// + /// + /// + /// + public Package(string name, string id, string version, ManagerSource source, PackageManager manager, PackageScope scope = PackageScope.Local) + { + Name = name; + Id = id; + Version = version; + Source = source; + Manager = manager; + Scope = scope; + UniqueId = $"{Manager.Properties.Name}\\{Id}\\{Version}"; + NewVersion = ""; + VersionAsFloat = GetFloatVersion(); + Tag = PackageTag.Default; + __hash = Manager.Name + "\\" + Source.Name + "\\" + Id; + } + + public string GetHash() + { + return __hash; + } + + + /// + /// Internal method + /// + /// + /// + public override bool Equals(object? obj) + { + if (!(obj is Package)) + return false; + else + return (obj as Package)?.GetHash() == GetHash(); + } + + /// + /// Load the package's normalized icon id, + /// + /// a string with the package's normalized icon id + public string GetIconId() + { + string iconId = Id.ToLower(); + if (Manager.Name == "Winget") + iconId = string.Join('.', iconId.Split(".")[1..]); + else if (Manager.Name == "Chocolatey") + iconId = iconId.Replace(".install", "").Replace(".portable", ""); + else if (Manager.Name == "Scoop") + iconId = iconId.Replace(".app", ""); + return iconId; + } + + /// + /// Get the package's icon url. If the package has no icon, a fallback image is returned. + /// + /// An always-valid URI object + public async Task GetIconUrl() + { + try + { + string iconId = GetIconId(); + + CacheableIcon? icon = await Manager.GetPackageIconUrl(this); + string path = await IconCacheEngine.DownloadIconOrCache(icon, Manager.Name, Id); + + Uri Icon; + if (path == "") + Icon = new Uri("ms-appx:///Assets/Images/package_color.png"); + else + Icon = new Uri("file:///" + path); + + Logger.Debug($"Icon for package {Id} was loaded from {Icon}"); + return Icon; + } + catch (Exception ex) + { + Logger.Error($"An error occurred while retrieving the icon for package {Id}"); + Logger.Error(ex); + return new Uri("ms-appx:///Assets/Images/package_color.png"); + } + } + + public async Task GetPackageScreenshots() + { + return await Manager.GetPackageScreenshotsUrl(this); + } + + /// + /// Returns a float representation of the package's version for comparison purposes. + /// + /// A float value. Returns 0.0F if the version could not be parsed + public float GetFloatVersion() + { + string _ver = ""; + bool _dotAdded = false; + foreach (char _char in Version) + { + if (char.IsDigit(_char)) + _ver += _char; + else if (_char == '.') + { + if (!_dotAdded) + { + _ver += _char; + _dotAdded = true; + } + } + } + float res = 0.0F; + if (_ver != "" && _ver != ".") + try + { + return float.Parse(_ver); + } + catch { } + return res; + } + + /// + /// Adds the package to the ignored updates list. If no version is provided, all updates are ignored. + /// Calling this method will override older ignored updates. + /// + /// + /// + public async Task AddToIgnoredUpdatesAsync(string version = "*") + { + try + { + string IgnoredId = $"{Manager.Properties.Name.ToLower()}\\{Id}"; + JsonObject? IgnoredUpdatesJson = JsonNode.Parse(await File.ReadAllTextAsync(CoreData.IgnoredUpdatesDatabaseFile)) as JsonObject; + + if (IgnoredUpdatesJson == null) + throw new Exception("The IgnoredUpdates database seems to be invalid!"); + + if (IgnoredUpdatesJson.ContainsKey(IgnoredId)) + IgnoredUpdatesJson.Remove(IgnoredId); + IgnoredUpdatesJson.Add(IgnoredId, version); + await File.WriteAllTextAsync(CoreData.IgnoredUpdatesDatabaseFile, IgnoredUpdatesJson.ToString()); + GetInstalledPackage()?.SetTag(PackageTag.Pinned); + } + catch (Exception ex) + { + Logger.Error($"Could not add package {Id} to ignored updates"); + Logger.Error(ex); + } + } + + /// + /// Removes the package from the ignored updates list, either if it is ignored for all updates or for a specific version only. + /// + /// + public async Task RemoveFromIgnoredUpdatesAsync() + { + try + { + + string IgnoredId = $"{Manager.Properties.Name.ToLower()}\\{Id}"; + JsonObject? IgnoredUpdatesJson = JsonNode.Parse(await File.ReadAllTextAsync(CoreData.IgnoredUpdatesDatabaseFile)) as JsonObject; + + if (IgnoredUpdatesJson == null) + throw new Exception("The IgnoredUpdates database seems to be invalid!"); + + if (IgnoredUpdatesJson.ContainsKey(IgnoredId)) + { + IgnoredUpdatesJson.Remove(IgnoredId); + await File.WriteAllTextAsync(CoreData.IgnoredUpdatesDatabaseFile, IgnoredUpdatesJson.ToString()); + } + + GetInstalledPackage()?.SetTag(PackageTag.Default); + } + catch (Exception ex) + { + Logger.Error($"Could not remove package {Id} from ignored updates"); + Logger.Error(ex); + } + } + + /// + /// Returns true if the package's updates are ignored. If the version parameter + /// is passed it will be checked if that version is ignored. Please note that if + /// all updates are ignored, calling this method with a specific version will + /// still return true, although the passed version is not explicitly ignored. + /// + /// + /// + public async Task HasUpdatesIgnoredAsync(string Version = "*") + { + try + { + string IgnoredId = $"{Manager.Properties.Name.ToLower()}\\{Id}"; + JsonObject? IgnoredUpdatesJson = JsonNode.Parse(await File.ReadAllTextAsync(CoreData.IgnoredUpdatesDatabaseFile)) as JsonObject; + + if (IgnoredUpdatesJson == null) + throw new Exception("The IgnoredUpdates database seems to be invalid!"); + + if (IgnoredUpdatesJson.ContainsKey(IgnoredId) && (IgnoredUpdatesJson[IgnoredId]?.ToString() == "*" || IgnoredUpdatesJson[IgnoredId]?.ToString() == Version)) + return true; + else + return false; + } + catch (Exception ex) + { + Logger.Error($"Could not check whether package {Id} has updates ignored"); + Logger.Error(ex); + return false; + } + + } + + /// + /// Returns (as a string) the version for which a package has been ignored. When no versions + /// are ignored, an empty string will be returned; and when all versions are ignored an asterisk + /// will be returned. + /// + /// + public async Task GetIgnoredUpdatesVersionAsync() + { + try + { + string IgnoredId = $"{Manager.Properties.Name.ToLower()}\\{Id}"; + JsonObject? IgnoredUpdatesJson = JsonNode.Parse(await File.ReadAllTextAsync(CoreData.IgnoredUpdatesDatabaseFile)) as JsonObject; + + if (IgnoredUpdatesJson == null) + throw new Exception("The IgnoredUpdates database seems to be invalid!"); + + if (IgnoredUpdatesJson.ContainsKey(IgnoredId)) + return IgnoredUpdatesJson[IgnoredId]?.ToString() ?? ""; + else + return ""; + } + catch (Exception ex) + { + Logger.Error($"Could not retrieve the ignored updates version for package {Id}"); + Logger.Error(ex); + return ""; + } + } + + /// + /// Internal method to raise the PropertyChanged event. + /// + /// + protected void OnPropertyChanged([CallerMemberName] string? name = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); + } + + /// + /// Returns the corresponding installed Package object. Will return null if not applicable + /// + /// a Package object if found, null if not + public Package? GetInstalledPackage() + { + return PackageFactory.FindPackageOnInstalledOrNull(this); + + } + + /// + /// Returns the corresponding available Package object. Will return null if not applicable + /// + /// a Package object if found, null if not + public Package? GetAvailablePackage() + { + return PackageFactory.FindPackageOnAvailableOrNull(this); + } + + /// + /// Returns the corresponding upgradable Package object. Will return null if not applicable + /// + /// a Package object if found, null if not + public Package? GetUpgradablePackage() + { + return PackageFactory.FindPackageOnUpdatesOrNull(this); + } + + /// + /// Sets the package tag. You may as well use the Tag property. + /// This function is used for compatibility with the ? operator + /// + /// + public void SetTag(PackageTag tag) + { + Tag = tag; + } + + public bool NewerVersionIsInstalled() + { + if(!IsUpgradable) return false; + return PackageFactory.NewerVersionIsInstalled(this); + } + + } +} diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/PackageDetails.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/PackageDetails.cs new file mode 100644 index 000000000..d7b6ab337 --- /dev/null +++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/PackageDetails.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers; + +namespace UniGetUI.PackageEngine.PackageClasses +{ + /// + /// The properties of a given package. + /// + public class PackageDetails + { + public Package Package { get; } + public string Name { get; } + public string Id { get; } + public string Version { get; } + public string NewVersion { get; } + public ManagerSource Source { get; } + public string Description { get; set; } = ""; + public string Publisher { get; set; } = ""; + public string Author { get; set; } = ""; + public Uri? HomepageUrl { get; set; } = null; + public string License { get; set; } = ""; + public Uri? LicenseUrl { get; set; } = null; + public Uri? InstallerUrl { get; set; } = null; + public string InstallerHash { get; set; } = ""; + public string InstallerType { get; set; } = ""; + public double InstallerSize { get; set; } = 0; // In Megabytes + public Uri? ManifestUrl { get; set; } = null; + public string UpdateDate { get; set; } = ""; + public string ReleaseNotes { get; set; } = ""; + public Uri? ReleaseNotesUrl { get; set; } = null; + public string[] Tags { get; set; } = new string[0]; + + public PackageDetails(Package package) + { + Package = package; + Name = package.Name; + Id = package.Id; + Version = package.Version; + Source = package.Source; + if (package is UpgradablePackage) + NewVersion = ((UpgradablePackage)package).NewVersion; + else + NewVersion = ""; + } + } + +} diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/PackageFactory.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/PackageFactory.cs new file mode 100644 index 000000000..c676cba57 --- /dev/null +++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/PackageFactory.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UniGetUI.PackageEngine.ManagerClasses.Manager; +using UniGetUI.PackageEngine.PackageClasses; + +namespace UniGetUI.PackageEngine.Classes.Packages +{ + internal static class PackageFactory + { + private static Dictionary> __available_packages = new(); + private static Dictionary> __upgradable_packages = new(); + private static Dictionary> __installed_packages = new(); + + public static Package GetAvailablePackageIfRepeated(Package p) + { + Package? old_package; + + if (!__available_packages.ContainsKey(p.Manager)) + __available_packages.Add(p.Manager, new()); + + if (__available_packages[p.Manager].TryGetValue(p.GetHash(), out old_package) && old_package != null) + return old_package; + + __available_packages[p.Manager].Add(p.GetHash(), p); + return p; + } + + public static Package GetUpgradablePackageIfRepeated(Package p) + { + Package? old_package; + + if (!__upgradable_packages.ContainsKey(p.Manager)) + __upgradable_packages.Add(p.Manager, new()); + + if (__upgradable_packages[p.Manager].TryGetValue(p.GetHash(), out old_package) && old_package != null) + return old_package; + + __upgradable_packages[p.Manager].Add(p.GetHash(), p); + return p; + } + + public static Package GetInstalledPackageIfRepeated(Package p) + { + Package? old_package; + + if (!__installed_packages.ContainsKey(p.Manager)) + __installed_packages.Add(p.Manager, new()); + + if (__installed_packages[p.Manager].TryGetValue(p.GetHash(), out old_package) && old_package != null) + return old_package; + + __installed_packages[p.Manager].Add(p.GetHash(), p); + return p; + } + + public static Package? FindPackageOnAvailableOrNull(Package p) + { + if (!__available_packages.ContainsKey(p.Manager)) + return null; + + foreach (var package in __available_packages[p.Manager].Values) + if (p.Equals(package)) + return package; + + return null; + } + + public static Package? FindPackageOnUpdatesOrNull(Package p) + { + if (!__upgradable_packages.ContainsKey(p.Manager)) + return null; + + foreach (var package in __upgradable_packages[p.Manager].Values) + if (p.Equals(package)) + return package; + + return null; + } + + public static Package? FindPackageOnInstalledOrNull(Package p) + { + if(!__installed_packages.ContainsKey(p.Manager)) + return null; + + foreach (var package in __installed_packages[p.Manager].Values) + if (p.Equals(package)) + return package; + + return null; + } + + public static bool NewerVersionIsInstalled(Package p) + { + if (!__installed_packages.ContainsKey(p.Manager)) + return false; + + foreach (Package package in __installed_packages[p.Manager].Values) + if (package.Manager == p.Manager && package.Id == p.Id && package.Version == p.NewVersion && package.Source.Name == p.Source.Name) + return true; + + return false; + } + } +} diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/UpgradablePackage.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/UpgradablePackage.cs new file mode 100644 index 000000000..3c7f04a53 --- /dev/null +++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/UpgradablePackage.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers; +using UniGetUI.PackageEngine.Classes.Packages; +using UniGetUI.PackageEngine.Enums; +using UniGetUI.PackageEngine.ManagerClasses.Manager; + +namespace UniGetUI.PackageEngine.PackageClasses +{ + + + public class UpgradablePackage : Package + { + // Public properties + public float NewVersionAsFloat { get; } + public override bool IsUpgradable { get; } = true; + + private string __hash; + + /// + /// Creates an UpgradablePackage object representing a package that can be upgraded; given its name, id, installed version, new version, source and manager, and an optional scope. + /// + /// + /// + /// + /// + /// + /// + /// + public UpgradablePackage(string name, string id, string installed_version, string new_version, ManagerSource source, PackageManager manager, PackageScope scope = PackageScope.Local) : base(name, id, installed_version, source, manager, scope) + { + NewVersion = new_version; + IsChecked = true; + NewVersionAsFloat = GetFloatNewVersion(); + + __hash = Manager.Name + "\\" + Source.Name + "\\" + Id + "\\" + Version + "->" + NewVersion; + } + public new string GetHash() + { + return __hash; + } + + /// + /// Returns a float value representing the new new version of the package, for comparison purposes. + /// + /// + public float GetFloatNewVersion() + { + string _ver = ""; + bool _dotAdded = false; + foreach (char _char in NewVersion) + { + if (char.IsDigit(_char)) + _ver += _char; + else if (_char == '.') + { + if (!_dotAdded) + { + _ver += _char; + _dotAdded = true; + } + } + } + float res = 0.0F; + if (_ver != "" && _ver != ".") + try + { + return float.Parse(_ver); + } + catch (Exception) + { + } + return res; + } + + /// + /// This version will check if the new version of the package is already present + /// on the InstalledPackages list, to prevent already installed updates from being updated again. + /// + /// + + } + +} diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/UniGetUI.PackageEngine.Classes.csproj b/src/UniGetUI.PackageEngine.PackageManagerClasses/UniGetUI.PackageEngine.Classes.csproj new file mode 100644 index 000000000..cca975e34 --- /dev/null +++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/UniGetUI.PackageEngine.Classes.csproj @@ -0,0 +1,20 @@ + + + + + + enable + + + + + + + + + + + + + + diff --git a/src/UniGetUI.PackageEngine.Serializable/SerializableInstallationOptions.cs b/src/UniGetUI.PackageEngine.Serializable/SerializableInstallationOptions.cs new file mode 100644 index 000000000..a23230591 --- /dev/null +++ b/src/UniGetUI.PackageEngine.Serializable/SerializableInstallationOptions.cs @@ -0,0 +1,15 @@ +namespace UniGetUI.PackageEngine.Serializable +{ + public class SerializableInstallationOptions_v1 + { + public bool SkipHashCheck { get; set; } = false; + public bool InteractiveInstallation { get; set; } = false; + public bool RunAsAdministrator { get; set; } = false; + public string Architecture { get; set; } = ""; + public string InstallationScope { get; set; } = ""; + public List CustomParameters { get; set; } = new(); + public bool PreRelease { get; set; } = false; + public string CustomInstallLocation { get; set; } = ""; + public string Version { get; set; } = ""; + } +} diff --git a/src/UniGetUI.PackageEngine.Serializable/UniGetUI.PackageEngine.Serializable.csproj b/src/UniGetUI.PackageEngine.Serializable/UniGetUI.PackageEngine.Serializable.csproj new file mode 100644 index 000000000..d82d51cab --- /dev/null +++ b/src/UniGetUI.PackageEngine.Serializable/UniGetUI.PackageEngine.Serializable.csproj @@ -0,0 +1,8 @@ + + + + + + enable + + diff --git a/src/UniGetUI.sln b/src/UniGetUI.sln index 71bd85cb8..e59ff9893 100644 --- a/src/UniGetUI.sln +++ b/src/UniGetUI.sln @@ -5,67 +5,247 @@ VisualStudioVersion = 17.9.34407.89 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI", "UniGetUI\UniGetUI.csproj", "{80305A17-2534-48DC-8F75-41F70FCCEAAF}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{91C79476-A7DF-4CC0-B610-FB095234847B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExternalLibraries.WindowsPackageManager.Interop", "WindowsPackageManager.Interop\ExternalLibraries.WindowsPackageManager.Interop.csproj", "{52AC982E-7382-4746-BB66-4003698FCC02}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UniGetUI.Core", "UniGetUI.Core", "{E05D1183-D360-4AFE-8968-314A34FAD3B2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI.Core.Data", "UniGetUI.Core.Data\UniGetUI.Core.Data.csproj", "{5F5EF76B-D755-4C12-ADAE-11F08CE3D936}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI.Core.LanguageEngine", "UniGetUI.Core.LanguageEngine\UniGetUI.Core.LanguageEngine.csproj", "{B70A6F17-08C8-4194-BBE8-668CA920CFF3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI.Core.Logging", "UniGetUI.Core.Logger\UniGetUI.Core.Logging.csproj", "{72180B0C-3D20-4AAD-B015-A9337B91406E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI.Core.Settings", "UniGetUI.Core.Settings\UniGetUI.Core.Settings.csproj", "{1977360F-2E42-45E6-9369-AB1EE59CC5C5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI.Core.Tools", "UniGetUI.Core.Tools\UniGetUI.Core.Tools.csproj", "{25C6CE64-2D61-4832-B6D2-45AFC52E2447}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI.Core.Classes", "UniGetUI.Core.Classes\UniGetUI.Core.Classes.csproj", "{8156B6D8-BD7E-4201-BD8B-8C9B00177F88}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI.Core.IconEngine", "UniGetUI.Core.IconStore\UniGetUI.Core.IconEngine.csproj", "{990F5AFF-ABF6-4019-865D-604D2B23DE2C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI.PackageEngine.Enums", "UniGetUI.PackageEngine.Enums\UniGetUI.PackageEngine.Enums.csproj", "{380E9F5A-23DE-4F5A-9644-EFA51AD1D8E8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI.Interface.Enums", "UniGetUI.Interface.Enums\UniGetUI.Interface.Enums.csproj", "{5A48C2FD-16E4-4B44-BC2C-D793C50E66F2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UniGetUI.PackageEngine", "UniGetUI.PackageEngine", "{7940E867-EEBA-4AFD-9904-1536F003239C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UniGetUI.Interface", "UniGetUI.Interface", "{8CF74C87-534F-4017-A4ED-F2918025E31A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ExternalLibraries", "ExternalLibraries", "{7648DA91-6239-4877-86C6-2E5A79755F1E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExternalLibraries.Clipboard", "ExternalLibraries.Clipboard\ExternalLibraries.Clipboard.csproj", "{9AD1DEC9-1561-4753-AB4B-E81FBDBA5C9E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExternalLibraries.FilePickers", "ExternalLibraries.FilePickers\ExternalLibraries.FilePickers.csproj", "{E40BFCBB-7A02-4E2C-AFDB-A717359EF4FC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{5B9575EA-B4F9-46E4-A75E-59D430779EC7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI.Core.Data.Tests", "UniGetUI.Core.Data.Tests\UniGetUI.Core.Data.Tests.csproj", "{562B4814-2A78-4692-90BE-A727AABCEC85}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI.Core.Classes.Tests", "UniGetUI.Core.Classes.Tests\UniGetUI.Core.Classes.Tests.csproj", "{1A51EA31-6D78-4E98-B767-41A02C6E34D8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI.Core.IconEngine.Tests", "UniGetUI.Core.IconEngine.Tests\UniGetUI.Core.IconEngine.Tests.csproj", "{230BF08C-C039-473B-933F-3BF647440E0E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI.Core.LanguageEngine.Tests", "UniGetUI.Core.Language.Tests\UniGetUI.Core.LanguageEngine.Tests.csproj", "{C55F4BA7-BBDD-42A4-88C1-FD3C411EB234}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI.Core.Logging.Tests", "UniGetUI.Core.Logging.Tests\UniGetUI.Core.Logging.Tests.csproj", "{2979E556-5859-4E88-A1D4-EAB72F82294E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI.PackageEngine.Classes", "UniGetUI.PackageEngine.PackageManagerClasses\UniGetUI.PackageEngine.Classes.csproj", "{7E098666-DE8C-4ABF-B709-4CE7B1A491B0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI.PackageEngine.Serializable", "UniGetUI.PackageEngine.Serializable\UniGetUI.PackageEngine.Serializable.csproj", "{B5E11AAA-B800-455E-9C64-051FFEFD6C0B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Managers", "Managers", "{95168D0B-1B2C-4295-B6D4-D5BAF781B9FA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI.PackageEngine.Managers.WinGet", "UniGetUI.PackageEngine.Managers.WinGet\UniGetUI.PackageEngine.Managers.WinGet.csproj", "{D47CC16E-466B-4D58-A8FC-ECAE5C9606FC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI.PackageEngine.Managers.Scoop", "UniGetUI.PackageEngine.Managers.Scoop\UniGetUI.PackageEngine.Managers.Scoop.csproj", "{1143176D-B7F0-477C-90BB-72289068D927}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI.PackageEngine.Managers.PowerShell", "UniGetUI.PackageEngine.Managers.PowerShell\UniGetUI.PackageEngine.Managers.PowerShell.csproj", "{E454D3A5-C5C6-4291-BE96-220CF0D5CFFD}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI.PackageEngine.Managers.Chocolatey", "UniGetUI.PackageEngine.Managers.Chocolatey\UniGetUI.PackageEngine.Managers.Chocolatey.csproj", "{57D094C1-6913-46BF-A657-84A5F46D4EE7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI.PackageEngine.Managers.Dotnet", "UniGetUI.PackageEngine.Managers.Dotnet\UniGetUI.PackageEngine.Managers.Dotnet.csproj", "{740E2894-903D-4B94-9C32-B630593BEB16}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI.PackageEngine.Managers.Pip", "UniGetUI.PackageEngine.Managers.Pip\UniGetUI.PackageEngine.Managers.Pip.csproj", "{D401F706-A182-46E3-A25C-B0BF5AA0D07E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI.PackageEngine.Managers.Npm", "UniGetUI.PackageEngine.Managers.Npm\UniGetUI.PackageEngine.Managers.Npm.csproj", "{0FFA3F96-A68A-453F-A5FE-0C281EC371C7}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Generic", "Generic", "{9BF1CD59-1A2C-4023-9C8D-171DCB728078}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI.PackageEngine.Managers.Generic.NuGet", "UniGetUI.PackageEngine.Managers.Generic.NuGet\UniGetUI.PackageEngine.Managers.Generic.NuGet.csproj", "{5FA79592-DE5B-46FF-9E05-34A2E72A7AF7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI.Core.Settings.Tests", "UniGetUI.Core.Settings.Tests\UniGetUI.Core.Settings.Tests.csproj", "{09FD3D3A-1EFC-4AEE-B3D7-096D238E0D1A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniGetUI.Core.Tools.Tests", "UniGetUI.Core.Tools.Tests\UniGetUI.Core.Tools.Tests.csproj", "{BDB7A8F3-87A6-4B77-9E0F-6BC785CBCF2B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F5F6CFEE-0E1E-42F6-A0F9-6D663F130A10}" ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig + Solution.props = Solution.props EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WindowsPackageManager Interop", "WindowsPackageManager.Interop\WindowsPackageManager Interop.csproj", "{52AC982E-7382-4746-BB66-4003698FCC02}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|ARM64 = Debug|ARM64 Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|ARM64 = Release|ARM64 Release|x64 = Release|x64 - Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {80305A17-2534-48DC-8F75-41F70FCCEAAF}.Debug|Any CPU.ActiveCfg = Debug|x64 - {80305A17-2534-48DC-8F75-41F70FCCEAAF}.Debug|Any CPU.Build.0 = Debug|x64 - {80305A17-2534-48DC-8F75-41F70FCCEAAF}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {80305A17-2534-48DC-8F75-41F70FCCEAAF}.Debug|ARM64.Build.0 = Debug|ARM64 - {80305A17-2534-48DC-8F75-41F70FCCEAAF}.Debug|ARM64.Deploy.0 = Debug|ARM64 {80305A17-2534-48DC-8F75-41F70FCCEAAF}.Debug|x64.ActiveCfg = Debug|x64 {80305A17-2534-48DC-8F75-41F70FCCEAAF}.Debug|x64.Build.0 = Debug|x64 - {80305A17-2534-48DC-8F75-41F70FCCEAAF}.Debug|x64.Deploy.0 = Debug|x64 - {80305A17-2534-48DC-8F75-41F70FCCEAAF}.Debug|x86.ActiveCfg = Debug|x86 - {80305A17-2534-48DC-8F75-41F70FCCEAAF}.Debug|x86.Build.0 = Debug|x86 - {80305A17-2534-48DC-8F75-41F70FCCEAAF}.Debug|x86.Deploy.0 = Debug|x86 - {80305A17-2534-48DC-8F75-41F70FCCEAAF}.Release|Any CPU.ActiveCfg = Release|x64 - {80305A17-2534-48DC-8F75-41F70FCCEAAF}.Release|Any CPU.Build.0 = Release|x64 - {80305A17-2534-48DC-8F75-41F70FCCEAAF}.Release|ARM64.ActiveCfg = Release|ARM64 - {80305A17-2534-48DC-8F75-41F70FCCEAAF}.Release|ARM64.Build.0 = Release|ARM64 - {80305A17-2534-48DC-8F75-41F70FCCEAAF}.Release|ARM64.Deploy.0 = Release|ARM64 {80305A17-2534-48DC-8F75-41F70FCCEAAF}.Release|x64.ActiveCfg = Release|x64 {80305A17-2534-48DC-8F75-41F70FCCEAAF}.Release|x64.Build.0 = Release|x64 - {80305A17-2534-48DC-8F75-41F70FCCEAAF}.Release|x64.Deploy.0 = Release|x64 - {80305A17-2534-48DC-8F75-41F70FCCEAAF}.Release|x86.ActiveCfg = Release|x86 - {80305A17-2534-48DC-8F75-41F70FCCEAAF}.Release|x86.Build.0 = Release|x86 - {80305A17-2534-48DC-8F75-41F70FCCEAAF}.Release|x86.Deploy.0 = Release|x86 - {52AC982E-7382-4746-BB66-4003698FCC02}.Debug|Any CPU.ActiveCfg = Debug|x64 - {52AC982E-7382-4746-BB66-4003698FCC02}.Debug|Any CPU.Build.0 = Debug|x64 - {52AC982E-7382-4746-BB66-4003698FCC02}.Debug|ARM64.ActiveCfg = Debug|arm64 - {52AC982E-7382-4746-BB66-4003698FCC02}.Debug|ARM64.Build.0 = Debug|arm64 {52AC982E-7382-4746-BB66-4003698FCC02}.Debug|x64.ActiveCfg = Debug|x64 {52AC982E-7382-4746-BB66-4003698FCC02}.Debug|x64.Build.0 = Debug|x64 - {52AC982E-7382-4746-BB66-4003698FCC02}.Debug|x86.ActiveCfg = Debug|x86 - {52AC982E-7382-4746-BB66-4003698FCC02}.Debug|x86.Build.0 = Debug|x86 - {52AC982E-7382-4746-BB66-4003698FCC02}.Release|Any CPU.ActiveCfg = Release|x64 - {52AC982E-7382-4746-BB66-4003698FCC02}.Release|Any CPU.Build.0 = Release|x64 - {52AC982E-7382-4746-BB66-4003698FCC02}.Release|ARM64.ActiveCfg = Release|arm64 - {52AC982E-7382-4746-BB66-4003698FCC02}.Release|ARM64.Build.0 = Release|arm64 {52AC982E-7382-4746-BB66-4003698FCC02}.Release|x64.ActiveCfg = Release|x64 {52AC982E-7382-4746-BB66-4003698FCC02}.Release|x64.Build.0 = Release|x64 - {52AC982E-7382-4746-BB66-4003698FCC02}.Release|x86.ActiveCfg = Release|x86 - {52AC982E-7382-4746-BB66-4003698FCC02}.Release|x86.Build.0 = Release|x86 + {5F5EF76B-D755-4C12-ADAE-11F08CE3D936}.Debug|x64.ActiveCfg = Debug|x64 + {5F5EF76B-D755-4C12-ADAE-11F08CE3D936}.Debug|x64.Build.0 = Debug|x64 + {5F5EF76B-D755-4C12-ADAE-11F08CE3D936}.Release|x64.ActiveCfg = Release|x64 + {5F5EF76B-D755-4C12-ADAE-11F08CE3D936}.Release|x64.Build.0 = Release|x64 + {B70A6F17-08C8-4194-BBE8-668CA920CFF3}.Debug|x64.ActiveCfg = Debug|x64 + {B70A6F17-08C8-4194-BBE8-668CA920CFF3}.Debug|x64.Build.0 = Debug|x64 + {B70A6F17-08C8-4194-BBE8-668CA920CFF3}.Release|x64.ActiveCfg = Release|x64 + {B70A6F17-08C8-4194-BBE8-668CA920CFF3}.Release|x64.Build.0 = Release|x64 + {72180B0C-3D20-4AAD-B015-A9337B91406E}.Debug|x64.ActiveCfg = Debug|x64 + {72180B0C-3D20-4AAD-B015-A9337B91406E}.Debug|x64.Build.0 = Debug|x64 + {72180B0C-3D20-4AAD-B015-A9337B91406E}.Release|x64.ActiveCfg = Release|x64 + {72180B0C-3D20-4AAD-B015-A9337B91406E}.Release|x64.Build.0 = Release|x64 + {1977360F-2E42-45E6-9369-AB1EE59CC5C5}.Debug|x64.ActiveCfg = Debug|x64 + {1977360F-2E42-45E6-9369-AB1EE59CC5C5}.Debug|x64.Build.0 = Debug|x64 + {1977360F-2E42-45E6-9369-AB1EE59CC5C5}.Release|x64.ActiveCfg = Release|x64 + {1977360F-2E42-45E6-9369-AB1EE59CC5C5}.Release|x64.Build.0 = Release|x64 + {25C6CE64-2D61-4832-B6D2-45AFC52E2447}.Debug|x64.ActiveCfg = Debug|x64 + {25C6CE64-2D61-4832-B6D2-45AFC52E2447}.Debug|x64.Build.0 = Debug|x64 + {25C6CE64-2D61-4832-B6D2-45AFC52E2447}.Release|x64.ActiveCfg = Release|x64 + {25C6CE64-2D61-4832-B6D2-45AFC52E2447}.Release|x64.Build.0 = Release|x64 + {8156B6D8-BD7E-4201-BD8B-8C9B00177F88}.Debug|x64.ActiveCfg = Debug|x64 + {8156B6D8-BD7E-4201-BD8B-8C9B00177F88}.Debug|x64.Build.0 = Debug|x64 + {8156B6D8-BD7E-4201-BD8B-8C9B00177F88}.Release|x64.ActiveCfg = Release|x64 + {8156B6D8-BD7E-4201-BD8B-8C9B00177F88}.Release|x64.Build.0 = Release|x64 + {990F5AFF-ABF6-4019-865D-604D2B23DE2C}.Debug|x64.ActiveCfg = Debug|x64 + {990F5AFF-ABF6-4019-865D-604D2B23DE2C}.Debug|x64.Build.0 = Debug|x64 + {990F5AFF-ABF6-4019-865D-604D2B23DE2C}.Release|x64.ActiveCfg = Release|x64 + {990F5AFF-ABF6-4019-865D-604D2B23DE2C}.Release|x64.Build.0 = Release|x64 + {380E9F5A-23DE-4F5A-9644-EFA51AD1D8E8}.Debug|x64.ActiveCfg = Debug|x64 + {380E9F5A-23DE-4F5A-9644-EFA51AD1D8E8}.Debug|x64.Build.0 = Debug|x64 + {380E9F5A-23DE-4F5A-9644-EFA51AD1D8E8}.Release|x64.ActiveCfg = Release|x64 + {380E9F5A-23DE-4F5A-9644-EFA51AD1D8E8}.Release|x64.Build.0 = Release|x64 + {5A48C2FD-16E4-4B44-BC2C-D793C50E66F2}.Debug|x64.ActiveCfg = Debug|x64 + {5A48C2FD-16E4-4B44-BC2C-D793C50E66F2}.Debug|x64.Build.0 = Debug|x64 + {5A48C2FD-16E4-4B44-BC2C-D793C50E66F2}.Release|x64.ActiveCfg = Release|x64 + {5A48C2FD-16E4-4B44-BC2C-D793C50E66F2}.Release|x64.Build.0 = Release|x64 + {9AD1DEC9-1561-4753-AB4B-E81FBDBA5C9E}.Debug|x64.ActiveCfg = Debug|x64 + {9AD1DEC9-1561-4753-AB4B-E81FBDBA5C9E}.Debug|x64.Build.0 = Debug|x64 + {9AD1DEC9-1561-4753-AB4B-E81FBDBA5C9E}.Release|x64.ActiveCfg = Release|x64 + {9AD1DEC9-1561-4753-AB4B-E81FBDBA5C9E}.Release|x64.Build.0 = Release|x64 + {E40BFCBB-7A02-4E2C-AFDB-A717359EF4FC}.Debug|x64.ActiveCfg = Debug|x64 + {E40BFCBB-7A02-4E2C-AFDB-A717359EF4FC}.Debug|x64.Build.0 = Debug|x64 + {E40BFCBB-7A02-4E2C-AFDB-A717359EF4FC}.Release|x64.ActiveCfg = Release|x64 + {E40BFCBB-7A02-4E2C-AFDB-A717359EF4FC}.Release|x64.Build.0 = Release|x64 + {562B4814-2A78-4692-90BE-A727AABCEC85}.Debug|x64.ActiveCfg = Debug|x64 + {562B4814-2A78-4692-90BE-A727AABCEC85}.Debug|x64.Build.0 = Debug|x64 + {562B4814-2A78-4692-90BE-A727AABCEC85}.Release|x64.ActiveCfg = Release|x64 + {562B4814-2A78-4692-90BE-A727AABCEC85}.Release|x64.Build.0 = Release|x64 + {1A51EA31-6D78-4E98-B767-41A02C6E34D8}.Debug|x64.ActiveCfg = Debug|x64 + {1A51EA31-6D78-4E98-B767-41A02C6E34D8}.Debug|x64.Build.0 = Debug|x64 + {1A51EA31-6D78-4E98-B767-41A02C6E34D8}.Release|x64.ActiveCfg = Release|x64 + {1A51EA31-6D78-4E98-B767-41A02C6E34D8}.Release|x64.Build.0 = Release|x64 + {230BF08C-C039-473B-933F-3BF647440E0E}.Debug|x64.ActiveCfg = Debug|x64 + {230BF08C-C039-473B-933F-3BF647440E0E}.Debug|x64.Build.0 = Debug|x64 + {230BF08C-C039-473B-933F-3BF647440E0E}.Release|x64.ActiveCfg = Release|x64 + {230BF08C-C039-473B-933F-3BF647440E0E}.Release|x64.Build.0 = Release|x64 + {C55F4BA7-BBDD-42A4-88C1-FD3C411EB234}.Debug|x64.ActiveCfg = Debug|x64 + {C55F4BA7-BBDD-42A4-88C1-FD3C411EB234}.Debug|x64.Build.0 = Debug|x64 + {C55F4BA7-BBDD-42A4-88C1-FD3C411EB234}.Release|x64.ActiveCfg = Release|x64 + {C55F4BA7-BBDD-42A4-88C1-FD3C411EB234}.Release|x64.Build.0 = Release|x64 + {2979E556-5859-4E88-A1D4-EAB72F82294E}.Debug|x64.ActiveCfg = Debug|x64 + {2979E556-5859-4E88-A1D4-EAB72F82294E}.Debug|x64.Build.0 = Debug|x64 + {2979E556-5859-4E88-A1D4-EAB72F82294E}.Release|x64.ActiveCfg = Release|x64 + {2979E556-5859-4E88-A1D4-EAB72F82294E}.Release|x64.Build.0 = Release|x64 + {7E098666-DE8C-4ABF-B709-4CE7B1A491B0}.Debug|x64.ActiveCfg = Debug|x64 + {7E098666-DE8C-4ABF-B709-4CE7B1A491B0}.Debug|x64.Build.0 = Debug|x64 + {7E098666-DE8C-4ABF-B709-4CE7B1A491B0}.Release|x64.ActiveCfg = Release|x64 + {7E098666-DE8C-4ABF-B709-4CE7B1A491B0}.Release|x64.Build.0 = Release|x64 + {B5E11AAA-B800-455E-9C64-051FFEFD6C0B}.Debug|x64.ActiveCfg = Debug|x64 + {B5E11AAA-B800-455E-9C64-051FFEFD6C0B}.Debug|x64.Build.0 = Debug|x64 + {B5E11AAA-B800-455E-9C64-051FFEFD6C0B}.Release|x64.ActiveCfg = Release|x64 + {B5E11AAA-B800-455E-9C64-051FFEFD6C0B}.Release|x64.Build.0 = Release|x64 + {D47CC16E-466B-4D58-A8FC-ECAE5C9606FC}.Debug|x64.ActiveCfg = Debug|x64 + {D47CC16E-466B-4D58-A8FC-ECAE5C9606FC}.Debug|x64.Build.0 = Debug|x64 + {D47CC16E-466B-4D58-A8FC-ECAE5C9606FC}.Release|x64.ActiveCfg = Release|x64 + {D47CC16E-466B-4D58-A8FC-ECAE5C9606FC}.Release|x64.Build.0 = Release|x64 + {1143176D-B7F0-477C-90BB-72289068D927}.Debug|x64.ActiveCfg = Debug|x64 + {1143176D-B7F0-477C-90BB-72289068D927}.Debug|x64.Build.0 = Debug|x64 + {1143176D-B7F0-477C-90BB-72289068D927}.Release|x64.ActiveCfg = Release|x64 + {1143176D-B7F0-477C-90BB-72289068D927}.Release|x64.Build.0 = Release|x64 + {E454D3A5-C5C6-4291-BE96-220CF0D5CFFD}.Debug|x64.ActiveCfg = Debug|x64 + {E454D3A5-C5C6-4291-BE96-220CF0D5CFFD}.Debug|x64.Build.0 = Debug|x64 + {E454D3A5-C5C6-4291-BE96-220CF0D5CFFD}.Release|x64.ActiveCfg = Release|x64 + {E454D3A5-C5C6-4291-BE96-220CF0D5CFFD}.Release|x64.Build.0 = Release|x64 + {57D094C1-6913-46BF-A657-84A5F46D4EE7}.Debug|x64.ActiveCfg = Debug|x64 + {57D094C1-6913-46BF-A657-84A5F46D4EE7}.Debug|x64.Build.0 = Debug|x64 + {57D094C1-6913-46BF-A657-84A5F46D4EE7}.Release|x64.ActiveCfg = Release|x64 + {57D094C1-6913-46BF-A657-84A5F46D4EE7}.Release|x64.Build.0 = Release|x64 + {740E2894-903D-4B94-9C32-B630593BEB16}.Debug|x64.ActiveCfg = Debug|x64 + {740E2894-903D-4B94-9C32-B630593BEB16}.Debug|x64.Build.0 = Debug|x64 + {740E2894-903D-4B94-9C32-B630593BEB16}.Release|x64.ActiveCfg = Release|x64 + {740E2894-903D-4B94-9C32-B630593BEB16}.Release|x64.Build.0 = Release|x64 + {D401F706-A182-46E3-A25C-B0BF5AA0D07E}.Debug|x64.ActiveCfg = Debug|x64 + {D401F706-A182-46E3-A25C-B0BF5AA0D07E}.Debug|x64.Build.0 = Debug|x64 + {D401F706-A182-46E3-A25C-B0BF5AA0D07E}.Release|x64.ActiveCfg = Release|x64 + {D401F706-A182-46E3-A25C-B0BF5AA0D07E}.Release|x64.Build.0 = Release|x64 + {0FFA3F96-A68A-453F-A5FE-0C281EC371C7}.Debug|x64.ActiveCfg = Debug|x64 + {0FFA3F96-A68A-453F-A5FE-0C281EC371C7}.Debug|x64.Build.0 = Debug|x64 + {0FFA3F96-A68A-453F-A5FE-0C281EC371C7}.Release|x64.ActiveCfg = Release|x64 + {0FFA3F96-A68A-453F-A5FE-0C281EC371C7}.Release|x64.Build.0 = Release|x64 + {5FA79592-DE5B-46FF-9E05-34A2E72A7AF7}.Debug|x64.ActiveCfg = Debug|x64 + {5FA79592-DE5B-46FF-9E05-34A2E72A7AF7}.Debug|x64.Build.0 = Debug|x64 + {5FA79592-DE5B-46FF-9E05-34A2E72A7AF7}.Release|x64.ActiveCfg = Release|x64 + {5FA79592-DE5B-46FF-9E05-34A2E72A7AF7}.Release|x64.Build.0 = Release|x64 + {09FD3D3A-1EFC-4AEE-B3D7-096D238E0D1A}.Debug|x64.ActiveCfg = Debug|x64 + {09FD3D3A-1EFC-4AEE-B3D7-096D238E0D1A}.Debug|x64.Build.0 = Debug|x64 + {09FD3D3A-1EFC-4AEE-B3D7-096D238E0D1A}.Release|x64.ActiveCfg = Release|x64 + {09FD3D3A-1EFC-4AEE-B3D7-096D238E0D1A}.Release|x64.Build.0 = Release|x64 + {BDB7A8F3-87A6-4B77-9E0F-6BC785CBCF2B}.Debug|x64.ActiveCfg = Debug|x64 + {BDB7A8F3-87A6-4B77-9E0F-6BC785CBCF2B}.Debug|x64.Build.0 = Debug|x64 + {BDB7A8F3-87A6-4B77-9E0F-6BC785CBCF2B}.Release|x64.ActiveCfg = Release|x64 + {BDB7A8F3-87A6-4B77-9E0F-6BC785CBCF2B}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {52AC982E-7382-4746-BB66-4003698FCC02} = {7648DA91-6239-4877-86C6-2E5A79755F1E} + {5F5EF76B-D755-4C12-ADAE-11F08CE3D936} = {E05D1183-D360-4AFE-8968-314A34FAD3B2} + {B70A6F17-08C8-4194-BBE8-668CA920CFF3} = {E05D1183-D360-4AFE-8968-314A34FAD3B2} + {72180B0C-3D20-4AAD-B015-A9337B91406E} = {E05D1183-D360-4AFE-8968-314A34FAD3B2} + {1977360F-2E42-45E6-9369-AB1EE59CC5C5} = {E05D1183-D360-4AFE-8968-314A34FAD3B2} + {25C6CE64-2D61-4832-B6D2-45AFC52E2447} = {E05D1183-D360-4AFE-8968-314A34FAD3B2} + {8156B6D8-BD7E-4201-BD8B-8C9B00177F88} = {E05D1183-D360-4AFE-8968-314A34FAD3B2} + {990F5AFF-ABF6-4019-865D-604D2B23DE2C} = {E05D1183-D360-4AFE-8968-314A34FAD3B2} + {380E9F5A-23DE-4F5A-9644-EFA51AD1D8E8} = {7940E867-EEBA-4AFD-9904-1536F003239C} + {5A48C2FD-16E4-4B44-BC2C-D793C50E66F2} = {8CF74C87-534F-4017-A4ED-F2918025E31A} + {9AD1DEC9-1561-4753-AB4B-E81FBDBA5C9E} = {7648DA91-6239-4877-86C6-2E5A79755F1E} + {E40BFCBB-7A02-4E2C-AFDB-A717359EF4FC} = {7648DA91-6239-4877-86C6-2E5A79755F1E} + {5B9575EA-B4F9-46E4-A75E-59D430779EC7} = {E05D1183-D360-4AFE-8968-314A34FAD3B2} + {562B4814-2A78-4692-90BE-A727AABCEC85} = {5B9575EA-B4F9-46E4-A75E-59D430779EC7} + {1A51EA31-6D78-4E98-B767-41A02C6E34D8} = {5B9575EA-B4F9-46E4-A75E-59D430779EC7} + {230BF08C-C039-473B-933F-3BF647440E0E} = {5B9575EA-B4F9-46E4-A75E-59D430779EC7} + {C55F4BA7-BBDD-42A4-88C1-FD3C411EB234} = {5B9575EA-B4F9-46E4-A75E-59D430779EC7} + {2979E556-5859-4E88-A1D4-EAB72F82294E} = {5B9575EA-B4F9-46E4-A75E-59D430779EC7} + {7E098666-DE8C-4ABF-B709-4CE7B1A491B0} = {7940E867-EEBA-4AFD-9904-1536F003239C} + {B5E11AAA-B800-455E-9C64-051FFEFD6C0B} = {7940E867-EEBA-4AFD-9904-1536F003239C} + {95168D0B-1B2C-4295-B6D4-D5BAF781B9FA} = {7940E867-EEBA-4AFD-9904-1536F003239C} + {D47CC16E-466B-4D58-A8FC-ECAE5C9606FC} = {95168D0B-1B2C-4295-B6D4-D5BAF781B9FA} + {1143176D-B7F0-477C-90BB-72289068D927} = {95168D0B-1B2C-4295-B6D4-D5BAF781B9FA} + {E454D3A5-C5C6-4291-BE96-220CF0D5CFFD} = {95168D0B-1B2C-4295-B6D4-D5BAF781B9FA} + {57D094C1-6913-46BF-A657-84A5F46D4EE7} = {95168D0B-1B2C-4295-B6D4-D5BAF781B9FA} + {740E2894-903D-4B94-9C32-B630593BEB16} = {95168D0B-1B2C-4295-B6D4-D5BAF781B9FA} + {D401F706-A182-46E3-A25C-B0BF5AA0D07E} = {95168D0B-1B2C-4295-B6D4-D5BAF781B9FA} + {0FFA3F96-A68A-453F-A5FE-0C281EC371C7} = {95168D0B-1B2C-4295-B6D4-D5BAF781B9FA} + {9BF1CD59-1A2C-4023-9C8D-171DCB728078} = {95168D0B-1B2C-4295-B6D4-D5BAF781B9FA} + {5FA79592-DE5B-46FF-9E05-34A2E72A7AF7} = {9BF1CD59-1A2C-4023-9C8D-171DCB728078} + {09FD3D3A-1EFC-4AEE-B3D7-096D238E0D1A} = {5B9575EA-B4F9-46E4-A75E-59D430779EC7} + {BDB7A8F3-87A6-4B77-9E0F-6BC785CBCF2B} = {5B9575EA-B4F9-46E4-A75E-59D430779EC7} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D044BB14-0B37-47E5-A579-8B30FCBA1F9F} EndGlobalSection diff --git a/src/UniGetUI.v3.ncrunchsolution b/src/UniGetUI.v3.ncrunchsolution new file mode 100644 index 000000000..10420ac91 --- /dev/null +++ b/src/UniGetUI.v3.ncrunchsolution @@ -0,0 +1,6 @@ + + + True + True + + \ No newline at end of file diff --git a/src/UniGetUI/App.xaml.cs b/src/UniGetUI/App.xaml.cs index d730693e1..cf6c79dd0 100644 --- a/src/UniGetUI/App.xaml.cs +++ b/src/UniGetUI/App.xaml.cs @@ -1,52 +1,104 @@ using CommunityToolkit.WinUI.Notifications; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; -using UniGetUI.Core.Data; -using UniGetUI.Interface; -using UniGetUI.PackageEngine.Classes; -using UniGetUI.PackageEngine.Managers; -using UniGetUI.Core; using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; -using System.Linq.Expressions; using System.Threading.Tasks; +using UniGetUI.Core; +using UniGetUI.Core.Data; +using UniGetUI.Core.IconEngine; +using UniGetUI.Interface; +using UniGetUI.PackageEngine.Classes; +using UniGetUI.PackageEngine.Managers; using Windows.Foundation.Collections; -using Windows.UI; -using YamlDotNet.Serialization; +using UniGetUI.Core.Logging; +using UniGetUI.Core.Tools; +using UniGetUI.Core.SettingsEngine; +using UniGetUI.PackageEngine.ManagerClasses.Manager; +using UniGetUI.PackageEngine.Managers.ScoopManager; +using UniGetUI.PackageEngine.Managers.WingetManager; +using UniGetUI.PackageEngine.Managers.ChocolateyManager; +using UniGetUI.PackageEngine.Managers.PowerShellManager; +using UniGetUI.PackageEngine.Managers.DotNetManager; +using UniGetUI.PackageEngine.Managers.PipManager; +using UniGetUI.PackageEngine.Managers.NpmManager; +using System.Diagnostics; +using System.Globalization; +using System.Security.Cryptography; +using System.Net.Http; +using System.ComponentModel; +using UniGetUI.PackageEngine.Operations; +using System.ComponentModel.Design; +using CommunityToolkit.WinUI.Helpers; namespace UniGetUI { public partial class MainApp : Application { - public Scoop Scoop; - public Winget Winget; - public Chocolatey Choco; - public Pip Pip; - public Npm Npm; - public Dotnet Dotnet; - public PowerShell PowerShell; + public class __tooltip_options + { + private int _errors_occurred = 0; + public int ErrorsOccurred { get { return _errors_occurred; } set { _errors_occurred = value; MainApp.Instance.MainWindow.UpdateSystemTrayStatus(); } } + private bool _restart_required = false; + public bool RestartRequired { get { return _restart_required; } set { _restart_required = value; MainApp.Instance.MainWindow.UpdateSystemTrayStatus(); } } + private int _operations_in_progress = 0; + public int OperationsInProgress { get { return _operations_in_progress; } set { _operations_in_progress = value; MainApp.Instance.MainWindow.UpdateSystemTrayStatus(); } } + private int _available_updates = 0; + public int AvailableUpdates { get { return _available_updates; } set { _available_updates = value; MainApp.Instance.MainWindow.UpdateSystemTrayStatus(); } } + } +#pragma warning disable CS8618 + public static Scoop Scoop; + public static WinGet Winget; + public static Chocolatey Choco; + public static Pip Pip; + public static Npm Npm; + public static DotNet Dotnet; + public static PowerShell PowerShell; + public List OperationQueue = new(); public bool RaiseExceptionAsFatal = true; - public readonly List PackageManagerList = new(); public Interface.SettingsInterface settings; public MainWindow MainWindow; + public string GSudoPath; + public ThemeListener ThemeListener; - private BackgroundApiRunner BackgroundApi = new BackgroundApiRunner(); + private BackgroundApiRunner BackgroundApi = new(); private const int ManagerLoadTimeout = 10000; // 10 seconds timeout for Package Manager initialization + public static MainApp Instance; + public __tooltip_options TooltipStatus = new(); - public MainApp() + public MainApp() : base() { try { + Instance = this; + Scoop = new(); + Winget = new(); + Choco = new(); + Pip = new(); + Npm = new(); + Dotnet = new(); + PowerShell = new(); + + PackageManagerList.AddRange(new PackageManager[] + { + Winget, + Scoop, + Choco, + Pip, + Npm, + Dotnet, + PowerShell + }); + InitializeComponent(); - string preferredTheme = AppTools.GetSettingsValue_Static("PreferredTheme"); + string preferredTheme = Settings.GetValue("PreferredTheme"); if (preferredTheme == "dark") { RequestedTheme = ApplicationTheme.Dark; @@ -55,8 +107,9 @@ public MainApp() { RequestedTheme = ApplicationTheme.Light; } + ThemeListener = new ThemeListener(); - + LoadGSudo(); RegisterErrorHandling(); SetUpWebViewUserDataFolder(); InitializeMainWindow(); @@ -67,7 +120,30 @@ public MainApp() } catch (Exception e) { - AppTools.ReportFatalException(e); + CoreTools.ReportFatalException(e); + } + } + + private async void LoadGSudo() + { + var gsudo_result = await CoreTools.Which("gsudo.exe"); + if (Settings.Get("UseUserGSudo")) + { + if (gsudo_result.Item1 != false) + { + Logger.Info($"Using System GSudo at {gsudo_result.Item2}"); + GSudoPath = gsudo_result.Item2; + } + else + { + Logger.Error("System GSudo enabled but not found!"); + GSudoPath = Path.Join(CoreData.UniGetUIExecutableDirectory, "Assets", "Utilities", "gsudo.exe"); + } + } + else + { + GSudoPath = Path.Join(CoreData.UniGetUIExecutableDirectory, "Assets", "Utilities", "gsudo.exe"); + Logger.Info($"Using bundled GSudo at {GSudoPath}"); } } @@ -75,26 +151,26 @@ private void RegisterErrorHandling() { UnhandledException += (sender, e) => { - var message = $"Unhandled Exception raised: {e.Message}"; - var stackTrace = $"Stack Trace: \n{e.Exception.StackTrace}"; - AppTools.Log(" -"); - AppTools.Log(" -"); - AppTools.Log(" ⚠️⚠️⚠️ START OF UNHANDLED ERROR TRACE ⚠️⚠️⚠️"); - AppTools.Log(message); - AppTools.Log(stackTrace); - AppTools.Log(" ⚠️⚠️⚠️ END OF UNHANDLED ERROR TRACE ⚠️⚠️⚠️"); - AppTools.Log(" -"); - AppTools.Log(" -"); + string message = $"Unhandled Exception raised: {e.Message}"; + string stackTrace = $"Stack Trace: \n{e.Exception.StackTrace}"; + Logger.Error(" -"); + Logger.Error(" -"); + Logger.Error(" ⚠️⚠️⚠️ START OF UNHANDLED ERROR TRACE ⚠️⚠️⚠️"); + Logger.Error(e.Message); + Logger.Error(e.Exception); + Logger.Error(" ⚠️⚠️⚠️ END OF UNHANDLED ERROR TRACE ⚠️⚠️⚠️"); + Logger.Error(" -"); + Logger.Error(" -"); if (Environment.GetCommandLineArgs().Contains("--report-all-errors") || RaiseExceptionAsFatal || MainWindow == null) - AppTools.ReportFatalException(e.Exception); + CoreTools.ReportFatalException(e.Exception); else { - MainWindow.ErrorBanner.Title = AppTools.Instance.Translate("Something went wrong"); - MainWindow.ErrorBanner.Message = AppTools.Instance.Translate("An interal error occurred. Please view the log for further details."); + MainWindow.ErrorBanner.Title = CoreTools.Translate("Something went wrong"); + MainWindow.ErrorBanner.Message = CoreTools.Translate("An interal error occurred. Please view the log for further details."); MainWindow.ErrorBanner.IsOpen = true; - var button = new Button() + Button button = new() { - Content = AppTools.Instance.Translate("WingetUI log"), + Content = CoreTools.Translate("WingetUI Log"), }; button.Click += (sender, args) => { @@ -117,7 +193,8 @@ private void SetUpWebViewUserDataFolder() } catch (Exception e) { - AppTools.Log(e); + Logger.Warn("Could not set up data folder for WebView2"); + Logger.Warn(e); } } @@ -131,12 +208,12 @@ private void InitializeMainWindow() BlockLoading = true }; MainWindow.Closed += (sender, args) => DisposeAndQuit(0); - - var hWnd = MainWindow.GetWindowHandle(); - var windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hWnd); - var appWindow = Microsoft.UI.Windowing.AppWindow.GetFromWindowId(windowId); - if(appWindow != null) + nint hWnd = MainWindow.GetWindowHandle(); + Microsoft.UI.WindowId windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hWnd); + Microsoft.UI.Windowing.AppWindow appWindow = Microsoft.UI.Windowing.AppWindow.GetFromWindowId(windowId); + + if (appWindow != null) appWindow.Closing += MainWindow.HandleClosingEvent; } @@ -152,7 +229,7 @@ private void ClearNotificationHistory_Safe() } catch (Exception ex) { - AppTools.Log(ex); + Logger.Warn(ex); } } @@ -165,8 +242,8 @@ private void RegisterNotificationActivationEvent_Safe() { ToastNotificationManagerCompat.OnActivated += toastArgs => { - var args = ToastArguments.Parse(toastArgs.Argument); - var userInput = toastArgs.UserInput; + ToastArguments args = ToastArguments.Parse(toastArgs.Argument); + ValueSet userInput = toastArgs.UserInput; MainWindow.DispatcherQueue.TryEnqueue(() => { @@ -176,10 +253,16 @@ private void RegisterNotificationActivationEvent_Safe() } catch (Exception ex) { - AppTools.Log(ex); + Logger.Error("Could not register notification event"); + Logger.Error(ex); } } + public void AddOperationToList(AbstractOperation operation) + { + MainWindow.NavigationPage.OperationStackPanel.Children.Add(operation); + } + /// /// Background component loader /// @@ -187,20 +270,23 @@ private void RegisterNotificationActivationEvent_Safe() private async Task LoadComponentsAsync() { try - { + { InitializePackageManagers(); // Run other initializations asynchronously - AppTools.Instance.UpdateUniGetUIIfPossible(); - _ = CoreData.LoadIconAndScreenshotsDatabase(); - if(!AppTools.Instance.GetSettings("DisableApi")) + UpdateUniGetUIIfPossible(); + + IconDatabase.InitializeInstance(); + IconDatabase.Instance.LoadIconAndScreenshotsDatabase(); + + if (!Settings.Get("DisableApi")) _ = BackgroundApi.Start(); _ = MainWindow.DoEntryTextAnimationAsync(); await InitializeAllManagersAsync(); - AppTools.Log("LoadComponentsAsync finished executing. All managers loaded. Proceeding to interface."); + Logger.Info("LoadComponentsAsync finished executing. All managers loaded. Proceeding to interface."); MainWindow.SwitchToInterface(); RaiseExceptionAsFatal = false; @@ -209,7 +295,7 @@ private async Task LoadComponentsAsync() } catch (Exception e) { - AppTools.ReportFatalException(e); + CoreTools.ReportFatalException(e); } } @@ -218,24 +304,7 @@ private async Task LoadComponentsAsync() ///
private void InitializePackageManagers() { - Winget = new Winget(); - Scoop = new Scoop(); - Choco = new Chocolatey(); - Pip = new Pip(); - Npm = new Npm(); - Dotnet = new Dotnet(); - PowerShell = new PowerShell(); - - PackageManagerList.AddRange(new PackageManager[] - { - Winget, - Scoop, - Choco, - Pip, - Npm, - Dotnet, - PowerShell - }); + } /// @@ -244,25 +313,25 @@ private void InitializePackageManagers() /// private async Task InitializeAllManagersAsync() { - var initializeTasks = new List(); + List initializeTasks = new(); - foreach (var manager in PackageManagerList) + foreach (PackageManager manager in PackageManagerList) { initializeTasks.Add(manager.InitializeAsync()); } - var ManagersMetaTask = Task.WhenAll(initializeTasks); + Task ManagersMetaTask = Task.WhenAll(initializeTasks); try { await ManagersMetaTask.WaitAsync(TimeSpan.FromMilliseconds(ManagerLoadTimeout)); } catch (Exception e) { - AppTools.Log(e); + Logger.Error(e); } if (ManagersMetaTask.IsCompletedSuccessfully == false) - AppTools.Log("Timeout: Not all package managers have finished initializing."); + Logger.Warn("Timeout: Not all package managers have finished initializing."); } protected override async void OnLaunched(LaunchActivatedEventArgs args) @@ -287,10 +356,138 @@ public async Task ShowMainWindowFromRedirectAsync() public void DisposeAndQuit(int outputCode = 0) { - AppTools.Log("Quitting..."); + Logger.Warn("Quitting..."); MainWindow?.Close(); BackgroundApi?.Stop(); Environment.Exit(outputCode); } + + private async void UpdateUniGetUIIfPossible(int round = 0) + { + InfoBar? banner = null; + try + { + Logger.Debug("Starting update check"); + + string fileContents = ""; + + using (HttpClient client = new()) + fileContents = await client.GetStringAsync("https://www.marticliment.com/versions/unigetui.ver"); + + + if (!fileContents.Contains("///")) + throw new FormatException("The updates file does not follow the FloatVersion///Sha256Hash format"); + + float LatestVersion = float.Parse(fileContents.Split("///")[0].Replace("\n", "").Trim(), CultureInfo.InvariantCulture); + string InstallerHash = fileContents.Split("///")[1].Replace("\n", "").Trim().ToLower(); + + if (LatestVersion > CoreData.VersionNumber) + { + Logger.Info("Updates found, downloading installer..."); + Logger.Info("Current version: " + CoreData.VersionNumber.ToString(CultureInfo.InvariantCulture)); + Logger.Info("Latest version : " + LatestVersion.ToString(CultureInfo.InvariantCulture)); + + banner = MainWindow.UpdatesBanner; + banner.Title = CoreTools.Translate("WingetUI version {0} is being downloaded.").Replace("{0}", LatestVersion.ToString(CultureInfo.InvariantCulture)); + banner.Message = CoreTools.Translate("This may take a minute or two"); + banner.Severity = InfoBarSeverity.Informational; + banner.IsOpen = true; + banner.IsClosable = false; + + Uri DownloadUrl = new("https://github.com/marticliment/WingetUI/releases/latest/download/UniGetUI.Installer.exe"); + string InstallerPath = Path.Join(Directory.CreateTempSubdirectory().FullName, "unigetui-updater.exe"); + + using (HttpClient client = new()) + { + HttpResponseMessage result = await client.GetAsync(DownloadUrl); + using (FileStream fs = new(InstallerPath, FileMode.CreateNew)) + await result.Content.CopyToAsync(fs); + } + + string Hash = ""; + SHA256 Sha256 = SHA256.Create(); + using (FileStream stream = File.OpenRead(InstallerPath)) + { + Hash = Convert.ToHexString(Sha256.ComputeHash(stream)).ToLower(); + } + + if (Hash == InstallerHash) + { + + banner.Title = CoreTools.Translate("WingetUI {0} is ready to be installed.").Replace("{0}", LatestVersion.ToString(CultureInfo.InvariantCulture)); + banner.Message = CoreTools.Translate("The update will be installed upon closing WingetUI"); + banner.ActionButton = new Button(); + banner.ActionButton.Content = CoreTools.Translate("Update now"); + banner.ActionButton.Click += (sender, args) => { MainWindow.HideWindow(); }; + banner.Severity = InfoBarSeverity.Success; + banner.IsOpen = true; + banner.IsClosable = true; + + if (MainWindow.Visible) + Logger.Debug("Waiting for mainWindow to be hidden"); + + while (MainWindow.Visible) + await Task.Delay(100); + + Logger.ImportantInfo("The hash matches the expected value, starting update process..."); + Process p = new(); + p.StartInfo.FileName = "cmd.exe"; + p.StartInfo.Arguments = $"/c start /B \"\" \"{InstallerPath}\" /silent"; + p.StartInfo.UseShellExecute = true; + p.StartInfo.CreateNoWindow = true; + p.Start(); + DisposeAndQuit(); + } + else + { + Logger.Error("Hash mismatch, not updating!"); + Logger.Error("Current hash : " + Hash); + Logger.Error("Expected hash: " + InstallerHash); + File.Delete(InstallerPath); + + banner.Title = CoreTools.Translate("The installer hash does not match the expected value."); + banner.Message = CoreTools.Translate("The update will not continue."); + banner.Severity = InfoBarSeverity.Error; + banner.IsOpen = true; + banner.IsClosable = true; + + await Task.Delay(3600000); // Check again in 1 hour + UpdateUniGetUIIfPossible(); + } + } + else + { + Logger.Info("UniGetUI is up to date"); + await Task.Delay(3600000); // Check again in 1 hour + UpdateUniGetUIIfPossible(); + } + } + catch (Exception e) + { + if (banner != null) + { + banner.Title = CoreTools.Translate("An error occurred when checking for updates: "); + banner.Message = e.Message; + banner.Severity = InfoBarSeverity.Error; + banner.IsOpen = true; + banner.IsClosable = true; + } + + Logger.Error(e); + + if (round >= 3) + return; + + await Task.Delay(600000); // Try again in 10 minutes + UpdateUniGetUIIfPossible(round + 1); + } + } + + public void RestartApp() + { + Logger.Info(Environment.GetCommandLineArgs()[0].Replace(".dll", ".exe")); + Process.Start(Environment.GetCommandLineArgs()[0].Replace(".dll", ".exe")); + DisposeAndQuit(0); + } } } diff --git a/src/UniGetUI/Core/Data/Core.cs b/src/UniGetUI/Core/Data/Core.cs deleted file mode 100644 index d8ca121dc..000000000 --- a/src/UniGetUI/Core/Data/Core.cs +++ /dev/null @@ -1,255 +0,0 @@ -using UniGetUI.Core; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Net; -using System.Security.Cryptography; -using System.Text.Json; -using System.Threading.Tasks; -using Windows.Storage.Search; -using System.Net.Http; - -namespace UniGetUI.Core.Data -{ - public static class CoreData - { - public static string VersionName = "3.1.0-beta"; // Do not modify this line, use file scripts/apply_versions.py - public static double VersionNumber = 3.09; // Do not modify this line, use file scripts/apply_versions.py - - /// - /// The directory where all the user data is stored. The directory is automatically created if it does not exist. - /// - public static string UniGetUIDataDirectory - { - get - { - var old_path = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".wingetui"); - var new_path = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "UniGetUI"); - return GetNewDataDirectoryOrMoveOld(old_path, new_path); - } - } - - /// - /// The directory where the installation options are stored. The directory is automatically created if it does not exist. - /// - public static string UniGetUIInstallationOptionsDirectory - { - get => Path.Join(UniGetUIDataDirectory, "InstallationOptions"); - } - - /// - /// The directory where the metadata cache is stored. The directory is automatically created if it does not exist. - /// - public static string UniGetUICacheDirectory_Data - { - get - { - var old_path = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "WingetUI", "CachedData"); - var new_path = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "UniGetUI", "CachedMetadata"); - return GetNewDataDirectoryOrMoveOld(old_path, new_path); - } - } - - /// - /// The directory where the cached icons and screenshots are saved. The directory is automatically created if it does not exist. - /// - public static string UniGetUICacheDirectory_Icons - { - get - { - var old_path = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "WingetUI", "CachedIcons"); - var new_path = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "UniGetUI", "CachedMedia"); - return GetNewDataDirectoryOrMoveOld(old_path, new_path); - } - } - - /// - /// The directory where the cached language files are stored. The directory is automatically created if it does not exist. - /// - public static string UniGetUICacheDirectory_Lang - { - get - { - var old_dir = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "WingetUI", "CachedLangFiles"); - var new_dir = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "UniGetUI", "CachedLanguageFiles"); - return GetNewDataDirectoryOrMoveOld(old_dir, new_dir); - } - } - - /// - /// The directory where package backups will be saved by default. - /// - public static string UniGetUI_DefaultBackupDirectory - { - get - { - var old_dir = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "WingetUI"); - var new_dir = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "UniGetUI"); - return GetNewDataDirectoryOrMoveOld(old_dir, new_dir); - } - } - - /// - /// The file where the screenshot metadata is stored. If the file does not exist, it will be created automatically. - /// - public static string IgnoredUpdatesDatabaseFile - { - get - { - // Calling the UniGetUIDataDirectory will create the directory if it does not exist - var file_path = Path.Join(UniGetUIDataDirectory, "IgnoredPackageUpdates.json"); - if (!File.Exists(file_path)) - File.WriteAllText(file_path, "{}"); - return file_path; - } - } - public static bool IsDaemon = false; - - public static string UniGetUILog = ""; - public static string ManagerLogs = ""; - - - private static int __volatile_notification_id_counter = 1235; - - /// - /// A self-incremented value to generate random notification IDs - /// - public static int VolatileNotificationIdCounter { - get => __volatile_notification_id_counter++; - } - - /// - /// The ID of the notification that is used to inform the user that updates are available - /// - public static int UpdatesAvailableNotificationId = 1234; - - /// - /// A path pointing to the location where the app is installed - /// - public static string UniGetUIExecutableDirectory = Directory.GetParent(Environment.ProcessPath).FullName; - - /// - /// A path pointing to the executable file of the app - /// - public static string UniGetUIExecutableFile = Environment.ProcessPath; - - /// - /// A path pointing to the gsudo executable bundled with the app - /// - public static string GSudoPath = Path.Join(UniGetUIExecutableDirectory, "Assets", "Utilities", "gsudo.exe"); - - /// - /// The icon and screensho database - /// - public static Dictionary IconDatabaseData = new(); - - /// - /// Tis class represents the structure of the icon and screenshot database. It is used to deserialize the JSON data. - /// - public class IconScreenshotDatabase_v2 - { - public class PackageCount - { - public int total { get; set; } - public int done { get; set; } - public int packages_with_icon { get; set; } - public int packages_with_screenshot { get; set; } - public int total_screenshots { get; set; } - } - public class PackageIconAndScreenshots - { - public string icon { get; set; } - public List images { get; set; } - } - - public PackageCount package_count { get; set; } - public Dictionary icons_and_screenshots { get; set; } - } - - /// - /// Download the icon and screenshots database to a local file, and load it into memory - /// - /// - public static async Task LoadIconAndScreenshotsDatabase() - { - string IconsAndScreenshotsFile = Path.Join(UniGetUICacheDirectory_Data, "Icon Database.json"); - - try - { - Uri DownloadUrl = new("https://raw.githubusercontent.com/marticliment/WingetUI/main/WebBasedData/screenshot-database-v2.json"); - if (AppTools.GetSettings_Static("IconDataBaseURL")) - DownloadUrl = new Uri(AppTools.GetSettingsValue_Static("IconDataBaseURL")); - - using (HttpClient client = new()) - { - string fileContents = await client.GetStringAsync(DownloadUrl); - await File.WriteAllTextAsync(IconsAndScreenshotsFile, fileContents); - } - - AppTools.Log("Downloaded icons and screenshots successfully!"); - - } - catch (Exception e) - { - AppTools.Log("Failed to download icons and screenshots"); - AppTools.Log(e); - } - - - if (!File.Exists(IconsAndScreenshotsFile)) - { - AppTools.Log("WARNING: Icon Database file not found"); - return; - } - - try - { - IconScreenshotDatabase_v2 JsonData = JsonSerializer.Deserialize(await File.ReadAllTextAsync(IconsAndScreenshotsFile)); - if (JsonData.icons_and_screenshots != null) - IconDatabaseData = JsonData.icons_and_screenshots; - } - catch (Exception ex) - { - AppTools.Log("Failed to load icon database"); - AppTools.Log(ex); - } - } - - /// - /// This method will return the most appropriate data directory. - /// If the new directory exists, it will be used. - /// If the new directory does not exist, but the old directory does, it will be moved to the new location, and the new location will be used. - /// If none exist, the new directory will be created. - /// - /// The old/legacy directory - /// The new directory - /// The path to an existing, valid directory - private static string GetNewDataDirectoryOrMoveOld(string old_path, string new_path) - { - if (Directory.Exists(new_path)) - return new_path; - else if (Directory.Exists(old_path)) - { - try - { - Directory.Move(old_path, new_path); - return new_path; - } - catch (Exception e) - { - AppTools.Log("WARNING: Cannot move old data directory to new location. Directory to move: " + old_path + ". Destination: " + new_path); - AppTools.Log(e); - return old_path; - } - } - else - { - AppTools.Log("Creating non-existing data directory at: " + new_path); - Directory.CreateDirectory(new_path); - return new_path; - } - } - - } -} diff --git a/src/UniGetUI/Core/Data/LanguageData.cs b/src/UniGetUI/Core/Data/LanguageData.cs deleted file mode 100644 index c792a75ed..000000000 --- a/src/UniGetUI/Core/Data/LanguageData.cs +++ /dev/null @@ -1,199 +0,0 @@ -using UniGetUI.PackageEngine.Classes; -using UniGetUI.Core; -using Nancy.Extensions; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Runtime.InteropServices; -using System.Text.Json.Nodes; -using System.Threading.Tasks; -using Windows.Media.Streaming.Adaptive; - -namespace UniGetUI.Core.Data -{ - public static class LanguageData - { - public static string TranslatorsJSON = File.ReadAllText( - Path.Join(CoreData.UniGetUIExecutableDirectory, "Assets", "Data", "Translators.json") - ); - - public static Dictionary LanguageList = (JsonObject.Parse( - File.ReadAllText(Path.Join(CoreData.UniGetUIExecutableDirectory, "Assets", "Data", "LanguagesReference.json")) - ) as JsonObject).ToDictionary(x => x.Key, x => x.Value.ToString()); - - public static Dictionary TranslatedPercentages = (JsonObject.Parse( - File.ReadAllText(Path.Join(CoreData.UniGetUIExecutableDirectory, "Assets", "Data", "TranslatedPercentages.json")) - ) as JsonObject).ToDictionary(x => x.Key, x => x.Value.ToString()); - } - - public class LanguageEngine - { - public static Dictionary MainLangDict = new(); - - public LanguageEngine() - { - string LangName = AppTools.GetSettingsValue_Static("PreferredLanguage"); - if (LangName == "default" || LangName == "") - { - LangName = System.Globalization.CultureInfo.CurrentCulture.ToString().Replace("-", "_"); - } - - if (LanguageData.LanguageList.ContainsKey(LangName)) - { - MainLangDict = LoadLanguageFile(LangName); - MainLangDict.TryAdd("locale", LangName); - } - else if (LanguageData.LanguageList.ContainsKey(LangName[0..2])) - { - MainLangDict = LoadLanguageFile(LangName[0..2]); - MainLangDict.TryAdd("locale", LangName[0..2]); - } - else - { - MainLangDict = LoadLanguageFile("en"); - MainLangDict.TryAdd("locale", "en"); - } - LoadStaticTranslation(); - AppTools.Log("Loaded language locale: " + MainLangDict["locale"]); - } - - public Dictionary LoadLanguageFile(string LangKey, bool ForceBundled = false) - { - try - { - Dictionary LangDict = new(); - string LangFileToLoad = Path.Join(CoreData.UniGetUICacheDirectory_Lang, "lang_" + LangKey + ".json"); - AppTools.Log(LangFileToLoad); - - if (!File.Exists(LangFileToLoad) || AppTools.GetSettings_Static("DisableLangAutoUpdater")) - { - ForceBundled = true; - } - - if (ForceBundled) - { - LangFileToLoad = Path.Join(CoreData.UniGetUIExecutableDirectory, "Assets", "Languages", "lang_" + LangKey + ".json"); - AppTools.Log(LangFileToLoad); - } - - LangDict = (JsonNode.Parse(File.ReadAllText(LangFileToLoad)) as JsonObject).ToDictionary(x => x.Key, x => x.Value != null ? x.Value.ToString() : ""); - - if (!AppTools.GetSettings_Static("DisableLangAutoUpdater")) - _ = UpdateLanguageFile(LangKey); - - return LangDict; - } - catch (Exception e) - { - AppTools.Log($"LoadLanguageFile Failed for LangKey={LangKey}, ForceBundled={ForceBundled}"); - AppTools.Log(e); - return new Dictionary(); - } - } - - public async Task UpdateLanguageFile(string LangKey, bool UseOldUrl = false) - { - try - { - Uri NewFile = new("https://raw.githubusercontent.com/marticliment/WingetUI/main/src/" + (UseOldUrl? "wingetui": "UniGetUI") + "/Assets/Languages/lang_" + LangKey + ".json"); - - HttpClient client = new(); - string fileContents = await client.GetStringAsync(NewFile); - - if (!Directory.Exists(CoreData.UniGetUICacheDirectory_Lang)) - Directory.CreateDirectory(CoreData.UniGetUICacheDirectory_Lang); - - File.WriteAllText(Path.Join(CoreData.UniGetUICacheDirectory_Lang, "lang_" + LangKey + ".json"), fileContents); - - AppTools.Log("Lang files were updated successfully"); - } - catch (Exception e) - { - if (e is HttpRequestException && !UseOldUrl) - await UpdateLanguageFile(LangKey, true); - else - AppTools.Log(e); - } - } - - public void LoadStaticTranslation() - { - CommonTranslations.ScopeNames[PackageScope.Local] = Translate("User | Local"); - CommonTranslations.ScopeNames[PackageScope.Global] = Translate("Machine | Global"); - - CommonTranslations.InvertedScopeNames.Clear(); - CommonTranslations.InvertedScopeNames.Add(Translate("Machine | Global"), PackageScope.Global); - CommonTranslations.InvertedScopeNames.Add(Translate("User | Local"), PackageScope.Local); - } - - public string Translate(string key) - { - if (key == "WingetUI") - { - if (MainLangDict.ContainsKey("formerly WingetUI") && MainLangDict["formerly WingetUI"] != "") - return "UniGetUI (" + MainLangDict["formerly WingetUI"] + ")"; - return "UniGetUI (formerly WingetUI)"; - } - else if (key == "Formerly known as WingetUI") - { - if (MainLangDict.ContainsKey(key)) - return MainLangDict[key]; - return key; - } - - if (key == null || key == "") - return ""; - else if (MainLangDict.ContainsKey(key) && MainLangDict[key] != "") - return MainLangDict[key].Replace("WingetUI", "UniGetUI"); - else - return key.Replace("WingetUI", "UniGetUI"); - } - } - - public static class CommonTranslations - { - public static Dictionary ArchNames = new() - { - { Architecture.X64, "x64" }, - { Architecture.X86, "x86" }, - { Architecture.Arm64, "arm64" }, - { Architecture.Arm, "arm32" }, - }; - - public static Dictionary InvertedArchNames = new() - { - { "x64", Architecture.X64 }, - { "x86", Architecture.X86 }, - { "arm64", Architecture.Arm64 }, - { "arm32", Architecture.Arm }, - }; - - public static Dictionary ScopeNames = new() - { - { PackageScope.Global, "Machine | Global" }, - { PackageScope.Local, "User | Local" }, - }; - - public static Dictionary InvertedScopeNames = new() - { - { "Machine | Global", PackageScope.Global }, - { "User | Local", PackageScope.Local }, - }; - - public static Dictionary ScopeNames_NonLang = new() - { - { PackageScope.Global, "machine" }, - { PackageScope.Local, "user" }, - }; - - public static Dictionary InvertedScopeNames_NonLang = new() - { - { "machine", PackageScope.Global }, - { "user", PackageScope.Local }, - }; - } -} - diff --git a/src/UniGetUI/Core/Tools.cs b/src/UniGetUI/Core/Tools.cs deleted file mode 100644 index b16622396..000000000 --- a/src/UniGetUI/Core/Tools.cs +++ /dev/null @@ -1,471 +0,0 @@ -using CommunityToolkit.WinUI.Controls; -using CommunityToolkit.WinUI.Helpers; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using UniGetUI.Core.Data; -using UniGetUI.PackageEngine; -using UniGetUI.PackageEngine.Classes; -using Nancy; -using Nancy.Conventions; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Security.Cryptography; -using System.Security.Principal; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using UniGetUI.PackageEngine.Operations; - -namespace UniGetUI.Core -{ - public class AppTools - { - - public class __tooltip_options - { - private int _errors_occurred = 0; - public int ErrorsOccurred { get { return _errors_occurred; } set { _errors_occurred = value; AppTools.Instance.App.MainWindow.UpdateSystemTrayStatus(); } } - private bool _restart_required = false; - public bool RestartRequired { get { return _restart_required; } set { _restart_required = value; AppTools.Instance.App.MainWindow.UpdateSystemTrayStatus(); } } - private int _operations_in_progress = 0; - public int OperationsInProgress { get { return _operations_in_progress; } set { _operations_in_progress = value; AppTools.Instance.App.MainWindow.UpdateSystemTrayStatus(); } } - private int _available_updates = 0; - public int AvailableUpdates { get { return _available_updates; } set { _available_updates = value; AppTools.Instance.App.MainWindow.UpdateSystemTrayStatus(); } } - } - - - public MainApp App; - - public ThemeListener ThemeListener; - public List OperationQueue = new(); - - public __tooltip_options TooltipStatus = new(); - - private LanguageEngine LanguageEngine = new(); - - private static AppTools instance; - string ApiAuthToken; - - public static AppTools Instance - { - get - { - if (instance == null) - { - instance = new AppTools(); - } - return instance; - } - } - - private AppTools() - { - App = (MainApp)Application.Current; - ThemeListener = new ThemeListener(); - - ApiAuthToken = RandomString(64); - SetSettingsValue("CurrentSessionToken", ApiAuthToken); - AppTools.Log("Api auth token: " + ApiAuthToken); - } - - private string RandomString(int length) - { - var random = new Random(); - const string pool = "abcdefghijklmnopqrstuvwxyz0123456789"; - var chars = Enumerable.Range(0, length) - .Select(x => pool[random.Next(0, pool.Length)]); - return new string(chars.ToArray()); - } - - public bool GetSettings(string setting, bool invert = false) - { return AppTools.GetSettings_Static(setting, invert); } - - public static bool GetSettings_Static(string setting, bool invert = false) - { - return File.Exists(Path.Join(CoreData.UniGetUIDataDirectory, setting)) ^ invert; - } - - public void SetSettings(string setting, bool value) - { AppTools.SetSettings_Static(setting, value); } - - public static void SetSettings_Static(string setting, bool value) - { - try { - if (value) - { - if (!File.Exists(Path.Join(CoreData.UniGetUIDataDirectory, setting))) - File.WriteAllText(Path.Join(CoreData.UniGetUIDataDirectory, setting), ""); - } - else - { - if (File.Exists(Path.Join(CoreData.UniGetUIDataDirectory, setting))) - File.Delete(Path.Join(CoreData.UniGetUIDataDirectory, setting)); - } - } - catch (Exception e) - { - Log($"CRITICAL ERROR: CANNOT SET SETTING FOR setting={setting} enabled={value}: " + e.Message); - } - } - public string GetSettingsValue(string setting) - { return AppTools.GetSettingsValue_Static(setting); } - - public static string GetSettingsValue_Static(string setting) - { - if (!File.Exists(Path.Join(CoreData.UniGetUIDataDirectory, setting))) - return ""; - return File.ReadAllText(Path.Join(CoreData.UniGetUIDataDirectory, setting)); - } - public void SetSettingsValue(string setting, string value) - { AppTools.SetSettingsValue_Static(setting, value); } - - public static void SetSettingsValue_Static(string setting, string value) - { - try - { - File.WriteAllText(Path.Join(CoreData.UniGetUIDataDirectory, setting), value); - } - catch (Exception e) - { - Log($"CRITICAL ERROR: CANNOT SET SETTING VALUE FOR setting={setting} value={value}: " + e.Message); - } - } - - /// - /// Translate a string to the current language - /// - /// The string to translate - /// The translated string if available, the original string otherwise - public string Translate(string text) - { - return LanguageEngine.Translate(text); - } - - /// - /// Dummy function to capture the strings that need to be translated but the translation is handled by a custom widget - /// - /// - /// - public string AutoTranslated(string text) - { - return text; - } - - public void RestartApp() - { - AppTools.Log(Environment.GetCommandLineArgs()[0].Replace(".dll", ".exe")); - System.Diagnostics.Process.Start(Environment.GetCommandLineArgs()[0].Replace(".dll", ".exe")); - App.DisposeAndQuit(); - } - - public async Task Which(string command) - { - Process process = new() - { - StartInfo = new ProcessStartInfo() - { - FileName = "cmd.exe", - Arguments = "/C where " + command, - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - } - }; - process.Start(); - string line = await process.StandardOutput.ReadLineAsync(); - string output; - if (line == null) - output = ""; - else - output = line.Trim(); - await process.WaitForExitAsync(); - if (process.ExitCode != 0 || output == "") - return Path.Join(Environment.GetLogicalDrives()[0], "ThisExe\\WasNotFound\\InPath", command); - else - return output; - } - - public string FormatAsName(string name) - { - name = name.Replace(".install", "").Replace(".portable", "").Replace("-", " ").Replace("_", " ").Split("/")[^1]; - string newName = ""; - for (int i = 0; i < name.Length; i++) - { - if (i == 0 || name[i - 1] == ' ') - newName += name[i].ToString().ToUpper(); - else - newName += name[i]; - } - return newName; - } - - public void AddOperationToList(AbstractOperation operation) - { - App.MainWindow.NavigationPage.OperationStackPanel.Children.Add(operation); - } - - public static void Log(string s) - { - CoreData.UniGetUILog += s + "\n"; - Debug.WriteLine(s); - } - - public static void Log(Exception e) - { Log(e.ToString()); } - - public static void Log(object o) - { if (o != null) Log(o.ToString()); else Log("null"); } - - - public static void LogManagerOperation(PackageManager manager, Process process, string output) - { - output = Regex.Replace(output, "\n.{0,6}\n", "\n"); - CoreData.ManagerLogs += $"\n▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄"; - CoreData.ManagerLogs += $"\n█▀▀▀▀▀▀▀▀▀ [{DateTime.Now}] {manager.Name} ▀▀▀▀▀▀▀▀▀▀▀"; - CoreData.ManagerLogs += $"\n█ Executable: {process.StartInfo.FileName}"; - CoreData.ManagerLogs += $"\n█ Arguments: {process.StartInfo.Arguments}"; - CoreData.ManagerLogs += "\n"; - CoreData.ManagerLogs += output; - CoreData.ManagerLogs += "\n"; - CoreData.ManagerLogs += $"[{DateTime.Now}] Exit Code: {process.ExitCode}"; - CoreData.ManagerLogs += "\n"; - CoreData.ManagerLogs += "\n"; - } - - public static void ReportFatalException(Exception e) - { - string LangName = "Unknown"; - try - { - LangName = LanguageEngine.MainLangDict["langName"]; - } - catch { } - - string Error_String = $@" - OS: {Environment.OSVersion.Platform} - Version: {Environment.OSVersion.VersionString} - OS Architecture: {Environment.Is64BitOperatingSystem} - APP Architecture: {Environment.Is64BitProcess} - Language: {LangName} - APP Version: {CoreData.VersionName} - Executable: {Environment.ProcessPath} - -Crash Message: {e.Message} - -Crash Traceback: -{e.StackTrace}"; - - Console.WriteLine(Error_String); - - - string ErrorBody = "https://www.marticliment.com/error-report/?appName=UniGetUI^&errorBody=" + Uri.EscapeDataString(Error_String.Replace("\n", "{l}")); - - Console.WriteLine(ErrorBody); - - using System.Diagnostics.Process cmd = new(); - cmd.StartInfo.FileName = "cmd.exe"; - cmd.StartInfo.RedirectStandardInput = true; - cmd.StartInfo.RedirectStandardOutput = false; - cmd.StartInfo.CreateNoWindow = true; - cmd.StartInfo.UseShellExecute = false; - cmd.Start(); - cmd.StandardInput.WriteLine("start " + ErrorBody); - cmd.StandardInput.WriteLine("exit"); - cmd.WaitForExit(); - Environment.Exit(1); - - } - - public static async void LaunchBatchFile(string path, string WindowTitle = "", bool RunAsAdmin = false) - { - Process p = new(); - p.StartInfo.FileName = "cmd.exe"; - p.StartInfo.Arguments = "/C start \"" + WindowTitle + "\" \"" + path + "\""; - p.StartInfo.UseShellExecute = true; - p.StartInfo.CreateNoWindow = true; - p.StartInfo.Verb = RunAsAdmin ? "runas" : ""; - p.Start(); - await p.WaitForExitAsync(); - } - - public bool AuthenticateToken(string token) - { - return token == ApiAuthToken - ; - } - - /// - /// Update UniGetUI - /// - /// - public async void UpdateUniGetUIIfPossible(int round = 0) - { - InfoBar? banner = null; ; - try - { - AppTools.Log("Starting update check"); - - string fileContents = ""; - - using (HttpClient client = new()) - fileContents = await client.GetStringAsync("https://www.marticliment.com/versions/unigetui.ver"); - - - if (!fileContents.Contains("///")) - throw new FormatException("The updates file does not follow the FloatVersion///Sha256Hash format"); - - float LatestVersion = float.Parse(fileContents.Split("///")[0].Replace("\n", "").Trim(), CultureInfo.InvariantCulture); - string InstallerHash = fileContents.Split("///")[1].Replace("\n", "").Trim().ToLower(); - - if (LatestVersion > CoreData.VersionNumber) - { - AppTools.Log("Updates found, downloading installer..."); - Log("Current version: " + CoreData.VersionNumber.ToString(CultureInfo.InvariantCulture)); - Log("Latest version : " + LatestVersion.ToString(CultureInfo.InvariantCulture)); - - banner = App.MainWindow.UpdatesBanner; - banner.Title= Translate("WingetUI version {0} is being downloaded.").Replace("{0}", LatestVersion.ToString(CultureInfo.InvariantCulture)); - banner.Message = Translate("This may take a minute or two"); - banner.Severity = InfoBarSeverity.Informational; - banner.IsOpen = true; - banner.IsClosable = false; - - Uri DownloadUrl = new Uri("https://github.com/marticliment/WingetUI/releases/latest/download/UniGetUI.Installer.exe"); - string InstallerPath = Path.Join(Directory.CreateTempSubdirectory().FullName, "unigetui-updater.exe"); - - using (HttpClient client = new()) - { - var result = await client.GetAsync(DownloadUrl); - using (var fs = new FileStream(InstallerPath, FileMode.CreateNew)) - await result.Content.CopyToAsync(fs); - } - - string Hash = ""; - SHA256 Sha256 = SHA256.Create(); - using (FileStream stream = File.OpenRead(InstallerPath)) - { - Hash = Convert.ToHexString(Sha256.ComputeHash(stream)).ToLower(); - } - - if (Hash == InstallerHash) - { - - banner.Title = Translate("WingetUI {0} is ready to be installed.").Replace("{0}", LatestVersion.ToString(CultureInfo.InvariantCulture)); - banner.Message = Translate("The update will be installed upon closing WingetUI"); - banner.ActionButton = new Button(); - banner.ActionButton.Content = Translate("Update now"); - banner.ActionButton.Click += (sender, args) => { Instance.App.MainWindow.HideWindow(); }; - banner.Severity = InfoBarSeverity.Success; - banner.IsOpen = true; - banner.IsClosable = true; - - if (Instance.App.MainWindow.Visible) - Log("Waiting for mainWindow to be hidden"); - - while (Instance.App.MainWindow.Visible) - await Task.Delay(100); - - Log("Hash ok, starting update"); - Process p = new Process(); - p.StartInfo.FileName = "cmd.exe"; - p.StartInfo.Arguments = $"/c start /B \"\" \"{InstallerPath}\" /silent"; - p.StartInfo.UseShellExecute = true; - p.StartInfo.CreateNoWindow = true; - p.Start(); - Instance.App.DisposeAndQuit(); - } - else - { - Log("Hash mismatch, not updating!"); - Log("Current hash : " + Hash); - Log("Expected hash: " + InstallerHash); - File.Delete(InstallerPath); - - banner.Title = Translate("The installer hash does not match the expected value."); - banner.Message = Translate("The update will not continue."); - banner.Severity = InfoBarSeverity.Error; - banner.IsOpen = true; - banner.IsClosable = true; - - await Task.Delay(7200000); // Check again in 2 hours - UpdateUniGetUIIfPossible(); - } - } - else - { - Log("UniGetUI is up to date"); - await Task.Delay(7200000); // Check again in 2 hours - UpdateUniGetUIIfPossible(); - } - } - catch (Exception e) - { - if(banner != null) - { - banner.Title = Translate("An error occurred when checking for updates: "); - banner.Message = e.Message; - banner.Severity = InfoBarSeverity.Error; - banner.IsOpen = true; - banner.IsClosable = true; - } - - Log(e); - - if (round >= 3) - return; - - await Task.Delay(600000); // Try again in 10 minutes - UpdateUniGetUIIfPossible(round + 1); - } - } - public bool IsAdministrator() - { - try - { - return (new WindowsPrincipal(WindowsIdentity.GetCurrent())) - .IsInRole(WindowsBuiltInRole.Administrator); - } - catch (Exception e) - { - Log(e); - return false; - } - } - - /// - /// Returns the size (in MB) of the file at the given URL - /// - /// a valid Uri object containing a URL to a file - /// a double representing the size in MBs, 0 if the process fails - public async Task GetFileSizeAsync(Uri url) - { - try - { -#pragma warning disable SYSLIB0014 // Type or member is obsolete - WebRequest req = WebRequest.Create(url); -#pragma warning restore SYSLIB0014 // Type or member is obsolete - req.Method = "HEAD"; - WebResponse resp = await req.GetResponseAsync(); - long ContentLength; - if (long.TryParse(resp.Headers.Get("Content-Length"), out ContentLength)) - { - return ContentLength / 1048576; - } - - } - catch (Exception e) - { - Log(e); - } - return 0; - } - } -} diff --git a/src/UniGetUI/EntryPoint.cs b/src/UniGetUI/EntryPoint.cs index 36fa1fa71..16ef33e06 100644 --- a/src/UniGetUI/EntryPoint.cs +++ b/src/UniGetUI/EntryPoint.cs @@ -1,13 +1,15 @@ using CommunityToolkit.WinUI.Notifications; using Microsoft.UI.Dispatching; using Microsoft.Windows.AppLifecycle; -using UniGetUI.Core.Data; -using UniGetUI.Core; using System; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using UniGetUI.Core; +using UniGetUI.Core.Data; +using UniGetUI.Core.Logging; +using UniGetUI.Core.Tools; namespace UniGetUI { @@ -32,10 +34,10 @@ static void Main(string[] args) } catch (Exception e) { - AppTools.ReportFatalException(e); + CoreTools.ReportFatalException(e); } } - + /// /// UniGetUI app main entry point /// @@ -45,10 +47,17 @@ static async Task AsyncMain(string[] args) { try { + var textart = @" + __ __ _ ______ __ __ ______ + / / / /___ (_) ____/__ / /_/ / / / _/ + / / / / __ \/ / / __/ _ \/ __/ / / // / +/ /_/ / / / / / /_/ / __/ /_/ /_/ // / +\____/_/ /_/_/\____/\___/\__/\____/___/ + Welcome to UniGetUI Version " + CoreData.VersionName; - AppTools.Log("Welcome to UniGetUI Version " + CoreData.VersionName); - AppTools.Log(" Version Code " + CoreData.VersionNumber.ToString()); - AppTools.Log(" "); + Logger.ImportantInfo(textart); + Logger.ImportantInfo(" "); + Logger.ImportantInfo("Version Code: " + CoreData.VersionNumber.ToString()); // WinRT single-instance fancy stuff WinRT.ComWrappersSupport.InitializeComWrappers(); @@ -68,7 +77,7 @@ static async Task AsyncMain(string[] args) } catch (Exception e) { - AppTools.ReportFatalException(e); + CoreTools.ReportFatalException(e); } } @@ -92,8 +101,9 @@ private static async Task DecideRedirection() { keyInstance.Activated += async (s, e) => { - MainApp AppInstance = MainApp.Current as MainApp; - await AppInstance.ShowMainWindowFromRedirectAsync(); + MainApp? AppInstance = MainApp.Current as MainApp; + if(AppInstance != null) + await AppInstance.ShowMainWindowFromRedirectAsync(); }; } else @@ -105,11 +115,11 @@ private static async Task DecideRedirection() } catch (Exception e) { - AppTools.Log(e); + Logger.Warn(e); return false; } } - + /// /// This method should be called when the app is being uninstalled /// It removes system links and other stuff that should be removed on uninstall @@ -121,7 +131,7 @@ private static void UninstallPreps() ToastNotificationManagerCompat.Uninstall(); } catch - { + { } } @@ -145,34 +155,34 @@ static private void WingetUIToUniGetUIMigrator() Environment.GetFolderPath(Environment.SpecialFolder.CommonStartMenu), }; - foreach (var path in BasePaths) - foreach (var old_wingetui_icon in new string[] { "WingetUI.lnk", "WingetUI .lnk", "UniGetUI (formerly WingetUI) .lnk" }) + foreach (string path in BasePaths) + foreach (string old_wingetui_icon in new string[] { "WingetUI.lnk", "WingetUI .lnk", "UniGetUI (formerly WingetUI) .lnk" }) try { - var old_file = Path.Join(path, old_wingetui_icon); - var new_file = Path.Join(path, "UniGetUI (formerly WingetUI).lnk"); - AppTools.Log(old_file); + string old_file = Path.Join(path, old_wingetui_icon); + string new_file = Path.Join(path, "UniGetUI (formerly WingetUI).lnk"); if (!File.Exists(old_file)) continue; else if (File.Exists(old_file) && File.Exists(new_file)) { - AppTools.Log("Deleting shortcut " + old_file + " since new shortcut already exists"); + Logger.Info("Deleting shortcut " + old_file + " since new shortcut already exists"); File.Delete(old_file); } else if (File.Exists(old_file) && !File.Exists(new_file)) { - AppTools.Log("Moving shortcut to " + new_file); + Logger.Info("Moving shortcut to " + new_file); File.Move(old_file, new_file); } } catch (Exception ex) { - AppTools.Log(ex); + Logger.Warn($"An error occurred while migrating the shortcut {Path.Join(path, old_wingetui_icon)}"); + Logger.Warn(ex); } - } + } catch (Exception ex) { - AppTools.Log(ex); + Logger.Error(ex); } } } diff --git a/src/UniGetUI/Interface/BackgroundApi.cs b/src/UniGetUI/Interface/BackgroundApi.cs index 9f8f9a931..37657b899 100644 --- a/src/UniGetUI/Interface/BackgroundApi.cs +++ b/src/UniGetUI/Interface/BackgroundApi.cs @@ -1,26 +1,35 @@ -using Microsoft.UI.Xaml.Data; -using UniGetUI.Core.Data; -using UniGetUI.Core; -using Nancy; +using Nancy; using Nancy.Hosting.Self; using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Security.Cryptography.X509Certificates; +using System.Drawing; using System.Text; using System.Threading.Tasks; -using Windows.ApplicationModel; -using Windows.Graphics.DirectX.Direct3D11; +using UniGetUI.Core; +using UniGetUI.Core.Data; +using UniGetUI.Core.IconEngine; +using UniGetUI.Core.Logging; +using UniGetUI.Core.SettingsEngine; +using UniGetUI.Core.Tools; +using UniGetUI.Interface.Enums; +using UniGetUI.PackageEngine.PackageClasses; namespace UniGetUI.Interface { + internal static class ApiTokenHolder + { + public static string Token = ""; + } + public class BackgroundApiRunner { private bool __running = false; + public static bool AuthenticateToken(string token) + { + return token == ApiTokenHolder.Token; + } + /// /// Run the background api and wait for it for being stopped with the Stop() method /// @@ -29,12 +38,18 @@ public async Task Start() { try { - if(AppTools.Instance.GetSettings("DisableWidgetsApi")) + + if (Settings.Get("DisableWidgetsApi")) { - AppTools.Log("Widgets API is disabled"); + Logger.Warn("Widgets API is disabled"); return; } + + ApiTokenHolder.Token = CoreTools.RandomString(64); + Settings.SetValue("CurrentSessionToken", ApiTokenHolder.Token); + Logger.Info("Api auth token: " + ApiTokenHolder.Token); + __running = true; NancyHost host; try @@ -44,23 +59,23 @@ public async Task Start() } catch { - host = new NancyHost(new Uri("http://localhost:7058/")); host.Start(); } - AppTools.Log("Api running on http://localhost:7058"); - - while(__running) + Logger.Info("Api running on http://localhost:7058"); + + while (__running) { await Task.Delay(100); } host.Stop(); - AppTools.Log("Api was shut down"); + Logger.Info("Api was shut down"); } catch (Exception e) { - AppTools.Log(e); + Logger.Error("An error occurred while initializing the API"); + Logger.Error(e); } } @@ -72,14 +87,18 @@ public void Stop() __running = false; } } - + /// /// The background api builder /// - public class BackgroundApi: NancyModule + public class BackgroundApi : NancyModule { + + static Dictionary PackageIconsPathReference = new(); + public BackgroundApi() { + // Enable CORS After.AddItemToEndOfPipeline((ctx) => { @@ -91,11 +110,13 @@ public BackgroundApi() BuildV1WidgetsApi(); } + + /// /// Build the endpoints required for the Share Interface /// public void BuildShareApi() - { + { // Show package from https://marticliment.com/unigetui/share Get("/v2/show-package", async (parameters) => { @@ -104,19 +125,17 @@ public void BuildShareApi() if (Request.Query.@pid == "" || Request.Query.@psource == "") return 400; - AppTools.Log(Request.Query.@pid); - - while (AppTools.Instance.App.MainWindow is null) await Task.Delay(100); - while (AppTools.Instance.App.MainWindow.NavigationPage is null) await Task.Delay(100); - while (AppTools.Instance.App.MainWindow.NavigationPage.DiscoverPage is null) await Task.Delay(100); + while (MainApp.Instance.MainWindow is null) await Task.Delay(100); + while (MainApp.Instance.MainWindow.NavigationPage is null) await Task.Delay(100); + while (MainApp.Instance.MainWindow.NavigationPage.DiscoverPage is null) await Task.Delay(100); - AppTools.Instance.App.MainWindow.NavigationPage.DiscoverPage.ShowSharedPackage_ThreadSafe(Request.Query.@pid.ToString(), Request.Query.@psource.ToString()); + MainApp.Instance.MainWindow.NavigationPage.DiscoverPage.ShowSharedPackage_ThreadSafe(Request.Query.@pid.ToString(), Request.Query.@psource.ToString()); return "{\"status\": \"success\"}"; } catch (Exception e) { - AppTools.Log(e); + Logger.Error(e); return 500; } }); @@ -131,40 +150,47 @@ public void BuildShareApi() /// /// Build the endpoints required for the /widgets/v1 endpoint. All of these - /// endpoints are authenticated with AppTools.Instance.AuthenticateToken + /// endpoints are authenticated with MainApp.Instance.AuthenticateToken /// public void BuildV1WidgetsApi() { // Basic version check Get("/widgets/v1/get_wingetui_version", (parameters) => { - if(!AppTools.Instance.AuthenticateToken(Request.Query.@token)) + if (!BackgroundApiRunner.AuthenticateToken(Request.Query.@token)) return 401; return CoreData.VersionNumber.ToString(); }); // Return found updates - Get("/widgets/v1/get_updates", (parameters) => + Get("/widgets/v1/get_updates", async (parameters) => { - if (!AppTools.Instance.AuthenticateToken(Request.Query.@token)) + if (!BackgroundApiRunner.AuthenticateToken(Request.Query.@token)) return 401; string packages = ""; - foreach(var package in AppTools.Instance.App.MainWindow.NavigationPage.UpdatesPage.Packages) + foreach (Package package in MainApp.Instance.MainWindow.NavigationPage.UpdatesPage.Packages) { - if(package.Tag == PackageEngine.Classes.PackageTag.OnQueue || package.Tag == PackageEngine.Classes.PackageTag.BeingProcessed) + if (package.Tag == PackageTag.OnQueue || package.Tag == PackageTag.BeingProcessed) continue; // Do not show already processed packages on queue - var icon = package.GetIconUrl().ToString(); - if(icon == "ms-appx:///Assets/Images/package_color.png") + string icon; + string icon_path = (await package.GetIconUrl()).ToString(); + if (icon_path == "ms-appx:///Assets/Images/package_color.png") + { icon = "https://marticliment.com/resources/widgets/package_color.png"; + } + else + { + PackageIconsPathReference[package.Id] = Path.Join(CoreData.UniGetUICacheDirectory_Icons, package.Manager.Name, $"{package.Id}.{icon_path.Split('.')[^1]}"); + icon = $"http://localhost:7058/widgets/v2/get_icon_for_package?packageId={package.Id}&token={ApiTokenHolder.Token}"; + } packages += $"{package.Name.Replace('|', '-')}|{package.Id}|{package.Version}|{package.NewVersion}|{package.Source}|{package.Manager.Name}|{icon}&&"; } - - if(packages.Length > 2) + + if (packages.Length > 2) packages = packages[..(packages.Length - 2)]; - AppTools.Log(packages); return packages; }); @@ -172,22 +198,23 @@ public void BuildV1WidgetsApi() // Open UniGetUI (as it was) Get("/widgets/v1/open_wingetui", (parameters) => { - if (!AppTools.Instance.AuthenticateToken(Request.Query.@token)) + if (!BackgroundApiRunner.AuthenticateToken(Request.Query.@token)) return 401; - AppTools.Instance.App.MainWindow.DispatcherQueue.TryEnqueue(() => { AppTools.Instance.App.MainWindow.Activate(); }); + MainApp.Instance.MainWindow.DispatcherQueue.TryEnqueue(() => { MainApp.Instance.MainWindow.Activate(); }); return 200; }); // Open UniGetUI with the Updates page shown Get("/widgets/v1/view_on_wingetui", (parameters) => { - if (!AppTools.Instance.AuthenticateToken(Request.Query.@token)) + if (!BackgroundApiRunner.AuthenticateToken(Request.Query.@token)) return 401; - AppTools.Instance.App.MainWindow.DispatcherQueue.TryEnqueue(() => { - AppTools.Instance.App.MainWindow.NavigationPage.UpdatesNavButton.ForceClick(); - AppTools.Instance.App.MainWindow.Activate(); + MainApp.Instance.MainWindow.DispatcherQueue.TryEnqueue(() => + { + MainApp.Instance.MainWindow.NavigationPage.UpdatesNavButton.ForceClick(); + MainApp.Instance.MainWindow.Activate(); }); return 200; }); @@ -195,15 +222,15 @@ public void BuildV1WidgetsApi() // Update a specific package given its Id Get("/widgets/v1/update_package", (parameters) => { - if (!AppTools.Instance.AuthenticateToken(Request.Query.@token)) + if (!BackgroundApiRunner.AuthenticateToken(Request.Query.@token)) return 401; - if(Request.Query.@id == "") + if (Request.Query.@id == "") return 400; - AppTools.Instance.App.MainWindow.DispatcherQueue.TryEnqueue(() => + MainApp.Instance.MainWindow.DispatcherQueue.TryEnqueue(() => { - AppTools.Instance.App.MainWindow.NavigationPage.UpdatesPage.UpdatePackageForId(Request.Query.@id); + MainApp.Instance.MainWindow.NavigationPage.UpdatesPage.UpdatePackageForId(Request.Query.@id); }); return 200; }); @@ -211,12 +238,12 @@ public void BuildV1WidgetsApi() // Update all packages Get("/widgets/v1/update_all_packages", (parameters) => { - if (!AppTools.Instance.AuthenticateToken(Request.Query.@token)) + if (!BackgroundApiRunner.AuthenticateToken(Request.Query.@token)) return 401; - AppTools.Instance.App.MainWindow.DispatcherQueue.TryEnqueue(() => + MainApp.Instance.MainWindow.DispatcherQueue.TryEnqueue(() => { - AppTools.Instance.App.MainWindow.NavigationPage.UpdatesPage.UpdateAllPackages(); + MainApp.Instance.MainWindow.NavigationPage.UpdatesPage.UpdateAllPackages(); }); return 200; }); @@ -224,18 +251,48 @@ public void BuildV1WidgetsApi() // Update all packages for a specific manager Get("/widgets/v1/update_all_packages_for_source", (parameters) => { - if (!AppTools.Instance.AuthenticateToken(Request.Query.@token)) + if (!BackgroundApiRunner.AuthenticateToken(Request.Query.@token)) return 401; if (Request.Query.@source == "") return 400; - AppTools.Instance.App.MainWindow.DispatcherQueue.TryEnqueue(() => + MainApp.Instance.MainWindow.DispatcherQueue.TryEnqueue(() => { - AppTools.Instance.App.MainWindow.NavigationPage.UpdatesPage.UpdateAllPackagesForManager(Request.Query.@source); + MainApp.Instance.MainWindow.NavigationPage.UpdatesPage.UpdateAllPackagesForManager(Request.Query.@source); }); return 200; }); + + + + // Update all packages for a specific manager + Get("/widgets/v2/get_icon_for_package", async (parameters) => + { + if (!BackgroundApiRunner.AuthenticateToken(Request.Query.@token)) + return 401; + + if (Request.Query.@packageId == "") + return 400; + + string path = ""; + if (PackageIconsPathReference.ContainsKey(Request.Query.@packageId) && File.Exists(PackageIconsPathReference[Request.Query.@packageId])) + path = PackageIconsPathReference[Request.Query.@packageId]; + else + path = Path.Join(CoreData.UniGetUIExecutableDirectory, "Assets", "Images", "package_color.png"); + + byte[] fileContents = await File.ReadAllBytesAsync(path); + return new Response() + { + ContentType = $"image/{path.Split('.')[^1]}", + Contents = (stream) => + { + stream.Write(fileContents, 0, fileContents.Length); + } + }; + + }); } } } + diff --git a/src/UniGetUI/Interface/Dialogs/AboutUniGetUI.xaml b/src/UniGetUI/Interface/Dialogs/AboutUniGetUI.xaml index 20b99ee60..0bb93a50e 100644 --- a/src/UniGetUI/Interface/Dialogs/AboutUniGetUI.xaml +++ b/src/UniGetUI/Interface/Dialogs/AboutUniGetUI.xaml @@ -17,27 +17,27 @@ - + - + - + - + - + diff --git a/src/UniGetUI/Interface/Dialogs/AboutUniGetUI.xaml.cs b/src/UniGetUI/Interface/Dialogs/AboutUniGetUI.xaml.cs index a545e7626..6739402d2 100644 --- a/src/UniGetUI/Interface/Dialogs/AboutUniGetUI.xaml.cs +++ b/src/UniGetUI/Interface/Dialogs/AboutUniGetUI.xaml.cs @@ -1,12 +1,9 @@ using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media.Animation; -using UniGetUI.Core.Data; -using UniGetUI.Interface.Pages.AboutPages; using UniGetUI.Core; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Text.Json.Nodes; +using UniGetUI.Core.Logging; +using UniGetUI.Core.Tools; +using UniGetUI.Interface.Pages.AboutPages; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. @@ -20,12 +17,16 @@ namespace UniGetUI.Interface public sealed partial class AboutUniGetUI : Page { - AppTools Tools = AppTools.Instance; int previousSelectedIndex = 0; public AboutUniGetUI() { InitializeComponent(); + SelectorBarItemPage1.Text = CoreTools.Translate("About"); + SelectorBarItemPage2.Text = CoreTools.Translate("Third-party licenses"); + SelectorBarItemPage3.Text = CoreTools.Translate("Contributors"); + SelectorBarItemPage4.Text = CoreTools.Translate("Translators"); + SelectorBarItemPage5.Text = CoreTools.Translate("Support me"); } private void SelectorBar_SelectionChanged(SelectorBar sender, SelectorBarSelectionChangedEventArgs args) @@ -53,7 +54,7 @@ private void SelectorBar_SelectionChanged(SelectorBar sender, SelectorBarSelecti break; } - var slideNavigationTransitionEffect = currentSelectedIndex - previousSelectedIndex > 0 ? SlideNavigationTransitionEffect.FromRight : SlideNavigationTransitionEffect.FromLeft; + SlideNavigationTransitionEffect slideNavigationTransitionEffect = currentSelectedIndex - previousSelectedIndex > 0 ? SlideNavigationTransitionEffect.FromRight : SlideNavigationTransitionEffect.FromLeft; ContentFrame.Navigate(pageType, null, new SlideNavigationTransitionInfo() { Effect = slideNavigationTransitionEffect }); diff --git a/src/UniGetUI/Interface/Dialogs/IgnoredUpdates.xaml.cs b/src/UniGetUI/Interface/Dialogs/IgnoredUpdates.xaml.cs index aefe6586d..5afcd411e 100644 --- a/src/UniGetUI/Interface/Dialogs/IgnoredUpdates.xaml.cs +++ b/src/UniGetUI/Interface/Dialogs/IgnoredUpdates.xaml.cs @@ -1,8 +1,5 @@ using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; -using UniGetUI.Core.Data; -using UniGetUI.PackageEngine.Classes; -using UniGetUI.Core; using System; using System.Collections.Generic; using System.IO; @@ -10,6 +7,14 @@ using System.Runtime.InteropServices.WindowsRuntime; using System.Text.Json.Nodes; using System.Threading.Tasks; +using UniGetUI.Core; +using UniGetUI.Core.Data; +using UniGetUI.PackageEngine.Classes; +using UniGetUI.Core.Logging; +using UniGetUI.Interface.Enums; +using UniGetUI.Core.Tools; +using UniGetUI.PackageEngine.ManagerClasses.Manager; +using UniGetUI.PackageEngine.PackageClasses; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. @@ -22,7 +27,6 @@ namespace UniGetUI.Interface public sealed partial class IgnoredUpdatesManager : Page { - AppTools Tools = AppTools.Instance; public IgnoredUpdatesManager() { InitializeComponent(); @@ -34,30 +38,36 @@ public async Task UpdateData() Dictionary ManagerNameReference = new(); - foreach (PackageManager Manager in Tools.App.PackageManagerList) + foreach (PackageManager Manager in MainApp.Instance.PackageManagerList) { ManagerNameReference.Add(Manager.Name.ToLower(), Manager); } - JsonObject IgnoredUpdatesJson = JsonNode.Parse(await File.ReadAllTextAsync(CoreData.IgnoredUpdatesDatabaseFile)) as JsonObject; + JsonObject? IgnoredUpdatesJson = JsonNode.Parse(await File.ReadAllTextAsync(CoreData.IgnoredUpdatesDatabaseFile)) as JsonObject; + if(IgnoredUpdatesJson == null) + { + Logger.Warn("Ignored updates JSON database was null after deserialization"); + return; + } IgnoredUpdatesList.Items.Clear(); - foreach (KeyValuePair keypair in IgnoredUpdatesJson) + foreach (KeyValuePair keypair in IgnoredUpdatesJson) { - PackageManager manager = Tools.App.Winget; // Manager by default + PackageManager manager = MainApp.Winget; // Manager by default if (ManagerNameReference.ContainsKey(keypair.Key.Split("\\")[0])) manager = ManagerNameReference[keypair.Key.Split("\\")[0]]; - IgnoredUpdatesList.Items.Add(new IgnoredPackage(keypair.Key.Split("\\")[^1], keypair.Value.ToString(), manager, IgnoredUpdatesList)); + IgnoredUpdatesList.Items.Add(new IgnoredPackage(keypair.Key.Split("\\")[^1], keypair.Value?.ToString() ?? "", manager, IgnoredUpdatesList)); } } private async void IgnoredUpdatesList_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e) { - if (IgnoredUpdatesList.SelectedItem != null) - await (IgnoredUpdatesList.SelectedItem as IgnoredPackage).RemoveFromIgnoredUpdates(); + var package = IgnoredUpdatesList.SelectedItem as IgnoredPackage; + if (package != null) + await package.RemoveFromIgnoredUpdates(); } public async void ManageIgnoredUpdates_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) @@ -78,9 +88,9 @@ public class IgnoredPackage public IgnoredPackage(string id, string version, PackageManager manager, ListView list) { Id = id; - Name = AppTools.Instance.FormatAsName(id); + Name = CoreTools.FormatAsName(id); if (version == "*") - Version = AppTools.Instance.Translate("All versions"); + Version = CoreTools.Translate("All versions"); else Version = version; Manager = manager; @@ -89,15 +99,16 @@ public IgnoredPackage(string id, string version, PackageManager manager, ListVie public async Task RemoveFromIgnoredUpdates() { string IgnoredId = $"{Manager.Properties.Name.ToLower()}\\{Id}"; - JsonObject IgnoredUpdatesJson = JsonNode.Parse(await File.ReadAllTextAsync(CoreData.IgnoredUpdatesDatabaseFile)) as JsonObject; - if (IgnoredUpdatesJson.ContainsKey(IgnoredId)) + JsonObject? IgnoredUpdatesJson = JsonNode.Parse(await File.ReadAllTextAsync(CoreData.IgnoredUpdatesDatabaseFile)) as JsonObject; + if (IgnoredUpdatesJson != null && IgnoredUpdatesJson.ContainsKey(IgnoredId)) { IgnoredUpdatesJson.Remove(IgnoredId); await File.WriteAllTextAsync(CoreData.IgnoredUpdatesDatabaseFile, IgnoredUpdatesJson.ToString()); } - foreach(var package in AppTools.Instance.App.MainWindow.NavigationPage.InstalledPage.Packages) - if(package.Id == Id && Manager == package.Manager) { + foreach (Package package in MainApp.Instance.MainWindow.NavigationPage.InstalledPage.Packages) + if (package.Id == Id && Manager == package.Manager) + { package.SetTag(PackageTag.Default); break; } diff --git a/src/UniGetUI/Interface/Dialogs/InstallOptions.xaml.cs b/src/UniGetUI/Interface/Dialogs/InstallOptions.xaml.cs index 320a9ca14..23d71058b 100644 --- a/src/UniGetUI/Interface/Dialogs/InstallOptions.xaml.cs +++ b/src/UniGetUI/Interface/Dialogs/InstallOptions.xaml.cs @@ -1,15 +1,17 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; -using UniGetUI.Core.Data; -using UniGetUI.PackageEngine.Classes; -using UniGetUI.PackageEngine.Operations; -using UniGetUI.Core; using System; using System.Linq; using System.Runtime.InteropServices; using System.Threading.Tasks; -using Windows.Storage; -using Windows.Storage.Pickers; +using UniGetUI.Core; +using UniGetUI.PackageEngine.Classes; +using UniGetUI.PackageEngine.Operations; +using UniGetUI.Core.Language; +using UniGetUI.Core.Logging; +using UniGetUI.PackageEngine.Enums; +using UniGetUI.PackageEngine.PackageClasses; +using UniGetUI.Core.Tools; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. @@ -21,7 +23,6 @@ namespace UniGetUI.Interface.Dialogs /// public sealed partial class InstallOptionsPage : Page { - AppTools Tools = AppTools.Instance; public InstallationOptions Options; public Package Package; @@ -44,7 +45,7 @@ public InstallOptionsPage(Package package, OperationType Operation, Installation HashCheckbox.IsEnabled = Operation != OperationType.Uninstall && Package.Manager.Capabilities.CanSkipIntegrityChecks; ArchitectureComboBox.IsEnabled = Operation != OperationType.Uninstall && Package.Manager.Capabilities.SupportsCustomArchitectures; - ArchitectureComboBox.Items.Add(Tools.Translate("Default")); + ArchitectureComboBox.Items.Add(CoreTools.Translate("Default")); ArchitectureComboBox.SelectedIndex = 0; @@ -58,14 +59,14 @@ public InstallOptionsPage(Package package, OperationType Operation, Installation VersionComboBox.IsEnabled = (Operation == OperationType.Install || Operation == OperationType.None) && (Package.Manager.Capabilities.SupportsCustomVersions || Package.Manager.Capabilities.SupportsPreRelease); VersionComboBox.SelectionChanged += (s, e) => - { IgnoreUpdatesCheckbox.IsChecked = !new string[] { Tools.Translate("Latest"), Tools.Translate("PreRelease"), "" }.Contains(VersionComboBox.SelectedValue.ToString()); }; - VersionComboBox.Items.Add(Tools.Translate("Latest")); + { IgnoreUpdatesCheckbox.IsChecked = !new string[] { CoreTools.Translate("Latest"), CoreTools.Translate("PreRelease"), "" }.Contains(VersionComboBox.SelectedValue.ToString()); }; + VersionComboBox.Items.Add(CoreTools.Translate("Latest")); VersionComboBox.SelectedIndex = 0; if (package.Manager.Capabilities.SupportsPreRelease) { - VersionComboBox.Items.Add(Tools.Translate("PreRelease")); + VersionComboBox.Items.Add(CoreTools.Translate("PreRelease")); if (Options.PreRelease) - VersionComboBox.SelectedValue = Tools.Translate("PreRelease"); + VersionComboBox.SelectedValue = CoreTools.Translate("PreRelease"); } if (Package.Manager.Capabilities.SupportsCustomVersions) @@ -74,14 +75,14 @@ public InstallOptionsPage(Package package, OperationType Operation, Installation VersionProgress.Visibility = Visibility.Collapsed; ScopeCombo.IsEnabled = Package.Manager.Capabilities.SupportsCustomScopes; - ScopeCombo.Items.Add(Tools.Translate("Default")); + ScopeCombo.Items.Add(CoreTools.Translate("Default")); ScopeCombo.SelectedIndex = 0; if (package.Manager.Capabilities.SupportsCustomScopes) { - ScopeCombo.Items.Add(Tools.Translate(CommonTranslations.ScopeNames[PackageScope.Local])); + ScopeCombo.Items.Add(CoreTools.Translate(CommonTranslations.ScopeNames[PackageScope.Local])); if (Options.InstallationScope == PackageScope.Local) ScopeCombo.SelectedValue = CommonTranslations.ScopeNames[PackageScope.Local]; - ScopeCombo.Items.Add(Tools.Translate(CommonTranslations.ScopeNames[PackageScope.Global])); + ScopeCombo.Items.Add(CoreTools.Translate(CommonTranslations.ScopeNames[PackageScope.Global])); if (Options.InstallationScope == PackageScope.Global) ScopeCombo.SelectedValue = CommonTranslations.ScopeNames[PackageScope.Global]; } @@ -120,30 +121,30 @@ private async Task LoadVersions() public async Task GetUpdatedOptions() { - Options.RunAsAdministrator = AdminCheckBox.IsChecked.Value; - Options.InteractiveInstallation = InteractiveCheckBox.IsChecked.Value; - Options.SkipHashCheck = HashCheckbox.IsChecked.Value; + Options.RunAsAdministrator = AdminCheckBox?.IsChecked ?? false; + Options.InteractiveInstallation = InteractiveCheckBox?.IsChecked ?? false; + Options.SkipHashCheck = HashCheckbox?.IsChecked ?? false; - if (CommonTranslations.InvertedArchNames.ContainsKey(ArchitectureComboBox.SelectedValue.ToString())) - Options.Architecture = CommonTranslations.InvertedArchNames[ArchitectureComboBox.SelectedValue.ToString()]; + if (CommonTranslations.InvertedArchNames.ContainsKey(ArchitectureComboBox.SelectedValue.ToString() ?? "")) + Options.Architecture = CommonTranslations.InvertedArchNames[ArchitectureComboBox.SelectedValue.ToString() ?? ""]; else Options.Architecture = null; - if (CommonTranslations.InvertedScopeNames.ContainsKey(ScopeCombo.SelectedValue.ToString())) - Options.InstallationScope = CommonTranslations.InvertedScopeNames[ScopeCombo.SelectedValue.ToString()]; + if (CommonTranslations.InvertedScopeNames.ContainsKey(ScopeCombo.SelectedValue.ToString() ?? "")) + Options.InstallationScope = CommonTranslations.InvertedScopeNames[ScopeCombo.SelectedValue.ToString() ?? ""]; else Options.InstallationScope = null; Options.CustomInstallLocation = CustomInstallLocation.Text; Options.CustomParameters = CustomParameters.Text.Split(' ').ToList(); - Options.PreRelease = VersionComboBox.SelectedValue.ToString() == Tools.Translate("PreRelease"); + Options.PreRelease = VersionComboBox.SelectedValue.ToString() == CoreTools.Translate("PreRelease"); - if (VersionComboBox.SelectedValue.ToString() != Tools.Translate("PreRelease") && VersionComboBox.SelectedValue.ToString() != Tools.Translate("Latest")) - Options.Version = VersionComboBox.SelectedValue.ToString(); + if (VersionComboBox.SelectedValue.ToString() != CoreTools.Translate("PreRelease") && VersionComboBox.SelectedValue.ToString() != CoreTools.Translate("Latest")) + Options.Version = VersionComboBox.SelectedValue.ToString() ?? ""; else Options.Version = ""; - if (IgnoreUpdatesCheckbox.IsChecked.Value) + if (IgnoreUpdatesCheckbox?.IsChecked ?? false) await Package.AddToIgnoredUpdatesAsync(version: "*"); else { @@ -161,7 +162,7 @@ public async void SaveToDisk() private void SelectDir_Click(object sender, RoutedEventArgs e) { - var openPicker = new UniGetUI.ExternalLibraries.Pickers.FolderPicker(Tools.App.MainWindow.GetWindowHandle()); + ExternalLibraries.Pickers.FolderPicker openPicker = new(MainApp.Instance.MainWindow.GetWindowHandle()); string folder = openPicker.Show(); if (folder != String.Empty) CustomInstallLocation.Text = folder; diff --git a/src/UniGetUI/Interface/Dialogs/ReleaseNotes.xaml.cs b/src/UniGetUI/Interface/Dialogs/ReleaseNotes.xaml.cs index dad702f82..ce58e04be 100644 --- a/src/UniGetUI/Interface/Dialogs/ReleaseNotes.xaml.cs +++ b/src/UniGetUI/Interface/Dialogs/ReleaseNotes.xaml.cs @@ -1,8 +1,9 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; -using UniGetUI.Core.Data; using System; using System.Threading.Tasks; +using UniGetUI.Core.Logging; +using UniGetUI.Core.Data; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. diff --git a/src/UniGetUI/Interface/MainView.xaml b/src/UniGetUI/Interface/MainView.xaml index b643b73d4..799bc014b 100644 --- a/src/UniGetUI/Interface/MainView.xaml +++ b/src/UniGetUI/Interface/MainView.xaml @@ -17,7 +17,7 @@ - + diff --git a/src/UniGetUI/Interface/MainView.xaml.cs b/src/UniGetUI/Interface/MainView.xaml.cs index 1d09f2921..411a8aaa3 100644 --- a/src/UniGetUI/Interface/MainView.xaml.cs +++ b/src/UniGetUI/Interface/MainView.xaml.cs @@ -4,19 +4,24 @@ using Microsoft.UI.Xaml.Controls.Primitives; using Microsoft.UI.Xaml.Documents; using Microsoft.UI.Xaml.Media.Imaging; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using UniGetUI.Core; using UniGetUI.Core.Data; using UniGetUI.Interface.Dialogs; using UniGetUI.Interface.Pages; using UniGetUI.Interface.Widgets; using UniGetUI.PackageEngine.Classes; using UniGetUI.PackageEngine.Operations; -using UniGetUI.Core; -using System; -using System.Collections.Generic; -using System.ComponentModel.Design; -using System.Threading.Tasks; -using Windows.Graphics.DirectX.Direct3D11; +using UniGetUI.Core.Logging; using Windows.UI.Core; +using UniGetUI.Core.SettingsEngine; +using UniGetUI.PackageEngine.PackageClasses; +using System.Reflection.Emit; +using UniGetUI.PackageEngine.Enums; +using UniGetUI.Core.Tools; +using UniGetUI.Interface.SoftwarePages; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. @@ -25,15 +30,14 @@ namespace UniGetUI.Interface { public sealed partial class MainView : UserControl { - public AppTools Tools = AppTools.Instance; public SettingsInterface SettingsPage; - public DiscoverPackagesPage DiscoverPage; - public SoftwareUpdatesPage UpdatesPage; - public InstalledPackagesPage InstalledPage; - public HelpDialog HelpPage; + public NewDiscoverSoftwarePage DiscoverPage; + public NewSoftwareUpdatesPage UpdatesPage; + public NewInstalledPackagesPage InstalledPage; + public HelpDialog? HelpPage; public PackageBundlePage BundlesPage; - public Page OldPage; - public Page CurrentPage; + public Page? OldPage; + public Page? CurrentPage; public InfoBadge UpdatesBadge; public InfoBadge BundleBadge; public StackPanel OperationStackPanel; @@ -48,12 +52,13 @@ public MainView() UpdatesBadge = __updates_count_badge; BundleBadge = __bundle_count_badge; OperationStackPanel = __operations_list_stackpanel; + DiscoverPage = new NewDiscoverSoftwarePage(); + UpdatesPage = new NewSoftwareUpdatesPage(); + UpdatesPage.ExternalCountBadge = UpdatesBadge; + InstalledPage = new NewInstalledPackagesPage(); + BundlesPage = new PackageBundlePage(); SettingsPage = new SettingsInterface(); - DiscoverPage = new DiscoverPackagesPage(); - UpdatesPage = new SoftwareUpdatesPage(); - InstalledPage = new InstalledPackagesPage(); AboutPage = new AboutUniGetUI(); - BundlesPage = new PackageBundlePage(); IgnoredUpdatesPage = new IgnoredUpdatesManager(); int i = 0; @@ -73,19 +78,19 @@ public MainView() DiscoverNavButton.ForceClick(); - if (Tools.IsAdministrator() && !Tools.GetSettings("AlreadyWarnedAboutAdmin")) + if (CoreTools.IsAdministrator() && !Settings.Get("AlreadyWarnedAboutAdmin")) { - Tools.SetSettings("AlreadyWarnedAboutAdmin", true); + Settings.Set("AlreadyWarnedAboutAdmin", true); WarnAboutAdminRights(); } - if (!Tools.GetSettings("AlreadyWarnedAboutNameChange")) + if (!Settings.Get("AlreadyWarnedAboutNameChange")) { - Tools.SetSettings("AlreadyWarnedAboutNameChange", true); + Settings.Set("AlreadyWarnedAboutNameChange", true); WarnAboutNewName(); } - var NextPageReference = new Dictionary + Dictionary NextPageReference = new() { { DiscoverPage, UpdatesNavButton }, { UpdatesPage, InstalledNavButton }, @@ -94,7 +99,7 @@ public MainView() { SettingsPage, DiscoverNavButton }, }; - var PreviousTabReference = new Dictionary + Dictionary PreviousTabReference = new() { { DiscoverPage, SettingsNavButton }, { UpdatesPage, DiscoverNavButton }, @@ -107,19 +112,20 @@ public MainView() { if (e.Key == Windows.System.VirtualKey.Tab && InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down)) { - if (!InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Shift).HasFlag(CoreVirtualKeyStates.Down)) - { - if (NextPageReference.ContainsKey(CurrentPage)) - NextPageReference[CurrentPage].ForceClick(); - else - DiscoverNavButton.ForceClick(); - } - else - { - if (NextPageReference.ContainsKey(CurrentPage)) - PreviousTabReference[CurrentPage].ForceClick(); + if(CurrentPage != null) + if (!InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Shift).HasFlag(CoreVirtualKeyStates.Down)) + { + if (NextPageReference.ContainsKey(CurrentPage)) + NextPageReference[CurrentPage].ForceClick(); + else + DiscoverNavButton.ForceClick(); + } else - DiscoverNavButton.ForceClick(); + { + if (NextPageReference.ContainsKey(CurrentPage)) + PreviousTabReference[CurrentPage].ForceClick(); + else + DiscoverNavButton.ForceClick(); } } }; @@ -148,17 +154,17 @@ private void BundlesNavButton_Click(object sender, NavButton.NavButtonEventArgs private void MoreNavButton_Click(object sender, NavButton.NavButtonEventArgs e) { - foreach (NavButton button in Tools.App.MainWindow.NavButtonList) + foreach (NavButton button in MainApp.Instance.MainWindow.NavButtonList) button.ToggleButton.IsChecked = false; MoreNavButton.ToggleButton.IsChecked = true; - (VersionMenuItem as MenuFlyoutItem).Text = Tools.Translate("WingetUI Version {0}").Replace("{0}", CoreData.VersionName); + (VersionMenuItem as MenuFlyoutItem).Text = CoreTools.Translate("WingetUI Version {0}").Replace("{0}", CoreData.VersionName); MoreNavButtonMenu.ShowAt(MoreNavButton, new FlyoutShowOptions() { ShowMode = FlyoutShowMode.Standard }); MoreNavButtonMenu.Closed += (s, e) => { - foreach (NavButton button in Tools.App.MainWindow.NavButtonList) - button.ToggleButton.IsChecked = (button == PageButtonReference[CurrentPage]); + foreach (NavButton button in MainApp.Instance.MainWindow.NavButtonList) + button.ToggleButton.IsChecked = (button == PageButtonReference[CurrentPage ?? DiscoverPage]); }; } @@ -169,40 +175,40 @@ private void SettingsNavButton_Click(object sender, NavButton.NavButtonEventArgs private async void AboutNavButton_Click(object sender, NavButton.NavButtonEventArgs e) { - ContentDialog AboutDialog = new(); + ContentDialog? AboutDialog = new(); AboutDialog.Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style; AboutDialog.XamlRoot = XamlRoot; AboutDialog.Resources["ContentDialogMaxWidth"] = 1200; AboutDialog.Resources["ContentDialogMaxHeight"] = 1000; AboutDialog.Content = AboutPage; - AboutDialog.PrimaryButtonText = Tools.Translate("Close"); - foreach (NavButton button in Tools.App.MainWindow.NavButtonList) + AboutDialog.PrimaryButtonText = CoreTools.Translate("Close"); + foreach (NavButton button in MainApp.Instance.MainWindow.NavButtonList) button.ToggleButton.IsChecked = false; - await Tools.App.MainWindow.ShowDialogAsync(AboutDialog); + await MainApp.Instance.MainWindow.ShowDialogAsync(AboutDialog); AboutDialog.Content = null; - foreach (NavButton button in Tools.App.MainWindow.NavButtonList) - button.ToggleButton.IsChecked = (button == PageButtonReference[CurrentPage]); + foreach (NavButton button in MainApp.Instance.MainWindow.NavButtonList) + button.ToggleButton.IsChecked = (button == PageButtonReference[CurrentPage ?? DiscoverPage]); AboutDialog = null; } public async Task ManageIgnoredUpdatesDialog() { - ContentDialog UpdatesDialog = new(); + ContentDialog? UpdatesDialog = new(); UpdatesDialog.Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style; UpdatesDialog.XamlRoot = XamlRoot; UpdatesDialog.Resources["ContentDialogMaxWidth"] = 1200; UpdatesDialog.Resources["ContentDialogMaxHeight"] = 1000; - UpdatesDialog.SecondaryButtonText = Tools.Translate("Close"); - UpdatesDialog.PrimaryButtonText = Tools.Translate("Reset"); + UpdatesDialog.SecondaryButtonText = CoreTools.Translate("Close"); + UpdatesDialog.PrimaryButtonText = CoreTools.Translate("Reset"); UpdatesDialog.DefaultButton = ContentDialogButton.Secondary; - UpdatesDialog.Title = Tools.Translate("Manage ignored updates"); + UpdatesDialog.Title = CoreTools.Translate("Manage ignored updates"); UpdatesDialog.PrimaryButtonClick += IgnoredUpdatesPage.ManageIgnoredUpdates_SecondaryButtonClick; UpdatesDialog.Content = IgnoredUpdatesPage; _ = IgnoredUpdatesPage.UpdateData(); - await Tools.App.MainWindow.ShowDialogAsync(UpdatesDialog); + await MainApp.Instance.MainWindow.ShowDialogAsync(UpdatesDialog); UpdatesDialog.Content = null; UpdatesDialog = null; @@ -213,19 +219,19 @@ public async void WarnAboutAdminRights() ContentDialog AdminDialog = new(); AdminDialog.Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style; - while(this.XamlRoot == null) + while (XamlRoot == null) { await Task.Delay(100); } - AdminDialog.XamlRoot = this.XamlRoot; - AdminDialog.PrimaryButtonText = Tools.Translate("I understand"); + AdminDialog.XamlRoot = XamlRoot; + AdminDialog.PrimaryButtonText = CoreTools.Translate("I understand"); AdminDialog.DefaultButton = ContentDialogButton.Primary; - AdminDialog.Title = Tools.Translate("Administrator privileges"); + AdminDialog.Title = CoreTools.Translate("Administrator privileges"); AdminDialog.SecondaryButtonClick += IgnoredUpdatesPage.ManageIgnoredUpdates_SecondaryButtonClick; - AdminDialog.Content = Tools.Translate("WingetUI has been ran as administrator, which is not recommended. When running WingetUI as administrator, EVERY operation launched from WingetUI will have administrator privileges. You can still use the program, but we highly recommend not running WingetUI with administrator privileges."); + AdminDialog.Content = CoreTools.Translate("WingetUI has been ran as administrator, which is not recommended. When running WingetUI as administrator, EVERY operation launched from WingetUI will have administrator privileges. You can still use the program, but we highly recommend not running WingetUI with administrator privileges."); - await Tools.App.MainWindow.ShowDialogAsync(AdminDialog); + await MainApp.Instance.MainWindow.ShowDialogAsync(AdminDialog); } public async void WarnAboutNewName() @@ -233,38 +239,38 @@ public async void WarnAboutNewName() ContentDialog AdminDialog = new(); AdminDialog.Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style; - while (this.XamlRoot == null) + while (XamlRoot == null) { await Task.Delay(100); } string NEW_NAME = "UnigetUI"; - AdminDialog.XamlRoot = this.XamlRoot; - AdminDialog.PrimaryButtonText = Tools.Translate("I understand"); + AdminDialog.XamlRoot = XamlRoot; + AdminDialog.PrimaryButtonText = CoreTools.Translate("I understand"); AdminDialog.DefaultButton = ContentDialogButton.Primary; AdminDialog.SecondaryButtonClick += IgnoredUpdatesPage.ManageIgnoredUpdates_SecondaryButtonClick; - StackPanel p = new StackPanel() { Spacing = 16 }; + StackPanel p = new() { Spacing = 16 }; AdminDialog.Content = p; p.Children.Add(new Image() { Source = new BitmapImage() { UriSource = new Uri("ms-appx:///Assets/Images/icon.png") }, Height = 96 }); - var par = new Paragraph(); - par.Inlines.Add(new Run() { Text = Tools.Translate("WingetUI will become {newname} soon!").Replace("{newname}", NEW_NAME), FontSize = 24, FontWeight = new Windows.UI.Text.FontWeight(700), FontFamily = new Microsoft.UI.Xaml.Media.FontFamily("Segoe UI Variable Display Bold") }); + Paragraph par = new(); + par.Inlines.Add(new Run() { Text = CoreTools.Translate("WingetUI will become {newname} soon!").Replace("{newname}", NEW_NAME), FontSize = 24, FontWeight = new Windows.UI.Text.FontWeight(700), FontFamily = new Microsoft.UI.Xaml.Media.FontFamily("Segoe UI Variable Display Bold") }); par.Inlines.Add(new LineBreak()); par.Inlines.Add(new LineBreak()); - par.Inlines.Add(new Run() { Text = Tools.Translate("WingetUI will soon be named {newname}. This will not represent any change in the application. I (the developer) will continue the development of this project as I am doing right now, but under a different name.").Replace("{newname}", NEW_NAME) }); + par.Inlines.Add(new Run() { Text = CoreTools.Translate("WingetUI will soon be named {newname}. This will not represent any change in the application. I (the developer) will continue the development of this project as I am doing right now, but under a different name.").Replace("{newname}", NEW_NAME) }); par.Inlines.Add(new LineBreak()); par.Inlines.Add(new LineBreak()); - par.Inlines.Add(new Run() { Text = Tools.Translate("WingetUI is being renamed in order to emphasize the difference between WingetUI (the interface you are using right now) and Winget (a package manager developed by Microsoft with which I am not related)"), FontSize = 12, FontStyle = Windows.UI.Text.FontStyle.Italic }); + par.Inlines.Add(new Run() { Text = CoreTools.Translate("WingetUI is being renamed in order to emphasize the difference between WingetUI (the interface you are using right now) and Winget (a package manager developed by Microsoft with which I am not related)"), FontSize = 12, FontStyle = Windows.UI.Text.FontStyle.Italic }); par.Inlines.Add(new LineBreak()); - par.Inlines.Add(new Run() { Text = Tools.Translate("While Winget can be used within WingetUI, WingetUI can be used with other package managers, which can be confusing. In the past, WingetUI was designed to work only with Winget, but this is not true anymore, and therefore WingetUI does not represent what this project aims to become."), FontSize = 12, FontStyle = Windows.UI.Text.FontStyle.Italic }); + par.Inlines.Add(new Run() { Text = CoreTools.Translate("While Winget can be used within WingetUI, WingetUI can be used with other package managers, which can be confusing. In the past, WingetUI was designed to work only with Winget, but this is not true anymore, and therefore WingetUI does not represent what this project aims to become."), FontSize = 12, FontStyle = Windows.UI.Text.FontStyle.Italic }); - var text = new RichTextBlock(); + RichTextBlock text = new(); text.Blocks.Add(par); p.Children.Add(text); - await Tools.App.MainWindow.ShowDialogAsync(AdminDialog); + await MainApp.Instance.MainWindow.ShowDialogAsync(AdminDialog); } @@ -272,25 +278,25 @@ public async Task ShowInstallationSettingsForPackageAndContinue(Package pa { InstallOptionsPage OptionsPage = new(package, Operation); - ContentDialog OptionsDialog = new(); + ContentDialog? OptionsDialog = new(); OptionsDialog.Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style; OptionsDialog.XamlRoot = XamlRoot; OptionsDialog.Resources["ContentDialogMaxWidth"] = 1200; OptionsDialog.Resources["ContentDialogMaxHeight"] = 1000; if (Operation == OperationType.Install) - OptionsDialog.SecondaryButtonText = Tools.Translate("Install"); + OptionsDialog.SecondaryButtonText = CoreTools.Translate("Install"); else if (Operation == OperationType.Update) - OptionsDialog.SecondaryButtonText = Tools.Translate("Update"); + OptionsDialog.SecondaryButtonText = CoreTools.Translate("Update"); else if (Operation == OperationType.Uninstall) - OptionsDialog.SecondaryButtonText = Tools.Translate("Uninstall"); + OptionsDialog.SecondaryButtonText = CoreTools.Translate("Uninstall"); else OptionsDialog.SecondaryButtonText = ""; - OptionsDialog.PrimaryButtonText = Tools.Translate("Save and close"); + OptionsDialog.PrimaryButtonText = CoreTools.Translate("Save and close"); OptionsDialog.DefaultButton = ContentDialogButton.Secondary; - OptionsDialog.Title = Tools.Translate("{0} installation options").Replace("{0}", package.Name); + OptionsDialog.Title = CoreTools.Translate("{0} installation options").Replace("{0}", package.Name); OptionsDialog.Content = OptionsPage; - ContentDialogResult result = await Tools.App.MainWindow.ShowDialogAsync(OptionsDialog); + ContentDialogResult result = await MainApp.Instance.MainWindow.ShowDialogAsync(OptionsDialog); OptionsPage.SaveToDisk(); OptionsDialog.Content = null; @@ -304,17 +310,17 @@ public async Task UpdateInstallationSettings(Package packag { InstallOptionsPage OptionsPage = new(package, options); - ContentDialog OptionsDialog = new(); + ContentDialog? OptionsDialog = new(); OptionsDialog.Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style; OptionsDialog.XamlRoot = XamlRoot; OptionsDialog.Resources["ContentDialogMaxWidth"] = 1200; OptionsDialog.Resources["ContentDialogMaxHeight"] = 1000; OptionsDialog.SecondaryButtonText = ""; - OptionsDialog.PrimaryButtonText = Tools.Translate("Save and close"); + OptionsDialog.PrimaryButtonText = CoreTools.Translate("Save and close"); OptionsDialog.DefaultButton = ContentDialogButton.Secondary; - OptionsDialog.Title = Tools.Translate("{0} installation options").Replace("{0}", package.Name); + OptionsDialog.Title = CoreTools.Translate("{0} installation options").Replace("{0}", package.Name); OptionsDialog.Content = OptionsPage; - await Tools.App.MainWindow.ShowDialogAsync(OptionsDialog); + await MainApp.Instance.MainWindow.ShowDialogAsync(OptionsDialog); OptionsDialog.Content = null; OptionsDialog = null; @@ -335,7 +341,7 @@ private void NavigateToPage(Page TargetPage) Grid.SetRow(TargetPage, 0); MainContentPresenterGrid.Children.Add(TargetPage); } - foreach (NavButton button in Tools.App.MainWindow.NavButtonList) + foreach (NavButton button in MainApp.Instance.MainWindow.NavButtonList) { button.ToggleButton.IsChecked = (button == PageButtonReference[TargetPage]); @@ -359,14 +365,14 @@ private void NavigateToPage(Page TargetPage) private async void ReleaseNotesMenu_Click(object sender, RoutedEventArgs e) { - ContentDialog NotesDialog = new(); + ContentDialog? NotesDialog = new(); NotesDialog.Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style; NotesDialog.XamlRoot = XamlRoot; NotesDialog.Resources["ContentDialogMaxWidth"] = 12000; NotesDialog.Resources["ContentDialogMaxHeight"] = 10000; - NotesDialog.CloseButtonText = Tools.Translate("Close"); - NotesDialog.Title = Tools.Translate("Release notes"); - ReleaseNotes notes = new(); + NotesDialog.CloseButtonText = CoreTools.Translate("Close"); + NotesDialog.Title = CoreTools.Translate("Release notes"); + ReleaseNotes? notes = new(); NotesDialog.Content = notes; NotesDialog.SizeChanged += (s, e) => { @@ -374,7 +380,7 @@ private async void ReleaseNotesMenu_Click(object sender, RoutedEventArgs e) notes.MinHeight = ActualHeight - 200; }; - await Tools.App.MainWindow.ShowDialogAsync(NotesDialog); + await MainApp.Instance.MainWindow.ShowDialogAsync(NotesDialog); notes.Dispose(); notes = null; @@ -383,9 +389,9 @@ private async void ReleaseNotesMenu_Click(object sender, RoutedEventArgs e) public async Task ShowPackageDetails(Package package, OperationType ActionOperation) { - PackageDetailsPage DetailsPage = new(package, ActionOperation); + PackageDetailsPage? DetailsPage = new(package, ActionOperation); - ContentDialog DetailsDialog = new(); + ContentDialog? DetailsDialog = new(); DetailsDialog.Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style; DetailsDialog.XamlRoot = XamlRoot; DetailsDialog.Resources["ContentDialogMaxWidth"] = 8000; @@ -402,7 +408,7 @@ public async Task ShowPackageDetails(Package package, OperationType ActionOperat DetailsPage.Close += (s, e) => { DetailsDialog.Hide(); }; - await Tools.App.MainWindow.ShowDialogAsync(DetailsDialog); + await MainApp.Instance.MainWindow.ShowDialogAsync(DetailsDialog); DetailsDialog.Content = null; DetailsDialog = null; @@ -411,17 +417,17 @@ public async Task ShowPackageDetails(Package package, OperationType ActionOperat private void OperationHistoryMenu_Click(object sender, RoutedEventArgs e) { - NavigateToPage(new LogPage(LogType.OperationHistory)); + NavigateToPage(new Logger_LogPage(Logger_LogType.OperationHistory)); } private void ManagerLogsMenu_Click(object sender, RoutedEventArgs e) { - NavigateToPage(new LogPage(LogType.ManagerLogs)); + NavigateToPage(new Logger_LogPage(Logger_LogType.ManagerLogs)); } public void UniGetUILogs_Click(object sender, RoutedEventArgs e) { - NavigateToPage(new LogPage(LogType.UniGetUILog)); + NavigateToPage(new Logger_LogPage(Logger_LogType.UniGetUILog)); } @@ -431,14 +437,14 @@ private void HelpMenu_Click(object sender, RoutedEventArgs e) } public void ShowHelp() { - if(HelpPage == null) + if (HelpPage == null) HelpPage = new HelpDialog(); NavigateToPage(HelpPage); } private void QuitUniGetUI_Click(object sender, RoutedEventArgs e) { - Tools.App.DisposeAndQuit(); + MainApp.Instance.DisposeAndQuit(); } } } diff --git a/src/UniGetUI/Interface/MainWindow.xaml.cs b/src/UniGetUI/Interface/MainWindow.xaml.cs index 62333e985..7a491ca9b 100644 --- a/src/UniGetUI/Interface/MainWindow.xaml.cs +++ b/src/UniGetUI/Interface/MainWindow.xaml.cs @@ -7,20 +7,21 @@ using Microsoft.UI.Xaml.Input; using Microsoft.UI.Xaml.Media.Imaging; using Microsoft.Win32; -using UniGetUI.Core.Data; -using UniGetUI.Interface; -using UniGetUI.Interface.Widgets; -using UniGetUI.PackageEngine.Classes; -using UniGetUI.Core; using System; using System.Collections.Generic; -using System.ComponentModel.Design; using System.IO; using System.Reflection; using System.Threading.Tasks; +using UniGetUI.Core; +using UniGetUI.Core.Data; +using UniGetUI.Interface.Widgets; +using UniGetUI.PackageEngine.Classes; using Windows.ApplicationModel.DataTransfer; -using Windows.ApplicationModel.VoiceCommands; +using UniGetUI.Core.Logging; using Windows.Foundation.Collections; +using UniGetUI.Core.SettingsEngine; +using UniGetUI.PackageEngine.PackageClasses; +using UniGetUI.Core.Tools; namespace UniGetUI.Interface @@ -38,13 +39,12 @@ IntPtr GetForWindow([System.Runtime.InteropServices.In] IntPtr appWindow, void ShowShareUIForWindow(IntPtr appWindow); } - TaskbarIcon TrayIcon; + TaskbarIcon? TrayIcon; private bool RecentlyActivated = false; static readonly Guid _dtm_iid = new(0xa5caee9b, 0x8708, 0x49d1, 0x8d, 0x36, 0x67, 0xd2, 0x5a, 0x8d, 0xa0, 0x0c); - AppTools Tools = AppTools.Instance; public MainView NavigationPage; public Grid ContentRoot; public bool BlockLoading = false; @@ -55,6 +55,7 @@ IntPtr GetForWindow([System.Runtime.InteropServices.In] IntPtr appWindow, public List DialogQueue = new(); public List NavButtonList = new(); +#pragma warning disable CS8618 public MainWindow() { InitializeComponent(); @@ -65,18 +66,18 @@ public MainWindow() ApplyTheme(); AppWindow.SetIcon(Path.Join(CoreData.UniGetUIExecutableDirectory, "Assets", "Images", "icon.ico")); - if (Tools.IsAdministrator()) + if (CoreTools.IsAdministrator()) { - Title = "UniGetUI " + Tools.Translate("[RAN AS ADMINISTRATOR]"); + Title = "UniGetUI " + CoreTools.Translate("[RAN AS ADMINISTRATOR]"); AppTitle.Text = Title; } LoadingSthDalog = new ContentDialog(); LoadingSthDalog.Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style; - LoadingSthDalog.Title = Tools.Translate("Please wait"); + LoadingSthDalog.Title = CoreTools.Translate("Please wait"); LoadingSthDalog.Content = new ProgressBar() { IsIndeterminate = true, Width = 300 }; } - +#pragma warning restore CS8618 public void HandleNotificationActivation(ToastArguments args, ValueSet input) { if (args.Contains("action") && args["action"] == "updateAll") @@ -86,17 +87,17 @@ public void HandleNotificationActivation(ToastArguments args, ValueSet input) NavigationPage.UpdatesNavButton.ForceClick(); if (NavigationPage != null && NavigationPage.InstalledPage != null) - NavigationPage.InstalledPage.ReloadPackages(); + _ = NavigationPage.InstalledPage.LoadPackages(); Activate(); } else { if (NavigationPage != null && NavigationPage.InstalledPage != null) - NavigationPage.InstalledPage.ReloadPackages(); + _ = NavigationPage.InstalledPage.LoadPackages(); Activate(); } - AppTools.Log("Notification activated: " + args.ToString() + " " + input.ToString()); + Logger.Debug("Notification activated: " + args.ToString() + " " + input.ToString()); } @@ -107,7 +108,7 @@ public void HandleNotificationActivation(ToastArguments args, ValueSet input) /// public async void HandleClosingEvent(AppWindow sender, AppWindowClosingEventArgs args) { - if (!Tools.GetSettings("DisableSystemTray")) + if (!Settings.Get("DisableSystemTray")) { args.Cancel = true; RecentlyActivated = false; @@ -118,26 +119,27 @@ public async void HandleClosingEvent(AppWindow sender, AppWindowClosingEventArgs catch (Exception ex) { // Somewhere, Sometimes, MS Window Efficiency mode just crashes - AppTools.Log(ex); + Logger.Debug("Windows efficiency mode API crashed, but this was expected"); + Logger.Debug(ex); this.Hide(enableEfficiencyMode: false); } } else { - if (Tools.OperationQueue.Count > 0) + if (MainApp.Instance.OperationQueue.Count > 0) { args.Cancel = true; - ContentDialog d = new ContentDialog(); + ContentDialog d = new(); d.XamlRoot = NavigationPage.XamlRoot; - d.Title = Tools.Translate("Operation in progress"); - d.Content = Tools.Translate("There are ongoing operations. Quitting WingetUI may cause them to fail. Do you want to continue?"); - d.PrimaryButtonText = Tools.Translate("Quit"); - d.SecondaryButtonText = Tools.Translate("Cancel"); + d.Title = CoreTools.Translate("Operation in progress"); + d.Content = CoreTools.Translate("There are ongoing operations. Quitting WingetUI may cause them to fail. Do you want to continue?"); + d.PrimaryButtonText = CoreTools.Translate("Quit"); + d.SecondaryButtonText = CoreTools.Translate("Cancel"); d.DefaultButton = ContentDialogButton.Secondary; - var result = await ShowDialogAsync(d); + ContentDialogResult result = await ShowDialogAsync(d); if (result == ContentDialogResult.Primary) - Tools.App.DisposeAndQuit(); + MainApp.Instance.DisposeAndQuit(); } } } @@ -145,7 +147,7 @@ public async void HandleClosingEvent(AppWindow sender, AppWindowClosingEventArgs public new void Activate() { if (NavigationPage != null && NavigationPage.InstalledPage != null) - NavigationPage.InstalledPage.ReloadPackages(); + _ = NavigationPage.InstalledPage.LoadPackages(); (this as Window).Activate(); } @@ -178,7 +180,7 @@ private void LoadTrayMenu() foreach (KeyValuePair item in Labels) { - item.Key.Label = Tools.Translate(item.Value); + item.Key.Label = CoreTools.Translate(item.Value); } Dictionary Icons = new() @@ -199,9 +201,9 @@ private void LoadTrayMenu() DiscoverPackages.ExecuteRequested += (s, e) => { NavigationPage.DiscoverNavButton.ForceClick(); Activate(); }; AvailableUpdates.ExecuteRequested += (s, e) => { NavigationPage.UpdatesNavButton.ForceClick(); Activate(); }; InstalledPackages.ExecuteRequested += (s, e) => { NavigationPage.InstalledNavButton.ForceClick(); Activate(); }; - AboutUniGetUI.Label = Tools.Translate("WingetUI Version {0}").Replace("{0}", CoreData.VersionName); + AboutUniGetUI.Label = CoreTools.Translate("WingetUI Version {0}").Replace("{0}", CoreData.VersionName); ShowUniGetUI.ExecuteRequested += (s, e) => { Activate(); }; - QuitUniGetUI.ExecuteRequested += (s, e) => { Tools.App.DisposeAndQuit(); }; + QuitUniGetUI.ExecuteRequested += (s, e) => { MainApp.Instance.DisposeAndQuit(); }; TrayMenu.Items.Add(new MenuFlyoutItem() { Command = DiscoverPackages }); TrayMenu.Items.Add(new MenuFlyoutItem() { Command = AvailableUpdates }); @@ -249,30 +251,35 @@ private void LoadTrayMenu() public void UpdateSystemTrayStatus() { string modifier = "_empty"; - string tooltip = Tools.Translate("Everything is up to date") + " - " + Title; + string tooltip = CoreTools.Translate("Everything is up to date") + " - " + Title; - if (Tools.TooltipStatus.OperationsInProgress > 0) + if (MainApp.Instance.TooltipStatus.OperationsInProgress > 0) { modifier = "_blue"; - tooltip = Tools.Translate("Operation in progress") + " - " + Title; + tooltip = CoreTools.Translate("Operation in progress") + " - " + Title; } - else if (Tools.TooltipStatus.ErrorsOccurred > 0) + else if (MainApp.Instance.TooltipStatus.ErrorsOccurred > 0) { modifier = "_orange"; - tooltip = Tools.Translate("Attention required") + " - " + Title ; + tooltip = CoreTools.Translate("Attention required") + " - " + Title; } - else if (Tools.TooltipStatus.RestartRequired) + else if (MainApp.Instance.TooltipStatus.RestartRequired) { modifier = "_turquoise"; - tooltip = Tools.Translate("Restart required") + " - " + Title; + tooltip = CoreTools.Translate("Restart required") + " - " + Title; } - else if (Tools.TooltipStatus.AvailableUpdates > 0) + else if (MainApp.Instance.TooltipStatus.AvailableUpdates > 0) { modifier = "_green"; - if (Tools.TooltipStatus.AvailableUpdates == 1) - tooltip = Tools.Translate("1 update is available") + " - " + Title; + if (MainApp.Instance.TooltipStatus.AvailableUpdates == 1) + tooltip = CoreTools.Translate("1 update is available") + " - " + Title; else - tooltip = Tools.Translate("{0} updates are available").Replace("{0}", Tools.TooltipStatus.AvailableUpdates.ToString()) + " - " + Title; + tooltip = CoreTools.Translate("{0} updates are available").Replace("{0}", MainApp.Instance.TooltipStatus.AvailableUpdates.ToString()) + " - " + Title; + } + if(TrayIcon == null) + { + Logger.Warn("Attempting to update a null taskbar icon tray, aborting!"); + return; } TrayIcon.ToolTipText = tooltip; @@ -280,8 +287,8 @@ public void UpdateSystemTrayStatus() ApplicationTheme theme = ApplicationTheme.Light; string RegistryKeyPath = @"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"; string RegistryValueName = "SystemUsesLightTheme"; - RegistryKey key = Registry.CurrentUser.OpenSubKey(RegistryKeyPath); - object registryValueObject = key?.GetValue(RegistryValueName); + RegistryKey? key = Registry.CurrentUser.OpenSubKey(RegistryKeyPath); + object? registryValueObject = key?.GetValue(RegistryValueName) ?? null; if (registryValueObject != null) { int registryValue = (int)registryValueObject; @@ -293,7 +300,7 @@ public void UpdateSystemTrayStatus() modifier += "_white"; - string FullIconPath = Path.Join(Directory.GetParent(Assembly.GetEntryAssembly().Location).ToString(), "\\Assets\\Images\\tray" + modifier + ".ico"); + string FullIconPath = Path.Join(CoreData.UniGetUIExecutableDirectory, "\\Assets\\Images\\tray" + modifier + ".ico"); TrayIcon.SetValue(TaskbarIcon.IconSourceProperty, new BitmapImage() { UriSource = new Uri(FullIconPath) }); } @@ -319,36 +326,36 @@ public void SwitchToInterface() public void ApplyTheme() { - string preferredTheme = Tools.GetSettingsValue("PreferredTheme"); + string preferredTheme = Settings.GetValue("PreferredTheme"); if (preferredTheme == "dark") { - Tools.ThemeListener.CurrentTheme = ApplicationTheme.Dark; + MainApp.Instance.ThemeListener.CurrentTheme = ApplicationTheme.Dark; ContentRoot.RequestedTheme = ElementTheme.Dark; } else if (preferredTheme == "light") { - Tools.ThemeListener.CurrentTheme = ApplicationTheme.Light; + MainApp.Instance.ThemeListener.CurrentTheme = ApplicationTheme.Light; ContentRoot.RequestedTheme = ElementTheme.Light; } else { if (ContentRoot.ActualTheme == ElementTheme.Dark) - Tools.ThemeListener.CurrentTheme = ApplicationTheme.Dark; + MainApp.Instance.ThemeListener.CurrentTheme = ApplicationTheme.Dark; else - Tools.ThemeListener.CurrentTheme = ApplicationTheme.Light; + MainApp.Instance.ThemeListener.CurrentTheme = ApplicationTheme.Light; ContentRoot.RequestedTheme = ElementTheme.Default; } if (AppWindowTitleBar.IsCustomizationSupported()) { - if (Tools.ThemeListener.CurrentTheme == ApplicationTheme.Light) + if (MainApp.Instance.ThemeListener.CurrentTheme == ApplicationTheme.Light) AppWindow.TitleBar.ButtonForegroundColor = Colors.Black; else AppWindow.TitleBar.ButtonForegroundColor = Colors.White; } else { - AppTools.Log("Taskbar foreground color customization is not available"); + Logger.Info("Taskbar foreground color customization is not available"); } @@ -357,7 +364,7 @@ public void ApplyTheme() public void ShowLoadingDialog(string text) { - if(LoadingDialogCount == 0 && DialogQueue.Count == 0) + if (LoadingDialogCount == 0 && DialogQueue.Count == 0) { LoadingSthDalog.Title = text; LoadingSthDalog.XamlRoot = NavigationPage.XamlRoot; @@ -369,7 +376,7 @@ public void ShowLoadingDialog(string text) public void HideLoadingDialog() { LoadingDialogCount--; - if(LoadingDialogCount <= 0) + if (LoadingDialogCount <= 0) { LoadingSthDalog.Hide(); } @@ -377,7 +384,7 @@ public void HideLoadingDialog() LoadingDialogCount = 0; } - public void SharePackage(Package package) + public void SharePackage(Package? package) { if (package == null) return; @@ -431,7 +438,8 @@ public async Task ShowDialogAsync(ContentDialog dialog, boo } catch (Exception e) { - AppTools.Log(e); + Logger.Error("An error occurred while showing a ContentDialog via ShowDialogAsync()"); + Logger.Error(e); if (DialogQueue.Contains(dialog)) DialogQueue.Remove(dialog); return ContentDialogResult.None; diff --git a/src/UniGetUI/Interface/Pages/AboutPages/AboutUniGetUI.xaml.cs b/src/UniGetUI/Interface/Pages/AboutPages/AboutUniGetUI.xaml.cs index 0c189e762..e69ef541c 100644 --- a/src/UniGetUI/Interface/Pages/AboutPages/AboutUniGetUI.xaml.cs +++ b/src/UniGetUI/Interface/Pages/AboutPages/AboutUniGetUI.xaml.cs @@ -1,19 +1,8 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using Windows.Foundation; -using Windows.Foundation.Collections; -using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; -using Microsoft.UI.Xaml.Data; -using Microsoft.UI.Xaml.Input; -using Microsoft.UI.Xaml.Media; -using Microsoft.UI.Xaml.Navigation; -using UniGetUI.Core.Data; using UniGetUI.Core; +using UniGetUI.Core.Logging; +using UniGetUI.Core.Data; +using UniGetUI.Core.Tools; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. @@ -26,11 +15,10 @@ namespace UniGetUI.Interface.Pages.AboutPages /// public sealed partial class AboutUniGetUI : Page { - AppTools Tools = AppTools.Instance; public AboutUniGetUI() { - this.InitializeComponent(); - VersionText.Text = Tools.Translate("You have installed WingetUI Version {0}").Replace("{0}", CoreData.VersionName); + InitializeComponent(); + VersionText.Text = CoreTools.Translate("You have installed WingetUI Version {0}").Replace("{0}", CoreData.VersionName); } } diff --git a/src/UniGetUI/Interface/Pages/AboutPages/Contributors.xaml b/src/UniGetUI/Interface/Pages/AboutPages/Contributors.xaml index 5eaaad6ec..bc2469fc0 100644 --- a/src/UniGetUI/Interface/Pages/AboutPages/Contributors.xaml +++ b/src/UniGetUI/Interface/Pages/AboutPages/Contributors.xaml @@ -3,7 +3,7 @@ x:Class="UniGetUI.Interface.Pages.AboutPages.Contributors" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:local="using:UniGetUI.Interface.Pages.AboutPages" + xmlns:local="using:UniGetUI.Core.Classes" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:widgets="using:UniGetUI.Interface.Widgets" mc:Ignorable="d" diff --git a/src/UniGetUI/Interface/Pages/AboutPages/Contributors.xaml.cs b/src/UniGetUI/Interface/Pages/AboutPages/Contributors.xaml.cs index f128aa538..2fea8248f 100644 --- a/src/UniGetUI/Interface/Pages/AboutPages/Contributors.xaml.cs +++ b/src/UniGetUI/Interface/Pages/AboutPages/Contributors.xaml.cs @@ -1,19 +1,8 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using Windows.Foundation; -using Windows.Foundation.Collections; -using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; -using Microsoft.UI.Xaml.Data; -using Microsoft.UI.Xaml.Input; -using Microsoft.UI.Xaml.Media; -using Microsoft.UI.Xaml.Navigation; -using UniGetUI.Core.Data; +using System; using System.Collections.ObjectModel; +using UniGetUI.Core.Data; +using UniGetUI.Core.Classes; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. @@ -24,32 +13,21 @@ namespace UniGetUI.Interface.Pages.AboutPages /// An empty page that can be used on its own or navigated to within a Frame. ///
/// - public class Person - { - public string Name { get; set; } - public Uri? ProfilePicture; - public Uri? GitHubUrl; - public bool HasPicture = false; - public bool HasGitHubProfile = false; - public string Language = ""; - } + public sealed partial class Contributors : Page { public ObservableCollection ContributorList = []; public Contributors() { - this.InitializeComponent(); + InitializeComponent(); foreach (string contributor in ContributorsData.Contributors) { - Person person = new() - { - Name = "@" + contributor, - ProfilePicture = new Uri("https://github.com/" + contributor + ".png"), - GitHubUrl = new Uri("https://github.com/" + contributor), - HasPicture = true, - HasGitHubProfile = true, - }; + Person person = new( + Name: "@" + contributor, + ProfilePicture: new Uri("https://github.com/" + contributor + ".png"), + GitHubUrl: new Uri("https://github.com/" + contributor) + ); ContributorList.Add(person); } } diff --git a/src/UniGetUI/Interface/Pages/AboutPages/SupportMe.xaml.cs b/src/UniGetUI/Interface/Pages/AboutPages/SupportMe.xaml.cs index 17243ebfb..d302702b0 100644 --- a/src/UniGetUI/Interface/Pages/AboutPages/SupportMe.xaml.cs +++ b/src/UniGetUI/Interface/Pages/AboutPages/SupportMe.xaml.cs @@ -1,17 +1,4 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using Windows.Foundation; -using Windows.Foundation.Collections; -using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; -using Microsoft.UI.Xaml.Data; -using Microsoft.UI.Xaml.Input; -using Microsoft.UI.Xaml.Media; -using Microsoft.UI.Xaml.Navigation; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. @@ -25,7 +12,7 @@ public sealed partial class SupportMe : Page { public SupportMe() { - this.InitializeComponent(); + InitializeComponent(); } } } diff --git a/src/UniGetUI/Interface/Pages/AboutPages/ThirdPartyLicenses.xaml.cs b/src/UniGetUI/Interface/Pages/AboutPages/ThirdPartyLicenses.xaml.cs index 23f9b13be..503b9a262 100644 --- a/src/UniGetUI/Interface/Pages/AboutPages/ThirdPartyLicenses.xaml.cs +++ b/src/UniGetUI/Interface/Pages/AboutPages/ThirdPartyLicenses.xaml.cs @@ -1,21 +1,9 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using Windows.Foundation; -using Windows.Foundation.Collections; -using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; -using Microsoft.UI.Xaml.Data; -using Microsoft.UI.Xaml.Input; -using Microsoft.UI.Xaml.Media; -using Microsoft.UI.Xaml.Navigation; -using UniGetUI.Core.Data; -using System.ComponentModel; -using UniGetUI.Core; +using System; using System.Collections.ObjectModel; +using UniGetUI.Core; +using UniGetUI.Core.Data; +using UniGetUI.Core.Tools; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. @@ -28,21 +16,20 @@ namespace UniGetUI.Interface.Pages.AboutPages /// public class LibraryLicense { - public string Name { get; set; } - public string License { get; set; } - public Uri LicenseURL { get; set; } - public string HomepageText { get; set; } - public Uri HomepageUrl { get; set; } + public string Name { get; set; } = ""; + public string License { get; set; } = ""; + public Uri? LicenseURL { get; set; } + public string HomepageText { get; set; } = ""; + public Uri? HomepageUrl { get; set; } } public sealed partial class ThirdPartyLicenses : Page { - public AppTools Tools = AppTools.Instance; public ObservableCollection Licenses = new(); public ThirdPartyLicenses() { - this.InitializeComponent(); + InitializeComponent(); foreach (string license in LicenseData.LicenseNames.Keys) { Licenses.Add(new LibraryLicense() @@ -51,7 +38,7 @@ public ThirdPartyLicenses() License = LicenseData.LicenseNames[license], LicenseURL = LicenseData.LicenseURLs[license], HomepageUrl = LicenseData.HomepageUrls[license], - HomepageText = Tools.Translate("{0} homepage").Replace("{0}", license) + HomepageText = CoreTools.Translate("{0} homepage").Replace("{0}", license) }); } diff --git a/src/UniGetUI/Interface/Pages/AboutPages/Translators.xaml b/src/UniGetUI/Interface/Pages/AboutPages/Translators.xaml index d125b824a..d99741023 100644 --- a/src/UniGetUI/Interface/Pages/AboutPages/Translators.xaml +++ b/src/UniGetUI/Interface/Pages/AboutPages/Translators.xaml @@ -3,7 +3,7 @@ x:Class="UniGetUI.Interface.Pages.AboutPages.Translators" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:local="using:UniGetUI.Interface.Pages.AboutPages" + xmlns:local="using:UniGetUI.Core.Classes" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:widgets="using:UniGetUI.Interface.Widgets" mc:Ignorable="d" diff --git a/src/UniGetUI/Interface/Pages/AboutPages/Translators.xaml.cs b/src/UniGetUI/Interface/Pages/AboutPages/Translators.xaml.cs index 5748d3821..cb293f867 100644 --- a/src/UniGetUI/Interface/Pages/AboutPages/Translators.xaml.cs +++ b/src/UniGetUI/Interface/Pages/AboutPages/Translators.xaml.cs @@ -1,21 +1,12 @@ +using Microsoft.UI.Xaml.Controls; using System; using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using Windows.Foundation; -using Windows.Foundation.Collections; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; -using Microsoft.UI.Xaml.Data; -using Microsoft.UI.Xaml.Input; -using Microsoft.UI.Xaml.Media; -using Microsoft.UI.Xaml.Navigation; -using UniGetUI.Core.Data; -using UniGetUI.Core; -using System.Text.Json.Nodes; using System.Collections.ObjectModel; +using System.Text.Json.Nodes; +using UniGetUI.Core; +using UniGetUI.Core.Language; +using UniGetUI.Core.Logging; +using UniGetUI.Core.Classes; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. @@ -30,37 +21,9 @@ public sealed partial class Translators : Page public ObservableCollection TranslatorList = new(); public Translators() { - this.InitializeComponent(); - JsonObject TranslatorsInfo = JsonNode.Parse(LanguageData.TranslatorsJSON).AsObject(); - - - foreach (KeyValuePair langKey in TranslatorsInfo) - { - if (!LanguageData.LanguageList.ContainsKey(langKey.Key)) - { - AppTools.Log($"Language {langKey.Key} not in list, maybe has not been added yet?"); - continue; - } - JsonArray TranslatorsForLang = langKey.Value.AsArray(); - bool LangShown = false; - foreach (JsonNode translator in TranslatorsForLang) - { - Uri? url = null; - if (translator["link"].ToString() != "") - url = new Uri(translator["link"].ToString()); - Person person = new() - { - Name = (url != null ? "@" : "") + translator["name"].ToString(), - HasPicture = url != null, - HasGitHubProfile = url != null, - GitHubUrl = url != null ? url : new Uri("https://github.com/"), - ProfilePicture = url != null ? new Uri(url.ToString() + ".png") : new Uri("https://github.com/"), - Language = !LangShown ? LanguageData.LanguageList[langKey.Key] : "", - }; - LangShown = true; - TranslatorList.Add(person); - } - } + InitializeComponent(); + foreach(Person person in LanguageData.TranslatorsList) + TranslatorList.Add(person); } } } diff --git a/src/UniGetUI/Interface/Pages/HelpPage.xaml.cs b/src/UniGetUI/Interface/Pages/HelpPage.xaml.cs index f476a9955..4524fafa8 100644 --- a/src/UniGetUI/Interface/Pages/HelpPage.xaml.cs +++ b/src/UniGetUI/Interface/Pages/HelpPage.xaml.cs @@ -2,6 +2,7 @@ using Microsoft.UI.Xaml.Controls; using System; using System.Diagnostics; +using UniGetUI.Core.Logging; using System.Threading.Tasks; // To learn more about WinUI, the WinUI project structure, diff --git a/src/UniGetUI/Interface/Pages/LogPage.xaml b/src/UniGetUI/Interface/Pages/LogPage.xaml index 4068f0d6a..0393fc319 100644 --- a/src/UniGetUI/Interface/Pages/LogPage.xaml +++ b/src/UniGetUI/Interface/Pages/LogPage.xaml @@ -1,6 +1,6 @@ - + + + - - - - + diff --git a/src/UniGetUI/Interface/Pages/LogPage.xaml.cs b/src/UniGetUI/Interface/Pages/LogPage.xaml.cs index 74ef0a826..564b05c08 100644 --- a/src/UniGetUI/Interface/Pages/LogPage.xaml.cs +++ b/src/UniGetUI/Interface/Pages/LogPage.xaml.cs @@ -1,16 +1,25 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Documents; -using UniGetUI.Core.Data; -using UniGetUI.Core; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using UniGetUI.Core; +using UniGetUI.Core.Data; +using ExternalLibraries.Clipboard; +using UniGetUI.Core.Logging; using Windows.Storage; using Windows.Storage.Pickers; -using Windows.ApplicationModel.DataTransfer; -using UniGetUI.ExternalLibraries.Clipboard; +using UniGetUI.Core.SettingsEngine; +using UniGetUI.Core.Tools; +using System.Linq; +using Windows.UI.WebUI; +using Microsoft.UI.Xaml.Media; +using Windows.Media.Playback; +using Windows.UI; +using CommunityToolkit.WinUI.Controls; +using CommunityToolkit.WinUI.Helpers; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. @@ -21,31 +30,39 @@ namespace UniGetUI.Interface.Pages /// An empty page that can be used on its own or navigated to within a Frame. ///
/// - public enum LogType + public enum Logger_LogType { UniGetUILog, ManagerLogs, OperationHistory } - public sealed partial class LogPage : Page + public sealed partial class Logger_LogPage : Page { - public LogType LogType; - public LogPage(LogType logType = LogType.UniGetUILog) + private int LOG_LEVEL = 4; + public Logger_LogType Logger_LogType; + public Logger_LogPage(Logger_LogType logger_LogType = Logger_LogType.UniGetUILog) { InitializeComponent(); - LogType = logType; + Logger_LogType = logger_LogType; LoadLog(); + + LogLevelCombo.Items.Add(CoreTools.Translate("1 - Errors")); + LogLevelCombo.Items.Add(CoreTools.Translate("2 - Warnings")); + LogLevelCombo.Items.Add(CoreTools.Translate("3 - Information (less)")); + LogLevelCombo.Items.Add(CoreTools.Translate("4 - Information (more)")); + LogLevelCombo.Items.Add(CoreTools.Translate("5 - information (debug)")); + LogLevelCombo.SelectedIndex = 3; } public void SetText(string body) { Paragraph paragraph = new(); - foreach(var line in body.Split("\n")) + foreach (string line in body.Split("\n")) { if (line.Replace("\r", "").Replace("\n", "").Trim() == "") continue; - paragraph.Inlines.Add(new Run() { Text = line.Replace("\r", "").Replace("\n", "")}); + paragraph.Inlines.Add(new Run() { Text = line.Replace("\r", "").Replace("\n", "") }); paragraph.Inlines.Add(new LineBreak()); } LogTextBox.Blocks.Clear(); @@ -54,17 +71,92 @@ public void SetText(string body) public void LoadLog() { - if (LogType == LogType.UniGetUILog) + + + if (Logger_LogType == Logger_LogType.UniGetUILog) { - SetText(CoreData.UniGetUILog); + // Dark theme colors + Color DARK_GREY = Color.FromArgb(255, 130, 130, 130); + Color DARK_BLUE = Color.FromArgb(255, 190, 190, 190); + Color DARK_WHITE = Color.FromArgb(255, 250, 250, 250); + Color DARK_YELLOW = Color.FromArgb(255, 255, 255, 90); + Color DARK_RED = Color.FromArgb(255, 255, 80, 80); + + // Light theme colors + Color LIGHT_GREY = Color.FromArgb(255, 125, 125, 225); + Color LIGHT_BLUE = Color.FromArgb(255, 50, 50, 150); + Color LIGHT_WHITE = Color.FromArgb(255, 0, 0, 0); + Color LIGHT_YELLOW = Color.FromArgb(255, 150, 150, 0); + Color LIGHT_RED = Color.FromArgb(255, 205, 0, 0); + + bool IS_DARK = MainApp.Instance.ThemeListener.CurrentTheme == ApplicationTheme.Dark; + + var logs = Logger.GetLogs(); + LogTextBox.Blocks.Clear(); + foreach (var log_entry in logs) + { + var p = new Paragraph(); + if (log_entry.Content == "") + continue; + + if (LOG_LEVEL == 1 && (log_entry.Severity == LogEntry.SeverityLevel.Debug || log_entry.Severity == LogEntry.SeverityLevel.Info || log_entry.Severity == LogEntry.SeverityLevel.Success || log_entry.Severity == LogEntry.SeverityLevel.Warning)) + continue; + else if(LOG_LEVEL == 2 && (log_entry.Severity == LogEntry.SeverityLevel.Debug || log_entry.Severity == LogEntry.SeverityLevel.Info || log_entry.Severity == LogEntry.SeverityLevel.Success)) + continue; + else if(LOG_LEVEL == 3 && (log_entry.Severity == LogEntry.SeverityLevel.Debug || log_entry.Severity == LogEntry.SeverityLevel.Info)) + continue; + else if(LOG_LEVEL == 4 && (log_entry.Severity == LogEntry.SeverityLevel.Debug)) + continue; + + Brush color; + + + switch (log_entry.Severity) + { + case LogEntry.SeverityLevel.Debug: + color = new SolidColorBrush() { Color = IS_DARK? DARK_GREY: LIGHT_GREY }; + break; + case LogEntry.SeverityLevel.Info: + color = new SolidColorBrush() { Color = IS_DARK ? DARK_BLUE : LIGHT_BLUE }; + break; + case LogEntry.SeverityLevel.Success: + color = new SolidColorBrush() { Color = IS_DARK ? DARK_WHITE : LIGHT_WHITE}; + break; + case LogEntry.SeverityLevel.Warning: + color = new SolidColorBrush() { Color = IS_DARK ? DARK_YELLOW : LIGHT_YELLOW }; + break; + case LogEntry.SeverityLevel.Error: + color = new SolidColorBrush() { Color = IS_DARK ? DARK_RED : LIGHT_RED }; + break; + default: + color = new SolidColorBrush() { Color = IS_DARK ? DARK_GREY : LIGHT_GREY }; + break; + } + + var lines = log_entry.Content.Split('\n'); + var date_length = -1; + foreach(var line in lines) + if (date_length == -1) + { + p.Inlines.Add(new Run() { Text = $"[{log_entry.Time}] {line}\n", Foreground = color }); + date_length = $"[{log_entry.Time}] ".Length; + } + else + { + p.Inlines.Add(new Run() { Text = new string(' ', date_length) + line + "\n", Foreground = color }); + } + ((Run)p.Inlines[^1]).Text = ((Run)p.Inlines[^1]).Text.TrimEnd(); + LogTextBox.Blocks.Add(p); + } + //SetText(text); } - else if (LogType == LogType.ManagerLogs) + else if (Logger_LogType == Logger_LogType.ManagerLogs) { SetText(CoreData.ManagerLogs); } - else if (LogType == LogType.OperationHistory) + else if (Logger_LogType == Logger_LogType.OperationHistory) { - SetText(AppTools.GetSettingsValue_Static("OperationHistory")); + SetText(Settings.GetValue("OperationHistory")); } } @@ -79,9 +171,9 @@ public async void ExportButton_Click(object sender, RoutedEventArgs e) { FileSavePicker savePicker = new(); savePicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary; - WinRT.Interop.InitializeWithWindow.Initialize(savePicker, WinRT.Interop.WindowNative.GetWindowHandle(AppTools.Instance.App.MainWindow)); - savePicker.FileTypeChoices.Add(AppTools.Instance.Translate("Text"), new List() { ".txt" }); - savePicker.SuggestedFileName = AppTools.Instance.Translate("WingetUI Log"); + WinRT.Interop.InitializeWithWindow.Initialize(savePicker, WinRT.Interop.WindowNative.GetWindowHandle(MainApp.Instance.MainWindow)); + savePicker.FileTypeChoices.Add(CoreTools.Translate("Text"), new List() { ".txt" }); + savePicker.SuggestedFileName = CoreTools.Translate("WingetUI Log"); StorageFile file = await savePicker.PickSaveFileAsync(); if (file != null) @@ -102,5 +194,12 @@ public void ReloadButton_Click(object sender, RoutedEventArgs e) { LoadLog(); } + + + private void LogLevelCombo_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + LOG_LEVEL = LogLevelCombo.SelectedIndex + 1; + LoadLog(); + } } } diff --git a/src/UniGetUI/Interface/Pages/PackageDetailsPage.xaml.cs b/src/UniGetUI/Interface/Pages/PackageDetailsPage.xaml.cs index 98b4dc535..f9b968d70 100644 --- a/src/UniGetUI/Interface/Pages/PackageDetailsPage.xaml.cs +++ b/src/UniGetUI/Interface/Pages/PackageDetailsPage.xaml.cs @@ -1,17 +1,23 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media.Imaging; -using UniGetUI.Core.Data; -using UniGetUI.PackageEngine.Classes; -using UniGetUI.PackageEngine.Operations; -using UniGetUI.Core; using System; using System.Collections.ObjectModel; using System.IO; +using System.Linq; using System.Net.Http; using System.Threading.Tasks; +using UniGetUI.Core; +using UniGetUI.Core.Data; +using UniGetUI.PackageEngine.Classes; +using UniGetUI.PackageEngine.Operations; +using UniGetUI.Core.Logging; using Windows.Storage; using Windows.Storage.Pickers; +using UniGetUI.PackageEngine.PackageClasses; +using UniGetUI.PackageEngine.Enums; +using UniGetUI.Core.Tools; +using UniGetUI.Core.IconEngine; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. @@ -23,11 +29,10 @@ namespace UniGetUI.Interface.Dialogs /// public sealed partial class PackageDetailsPage : Page { - public AppTools Tools = AppTools.Instance; public Package Package; private InstallOptionsPage InstallOptionsPage; - public event EventHandler Close; - private PackageDetails Info; + public event EventHandler? Close; + private PackageDetails? Info; OperationType FutureOperation; bool PackageHasScreenshots = false; public ObservableCollection ShowableTags = new(); @@ -39,7 +44,7 @@ private enum LayoutMode Unloaded } - private LayoutMode layoutMode = LayoutMode.Unloaded; + private LayoutMode __layout_mode = LayoutMode.Unloaded; public PackageDetailsPage(Package package, OperationType futureOperation) { FutureOperation = futureOperation; @@ -58,26 +63,25 @@ public PackageDetailsPage(Package package, OperationType futureOperation) switch (futureOperation) { case OperationType.Install: - ActionButton.Content = Tools.Translate("Install"); + ActionButton.Content = CoreTools.Translate("Install"); break; case OperationType.Uninstall: - ActionButton.Content = Tools.Translate("Uninstall"); + ActionButton.Content = CoreTools.Translate("Uninstall"); break; case OperationType.Update: - ActionButton.Content = Tools.Translate("Update"); + ActionButton.Content = CoreTools.Translate("Update"); break; } IdTextBlock.Text = package.Id; VersionTextBlock.Text = package.Version; - if (package is UpgradablePackage) - VersionTextBlock.Text += " - " + Tools.Translate("Update to {0} available").Replace("{0}", (package as UpgradablePackage).NewVersion); + if (package.IsUpgradable) + VersionTextBlock.Text += " - " + CoreTools.Translate("Update to {0} available").Replace("{0}", package.NewVersion); PackageName.Text = package.Name; - PackageIcon.Source = new BitmapImage() { UriSource = package.GetIconUrl() }; SourceNameTextBlock.Text = package.SourceAsString; - string LoadingString = Tools.Translate("Loading..."); + string LoadingString = CoreTools.Translate("Loading..."); LoadingIndicator.Visibility = Visibility.Visible; @@ -98,19 +102,6 @@ public PackageDetailsPage(Package package, OperationType futureOperation) DownloadInstallerButton.IsEnabled = false; ReleaseNotesUrlButton.Content = LoadingString; - if (CoreData.IconDatabaseData.ContainsKey(Package.GetIconId())) - { - if (CoreData.IconDatabaseData[Package.GetIconId()].images.Count > 0) - { - PackageHasScreenshots = true; - IconsExtraBanner.Visibility = Visibility.Visible; - ScreenshotsCarroussel.Items.Clear(); - foreach (string image in CoreData.IconDatabaseData[Package.GetIconId()].images) - ScreenshotsCarroussel.Items.Add(new Image() { Source = new BitmapImage(new Uri(image)) }); - } - } - - _ = LoadInformation(); } @@ -118,10 +109,13 @@ public async Task LoadInformation() { LoadingIndicator.Visibility = Visibility.Visible; - string NotFound = Tools.Translate("Not available"); + LoadIcon(); + LoadScreenshots(); + + string NotFound = CoreTools.Translate("Not available"); Uri InvalidUri = new("about:blank"); Info = await Package.Manager.GetPackageDetails(Package); - AppTools.Log("Received info " + Info); + Logger.Debug("Received info " + Info); string command = ""; @@ -133,7 +127,7 @@ public async Task LoadInformation() case OperationType.Uninstall: command = Package.Manager.Properties.ExecutableFriendlyName + " " + String.Join(' ', Package.Manager.GetUninstallParameters(Package, await InstallationOptions.FromPackageAsync(Package))); - break; + break; case OperationType.Update: command = Package.Manager.Properties.ExecutableFriendlyName + " " + String.Join(' ', Package.Manager.GetUpdateParameters(Package, await InstallationOptions.FromPackageAsync(Package))); @@ -192,6 +186,29 @@ public async Task LoadInformation() ShowableTags.Add(new TextBlock() { Text = tag }); } + public async void LoadIcon() + { + PackageIcon.Source = new BitmapImage() { UriSource = (await Package.GetIconUrl()) }; + } + + public async void LoadScreenshots() + { + var screenshots = await Package.GetPackageScreenshots(); + PackageHasScreenshots = screenshots.Count() > 0; + if (PackageHasScreenshots) + { + PackageHasScreenshots = true; + IconsExtraBanner.Visibility = Visibility.Visible; + ScreenshotsCarroussel.Items.Clear(); + foreach (Uri image in screenshots) + ScreenshotsCarroussel.Items.Add(new Image() { Source = new BitmapImage(image) }); + } + + __layout_mode = LayoutMode.Unloaded; + PackageDetailsPage_SizeChanged(); + + } + public void ActionButton_Click(object sender, RoutedEventArgs e) { Close?.Invoke(this, new EventArgs()); @@ -199,32 +216,32 @@ public void ActionButton_Click(object sender, RoutedEventArgs e) switch (FutureOperation) { case OperationType.Install: - Tools.AddOperationToList(new InstallPackageOperation(Package, InstallOptionsPage.Options)); + MainApp.Instance.AddOperationToList(new InstallPackageOperation(Package, InstallOptionsPage.Options)); break; case OperationType.Uninstall: - Tools.App.MainWindow.NavigationPage.InstalledPage.ConfirmAndUninstall(Package, InstallOptionsPage.Options); + MainApp.Instance.MainWindow.NavigationPage.InstalledPage.ConfirmAndUninstall(Package, InstallOptionsPage.Options); break; case OperationType.Update: - Tools.AddOperationToList(new UpdatePackageOperation(Package, InstallOptionsPage.Options)); + MainApp.Instance.AddOperationToList(new UpdatePackageOperation(Package, InstallOptionsPage.Options)); break; } } public void ShareButton_Click(object sender, RoutedEventArgs e) { - Tools.App.MainWindow.SharePackage(Package); + MainApp.Instance.MainWindow.SharePackage(Package); } public async void DownloadInstallerButton_Click(object sender, RoutedEventArgs e) { try { - if (Info.InstallerUrl == null) + if (Info?.InstallerUrl == null) return; ErrorOutput.Text = ""; FileSavePicker savePicker = new(); - MainWindow window = Tools.App.MainWindow; + MainWindow window = MainApp.Instance.MainWindow; IntPtr hWnd = WinRT.Interop.WindowNative.GetWindowHandle(window); WinRT.Interop.InitializeWithWindow.Initialize(savePicker, hWnd); savePicker.SuggestedStartLocation = PickerLocationId.Downloads; @@ -235,23 +252,25 @@ public async void DownloadInstallerButton_Click(object sender, RoutedEventArgs e StorageFile file = await savePicker.PickSaveFileAsync(); if (file != null) { - DownloadInstallerButton.Content = Tools.Translate("Downloading"); + DownloadInstallerButton.Content = CoreTools.Translate("Downloading"); DownloadInstallerButtonProgress.Visibility = Visibility.Visible; - AppTools.Log(file.Path.ToString()); + Logger.Debug($"Downloading installer ${file.Path.ToString()}"); using HttpClient httpClient = new(); await using Stream s = await httpClient.GetStreamAsync(Info.InstallerUrl); await using FileStream fs = File.OpenWrite(file.Path.ToString()); await s.CopyToAsync(fs); fs.Dispose(); + Logger.ImportantInfo($"Installer for {Package.Id} has been downloaded successfully"); DownloadInstallerButtonProgress.Visibility = Visibility.Collapsed; System.Diagnostics.Process.Start("explorer.exe", "/select," + file.Path.ToString()); - DownloadInstallerButton.Content = Tools.Translate("Download succeeded"); + DownloadInstallerButton.Content = CoreTools.Translate("Download succeeded"); } } catch (Exception ex) { - AppTools.Log(ex); - DownloadInstallerButton.Content = Tools.Translate("An error occurred"); + Logger.Error($"An error occurred while downloading the installer for the package {Package.Id}"); + Logger.Error(ex); + DownloadInstallerButton.Content = CoreTools.Translate("An error occurred"); DownloadInstallerButtonProgress.Visibility = Visibility.Collapsed; ErrorOutput.Text = ex.Message; } @@ -263,13 +282,13 @@ public void CloseButton_Click(object sender, RoutedEventArgs e) Close?.Invoke(this, new EventArgs()); } - public void PackageDetailsPage_SizeChanged(object sender, SizeChangedEventArgs e) + public void PackageDetailsPage_SizeChanged(object? sender = null, SizeChangedEventArgs? e = null) { - if (e.NewSize.Width < 950) + if (MainApp.Instance.MainWindow.AppWindow.Size.Width < 950) { - if (layoutMode != LayoutMode.Normal) + if (__layout_mode != LayoutMode.Normal) { - layoutMode = LayoutMode.Normal; + __layout_mode = LayoutMode.Normal; MainGrid.ColumnDefinitions.Clear(); MainGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) }); @@ -314,9 +333,9 @@ public void PackageDetailsPage_SizeChanged(object sender, SizeChangedEventArgs e } else { - if (layoutMode != LayoutMode.Wide) + if (__layout_mode != LayoutMode.Wide) { - layoutMode = LayoutMode.Wide; + __layout_mode = LayoutMode.Wide; MainGrid.ColumnDefinitions.Clear(); MainGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star), MinWidth = 550 }); diff --git a/src/UniGetUI/Interface/Pages/SettingsPage.xaml.cs b/src/UniGetUI/Interface/Pages/SettingsPage.xaml.cs index d411122a9..9fe635d17 100644 --- a/src/UniGetUI/Interface/Pages/SettingsPage.xaml.cs +++ b/src/UniGetUI/Interface/Pages/SettingsPage.xaml.cs @@ -1,22 +1,23 @@ using CommunityToolkit.WinUI.Controls; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Data; using Microsoft.UI.Xaml.Media; -using UniGetUI.Core.Data; -using UniGetUI.Interface.Widgets; -using UniGetUI.PackageEngine.Classes; -using UniGetUI.Core; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; -using Windows.ApplicationModel.VoiceCommands; -using Windows.Storage; -using Windows.Storage.Pickers; -using UniGetUI.ExternalLibraries.Clipboard; +using UniGetUI.Core; +using UniGetUI.Core.Data; +using ExternalLibraries.Clipboard; +using UniGetUI.Interface.Widgets; +using UniGetUI.PackageEngine.Classes; +using UniGetUI.Core.Logging; +using UniGetUI.Core.Language; +using UniGetUI.Core.Tools; +using UniGetUI.Core.SettingsEngine; +using UniGetUI.PackageEngine.ManagerClasses.Manager; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. @@ -28,7 +29,6 @@ namespace UniGetUI.Interface /// public sealed partial class SettingsInterface : Page { - private AppTools Tools = AppTools.Instance; HyperlinkButton ResetBackupDirectory; HyperlinkButton OpenBackupDirectory; TextBlock BackupDirectoryLabel; @@ -39,12 +39,12 @@ public SettingsInterface() InitializeComponent(); // General Settings Section - Dictionary lang_dict = LanguageData.LanguageList; + Dictionary lang_dict = new(LanguageData.LanguageReference.AsEnumerable()); - foreach(var key in lang_dict.Keys) + foreach (string key in lang_dict.Keys) { - if(LanguageData.TranslatedPercentages.ContainsKey(key) && key != "en") - lang_dict[key] = lang_dict[key] + " (" + LanguageData.TranslatedPercentages[key] + ")"; + if (LanguageData.TranslationPercentages.ContainsKey(key) && key != "en") + lang_dict[key] = lang_dict[key] + " (" + LanguageData.TranslationPercentages[key] + ")"; } bool isFirst = true; @@ -59,17 +59,17 @@ public SettingsInterface() Dictionary updates_dict = new() { - {Tools.Translate("{0} minutes").Replace("{0}", "10"), "600"}, - {Tools.Translate("{0} minutes").Replace("{0}", "30"), "1800"}, - {Tools.Translate("1 hour"), "3600"}, - {Tools.Translate("{0} hours").Replace("{0}", "2"), "7200"}, - {Tools.Translate("{0} hours").Replace("{0}", "4"), "14400"}, - {Tools.Translate("{0} hours").Replace("{0}", "8"), "28800"}, - {Tools.Translate("{0} hours").Replace("{0}", "12"), "43200"}, - {Tools.Translate("1 day"), "86400"}, - {Tools.Translate("{0} days").Replace("{0}", "2"), "172800"}, - {Tools.Translate("{0} days").Replace("{0}", "3"), "259200"}, - {Tools.Translate("1 week"), "604800"} + {CoreTools.Translate("{0} minutes").Replace("{0}", "10"), "600"}, + {CoreTools.Translate("{0} minutes").Replace("{0}", "30"), "1800"}, + {CoreTools.Translate("1 hour"), "3600"}, + {CoreTools.Translate("{0} hours").Replace("{0}", "2"), "7200"}, + {CoreTools.Translate("{0} hours").Replace("{0}", "4"), "14400"}, + {CoreTools.Translate("{0} hours").Replace("{0}", "8"), "28800"}, + {CoreTools.Translate("{0} hours").Replace("{0}", "12"), "43200"}, + {CoreTools.Translate("1 day"), "86400"}, + {CoreTools.Translate("{0} days").Replace("{0}", "2"), "172800"}, + {CoreTools.Translate("{0} days").Replace("{0}", "3"), "259200"}, + {CoreTools.Translate("1 week"), "604800"} }; foreach (KeyValuePair entry in updates_dict) @@ -78,36 +78,36 @@ public SettingsInterface() } UpdatesCheckIntervalSelector.ShowAddedItems(); - if (Tools.GetSettingsValue("PreferredTheme") == "") - Tools.SetSettingsValue("PreferredTheme", "auto"); + if (Settings.GetValue("PreferredTheme") == "") + Settings.SetValue("PreferredTheme", "auto"); - ThemeSelector.AddItem(Tools.AutoTranslated("Light"), "light"); - ThemeSelector.AddItem(Tools.AutoTranslated("Dark"), "dark"); - ThemeSelector.AddItem(Tools.AutoTranslated("Follow system color scheme"), "auto"); + ThemeSelector.AddItem(CoreTools.AutoTranslated("Light"), "light"); + ThemeSelector.AddItem(CoreTools.AutoTranslated("Dark"), "dark"); + ThemeSelector.AddItem(CoreTools.AutoTranslated("Follow system color scheme"), "auto"); ThemeSelector.ShowAddedItems(); // Backup Section BackupDirectoryLabel = (TextBlock)(((StackPanel)ChangeBackupDirectory.Description).Children.ElementAt(0)); ResetBackupDirectory = (HyperlinkButton)(((StackPanel)ChangeBackupDirectory.Description).Children.ElementAt(1)); OpenBackupDirectory = (HyperlinkButton)(((StackPanel)ChangeBackupDirectory.Description).Children.ElementAt(2)); - if (!Tools.GetSettings("ChangeBackupOutputDirectory")) + if (!Settings.Get("ChangeBackupOutputDirectory")) { BackupDirectoryLabel.Text = CoreData.UniGetUI_DefaultBackupDirectory; ResetBackupDirectory.IsEnabled = false; } else { - BackupDirectoryLabel.Text = Tools.GetSettingsValue("ChangeBackupOutputDirectory"); + BackupDirectoryLabel.Text = Settings.GetValue("ChangeBackupOutputDirectory"); ResetBackupDirectory.IsEnabled = true; } - ResetBackupDirectory.Content = Tools.Translate("Reset"); + ResetBackupDirectory.Content = CoreTools.Translate("Reset"); - OpenBackupDirectory.Content = Tools.Translate("Open"); + OpenBackupDirectory.Content = CoreTools.Translate("Open"); // Admin Settings Section int index = 2; - foreach (PackageManager manager in Tools.App.PackageManagerList) + foreach (PackageManager manager in MainApp.Instance.PackageManagerList) { } @@ -119,53 +119,53 @@ public SettingsInterface() Dictionary PackageManagerExpanders = new(); Dictionary> ExtraSettingsCards = new(); - foreach (PackageManager Manager in Tools.App.PackageManagerList) + foreach (PackageManager Manager in MainApp.Instance.PackageManagerList) { ExtraSettingsCards.Add(Manager, new List()); } - ButtonCard Winget_ResetSources = new() { Text = Tools.AutoTranslated("Reset Winget sources (might help if no packages are listed)"), ButtonText = Tools.AutoTranslated("Reset") }; + ButtonCard Winget_ResetSources = new() { Text = CoreTools.AutoTranslated("Reset Winget sources (might help if no packages are listed)"), ButtonText = CoreTools.AutoTranslated("Reset") }; Winget_ResetSources.Click += (s, e) => { - AppTools.LaunchBatchFile(Path.Join(CoreData.UniGetUIExecutableDirectory, "Assets", "Utilities", "reset_winget_sources.cmd"), Tools.Translate("Resetting Winget sources - WingetUI"), RunAsAdmin: true); + CoreTools.LaunchBatchFile(Path.Join(CoreData.UniGetUIExecutableDirectory, "Assets", "Utilities", "reset_winget_sources.cmd"), CoreTools.Translate("Resetting Winget sources - WingetUI"), RunAsAdmin: true); }; - ExtraSettingsCards[Tools.App.Winget].Add(Winget_ResetSources); + ExtraSettingsCards[MainApp.Winget].Add(Winget_ResetSources); - ButtonCard Scoop_Install = new() { Text = Tools.AutoTranslated("Install Scoop"), ButtonText = Tools.AutoTranslated("Install") }; + ButtonCard Scoop_Install = new() { Text = CoreTools.AutoTranslated("Install Scoop"), ButtonText = CoreTools.AutoTranslated("Install") }; Scoop_Install.Click += (s, e) => { - AppTools.LaunchBatchFile(Path.Join(CoreData.UniGetUIExecutableDirectory, "Assets", "Utilities", "install_scoop.cmd"), Tools.Translate("Scoop Installer - WingetUI")); - PackageManagerExpanders[Tools.App.Scoop].ShowRestartRequiredBanner(); + CoreTools.LaunchBatchFile(Path.Join(CoreData.UniGetUIExecutableDirectory, "Assets", "Utilities", "install_scoop.cmd"), CoreTools.Translate("Scoop Installer - WingetUI")); + PackageManagerExpanders[MainApp.Scoop].ShowRestartRequiredBanner(); }; - ButtonCard Scoop_Uninstall = new() { Text = Tools.AutoTranslated("Uninstall Scoop (and its packages)"), ButtonText = Tools.AutoTranslated("Uninstall") }; + ButtonCard Scoop_Uninstall = new() { Text = CoreTools.AutoTranslated("Uninstall Scoop (and its packages)"), ButtonText = CoreTools.AutoTranslated("Uninstall") }; Scoop_Uninstall.Click += (s, e) => { - AppTools.LaunchBatchFile(Path.Join(CoreData.UniGetUIExecutableDirectory, "Assets", "Utilities", "uninstall_scoop.cmd"), Tools.Translate("Scoop Uninstaller - WingetUI")); - PackageManagerExpanders[Tools.App.Scoop].ShowRestartRequiredBanner(); + CoreTools.LaunchBatchFile(Path.Join(CoreData.UniGetUIExecutableDirectory, "Assets", "Utilities", "uninstall_scoop.cmd"), CoreTools.Translate("Scoop Uninstaller - WingetUI")); + PackageManagerExpanders[MainApp.Scoop].ShowRestartRequiredBanner(); }; - ButtonCard Scoop_ResetAppCache = new() { Text = Tools.AutoTranslated("Run cleanup and clear cache"), ButtonText = Tools.AutoTranslated("Run") }; + ButtonCard Scoop_ResetAppCache = new() { Text = CoreTools.AutoTranslated("Run cleanup and clear cache"), ButtonText = CoreTools.AutoTranslated("Run") }; Scoop_ResetAppCache.Click += (s, e) => { - AppTools.LaunchBatchFile(Path.Join(CoreData.UniGetUIExecutableDirectory, "Assets", "Utilities", "scoop_cleanup.cmd"), Tools.Translate("Clearing Scoop cache - WingetUI"), RunAsAdmin: true); + CoreTools.LaunchBatchFile(Path.Join(CoreData.UniGetUIExecutableDirectory, "Assets", "Utilities", "scoop_cleanup.cmd"), CoreTools.Translate("Clearing Scoop cache - WingetUI"), RunAsAdmin: true); }; - ExtraSettingsCards[Tools.App.Scoop].Add(Scoop_Install); - ExtraSettingsCards[Tools.App.Scoop].Add(Scoop_Uninstall); - ExtraSettingsCards[Tools.App.Scoop].Add(Scoop_ResetAppCache); + ExtraSettingsCards[MainApp.Scoop].Add(Scoop_Install); + ExtraSettingsCards[MainApp.Scoop].Add(Scoop_Uninstall); + ExtraSettingsCards[MainApp.Scoop].Add(Scoop_ResetAppCache); - CheckboxCard Chocolatey_SystemChoco = new() { Text = Tools.AutoTranslated("Use system Chocolatey"), SettingName = "UseSystemChocolatey" }; + CheckboxCard Chocolatey_SystemChoco = new() { Text = CoreTools.AutoTranslated("Use system Chocolatey"), SettingName = "UseSystemChocolatey" }; Chocolatey_SystemChoco.StateChanged += (s, e) => { - PackageManagerExpanders[Tools.App.Choco].ShowRestartRequiredBanner(); + PackageManagerExpanders[MainApp.Choco].ShowRestartRequiredBanner(); }; - ExtraSettingsCards[Tools.App.Choco].Add(Chocolatey_SystemChoco); + ExtraSettingsCards[MainApp.Choco].Add(Chocolatey_SystemChoco); - foreach (PackageManager Manager in Tools.App.PackageManagerList) + foreach (PackageManager Manager in MainApp.Instance.PackageManagerList) { SettingsEntry ManagerExpander = new() @@ -179,7 +179,7 @@ public SettingsInterface() TextBlock LongVersion = new(); HyperlinkButton ShowVersionButton = new(); - ShowVersionButton.Content = Tools.Translate("Expand version"); + ShowVersionButton.Content = CoreTools.Translate("Expand version"); ShowVersionButton.Visibility = Visibility.Collapsed; ManagerStatus.ActionButton = ShowVersionButton; ShowVersionButton.Click += (s, e) => { SetManagerStatus(Manager, true); }; @@ -197,12 +197,12 @@ void SetManagerStatus(PackageManager Manager, bool ShowVersion = false) if (Manager.IsEnabled() && Manager.Status.Found) { ManagerStatus.Severity = InfoBarSeverity.Success; - ManagerStatus.Title = Tools.Translate("{pm} is enabled and ready to go").Replace("{pm}", Manager.Name); + ManagerStatus.Title = CoreTools.Translate("{pm} is enabled and ready to go").Replace("{pm}", Manager.Name); if (!Manager.Status.Version.Contains("\n")) - ManagerStatus.Message = Tools.Translate("{pm} version:").Replace("{pm}", Manager.Name) + " " + Manager.Status.Version; + ManagerStatus.Message = CoreTools.Translate("{pm} version:").Replace("{pm}", Manager.Name) + " " + Manager.Status.Version; else if (ShowVersion) { - ManagerStatus.Message = Tools.Translate("{pm} version:").Replace("{pm}", Manager.Name); + ManagerStatus.Message = CoreTools.Translate("{pm} version:").Replace("{pm}", Manager.Name); LongVersion.Visibility = Visibility.Visible; } else @@ -215,14 +215,14 @@ void SetManagerStatus(PackageManager Manager, bool ShowVersion = false) else if (Manager.IsEnabled() && !Manager.Status.Found) { ManagerStatus.Severity = InfoBarSeverity.Error; - ManagerStatus.Title = Tools.Translate("{pm} was not found!").Replace("{pm}", Manager.Name); - ManagerStatus.Message = Tools.Translate("You may need to install {pm} in order to use it with WingetUI.").Replace("{pm}", Manager.Name); + ManagerStatus.Title = CoreTools.Translate("{pm} was not found!").Replace("{pm}", Manager.Name); + ManagerStatus.Message = CoreTools.Translate("You may need to install {pm} in order to use it with WingetUI.").Replace("{pm}", Manager.Name); } else if (!Manager.IsEnabled()) { ManagerStatus.Severity = InfoBarSeverity.Informational; - ManagerStatus.Title = Tools.Translate("{pm} is disabled").Replace("{pm}", Manager.Name); - ManagerStatus.Message = Tools.Translate("Enable it to install packages from {pm}.").Replace("{pm}", Manager.Name); + ManagerStatus.Title = CoreTools.Translate("{pm} is disabled").Replace("{pm}", Manager.Name); + ManagerStatus.Message = CoreTools.Translate("Enable it to install packages from {pm}.").Replace("{pm}", Manager.Name); } } @@ -240,7 +240,7 @@ void SetManagerStatus(PackageManager Manager, bool ShowVersion = false) }; ManagerSwitch.Toggled += (s, e) => { - Tools.SetSettings("Disable" + Manager.Name, !ManagerSwitch.IsOn); + Settings.Set("Disable" + Manager.Name, !ManagerSwitch.IsOn); SetManagerStatus(Manager); EnableOrDisableEntries(); }; @@ -259,39 +259,39 @@ void EnableOrDisableEntries() } } - + index = 0; SettingsCard ManagerPath = new() { Description = Manager.Status.ExecutablePath + " " + Manager.Properties.ExecutableCallArgs, IsClickEnabled = true }; ManagerPath.ActionIcon = new SymbolIcon(Symbol.Copy); ManagerPath.Click += (s, e) => { - WindowsClipboard.SetText(ManagerPath.Description.ToString()); + WindowsClipboard.SetText(ManagerPath.Description.ToString() ?? ""); }; ExtraSettingsCards[Manager].Insert(index++, ManagerPath); CheckboxCard AdminCard = new() { - Text = Tools.AutoTranslated("Always run {pm} operations with administrator rights"), + Text = CoreTools.AutoTranslated("Always run {pm} operations with administrator rights"), SettingName = "AlwaysElevate" + Manager.Name, }; - AdminCard._checkbox.Content = AdminCard._checkbox.Content.ToString().Replace("{pm}", Manager.Name); + AdminCard._checkbox.Content = (AdminCard._checkbox.Content.ToString() ?? "").Replace("{pm}", Manager.Name); ExtraSettingsCards[Manager].Insert(index++, AdminCard); CheckboxCard ParallelCard = new() { - Text = Tools.AutoTranslated("Allow {pm} operations to be performed in parallel"), + Text = CoreTools.AutoTranslated("Allow {pm} operations to be performed in parallel"), SettingName = "AllowParallelInstallsForManager" + Manager.Name, }; - ParallelCard._checkbox.Content = ParallelCard._checkbox.Content.ToString().Replace("{pm}", Manager.Name); + ParallelCard._checkbox.Content = (ParallelCard._checkbox.Content.ToString() ?? "").Replace("{pm}", Manager.Name); ExtraSettingsCards[Manager].Insert(index++, ParallelCard); - if (Manager is PackageManagerWithSources) + if (Manager.Capabilities.SupportsCustomSources) { SettingsCard SourceManagerCard = new(); SourceManagerCard.Resources["SettingsCardLeftIndention"] = 10; - SourceManager SourceManager = new(Manager as PackageManagerWithSources); + SourceManager SourceManager = new(Manager); SourceManagerCard.Description = SourceManager; ExtraSettingsCards[Manager].Insert(index++, SourceManagerCard); } @@ -311,7 +311,7 @@ void EnableOrDisableEntries() public MainWindow GetWindow() { - return Tools.App.MainWindow; + return MainApp.Instance.MainWindow; } public int GetHwnd() { @@ -324,15 +324,15 @@ private void OpenWelcomeWizard(object sender, Interface.Widgets.ButtonCardEventA private void ImportSettings(object sender, Interface.Widgets.ButtonCardEventArgs e) { - var picker = new UniGetUI.ExternalLibraries.Pickers.FileOpenPicker(Tools.App.MainWindow.GetWindowHandle()); - var file = picker.Show(new List { "*.json" }); + ExternalLibraries.Pickers.FileOpenPicker picker = new(MainApp.Instance.MainWindow.GetWindowHandle()); + string file = picker.Show(new List { "*.json" }); if (file != string.Empty) { ResetWingetUI(sender, e); - var settings = JsonConvert.DeserializeObject>(File.ReadAllText(file)); - foreach(var entry in settings) - Tools.SetSettingsValue(entry.Key, entry.Value); + Dictionary settings = JsonConvert.DeserializeObject>(File.ReadAllText(file)) ?? new(); + foreach (KeyValuePair entry in settings) + Settings.SetValue(entry.Key, entry.Value); GeneralSettingsExpander.ShowRestartRequiredBanner(); } @@ -342,17 +342,17 @@ private async void ExportSettings(object sender, Interface.Widgets.ButtonCardEve { try { - var picker = new UniGetUI.ExternalLibraries.Pickers.FileSavePicker(Tools.App.MainWindow.GetWindowHandle()); - var file = picker.Show(new List { "*.json" }, Tools.Translate("WingetUI Settings") + ".json"); + ExternalLibraries.Pickers.FileSavePicker picker = new(MainApp.Instance.MainWindow.GetWindowHandle()); + string file = picker.Show(new List { "*.json" }, CoreTools.Translate("WingetUI Settings") + ".json"); if (file != String.Empty) { - Tools.App.MainWindow.ShowLoadingDialog(Tools.Translate("Please wait...")); + MainApp.Instance.MainWindow.ShowLoadingDialog(CoreTools.Translate("Please wait...")); - var IgnoredSettings = new string[] { "OperationHistory", "CurrentSessionToken", "OldWindowGeometry" }; + string[] IgnoredSettings = new string[] { "OperationHistory", "CurrentSessionToken", "OldWindowGeometry" }; Dictionary settings = new(); - foreach (var path in Directory.EnumerateFiles(CoreData.UniGetUIDataDirectory)) + foreach (string path in Directory.EnumerateFiles(CoreData.UniGetUIDataDirectory)) { if (Path.GetFileName(path).Contains('.') || IgnoredSettings.Contains(Path.GetFileName(path))) continue; @@ -361,13 +361,14 @@ private async void ExportSettings(object sender, Interface.Widgets.ButtonCardEve await File.WriteAllTextAsync(file, JsonConvert.SerializeObject(settings)); - Tools.App.MainWindow.HideLoadingDialog(); + MainApp.Instance.MainWindow.HideLoadingDialog(); } } catch (Exception ex) { - Tools.App.MainWindow.HideLoadingDialog(); - AppTools.Log(ex); + MainApp.Instance.MainWindow.HideLoadingDialog(); + Logger.Error("An error occurred when exporting settings"); + Logger.Error(ex); } } @@ -376,14 +377,15 @@ private void ResetWingetUI(object sender, Interface.Widgets.ButtonCardEventArgs { try { - foreach (var path in Directory.EnumerateFiles(CoreData.UniGetUIDataDirectory)) + foreach (string path in Directory.EnumerateFiles(CoreData.UniGetUIDataDirectory)) { File.Delete(path); } } catch (Exception ex) { - AppTools.Log(ex); + Logger.Error("An error occurred when resetting UniGetUI"); + Logger.Error(ex); } GeneralSettingsExpander.ShowRestartRequiredBanner(); } @@ -406,18 +408,18 @@ private void ThemeSelector_ValueChanged(object sender, Interface.Widgets.ComboCa private void ResetBackupPath_Click(object sender, dynamic e) { BackupDirectoryLabel.Text = CoreData.UniGetUI_DefaultBackupDirectory; - Tools.SetSettings("ChangeBackupOutputDirectory", false); + Settings.Set("ChangeBackupOutputDirectory", false); ResetBackupDirectory.IsEnabled = false; } private void ChangeBackupDirectory_Click(object sender, dynamic e) { - var openPicker = new UniGetUI.ExternalLibraries.Pickers.FolderPicker(Tools.App.MainWindow.GetWindowHandle()); + ExternalLibraries.Pickers.FolderPicker openPicker = new(MainApp.Instance.MainWindow.GetWindowHandle()); string folder = openPicker.Show(); if (folder != String.Empty) { - Tools.SetSettingsValue("ChangeBackupOutputDirectory", folder); + Settings.SetValue("ChangeBackupOutputDirectory", folder); BackupDirectoryLabel.Text = folder; ResetBackupDirectory.IsEnabled = true; } @@ -430,7 +432,7 @@ private void ChangeBackupDirectory_Click(object sender, dynamic e) private void OpenBackupPath_Click(object sender, RoutedEventArgs e) { - string directory = Tools.GetSettingsValue("ChangeBackupOutputDirectory"); + string directory = Settings.GetValue("ChangeBackupOutputDirectory"); if (directory == "") directory = CoreData.UniGetUI_DefaultBackupDirectory; @@ -478,21 +480,22 @@ private void ResetIconCache_Click(object sender, ButtonCardEventArgs e) } catch (Exception ex) { - AppTools.Log(ex); + Logger.Error("An error occurred while deleting icon cache"); + Logger.Error(ex); } ExperimentalSettingsExpander.ShowRestartRequiredBanner(); } private async void DoBackup_Click(object sender, ButtonCardEventArgs e) { - Tools.App.MainWindow.ShowLoadingDialog(Tools.Translate("Performing backup, please wait...")); - await Tools.App.MainWindow.NavigationPage.InstalledPage.BackupPackages(); - Tools.App.MainWindow.HideLoadingDialog(); + MainApp.Instance.MainWindow.ShowLoadingDialog(CoreTools.Translate("Performing backup, please wait...")); + await MainApp.Instance.MainWindow.NavigationPage.InstalledPage.BackupPackages(); + MainApp.Instance.MainWindow.HideLoadingDialog(); } private void EditAutostartSettings_Click(object sender, ButtonCardEventArgs e) { - Process p = new Process(); + Process p = new(); p.StartInfo = new ProcessStartInfo() { FileName = "cmd.exe", diff --git a/src/UniGetUI/Interface/SoftwarePages/SoftwareUpdates.xaml b/src/UniGetUI/Interface/SoftwarePages/AbstractPackagesPage.xaml similarity index 92% rename from src/UniGetUI/Interface/SoftwarePages/SoftwareUpdates.xaml rename to src/UniGetUI/Interface/SoftwarePages/AbstractPackagesPage.xaml index 655ab1e99..4a0aff0eb 100644 --- a/src/UniGetUI/Interface/SoftwarePages/SoftwareUpdates.xaml +++ b/src/UniGetUI/Interface/SoftwarePages/AbstractPackagesPage.xaml @@ -1,19 +1,18 @@ + xmlns:animations="using:CommunityToolkit.WinUI.Animations" xmlns:pkgClasses="using:UniGetUI.PackageEngine.PackageClasses"> @@ -295,9 +294,6 @@ - - - - + @@ -514,8 +510,8 @@ - - + + @@ -565,13 +561,13 @@ ToolTipService.ToolTip="{x:Bind Version}" /> @@ -618,14 +614,14 @@ - @@ -635,11 +631,11 @@ - - - + @@ -668,7 +664,7 @@ - + - + @@ -756,7 +752,7 @@ @@ -767,27 +763,32 @@ - - + + - + - + - + @@ -827,8 +828,8 @@ - - + + @@ -837,69 +838,14 @@ + + + diff --git a/src/UniGetUI/Interface/SoftwarePages/AbstractPackagesPage.xaml.cs b/src/UniGetUI/Interface/SoftwarePages/AbstractPackagesPage.xaml.cs new file mode 100644 index 000000000..c9b92168c --- /dev/null +++ b/src/UniGetUI/Interface/SoftwarePages/AbstractPackagesPage.xaml.cs @@ -0,0 +1,732 @@ +using CommunityToolkit.WinUI.Notifications; +using Microsoft.UI.Input; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using System.Threading.Tasks; +using UniGetUI.Core; +using UniGetUI.Core.Classes; +using UniGetUI.Core.Data; +using UniGetUI.Interface.Enums; +using UniGetUI.Interface.Widgets; +using UniGetUI.PackageEngine.Classes; +using UniGetUI.PackageEngine.Operations; +using UniGetUI.Core.Logging; +using Windows.UI.Core; +using UniGetUI.Core.SettingsEngine; +using UniGetUI.PackageEngine.PackageClasses; +using UniGetUI.PackageEngine.Enums; +using UniGetUI.PackageEngine.ManagerClasses.Manager; +using UniGetUI.PackageEngine.Classes.Manager.ManagerHelpers; +using UniGetUI.Core.Tools; + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. + +namespace UniGetUI.Interface +{ + + public abstract partial class AbstractPackagesPage : Page + { + protected bool DISABLE_AUTOMATIC_PACKAGE_LOAD_ON_START = false; + protected bool MEGA_QUERY_BOX_ENABLED = false; + protected bool SHOW_LAST_CHECKED_TIME = false; + + protected enum ReloadReason + { + FirstRun, + Automated, + Manual, + External + } + + protected enum FilterReason + { + FirstRun, + PackagesChanged, + FilterChanged, + } + + protected OperationType PageRole = OperationType.Install; + + public DateTime LastPackageLoadTime { get; protected set; } + + public ObservableCollection Packages = new(); + public SortableObservableCollection FilteredPackages = new() { SortingSelector = (a) => (a.Name) }; + protected List UsedManagers = new(); + protected Dictionary> UsedSourcesForManager = new(); + protected Dictionary RootNodeForManager = new(); + protected Dictionary NodesForSources = new(); + + public int NewVersionLabelWidth { get { return RoleIsUpdateLike ? 125 : 0; } } + public int NewVersionIconWidth { get { return RoleIsUpdateLike ? 24 : 0; } } + + private TreeViewNode LocalPackagesNode; + + public InfoBadge? ExternalCountBadge; + + protected bool Initialized = false; + private bool AllSelected = true; + + + int lastSavedWidth = 0; + + protected string PAGE_NAME = "UNDEFINED"; + public string InstantSearchSettingString { get { return $"DisableInstantSearch{PAGE_NAME}Tab"; } } + public string SidepalWidthSettingString { get { return $"SidepanelWidth{PAGE_NAME}Page"; } } + + public bool RoleIsUpdateLike { get { return PageRole == OperationType.Update; } } + + + + protected abstract Task LoadPackagesFromManager(PackageManager manager); + protected abstract Task IsPackageValid(Package package); + protected abstract Task WhenAddingPackage(Package package); + protected abstract Task WhenPackagesLoaded(ReloadReason reason); + protected abstract void WhenPackageCountUpdated(); + protected abstract void WhenShowingContextMenu(Package package); + public abstract void GenerateToolBar(); + public abstract BetterMenu GenerateContextMenu(); + public abstract void GenerateUIText(); + + protected string NoPackages_BackgroundText = CoreTools.Translate("Hooray! No updates were found."); + protected string NoPackages_SourcesText = CoreTools.Translate("Everything is up to date"); + protected string NoPackages_SubtitleMainText = CoreTools.Translate("Everything is up to date"); + + + protected string NoPackages_SubtitleText + { + get + { + return NoPackages_SubtitleMainText + " " + + (SHOW_LAST_CHECKED_TIME ? CoreTools.Translate("(Last checked: {0})").Replace("{0}", LastPackageLoadTime.ToString()) : ""); + } + } + + protected string NoMatches_BackgroundText = CoreTools.Translate("No results were found matching the input criteria"); + protected string NoMatches_SourcesText = CoreTools.Translate("No packages were found"); + protected string NoMatches_SubtitleText + { + get + { + return CoreTools.Translate("{0} packages were found, {1} of which match the specified filters.") + .Replace("{0}", Packages.Count.ToString()) + .Replace("{1}", (FilteredPackages.Count()).ToString()) + " " + + (SHOW_LAST_CHECKED_TIME? CoreTools.Translate("(Last checked: {0})").Replace("{0}", LastPackageLoadTime.ToString()): ""); + } + } + protected string FoundPackages_SubtitleText { get { return NoMatches_SubtitleText; } } + protected string MainTitleText = CoreTools.AutoTranslated("Software Updates"); + protected string MainTitleGlyph = "\uE895"; + + protected string MainSubtitle_StillLoading = CoreTools.Translate("Loading..."); + + + + public AbstractPackagesPage() + { + InitializeComponent(); + LastPackageLoadTime = DateTime.Now; + QueryBothRadio.IsChecked = true; + QueryOptionsGroup.SelectedIndex = 2; + LoadingProgressBar.Visibility = Visibility.Collapsed; + Initialized = true; + ReloadButton.Click += async (s, e) => { await LoadPackages(); }; + FindButton.Click += (s, e) => { + MegaQueryBlockGrid.Visibility = Visibility.Collapsed; + FilterPackages(QueryBlock.Text); + }; + QueryBlock.TextChanged += (s, e) => { if (InstantSearchCheckbox.IsChecked == true) FilterPackages(QueryBlock.Text); }; + QueryBlock.KeyUp += (s, e) => { + if (e.Key == Windows.System.VirtualKey.Enter) + { + MegaQueryBlockGrid.Visibility = Visibility.Collapsed; + FilterPackages(QueryBlock.Text); + } + }; + + QueryBlock.TextChanged += (s, e) => { + if (MEGA_QUERY_BOX_ENABLED && QueryBlock.Text.Trim() == "") + { + MegaQueryBlockGrid.Visibility = Visibility.Visible; + BackgroundText.Visibility = Visibility.Collapsed; + ClearPackageList(); + UpdatePackageCount(); + MegaQueryBlock.Focus(FocusState.Programmatic); + MegaQueryBlock.Text = ""; + } + }; + + MegaQueryBlock.KeyUp += (s, e) => { + if (e.Key == Windows.System.VirtualKey.Enter) + { + MegaQueryBlockGrid.Visibility = Visibility.Collapsed; + QueryBlock.Text = MegaQueryBlock.Text.Trim(); + FilterPackages(QueryBlock.Text); + } + }; + + MegaFindButton.Click += (s, e) => + { + MegaQueryBlockGrid.Visibility = Visibility.Collapsed; + QueryBlock.Text = MegaQueryBlock.Text.Trim(); + FilterPackages(QueryBlock.Text); + }; + + + LocalPackagesNode = new TreeViewNode() { Content = CoreTools.Translate("Local"), IsExpanded = false }; + + SourcesTreeView.Tapped += (s, e) => + { + if (e.OriginalSource != null && (e.OriginalSource as FrameworkElement)?.DataContext != null) + { + if ((e.OriginalSource as FrameworkElement)?.DataContext is TreeViewNode) + { + TreeViewNode? node = (e.OriginalSource as FrameworkElement)?.DataContext as TreeViewNode; + if (node == null) + return; + if (SourcesTreeView.SelectedNodes.Contains(node)) + SourcesTreeView.SelectedNodes.Remove(node); + else + SourcesTreeView.SelectedNodes.Add(node); + FilterPackages(QueryBlock.Text.Trim()); + } + } + }; + + SourcesTreeView.RightTapped += (s, e) => + { + if (e.OriginalSource != null && (e.OriginalSource as FrameworkElement)?.DataContext != null) + { + if ((e.OriginalSource as FrameworkElement)?.DataContext is TreeViewNode) + { + TreeViewNode? node = (e.OriginalSource as FrameworkElement)?.DataContext as TreeViewNode; + if (node == null) + return; + + SourcesTreeView.SelectedNodes.Clear(); + SourcesTreeView.SelectedNodes.Add(node); + FilterPackages(QueryBlock.Text.Trim()); + } + } + }; + + PackageList.DoubleTapped += (s, e) => + { + ShowDetailsForPackage(PackageList.SelectedItem as Package); + }; + + PackageList.RightTapped += (s, e) => + { + if (e.OriginalSource is FrameworkElement element) + { + try + { + if (element.DataContext != null && element.DataContext is Package package) + { + PackageList.SelectedItem = package; + WhenShowingContextMenu(package); + (PackageList.ContextFlyout as BetterMenu)?.ShowAt(PackageList, e.GetPosition(PackageList)); + } + } + catch (Exception ex) + { + Logger.Warn(ex); + } + } + }; + + PackageList.KeyUp += (s, e) => + { + if (e.Key == Windows.System.VirtualKey.Enter && PackageList.SelectedItem != null) + { + if (InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Menu).HasFlag(CoreVirtualKeyStates.Down)) + ShowInstallationOptionsForPackage(PackageList.SelectedItem as Package); + else if (InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down)) + PerformMainPackageAction(PackageList.SelectedItem as Package); + else + ShowDetailsForPackage(PackageList.SelectedItem as Package); + } + else if (e.Key == Windows.System.VirtualKey.A && InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down)) + { + if (AllSelected) + ClearItemSelection(); + else + SelectAllItems(); + } + else if (e.Key == Windows.System.VirtualKey.Space && PackageList.SelectedItem != null) + { + var package = PackageList.SelectedItem as Package; + if(package != null) + package.IsChecked = !package.IsChecked; + } + else if (e.Key == Windows.System.VirtualKey.F5) + { + _ = LoadPackages(ReloadReason.Manual); + } + else if (e.Key == Windows.System.VirtualKey.F1) + { + MainApp.Instance.MainWindow.NavigationPage.ShowHelp(); + } + else if (e.Key == Windows.System.VirtualKey.F && InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down)) + { + QueryBlock.Focus(FocusState.Programmatic); + } + }; + + int width = 250; + try + { + width = int.Parse(Settings.GetValue(SidepalWidthSettingString)); + } + catch + { + Settings.SetValue(SidepalWidthSettingString, "250"); + } + BodyGrid.ColumnDefinitions.ElementAt(0).Width = new GridLength(width); + + + GenerateToolBar(); + LoadInterface(); + _ = LoadPackages(ReloadReason.FirstRun); + + QueryBlock.PlaceholderText = CoreTools.Translate("Search for packages"); + MegaQueryBlock.PlaceholderText = CoreTools.Translate("Search for packages"); + } + + protected void AddPackageToSourcesList(Package package) + { + if (!Initialized) + return; + ManagerSource source = package.Source; + if (!UsedManagers.Contains(source.Manager)) + { + UsedManagers.Add(source.Manager); + TreeViewNode Node; + Node = new TreeViewNode() { Content = source.Manager.Name + " .", IsExpanded = false }; + SourcesTreeView.RootNodes.Add(Node); + + // Smart way to decide whether to check a source or not. + // - Always check a source by default if no sources are present + // - Otherwise, Check a source only if half of the sources have already been checked + if(SourcesTreeView.RootNodes.Count == 0) + SourcesTreeView.SelectedNodes.Add(Node); + else if (SourcesTreeView.SelectedNodes.Count >= SourcesTreeView.RootNodes.Count/2) + SourcesTreeView.SelectedNodes.Add(Node); + + + RootNodeForManager.Add(source.Manager, Node); + UsedSourcesForManager.Add(source.Manager, new List()); + SourcesPlaceholderText.Visibility = Visibility.Collapsed; + SourcesTreeViewGrid.Visibility = Visibility.Visible; + } + + if ((!UsedSourcesForManager.ContainsKey(source.Manager) || !UsedSourcesForManager[source.Manager].Contains(source)) && source.Manager.Capabilities.SupportsCustomSources) + { + UsedSourcesForManager[source.Manager].Add(source); + TreeViewNode item = new() { Content = source.Name + " ." }; + NodesForSources.Add(source, item); + + if (source.IsVirtualManager) + { + LocalPackagesNode.Children.Add(item); + if (!SourcesTreeView.RootNodes.Contains(LocalPackagesNode)) + { + SourcesTreeView.RootNodes.Add(LocalPackagesNode); + SourcesTreeView.SelectedNodes.Add(LocalPackagesNode); + } + } + else + RootNodeForManager[source.Manager].Children.Add(item); + } + } + + private void PackageContextMenu_AboutToShow(object sender, Package package) + { + if (!Initialized) + return; + PackageList.SelectedItem = package; + } + + private void FilterOptionsChanged(object sender, RoutedEventArgs e) + { + if (!Initialized) + return; + FilterPackages(QueryBlock.Text); + } + + private void InstantSearchValueChanged(object sender, RoutedEventArgs e) + { + if (!Initialized) + return; + Settings.Set(InstantSearchSettingString, InstantSearchCheckbox.IsChecked == false); + } + private void SourcesTreeView_SelectionChanged(TreeView sender, TreeViewSelectionChangedEventArgs args) + { + FilterPackages(QueryBlock.Text); + } + + /* + * + * + * DO NOT MODIFY THE UPPER PART OF THIS FILE + * + * + */ + + public async Task LoadPackages() + { + await LoadPackages(ReloadReason.External); + } + + protected void ClearPackageList() + { + Packages.Clear(); + FilteredPackages.Clear(); + UsedManagers.Clear(); + SourcesTreeView.RootNodes.Clear(); + UsedSourcesForManager.Clear(); + RootNodeForManager.Clear(); + NodesForSources.Clear(); + } + + protected async Task LoadPackages(ReloadReason reason) + { + if (!Initialized) + return; + + if (LoadingProgressBar.Visibility == Visibility.Visible) + return; // If already loading, don't load again + + if (DISABLE_AUTOMATIC_PACKAGE_LOAD_ON_START && reason == ReloadReason.FirstRun) + return; + + MainSubtitle.Text = CoreTools.Translate("Loading..."); + BackgroundText.Text = CoreTools.AutoTranslated("Loading..."); + BackgroundText.Visibility = Visibility.Visible; + LoadingProgressBar.Visibility = Visibility.Visible; + SourcesPlaceholderText.Visibility = Visibility.Visible; + SourcesPlaceholderText.Text = CoreTools.AutoTranslated("Loading..."); + SourcesTreeViewGrid.Visibility = Visibility.Collapsed; + + ClearPackageList(); + + await Task.Delay(100); + + List> tasks = new(); + + foreach (PackageManager manager in MainApp.Instance.PackageManagerList) + { + if (manager.IsEnabled() && manager.Status.Found) + { + Task task = LoadPackagesFromManager(manager); + tasks.Add(task); + } + } + + while (tasks.Count > 0) + { + foreach (Task task in tasks.ToArray()) + { + if (!task.IsCompleted) + await Task.Delay(100); + + if (task.IsCompleted) + { + if (task.IsCompletedSuccessfully) + { + int InitialCount = Packages.Count; + foreach (Package package in task.Result) + { + if (!await IsPackageValid(package)) + continue; + + Packages.Add(package); + await WhenAddingPackage(package); + AddPackageToSourcesList(package); + } + if (InitialCount < Packages.Count) + FilterPackages(QueryBlock.Text.Trim(), StillLoading: true); + } + tasks.Remove(task); + } + } + } + + LoadingProgressBar.Visibility = Visibility.Collapsed; + LastPackageLoadTime = DateTime.Now; + FilterPackages(QueryBlock.Text, StillLoading: false); + await WhenPackagesLoaded(reason); + } + + public void FilterPackages(string query, bool StillLoading = false) + { + if (!Initialized) + return; + + FilteredPackages.Clear(); + + List VisibleSources = new(); + List VisibleManagers = new(); + + if (SourcesTreeView.SelectedNodes.Count > 0) + { + foreach (TreeViewNode node in SourcesTreeView.SelectedNodes) + { + if (NodesForSources.ContainsValue(node)) + VisibleSources.Add(NodesForSources.First(x => x.Value == node).Key); + else if (RootNodeForManager.ContainsValue(node)) + VisibleManagers.Add(RootNodeForManager.First(x => x.Value == node).Key); + } + } + + Package[] MatchingList; + + Func CaseFunc; + if (UpperLowerCaseCheckbox.IsChecked == true) + CaseFunc = (x) => { return x; }; + else + CaseFunc = (x) => { return x.ToLower(); }; + + Func CharsFunc; + if (IgnoreSpecialCharsCheckbox.IsChecked == true) + CharsFunc = (x) => + { + string temp_x = CaseFunc(x).Replace("-", "").Replace("_", "").Replace(" ", "").Replace("@", "").Replace("\t", "").Replace(".", "").Replace(",", "").Replace(":", ""); + foreach (KeyValuePair entry in new Dictionary + { + {'a', "àáäâ"}, + {'e', "èéëê"}, + {'i', "ìíïî"}, + {'o', "òóöô"}, + {'u', "ùúüû"}, + {'y', "ýÿ"}, + {'c', "ç"}, + {'ñ', "n"}, + }) + { + foreach (char InvalidChar in entry.Value) + x = x.Replace(InvalidChar, entry.Key); + } + return temp_x; + }; + else + CharsFunc = (x) => { return CaseFunc(x); }; + + if (QueryIdRadio.IsChecked == true) + MatchingList = Packages.Where(x => CharsFunc(x.Name).Contains(CharsFunc(query))).ToArray(); + else if (QueryNameRadio.IsChecked == true) + MatchingList = Packages.Where(x => CharsFunc(x.Id).Contains(CharsFunc(query))).ToArray(); + else if (QueryBothRadio.IsChecked == true) + MatchingList = Packages.Where(x => CharsFunc(x.Name).Contains(CharsFunc(query)) | CharsFunc(x.Id).Contains(CharsFunc(query))).ToArray(); + else if (QueryExactMatch.IsChecked == true) + MatchingList = Packages.Where(x => CharsFunc(x.Name) == CharsFunc(query) | CharsFunc(x.Id) == CharsFunc(query)).ToArray(); + else // QuerySimilarResultsRadio == true + MatchingList = Packages.ToArray(); + + FilteredPackages.BlockSorting = true; + foreach (Package match in MatchingList) + { + if (VisibleManagers.Contains(match.Manager) || VisibleSources.Contains(match.Source)) + FilteredPackages.Add(match); + } + FilteredPackages.BlockSorting = false; + FilteredPackages.Sort(); + UpdatePackageCount(StillLoading); + } + + public void UpdatePackageCount(bool StillLoading = false) + { + if (FilteredPackages.Count() == 0) + { + if (!StillLoading) + { + if (Packages.Count() == 0) + { + BackgroundText.Text = NoPackages_BackgroundText; + SourcesPlaceholderText.Text = NoPackages_SourcesText; + MainSubtitle.Text = NoPackages_SubtitleText; + } + else + { + BackgroundText.Text = NoMatches_BackgroundText; + SourcesPlaceholderText.Text = NoMatches_SourcesText; + MainSubtitle.Text = NoMatches_SubtitleText; + } + BackgroundText.Visibility = Visibility.Visible; + } + else + { + BackgroundText.Text = MainSubtitle_StillLoading; + BackgroundText.Visibility = Packages.Count > 0 ? Visibility.Collapsed : Visibility.Visible; + MainSubtitle.Text = MainSubtitle_StillLoading; + } + + } + else + { + BackgroundText.Text = NoPackages_BackgroundText; + BackgroundText.Visibility = Packages.Count > 0? Visibility.Collapsed: Visibility.Visible; + MainSubtitle.Text = FoundPackages_SubtitleText; + } + + if (ExternalCountBadge != null) + { + ExternalCountBadge.Visibility = Packages.Count() == 0 ? Visibility.Collapsed : Visibility.Visible; + ExternalCountBadge.Value = Packages.Count(); + } + + if (MegaQueryBlockGrid.Visibility == Visibility.Visible) + BackgroundText.Visibility = Visibility.Collapsed; + + WhenPackageCountUpdated(); + } + + public void SortPackages(string Sorter) + { + if (!Initialized) + return; + + FilteredPackages.Descending = !FilteredPackages.Descending; + FilteredPackages.SortingSelector = (a) => + { + if (a.GetType()?.GetProperty(Sorter)?.GetValue(a) == null) + Logger.Warn("Sorter element is null on AbstractPackagePage"); + return a.GetType()?.GetProperty(Sorter)?.GetValue(a) ?? 0; + }; FilteredPackages.Sort(); + + if (FilteredPackages.Count > 0) + PackageList.ScrollIntoView(FilteredPackages[0]); + } + + public void LoadInterface() + { + if (!Initialized) + return; + + GenerateUIText(); + + if (MEGA_QUERY_BOX_ENABLED) + { + MegaQueryBlockGrid.Visibility = Visibility.Visible; + MegaQueryBlock.Focus(FocusState.Programmatic); + BackgroundText.Visibility = Visibility.Collapsed; + + } + + InstantSearchCheckbox.IsChecked = !Settings.Get(InstantSearchSettingString); + + MainTitle.Text = MainTitleText; + HeaderIcon.Glyph = MainTitleGlyph; + HeaderIcon.FontWeight = new Windows.UI.Text.FontWeight(700); + CheckboxHeader.Content = " "; + NameHeader.Content = CoreTools.Translate("Package Name"); + IdHeader.Content = CoreTools.Translate("Package ID"); + VersionHeader.Content = CoreTools.Translate("Version"); + NewVersionHeader.Content = CoreTools.Translate("New version"); + SourceHeader.Content = CoreTools.Translate("Source"); + + CheckboxHeader.Click += (s, e) => { SortPackages("IsCheckedAsString"); }; + NameHeader.Click += (s, e) => { SortPackages("Name"); }; + IdHeader.Click += (s, e) => { SortPackages("Id"); }; + VersionHeader.Click += (s, e) => { SortPackages("VersionAsFloat"); }; + NewVersionHeader.Click += (s, e) => { SortPackages("NewVersionAsFloat"); }; + SourceHeader.Click += (s, e) => { SortPackages("SourceAsString"); }; + PackageList.ContextFlyout = GenerateContextMenu(); + } + + + protected void SelectAllSourcesButton_Click(object sender, RoutedEventArgs e) + { + SourcesTreeView.SelectAll(); + } + + protected void ClearSourceSelectionButton_Click(object sender, RoutedEventArgs e) + { + SourcesTreeView.SelectedItems.Clear(); + FilterPackages(QueryBlock.Text.Trim()); + } + + protected async void ShowDetailsForPackage(Package? package) + { + if (package == null) + return; + + Logger.Warn(PageRole.ToString()); + await MainApp.Instance.MainWindow.NavigationPage.ShowPackageDetails(package, PageRole); + } + + protected void SharePackage(Package? package) + { + if(package == null) + return; + MainApp.Instance.MainWindow.SharePackage(package); + } + + protected async void ShowInstallationOptionsForPackage(Package? package) + { + if(package == null) + return; + + if (await MainApp.Instance.MainWindow.NavigationPage.ShowInstallationSettingsForPackageAndContinue(package, PageRole)) + PerformMainPackageAction(package); + } + + protected void SelectAllItems() + { + foreach (UpgradablePackage package in FilteredPackages) + package.IsChecked = true; + AllSelected = true; + } + + protected void ClearItemSelection() + { + foreach (UpgradablePackage package in FilteredPackages) + package.IsChecked = false; + AllSelected = false; + } + + public void RemoveCorrespondingPackages(Package foreignPackage) + { + foreach (Package package in Packages.ToArray()) + if (package == foreignPackage || package.Equals(foreignPackage)) + { + Packages.Remove(package); + package.Tag = PackageTag.Default; + if (FilteredPackages.Contains(package)) + FilteredPackages.Remove(package); + } + UpdatePackageCount(); + } + + private void SidepanelWidth_SizeChanged(object sender, SizeChangedEventArgs e) + { + if (e.NewSize.Width == ((int)(e.NewSize.Width / 10)) || e.NewSize.Width == 25) + return; + + lastSavedWidth = ((int)(e.NewSize.Width / 10)); + Settings.SetValue("SidepanelWidthUpdatesPage", ((int)e.NewSize.Width).ToString()); + foreach (UIElement control in SidePanelGrid.Children) + { + control.Visibility = e.NewSize.Width > 20 ? Visibility.Visible : Visibility.Collapsed; + } + } + + protected void PerformMainPackageAction(Package? package) + { + if(package == null) return; + + if (PageRole == OperationType.Install) + MainApp.Instance.AddOperationToList(new InstallPackageOperation(package)); + else if (PageRole == OperationType.Update) + MainApp.Instance.AddOperationToList(new UpdatePackageOperation(package)); + else // if (PageRole == OperationType.Uninstall) + MainApp.Instance.AddOperationToList(new UninstallPackageOperation(package)); + } + + } +} diff --git a/src/UniGetUI/Interface/SoftwarePages/DiscoverPackages.xaml b/src/UniGetUI/Interface/SoftwarePages/DiscoverPackages.xaml deleted file mode 100644 index 18fb0069b..000000000 --- a/src/UniGetUI/Interface/SoftwarePages/DiscoverPackages.xaml +++ /dev/null @@ -1,891 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -