diff --git a/electron/app/locales/en/webui.json b/electron/app/locales/en/webui.json index 30de76d27..11ed92e70 100644 --- a/electron/app/locales/en/webui.json +++ b/electron/app/locales/en/webui.json @@ -1,6 +1,7 @@ { "copyright-footer-text": "Copyright (c) 2021, {{currentYear}}, Oracle and/or its affiliates. Licensed under The Universal Permissive License (UPL), Version 1.0, as shown at", + "nav-aria-label": "Choose a navigation item", "nav-model": "Model", "nav-project-settings": "Project Settings", "nav-image": "Image", @@ -81,12 +82,14 @@ "project-settings-form-name": "Project Settings", "project-settings-title": "Project Settings", "project-settings-mac-environment-question": "For macOS, do you need to add directories to the PATH or define other environment variables for Docker/Podman or Kubernetes?", + "project-settings-mac-path-table-aria-label": "macOS Extra Path Directories Table", "project-settings-add-path-row-icon-text": "Add Extra Path Directory", "project-settings-choose-path-icon-text": "Choose Extra Path Directory", "project-settings-delete-path-row-icon-text": "Remove Extra Path Directory", "project-settings-extra-path-directory-header": "Extra Path Directory", "project-settings-hints-macos-path-directories": "Show System Path Directories", "project-settings-button-macos-path-directories": "Show System Path", + "project-settings-mac-env-var-table-aria-label": "macOS Extra Environment Variables Table", "project-settings-add-env-var-row-icon-text": "Add Extra Environment Variable", "project-settings-delete-env-var-row-icon-text": "Remove Extra Environment Variable", "project-settings-extra-environment-variable-name-header": "Extra Environment Variable Name", @@ -144,9 +147,11 @@ "project-settings-credential-store-policy-answer-message": "The Credential Store Policy controls how credentials are handled when the project is saved and loaded. The options are to store them in the machine's native credential store, store them in the project file encrypted with a passphrase, or not store them at all.", "macos-path-directories-dialog-title": "System Path", + "macos-path-directories-dialog-table-aria-label": "System Path Environment Variable Directories", "macos-path-directories-dialog-name-header": "Path Directory", "macos-environment-variables-dialog-title": "System Environment Variables", + "macos-environment-variables-dialog-table-aria-label": "System Environment Variables Table", "macos-environment-variables-dialog-name-header": "Environment Variable Name", "macos-environment-variables-dialog-value-header": "Environment Variable Value", @@ -563,6 +568,7 @@ "domain-design-cluster-additional-java-options-label": "Additional Java Options", "domain-design-cluster-additional-java-options-help": "A list of additional Java runtime options, separated by spaces, tabs, or commas.", + "domain-design-configmap-table-aria-label": "ConfigMap Properties Table", "domain-design-configmap-title": "Model Variables Overrides", "domain-design-configmap-label": "Kubernetes ConfigMap Name", "domain-design-configmap-help": "The name of the Kubernetes ConfigMap that holds the model variable overrides.", @@ -599,6 +605,7 @@ "domain-design-propname-header": "Model Variable Name", "domain-design-propvalue-header": "Model Variable File Value", "domain-design-propoverride-header": "Model Variable Override Value", + "domain-design-secrets-table-aria-label": "Secrets Table", "domain-design-secretname-header": "Secret Name", "domain-design-username-header": "Username", "domain-design-password-header": "Password", @@ -645,6 +652,7 @@ "ingress-design-ingress-docker-reg-secret-userpwd-help": "The password used to log in to Docker Hub.", "ingress-design-ingress-docker-reg-secret-useremail": "Docker Hub Email Address", "ingress-design-ingress-docker-reg-secret-useremail-help": "The email address of the user logging in to Docker Hub.", + "ingress-design-route-get-services-title": "Getting services for namespace {{namespace}}", "ingress-design-ingress-routes-title": "Ingress Routes Configuration", "ingress-design-ingress-route-virtualhost-label": "Virtual Host", @@ -652,7 +660,11 @@ "ingress-design-ingress-route-targetservicenamespace-help": "The target service namespace value points to the namespace where the domain is running.", "ingress-design-ingress-route-accesspoint-label": "Access Point", "ingress-design-ingress-route-targetservice-label": "Target Service", + "ingress-design-ingress-route-targetservice-placeholder": "Select Domain Service", + "ingress-design-ingress-route-targetservice-placeholder-empty": "No Domain Services Available", "ingress-design-ingress-route-targetport-label": "Target Port", + "ingress-design-ingress-route-targetport-placeholder": "Select Target Service Port", + "ingress-design-ingress-route-targetport-placeholder-empty": "No Target Service Selected", "ingress-design-ingress-route-path-label": "Path Expression", "ingress-design-ingress-route-tls-label": "Transport Options", "ingress-design-ingress-route-tls-help": "Select the transport option for this ingress route", @@ -663,6 +675,7 @@ "ingress-design-ingress-route-is-console-svc-help": "For SSL terminating at ingress and accessing WebLogic Console Service, turn on this option. Your domain must have 'WeblogicPluginEnabled: true' in the 'resources->WebAppContainer' section", "ingress-design-ingress-route-name-label": "Name", "ingress-design-ingress-route-dialog-title": "Edit Ingress Route", + "ingress-design-ingress-route-annotations-table-aria-label": "Ingress Route Annotations Table", "ingress-design-ingress-route-annotation-label": "Annotation", "ingress-design-ingress-route-annotationValue-label": "Value", "ingress-design-ingress-annotations": "Optional Annotations", @@ -705,6 +718,7 @@ "ingress-design-generate-tls-subject-label": "Subject to Use for the Generated TLS Certificate", "ingress-design-generate-tls-subject-help": "The Subject (e.g., /CN=www.mydomain.org) to use when generating TLS certificate. The format must be /type0=value0/type1=value1/type2=...", + "ingress-design-routes-table-aria-label": "Ingress Routes Table", "ingress-design-add-route-label": "Add Route", "ingress-design-edit-route-label": "Edit Route", "ingress-design-delete-route-label": "Delete Route", @@ -989,10 +1003,14 @@ "model-page-hints-prepareModel": "Prepare Model for Kubernetes", "model-page-model-editor-contents": "Model Editor Contents", + "model-properties-table-aria-label": "Model Variables Editable Table", "model-properties-name-header": "Variable Name", "model-properties-value-header": "Variable Value", "model-properties-add-label": "Add Variable", "model-properties-delete-label": "Delete", + + "model-archive-toolbar-aria-label": "Archive Editing Toolbar", + "model-archive-tree-view-aria-label": "Archive Editor Tree View", "model-archive-add-label": "Add", "model-archive-delete-label": "Delete", "model-archive-no-archive-label": "(no archive)", @@ -1284,6 +1302,7 @@ "quickstart-dialog-title": "WebLogic Kubernetes Toolkit (WKT) UI Introduction", "quickstart-dialog-stop-showing-quickstart-message": "Stop showing this introduction at startup.", + "quickstart-dialog-filmstrip-aria-label": "WebLogic Kubernetes Toolkit UI Tutorial", "quickstart-page1-title": "Introduction", "quickstart-page1-paragraph-1": "Ready to get your WebLogic Server domain running in Kubernetes?", "quickstart-page1-paragraph-2": "This application will guide you through the process of creating a WebLogic Server domain in Kubernetes.", diff --git a/webui/scripts/hooks/after_build.js b/webui/scripts/hooks/after_build.js index a0a9c7bfc..e86910170 100644 --- a/webui/scripts/hooks/after_build.js +++ b/webui/scripts/hooks/after_build.js @@ -12,6 +12,9 @@ const fsPromises = require('fs/promises'); const { copyFile, lstat, mkdir, readdir } = require('fs/promises'); const path = require('path'); +const purgeLocations = [ + path.normalize(path.join(__dirname, '..', '..', 'web', 'test')) +]; const sourceDirectories = [ path.normalize(path.join(__dirname, '..', '..', 'web')) // path.normalize(path.join(__dirname, '..', '..', 'staged-themes')) @@ -21,6 +24,13 @@ const targetDirectory = path.normalize(path.join(__dirname, '..', '..', '..', 'e module.exports = function (configObj) { return new Promise(async (resolve, reject) => { console.log("Running after_build hook."); + console.log('Purging unnecessary files created by the build...'); + for (const purgeLocation of purgeLocations) { + if (fs.existsSync(purgeLocation)) { + fs.rmSync(purgeLocation, { force: true, recursive: true }); + } + } + if (configObj.buildType === 'release') { console.log('Consolidating files for building the release'); for (const sourceDirectory of sourceDirectories) { diff --git a/webui/src/js/viewModels/ingress-design-view-impl.js b/webui/src/js/viewModels/ingress-design-view-impl.js index 570be273c..a11bf91db 100644 --- a/webui/src/js/viewModels/ingress-design-view-impl.js +++ b/webui/src/js/viewModels/ingress-design-view-impl.js @@ -209,12 +209,11 @@ function(i18n, accUtils, ko, ArrayDataProvider, BufferingDataProvider, project, window.api.ipc.invoke('show-error-message', title, message); }; - async function getTargetServiceDetails (project) { - + async function getTargetServiceDetails(myProject) { const kubectlExe = k8sHelper.getKubectlExe(); const kubectlOptions = k8sHelper.getKubectlOptions(); const results = await window.api.ipc.invoke('k8s-get-service-details', - kubectlExe, project.k8sDomain.kubernetesNamespace.value, '', kubectlOptions); + kubectlExe, myProject.k8sDomain.kubernetesNamespace.value, '', kubectlOptions); let serviceLists = {}; if (results.isSuccess) { for (const item of results.serviceDetails.items) { @@ -230,37 +229,37 @@ function(i18n, accUtils, ko, ArrayDataProvider, BufferingDataProvider, project, return Promise.resolve(false); } return Promise.resolve( { serviceList: serviceLists}); - }; + } this.handleEditRoute = async (event, context) => { // using context.item.data directly was causing problems // when project data was reloaded with matching UIDs. const index = context.item.index; let route = this.routes.observable()[index]; - getTargetServiceDetails(this.project).then( svc => { - const options = {route: route, serviceList: svc.serviceList}; - dialogHelper.promptDialog('route-edit-dialog', options) - .then(result => { - - // no result indicates operation was cancelled - if (result) { - let changed = false; - project.ingress.ingressRouteKeys.forEach(key => { - if ((key !== 'uid') && result.hasOwnProperty(key)) { - route[key] = result[key]; - changed = true; - } - }); - - if (changed) { - this.routes.observable.replace(route, route); + + dialogHelper.openBusyDialog(this.labelMapper('route-get-services-title', + { namespace: this.project.k8sDomain.kubernetesNamespace.value })); + const targetServiceDetails = await getTargetServiceDetails(this.project); + dialogHelper.closeBusyDialog(); + if (targetServiceDetails) { + const options = {route: route, serviceList: targetServiceDetails.serviceList}; + dialogHelper.promptDialog('route-edit-dialog', options).then(result => { + // no result indicates operation was cancelled + if (result) { + let changed = false; + project.ingress.ingressRouteKeys.forEach(key => { + if ((key !== 'uid') && result.hasOwnProperty(key)) { + route[key] = result[key]; + changed = true; } + }); + + if (changed) { + this.routes.observable.replace(route, route); } - }); + } + }); } - ); - - }; this.handleCancel = () => { diff --git a/webui/src/js/viewModels/route-edit-dialog.js b/webui/src/js/viewModels/route-edit-dialog.js index 1fdd2847f..50c99f22f 100644 --- a/webui/src/js/viewModels/route-edit-dialog.js +++ b/webui/src/js/viewModels/route-edit-dialog.js @@ -6,21 +6,26 @@ 'use strict'; define(['accUtils', 'knockout', 'utils/i18n', 'models/wkt-project', 'utils/view-helper', 'ojs/ojarraydataprovider', - 'ojs/ojbufferingdataprovider', 'utils/observable-properties', 'ojs/ojconverter-number', 'ojs/ojinputtext', - 'ojs/ojlabel', 'ojs/ojbutton', 'ojs/ojdialog', 'ojs/ojformlayout', 'ojs/ojvalidationgroup', 'ojs/ojselectcombobox'], -function(accUtils, ko, i18n, project, viewHelper, ArrayDataProvider, BufferingDataProvider, props, ojConverterNumber) { + 'ojs/ojbufferingdataprovider', 'utils/observable-properties', 'ojs/ojconverter-number', 'utils/wkt-logger', + 'ojs/ojinputtext', 'ojs/ojlabel', 'ojs/ojbutton', 'ojs/ojdialog', 'ojs/ojformlayout', 'ojs/ojvalidationgroup', + 'ojs/ojselectcombobox'], +function(accUtils, ko, i18n, project, viewHelper, ArrayDataProvider, BufferingDataProvider, props, + ojConverterNumber) { function RouteEditDialogModel(args) { const DIALOG_SELECTOR = '#routeEditDialog'; - const DEFAULT_ROUTE_PORT = 0; + const DEFAULT_ROUTE_PORT = undefined; // SIMPLE_PROPERTIES - names matching simple route fields let EXCLUDE_PROPERTIES = ['uid', 'annotations']; let SIMPLE_PROPERTIES = project.ingress.ingressRouteKeys.filter(key => !EXCLUDE_PROPERTIES.includes(key)); + this.project = project; + this.route = args.route; + this.serviceList = args.serviceList; + this.connected = () => { accUtils.announce('Route edit dialog loaded.', 'assertive'); - // open the dialog after the current thread, which is loading this view model. // using oj-dialog initial-visibility="show" causes vertical centering issues. setTimeout(function() { @@ -36,13 +41,9 @@ function(accUtils, ko, i18n, project, viewHelper, ArrayDataProvider, BufferingDa return i18n.t(labelId, arg); }; - this.project = project; - this.route = args.route; - this.serviceList = args.serviceList; - this.buildTargetSvcNames = () => { let options = []; - for(var name in this.serviceList){ + for (const name in this.serviceList) { options.push( { id : name, value: name, text: name}); } return options; @@ -52,7 +53,7 @@ function(accUtils, ko, i18n, project, viewHelper, ArrayDataProvider, BufferingDa let options = []; if (this.serviceList[svcName]) { for (const port of this.serviceList[svcName].ports) { - options.push( { id : port.port, value: port.port, text: port.port}); + options.push( { id : port.port, value: port.port, text: port.port} ); } } @@ -64,7 +65,7 @@ function(accUtils, ko, i18n, project, viewHelper, ArrayDataProvider, BufferingDa this.targetSvcPorts = ko.observableArray([] ); if (this.route.targetService) { - this.targetSvcPorts = this.buildTargetSvcPorts(this.route.targetService); + this.buildTargetSvcPorts(this.route.targetService).forEach(port => this.targetSvcPorts.push(port)); } this.savedAnnotations = args.route.annotations || {}; @@ -144,6 +145,24 @@ function(accUtils, ko, i18n, project, viewHelper, ArrayDataProvider, BufferingDa this[propertyName] = props.createProperty(defaultValue); }); + this.getTargetServicePlaceholder = ko.computed(() => { + if (Array.isArray(this.targetSvcNames) && this.targetSvcNames.length > 0) { + return this.labelMapper('route-targetservice-placeholder'); + } else { + return this.labelMapper('route-targetservice-placeholder-empty'); + } + }); + + this.getTargetPortPlaceholder = ko.computed(() => { + console.log('targetSvcPorts is a ' + typeof(this.targetSvcPorts)); + const ports = this.targetSvcPorts(); + if (Array.isArray(ports) && ports.length > 0) { + return this.labelMapper('route-targetport-placeholder'); + } else { + return this.labelMapper('route-targetport-placeholder-empty'); + } + }); + this.handleAddAnnotation = () => { const nextIndex = this.nextAnnotationIndex(); const annotation = {uid: getAnnotationUid(nextIndex), key: `annotation-${nextIndex}`}; @@ -186,10 +205,7 @@ function(accUtils, ko, i18n, project, viewHelper, ArrayDataProvider, BufferingDa this.targetSvcNameChanged = (event) => { this.targetSvcPorts.removeAll(); - const list = this.buildTargetSvcPorts(event.detail.value); - for (const item of list) { - this.targetSvcPorts.push(item); - } + this.buildTargetSvcPorts(event.detail.value).forEach(port => this.targetSvcPorts.push(port)); }; this.okInput = () => { diff --git a/webui/src/js/views/app-main.html b/webui/src/js/views/app-main.html index a8a01308d..684da44ba 100644 --- a/webui/src/js/views/app-main.html +++ b/webui/src/js/views/app-main.html @@ -16,7 +16,7 @@