diff --git a/app/lib/index.js b/app/lib/index.js index bc045574ca..4dbd0a1109 100644 --- a/app/lib/index.js +++ b/app/lib/index.js @@ -690,7 +690,8 @@ function bootstrap() { errorTracking.setTag(Sentry, 'plugins', generatePluginsTag(plugins)); // (9) zeebe API - const zeebeAPI = new ZeebeAPI({ readFile }, ZeebeNode, flags); + const zeebeCustomCertificatePath = flags.get('zeebe-ssl-certificate'); + const zeebeAPI = new ZeebeAPI({ readFile }, ZeebeNode, zeebeCustomCertificatePath); // (10) connector templates if (flags.get('enable-connector-templates', false)) { diff --git a/app/lib/menu/menu-builder.js b/app/lib/menu/menu-builder.js index 59d88e2fcf..e74189ddc8 100644 --- a/app/lib/menu/menu-builder.js +++ b/app/lib/menu/menu-builder.js @@ -75,6 +75,8 @@ class MenuBuilder { .appendExportAs() .appendCloseTab() .appendSeparator() + .appendSettings() + .appendSeparator() .appendQuit() .get() ); @@ -350,6 +352,17 @@ class MenuBuilder { return this; } + appendSettings() { + this.menu.append(new MenuItem({ + label: 'Settings', + click: function() { + app.emit('menu:action', 'open-settings'); + } + })); + + return this; + } + appendMenuItem(builder, menuItem) { const { accelerator, diff --git a/app/lib/zeebe-api/zeebe-api.js b/app/lib/zeebe-api/zeebe-api.js index 67b11ba15b..b7ba116ee5 100644 --- a/app/lib/zeebe-api/zeebe-api.js +++ b/app/lib/zeebe-api/zeebe-api.js @@ -110,11 +110,11 @@ const CLIENT_OPTIONS_SECRETS = [ */ class ZeebeAPI { - constructor(fs, ZeebeNode, flags, log = createLog('app:zeebe-api')) { + constructor(fs, ZeebeNode, customCertificatePath, log = createLog('app:zeebe-api')) { this._fs = fs; this._ZeebeNode = ZeebeNode; - this._flags = flags; + this._customCertificatePath = customCertificatePath; this._log = log; this._zeebeClient = null; @@ -400,8 +400,8 @@ class ZeebeAPI { useTLS: options.useTLS || /^https:\/\//.test(url) }; - // (1) use certificate from flag - const customCertificatePath = this._flags.get('zeebe-ssl-certificate'); + // (1) use certificate from custom path + const customCertificatePath = this._customCertificatePath; if (customCertificatePath) { const cert = this._readRootCertificate(customCertificatePath); diff --git a/app/test/spec/zeebe-api/zeebe-api-spec.js b/app/test/spec/zeebe-api/zeebe-api-spec.js index 41ac0153d3..074c3c9211 100644 --- a/app/test/spec/zeebe-api/zeebe-api-spec.js +++ b/app/test/spec/zeebe-api/zeebe-api-spec.js @@ -2148,11 +2148,7 @@ describe('ZeebeAPI', function() { deployResource: noop }; }, - flags: { - get() { - return '/path/to/cert.pem'; - } - }, + customCertificatePath: '/path/to/cert.pem', fs: { readFile() { return { contents: certificate }; @@ -2173,7 +2169,7 @@ describe('ZeebeAPI', function() { } - it('should pass root certificate from flag', async () => { + it('should pass root certificate', async () => { // given const cert = readFile('./root-self-signed.pem'); @@ -2531,9 +2527,7 @@ function mockZeebeNode(options = {}) { const fs = options.fs || { readFile: () => ({}) }; - const flags = options.flags || { - get: () => {} - }; + const customCertificatePath = options.customCertificatePath; const log = { error() {}, debug() {}, @@ -2553,7 +2547,7 @@ function mockZeebeNode(options = {}) { } }; - return new ZeebeAPI(fs, ZeebeNode, flags, log); + return new ZeebeAPI(fs, ZeebeNode, customCertificatePath, log); } function noop() {} diff --git a/client/src/app/App.js b/client/src/app/App.js index 5e1ac3ed40..fa2cf3e5ee 100644 --- a/client/src/app/App.js +++ b/client/src/app/App.js @@ -78,6 +78,13 @@ export const EMPTY_TAB = { type: 'empty' }; +export const SETTINGS_TAB = { + id: '__settings', + type: 'settings', + name: 'Settings', + title: 'Settings' +}; + const ENCODING_UTF8 = 'utf8'; const FILTER_ALL_EXTENSIONS = { @@ -380,11 +387,14 @@ export class App extends PureComponent { * @return {Promise} resolved to true if tab can be safely closed */ saveBeforeClose = async (tab) => { - const { file } = tab; - - const { name } = file; if (this.isDirty(tab)) { + const { + file: { + name + } + } = tab; + const { button } = await this.showCloseFileDialog({ name }); if (button === 'save') { @@ -471,6 +481,10 @@ export class App extends PureComponent { return tab === EMPTY_TAB; }; + isSettingsTab = (tab) => { + return tab === SETTINGS_TAB; + }; + isDirty = (tab) => { return !!this.state.dirtyTabs[tab.id]; }; @@ -1603,6 +1617,14 @@ export class App extends PureComponent { return Promise.reject(new Error('no last tab')); }; + openSettings = () => { + if (!this.state.tabs.includes(SETTINGS_TAB)) { + this.addTab(SETTINGS_TAB); + } + + return this.selectTab(SETTINGS_TAB); + }; + showShortcuts = () => this.openModal('KEYBOARD_SHORTCUTS'); /** @@ -1783,6 +1805,10 @@ export class App extends PureComponent { return this.createDiagram('cmmn'); } + if (action === 'open-settings') { + return this.openSettings(); + } + if (action === 'create-form') { return this.createDiagram('form'); } diff --git a/client/src/app/AppParent.js b/client/src/app/AppParent.js index ec90a76ee5..cbb875a06b 100644 --- a/client/src/app/AppParent.js +++ b/client/src/app/AppParent.js @@ -280,11 +280,13 @@ export default class AppParent extends PureComponent { log('restoring / opening files', files, activeFile); - await this.getApp().openFiles(files, activeFile); + await this.getApp().openFiles(files, false); if (typeof onStarted === 'function') { onStarted(); } + + await this.getApp().openSettings(); }; getApp() { diff --git a/client/src/app/RecentTabs.js b/client/src/app/RecentTabs.js index e6dffedad5..f9187c31a7 100644 --- a/client/src/app/RecentTabs.js +++ b/client/src/app/RecentTabs.js @@ -43,6 +43,12 @@ export class RecentTabs { } push(element) { + + // a special tab + if (!element.file) { + return; + } + this.elements = [ ...this.elements .filter(e => (e.file.path !== element.file.path)) @@ -50,4 +56,4 @@ export class RecentTabs { element ]; } -} \ No newline at end of file +} diff --git a/client/src/app/SettingsTab.js b/client/src/app/SettingsTab.js new file mode 100644 index 0000000000..4a1a3db80d --- /dev/null +++ b/client/src/app/SettingsTab.js @@ -0,0 +1,74 @@ +/** + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright + * ownership. + * + * Camunda licenses this file to you under the MIT; you may not use this file + * except in compliance with the MIT License. + */ + +import React, { PureComponent } from 'react'; + +import * as css from './SettingsTab.less'; + +import { + Tab +} from './primitives'; + +import Flags, { DISABLE_DMN, DISABLE_FORM, DISABLE_ZEEBE, DISABLE_PLATFORM } from '../util/Flags'; + + +export default class SettingsTab extends PureComponent { + + componentDidMount() { + this.props.onShown(); + } + + triggerAction() { } + + render() { + + return ( + +
+

Settings

+ +

+ Configure the look and feel of the editor. +

+ +
+

Canvas

+ +

+

+ This setting is overridden via a feature flag. +
+ + +

+
+ +
+

Connectors

+ +

+ +

+
+ +
+

Startup Flags

+ +

Feature flags recognized at application startup, overriding local settings:

+ +
+              { JSON.stringify(Flags.data, 0, 2) }
+            
+
+
+
+ ); + } +} diff --git a/client/src/app/SettingsTab.less b/client/src/app/SettingsTab.less new file mode 100644 index 0000000000..ef7c20d8b1 --- /dev/null +++ b/client/src/app/SettingsTab.less @@ -0,0 +1,45 @@ +:local(.SettingsTab) { + --warning-color: var(--color-yellow-47-88-53); + --warning-background: var(--color-yellow-47-86-91); + + display: flex; + flex-direction: column; + + align-items: flex-start; + justify-content: flex-start; + overflow-y: auto; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + overflow-y: auto; + + font-size: 14px; + font-weight: 400; + font-family: 'IBM Plex Sans'; + + .settings-container { + background-color: var(--color-grey-225-10-97); + color: var(--color-grey-225-10-15); + border-radius: 10px; + width: 100%; + padding: 0 15px; + margin: 7px; + position: relative; + + h2 { + font-size: 20px; + font-weight: 600; + margin: 20px 0 14px 0; + } + } + + .alert-warning { + padding: 12px; + border: solid 1px var(--warning-color); + border-left-width: 3px; + background: var(--warning-background); + margin-bottom: 16px; + } +} diff --git a/client/src/app/TabsProvider.js b/client/src/app/TabsProvider.js index a0ba350ed4..94fdff0164 100644 --- a/client/src/app/TabsProvider.js +++ b/client/src/app/TabsProvider.js @@ -32,6 +32,7 @@ import { } from '../util/Engines'; import EmptyTab from './EmptyTab'; +import SettingsTab from './SettingsTab'; import parseDiagramType from './util/parseDiagramType'; @@ -142,6 +143,17 @@ export default class TabsProvider { return null; } }, + settings: { + canOpen(file) { + return false; + }, + getComponent() { + return SettingsTab; + }, + getIcon() { + return null; + } + }, 'cloud-bpmn': { name: 'BPMN', encoding: ENCODING_UTF8, diff --git a/client/src/plugins/user-journey-statistics/event-handlers/TabEventHandler.js b/client/src/plugins/user-journey-statistics/event-handlers/TabEventHandler.js index 4ac2997d22..941f96bbd6 100644 --- a/client/src/plugins/user-journey-statistics/event-handlers/TabEventHandler.js +++ b/client/src/plugins/user-journey-statistics/event-handlers/TabEventHandler.js @@ -60,6 +60,11 @@ export default class TabEventHandler { type } = tab; + // a special tab + if (!file) { + return; + } + const { contents } = file; diff --git a/package.json b/package.json index eed54df569..448b717d69 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "description": "Camunda Modeler for BPMN, DMN and CMMN, based on bpmn.io", "scripts": { "app:auto-test": "npm run app:test -- --watch", - "app:dev": "electron ./dev.js --force-update-checks resources/diagram/simple.bpmn", + "app:dev": "electron ./dev.js --force-update-checks -- resources/diagram/simple.bpmn", "app:test": "nyc --reporter lcov --exclude \"app/test/**\" --exclude \"app/**/__tests__\" mocha --require ./app/test/expect \"app/**/*-spec.js\"", "app:collect-licenses": "webpack -c webpack.config.license.js", "client:build": "npm run build --workspace=client",