diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 18f8434855..0000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,12 +0,0 @@ -# These are supported funding model platforms -github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: # Replace with a single Patreon username -open_collective: # Replace with a single Open Collective username -ko_fi: martinet101 -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -otechie: # Replace with a single Otechie username -lfx_crowdfunding: # Replace with a single LFX Crowdfunding project name e.g., cloud-foundry -custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/ISSUE_TEMPLATE/bug-issue.yml b/.github/ISSUE_TEMPLATE/bug-issue.yml index bfdd04c118..47850e7f8a 100644 --- a/.github/ISSUE_TEMPLATE/bug-issue.yml +++ b/.github/ISSUE_TEMPLATE/bug-issue.yml @@ -2,7 +2,6 @@ name: '🐞 Report a bug or an issue' description: Report issues or unexpected behaviors. title: "[BUG] (Enter your description here)" labels: ["bug"] -assignees: marticliment body: - type: checkboxes attributes: diff --git a/.github/ISSUE_TEMPLATE/enhancement-improvement.yml b/.github/ISSUE_TEMPLATE/enhancement-improvement.yml index 78592816d0..5774b2f2a7 100644 --- a/.github/ISSUE_TEMPLATE/enhancement-improvement.yml +++ b/.github/ISSUE_TEMPLATE/enhancement-improvement.yml @@ -2,7 +2,6 @@ name: '🚧 Suggest an improvement for an existing feature' description: Propose an improvement to UniGetUI. title: "[IMPROVEMENT] (Enter your description here)" labels: ["enhancement"] -assignees: marticliment body: - type: checkboxes attributes: @@ -11,9 +10,9 @@ body: options: - label: I have searched for my feature proposal and have not found a work-in-progress/duplicate/resolved/discarded issue. required: true - - label: This improvement refers to an existing feature. If you want to suggest a new feature, please use [this template](https://github.com/Devolutions/UniGetUI/issues/new?assignees=marticliment&labels=new-feature&projects=&template=feature-request.yml&title=%5BFEATURE+REQUEST%5D+%28Enter+your+description+here%29). + - label: This improvement refers to an existing feature. If you want to suggest a new feature, please use [this template](https://github.com/Devolutions/UniGetUI/issues/new?labels=new-feature&projects=&template=feature-request.yml&title=%5BFEATURE+REQUEST%5D+%28Enter+your+description+here%29). required: true - - label: This improvement is not a bug. If you want to report a bug, please use [this template](https://github.com/Devolutions/UniGetUI/issues/new?assignees=marticliment&labels=bug&projects=&template=bug-issue.yml&title=%5BBUG%5D+%28Enter+your+description+here%29). + - label: This improvement is not a bug. If you want to report a bug, please use [this template](https://github.com/Devolutions/UniGetUI/issues/new?labels=bug&projects=&template=bug-issue.yml&title=%5BBUG%5D+%28Enter+your+description+here%29). required: true - type: textarea attributes: diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml index 9271ddaa56..52f7f6dd3c 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.yml +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -2,7 +2,6 @@ name: 'πŸ’‘ Propose a new feature' description: Propose a new feature that could be useful in UniGetUI. title: "[FEATURE REQUEST] (Enter your description here)" labels: ["new-feature"] -assignees: marticliment body: - type: checkboxes attributes: @@ -11,7 +10,7 @@ body: options: - label: I have searched for my feature proposal and have not found a work-in-progress/duplicate/resolved/discarded issue. required: true - - label: This proposal is a completely new feature. If you want to suggest an improvement or an enhancement, please use [this template](https://github.com/Devolutions/UniGetUI/issues/new?assignees=marticliment&labels=enhancement&projects=&template=enhancement-improvement.yml&title=%5BENHANCEMENT%5D+%28Enter+your+description+here%29). + - label: This proposal is a completely new feature. If you want to suggest an improvement or an enhancement, please use [this template](https://github.com/Devolutions/UniGetUI/issues/new?labels=enhancement&projects=&template=enhancement-improvement.yml&title=%5BENHANCEMENT%5D+%28Enter+your+description+here%29). required: true - type: textarea attributes: diff --git a/.github/ISSUE_TEMPLATE/hard-crash.yml b/.github/ISSUE_TEMPLATE/hard-crash.yml index f6f2fc4d0b..7a1b4465b9 100644 --- a/.github/ISSUE_TEMPLATE/hard-crash.yml +++ b/.github/ISSUE_TEMPLATE/hard-crash.yml @@ -2,7 +2,6 @@ name: 'πŸš‘ Report a crash or a hang' description: UniGetUI is not launching, is hard-crashing, or is hanging at some point. title: "[CRASH] (Enter your description here)" labels: ["bug", "important"] -assignees: marticliment body: - type: checkboxes attributes: diff --git a/.github/ISSUE_TEMPLATE/widgets-issue.yml b/.github/ISSUE_TEMPLATE/widgets-issue.yml index 083f7f34ad..23fa40a1a1 100644 --- a/.github/ISSUE_TEMPLATE/widgets-issue.yml +++ b/.github/ISSUE_TEMPLATE/widgets-issue.yml @@ -2,7 +2,6 @@ name: '🧩 Report an issue with Widgets for UniGetUI' description: Report issues or unexpected behaviors with Widgets for UniGetUI. title: "[WIDGETS BUG] (Enter your description here)" labels: ["bug", "widgets-for-unigetui"] -assignees: marticliment body: - type: checkboxes attributes: diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 1a375e2e36..bf1a481c45 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -4,8 +4,7 @@ on: workflow_dispatch: inputs: version: - description: 'Release version (e.g. 3.3.7)' - default: "latest" + description: 'Release version (required, e.g. 2026.1.0)' required: true draft-release: description: 'Create the GitHub Release as a draft' @@ -63,14 +62,8 @@ jobs: } $PackageVersion = '${{ inputs.version }}' - if ([string]::IsNullOrEmpty($PackageVersion) -or $PackageVersion -eq 'latest') { - # Read version from SharedAssemblyInfo.cs - $match = Select-String -Path 'src/SharedAssemblyInfo.cs' -Pattern 'AssemblyInformationalVersion\("([^"]+)"\)' - if ($match) { - $PackageVersion = $match.Matches[0].Groups[1].Value - } else { - $PackageVersion = (Get-Date -Format "yyyy.M.d") + ".0" - } + if ([string]::IsNullOrWhiteSpace($PackageVersion)) { + throw "The workflow_dispatch version input is required." } echo "package-env=$PackageEnv" >> $Env:GITHUB_OUTPUT diff --git a/.github/workflows/translations-test.yml b/.github/workflows/translations-test.yml deleted file mode 100644 index fe6d965a9c..0000000000 --- a/.github/workflows/translations-test.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Translation integrity -on: - pull_request: - branches: [ "main" ] - - workflow_dispatch: - -jobs: - test-translations-and-approve: - if: contains(github.event.pull_request.labels.*.name, 'tolgee-ci') - runs-on: ubuntu-latest # For a list of available runner types, refer to - # https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on - - steps: - - name: Checkout the repository - id: checkout - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Test translations - id: tests - run: | - python scripts/verify_translations.py - - - name: Approve PR since checks are Ok - uses: hmarr/auto-approve-action@v4 - with: - review-message: "Tolgee-CI Changes have been approved since they passed testing" diff --git a/.github/workflows/update-icons.yaml b/.github/workflows/update-icons.yaml index f572197e36..a821a2922c 100644 --- a/.github/workflows/update-icons.yaml +++ b/.github/workflows/update-icons.yaml @@ -7,7 +7,7 @@ on: jobs: load-icons: - if: github.repository == 'marticliment/UniGetUI' + if: github.repository == 'Devolutions/UniGetUI' runs-on: windows-latest steps: diff --git a/.github/workflows/update-tolgee.yml b/.github/workflows/update-tolgee.yml deleted file mode 100644 index c8a7b9a5b2..0000000000 --- a/.github/workflows/update-tolgee.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Tolgee CI - -on: - schedule: - - cron: 0 0 */2 * * - workflow_dispatch: -jobs: - load-translations: - if: github.repository == 'marticliment/UniGetUI' - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Download Tolgee translations - env: - TOLGEE_KEY: "${{ secrets.TOLGEE_TOKEN }}" - run: | - cd scripts - python download_translations.py --autocommit - - - name: Create Pull Request - uses: peter-evans/create-pull-request@v8 - with: - delete-branch: true - base: main - token: "${{ secrets.REPO_SCOPED_TOKEN }}" - labels: tolgee-ci - title: "Load translations from Tolgee" - body: "" - assignees: marticliment - author: "MartΓ­ Climent " - commit-message: | - Load updated translations from Tolgee - Co-authored-by: MartΓ­ Climent from the multiverse <121457539+martinet101@users.noreply.github.com> - branch: pull-request/update-translation diff --git a/AGENTS.md b/AGENTS.md index acf1345332..f72a2c4e01 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -58,7 +58,7 @@ File-based settings via `Settings.Get(Settings.K.*)` / `Settings.Set(Settings.K. Use `Logger.Info()`, `Logger.Warn()`, `Logger.Error()`, `Logger.Debug()`, `Logger.ImportantInfo()` from `UniGetUI.Core.Logging`. Accepts both `string` and `Exception` parameters. ### Localization -Use `CoreTools.Translate("text")` for all user-facing strings. Parameterized: `CoreTools.Translate("{0} packages found", count)`. In XAML, use the `TranslatedTextBlock` control. Translation files are managed externally via Tolgee; Python scripts in `scripts/` handle download and verification. +Use `CoreTools.Translate("text")` for all user-facing strings. Parameterized: `CoreTools.Translate("{0} packages found", count)`. In XAML, use the `TranslatedTextBlock` control. Translation assets live under `src/UniGetUI.Core.LanguageEngine/Assets/`; do not assume Tolgee-based automation exists in this repository. ### Naming - Types, methods, properties: **PascalCase** diff --git a/README.md b/README.md index add744b335..52cf2a41e9 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Check out the [Supported Package Managers Table](#supported-package-managers) fo > **The official source repository is [https://github.com/Devolutions/UniGetUI](https://github.com/Devolutions/UniGetUI).**
> **Any other website should be considered unofficial, despite what they may say.** -πŸ”’ Found a security issue? Please report it via [our disclosure program](https://whitehub.net/programs/unigetui/) +πŸ”’ Found a security issue? Please report it via the [Devolutions security page](https://devolutions.net/security/) ## Project stewardship @@ -111,7 +111,7 @@ UniGetUI has a built-in autoupdater. However, it can also be updated like any ot **NOTE:** All package managers do support basic install, update, and uninstall processes, as well as checking for updates, finding new packages, and retrieving details from a package. - +![image](media/supported-managers.svg) βœ…: Supported on UniGetUI
β˜‘οΈ: Not directly supported but can be easily achieved
@@ -120,7 +120,7 @@ UniGetUI has a built-in autoupdater. However, it can also be updated like any ot
# Translating UniGetUI to other languages -To translate UniGetUI to other languages or to update an old translation, please see [Translating UniGetUI - UniGetUI Wiki](https://github.com/Devolutions/UniGetUI/wiki#translating-wingetui) for more info. +To translate UniGetUI to other languages or to update an old translation, please see [Translating UniGetUI - UniGetUI Wiki](https://github.com/Devolutions/UniGetUI/wiki/Translating-UniGetUI#translating-wingetui) for more info. ## Currently Supported languages diff --git a/SECURITY.md b/SECURITY.md index f05bcab618..43f97b7ec8 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,4 +2,4 @@ ## Reporting a Vulnerability -Found a security issue? Please report it via [our disclosure program](https://whitehub.net/programs/unigetui) +Found a security issue? Please report it via the [Devolutions security page](https://devolutions.net/security/). diff --git a/deep_clean.ps1 b/deep_clean.ps1 deleted file mode 100644 index cd08f59948..0000000000 --- a/deep_clean.ps1 +++ /dev/null @@ -1,10 +0,0 @@ -Get-ChildItem -Path . -Recurse -Directory -Force -Include bin, obj | -Where-Object { $_.FullName -notmatch 'choco-cli' } | -ForEach-Object { - Write-Host "Removing folder: $($_.FullName)" -ForegroundColor Yellow - Remove-Item -Path $_.FullName -Recurse -Force -} - -Write-Host "Cleanup completed." -ForegroundColor Green - -pause \ No newline at end of file diff --git a/media/supported-managers.svg b/media/supported-managers.svg new file mode 100644 index 0000000000..e218480459 --- /dev/null +++ b/media/supported-managers.svg @@ -0,0 +1,178 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
compatibility chartWINGETSCOOPCHOCONPMPIP.net toolPWSH 5pwsh 7CARGOVCPKG
INSTALL AS ADMINISTRATORβœ…βœ…βœ…βœ…βœ…βœ…βœ…βœ…βœ…βœ…
SKIP INTEGRITY CHECKSβœ…βœ…βœ…βŒβŒβŒβœ…βŒβœ…βŒ
INTERACTIVE INSTALLATIONβœ…βŒβœ…βŒβŒβŒβŒβŒβŒβŒ
CHOOSE VERSION TO INSTALLβœ…βŒβœ…βœ…βœ…βœ…βœ…βœ…βŒβŒ
PRERELEASE VERSIONSβ˜‘οΈβ˜‘οΈβœ…βŒβœ…βœ…βœ…βœ…βœ…βŒ
CHANGEΒ INSTALL ARCHITECTUREβœ…βœ…βœ…βŒβŒβœ…βŒβŒβŒβ˜‘οΈ
CHANGE INSTALL SCOPEβš οΈβœ…βŒβœ…βœ…βœ…βœ…βœ…βŒβ˜‘οΈ
CHANGEΒ INSTALL LOCATIONβš οΈβŒβŒβŒβŒβœ…βŒβŒβœ…βŒ
CUSTOM SOURCES/INDEXESβœ…βœ…βœ…βŒβŒβŒβœ…βœ…βŒβ˜‘οΈ
DOWNLOAD INSTALLERβš οΈβœ…βœ…βœ…βœ…βœ…βœ…βœ…βŒβŒ
SUPPORTED SINCE VERSION0.1.00.1.01.6.02.0.02.0.02.1.02.2.03.1.13.1.23.1.4
+
+
+
+
diff --git a/scripts/download_translations.py b/scripts/download_translations.py deleted file mode 100644 index a3728bc2bf..0000000000 --- a/scripts/download_translations.py +++ /dev/null @@ -1,186 +0,0 @@ -import json -import os -import sys -import time -import tolgee_requests -from Languages.LangData import * -import glob -import zipfile - -try: - - root_dir = os.path.join(os.path.dirname(__file__), "..") - os.chdir(root_dir) # move to root project - - sys.path.append("./") - - # Update contributors - os.system("python scripts/get_contributors.py") - - countOfChanges = len(os.popen("git status -s").readlines()) - - isAutoCommit = False - isSomeChanges = False - - if len(sys.argv) > 1: - if (sys.argv[1] == "--autocommit"): - isAutoCommit = True - else: - print("nocommit") - print(sys.argv[1]) - - os.chdir(os.path.normpath(os.path.join(root_dir, "src/UniGetUI.Core.LanguageEngine/Assets/Languages"))) - - print() - print("-------------------------------------------------------") - print() - print(" Downloading updated translations...") - - response = tolgee_requests.export() - if (not response.ok): - statusCode = response.status_code - print(f" Error {statusCode}: {response.text}") - if (statusCode == 403): - print(" APIKEY is probably wrong!") - exit(1) - with open("langs.zip", "wb") as f: - f.write(response.content) - langArchiveName = f.name - - print(" Download complete!") - print() - print("-------------------------------------------------------") - print() - print(" Extracting language files...") - - downloadedLanguages = [] - zip_file = zipfile.ZipFile(langArchiveName) - - for file in glob.glob('lang_*.json'): # If the downloaded zip file is valid, delete old language files and extract the new ones - os.remove(file) - - for name in zip_file.namelist(): - lang = os.path.splitext(name)[0] - if (lang in languageRemap): - lang = languageRemap[lang] - newFilename = f"lang_{lang}.json" - downloadedLanguages.append(lang) - - try: - zip_file.extract(name, "./") - os.replace(name, newFilename) - print(f" Extracted {newFilename}") - - except KeyError: - print(type(name)) - f = input(f" The file {name} was not expected to be in here. Please write the name for the file. It should follow the following structure: lang_[CODE].json: ") - zip_file.extract(f, "./") - os.replace(f, newFilename) - print(f" Extracted {f}") - zip_file.close() - downloadedLanguages.sort() - os.remove("langs.zip") - - print(" Process complete!") - print() - print("-------------------------------------------------------") - print() - print(" Generating translations file...") - - langPerc = {} - langCredits = {} - - for lang in downloadedLanguages: - with open(f"lang_{lang}.json", "r", encoding='utf-8') as f: - data = dict(json.load(f)) - c = 0 - a = 0 - for key, value in data.items(): - c += 1 - if (value is not None): - a += 1 - credits = [] - try: - if ("0 0 0 Contributors, please add your names/usernames separated by comas (for credit purposes). DO NOT Translate this entry" in data.keys()): - credits = getTranslatorsFromCredits(data["0 0 0 Contributors, please add your names/usernames separated by comas (for credit purposes). DO NOT Translate this entry"]) - else: - credits = getTranslatorsFromCredits(data["{0} {0} {0} Contributors, please add your names/usernames separated by comas (for credit purposes)"]) - except KeyError as e: - print(e) - print("Can't get translator list!") - langCredits[lang] = credits - percNum = a / c - perc = f"{percNum:.0%}" - if (perc == "100%" and percNum < 1): - perc = "99%" - if (perc == "100%" or lang == "en"): - continue - langPerc[lang] = perc - - if (isAutoCommit): - os.system("git add .") - countOfChanges = len(os.popen("git status -s").readlines()) - countOfChanges - isSomeChanges = True if countOfChanges > 0 else False - - if not isAutoCommit: - isSomeChanges = True - - # outputString = f""" - # Autogenerated file, do not modify it!!! - - # untranslatedPercentage = {json.dumps(langPerc, indent=2, ensure_ascii=False)} - - # languageCredits = {json.dumps(langCredits, indent=2, ensure_ascii=False)} - # """ - - 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.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.LanguageEngine/Core/Data/Translations.py")) - # with open(translations_filepath, "w", encoding="utf-8") as f: - # f.write(outputString.strip()) - - print(" Process complete!") - print() - print("-------------------------------------------------------") - print() - print(" Updating README.md...") - - time.sleep(1) - - # Generate language table - readmeFilename = os.path.join(root_dir, "README.md") - - with open(readmeFilename, "r+", encoding="utf-8") as f: - skip = False - data = "" - for line in f.readlines(): - if "" in line: - data += f'{line}{getMarkdownSupportLangs()}\nLast updated: {str(time.ctime(time.time()))}\n' - print(" Text modified") - skip = True - if "" in line: - skip = False - if (not skip): - data += line - if (isSomeChanges): - f.seek(0) - f.write(data) - f.truncate() - - print(" Process complete!") - print() - print("-------------------------------------------------------") - print() - - if (isAutoCommit): - if (not isSomeChanges): - os.system("git reset --hard") # prevent clean - -except Exception as e: - print(e) - print(e.__traceback__.tb_lineno) - os.system("pause") diff --git a/scripts/get_untranslatd_strings.cmd b/scripts/get_untranslatd_strings.cmd deleted file mode 100644 index 6c7d1fb955..0000000000 --- a/scripts/get_untranslatd_strings.cmd +++ /dev/null @@ -1,3 +0,0 @@ -@echo off -python translation_commands.py --print --online -pause \ No newline at end of file diff --git a/scripts/purge_tolgee_inactiveusers.py b/scripts/purge_tolgee_inactiveusers.py deleted file mode 100644 index cdc5f292a6..0000000000 --- a/scripts/purge_tolgee_inactiveusers.py +++ /dev/null @@ -1,123 +0,0 @@ -HOST = "tolgee.marticliment.com" -TOKEN = "" -import requests, json, getpass - -def Get_JWTToken(): - url = f"https://{HOST}/api/public/generatetoken" - payload = json.dumps({ - "username": input("Username: "), - "password": getpass.getpass(), - "otp": "string" - }) - headers = { - 'Content-Type': 'application/json', - 'Accept': 'application/json' - } - response = requests.request("POST", url, headers=headers, data=payload) - return (json.loads(response.text)["accessToken"]) - - -def Get_AllUsers(page: int = 0) -> list[dict]: - url = f"https://{HOST}/v2/administration/users" - - payload = {} - query = { - 'page': str(page) - } - headers = { - 'Accept': 'application/json', - 'Authorization': f'Bearer {TOKEN}', - } - - response = requests.request("GET", url, headers=headers, data=payload, params=query) - - if(response.status_code != 200): - print(response.text) - return [] - - - res_content = json.loads(response.text) - MAX_PAGE = int(res_content["page"]["totalPages"]) - - if page + 1 == MAX_PAGE: - print(f"USER: Loaded page {page + 1} out of {MAX_PAGE}") - return list(res_content["_embedded"]["users"]) - else: - print(f"USER: Loaded page {page + 1} out of {MAX_PAGE}") - return list(res_content["_embedded"]["users"]) + Get_AllUsers(page+1) - -def Get_AllUsersOnProject(project: int) ->list[dict]: - url = f"https://{HOST}/v2/projects/{project}/users" - - payload={} - query = { - 'page': "0", - 'size': "300", - } - headers = { - 'Accept': 'application/json', - 'Authorization': f'Bearer {TOKEN}', - } - - response = requests.request("GET", url, headers=headers, data=payload, params=query) - - if(response.status_code != 200): - print(response.text) - return [] - - res_content = json.loads(response.text) - print(f"PROJ: Loaded active users for project") - return list(res_content["_embedded"]["users"]) - - -def Delete_User(user_id: int) -> bool: - url = f"https://{HOST}/v2/administration/users/{user_id}" - - payload={} - headers = { - 'Authorization': f'Bearer {TOKEN}' - } - - response = requests.request("DELETE", url, headers=headers, data=payload) - - if(response.status_code != 200): - print(response.text) - - return response.status_code == 200 - - -print(f"Running for host {HOST}") - -TOKEN = Get_JWTToken() - -active_ids = [] -for user in Get_AllUsersOnProject(1): - active_ids.append(int(user["id"])) -active_ids.sort() - -ids = [] -for user in Get_AllUsers(): - ids.append(int(user["id"])) -ids.sort() - -INACTIVEs = [] -for user in ids: - if user not in active_ids: - INACTIVEs.append(user) -INACTIVEs.sort() - -total = len(INACTIVEs) -SKIP_VERIFICATIION = "" -input(f"Press to confirm deletion of {total} users (users in server are {len(ids)}, active users are {len(active_ids)}): ") - -for i in range(total): - user = INACTIVEs[i] - - if(user == "1"): - continue - - if SKIP_VERIFICATIION != "12": - SKIP_VERIFICATIION = input("About to delete User id=" + str(user) + ". Press intro to proceed, type 12 to run all: ") - - print(f"Deleting user id={user}, ({i+1} out of {total})... ", end="", flush=True) - print("SUCCESS" if Delete_User(user) else "FAILED") \ No newline at end of file diff --git a/scripts/purge_tolgee_inactiveusers_v2.py b/scripts/purge_tolgee_inactiveusers_v2.py deleted file mode 100644 index 7e9f113b87..0000000000 --- a/scripts/purge_tolgee_inactiveusers_v2.py +++ /dev/null @@ -1,183 +0,0 @@ -HOST = "tolgee.marticliment.com" -TOKEN = "" -import requests, json, getpass - -def Get_JWTToken(): - url = f"https://{HOST}/api/public/generatetoken" - payload = json.dumps({ - "username": input("Username: "), - "password": getpass.getpass(), - "otp": "string" - }) - headers = { - 'Content-Type': 'application/json', - 'Accept': 'application/json' - } - response = requests.request("POST", url, headers=headers, data=payload) - return (json.loads(response.text)["accessToken"]) - - -def Get_AllUsers(page: int = 0) -> list[dict]: - url = f"https://{HOST}/v2/administration/users" - - payload = {} - query = { - 'page': str(page) - } - headers = { - 'Accept': 'application/json', - 'Authorization': f'Bearer {TOKEN}', - } - - response = requests.request("GET", url, headers=headers, data=payload, params=query) - - if(response.status_code != 200): - print(response.text) - return [] - - - res_content = json.loads(response.text) - MAX_PAGE = int(res_content["page"]["totalPages"]) - - if page + 1 == MAX_PAGE: - print(f"USER: Loaded page {page + 1} out of {MAX_PAGE}") - return list(res_content["_embedded"]["users"]) - else: - print(f"USER: Loaded page {page + 1} out of {MAX_PAGE}") - return list(res_content["_embedded"]["users"]) + Get_AllUsers(page+1) - - -def Get_ActivityHistory(project: int, page: int = 0): - url = f"https://{HOST}/v2/projects/{project}/activity" - - payload = {} - query = { - 'page': str(page), - 'size': "300", - 'sort': "timestamp,desc" - } - headers = { - 'Accept': 'application/json', - 'Authorization': f'Bearer {TOKEN}', - } - - response = requests.request("GET", url, headers=headers, data=payload, params=query) - - if(response.status_code != 200): - print(response.text) - return [] - - - res_content = json.loads(response.text) - MAX_PAGE = int(res_content["page"]["totalPages"]) - - results = [id["author"]["id"] for id in list(res_content["_embedded"]["activities"])] - - if page + 1 == MAX_PAGE: - print(f"USER: Loaded page {page + 1} out of {MAX_PAGE}") - return results - else: - print(f"USER: Loaded page {page + 1} out of {MAX_PAGE}") - return results + Get_ActivityHistory(project, page+1) - - - -def Get_AllUsersOnProject(project: int) ->list[dict]: - url = f"https://{HOST}/v2/projects/{project}/users" - - payload={} - query = { - 'page': "0", - 'size': "2000", - 'sort': "timestamp,desc" - } - headers = { - 'Accept': 'application/json', - 'Authorization': f'Bearer {TOKEN}', - } - - response = requests.request("GET", url, headers=headers, data=payload, params=query) - - if(response.status_code != 200): - print(response.text) - return [] - - res_content = json.loads(response.text) - print(f"PROJ: Loaded active users for project") - return list(res_content["_embedded"]["users"]) - - -def Delete_User(user_id: int) -> bool: - url = f"https://{HOST}/v2/administration/users/{user_id}" - - payload={} - headers = { - 'Authorization': f'Bearer {TOKEN}' - } - - response = requests.request("DELETE", url, headers=headers, data=payload) - - if(response.status_code != 200): - print(response.text) - - return response.status_code == 200 - - -print(f"Running for host {HOST}") - -TOKEN = Get_JWTToken() - -ids = [] -for user in Get_AllUsers(): - ids.append(int(user["id"])) -ids.sort() - -usersOnTimeline = Get_ActivityHistory(1) -usersOnTimeline = list(dict.fromkeys(usersOnTimeline)) - -INACTIVE = [] -for id in ids: - if id not in usersOnTimeline: - INACTIVE.append(id) - - -print("Active users: [the higher the position the older the last contribution is]") -print(usersOnTimeline) -print() -print("Users found on timeline:", len(usersOnTimeline)) -print("Users NOT found on timeline:" , len(INACTIVE)) -print("Print how many users you wish to delete from the server. Order of deletion is: first, inactive users. Then, active users from back to front") - - -TO_DELETE = [] -amount = int(input("Amount of users to delete: ")) -if(len(INACTIVE) < amount): - TO_DELETE += INACTIVE - amount -= len(INACTIVE) -else: - TO_DELETE += INACTIVE[:amount] - amount -= amount - -while(amount > 0): - TO_DELETE.append(usersOnTimeline.pop()) - amount -= 1 - -print(TO_DELETE) -print("Users to delete count:", len(TO_DELETE)) - - -total = len(TO_DELETE) -SKIP_VERIFICATIION = "" -input(f"Press to confirm deletion of {total} users (users in server are {len(ids)}): ") - -for i in range(total): - user = TO_DELETE[i] - - if(user == "1" or user == 1): - continue - - if SKIP_VERIFICATIION != "12": - SKIP_VERIFICATIION = input("About to delete User id=" + str(user) + ". Press intro to proceed, type 12 to run all: ") - - print(f"Deleting user id={user}, ({i+1} out of {total})... ", end="", flush=True) - print("SUCCESS" if Delete_User(user) else "FAILED") \ No newline at end of file diff --git a/scripts/tolgee_requests.py b/scripts/tolgee_requests.py deleted file mode 100644 index 58ab2d600a..0000000000 --- a/scripts/tolgee_requests.py +++ /dev/null @@ -1,100 +0,0 @@ -import json -import os - -try: - import requests -except ImportError: - os.system("pip install requests") - import requests - - -__project_id = 1 # UniGetUI -__api_url = f"https://tolgee.marticliment.com/v2/projects/{__project_id}" -__api_key = "" -__headers: dict[str, str] = {} -__all_keys: dict = None - - -try: - with open("APIKEY.txt", "r") as f: - __api_key = f.read().strip() - if not __api_key: - raise ValueError("APIKEY.txt is empty") - # print("API key found in APIKEY.txt") -except FileNotFoundError: - __api_key = os.environ.get("TOLGEE_KEY", "") - if not __api_key: - __api_key = input("Write api key and press enter: ") -__headers["X-API-Key"] = __api_key - - - -def check_api_key(): - url = f"{__api_url}/activity" - response = requests.get(url, headers=__headers) - if (not response.ok): - print("Issue with API key!") - print("Error", response.status_code, response.json().get("error")) - exit(1) - - -def export(format = "JSON", zip = True, langs: list[str] = []): - url = f"{__api_url}/export" - params = { - "format": format, - "languages": langs, - "structureDelimiter": "", - "filterState": [ - "REVIEWED", - "TRANSLATED", - "UNTRANSLATED", - ], - "zip": zip, - } - response = requests.get(url, headers=__headers, params=params) - return response - - -def create_key(key): - url = f"{__api_url}/keys/create" - json: dict[str, str] = { - "name": key - } - response = requests.post(url, headers=__headers, json=json) - return response - - -def get_keys(): - global __all_keys - if __all_keys: - return __all_keys - url = f"{__api_url}/keys" - params = { - "size": 1000 - } - response = requests.get(url, headers=__headers, params=params) - if not response.ok: - return False - data = json.loads(response.text) - retValue = {} - for value in data["_embedded"]["keys"]: - retValue[value["name"]] = value - __all_keys = retValue - return retValue - - -def delete_key(key): - all_keys = get_keys() - key_data = all_keys.get(key) - if not key_data: - return False - id = key_data.get("id") - url = f"{__api_url}/keys" - json: dict[str, str] = { - "ids": [id], - } - response = requests.delete(url, headers=__headers, json=json) - return response - - -check_api_key() diff --git a/scripts/translation_commands.py b/scripts/translation_commands.py deleted file mode 100644 index 2655bdeec7..0000000000 --- a/scripts/translation_commands.py +++ /dev/null @@ -1,133 +0,0 @@ -import argparse -import json -from time import sleep - -import tolgee_requests -import translation_utils - -__parser = argparse.ArgumentParser() -__group = __parser.add_mutually_exclusive_group(required=True) -__group.add_argument("-p", "--print", help="Print unused and not translated strings", action="store_true") -__group.add_argument("-c", "--create", help="Create not translated strings", action="store_true") -__group.add_argument("-d", "--delete", help="Delete unused strings", action="store_true") -__parser.add_argument("--online", help="Compare with Tolgee translations via API", action="store_true") -__parser.add_argument("-y", "--yes", help="All answers are YES", action="store_true") -__args = __parser.parse_args() - - -def __confirm(message: str, choices: list[str], defaultValue = ""): - def createChoices(): - _choices: list[str] = [] - for key in choices: - if key == defaultValue: - key = key.upper() - _choices.append(key) - return '/'.join(_choices) - - try: - return (input(f"{message} [{createChoices()}]: ") or defaultValue).lower() - except KeyboardInterrupt: - exit(1) - - -def encode_str(str: str, strip = 0): - new_str = str - if strip > 0: - new_str = str[:strip].strip() - return new_str.encode("utf-8") - - -def create(strs: list[str]): - count = len(strs) - i = 1 - for key in strs: - print(f"[{i}/{count}] Key: {encode_str(key, strip=100)}") - i += 1 - if not __args.yes and __confirm("Create?", ["y", "n"], "y") != "y": - continue - print("Creating key... ", end="") - response = tolgee_requests.create_key(key) - if (not response.ok): - print("Failed") - print("Error", response.status_code, response.text) - return - else: - print("Ok") - sleep(0.150) - print("Done") - - -def delete(strs: list[str]): - count = len(strs) - i = 1 - for key in strs: - print(f"[{i}/{count}] Key: {encode_str(key, strip=100)}") - i += 1 - if not __args.yes and __confirm("Delete?", ["y", "n"], "y") != "y": - continue - print("Deleting key... ", end="") - response = tolgee_requests.delete_key(key) - if (not response.ok): - print("Failed") - print("Error", response.status_code, response.text) - return - else: - print("Ok") - sleep(0.150) - print("Done") - - -def __print(strs: list[str]): - output = json.dumps(strs, ensure_ascii=False, indent=" ") - print(output) - - -def __print_all(): - output = json.dumps(translation_utils.compare_strings(online=__args.online), ensure_ascii=False, indent=" ") - print(output) - - -def __delete(strs): - key_name = "not_used" - if key_name in strs: - stringsFound = len(strs[key_name]) - print(f"Found not used strings: {stringsFound}") - if stringsFound > 0: - sleep(1) - delete(strs[key_name]) - else: - print(f"Key '{key_name}' missing") - - -def __create(strs): - key_name = "not_translated" - if key_name in strs: - stringsFound = len(strs[key_name]) - print(f"Found not translated strings: {stringsFound}") - if stringsFound > 0: - if __args.print: - __print(strs[key_name]) - else: - sleep(1) - create(strs[key_name]) - else: - print(f"Key '{key_name}' missing") - - -def __init__(): - strs = translation_utils.compare_strings(online=__args.online) - print("Online mode:", __args.online) - - if __args.print: - return __print_all() - - if __args.create: - __create(strs) - return - - if __args.delete: - __delete(strs) - return - - -__init__() diff --git a/scripts/translation_utils.py b/scripts/translation_utils.py deleted file mode 100644 index fe1fd78be5..0000000000 --- a/scripts/translation_utils.py +++ /dev/null @@ -1,120 +0,0 @@ -import json -import os -import re - -import tolgee_requests - -root_dir = os.path.join(os.path.dirname(__file__), "..") -os.chdir(os.path.join(root_dir, "src/")) - - -__blacklist_strings = [ - "0 0 0 Contributors, please add your names/usernames separated by comas (for credit purposes). DO NOT Translate this entry", -] - - -# Function to remove special characters from a string -def remove_special_chars(string): - # Regular expression for special characters (excluding letters and digits) - special_chars = r'[^a-zA-Z0-9]' - # Use regular expression to remove special characters from the string - return re.sub(special_chars, '', string) - - -def get_all_strings(): - translation_strings: list[str] = [] - - # Find C# translation strings - regex1 = r'Translate\([\r\n ]{0,}["\']((?:\\.|[^\"])+)?["\'][,\) \n]' - regex2 = r'(?<=AutoTranslated\(["\']).+?(?=["\']\))' - for (dirpath, _dirnames, filenames) in os.walk(".", topdown=True): - for file in filenames: - _file_name, file_ext = os.path.splitext(file) - if (file_ext != ".cs"): - continue - with open(os.path.join(dirpath, file), "r", encoding="utf-8") as f: - file_data = f.read() - matches: list[str] = re.findall(regex1, file_data) - for match in matches: - translation_strings.append(match.encode('raw_unicode_escape').decode('unicode_escape')) - matches: list[str] = re.findall(regex2, file_data) - for match in matches: - translation_strings.append(match.encode('raw_unicode_escape').decode('unicode_escape')) - - # Find XAML translation strings - MAIN_WILDCARD = r'(?:x:|"&#x[a-zA-Z0-9]{4};"|[ a-zA-Z0-9=\-\"\'\r\n\t_\.\,\:\;\{\}])' - - regex_data = { - r'(?<=Translate\(["\']).+?(?=["\']\))': lambda match: match.encode('raw_unicode_escape').decode('unicode_escape'), - r'<[a-zA-Z0-9]+:TranslatedTextBlock' + MAIN_WILDCARD + r'+Text=["\'].+["\']' + MAIN_WILDCARD + r'*\/?>': lambda match: match.split(" Text=\"")[1].split("\"")[0].encode('raw_unicode_escape').decode('unicode_escape'), - r'<[a-zA-Z0-9]+:ButtonCard' + MAIN_WILDCARD + r'+Text=["\'].+["\']' + MAIN_WILDCARD + r'*\/?>': lambda match: match.split(" Text=\"")[1].split("\"")[0].encode('raw_unicode_escape').decode('unicode_escape'), - r'<[a-zA-Z0-9]+:ButtonCard' + MAIN_WILDCARD + r'+ButtonText=["\'].+["\']' + MAIN_WILDCARD + r'*\/?>': lambda match: match.split(" ButtonText=\"")[1].split("\"")[0].encode('raw_unicode_escape').decode('unicode_escape'), - r'<[a-zA-Z0-9]+:CheckboxCard' + MAIN_WILDCARD + r'+Text=["\'].+["\']' + MAIN_WILDCARD + r'*\/?>': lambda match: match.split(" Text=\"")[1].split("\"")[0].encode('raw_unicode_escape').decode('unicode_escape'), - r'<[a-zA-Z0-9]+:CheckboxCard' + MAIN_WILDCARD + r'+WarningText=["\'].+["\']' + MAIN_WILDCARD + r'*\/?>': lambda match: match.split(" WarningText=\"")[1].split("\"")[0].encode('raw_unicode_escape').decode('unicode_escape'), - r'<[a-zA-Z0-9]+:SecureCheckboxCard' + MAIN_WILDCARD + r'+Text=["\'].+["\']' + MAIN_WILDCARD + r'*\/?>': lambda match: match.split(" Text=\"")[1].split("\"")[0].encode('raw_unicode_escape').decode('unicode_escape'), - r'<[a-zA-Z0-9]+:SecureCheckboxCard' + MAIN_WILDCARD + r'+WarningText=["\'].+["\']' + MAIN_WILDCARD + r'*\/?>': lambda match: match.split(" WarningText=\"")[1].split("\"")[0].encode('raw_unicode_escape').decode('unicode_escape'), - r'<[a-zA-Z0-9]+:CheckboxButtonCard' + MAIN_WILDCARD + r'+CheckboxText=["\'].+["\']' + MAIN_WILDCARD + r'*\/?>': lambda match: match.split(" CheckboxText=\"")[1].split("\"")[0].encode('raw_unicode_escape').decode('unicode_escape'), - r'<[a-zA-Z0-9]+:CheckboxButtonCard' + MAIN_WILDCARD + r'+ButtonText=["\'].+["\']' + MAIN_WILDCARD + r'*\/?>': lambda match: match.split(" ButtonText=\"")[1].split("\"")[0].encode('raw_unicode_escape').decode('unicode_escape'), - r'<[a-zA-Z0-9]+:ComboboxCard' + MAIN_WILDCARD + r'+Text=["\'].+["\']' + MAIN_WILDCARD + r'*\/?>': lambda match: match.split(" Text=\"")[1].split("\"")[0].encode('raw_unicode_escape').decode('unicode_escape'), - r'<[a-zA-Z0-9]+:SettingsPageButton' + MAIN_WILDCARD + r'+Text=["\'].+["\']' + MAIN_WILDCARD + r'*\/?>': lambda match: match.split(" Text=\"")[1].split("\"")[0].encode('raw_unicode_escape').decode('unicode_escape'), - r'<[a-zA-Z0-9]+:SettingsPageButton' + MAIN_WILDCARD + r'+UnderText=["\'].+["\']' + MAIN_WILDCARD + r'*\/?>': lambda match: match.split(" UnderText=\"")[1].split("\"")[0].encode('raw_unicode_escape').decode('unicode_escape'), - r'<[a-zA-Z0-9]+:BetterMenuItem' + MAIN_WILDCARD + r'+Text=["\'].+["\']' + MAIN_WILDCARD + r'*\/?>': lambda match: match.split(" Text=\"")[1].split("\"")[0].encode('raw_unicode_escape').decode('unicode_escape'), - r'<[a-zA-Z0-9]+:BetterToggleMenuItem' + MAIN_WILDCARD + r'+Text=["\'].+["\']' + MAIN_WILDCARD + r'*\/?>': lambda match: match.split(" Text=\"")[1].split("\"")[0].encode('raw_unicode_escape').decode('unicode_escape'), - r'<[a-zA-Z0-9]+:SourceManager' + MAIN_WILDCARD + r'+Text=["\'].+["\']' + MAIN_WILDCARD + r'*\/?>': lambda match: match.split(" Text=\"")[1].split("\"")[0].encode('raw_unicode_escape').decode('unicode_escape'), - r'<[a-zA-Z0-9]+:TextboxCard' + MAIN_WILDCARD + r'+Text=["\'].+["\']' + MAIN_WILDCARD + r'*\/?>': lambda match: match.split(" Text=\"")[1].split("\"")[0].encode('raw_unicode_escape').decode('unicode_escape'), - r'<[a-zA-Z0-9]+:TextboxCard' + MAIN_WILDCARD + r'+Placeholder=["\'].+["\']' + MAIN_WILDCARD + r'*\/?>': lambda match: match.split(" Placeholder=\"")[1].split("\"")[0].encode('raw_unicode_escape').decode('unicode_escape'), - r'<[a-zA-Z0-9]+:CustomNavViewItem' + MAIN_WILDCARD + r'+Text=["\'].+["\']' + MAIN_WILDCARD + r'*\/?>': lambda match: match.split(" Text=\"")[1].split("\"")[0].encode('raw_unicode_escape').decode('unicode_escape'), - } - - for regex in regex_data.keys(): - for (dirpath, _dirnames, filenames) in os.walk(".", topdown=True): - for file in filenames: - _file_name, file_ext = os.path.splitext(file) - if (file_ext != ".xaml"): - continue - with open(os.path.join(dirpath, file), "r", encoding="utf-8") as f: - matches: list[str] = re.findall(regex, f.read()) - for match in matches: - try: - translation_strings.append(regex_data[regex](match.replace("\n", " ").replace("\t", " "))) - except Exception as e: - print(match) - raise e - - translation_strings = list(set(translation_strings)) # uniq - translation_strings.sort(key=lambda x: (remove_special_chars(x.lower()), x)) - return translation_strings - - -def get_all_translations(lang="en"): - with open(f"Core/Languages/lang_{lang}.json", "r", encoding="utf-8") as f: - lang_strings: dict[str, str] = json.load(f) - return lang_strings - - -def get_all_translations_online(lang="en") -> dict[str, str]: - response = tolgee_requests.export(zip=False, langs=["en"]) - return json.loads(response.text) - - -def compare_strings(online=False): - not_used: list[str] = [] - translation_obj: dict[str, str] = {} - lang_strings: dict[str, str] = {} - if (online): - lang_strings = get_all_translations_online() - else: - lang_strings = get_all_translations() - for key in get_all_strings(): - translation_obj[key] = "" - for key in lang_strings.keys(): - if (key in __blacklist_strings): - continue - if (key in translation_obj): - del translation_obj[key] - else: - not_used.append(key) - return { - "not_used": not_used, - "not_translated": list(translation_obj), - } diff --git a/src/UniGetUI/Pages/AboutPages/SupportMe.xaml b/src/UniGetUI/Pages/AboutPages/SupportMe.xaml deleted file mode 100644 index b63cfd5048..0000000000 --- a/src/UniGetUI/Pages/AboutPages/SupportMe.xaml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/UniGetUI/Pages/AboutPages/SupportMe.xaml.cs b/src/UniGetUI/Pages/AboutPages/SupportMe.xaml.cs deleted file mode 100644 index d302702b00..0000000000 --- a/src/UniGetUI/Pages/AboutPages/SupportMe.xaml.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Microsoft.UI.Xaml.Controls; - -// 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.Pages.AboutPages -{ - /// - /// An empty page that can be used on its own or navigated to within a Frame. - /// - public sealed partial class SupportMe : Page - { - public SupportMe() - { - InitializeComponent(); - } - } -} diff --git a/src/UniGetUI/Pages/DialogPages/AboutUniGetUI.xaml b/src/UniGetUI/Pages/DialogPages/AboutUniGetUI.xaml index d966e64888..af419d5691 100644 --- a/src/UniGetUI/Pages/DialogPages/AboutUniGetUI.xaml +++ b/src/UniGetUI/Pages/DialogPages/AboutUniGetUI.xaml @@ -53,14 +53,6 @@ Glyph="" /> - - - - - typeof(Pages.AboutPages.AboutUniGetUI), 1 => typeof(ThirdPartyLicenses), 2 => typeof(Contributors), - 3 => typeof(Translators), - _ => typeof(SupportMe), + _ => typeof(Translators), }; SlideNavigationTransitionEffect slideNavigationTransitionEffect = currentSelectedIndex - previousSelectedIndex > 0 ? SlideNavigationTransitionEffect.FromRight : SlideNavigationTransitionEffect.FromLeft; diff --git a/src/UniGetUI/UniGetUI.csproj b/src/UniGetUI/UniGetUI.csproj index 186066534a..b007155f32 100644 --- a/src/UniGetUI/UniGetUI.csproj +++ b/src/UniGetUI/UniGetUI.csproj @@ -74,7 +74,6 @@ -