From 6e2256fb1f5cc32e4770cb39593341836fd8367f Mon Sep 17 00:00:00 2001 From: Allen Houchins <32207388+allenhouchins@users.noreply.github.com> Date: Fri, 5 Jun 2026 10:35:23 -0500 Subject: [PATCH] Add MindManager as a macOS FMA (#46906) Introduce MindManager to the maintained apps set: add a Homebrew input (mindmanager.json), register the app in ee/maintained-apps/outputs/apps.json, and provide a darwin versions file for v25.2.105 including installer URL, sha256, and install/uninstall script refs. Also add a frontend SVG icon component, wire it into the icon map, and include the 2x PNG asset. These changes enable the app to be detected, installed/uninstalled, and displayed in the UI. --- .../inputs/homebrew/mindmanager.json | 8 +++++++ ee/maintained-apps/outputs/apps.json | 7 ++++++ .../outputs/mindmanager/darwin.json | 22 ++++++++++++++++++ .../components/icons/MindManager.tsx | 14 +++++++++++ .../SoftwarePage/components/icons/index.ts | 2 ++ .../images/app-icon-mindmanager-60x60@2x.png | Bin 0 -> 5034 bytes 6 files changed, 53 insertions(+) create mode 100644 ee/maintained-apps/inputs/homebrew/mindmanager.json create mode 100644 ee/maintained-apps/outputs/mindmanager/darwin.json create mode 100644 frontend/pages/SoftwarePage/components/icons/MindManager.tsx create mode 100644 website/assets/images/app-icon-mindmanager-60x60@2x.png diff --git a/ee/maintained-apps/inputs/homebrew/mindmanager.json b/ee/maintained-apps/inputs/homebrew/mindmanager.json new file mode 100644 index 0000000000..84cf85335d --- /dev/null +++ b/ee/maintained-apps/inputs/homebrew/mindmanager.json @@ -0,0 +1,8 @@ +{ + "name": "MindManager", + "unique_identifier": "com.mindjet.mindmanager.25", + "token": "mindmanager", + "installer_format": "dmg", + "slug": "mindmanager/darwin", + "default_categories": ["Productivity"] +} diff --git a/ee/maintained-apps/outputs/apps.json b/ee/maintained-apps/outputs/apps.json index ad677bef9d..8ce9ed1c4a 100644 --- a/ee/maintained-apps/outputs/apps.json +++ b/ee/maintained-apps/outputs/apps.json @@ -1632,6 +1632,13 @@ "unique_identifier": "com.microsoft.Word", "description": "Microsoft Word is the industry-standard word processor for creating, editing, and sharing professional documents." }, + { + "name": "MindManager", + "slug": "mindmanager/darwin", + "platform": "darwin", + "unique_identifier": "com.mindjet.mindmanager.25", + "description": "MindManager is a mind mapping and visual work-management tool for organizing ideas, planning projects, and presenting information in diagrams." + }, { "name": "Miro", "slug": "miro/darwin", diff --git a/ee/maintained-apps/outputs/mindmanager/darwin.json b/ee/maintained-apps/outputs/mindmanager/darwin.json new file mode 100644 index 0000000000..326909536a --- /dev/null +++ b/ee/maintained-apps/outputs/mindmanager/darwin.json @@ -0,0 +1,22 @@ +{ + "versions": [ + { + "version": "25.2.105", + "queries": { + "exists": "SELECT 1 FROM apps WHERE bundle_identifier = 'com.mindjet.mindmanager.25';", + "patched": "SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM apps WHERE bundle_identifier = 'com.mindjet.mindmanager.25' AND version_compare(bundle_short_version, '25.2.105') < 0);" + }, + "installer_url": "https://download.mindjet.com/MindManager_Mac_25.2.105.dmg", + "install_script_ref": "51218db9", + "uninstall_script_ref": "d0d5a2f1", + "sha256": "99034e012d02e6ffd0f92a08e7b351afbe1940d9f8d6a858cec4d6f7a9568d47", + "default_categories": [ + "Productivity" + ] + } + ], + "refs": { + "51218db9": "#!/bin/bash\n\n# variables\nAPPDIR=\"/Applications/\"\nTMPDIR=$(dirname \"$(realpath \"$INSTALLER_PATH\")\")\n# functions\n\nquit_and_track_application() {\n local bundle_id=\"$1\"\n local var_name=\"APP_WAS_RUNNING_$(echo \"$bundle_id\" | tr '.-' '__')\"\n local timeout_duration=10\n\n # check if the application is running\n local app_running\n app_running=$(osascript -e \"application id \\\"$bundle_id\\\" is running\" 2>/dev/null)\n if [[ \"$app_running\" != \"true\" ]]; then\n eval \"export $var_name=0\"\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ -z \"$console_user\" || \"$console_user\" == \"root\" || \"$console_user\" == \"loginwindow\" ]]; then\n echo \"Not logged into a non-root GUI; skipping quitting application ID '$bundle_id'.\"\n eval \"export $var_name=0\"\n return\n fi\n\n # App was running, mark it for relaunch\n eval \"export $var_name=1\"\n echo \"Application '$bundle_id' was running; will relaunch after installation.\"\n\n echo \"Quitting application '$bundle_id'...\"\n\n # try to quit the application within the timeout period\n local quit_success=false\n SECONDS=0\n while (( SECONDS < timeout_duration )); do\n if osascript -e \"tell application id \\\"$bundle_id\\\" to quit\" >/dev/null 2>&1; then\n if ! pgrep -f \"$bundle_id\" >/dev/null 2>&1; then\n echo \"Application '$bundle_id' quit successfully.\"\n quit_success=true\n break\n fi\n fi\n sleep 1\n done\n\n if [[ \"$quit_success\" = false ]]; then\n echo \"Application '$bundle_id' did not quit.\"\n fi\n}\n\n\nrelaunch_application() {\n local bundle_id=\"$1\"\n local var_name=\"APP_WAS_RUNNING_$(echo \"$bundle_id\" | tr '.-' '__')\"\n local was_running\n\n # Check if the app was running before installation\n eval \"was_running=\\$$var_name\"\n if [[ \"$was_running\" != \"1\" ]]; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ -z \"$console_user\" || \"$console_user\" == \"root\" || \"$console_user\" == \"loginwindow\" ]]; then\n echo \"Not logged into a non-root GUI; skipping relaunching application ID '$bundle_id'.\"\n return\n fi\n\n echo \"Relaunching application '$bundle_id'...\"\n\n # Launch the app in the logged-in user's GUI session. Apps launched by root\n # won't register with the user's Dock/GUI, so run 'open' as the console user.\n # Use 'launchctl asuser' to bootstrap into the console user's Mach namespace\n # and GUI session — 'sudo -u' alone doesn't do this, which can cause\n # LSOpenURLsWithRole() failures even when 'open' exits 0.\n local open_status=0\n if [[ $EUID -eq 0 ]]; then\n local console_uid\n console_uid=$(id -u \"$console_user\")\n /bin/launchctl asuser \"$console_uid\" sudo -u \"$console_user\" open -b \"$bundle_id\" >/dev/null 2>&1 || open_status=$?\n else\n open -b \"$bundle_id\" >/dev/null 2>&1 || open_status=$?\n fi\n\n if [[ $open_status -eq 0 ]]; then\n echo \"Application '$bundle_id' relaunched successfully.\"\n else\n echo \"Failed to relaunch application '$bundle_id'.\"\n fi\n}\n\n\n# extract contents\nMOUNT_POINT=$(mktemp -d /tmp/dmg_mount_XXXXXX)\nyes | hdiutil attach -plist -nobrowse -readonly -mountpoint \"$MOUNT_POINT\" \"$INSTALLER_PATH\" || exit 1\nsudo cp -R \"$MOUNT_POINT\"/* \"$TMPDIR\"\nhdiutil detach \"$MOUNT_POINT\" || true\n# copy to the applications folder\nquit_and_track_application 'com.mindjet.mindmanager.25'\nif [ -d \"$APPDIR/MindManager.app\" ]; then\n\tsudo mv \"$APPDIR/MindManager.app\" \"$TMPDIR/MindManager.app.bkp\"\nfi\nsudo cp -R \"$TMPDIR/MindManager.app\" \"$APPDIR\"\nrelaunch_application 'com.mindjet.mindmanager.25'\n", + "d0d5a2f1": "#!/bin/bash\n\n# variables\nAPPDIR=\"/Applications/\"\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\n\nquit_application() {\n local bundle_id=\"$1\"\n local timeout_duration=10\n\n # check if the application is running\n local app_running\n app_running=$(osascript -e \"application id \\\"$bundle_id\\\" is running\" 2>/dev/null)\n if [[ \"$app_running\" != \"true\" ]]; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ -z \"$console_user\" || \"$console_user\" == \"root\" || \"$console_user\" == \"loginwindow\" ]]; then\n echo \"Not logged into a non-root GUI; skipping quitting application ID '$bundle_id'.\"\n return\n fi\n\n echo \"Quitting application '$bundle_id'...\"\n\n # try to quit the application within the timeout period\n local quit_success=false\n SECONDS=0\n while (( SECONDS < timeout_duration )); do\n if osascript -e \"tell application id \\\"$bundle_id\\\" to quit\" >/dev/null 2>&1; then\n if ! pgrep -f \"$bundle_id\" >/dev/null 2>&1; then\n echo \"Application '$bundle_id' quit successfully.\"\n quit_success=true\n break\n fi\n fi\n sleep 1\n done\n\n if [[ \"$quit_success\" = false ]]; then\n echo \"Application '$bundle_id' did not quit.\"\n fi\n}\n\n\ntrash() {\n local logged_in_user=\"$1\"\n local target_file=\"$2\"\n local timestamp=\"$(date +%Y-%m-%d-%s)\"\n local rand=\"$(jot -r 1 0 99999)\"\n\n # replace ~ with /Users/$logged_in_user\n if [[ \"$target_file\" == ~* ]]; then\n target_file=\"/Users/$logged_in_user${target_file:1}\"\n fi\n\n local trash=\"/Users/$logged_in_user/.Trash\"\n\n # If the target contains glob characters, expand it and move each match.\n if [[ \"$target_file\" == *[*?[]* ]]; then\n local file file_name\n local matched=false\n local i=0\n # compgen -G expands the (quoted) pattern itself, so paths containing\n # spaces glob correctly; reading line by line keeps each match intact.\n while IFS= read -r file; do\n [[ -n \"$file\" ]] || continue\n [[ -e \"$file\" || -L \"$file\" ]] || continue\n matched=true\n i=$((i + 1))\n file_name=\"$(basename \"$file\")\"\n echo \"removing $file.\"\n # The per-match counter keeps matches that share a basename from\n # overwriting each other in the trash.\n mv -f \"$file\" \"$trash/${file_name}_${timestamp}_${rand}_${i}\"\n done < <(compgen -G \"$target_file\" 2>/dev/null)\n if [[ \"$matched\" == false ]]; then\n echo \"$target_file doesn't exist.\"\n fi\n return\n fi\n\n local file_name=\"$(basename \"${target_file}\")\"\n\n if [[ -e \"$target_file\" ]]; then\n echo \"removing $target_file.\"\n mv -f \"$target_file\" \"$trash/${file_name}_${timestamp}_${rand}\"\n else\n echo \"$target_file doesn't exist.\"\n fi\n}\n\nquit_application 'com.mindjet.mindmanager.25'\nsudo rm -rf \"$APPDIR/MindManager.app\"\ntrash $LOGGED_IN_USER '~/Library/Application Support/Mindjet'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.mindjet.mindmanager.*'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.mindjet.mindmanager.*.plist'\n" + } +} diff --git a/frontend/pages/SoftwarePage/components/icons/MindManager.tsx b/frontend/pages/SoftwarePage/components/icons/MindManager.tsx new file mode 100644 index 0000000000..93e73887f6 --- /dev/null +++ b/frontend/pages/SoftwarePage/components/icons/MindManager.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; + +import type { SVGProps } from "react"; + +const MindManager = (props: SVGProps) => ( + + + +); +export default MindManager; diff --git a/frontend/pages/SoftwarePage/components/icons/index.ts b/frontend/pages/SoftwarePage/components/icons/index.ts index 813e9bb76e..11aeff5e12 100644 --- a/frontend/pages/SoftwarePage/components/icons/index.ts +++ b/frontend/pages/SoftwarePage/components/icons/index.ts @@ -43,6 +43,7 @@ import Marvel from "./Marvel"; import Max from "./Max"; import MicrosoftDotnetRuntime from "./MicrosoftDotnetRuntime"; import MicrosoftRemoteHelp from "./MicrosoftRemoteHelp"; +import MindManager from "./MindManager"; import NessusAgent from "./NessusAgent"; import Nextcloud from "./Nextcloud"; import Notepad from "./Notepad++"; @@ -447,6 +448,7 @@ export const SOFTWARE_NAME_TO_ICON_MAP = { mattermost: Mattermost, "microsoft autoupdate": MicrosoftAutoUpdate, "microsoft auto update": MicrosoftAutoUpdate, + mindmanager: MindManager, "mongodb compass": MongoDbCompass, "mozilla firefox": Firefox, "github desktop": GitHubDesktop, diff --git a/website/assets/images/app-icon-mindmanager-60x60@2x.png b/website/assets/images/app-icon-mindmanager-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b8d2be021c31e2c8b3086c1c10a26ca2e3304df5 GIT binary patch literal 5034 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 C%zill literal 0 HcmV?d00001