From c020b84cc9205866dae1fcc42c22a3db5a1e69d7 Mon Sep 17 00:00:00 2001 From: Sidheshwar Sarangal Date: Thu, 23 Jan 2025 19:57:41 +0530 Subject: [PATCH 1/4] Fix: Add custom About dialog for non-macOS platforms (#421) --- td.vue/src/desktop/menu.js | 59 ++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/td.vue/src/desktop/menu.js b/td.vue/src/desktop/menu.js index 8c24e1302..7af75dbf4 100644 --- a/td.vue/src/desktop/menu.js +++ b/td.vue/src/desktop/menu.js @@ -30,7 +30,7 @@ import zho from '@/i18n/zh.js'; const messages = { ara, deu, ell, eng, fin, fra, hin, id, jpn, ms, por, spa, zho }; // hide RUS & UKR for now: const messages = { ara, deu, ell, eng, fin, fra, hin, id, jpn, ms, por, rus, spa, ukr, zho }; -const languages = [ 'ara', 'deu', 'ell', 'eng', 'fin', 'fra', 'hin', 'id', 'jpn', 'ms', 'por', 'spa', 'zho' ]; +const languages = ['ara', 'deu', 'ell', 'eng', 'fin', 'fra', 'hin', 'id', 'jpn', 'ms', 'por', 'spa', 'zho']; // hide RUS & UKR for now: const languages = [ 'ara', 'deu', 'ell', 'eng', 'fin', 'fra', 'hin', 'id', 'jpn', 'ms', 'por', 'rus', 'spa', 'ukr', 'zho' ]; const defaultLanguage = 'eng'; var language = defaultLanguage; @@ -41,7 +41,7 @@ export const model = { isOpen: false }; -export function getMenuTemplate () { +export function getMenuTemplate() { var menuTemplate = (isMacOS ? [{ role: 'appMenu' }] : []); menuTemplate.push( { @@ -49,25 +49,25 @@ export function getMenuTemplate () { submenu: [ { label: messages[language].desktop.file.open, - click () { + click() { openModelRequest(''); } }, { label: messages[language].desktop.file.save, - click () { + click() { saveModel(); } }, { label: messages[language].desktop.file.saveAs, - click () { + click() { saveModelAs(); } }, { label: messages[language].desktop.file.new, - click () { + click() { newModel(); } }, @@ -76,13 +76,13 @@ export function getMenuTemplate () { submenu: [ { label: messages[language].forms.exportHtml, - click () { + click() { printModel('HTML'); } }, { label: messages[language].forms.exportPdf, - click () { + click() { printModel('PDF'); } }, @@ -98,7 +98,7 @@ export function getMenuTemplate () { }, { label: messages[language].desktop.file.close, - click () { + click() { closeModelRequest(); } }, @@ -153,6 +153,21 @@ export function getMenuTemplate () { } }, { type: 'separator' }, + { + label: 'about Electron', // New "About Electron" label + click: () => { + const aboutWin = new BrowserWindow({ + width: 300, + height: 200, + modal: true, + parent: win, + resizable: false + }); + aboutWin.loadFile('about.html'); // Load the 'about.html' file in the modal + }, + ...(process.platform === 'darwin' && { role: 'about' }) // Only add 'role: about' for macOS + }, + { role: 'about' } ] } @@ -178,7 +193,7 @@ export function getMenuTemplate () { } // Open file system dialog and read file contents into model -function openModel (filename) { +function openModel(filename) { logger.log.debug('Open file with name : ' + filename); if (filename !== '') { @@ -207,13 +222,13 @@ function openModel (filename) { } // request to the renderer for confirmation that it is OK to open a model file -function openModelRequest (filename) { +function openModelRequest(filename) { logger.log.debug('Request to renderer to open an existing model'); mainWindow.webContents.send('open-model-request', filename); } // request to the renderer for confirmation that it is OK to open a model file -function openModelFile (filename) { +function openModelFile(filename) { logger.log.debug(messages[language].desktop.file.open + ': ' + filename); fs.readFile(filename, (err, data) => { if (!err) { @@ -231,7 +246,7 @@ function openModelFile (filename) { } // request that the renderer send the model data, retain existing filename -function saveModel () { +function saveModel() { if (model.isOpen === false) { logger.log.debug('Skip save request because no model is open'); return; @@ -241,7 +256,7 @@ function saveModel () { } // request that the renderer send the model data -function saveModelAs () { +function saveModelAs() { if (model.isOpen === false) { logger.log.debug('Skip saveAs request because no model is open'); return; @@ -253,7 +268,7 @@ function saveModelAs () { } // Open saveAs file system dialog and write contents to new file location -function saveModelDataAs (modelData, fileName) { +function saveModelDataAs(modelData, fileName) { let newName = 'new-model.json'; if (fileName) { newName = fileName; @@ -282,31 +297,31 @@ function saveModelDataAs (modelData, fileName) { } // request that the renderer open a new model -function newModel () { +function newModel() { let newName = 'new-model.json'; logger.log.debug(messages[language].desktop.file.new + ': ' + newName); mainWindow.webContents.send('new-model-request', newName); } // request that the renderer display the model report/print page -function printModel (format) { +function printModel(format) { if (model.isOpen === false) { logger.log.debug('Skip print request because no model open'); return; } - logger.log.debug(messages[language].forms.exportPdf+ ': ' + model.filePath); + logger.log.debug(messages[language].forms.exportPdf + ': ' + model.filePath); // prompt the renderer to open the print/report window mainWindow.webContents.send('print-model-request', format); } // request that the renderer close the model -function closeModelRequest () { +function closeModelRequest() { logger.log.debug(messages[language].desktop.file.close + ': ' + model.filePath); mainWindow.webContents.send('close-model-request', path.basename(model.filePath)); } // save the threat model -function saveModelData (modelData) { +function saveModelData(modelData) { if (model.isOpen === true) { fs.writeFile(model.filePath, JSON.stringify(modelData, undefined, 2), (err) => { if (err) { @@ -322,7 +337,7 @@ function saveModelData (modelData) { } // Open saveAs file system dialog and write report contents as HTML -function saveHTMLReport (htmlPath) { +function saveHTMLReport(htmlPath) { htmlPath += '.html'; var dialogOptions = { title: messages[language].forms.saveAS, @@ -347,7 +362,7 @@ function saveHTMLReport (htmlPath) { } // Open saveAs file system dialog and write PDF report -function savePDFReport (pdfPath) { +function savePDFReport(pdfPath) { pdfPath += '.pdf'; var dialogOptions = { title: messages[language].forms.exportPdf, From f9d7588e5b073f8f6b80d11ddde482a50ffe3386 Mon Sep 17 00:00:00 2001 From: Sidheshwar Sarangal Date: Fri, 24 Jan 2025 14:41:13 +0530 Subject: [PATCH 2/4] Updated the code with required changes --- td.vue/src/desktop/menu.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/td.vue/src/desktop/menu.js b/td.vue/src/desktop/menu.js index 7af75dbf4..f0af7160a 100644 --- a/td.vue/src/desktop/menu.js +++ b/td.vue/src/desktop/menu.js @@ -1,6 +1,6 @@ 'use strict'; -import { app, dialog } from 'electron'; +import { app, dialog, BrowserWindow } from 'electron'; import path from 'path'; import logger from './logger.js'; import { isMacOS } from './utils.js'; @@ -154,18 +154,18 @@ export function getMenuTemplate() { }, { type: 'separator' }, { - label: 'about Electron', // New "About Electron" label + label: 'about Electron', // New "About Electron" label click: () => { const aboutWin = new BrowserWindow({ width: 300, height: 200, modal: true, - parent: win, - resizable: false + parent: mainWindow, // Use mainWindow as the parent if applicable + resizable: false, }); - aboutWin.loadFile('about.html'); // Load the 'about.html' file in the modal + aboutWin.loadFile('about.html'); // Load the 'about.html' file in the modal }, - ...(process.platform === 'darwin' && { role: 'about' }) // Only add 'role: about' for macOS + ...(process.platform === 'darwin' && { role: 'about' }), // Only add 'role: about' for macOS }, { role: 'about' } From 862eefc04db108bb96efa43b8a1803bb510435de Mon Sep 17 00:00:00 2001 From: Sidheshwar Sarangal Date: Mon, 27 Jan 2025 15:46:36 +0530 Subject: [PATCH 3/4] Updated the desktop functionality for About for non mac os --- td.vue/src/desktop/menu.js | 88 ++++++++++++++++++++++++++++++++------ 1 file changed, 76 insertions(+), 12 deletions(-) diff --git a/td.vue/src/desktop/menu.js b/td.vue/src/desktop/menu.js index f0af7160a..fe5d8ac17 100644 --- a/td.vue/src/desktop/menu.js +++ b/td.vue/src/desktop/menu.js @@ -154,21 +154,85 @@ export function getMenuTemplate() { }, { type: 'separator' }, { - label: 'about Electron', // New "About Electron" label + label: 'About', // "About Electron" label click: () => { - const aboutWin = new BrowserWindow({ - width: 300, - height: 200, - modal: true, - parent: mainWindow, // Use mainWindow as the parent if applicable - resizable: false, - }); - aboutWin.loadFile('about.html'); // Load the 'about.html' file in the modal + if (process.platform !== 'darwin') { // Check for non-macOS systems + const aboutWin = new BrowserWindow({ + width: 800, + height: 700, + modal: true, + parent: mainWindow, // Use mainWindow as the parent if applicable + resizable: false, + webPreferences: { + nodeIntegration: true, // Optional, if you need node integration + } + }); + + // Set the content directly within the BrowserWindow + const aboutContent = ` + + + About Threat Dragon + + + +

About Threat Dragon

+

Threat Dragon is an open-source threat modeling tool developed by OWASP (Open Web Application Security Project). It allows you to design and analyze your application’s security by identifying potential threats and vulnerabilities.

+

It is available as a desktop application that works across multiple platforms, helping you create threat models that can be shared and collaborated upon. This tool is designed for developers and security professionals to visualize their system architecture and the threats that could potentially affect it.

+

Features

+
    +
  • Drag-and-drop interface for building threat models.
  • +
  • Automatic threat analysis and risk scoring.
  • +
  • Integration with other security tools and frameworks.
  • +
  • Export models to various formats (PDF, HTML, JSON).
  • +
  • Collaborative features for team-based threat modeling.
  • +
+

Learn More

+

For more information, visit the official OWASP Threat Dragon Project Page.

+

Visit our GitHub repository for source code, bug reports, and contributing guidelines.

+

If you encounter any issues or have suggestions, feel free to submit an issue or create a pull request.

+ + + `; + + // Load the content directly into the window + aboutWin.loadURL('data:text/html;charset=utf-8,' + encodeURIComponent(aboutContent)); + + // Optional: Open DevTools to debug + aboutWin.webContents.openDevTools(); + + // Optional: Log load success + aboutWin.webContents.on('did-finish-load', () => { + console.log('About page content loaded'); + }); + } }, - ...(process.platform === 'darwin' && { role: 'about' }), // Only add 'role: about' for macOS - }, + ...(process.platform === 'darwin' && { role: 'about' }) // Only add 'role: about' for macOS + } + , - { role: 'about' } + // { role: 'about' } ] } ); From 40dd8d1f6902ad934b1e2ddf691bf58138404f57 Mon Sep 17 00:00:00 2001 From: Sidheshwar Sarangal Date: Tue, 28 Jan 2025 21:37:23 +0530 Subject: [PATCH 4/4] Updated the desktop functionality for About for non mac os using about-window --- package-lock.json | 8 +++ package.json | 3 + td.vue/src/desktop/menu.js | 124 +++++++++++++------------------------ 3 files changed, 55 insertions(+), 80 deletions(-) diff --git a/package-lock.json b/package-lock.json index 74916fb9f..3a988a480 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,9 @@ "version": "2.3.0", "hasInstallScript": true, "license": "Apache-2.0", + "dependencies": { + "electron-about-window": "^1.15.2" + }, "devDependencies": { "@wdio/cli": "^8.38.2", "npm-run-all": "^4.1.5", @@ -2067,6 +2070,11 @@ "node": ">=0.10.0" } }, + "node_modules/electron-about-window": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/electron-about-window/-/electron-about-window-1.15.2.tgz", + "integrity": "sha512-iKQOdMOxybXRAycwr8rGYfCky1lcADlFGdQRnOW+ZUq24agIOgzQqRsaVv+2Tiah24wS13yKuHMuyORcKybEEg==" + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", diff --git a/package.json b/package.json index 9b955137b..e6a77ded0 100644 --- a/package.json +++ b/package.json @@ -72,5 +72,8 @@ "micromatch": ">=4.0.8", "minimatch@<3.0.5": ">=3.0.5", "ws": ">=8.18.0" + }, + "dependencies": { + "electron-about-window": "^1.15.2" } } diff --git a/td.vue/src/desktop/menu.js b/td.vue/src/desktop/menu.js index fe5d8ac17..87f855e52 100644 --- a/td.vue/src/desktop/menu.js +++ b/td.vue/src/desktop/menu.js @@ -1,10 +1,41 @@ 'use strict'; -import { app, dialog, BrowserWindow } from 'electron'; +import { app, dialog, BrowserWindow, ipcMain } from 'electron'; import path from 'path'; import logger from './logger.js'; import { isMacOS } from './utils.js'; + + + +function openAboutWindow({ app, BrowserWindow, ipcMain }) { + const aboutWindow = new BrowserWindow({ + width: 400, + height: 300, + resizable: false, + title: 'About', + modal: true, + parent: BrowserWindow.getFocusedWindow(), // Open as a modal window + show: false, + }); + + aboutWindow.loadURL('data:text/html;charset=utf-8,' + encodeURIComponent(` +

About

+

${app.getName()}

+

App version: ${app.getVersion()}

+ + `)); + + aboutWindow.once('ready-to-show', () => { + aboutWindow.show(); + }); + + ipcMain.on('close-about', () => { + aboutWindow.close(); + }); +} + + const { shell } = require('electron'); const fs = require('fs'); @@ -153,90 +184,23 @@ export function getMenuTemplate() { } }, { type: 'separator' }, - { - label: 'About', // "About Electron" label - click: () => { - if (process.platform !== 'darwin') { // Check for non-macOS systems - const aboutWin = new BrowserWindow({ - width: 800, - height: 700, - modal: true, - parent: mainWindow, // Use mainWindow as the parent if applicable - resizable: false, - webPreferences: { - nodeIntegration: true, // Optional, if you need node integration - } + process.platform === 'darwin' + ? { role: 'about' } // Use native "About" role for macOS + : { + label: 'About', // For non mac os + click: () => { + openAboutWindow({ + app, + BrowserWindow, + ipcMain, }); - - // Set the content directly within the BrowserWindow - const aboutContent = ` - - - About Threat Dragon - - - -

About Threat Dragon

-

Threat Dragon is an open-source threat modeling tool developed by OWASP (Open Web Application Security Project). It allows you to design and analyze your application’s security by identifying potential threats and vulnerabilities.

-

It is available as a desktop application that works across multiple platforms, helping you create threat models that can be shared and collaborated upon. This tool is designed for developers and security professionals to visualize their system architecture and the threats that could potentially affect it.

-

Features

-
    -
  • Drag-and-drop interface for building threat models.
  • -
  • Automatic threat analysis and risk scoring.
  • -
  • Integration with other security tools and frameworks.
  • -
  • Export models to various formats (PDF, HTML, JSON).
  • -
  • Collaborative features for team-based threat modeling.
  • -
-

Learn More

-

For more information, visit the official OWASP Threat Dragon Project Page.

-

Visit our GitHub repository for source code, bug reports, and contributing guidelines.

-

If you encounter any issues or have suggestions, feel free to submit an issue or create a pull request.

- - - `; - - // Load the content directly into the window - aboutWin.loadURL('data:text/html;charset=utf-8,' + encodeURIComponent(aboutContent)); - - // Optional: Open DevTools to debug - aboutWin.webContents.openDevTools(); - - // Optional: Log load success - aboutWin.webContents.on('did-finish-load', () => { - console.log('About page content loaded'); - }); - } - }, - ...(process.platform === 'darwin' && { role: 'about' }) // Only add 'role: about' for macOS - } - , - - // { role: 'about' } + }, + } ] } ); + if (isMacOS) { // recent docs only for macos, see www.electronjs.org/docs/latest/api/menu-item#roles menuTemplate[1].submenu.push(