diff --git a/electron/app/js/ipcRendererPreload.js b/electron/app/js/ipcRendererPreload.js index 5ecddae6f..2375690fd 100644 --- a/electron/app/js/ipcRendererPreload.js +++ b/electron/app/js/ipcRendererPreload.js @@ -31,6 +31,7 @@ contextBridge.exposeInMainWorld( send: (channel, ...args) => { const validChannels = [ 'close-window', + 'download-file', 'new-project', 'open-project', 'window-app-quit', diff --git a/electron/app/js/project.js b/electron/app/js/project.js index aa06f07fb..a2e818004 100644 --- a/electron/app/js/project.js +++ b/electron/app/js/project.js @@ -8,6 +8,7 @@ const {getCredentialPassphrase} = require('./promptUtils'); const {copyFile, mkdir, readFile, writeFile} = require('fs/promises'); const path = require('path'); const uuid = require('uuid'); +const { EOL } = require('os'); const model = require('./model'); const modelProperties = require('./modelProperties'); @@ -972,6 +973,32 @@ function getProjectFileName(dialogReturnedFileName) { return result; } +function downloadFile(targetWindow, lines, fileType, format, formatName) { + const title = i18n.t('dialog-saveTitle', {type: fileType}); + const filterName = i18n.t('dialog-saveFilterLabel', {type: formatName}); + + dialog.showSaveDialog(targetWindow, { + title: title, + message: title, + filters: [ + {name: filterName, extensions: [format]} + ], + properties: [ + 'createDirectory', + 'showOverwriteConfirmation' + ] + }).then(saveResponse => { + if (saveResponse.filePath) { + const contents = lines.join(EOL); + writeFile(saveResponse.filePath, contents, { encoding: 'utf8' }) + .catch(err => { + dialog.showErrorBox(title, + i18n.t('dialog-saveFileErrorMessage', { file: saveResponse.filePath, error: err })); + }); + } + }); +} + module.exports = { chooseArchiveFile, chooseModelFile, @@ -980,6 +1007,7 @@ module.exports = { closeProject, confirmProjectFile, createNewProject, + downloadFile, getModelFileContent, getWindowForProject, exportArchiveFile, diff --git a/electron/app/locales/en/electron.json b/electron/app/locales/en/electron.json index 5efd35774..34739b331 100644 --- a/electron/app/locales/en/electron.json +++ b/electron/app/locales/en/electron.json @@ -159,6 +159,9 @@ "dialog-createNewProjectTitle": "Create WebLogic Kubernetes Toolkit Project", "dialog-projectSaveFileLocationNotWritableTitle": "Project Directory Not Writable", "dialog-projectSaveFileLocationNotWritableError": "The {{projectFileDirectory}} directory chosen to write the new project file is not writable by the current process.", + "dialog-saveTitle": "Save {{type}}", + "dialog-saveFilterLabel": "{{type}} Files", + "dialog-saveFileErrorMessage": "Unable to save {{file}}: {{error}}", "dialog-openProjectWindowPrompt": "Choose the window where the project should be opened.", "dialog-chooseDomainHome": "Select the Domain Home directory to use", diff --git a/electron/app/locales/en/webui.json b/electron/app/locales/en/webui.json index ac3405bb1..06314c83c 100644 --- a/electron/app/locales/en/webui.json +++ b/electron/app/locales/en/webui.json @@ -33,6 +33,9 @@ "script-sh-label": "sh Script", "script-ps1-label": "Powershell Script", "script-cmd-label": "Windows CMD Script", + "script-button-download": "Download", + "script-file-type-label": "{{type}} {{subType}}", + "script-resource-file-format": "Resource", "kubectl-form-name": "Kubernetes Client Configuration", "kubectl-title": "Configure the Kubernetes Client", @@ -229,6 +232,7 @@ "image-code-primary-image-script-no-content": "Create New Primary Image is turned off so no content to show.", "image-code-auxiliary-image-script-no-content": "Create New Auxiliary Image is turned off so no content to show.", "image-code-not-creating-images-no-content": "This project is not configured to create any images so no content to show.", + "image-code-script-title": "{{type}} Script", "image-design-form-name": "Image", "image-design-form-primary-tab-name": "Primary Image", diff --git a/electron/app/main.js b/electron/app/main.js index 4ec007e62..b0ee2364b 100644 --- a/electron/app/main.js +++ b/electron/app/main.js @@ -287,6 +287,11 @@ class Main { await currentWindow.close(); }); + ipcMain.on('download-file', (event, lines, fileType, fileFormat, fileFormatName) => { + const window = event.sender.getOwnerBrowserWindow(); + return project.downloadFile(window, lines, fileType, fileFormat, fileFormatName); + }); + // eslint-disable-next-line no-unused-vars ipcMain.on('skip-quickstart-at-startup', async (event) => { userSettings.setSkipQuickstartAtStartup(true); diff --git a/webui/src/js/viewModels/domain-code-view.js b/webui/src/js/viewModels/domain-code-view.js index 6db7701a6..8de287b7c 100644 --- a/webui/src/js/viewModels/domain-code-view.js +++ b/webui/src/js/viewModels/domain-code-view.js @@ -115,6 +115,37 @@ function (accUtils, ko, project, K8sDomainScriptGenerator, K8sDomainConfigMapGen }; this.renderScript(this.selectedSubview()); + + this.downloadInstaller = () => { + const format = this.shellScriptType(); + const generator = new K8sDomainScriptGenerator(format); + const lines = generator.generate(); + const fileType = i18n.t('script-file-type-label', { + type: i18n.t('nav-domain'), + subType: i18n.t('domain-code-script-title') + }); + const formatLabel = this.shellLabelMapper(format + '-label'); + + window.api.ipc.send('download-file', lines, fileType, format, formatLabel); + }; + + this.downloadComponentResource = () => { + const generator = this.getDomainResourceGenerator(); + const lines = generator.generate(); + const fileType = i18n.t('domain-code-domain-resource-title'); + const formatLabel = this.shellLabelMapper('resource-file-format'); + + window.api.ipc.send('download-file', lines, fileType, 'yaml', formatLabel); + }; + + this.downloadConfigMap = () => { + const generator = this.k8sConfigMapGenerator; + const lines = generator.generate(); + const fileType = i18n.t('domain-code-configmap-resource-title'); + const formatLabel = this.shellLabelMapper('resource-file-format'); + + window.api.ipc.send('download-file', lines, fileType, 'yaml', formatLabel); + }; } return DomainCodeViewModel; diff --git a/webui/src/js/viewModels/image-code-view.js b/webui/src/js/viewModels/image-code-view.js index 0d0f3a736..886a1fc80 100644 --- a/webui/src/js/viewModels/image-code-view.js +++ b/webui/src/js/viewModels/image-code-view.js @@ -116,6 +116,18 @@ function(accUtils, ko, i18n, project, ImageScriptGenerator, ArrayDataProvider) { }; this.codeText = ko.observable(); + + this.downloadScript = () => { + const format = this.codeViewScriptLanguage(); + const generator = new ImageScriptGenerator(format); + const auxiliary = this.selectedSubview() === 'auxiliaryImageScript'; + const lines = auxiliary ? generator.generateAuxiliary() : generator.generatePrimary(); + const typeName = this.labelMapper(auxiliary ? 'auxiliary-image-script-tab' : 'primary-image-script-tab'); + const fileType = i18n.t('image-code-script-title', {type: typeName}); + const formatLabel = this.shellLabelMapper(format + '-label'); + + window.api.ipc.send('download-file', lines, fileType, format, formatLabel); + }; } /* diff --git a/webui/src/js/viewModels/ingress-code-view.js b/webui/src/js/viewModels/ingress-code-view.js index b8087f033..a3e803677 100644 --- a/webui/src/js/viewModels/ingress-code-view.js +++ b/webui/src/js/viewModels/ingress-code-view.js @@ -111,6 +111,38 @@ function(accUtils, ko, i18n, project, IngressInstallScriptGenerator, IngressRout const lines = generator.generate(); this.ingressRoutesYamlText(lines.join('\n')); }; + + this.downloadInstaller = () => { + const format = this.shellScriptType(); + const generator = new IngressInstallScriptGenerator(format); + const lines = generator.generate(); + const fileType = i18n.t('script-file-type-label', { + type: i18n.t('nav-ingress'), + subType: i18n.t('ingress-code-install-script-title') + }); + const formatLabel = this.shellLabelMapper(format + '-label'); + + window.api.ipc.send('download-file', lines, fileType, format, formatLabel); + }; + + this.downloadAddRoutesScript = () => { + const format = this.shellScriptType(); + const generator = new IngressRoutesScriptGenerator(format); + const lines = generator.generate(); + const fileType = i18n.t('ingress-code-add-routes-script-title'); + const formatLabel = this.shellLabelMapper(format + '-label'); + + window.api.ipc.send('download-file', lines, fileType, format, formatLabel); + }; + + this.downloadRoutesResource = () => { + const generator = new IngressResourceGenerator(); + const lines = generator.generate(); + const fileType = i18n.t('ingress-code-ingress-yaml-title'); + const formatLabel = this.shellLabelMapper('resource-file-format'); + + window.api.ipc.send('download-file', lines, fileType, 'yaml', formatLabel); + }; } /* diff --git a/webui/src/js/viewModels/operator-code-view.js b/webui/src/js/viewModels/operator-code-view.js index b574654ca..f5fff4c9c 100644 --- a/webui/src/js/viewModels/operator-code-view.js +++ b/webui/src/js/viewModels/operator-code-view.js @@ -50,6 +50,19 @@ function(i18n, accUtils, ko, project, OperatorScriptGenerator, ArrayDataProvider this.shellScriptType(event.detail.value); this.updateCodeText(event.detail.value); }; + + this.download = () => { + const format = this.shellScriptType(); + const generator = new OperatorScriptGenerator(format); + const lines = generator.generate(); + const fileType = i18n.t('script-file-type-label', { + type: i18n.t('nav-operator'), + subType: i18n.t('domain-code-script-title') + }); + const formatLabel = this.shellLabelMapper(format + '-label'); + + window.api.ipc.send('download-file', lines, fileType, format, formatLabel); + }; } /* diff --git a/webui/src/js/viewModels/vz-application-code-view.js b/webui/src/js/viewModels/vz-application-code-view.js index 43ddb9a2d..bf4be3b88 100644 --- a/webui/src/js/viewModels/vz-application-code-view.js +++ b/webui/src/js/viewModels/vz-application-code-view.js @@ -107,6 +107,37 @@ function (accUtils, ko, project, VerrazzanoApplicationScriptGenerator, Verrazzan }; this.renderScript(this.selectedSubview()); + + this.downloadDeployer = () => { + const format = this.shellScriptType(); + const generator = new VerrazzanoApplicationScriptGenerator(format); + const lines = generator.generate(); + const fileType = i18n.t('script-file-type-label', { + type: i18n.t('nav-vz-component'), + subType: i18n.t('vz-install-code-script-title') + }); + const formatLabel = this.shellLabelMapper(format + '-label'); + + window.api.ipc.send('download-file', lines, fileType, format, formatLabel); + }; + + this.downloadApplicationResource = () => { + const generator = this.vzApplicationResourceGenerator; + const lines = generator.generate(); + const fileType = i18n.t('vz-application-code-application-resource-title'); + const formatLabel = this.shellLabelMapper('resource-file-format'); + + window.api.ipc.send('download-file', lines, fileType, 'yaml', formatLabel); + }; + + this.downloadProjectResource = () => { + const generator = this.vzProjectResourceGenerator; + const lines = generator.generate(); + const fileType = i18n.t('vz-application-code-project-resource-title'); + const formatLabel = this.shellLabelMapper('resource-file-format'); + + window.api.ipc.send('download-file', lines, fileType, 'yaml', formatLabel); + }; } return VerrazzanoApplicationCodeViewModel; diff --git a/webui/src/js/viewModels/vz-component-code-view.js b/webui/src/js/viewModels/vz-component-code-view.js index 951459be0..a7a0577eb 100644 --- a/webui/src/js/viewModels/vz-component-code-view.js +++ b/webui/src/js/viewModels/vz-component-code-view.js @@ -108,6 +108,37 @@ function (accUtils, ko, project, VerrazzanoComponentScriptGenerator, VerrazzanoC }; this.renderScript(this.selectedSubview()); + + this.downloadInstaller = () => { + const format = this.shellScriptType(); + const generator = new VerrazzanoComponentScriptGenerator(format); + const lines = generator.generate(); + const fileType = i18n.t('script-file-type-label', { + type: i18n.t('nav-vz-component'), + subType: i18n.t('vz-install-code-script-title') + }); + const formatLabel = this.shellLabelMapper(format + '-label'); + + window.api.ipc.send('download-file', lines, fileType, format, formatLabel); + }; + + this.downloadComponentResource = () => { + const generator = this.vzComponentResourceGenerator; + const lines = generator.generate(); + const fileType = i18n.t('vz-component-code-component-resource-title'); + const formatLabel = this.shellLabelMapper('resource-file-format'); + + window.api.ipc.send('download-file', lines, fileType, 'yaml', formatLabel); + }; + + this.downloadConfigMap = () => { + const generator = this.vzComponentConfigMapGenerator; + const lines = generator.generate(); + const fileType = i18n.t('vz-component-code-configmap-resource-title'); + const formatLabel = this.shellLabelMapper('resource-file-format'); + + window.api.ipc.send('download-file', lines, fileType, 'yaml', formatLabel); + }; } return VerrazzanoComponentCodeViewModel; diff --git a/webui/src/js/viewModels/vz-install-code-view.js b/webui/src/js/viewModels/vz-install-code-view.js index 89e5e5925..03d8f51da 100644 --- a/webui/src/js/viewModels/vz-install-code-view.js +++ b/webui/src/js/viewModels/vz-install-code-view.js @@ -92,6 +92,27 @@ function (accUtils, ko, project, VerrazzanoInstallScriptGenerator, VerrazzanoIns this.renderScript(this.selectedSubview()); + this.downloadInstaller = () => { + const format = this.shellScriptType(); + const generator = new VerrazzanoInstallScriptGenerator(format); + const lines = generator.generate(); + const fileType = i18n.t('script-file-type-label', { + type: i18n.t('nav-verrazzano'), + subType: i18n.t('vz-install-code-script-title') + }); + const formatLabel = this.shellLabelMapper(format + '-label'); + + window.api.ipc.send('download-file', lines, fileType, format, formatLabel); + }; + + this.downloadResource = () => { + const generator = this.vzInstallResourceGenerator; + const lines = generator.generate(); + const fileType = i18n.t('vz-install-code-verrazzano-resource-title'); + const formatLabel = this.shellLabelMapper('resource-file-format'); + + window.api.ipc.send('download-file', lines, fileType, 'yaml', formatLabel); + }; } return VerrazzanoInstallCodeViewModel; diff --git a/webui/src/js/views/domain-code-view.html b/webui/src/js/views/domain-code-view.html index 81f0683eb..15a5e99e5 100644 --- a/webui/src/js/views/domain-code-view.html +++ b/webui/src/js/views/domain-code-view.html @@ -1,5 +1,5 @@
@@ -19,24 +19,37 @@ on-value-changed="[[selectedSubviewValueChangedHandler]]" value="{{selectedSubview}}">
- + + + +
+ + + + +
+ + + + +
diff --git a/webui/src/js/views/image-code-view.html b/webui/src/js/views/image-code-view.html index bdf276648..76b845d6c 100644 --- a/webui/src/js/views/image-code-view.html +++ b/webui/src/js/views/image-code-view.html @@ -18,13 +18,16 @@ class="wkt-code-view-switcher" value="{{tabsStatus}}">
- + + + +
- + + + +
- + + + +
+ + + + +
diff --git a/webui/src/js/views/operator-code-view.html b/webui/src/js/views/operator-code-view.html index 98ac2b191..b949a6ef3 100644 --- a/webui/src/js/views/operator-code-view.html +++ b/webui/src/js/views/operator-code-view.html @@ -3,13 +3,16 @@ Licensed under The Universal Permissive License (UPL), Version 1.0 as shown at https://oss.oracle.com/licenses/upl/ -->
- + + + +
- + + + +
+ + + + +
+ + + + +
diff --git a/webui/src/js/views/vz-component-code-view.html b/webui/src/js/views/vz-component-code-view.html index 05f2992fb..566825bf7 100644 --- a/webui/src/js/views/vz-component-code-view.html +++ b/webui/src/js/views/vz-component-code-view.html @@ -19,24 +19,37 @@ on-value-changed="[[selectedSubviewValueChangedHandler]]" value="{{selectedSubview}}">
- + + + +
+ + + + +
+ + + + +
diff --git a/webui/src/js/views/vz-install-code-view.html b/webui/src/js/views/vz-install-code-view.html index fe5963fd6..48c49c5d6 100644 --- a/webui/src/js/views/vz-install-code-view.html +++ b/webui/src/js/views/vz-install-code-view.html @@ -19,19 +19,27 @@ on-value-changed="[[selectedSubviewValueChangedHandler]]" value="{{selectedSubview}}">
- + + + +
+ + + + +