diff --git a/esp/src/eclwatch/CurrentUserDetailsWidget.js b/esp/src/eclwatch/CurrentUserDetailsWidget.js index b96e014a669..eebf772193a 100644 --- a/esp/src/eclwatch/CurrentUserDetailsWidget.js +++ b/esp/src/eclwatch/CurrentUserDetailsWidget.js @@ -26,89 +26,96 @@ define([ ], function (declare, lang, nlsHPCCMod, dom, domForm, arrayUtil, - registry, - _Widget, WsAccount, - template) { - - var nlsHPCC = nlsHPCCMod.default; - return declare("CurrentUserDetailsWidget", [_Widget], { - templateString: template, - baseClass: "CurrentUserDetailsWidget", - i18n: nlsHPCC, - - user: null, - - getTitle: function () { - return this.i18n.UserDetails; - }, - - postCreate: function (args) { - this.inherited(arguments); - this.userForm = registry.byId(this.id + "UserForm"); - }, - - resize: function (args) { - this.inherited(arguments); - this.widget.BorderContainer.resize(); - }, - - // Hitched actions --- - _onSave: function (event) { - var context = this; - var dialog = this.params.Widget; - - if (this.userForm.validate()) { - var formInfo = domForm.toObject(this.id + "UserForm"); - WsAccount.UpdateUser({ - showOkMsg: true, - request: { - username: this.user, - oldpass: formInfo.oldPassword, - newpass1: formInfo.newPassword, - newpass2: formInfo.newPassword - } - }).then(function (response) { - if (lang.exists("UpdateUserResponse", response)) { - arrayUtil.forEach(context.userForm.getDescendants(), function (item, idx) { - item.set("value", ""); - }); - } - }); - dialog.hide(); - } - }, - - // Implementation --- - init: function (params) { - if (this.inherited(arguments)) - return; - - this.user = params.Username; - this.refresh(); - }, - - refresh: function () { - if (this.user) { - this.updateInput("User", null, this.user); - this.updateInput("Username", null, this.user); - + registry, + _Widget, WsAccount, + template) { + + var nlsHPCC = nlsHPCCMod.default; + return declare("CurrentUserDetailsWidget", [_Widget], { + templateString: template, + baseClass: "CurrentUserDetailsWidget", + i18n: nlsHPCC, + + user: null, + canUpdatePassword: false, + + getTitle: function () { + return this.i18n.UserDetails; + }, + + postCreate: function (args) { + this.inherited(arguments); + this.userForm = registry.byId(this.id + "UserForm"); + }, + + resize: function (args) { + this.inherited(arguments); + this.widget.BorderContainer.resize(); + }, + + // Hitched actions --- + _onSave: function (event) { var context = this; - WsAccount.MyAccount({ - }).then(function (response) { - if (lang.exists("MyAccountResponse.firstName", response)) { - context.updateInput("FirstName", null, response.MyAccountResponse.firstName); - } - if (lang.exists("MyAccountResponse.employeeID", response)) { - context.updateInput("EmployeeID", null, response.MyAccountResponse.employeeID); - } - if (lang.exists("MyAccountResponse.lastName", response)) { - context.updateInput("LastName", null, response.MyAccountResponse.lastName); - } - if (lang.exists("MyAccountResponse.passwordExpiration", response)) { - context.updateInput("PasswordExpiration", null, response.MyAccountResponse.passwordExpiration); - } - }); + var dialog = this.params.Widget; + + if (this.canUpdatePassword && this.userForm.validate()) { + var formInfo = domForm.toObject(this.id + "UserForm"); + WsAccount.UpdateUser({ + showOkMsg: true, + request: { + username: this.user, + oldpass: formInfo.oldPassword, + newpass1: formInfo.newPassword, + newpass2: formInfo.newPassword + } + }).then(function (response) { + if (lang.exists("UpdateUserResponse", response)) { + arrayUtil.forEach(context.userForm.getDescendants(), function (item, idx) { + item.set("value", ""); + }); + } + }); + dialog.hide(); + } + }, + + // Implementation --- + init: function (params) { + if (this.inherited(arguments)) + return; + + this.user = params.Username; + this.refresh(); + }, + + refresh: function () { + if (this.user) { + this.updateInput("User", null, this.user); + this.updateInput("Username", null, this.user); + + var context = this; + WsAccount.MyAccount({ + }).then(function (response) { + if (lang.exists("MyAccountResponse.firstName", response)) { + context.updateInput("FirstName", null, response.MyAccountResponse.firstName); + } + if (lang.exists("MyAccountResponse.employeeID", response)) { + context.updateInput("EmployeeID", null, response.MyAccountResponse.employeeID); + } + if (lang.exists("MyAccountResponse.lastName", response)) { + context.updateInput("LastName", null, response.MyAccountResponse.lastName); + } + if (lang.exists("MyAccountResponse.passwordExpiration", response)) { + context.updateInput("PasswordExpiration", null, response.MyAccountResponse.passwordExpiration); + } + if (!response?.MyAccountResponse?.CanUpdatePassword) { + context.setDisabled("dijit_form_ValidationTextBox_0", true); + context.setDisabled("dojox_form__NewPWBox_0", true); + context.setDisabled("dojox_form__VerifyPWBox_0", true); + } + context.canUpdatePassword = response?.MyAccountResponse?.CanUpdatePassword; + }); + } } - } + }); }); -}); diff --git a/esp/src/eclwatch/WUQueryWidget.js b/esp/src/eclwatch/WUQueryWidget.js index 8936500ec04..6cffa29a6f3 100644 --- a/esp/src/eclwatch/WUQueryWidget.js +++ b/esp/src/eclwatch/WUQueryWidget.js @@ -158,19 +158,19 @@ define([ }, _onSetToFailed: function (event) { - WsWorkunits.WUAction(this.workunitsGrid.getSelected(), "SetToFailed"); + WsWorkunits.WUAction(this.workunitsGrid.getSelected(), "SetToFailed").then(() => this.refreshGrid()); }, _onAbort: function (event) { - WsWorkunits.WUAction(this.workunitsGrid.getSelected(), "Abort"); + WsWorkunits.WUAction(this.workunitsGrid.getSelected(), "Abort").then(() => this.refreshGrid()); }, _onProtect: function (event) { - WsWorkunits.WUAction(this.workunitsGrid.getSelected(), "Protect"); + WsWorkunits.WUAction(this.workunitsGrid.getSelected(), "Protect").then(() => this.refreshGrid()); }, _onUnprotect: function (event) { - WsWorkunits.WUAction(this.workunitsGrid.getSelected(), "Unprotect"); + WsWorkunits.WUAction(this.workunitsGrid.getSelected(), "Unprotect").then(() => this.refreshGrid()); }, _onReschedule: function (event) { diff --git a/esp/src/package-lock.json b/esp/src/package-lock.json index bb858aa4279..28aef7baf94 100644 --- a/esp/src/package-lock.json +++ b/esp/src/package-lock.json @@ -18,7 +18,7 @@ "@hpcc-js/chart": "2.83.4", "@hpcc-js/codemirror": "2.62.1", "@hpcc-js/common": "2.71.18", - "@hpcc-js/comms": "2.94.0", + "@hpcc-js/comms": "2.94.1", "@hpcc-js/dataflow": "8.1.7", "@hpcc-js/eclwatch": "2.74.8", "@hpcc-js/graph": "2.85.16", @@ -2079,9 +2079,9 @@ } }, "node_modules/@hpcc-js/comms": { - "version": "2.94.0", - "resolved": "https://registry.npmjs.org/@hpcc-js/comms/-/comms-2.94.0.tgz", - "integrity": "sha512-+AfJsqj648638hTUeLYd0Thvu1QMHX9zLflrep2xVtz7Wo1OmOiI/mrjClMqK8A8drMa3AduKuQS1R2rL15wZw==", + "version": "2.94.1", + "resolved": "https://registry.npmjs.org/@hpcc-js/comms/-/comms-2.94.1.tgz", + "integrity": "sha512-ROCHmHogsZ5/G9LsRPHRIx25rpVoR9d5TakbSkXeSugRkhPV+16+3C/Lfd4d8ZzNLR99Jmnvjd0NXZAMgpAkGQ==", "dependencies": { "@hpcc-js/ddl-shim": "^2.21.0", "@hpcc-js/util": "^2.52.0", diff --git a/esp/src/package.json b/esp/src/package.json index a30728c2cf2..7a4508f1cf3 100644 --- a/esp/src/package.json +++ b/esp/src/package.json @@ -44,7 +44,7 @@ "@hpcc-js/chart": "2.83.4", "@hpcc-js/codemirror": "2.62.1", "@hpcc-js/common": "2.71.18", - "@hpcc-js/comms": "2.94.0", + "@hpcc-js/comms": "2.94.1", "@hpcc-js/dataflow": "8.1.7", "@hpcc-js/eclwatch": "2.74.8", "@hpcc-js/graph": "2.85.16", diff --git a/esp/src/src-react/components/MyAccount.tsx b/esp/src/src-react/components/MyAccount.tsx index 51130178fca..09fb51bc9d2 100644 --- a/esp/src/src-react/components/MyAccount.tsx +++ b/esp/src/src-react/components/MyAccount.tsx @@ -1,10 +1,11 @@ import * as React from "react"; import { DefaultButton, Dialog, DialogFooter, DialogType, MessageBar, MessageBarType, PrimaryButton } from "@fluentui/react"; +import { useConst } from "@fluentui/react-hooks"; import { AccountService, WsAccount } from "@hpcc-js/comms"; import { scopedLogger } from "@hpcc-js/util"; +import { PasswordStatus } from "../hooks/user"; import nlsHPCC from "src/nlsHPCC"; import { TableGroup } from "./forms/Groups"; -import { useConst } from "@fluentui/react-hooks"; const logger = scopedLogger("src-react/components/MyAccount.tsx"); @@ -36,8 +37,14 @@ export const MyAccount: React.FunctionComponent = ({ }; }, [currentUser]); + const resetForm = React.useCallback(() => { + setOldPassword(""); + setNewPassword1(""); + setNewPassword2(""); + }, []); + const saveUser = React.useCallback(() => { - if (oldPassword !== "" && newPassword1 !== "") { + if (currentUser?.CanUpdatePassword && oldPassword !== "" && newPassword1 !== "") { service.UpdateUser({ username: currentUser.username, oldpass: oldPassword, @@ -51,13 +58,14 @@ export const MyAccount: React.FunctionComponent = ({ } else { setShowError(false); setErrorMessage(""); + resetForm(); onClose(); } }) .catch(err => logger.error(err)) ; } - }, [currentUser, newPassword1, newPassword2, oldPassword, onClose, service]); + }, [currentUser, newPassword1, newPassword2, oldPassword, onClose, resetForm, service]); return ; }; diff --git a/esp/src/src-react/components/Result.tsx b/esp/src/src-react/components/Result.tsx index a9d47294417..3490153b800 100644 --- a/esp/src/src-react/components/Result.tsx +++ b/esp/src/src-react/components/Result.tsx @@ -1,6 +1,6 @@ import * as React from "react"; import * as ReactDOM from "react-dom"; -import { Checkbox, CommandBar, ContextualMenuItemType, DefaultButton, Dialog, DialogFooter, DialogType, ICommandBarItemProps, PrimaryButton, SpinButton, Stack } from "@fluentui/react"; +import { Checkbox, CommandBar, ContextualMenuItemType, DefaultButton, Dialog, DialogFooter, DialogType, ICommandBarItemProps, PrimaryButton, SpinButton, Spinner, Stack } from "@fluentui/react"; import { useConst } from "@fluentui/react-hooks"; import { Result as CommsResult, XSDXMLNode } from "@hpcc-js/comms"; import { scopedLogger } from "@hpcc-js/util"; @@ -247,6 +247,7 @@ export const Result: React.FunctionComponent = ({ const [wu] = useWorkunit(wuid); const [result, setResult] = React.useState(resultTable.calcResult()); const [FilterFields, setFilterFields] = React.useState({}); + const [loading, setLoading] = React.useState(true); const [showFilter, setShowFilter] = React.useState(false); React.useEffect(() => { @@ -276,6 +277,7 @@ export const Result: React.FunctionComponent = ({ }; }); setFilterFields(filterFields); + setLoading(false); }).catch(err => { logger.error(err); if (err.message.indexOf("Cannot open the workunit result") > -1) { @@ -370,7 +372,10 @@ export const Result: React.FunctionComponent = ({ header={} main={ <> - + {loading ? + : + + } diff --git a/esp/src/src-react/components/Title.tsx b/esp/src/src-react/components/Title.tsx index f42b2e6ad77..5257ba34747 100644 --- a/esp/src/src-react/components/Title.tsx +++ b/esp/src/src-react/components/Title.tsx @@ -15,7 +15,7 @@ import { replaceUrl } from "../util/history"; import { useECLWatchLogger } from "../hooks/logging"; import { useBuildInfo, useModernMode, useCheckFeatures } from "../hooks/platform"; import { useGlobalStore } from "../hooks/store"; -import { useMyAccount, useUserSession } from "../hooks/user"; +import { PasswordStatus, useMyAccount, useUserSession } from "../hooks/user"; import { TitlebarConfig } from "./forms/TitlebarConfig"; import { switchTechPreview } from "./controls/ComingSoon"; @@ -246,10 +246,10 @@ export const DevTitle: React.FunctionComponent = ({ // cookie expires option expects whole number of days, use a decimal < 1 for hours cookie("PasswordExpiredCheck", "true", { expires: 0.5, path: "/" }); switch (currentUser.passwordDaysRemaining) { - case -1: // password has expired + case PasswordStatus.Expired: setPasswordExpiredConfirm(true); break; - case -2: // password never expires + case PasswordStatus.NeverExpires: case null: break; default: diff --git a/esp/src/src-react/components/Workunits.tsx b/esp/src/src-react/components/Workunits.tsx index b2047850ee7..b4ff6a3a291 100644 --- a/esp/src/src-react/components/Workunits.tsx +++ b/esp/src/src-react/components/Workunits.tsx @@ -213,7 +213,7 @@ export const Workunits: React.FunctionComponent = ({ { key: "divider_2", itemType: ContextualMenuItemType.Divider, onRender: () => }, { key: "setFailed", text: nlsHPCC.SetToFailed, disabled: !uiState.hasNotProtected, - onClick: () => { WsWorkunits.WUAction(selection, "SetToFailed"); } + onClick: () => { WsWorkunits.WUAction(selection, "SetToFailed").then(() => refreshTable.call()); } }, { key: "abort", text: nlsHPCC.Abort, disabled: !uiState.hasNotCompleted, diff --git a/esp/src/src-react/components/forms/Fields.tsx b/esp/src/src-react/components/forms/Fields.tsx index 6ec76f804a4..5ae9c59e80e 100644 --- a/esp/src/src-react/components/forms/Fields.tsx +++ b/esp/src/src-react/components/forms/Fields.tsx @@ -890,6 +890,7 @@ export function createInputs(fields: Fields, onChange?: (id: string, newValue: a onChange={(evt, newValue) => onChange(fieldID, newValue)} borderless={field.readonly && !field.multiline} readOnly={field.readonly} + disabled={field.disabled(field) ? true : false} required={field.required} multiline={field.multiline} errorMessage={field.errorMessage ?? ""} diff --git a/esp/src/src-react/hooks/user.ts b/esp/src/src-react/hooks/user.ts index 005ffc4ce5e..a1b49dab49e 100644 --- a/esp/src/src-react/hooks/user.ts +++ b/esp/src/src-react/hooks/user.ts @@ -12,6 +12,12 @@ const defaults = { const userSession = { ...defaults }; +export enum PasswordStatus { + NeverExpires = -2, + Expired = -1, + Unexpired = 0, +} + export interface UserSession { ESPSessionTimeout: number; ESPAuthenticated: boolean; diff --git a/esp/src/src/ESPWorkunit.ts b/esp/src/src/ESPWorkunit.ts index 77b406e53fd..0b7713df67f 100644 --- a/esp/src/src/ESPWorkunit.ts +++ b/esp/src/src/ESPWorkunit.ts @@ -1096,7 +1096,7 @@ export function CreateWUQueryStore(): BaseStore