mirror of
https://github.com/finos/SymphonyElectron.git
synced 2024-12-27 01:11:13 -06:00
Merge remote-tracking branch 'upstream/master' into skynet
# Conflicts: # js/windowMgr.js # package.json
This commit is contained in:
commit
0c652be973
32
PULL_REQUEST_TEMPLATE.md
Normal file
32
PULL_REQUEST_TEMPLATE.md
Normal file
@ -0,0 +1,32 @@
|
||||
## Description
|
||||
A few sentences describing the overall goals of the pull request's commits. Describe the problem or feature in addition to a link to the [JIRA-ticket](https://perzoinc.atlassian.net/browse/JIRA-ticket)
|
||||
|
||||
|
||||
## Approach
|
||||
How does this change address the problem?
|
||||
- #### Problem with the code:
|
||||
- #### Fix:
|
||||
|
||||
|
||||
## Learning
|
||||
Describe the research stage. Put link to Confluence page if possible. Links to blog posts, patterns, libraries or addons used to solve this problem.
|
||||
|
||||
|
||||
#### Blog Posts
|
||||
- [Alice and Bob](https://en.wikipedia.org/wiki/Alice_and_Bob) Wikipage for the famous placeholder names in engineering literature.
|
||||
|
||||
|
||||
## Related PRs
|
||||
List related PRs against other branches:
|
||||
|
||||
branch | PR
|
||||
------ | ------
|
||||
other_pr_rc | [link]()
|
||||
other_pr_dev | [link]()
|
||||
|
||||
|
||||
## Open Questions if any and Todos
|
||||
- [ ] Unit-Tests
|
||||
- [ ] Documentation
|
||||
- [ ] Automation-Tests
|
||||
When solved, check the box and explain the answer.
|
@ -2,6 +2,10 @@
|
||||
|
||||
# SymphonyElectron
|
||||
|
||||
## About:
|
||||
|
||||
SymphonyElectron is a desktop client of the Symphony Communication Platform built for macOS, Windows 10 and Windows 7.
|
||||
|
||||
## Project Goals:
|
||||
|
||||
Our goal is to improve the performance and development agility of Symphony's desktop wrapper and build a path to support other wrappers by:
|
||||
@ -64,4 +68,6 @@ In order to achieve those goals Symphony is participating and working in close c
|
||||
- Remote logging is enabled for local and production cases and are sent to the backend server via the remote objects
|
||||
|
||||
## Misc notes
|
||||
If desiring to run against server without proper cert use cmd line option: --ignore-certificate-errors
|
||||
- If desiring to run against server without proper cert use cmd line option: --ignore-certificate-errors
|
||||
- To start additional instance with custom data directory (if you want seperate user) use cmd line options: --multiInstance --userDataPath=<path to data dir>
|
||||
- if directory doesn't exist, it will be created
|
||||
|
@ -1,8 +1,9 @@
|
||||
{
|
||||
"url": "https://foundation-dev.symphony.com",
|
||||
"minimizeOnClose" : false,
|
||||
"minimizeOnClose" : true,
|
||||
"launchOnStartup" : true,
|
||||
"alwaysOnTop" : false,
|
||||
"whitelistUrl": "*",
|
||||
"notificationSettings": {
|
||||
"position": "upper-right",
|
||||
"display": ""
|
||||
@ -11,5 +12,9 @@
|
||||
"submitURL": "https://localhost:1127/post",
|
||||
"companyName": "Symphony",
|
||||
"uploadToServer": false
|
||||
},
|
||||
"customFlags": {
|
||||
"authServerWhitelist": "",
|
||||
"authNegotiateDelegateWhitelist": ""
|
||||
}
|
||||
}
|
@ -18,6 +18,10 @@
|
||||
<label for='image'>image url:</label>
|
||||
<input type='text' id='image' value='https://avatars0.githubusercontent.com/u/13243259?v=4&s=460'/>
|
||||
</p>
|
||||
<p>
|
||||
<label for='company'>company:</label>
|
||||
<input type='text' id='company' value='Symphony'/>
|
||||
</p>
|
||||
<p>
|
||||
<label for='flash'>flash:</label>
|
||||
<input type='checkbox' id='flash'/>
|
||||
@ -92,6 +96,7 @@
|
||||
var shouldStick = document.getElementById('sticky').checked;
|
||||
var color = document.getElementById('color').value;
|
||||
var tag = document.getElementById('tag').value;
|
||||
var company = document.getElementById('company').value;
|
||||
|
||||
num++;
|
||||
|
||||
@ -104,7 +109,8 @@
|
||||
data: {
|
||||
hello: 'hello word'
|
||||
},
|
||||
tag: tag
|
||||
tag: tag,
|
||||
company: company
|
||||
});
|
||||
|
||||
notf.addEventListener('click', onclick);
|
||||
|
@ -3,7 +3,7 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildMachineOSBuild</key>
|
||||
<string>17A405</string>
|
||||
<string>17B48</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
@ -27,17 +27,17 @@
|
||||
<key>DTCompiler</key>
|
||||
<string>com.apple.compilers.llvm.clang.1_0</string>
|
||||
<key>DTPlatformBuild</key>
|
||||
<string>9A1004</string>
|
||||
<string>9B55</string>
|
||||
<key>DTPlatformVersion</key>
|
||||
<string>GM</string>
|
||||
<key>DTSDKBuild</key>
|
||||
<string>17A360</string>
|
||||
<string>17B41</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>macosx10.13</string>
|
||||
<key>DTXcode</key>
|
||||
<string>0901</string>
|
||||
<string>0910</string>
|
||||
<key>DTXcodeBuild</key>
|
||||
<string>9A1004</string>
|
||||
<string>9B55</string>
|
||||
<key>InstallerSectionTitle</key>
|
||||
<string>Pod Settings</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
|
Binary file not shown.
Binary file not shown.
@ -6,7 +6,7 @@
|
||||
<dict>
|
||||
<key>Resources/Base.lproj/MyInstallerPane.nib</key>
|
||||
<data>
|
||||
TF/AqkGdS25ttnHMS1l76ES81/w=
|
||||
GZWN47BPj6b6mD6MnSVH/Qn0m3s=
|
||||
</data>
|
||||
<key>Resources/InstallerSections.plist</key>
|
||||
<data>
|
||||
@ -37,11 +37,11 @@
|
||||
<dict>
|
||||
<key>hash</key>
|
||||
<data>
|
||||
TF/AqkGdS25ttnHMS1l76ES81/w=
|
||||
GZWN47BPj6b6mD6MnSVH/Qn0m3s=
|
||||
</data>
|
||||
<key>hash2</key>
|
||||
<data>
|
||||
gxXMI4SoTYE7jYkP5QJ7i804TUXR4x8LGSh99n9qers=
|
||||
cJ/kr1HEozYL0YeZriWDtnOL1NEd22vHzH5WGp1T3hk=
|
||||
</data>
|
||||
</dict>
|
||||
<key>Resources/InstallerSections.plist</key>
|
||||
|
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13196" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13196"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13529"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@ -59,7 +59,7 @@
|
||||
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="XPe-yO-v9Y">
|
||||
<rect key="frame" x="194" y="85" width="22" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="check" bezelStyle="regularSquare" imagePosition="left" inset="2" id="uvu-EE-3sp">
|
||||
<buttonCell key="cell" type="check" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="uvu-EE-3sp">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
|
@ -31,7 +31,7 @@
|
||||
}
|
||||
|
||||
// Now, validate the url against a url regex
|
||||
NSString *regex = @"^((?:http:\/\/)|(?:https:\/\/))(www.)?((?:[a-zA-Z0-9]+\.[a-z]{3})|(?:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?::\d+)?))([\/a-zA-Z0-9\.]*)$";
|
||||
NSString *regex = @"^(https:\/\/|http:\/\/)(www.)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,}(:[0-9]{1,5})?(\/[a-zA-Z0-9-_.+!*'(),;/?:@=&$]*)?$";
|
||||
NSPredicate *podUrlTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];
|
||||
if ([podUrlTest evaluateWithObject:podUrl]) {
|
||||
return YES;
|
||||
@ -48,23 +48,23 @@
|
||||
|
||||
NSString *podUrl = [_podUrlTextBox stringValue];
|
||||
|
||||
// By default, set autoLaunchOnStart to true
|
||||
// By default, set autoLaunchOnStart and minimizeOnClose to true
|
||||
NSString *autoLaunchOnStart = @"true";
|
||||
NSString *minimizeOnClose = @"true";
|
||||
|
||||
// If the checkbox is changed, set the auto launch value accordingly
|
||||
if ([_autoLaunchCheckBox state] == 0) {
|
||||
autoLaunchOnStart = @"false";
|
||||
}
|
||||
|
||||
// By default, set minimizeOnClose and alwaysOnTop to false
|
||||
NSString *minimizeOnClose = @"false";
|
||||
NSString *alwaysOnTop = @"false\n";
|
||||
|
||||
// If the checkbox is changed, set the minimize on close value accordingly
|
||||
if ([_minimizeOnCloseCheckBox state] == 1) {
|
||||
minimizeOnClose = @"true";
|
||||
if ([_minimizeOnCloseCheckBox state] == 0) {
|
||||
minimizeOnClose = @"false";
|
||||
}
|
||||
|
||||
// By default, set alwaysOnTop to false
|
||||
NSString *alwaysOnTop = @"false\n";
|
||||
|
||||
// If the checkbox is changed, set the always on top value accordingly
|
||||
if ([_alwaysOnTopCheckBox state] == 1) {
|
||||
alwaysOnTop = @"true\n";
|
||||
|
@ -4,24 +4,40 @@
|
||||
tempFilePath='/tmp/sym_settings.txt'
|
||||
installPath="$2"
|
||||
configPath="/Symphony.app/Contents/config/Symphony.config"
|
||||
newPath=$installPath$configPath
|
||||
newPath=${installPath}${configPath}
|
||||
|
||||
## Get Symphony Settings from the temp file ##
|
||||
pod_url=$(sed -n '1p' $tempFilePath);
|
||||
pod_url=$(sed -n '1p' ${tempFilePath});
|
||||
minimize_on_close=$(sed -n '2p' '/tmp/sym_settings.txt');
|
||||
launch_on_startup=$(sed -n '3p' '/tmp/sym_settings.txt');
|
||||
always_on_top=$(sed -n '4p' '/tmp/sym_settings.txt');
|
||||
|
||||
if [ "$pod_url" == "" ]; then
|
||||
pod_url="https://corporate.symphony.com"
|
||||
fi
|
||||
|
||||
if [ "$minimize_on_close" == "" ]; then
|
||||
minimize_on_close=true;
|
||||
fi
|
||||
|
||||
if [ "$launch_on_startup" == "" ]; then
|
||||
launch_on_startup=true;
|
||||
fi
|
||||
|
||||
if [ "$always_on_top" == "" ]; then
|
||||
always_on_top=false;
|
||||
fi
|
||||
|
||||
## Replace the default settings with the user selected settings ##
|
||||
sed -i "" -E "s#\"url\" ?: ?\".*\"#\"url\"\: \"$pod_url\"#g" $newPath
|
||||
sed -i "" -E "s#\"minimizeOnClose\" ?: ?([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])#\"minimizeOnClose\":\ $minimize_on_close#g" $newPath
|
||||
sed -i "" -E "s#\"alwaysOnTop\" ?: ?([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])#\"alwaysOnTop\":\ $always_on_top#g" $newPath
|
||||
sed -i "" -E "s#\"launchOnStartup\" ?: ?([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])#\"launchOnStartup\":\ $launch_on_startup#g" $newPath
|
||||
sed -i "" -E "s#\"url\" ?: ?\".*\"#\"url\"\: \"$pod_url\"#g" ${newPath}
|
||||
sed -i "" -E "s#\"minimizeOnClose\" ?: ?([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])#\"minimizeOnClose\":\ $minimize_on_close#g" ${newPath}
|
||||
sed -i "" -E "s#\"alwaysOnTop\" ?: ?([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])#\"alwaysOnTop\":\ $always_on_top#g" ${newPath}
|
||||
sed -i "" -E "s#\"launchOnStartup\" ?: ?([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])#\"launchOnStartup\":\ $launch_on_startup#g" ${newPath}
|
||||
|
||||
## Remove the temp settings file created ##
|
||||
rm -f $tempFilePath
|
||||
rm -f ${tempFilePath}
|
||||
|
||||
## For launching symphony with sandbox enabled, create a shell script that is used as the launch point for the app
|
||||
EXEC_PATH=$installPath/Symphony.app/Contents/MacOS
|
||||
exec $EXEC_PATH/Symphony --install $newPath $launch_on_startup
|
||||
chmod 755 $EXEC_PATH/Symphony
|
||||
EXEC_PATH=${installPath}/Symphony.app/Contents/MacOS
|
||||
exec ${EXEC_PATH}/Symphony --install ${newPath} ${launch_on_startup}
|
||||
chmod 755 ${EXEC_PATH}/Symphony
|
||||
|
3
installer/mac/preinstall.sh
Normal file
3
installer/mac/preinstall.sh
Normal file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env bash
|
||||
sudo killall Symphony
|
||||
sudo rm -rf /Applications/Symphony.app
|
@ -450,6 +450,13 @@
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<key>PREINSTALL_PATH</key>
|
||||
<dict>
|
||||
<key>PATH</key>
|
||||
<string>preinstall.sh</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<key>RESOURCES</key>
|
||||
<array/>
|
||||
</dict>
|
||||
|
@ -24,6 +24,7 @@
|
||||
<ROW Property="CTRLS" Value="2"/>
|
||||
<ROW Property="DialogBitmap" Value="dialog" MultiBuildValue="DefaultBuild:Tabloid.jpg" Type="1" MsiKey="DialogBitmap"/>
|
||||
<ROW Property="INVALID_POD_URL" Value="valid" Type="4"/>
|
||||
<ROW Property="MINIMIZE_ON_CLOSE" Value="true"/>
|
||||
<ROW Property="MINIMIZE_ON_CLOSE_LABEL" Value="false" Type="4"/>
|
||||
<ROW Property="Manufacturer" Value="Symphony"/>
|
||||
<ROW Property="POD_URL" Value="https://corporate.symphony.com" Type="4"/>
|
||||
@ -552,7 +553,7 @@
|
||||
<ROW Action="AI_TxtUpdaterRollback" Type="11521" Source="TxtUpdater.dll" Target="OnTxtUpdaterRollback" WithoutSeq="true"/>
|
||||
<ROW Action="KillParagon" Type="1" Source="aicustact.dll" Target="StopProcess" Options="1" AdditionalSeq="AI_DATA_SETTER_2"/>
|
||||
<ROW Action="KillRenderer" Type="1" Source="aicustact.dll" Target="StopProcess" Options="1" AdditionalSeq="AI_DATA_SETTER"/>
|
||||
<ROW Action="PodUrlValidation" Type="37" Target="Script Text" TargetUnformatted="// First, check if the protocol is part of the url, if not, prepend it var prefix = "https://"; if (Session.Property("POD_URL").substr(0, prefix.length) !== prefix) { 	Session.Property("POD_URL") = prefix + Session.Property("POD_URL"); } // Check if the entered pod url is valid var podUrlRE = /^((?:http:\/\/)|(?:https:\/\/))(www.)?((?:[a-zA-Z0-9]+\.[a-z]{3})|(?:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?::\d+)?))([\/a-zA-Z0-9\.]*)$/; var podUrlTest = podUrlRE.test(Session.Property("POD_URL")); if (!podUrlTest) { 	Session.Property("INVALID_POD_URL") = "invalid"; } else { 	Session.Property("INVALID_POD_URL") = "valid"; } // By default, we set all the values to false and change based on conditions Session.Property("ALWAYS_ON_TOP_LABEL") = "false"; Session.Property("AUTO_START_LABEL") = "false"; Session.Property("MINIMIZE_ON_CLOSE_LABEL") = "false"; // If always on top is checked in the checkbox, set the label value to true if (Session.Property("ALWAYS_ON_TOP") && Session.Property("ALWAYS_ON_TOP") === "true") { Session.Property("ALWAYS_ON_TOP_LABEL") = "true"; } // If launch on startup is checked in the checkbox, set the label value to true if (Session.Property("MINIMIZE_ON_CLOSE") && Session.Property("MINIMIZE_ON_CLOSE") === "true") { Session.Property("MINIMIZE_ON_CLOSE_LABEL") = "true"; } // If minimise on close is checked in the checkbox, set the label value to true if (Session.Property("AUTO_START") && Session.Property("AUTO_START") === "true") { Session.Property("AUTO_START_LABEL") = "true"; }" WithoutSeq="true"/>
|
||||
<ROW Action="PodUrlValidation" Type="37" Target="Script Text" TargetUnformatted="// First, check if the protocol is part of the url, if not, prepend it var prefix1 = "https://"; var prefix2 = "http://"; if (Session.Property("POD_URL").substr(0, prefix1.length) !== prefix1 && Session.Property("POD_URL").substr(0, prefix2.length) !== prefix2) { Session.Property("POD_URL") = prefix1 + Session.Property("POD_URL"); } // Check if the entered pod url is valid var podUrlRE = /^(https:\/\/|http:\/\/)(www.)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,}(:[0-9]{1,5})?(\/[a-zA-Z0-9-_.+!*'(),;/?:@=&$]*)?$/; var podUrlTest = podUrlRE.test(Session.Property("POD_URL")); if (!podUrlTest) { 	Session.Property("INVALID_POD_URL") = "invalid"; } else { 	Session.Property("INVALID_POD_URL") = "valid"; } // By default, we set all the values to false and change based on conditions Session.Property("ALWAYS_ON_TOP_LABEL") = "false"; Session.Property("AUTO_START_LABEL") = "false"; Session.Property("MINIMIZE_ON_CLOSE_LABEL") = "false"; // If always on top is checked in the checkbox, set the label value to true if (Session.Property("ALWAYS_ON_TOP") && Session.Property("ALWAYS_ON_TOP") === "true") { Session.Property("ALWAYS_ON_TOP_LABEL") = "true"; } // If launch on startup is checked in the checkbox, set the label value to true if (Session.Property("MINIMIZE_ON_CLOSE") && Session.Property("MINIMIZE_ON_CLOSE") === "true") { Session.Property("MINIMIZE_ON_CLOSE_LABEL") = "true"; } // If minimise on close is checked in the checkbox, set the label value to true if (Session.Property("AUTO_START") && Session.Property("AUTO_START") === "true") { Session.Property("AUTO_START_LABEL") = "true"; }" WithoutSeq="true"/>
|
||||
<ROW Action="SET_APPDIR" Type="307" Source="APPDIR" Target="[ProgramFilesFolder][Manufacturer]\[ProductName]" MultiBuildTarget="DefaultBuild:[AI_UserProgramFiles][Manufacturer]\[ProductName]"/>
|
||||
<ROW Action="SET_SHORTCUTDIR" Type="307" Source="SHORTCUTDIR" Target="[ProgramMenuFolder][ProductName]"/>
|
||||
<ROW Action="SET_TARGETDIR_TO_APPDIR" Type="51" Source="TARGETDIR" Target="[APPDIR]"/>
|
||||
|
@ -26,6 +26,10 @@
|
||||
font-size: .9em;
|
||||
}
|
||||
|
||||
.credentials-error {
|
||||
color: red;
|
||||
}
|
||||
|
||||
form {
|
||||
padding-top: 15px;
|
||||
}
|
||||
@ -60,6 +64,7 @@
|
||||
<div class="container">
|
||||
<span>Please provide your login credentials for:</span>
|
||||
<span id="hostname" class="hostname">hostname</span>
|
||||
<span id="credentialsError" class="credentials-error">Invalid user name/password</span>
|
||||
<form id="basicAuth" name="Basic Auth" action="Login">
|
||||
<table class="form">
|
||||
<tbody>
|
||||
|
@ -7,6 +7,7 @@ const path = require('path');
|
||||
const fs = require('fs');
|
||||
const log = require('../log.js');
|
||||
const logLevels = require('../enums/logLevels.js');
|
||||
const { isMac } = require('../utils/misc');
|
||||
|
||||
let basicAuthWindow;
|
||||
|
||||
@ -14,7 +15,7 @@ const local = {};
|
||||
|
||||
let windowConfig = {
|
||||
width: 360,
|
||||
height: 270,
|
||||
height: isMac ? 270 : 295,
|
||||
show: false,
|
||||
modal: true,
|
||||
autoHideMenuBar: true,
|
||||
@ -45,14 +46,20 @@ function getTemplatePath() {
|
||||
* Opens the basic auth window for authentication
|
||||
* @param {String} windowName - name of the window upon which this window should show
|
||||
* @param {String} hostname - name of the website that requires authentication
|
||||
* @param {boolean} isValidCredentials - false if invalid username or password
|
||||
* @param {Function} clearSettings
|
||||
* @param {Function} callback
|
||||
*/
|
||||
function openBasicAuthWindow(windowName, hostname, callback) {
|
||||
function openBasicAuthWindow(windowName, hostname, isValidCredentials, clearSettings, callback) {
|
||||
|
||||
// Register callback function
|
||||
if (typeof callback === 'function') {
|
||||
local.authCallback = callback;
|
||||
}
|
||||
// Register close function
|
||||
if (typeof clearSettings === 'function') {
|
||||
local.clearSettings = clearSettings;
|
||||
}
|
||||
|
||||
// This prevents creating multiple instances of the
|
||||
// basic auth window
|
||||
@ -89,6 +96,7 @@ function openBasicAuthWindow(windowName, hostname, callback) {
|
||||
|
||||
basicAuthWindow.webContents.on('did-finish-load', () => {
|
||||
basicAuthWindow.webContents.send('hostname', hostname);
|
||||
basicAuthWindow.webContents.send('isValidCredentials', isValidCredentials);
|
||||
});
|
||||
|
||||
basicAuthWindow.on('close', () => {
|
||||
@ -103,14 +111,12 @@ function openBasicAuthWindow(windowName, hostname, callback) {
|
||||
ipc.on('login', (event, args) => {
|
||||
if (typeof args === 'object' && typeof local.authCallback === 'function') {
|
||||
local.authCallback(args.username, args.password);
|
||||
basicAuthWindow.close();
|
||||
closeAuthWindow(false);
|
||||
}
|
||||
});
|
||||
|
||||
ipc.on('close-basic-auth', () => {
|
||||
if (basicAuthWindow) {
|
||||
basicAuthWindow.close();
|
||||
}
|
||||
closeAuthWindow(true);
|
||||
});
|
||||
|
||||
/**
|
||||
@ -120,6 +126,19 @@ function destroyWindow() {
|
||||
basicAuthWindow = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to close the auth window
|
||||
* @param {boolean} clearSettings - Whether to clear the auth settings
|
||||
*/
|
||||
function closeAuthWindow(clearSettings) {
|
||||
if (clearSettings && typeof local.clearSettings === 'function') {
|
||||
local.clearSettings();
|
||||
}
|
||||
|
||||
if (basicAuthWindow) {
|
||||
basicAuthWindow.close();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
openBasicAuthWindow: openBasicAuthWindow
|
||||
|
@ -52,4 +52,15 @@ ipc.on('hostname', (event, host) => {
|
||||
if (hostname){
|
||||
hostname.innerHTML = host || 'unknown';
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Triggered if user credentials are invalid
|
||||
*/
|
||||
ipc.on('isValidCredentials', (event, isValidCredentials) => {
|
||||
let credentialsError = document.getElementById('credentialsError');
|
||||
|
||||
if (credentialsError){
|
||||
credentialsError.style.display = isValidCredentials ? 'none' : 'block'
|
||||
}
|
||||
});
|
35
js/config.js
35
js/config.js
@ -382,6 +382,34 @@ function clearCachedConfigs() {
|
||||
globalConfig = null;
|
||||
}
|
||||
|
||||
function readConfigFileSync() {
|
||||
|
||||
let configPath;
|
||||
let globalConfigFileName = path.join('config', configFileName);
|
||||
if (isDevEnv) {
|
||||
// for dev env, get config file from asar
|
||||
configPath = path.join(app.getAppPath(), globalConfigFileName);
|
||||
} else {
|
||||
// for non-dev, config file is placed by installer relative to exe.
|
||||
// this is so the config can be easily be changed post install.
|
||||
let execPath = path.dirname(app.getPath('exe'));
|
||||
// for mac exec is stored in subdir, for linux/windows config
|
||||
// dir is in the same location.
|
||||
configPath = path.join(execPath, isMac ? '..' : '', globalConfigFileName);
|
||||
}
|
||||
|
||||
let data = fs.readFileSync(configPath);
|
||||
|
||||
try {
|
||||
return JSON.parse(data);
|
||||
} catch (err) {
|
||||
console.log("parsing config failed", err);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
configFileName,
|
||||
@ -395,6 +423,11 @@ module.exports = {
|
||||
|
||||
// items below here are only exported for testing, do NOT use!
|
||||
saveUserConfig,
|
||||
clearCachedConfigs
|
||||
clearCachedConfigs,
|
||||
|
||||
readConfigFileSync,
|
||||
|
||||
// use only if you specifically need to read global config fields
|
||||
getGlobalConfigField,
|
||||
|
||||
};
|
||||
|
@ -12,7 +12,8 @@
|
||||
// renderer process, this will have to do. See github issue posted here to
|
||||
// electron: https://github.com/electron/electron/issues/9312
|
||||
|
||||
const { ipcRenderer } = require('electron');
|
||||
const { ipcRenderer, remote } = require('electron');
|
||||
const { isWindowsOS } = require('../utils/misc');
|
||||
|
||||
let nextId = 0;
|
||||
let includes = [].includes;
|
||||
@ -52,6 +53,17 @@ function getSources(options, callback) {
|
||||
};
|
||||
}
|
||||
|
||||
if (isWindowsOS) {
|
||||
/**
|
||||
* Sets the captureWindow to false if Desktop composition
|
||||
* is disabled otherwise true
|
||||
*
|
||||
* Setting captureWindow to false returns only screen sources
|
||||
* @type {boolean}
|
||||
*/
|
||||
captureWindow = remote.systemPreferences.isAeroGlassEnabled();
|
||||
}
|
||||
|
||||
id = getNextId();
|
||||
ipcRenderer.send('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', captureWindow, captureScreen, updatedOptions.thumbnailSize, id);
|
||||
|
||||
|
@ -3,6 +3,8 @@
|
||||
const electron = require('electron');
|
||||
|
||||
const basicAuth = require('../basicAuth');
|
||||
let currentAuthURL;
|
||||
let tries = 0;
|
||||
|
||||
/**
|
||||
* Having a proxy or hosts that requires authentication will allow user to
|
||||
@ -12,14 +14,33 @@ electron.app.on('login', (event, webContents, request, authInfo, callback) => {
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
// This check is to determine whether the request is for the same
|
||||
// host if so then increase the login tries from which we can
|
||||
// display invalid credentials
|
||||
if (currentAuthURL !== request.url) {
|
||||
currentAuthURL = request.url;
|
||||
} else {
|
||||
tries++
|
||||
}
|
||||
|
||||
// name of the host to display
|
||||
let hostname = authInfo.host || authInfo.realm;
|
||||
let browserWin = electron.BrowserWindow.fromWebContents(webContents);
|
||||
let windowName = browserWin.winName || '';
|
||||
|
||||
/**
|
||||
* Method that resets currentAuthURL and tries
|
||||
* if user closes the auth window
|
||||
*/
|
||||
function clearSettings() {
|
||||
callback();
|
||||
currentAuthURL = '';
|
||||
tries = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens an electron modal window in which
|
||||
* user can enter credentials fot the host
|
||||
*/
|
||||
basicAuth.openBasicAuthWindow(windowName, hostname, callback);
|
||||
basicAuth.openBasicAuthWindow(windowName, hostname, tries === 0, clearSettings, callback);
|
||||
});
|
||||
|
71
js/main.js
71
js/main.js
@ -11,7 +11,7 @@ const AutoLaunch = require('auto-launch');
|
||||
const urlParser = require('url');
|
||||
|
||||
// Local Dependencies
|
||||
const {getConfigField, updateUserConfigWin, updateUserConfigMac} = require('./config.js');
|
||||
const {getConfigField, updateUserConfigWin, updateUserConfigMac, readConfigFileSync} = require('./config.js');
|
||||
const {setCheckboxValues} = require('./menus/menuTemplate.js');
|
||||
const { isMac, isDevEnv } = require('./utils/misc.js');
|
||||
const protocolHandler = require('./protocolHandler');
|
||||
@ -81,8 +81,10 @@ const shouldQuit = app.makeSingleInstance((argv) => {
|
||||
processProtocolAction(argv);
|
||||
});
|
||||
|
||||
// quit if another instance is already running, ignore for dev env
|
||||
if (!isDevEnv && shouldQuit) {
|
||||
let allowMultiInstance = getCmdLineArg(process.argv, '--multiInstance', true) || isDevEnv;
|
||||
|
||||
// quit if another instance is already running, ignore for dev env or if app was started with multiInstance flag
|
||||
if (!allowMultiInstance && shouldQuit) {
|
||||
app.quit();
|
||||
}
|
||||
|
||||
@ -103,6 +105,39 @@ if (isMac) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets chrome authentication flags in electron
|
||||
*/
|
||||
function setChromeFlags() {
|
||||
|
||||
log.send(logLevels.INFO, 'checking if we need to set custom chrome flags!');
|
||||
|
||||
// Read the config parameters synchronously
|
||||
let config = readConfigFileSync();
|
||||
|
||||
// If we cannot find any config, just skip setting any flags
|
||||
if (config && config !== null && config.customFlags) {
|
||||
|
||||
log.send(logLevels.INFO, 'Chrome flags config found!');
|
||||
|
||||
// If we cannot find the authServerWhitelist config, move on
|
||||
if (config.customFlags.authServerWhitelist && config.customFlags.authServerWhitelist !== "") {
|
||||
log.send(logLevels.INFO, 'Setting auth server whitelist flag');
|
||||
app.commandLine.appendSwitch('auth-server-whitelist', config.customFlags.authServerWhitelist);
|
||||
}
|
||||
|
||||
// If we cannot find the authNegotiateDelegateWhitelist config, move on
|
||||
if (config.customFlags.authNegotiateDelegateWhitelist && config.customFlags.authNegotiateDelegateWhitelist !== "") {
|
||||
log.send(logLevels.INFO, 'Setting auth negotiate delegate whitelist flag');
|
||||
app.commandLine.appendSwitch('auth-negotiate-delegate-whitelist', config.customFlags.authNegotiateDelegateWhitelist);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Set the chrome flags
|
||||
setChromeFlags();
|
||||
|
||||
/**
|
||||
* This method will be called when Electron has finished
|
||||
* initialization and is ready to create browser windows.
|
||||
@ -176,6 +211,12 @@ function setupThenOpenMainWindow() {
|
||||
// allows installer to launch app and set appropriate global / user config params.
|
||||
let hasInstallFlag = getCmdLineArg(process.argv, '--install', true);
|
||||
let perUserInstall = getCmdLineArg(process.argv, '--peruser', true);
|
||||
let customDataArg = getCmdLineArg(process.argv, '--userDataPath=', false);
|
||||
|
||||
if (customDataArg && customDataArg.split('=').length > 1) {
|
||||
let customDataFolder = customDataArg.split('=')[1];
|
||||
app.setPath('userData', customDataFolder);
|
||||
}
|
||||
if (!isMac && hasInstallFlag) {
|
||||
getConfigField('launchOnStartup')
|
||||
.then(setStartup)
|
||||
@ -193,9 +234,8 @@ function setupThenOpenMainWindow() {
|
||||
let launchOnStartup = process.argv[3];
|
||||
// We wire this in via the post install script
|
||||
// to get the config file path where the app is installed
|
||||
let appGlobalConfigPath = process.argv[2];
|
||||
setStartup(launchOnStartup)
|
||||
.then(() => updateUserConfigMac(appGlobalConfigPath))
|
||||
.then(updateUserConfigMac)
|
||||
.then(app.quit)
|
||||
.catch(app.quit);
|
||||
return;
|
||||
@ -214,18 +254,15 @@ function setupThenOpenMainWindow() {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function setStartup(lStartup) {
|
||||
return symphonyAutoLauncher.isEnabled()
|
||||
.then(function(isEnabled) {
|
||||
if (!isEnabled && lStartup) {
|
||||
return symphonyAutoLauncher.enable();
|
||||
}
|
||||
|
||||
if (isEnabled && !lStartup) {
|
||||
return symphonyAutoLauncher.disable();
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
return new Promise((resolve) => {
|
||||
let launchOnStartup = (lStartup === 'true');
|
||||
if (launchOnStartup) {
|
||||
symphonyAutoLauncher.enable();
|
||||
return resolve();
|
||||
}
|
||||
symphonyAutoLauncher.disable();
|
||||
return resolve();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -65,7 +65,23 @@ const template = [{
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Open Crashes Directory',
|
||||
label: 'Set Downloads Directory',
|
||||
click() {
|
||||
electron.dialog.showOpenDialog({
|
||||
title: 'Select Downloads Directory',
|
||||
buttonLabel: 'Select',
|
||||
properties: ['openDirectory', 'createDirectory']
|
||||
}, (filePaths) => {
|
||||
if (!filePaths || !Array.isArray(filePaths) || filePaths.length < 1) {
|
||||
return;
|
||||
}
|
||||
updateConfigField('downloadsDirectory', filePaths[0]);
|
||||
eventEmitter.emit('setDownloadsDirectory', filePaths[0]);
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Open Crashes Directory',
|
||||
click() {
|
||||
const crashesDirectory = electron.crashReporter.getCrashesDirectory() + '/completed';
|
||||
electron.shell.showItemInFolder(crashesDirectory);
|
||||
|
@ -23,7 +23,7 @@ function setStyle(config) {
|
||||
let image = notiDoc.getElementById('image');
|
||||
let logo = notiDoc.getElementById('symphony-logo');
|
||||
let title = notiDoc.getElementById('title');
|
||||
let pod = notiDoc.getElementById('pod');
|
||||
let company = notiDoc.getElementById('company');
|
||||
let message = notiDoc.getElementById('message');
|
||||
let close = notiDoc.getElementById('close');
|
||||
|
||||
@ -45,7 +45,7 @@ function setStyle(config) {
|
||||
|
||||
setStyleOnDomElement(config.defaultStyleTitle, title);
|
||||
|
||||
setStyleOnDomElement(config.defaultStylePod, pod);
|
||||
setStyleOnDomElement(config.defaultStyleCompany, company);
|
||||
|
||||
setStyleOnDomElement(config.defaultStyleText, message);
|
||||
|
||||
@ -79,7 +79,13 @@ function setContents(event, notificationObj) {
|
||||
|
||||
let notiDoc = window.document;
|
||||
|
||||
// All the required DOM elements to update the content
|
||||
let container = notiDoc.getElementById('container');
|
||||
let titleDoc = notiDoc.getElementById('title');
|
||||
let companyDoc = notiDoc.getElementById('company');
|
||||
let messageDoc = notiDoc.getElementById('message');
|
||||
let imageDoc = notiDoc.getElementById('image');
|
||||
let closeButton = notiDoc.getElementById('close');
|
||||
|
||||
if (notificationObj.color) {
|
||||
container.style.backgroundColor = notificationObj.color;
|
||||
@ -88,13 +94,9 @@ function setContents(event, notificationObj) {
|
||||
if (notificationObj.color.match(whiteColorRegExp)) {
|
||||
logo.src = './assets/symphony-logo-black.png';
|
||||
} else {
|
||||
let title = notiDoc.getElementById('title');
|
||||
let pod = notiDoc.getElementById('pod');
|
||||
let message = notiDoc.getElementById('message');
|
||||
|
||||
message.style.color = '#ffffff';
|
||||
title.style.color = '#ffffff';
|
||||
pod.style.color = notificationObj.color;
|
||||
messageDoc.style.color = '#ffffff';
|
||||
titleDoc.style.color = '#ffffff';
|
||||
companyDoc.style.color = notificationObj.color;
|
||||
logo.src = './assets/symphony-logo-white.png';
|
||||
}
|
||||
}
|
||||
@ -111,24 +113,26 @@ function setContents(event, notificationObj) {
|
||||
}
|
||||
|
||||
// Title
|
||||
let titleDoc = notiDoc.getElementById('title');
|
||||
titleDoc.innerHTML = notificationObj.title || '';
|
||||
|
||||
// message
|
||||
let messageDoc = notiDoc.getElementById('message');
|
||||
messageDoc.innerHTML = notificationObj.text || '';
|
||||
|
||||
// Image
|
||||
let imageDoc = notiDoc.getElementById('image');
|
||||
if (notificationObj.image) {
|
||||
imageDoc.src = notificationObj.image;
|
||||
} else {
|
||||
setStyleOnDomElement({ display: 'none'}, imageDoc);
|
||||
}
|
||||
|
||||
const winId = notificationObj.windowId;
|
||||
// Company
|
||||
if (notificationObj.company) {
|
||||
companyDoc.innerHTML = notificationObj.company
|
||||
} else {
|
||||
messageDoc.style.marginTop = '15px';
|
||||
}
|
||||
|
||||
let closeButton = notiDoc.getElementById('close');
|
||||
const winId = notificationObj.windowId;
|
||||
|
||||
// note: use onclick because we only want one handler, for case
|
||||
// when content gets overwritten by notf with same tag
|
||||
|
@ -7,7 +7,7 @@
|
||||
</div>
|
||||
<div id="header">
|
||||
<span id="title"></span>
|
||||
<span id="pod"></span>
|
||||
<span id="company"></span>
|
||||
<span id="message"></span>
|
||||
</div>
|
||||
<div id="picture">
|
||||
|
@ -76,7 +76,7 @@ let config = {
|
||||
defaultStyleHeader: {
|
||||
width: 245,
|
||||
minWidth: 230,
|
||||
margin: "12px 10px"
|
||||
margin: "10px 10px"
|
||||
},
|
||||
defaultStyleImage: {
|
||||
height: 43,
|
||||
@ -102,7 +102,7 @@ let config = {
|
||||
webkitLineClamp: 1,
|
||||
webkitBoxOrient: 'vertical',
|
||||
},
|
||||
defaultStylePod: {
|
||||
defaultStyleCompany: {
|
||||
fontFamily: 'sans-serif',
|
||||
fontSize: 11,
|
||||
color: '#adadad',
|
||||
@ -116,7 +116,7 @@ let config = {
|
||||
fontFamily: 'sans-serif',
|
||||
fontSize: 12,
|
||||
color: '#4a4a4a',
|
||||
marginTop: 12,
|
||||
marginTop: 5,
|
||||
overflow: 'hidden',
|
||||
display: '-webkit-box',
|
||||
webkitLineClamp: 1,
|
||||
|
@ -41,6 +41,7 @@ class Notify {
|
||||
color: options.color,
|
||||
tag: options.tag,
|
||||
sticky: options.sticky || false,
|
||||
company: options.company,
|
||||
onShowFunc: onShow.bind(this),
|
||||
onClickFunc: onClick.bind(this),
|
||||
onCloseFunc: onClose.bind(this),
|
||||
|
@ -4,11 +4,13 @@ const isDevEnv = process.env.ELECTRON_DEV ?
|
||||
process.env.ELECTRON_DEV.trim().toLowerCase() === 'true' : false;
|
||||
|
||||
const isMac = (process.platform === 'darwin');
|
||||
const isWindowsOS = (process.platform === 'win32');
|
||||
|
||||
const isNodeEnv = !!process.env.NODE_ENV;
|
||||
|
||||
module.exports = {
|
||||
isDevEnv: isDevEnv,
|
||||
isMac: isMac,
|
||||
isWindowsOS: isWindowsOS,
|
||||
isNodeEnv: isNodeEnv
|
||||
};
|
||||
|
120
js/utils/whitelistHandler.js
Normal file
120
js/utils/whitelistHandler.js
Normal file
@ -0,0 +1,120 @@
|
||||
'use strict';
|
||||
|
||||
const { getGlobalConfigField } = require('./../config.js');
|
||||
const parseDomain = require('parse-domain');
|
||||
const isEqual = require('lodash.isequal');
|
||||
|
||||
/**
|
||||
* Loops through the list of whitelist urls
|
||||
* @param url {String} - url the electron is navigated to
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function isWhitelisted(url) {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
getGlobalConfigField('whitelistUrl').then((whitelist) => {
|
||||
|
||||
if (checkWhitelist(url, whitelist)) {
|
||||
return resolve();
|
||||
}
|
||||
|
||||
return reject(new Error('URL does not match with the whitelist'));
|
||||
|
||||
}).catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that compares url against a list of whitelist
|
||||
* returns true if hostName or domain present in the whitelist
|
||||
* @param url {String} - url the electron is navigated to
|
||||
* @param whitelist {String} - coma separated whitelists
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function checkWhitelist(url, whitelist) {
|
||||
let whitelistArray = whitelist.split(',');
|
||||
const parsedUrl = parseDomain(url);
|
||||
|
||||
if (!parsedUrl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!whitelist) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!whitelistArray.length || whitelistArray.indexOf('*') !== -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return whitelistArray.some((whitelistHost) => {
|
||||
let parsedWhitelist = parseDomain(whitelistHost);
|
||||
|
||||
if (!parsedWhitelist) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return matchDomains(parsedUrl, parsedWhitelist);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches the respective hostName
|
||||
* @param parsedUrl {Object} - parsed url
|
||||
* @param parsedWhitelist {Object} - parsed whitelist
|
||||
*
|
||||
* example:
|
||||
* matchDomain({ subdomain: www, domain: example, tld: com }, { subdomain: app, domain: example, tld: com })
|
||||
*
|
||||
* @returns {*}
|
||||
*/
|
||||
function matchDomains(parsedUrl, parsedWhitelist) {
|
||||
|
||||
if (isEqual(parsedUrl, parsedWhitelist)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const hostNameFromUrl = parsedUrl.domain + parsedUrl.tld;
|
||||
const hostNameFromWhitelist = parsedWhitelist.domain + parsedWhitelist.tld;
|
||||
|
||||
if (!parsedWhitelist.subdomain) {
|
||||
return hostNameFromUrl === hostNameFromWhitelist
|
||||
}
|
||||
|
||||
return hostNameFromUrl === hostNameFromWhitelist && matchSubDomains(parsedUrl.subdomain, parsedWhitelist.subdomain);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches the last occurrence in the sub-domain
|
||||
* @param subDomainUrl {String} - sub-domain from url
|
||||
* @param subDomainWhitelist {String} - sub-domain from whitelist
|
||||
*
|
||||
* example: matchSubDomains('www', 'app')
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function matchSubDomains(subDomainUrl, subDomainWhitelist) {
|
||||
|
||||
if (subDomainUrl === subDomainWhitelist) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const subDomainUrlArray = subDomainUrl.split('.');
|
||||
const lastCharSubDomainUrl = subDomainUrlArray[subDomainUrlArray.length - 1];
|
||||
|
||||
const subDomainWhitelistArray = subDomainWhitelist.split('.');
|
||||
const lastCharWhitelist = subDomainWhitelistArray[subDomainWhitelistArray.length - 1];
|
||||
|
||||
return lastCharSubDomainUrl === lastCharWhitelist;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isWhitelisted,
|
||||
|
||||
// items below here are only exported for testing, do NOT use!
|
||||
checkWhitelist
|
||||
|
||||
};
|
@ -1,5 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const electron = require('electron');
|
||||
const app = electron.app;
|
||||
const crashReporter = electron.crashReporter;
|
||||
@ -21,6 +22,7 @@ const throttle = require('./utils/throttle.js');
|
||||
const { getConfigField, updateConfigField } = require('./config.js');
|
||||
const { isMac, isNodeEnv } = require('./utils/misc');
|
||||
const { deleteIndexFolder } = require('./search/search.js');
|
||||
const { isWhitelisted } = require('./utils/whitelistHandler');
|
||||
|
||||
// show dialog when certificate errors occur
|
||||
require('./dialogs/showCertError.js');
|
||||
@ -37,6 +39,7 @@ let alwaysOnTop = false;
|
||||
let position = 'lower-right';
|
||||
let display;
|
||||
let sandboxed = false;
|
||||
let downloadsDirectory;
|
||||
|
||||
// note: this file is built using browserify in prebuild step.
|
||||
const preloadMainScript = path.join(__dirname, 'preload/_preloadMain.js');
|
||||
@ -128,8 +131,8 @@ function doCreateMainWindow(initialUrl, initialBounds) {
|
||||
newWinOpts.width = bounds.width;
|
||||
newWinOpts.height = bounds.height;
|
||||
} else {
|
||||
newWinOpts.width = 1024;
|
||||
newWinOpts.height = 768;
|
||||
newWinOpts.width = 900;
|
||||
newWinOpts.height = 900;
|
||||
}
|
||||
|
||||
// will center on screen if values not provided
|
||||
@ -238,11 +241,36 @@ function doCreateMainWindow(initialUrl, initialBounds) {
|
||||
|
||||
mainWindow.on('closed', destroyAllWindows);
|
||||
|
||||
// if an user has set a custom downloads directory,
|
||||
// we get that data from the user config file
|
||||
getConfigField('downloadsDirectory')
|
||||
.then((value) => {
|
||||
downloadsDirectory = value;
|
||||
// if the directory has been deleted, try creating it.
|
||||
if (!fs.existsSync(downloadsDirectory)) {
|
||||
const directoryCreated = fs.mkdirSync(downloadsDirectory);
|
||||
// If the directory creation failed, we use the default downloads directory
|
||||
if (!directoryCreated) {
|
||||
downloadsDirectory = null;
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
log.send(logLevels.ERROR, 'Could not find the downloads directory config -> ' + error);
|
||||
});
|
||||
|
||||
// Manage File Downloads
|
||||
mainWindow.webContents.session.on('will-download', (event, item, webContents) => {
|
||||
|
||||
// When download is in progress, send necessary data to indicate the same
|
||||
webContents.send('downloadProgress');
|
||||
|
||||
// if the user has set a custom downloads directory, save file to that directory
|
||||
// if otherwise, we save it to the operating system's default downloads directory
|
||||
if (downloadsDirectory) {
|
||||
item.setSavePath(downloadsDirectory + "/" + item.getFilename());
|
||||
}
|
||||
|
||||
// Send file path when download is complete
|
||||
item.once('done', (e, state) => {
|
||||
if (state === 'completed') {
|
||||
@ -257,11 +285,6 @@ function doCreateMainWindow(initialUrl, initialBounds) {
|
||||
});
|
||||
});
|
||||
|
||||
// To delete the user index data folder on navigation
|
||||
mainWindow.webContents.on('will-navigate', () => {
|
||||
deleteIndexFolder();
|
||||
});
|
||||
|
||||
getConfigField('url')
|
||||
.then(initializeCrashReporter)
|
||||
.catch(app.quit);
|
||||
@ -353,6 +376,12 @@ function doCreateMainWindow(initialUrl, initialBounds) {
|
||||
if (browserWin) {
|
||||
log.send(logLevels.INFO, 'loaded pop-out window url: ' + newWinParsedUrl);
|
||||
|
||||
if (!isMac) {
|
||||
// Removes the menu bar from the pop-out window
|
||||
// setMenu is currently only supported on Windows and Linux
|
||||
browserWin.setMenu(null);
|
||||
}
|
||||
|
||||
getConfigField('url')
|
||||
.then((podUrl) => {
|
||||
getConfigField('crashReporter')
|
||||
@ -435,6 +464,21 @@ function doCreateMainWindow(initialUrl, initialBounds) {
|
||||
}
|
||||
});
|
||||
|
||||
// whenever the main window is navigated for ex: window.location.href or url redirect
|
||||
mainWindow.webContents.on('will-navigate', function(event, navigatedURL) {
|
||||
deleteIndexFolder();
|
||||
isWhitelisted(navigatedURL)
|
||||
.catch(() => {
|
||||
event.preventDefault();
|
||||
electron.dialog.showMessageBox(mainWindow, {
|
||||
type: 'warning',
|
||||
buttons: [ 'Ok' ],
|
||||
title: 'Not Allowed',
|
||||
message: `Sorry, you are not allowed to access this website (${navigatedURL}), please contact your administrator for more details`,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -590,6 +634,13 @@ function isAlwaysOnTop(boolean) {
|
||||
browserWins.forEach(function (browser) {
|
||||
browser.setAlwaysOnTop(boolean);
|
||||
});
|
||||
|
||||
// An issue where changing the alwaysOnTop property
|
||||
// focus the pop-out window
|
||||
// Issue - Electron-209
|
||||
if (mainWindow && mainWindow.winName) {
|
||||
activate(mainWindow.winName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -598,6 +649,11 @@ eventEmitter.on('isAlwaysOnTop', (boolean) => {
|
||||
isAlwaysOnTop(boolean);
|
||||
});
|
||||
|
||||
// set downloads directory
|
||||
eventEmitter.on('setDownloadsDirectory', (newDirectory) => {
|
||||
downloadsDirectory = newDirectory;
|
||||
});
|
||||
|
||||
// node event emitter for notification settings
|
||||
eventEmitter.on('notificationSettings', (notificationSettings) => {
|
||||
position = notificationSettings.position;
|
||||
|
@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "Symphony",
|
||||
"productName": "Symphony",
|
||||
"version": "2.0.0",
|
||||
"buildNumber": "",
|
||||
"version": "2.2.0",
|
||||
"buildNumber": "124",
|
||||
"description": "Symphony desktop app (Foundation ODP)",
|
||||
"author": "Symphony",
|
||||
"main": "js/main.js",
|
||||
@ -112,9 +112,11 @@
|
||||
"ffi": "^2.2.0",
|
||||
"filesize": "^3.5.10",
|
||||
"keymirror": "0.1.1",
|
||||
"lodash.difference": "^4.5.0",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"lodash.omit": "^4.5.0",
|
||||
"lodash.pick": "^4.4.0",
|
||||
"lodash.difference": "^4.5.0",
|
||||
"parse-domain": "^2.0.0",
|
||||
"randomstring": "^1.1.5",
|
||||
"ref": "^1.3.4",
|
||||
"shell-path": "^2.1.0",
|
||||
|
74
tests/utils/whitelist.test.js
Normal file
74
tests/utils/whitelist.test.js
Normal file
@ -0,0 +1,74 @@
|
||||
const { checkWhitelist } = require('../../js/utils/whitelistHandler');
|
||||
|
||||
describe('validate url with whitelist', function() {
|
||||
|
||||
describe('checkWhitelist truth tests', function() {
|
||||
|
||||
it('should return true when the url is valid', function() {
|
||||
const whitelist = 'www.symphony.com, app.symphony.com, my.symphony.com';
|
||||
const url = 'https://my.symphony.com/';
|
||||
|
||||
return expect(checkWhitelist(url, whitelist)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return true when if hostName is defined', function() {
|
||||
const whitelist = 'www.symphony.com, app.symphony.com, symphony.com';
|
||||
const url = 'https://xyz.symphony.com/';
|
||||
|
||||
return expect(checkWhitelist(url, whitelist)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return true when the first occurrence of sub-domain is matched', function() {
|
||||
const whitelist = 'www.symphony.com, app.symphony.com, my.symphony.com';
|
||||
const url = 'https://xyz.my.symphony.com/';
|
||||
|
||||
return expect(checkWhitelist(url, whitelist)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return true when for any URL if whitelist has *', function() {
|
||||
const whitelist = '*';
|
||||
const url = 'https://www.example.com/';
|
||||
|
||||
return expect(checkWhitelist(url, whitelist)).toBeTruthy();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('checkWhitelist falsity tests', function () {
|
||||
|
||||
it('should return false when sub-domain does not match', function () {
|
||||
const whitelist = 'www.symphony.com, app.symphony.com, my.symphony.com';
|
||||
const url = 'https://xyz.symphony.com/';
|
||||
|
||||
return expect(checkWhitelist(url, whitelist)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should return false when hostName does not match', function () {
|
||||
const whitelist = 'www.symphony.com, app.symphony.com, my.symphony.com';
|
||||
const url = 'https://my.example.com/';
|
||||
|
||||
return expect(checkWhitelist(url, whitelist)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should return false when the URL is invalid', function () {
|
||||
const whitelist = 'www.symphony.com, app.symphony.com, my.symphony.com';
|
||||
const url = 'invalidUrl';
|
||||
|
||||
return expect(checkWhitelist(url, whitelist)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should return false when the whitelist is invalid', function () {
|
||||
const whitelist = 'invalidWhitelist';
|
||||
const url = 'https://www.symphony.com';
|
||||
|
||||
return expect(checkWhitelist(url, whitelist)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should return false if whitelist is empty', function() {
|
||||
const whitelist = '';
|
||||
const url = 'https://www.example.com/';
|
||||
|
||||
return expect(checkWhitelist(url, whitelist)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user