فهرست منبع

HPCC-2064 Add Permissions to new ECL Watch

Fixes HPCC-2064

Signed-off-by: Gordon Smith <gordon.smith@lexisnexis.com>
Gordon Smith 11 سال پیش
والد
کامیت
cd25479969

+ 1 - 1
esp/files/scripts/ESPRequest.js

@@ -120,7 +120,7 @@ define([
                         dojo.publish("hpcc/brToaster", {
                             Severity: "Error",
                             Source: service + "." + action,
-                            Exceptions: response.Exceptions
+                            Exceptions: response.Exceptions.Exception
                         });
                     }
                 }

+ 101 - 0
esp/files/scripts/GroupDetailsWidget.js

@@ -0,0 +1,101 @@
+/*##############################################################################
+#   HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+############################################################################## */
+define([
+    "dojo/_base/declare",
+    "dojo/_base/lang",
+    "dojo/i18n",
+    "dojo/i18n!./nls/common",
+    "dojo/i18n!./nls/UserQueryWidget",
+    "dojo/dom",
+    "dojo/dom-attr",
+
+    "dijit/registry",
+
+    "dgrid/OnDemandGrid",
+    "dgrid/Keyboard",
+    "dgrid/Selection",
+    "dgrid/selector",
+    "dgrid/extensions/ColumnResizer",
+    "dgrid/extensions/DijitRegistry",
+
+    "hpcc/_TabContainerWidget",
+    "hpcc/ws_access",
+    "hpcc/MembersWidget",
+    "hpcc/PermissionsWidget",
+
+    "dojo/text!../templates/GroupDetailsWidget.html",
+
+    "dijit/form/Textarea",
+    "dijit/form/TextBox",
+    "dijit/form/Button",
+    "dijit/Toolbar",
+    "dijit/TooltipDialog",
+    "dijit/TitlePane",
+    "dijit/Dialog"
+
+], function (declare, lang, i18n, nlsCommon, nlsSpecific, dom, domAttr,
+                registry,
+                OnDemandGrid, Keyboard, Selection, selector, ColumnResizer, DijitRegistry,
+                _TabContainerWidget, WsAccess, MembersWidget, PermissionsWidget,
+                template) {
+    return declare("GroupDetailsWidget", [_TabContainerWidget], {
+        templateString: template,
+        baseClass: "GroupDetailsWidget",
+        i18n: lang.mixin(nlsCommon, nlsSpecific),
+
+        postCreate: function (args) {
+            this.inherited(arguments);
+            this.summaryWidget = registry.byId(this.id + "_Summary");
+            this.membersWidget = registry.byId(this.id + "_Members");
+            this.permissionsWidget = registry.byId(this.id + "_Permissions");
+        },
+
+        getTitle: function () {
+            return this.i18n.GroupDetails;
+        },
+
+        //  Hitched actions  ---
+        _onSave: function (event) {
+            //  Currently disabled.  TODO:  Add ESP Method to rename group?  ---
+        },
+
+        //  Implementation  ---
+        init: function (params) {
+            if (this.inherited(arguments))
+                return;
+
+            this.group = params.Name;
+            if (this.group) {
+                this.updateInput("Group", null, this.group);
+                this.updateInput("Name", null, this.group);
+            }
+        },
+
+        initTab: function () {
+            var currSel = this.getSelectedChild();
+
+            if (currSel.id == this.membersWidget.id) {
+                this.membersWidget.init({
+                    groupname: this.group
+                });
+            } else if (currSel.id == this.permissionsWidget.id) {
+                this.permissionsWidget.init({
+                    groupname: this.group
+                });
+            }
+        }
+    });
+});

+ 1 - 15
esp/files/scripts/HPCCPlatformOpsWidget.js

@@ -27,6 +27,7 @@ define([
 
     "dojo/text!../templates/HPCCPlatformOpsWidget.html",
 
+    "hpcc/UserQueryWidget",
     "dijit/layout/BorderContainer",
     "dijit/layout/TabContainer",
     "dijit/layout/ContentPane"
@@ -70,21 +71,6 @@ define([
                         src: "/esp/files/stub.htm?Widget=IFrameWidget&src=" + encodeURIComponent(ESPRequest.getBaseURL("WsSMC") + "/BrowseResources"),
                         style: "border: 0; width: 100%; height: 100%"
                     }));
-                } else if (currSel.id === this.id + "_Users") {
-                    currSel.set("content", dojo.create("iframe", {
-                        src: "/esp/files/stub.htm?Widget=IFrameWidget&src=" + encodeURIComponent(ESPRequest.getBaseURL("ws_access") + "/Users"),
-                        style: "border: 0; width: 100%; height: 100%"
-                    }));
-                } else if (currSel.id === this.id + "_Groups") {
-                    currSel.set("content", dojo.create("iframe", {
-                        src: "/esp/files/stub.htm?Widget=IFrameWidget&src=" + encodeURIComponent(ESPRequest.getBaseURL("ws_access") + "/Groups"),
-                        style: "border: 0; width: 100%; height: 100%"
-                    }));
-                } else if (currSel.id === this.id + "_Permissions") {
-                    currSel.set("content", dojo.create("iframe", {
-                        src: "/esp/files/stub.htm?Widget=IFrameWidget&src=" + encodeURIComponent(ESPRequest.getBaseURL("ws_access") + "/Permissions"),
-                        style: "border: 0; width: 100%; height: 100%"
-                    }));
                 } else if (currSel.id === this.id + "_TargetClusters") {
                     currSel.set("content", dojo.create("iframe", {
                         src: "/esp/files/stub.htm?Widget=IFrameWidget&src=" + encodeURIComponent(ESPRequest.getBaseURL("WsTopology") + "/TpTargetClusterQuery?Type=ROOT"),

+ 1 - 1
esp/files/scripts/HPCCPlatformWidget.js

@@ -30,7 +30,7 @@ define([
 
     "hpcc/_TabContainerWidget",
     "hpcc/ESPActivity",
-    "hpcc/WsAccount",
+    "hpcc/ws_account",
     "hpcc/ws_access",
     "hpcc/WsSMC",
     "hpcc/GraphWidget",

+ 85 - 0
esp/files/scripts/MemberOfWidget.js

@@ -0,0 +1,85 @@
+/*##############################################################################
+#    HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+############################################################################## */
+define([
+    "dojo/_base/declare",
+    "dojo/_base/lang",
+    "dojo/i18n",
+    "dojo/i18n!./nls/common",
+    "dojo/i18n!./nls/UserQueryWidget",
+
+    "dijit/registry",
+
+    "dgrid/OnDemandGrid",
+    "dgrid/Keyboard",
+    "dgrid/Selection",
+    "dgrid/editor",
+    "dgrid/extensions/ColumnResizer",
+    "dgrid/extensions/DijitRegistry",
+
+    "hpcc/GridDetailsWidget",
+    "hpcc/ws_access",
+    "hpcc/ESPUtil"
+
+], function (declare, lang, i18n, nlsCommon, nlsSpecific,
+                registry,
+                OnDemandGrid, Keyboard, Selection, editor, ColumnResizer, DijitRegistry,
+                GridDetailsWidget, WsAccess, ESPUtil) {
+    return declare("MemberOfWidget", [GridDetailsWidget], {
+        i18n: lang.mixin(nlsCommon, nlsSpecific),
+
+        gridTitle: nlsSpecific.MemberOf,
+        idProperty: "__hpcc_id",
+
+        //  Hitched Actions  ---
+        _onRefresh: function (args) {
+            this.grid.refresh();
+        },
+
+        //  Implementation  ---
+        init: function (params) {
+            if (this.inherited(arguments))
+                return;
+
+            this.store = WsAccess.CreateGroupsStore(params.username);
+            this.grid.setStore(this.store);
+            this._refreshActionState();
+        },
+
+        createGrid: function (domID) {
+            var retVal = new declare([OnDemandGrid, Keyboard, ColumnResizer, DijitRegistry, ESPUtil.GridHelper])({
+                store: this.store,
+                columns: {
+                    isMember: editor({
+                        label: "",
+                        width: 27,
+                        editor: "checkbox",
+                        autoSave: true
+                    }),
+                    name: {
+                        label: this.i18n.GroupName,
+                        sortable: true
+                    }
+                }
+            }, domID);
+
+            return retVal;
+        },
+
+        refreshActionState: function (selection) {
+            registry.byId(this.id + "Open").set("disabled", true);
+        }
+    });
+});

+ 92 - 0
esp/files/scripts/MembersWidget.js

@@ -0,0 +1,92 @@
+/*##############################################################################
+#    HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+############################################################################## */
+define([
+    "dojo/_base/declare",
+    "dojo/_base/lang",
+    "dojo/i18n",
+    "dojo/i18n!./nls/common",
+    "dojo/i18n!./nls/UserQueryWidget",
+
+    "dijit/registry",
+
+    "dgrid/OnDemandGrid",
+    "dgrid/Keyboard",
+    "dgrid/Selection",
+    "dgrid/editor",
+    "dgrid/extensions/ColumnResizer",
+    "dgrid/extensions/DijitRegistry",
+
+    "hpcc/GridDetailsWidget",
+    "hpcc/ws_access",
+    "hpcc/ESPUtil"
+
+], function (declare, lang, i18n, nlsCommon, nlsSpecific,
+                registry,
+                OnDemandGrid, Keyboard, Selection, editor, ColumnResizer, DijitRegistry,
+                GridDetailsWidget, WsAccess, ESPUtil) {
+    return declare("MembersWidget", [GridDetailsWidget], {
+        i18n: lang.mixin(nlsCommon, nlsSpecific),
+
+        gridTitle: nlsSpecific.Members,
+        idProperty: "__hpcc_id",
+
+        //  Hitched Actions  ---
+        _onRefresh: function (args) {
+            this.grid.refresh();
+        },
+
+        //  Implementation  ---
+        init: function (params) {
+            if (this.inherited(arguments))
+                return;
+
+            this.store = WsAccess.CreateUsersStore(params.groupname);
+            this.grid.setStore(this.store);
+            this._refreshActionState();
+        },
+
+        createGrid: function (domID) {
+            var retVal = new declare([OnDemandGrid, Keyboard, ColumnResizer, DijitRegistry, ESPUtil.GridHelper])({
+                store: this.store,
+                columns: {
+                    isMember: editor({
+                        label: "",
+                        width: 27,
+                        editor: "checkbox",
+                        autoSave: true
+                    }),
+                    username: {
+                        width: 180,
+                        label: this.i18n.Username
+                    },
+                    fullname: {
+                        label: this.i18n.FullName
+                    },
+                    passwordexpiration: {
+                        width: 180,
+                        label: this.i18n.PasswordExpiration
+                    }
+                }
+            }, domID);
+
+            return retVal;
+        },
+
+        refreshActionState: function (selection) {
+            registry.byId(this.id + "Open").set("disabled", true);
+        }
+    });
+});

+ 214 - 0
esp/files/scripts/PermissionsWidget.js

@@ -0,0 +1,214 @@
+/*##############################################################################
+#    HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+############################################################################## */
+define([
+    "dojo/_base/declare",
+    "dojo/_base/lang",
+    "dojo/i18n",
+    "dojo/i18n!./nls/common",
+    "dojo/i18n!./nls/UserQueryWidget",
+
+    "dijit/registry",
+
+    "dgrid/OnDemandGrid",
+    "dgrid/Keyboard",
+    "dgrid/Selection",
+    "dgrid/tree",
+    "dgrid/editor",
+    "dgrid/extensions/ColumnResizer",
+    "dgrid/extensions/DijitRegistry",
+
+    "hpcc/GridDetailsWidget",
+    "hpcc/ws_access",
+    "hpcc/ESPUtil"
+
+], function (declare, lang, i18n, nlsCommon, nlsSpecific,
+                registry,
+                OnDemandGrid, Keyboard, Selection, tree, editor, ColumnResizer, DijitRegistry,
+                GridDetailsWidget, WsAccess, ESPUtil) {
+    return declare("PermissionsWidget", [GridDetailsWidget], {
+        i18n: lang.mixin(nlsCommon, nlsSpecific),
+
+        gridTitle: nlsSpecific.UserPermissions,
+        idProperty: "__hpcc_id",
+
+        //  Hitched Actions  ---
+        _onRefresh: function (args) {
+            this.grid.refresh();
+        },
+
+        //  Implementation  ---
+        init: function (params) {
+            if (this.inherited(arguments))
+                return;
+
+            this.store = WsAccess.CreatePermissionsStore(params.groupname, params.username);
+            this.grid.setStore(this.store);
+            this._refreshActionState();
+        },
+
+        createGrid: function (domID) {
+            var context = this;
+            var retVal = new declare([OnDemandGrid, Keyboard, Selection, ColumnResizer, DijitRegistry, ESPUtil.GridHelper])({
+                store: this.store,
+                columns: {
+                    DisplayName: tree({
+                        label: this.i18n.Resource,
+                        formatter: function (_name, row) {
+                            return _name;
+                        }
+                    }),
+                    allow_access: editor({
+                        width: 54,
+                        editor: "checkbox",
+                        autoSave: true,
+                        canEdit: function (object, value) { return object.__hpcc_type != "Permission"; },
+                        renderHeaderCell: function (node) {
+                            node.innerHTML = context.i18n.AllowAccess;
+                        }
+                    }),
+                    allow_read: editor({
+                        width: 54,
+                        editor: "checkbox",
+                        autoSave: true,
+                        canEdit: function (object, value) { return object.__hpcc_type != "Permission"; },
+                        renderHeaderCell: function (node) {
+                            node.innerHTML = context.i18n.AllowRead;
+                        }
+                    }),
+                    allow_write: editor({
+                        width: 54,
+                        editor: "checkbox",
+                        autoSave: true,
+                        canEdit: function (object, value) { return object.__hpcc_type != "Permission"; },
+                        renderHeaderCell: function (node) {
+                            node.innerHTML = context.i18n.AllowWrite;
+                        }
+                    }),
+                    allow_full: editor({
+                        width: 54,
+                        editor: "checkbox",
+                        autoSave: true,
+                        canEdit: function (object, value) { return object.__hpcc_type != "Permission"; },
+                        renderHeaderCell: function (node) {
+                            node.innerHTML = context.i18n.AllowFull;
+                        }
+                    }),
+                    padding: {
+                        width:20,
+                        label: " "
+                    },
+                    deny_access: editor({
+                        width: 54,
+                        editor: "checkbox",
+                        autoSave: true,
+                        canEdit: function (object, value) { return object.__hpcc_type != "Permission"; },
+                        renderHeaderCell: function (node) {
+                            node.innerHTML = context.i18n.DenyAccess
+                        }
+                    }),
+                    deny_read: editor({
+                        width: 54,
+                        editor: "checkbox",
+                        autoSave: true,
+                        canEdit: function (object, value) { return object.__hpcc_type != "Permission"; },
+                        renderHeaderCell: function (node) {
+                            node.innerHTML = context.i18n.DenyRead
+                        }
+                    }),
+                    deny_write: editor({
+                        width: 54,
+                        editor: "checkbox",
+                        autoSave: true,
+                        canEdit: function (object, value) { return object.__hpcc_type != "Permission"; },
+                        renderHeaderCell: function (node) {
+                            node.innerHTML = context.i18n.DenyWrite
+                        }
+                    }),
+                    deny_full: editor({
+                        width: 54,
+                        editor: "checkbox",
+                        autoSave: true,
+                        canEdit: function (object, value) { return object.__hpcc_type != "Permission"; },
+                        renderHeaderCell: function (node) {
+                            node.innerHTML = context.i18n.DenyFull
+                        }
+                    }),
+                }
+            }, domID);
+
+            retVal.on("dgrid-datachange", function (evt) {
+                evt.preventDefault();
+                context.calcPermissionState(evt.cell.column.field, evt.value, evt.cell.row.data);
+                evt.grid.store.putChild(evt.cell.row.data);
+            });
+            return retVal;
+        },
+
+        calcPermissionState: function(field, value, row) {
+            switch (field) {
+                case "allow_access":
+                    row.allow_full = value && row.allow_read && row.allow_write;
+                    if (value)
+                        this.calcPermissionState("deny_access", false, row);
+                    break;
+                case "allow_read":
+                    row.allow_full = row.allow_access && value && row.allow_write;
+                    if (value)
+                        this.calcPermissionState("deny_read", false, row);
+                    break;
+                case "allow_write":
+                    row.allow_full = row.allow_access && row.allow_read && value;
+                    if (value)
+                        this.calcPermissionState("deny_write", false, row);
+                    break;
+                case "allow_full":
+                    row.allow_access = value;
+                    row.allow_read = value;
+                    row.allow_write = value;
+                    if (value)
+                        this.calcPermissionState("deny_full", false, row);
+                    break;
+                case "deny_access":
+                    row.deny_full = value && row.deny_read && row.deny_write;
+                    if (value)
+                        this.calcPermissionState("allow_access", false, row);
+                    break;
+                case "deny_read":
+                    row.deny_full = row.deny_access && value && row.deny_write;
+                    if (value)
+                        this.calcPermissionState("allow_read", false, row);
+                    break;
+                case "deny_write":
+                    row.deny_full = row.deny_access && row.deny_read && value;
+                    if (value)
+                        this.calcPermissionState("allow_write", false, row);
+                    break;
+                case "deny_full":
+                    row.deny_access = value;
+                    row.deny_read = value;
+                    row.deny_write = value;
+                    if (value)
+                        this.calcPermissionState("allow_full", false, row);
+                    break;
+            }
+            row[field] = value;
+        },
+
+        refreshActionState: function (selection) {
+            registry.byId(this.id + "Open").set("disabled", true);
+        }
+    });
+});

+ 144 - 0
esp/files/scripts/UserDetailsWidget.js

@@ -0,0 +1,144 @@
+/*##############################################################################
+#   HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+############################################################################## */
+define([
+    "dojo/_base/declare",
+    "dojo/_base/lang",
+    "dojo/i18n",
+    "dojo/i18n!./nls/common",
+    "dojo/i18n!./nls/UserQueryWidget",
+    "dojo/dom",
+    "dojo/dom-attr",
+    "dojo/dom-form",
+
+    "dijit/registry",
+
+    "dgrid/OnDemandGrid",
+    "dgrid/Keyboard",
+    "dgrid/Selection",
+    "dgrid/selector",
+    "dgrid/extensions/ColumnResizer",
+    "dgrid/extensions/DijitRegistry",
+
+    "hpcc/_TabContainerWidget",
+    "hpcc/ws_access",
+    "hpcc/MemberOfWidget",
+    "hpcc/PermissionsWidget",
+
+    "dojo/text!../templates/UserDetailsWidget.html",
+
+    "dijit/form/Textarea",
+    "dijit/form/TextBox",
+    "dijit/form/Button",
+    "dijit/Toolbar",
+    "dijit/TooltipDialog",
+    "dijit/TitlePane",
+    "dijit/Dialog",
+
+    "dojox/form/PasswordValidator"
+
+], function (declare, lang, i18n, nlsCommon, nlsSpecific, dom, domAttr, domForm,
+                registry,
+                OnDemandGrid, Keyboard, Selection, selector, ColumnResizer, DijitRegistry,
+                _TabContainerWidget, WsAccess, MemberOfWidget, PermissionsWidget,
+                template) {
+    return declare("UserDetailsWidget", [_TabContainerWidget], {
+        templateString: template,
+        baseClass: "UserDetailsWidget",
+        i18n: lang.mixin(nlsCommon, nlsSpecific),
+
+        summaryWidget: null,
+        memberOfWidget: null,
+        permissionsWidget: null,
+        user: null,
+
+        getTitle: function () {
+            return this.i18n.UserDetails;
+        },
+
+        postCreate: function (args) {
+            this.inherited(arguments);
+            this.summaryWidget = registry.byId(this.id + "_Summary");
+            this.memberOfWidget = registry.byId(this.id + "_MemberOf");
+            this.permissionsWidget = registry.byId(this.id + "_UserPermissions");
+            this.userForm = registry.byId(this.id + "UserForm");
+        },
+
+        //  Hitched actions  ---
+        _onSave: function (event) {
+            if (this.userForm.validate()) {
+                var formInfo = domForm.toObject(this.id + "UserForm");
+                WsAccess.UserInfoEdit({
+                    request: {
+                        username: this.user,
+                        firstname: formInfo.firstname,
+                        lastname: formInfo.lastname
+                    }
+                });
+
+                if (formInfo.newPassword) {
+                    WsAccess.UserResetPass({
+                        request: {
+                            username: this.user,
+                            newPassword: formInfo.newPassword,
+                            newPasswordRetype: formInfo.newPassword
+                        }
+                    });
+                }
+            }
+        },
+
+        //  Implementation  ---
+        init: function (params) {
+            if (this.inherited(arguments))
+                return;
+
+            this.user = params.Username;
+            if (this.user) {
+                this.updateInput("User", null, this.user);
+                this.updateInput("Username", null, this.user);
+                this.updateInput("PasswordExpiration", null, params.Passwordexpiration);
+
+                var context = this;
+                WsAccess.UserInfoEditInput({
+                    request: {
+                        username: this.user
+                    }
+                }).then(function (response) {
+                    if (lang.exists("UserInfoEditInputResponse.firstname", response)) {
+                        context.updateInput("FirstName", null, response.UserInfoEditInputResponse.firstname);
+                    }
+                    if (lang.exists("UserInfoEditInputResponse.lastname", response)) {
+                        context.updateInput("LastName", null, response.UserInfoEditInputResponse.lastname);
+                    }
+                });
+            }
+        },
+
+        initTab: function () {
+            var currSel = this.getSelectedChild();
+
+            if (currSel.id == this.memberOfWidget.id) {
+                this.memberOfWidget.init({
+                    username: this.user
+                });
+            } else if (currSel.id == this.permissionsWidget.id) {
+                this.permissionsWidget.init({
+                    username: this.user
+                });
+            }
+        }
+    });
+});

+ 435 - 0
esp/files/scripts/UserQueryWidget.js

@@ -0,0 +1,435 @@
+/*##############################################################################
+#    HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+############################################################################## */
+define([
+    "dojo/_base/declare",
+    "dojo/_base/lang",
+    "dojo/i18n",
+    "dojo/i18n!./nls/common",
+    "dojo/i18n!./nls/UserQueryWidget",
+    "dojo/_base/array",
+    "dojo/dom",
+    "dojo/dom-form",
+    "dojo/on",
+
+    "dijit/registry",
+    "dijit/Menu",
+    "dijit/MenuItem",
+    "dijit/MenuSeparator",
+
+    "dgrid/OnDemandGrid",
+    "dgrid/Keyboard",
+    "dgrid/Selection",
+    "dgrid/selector",
+    "dgrid/extensions/ColumnResizer",
+    "dgrid/extensions/DijitRegistry",
+
+    "hpcc/_TabContainerWidget",
+    "hpcc/ws_access",
+    "hpcc/ESPUtil",
+    "hpcc/UserDetailsWidget",
+    "hpcc/GroupDetailsWidget",
+
+    "dojo/text!../templates/UserQueryWidget.html",
+
+    "dijit/layout/BorderContainer",
+    "dijit/layout/TabContainer",
+    "dijit/layout/ContentPane",
+    "dijit/Toolbar",
+    "dijit/form/Form",
+    "dijit/form/Button",
+    "dijit/form/DropDownButton",
+    "dijit/form/ValidationTextBox",
+    "dijit/ToolbarSeparator",
+    "dijit/form/TextBox",
+    "dijit/Dialog",
+
+    "dojox/form/PasswordValidator",
+
+    "hpcc/TableContainer"
+
+], function (declare, lang, i18n, nlsCommon, nlsSpecific, arrayUtil, dom, domForm, on,
+                registry, Menu, MenuItem, MenuSeparator,
+                OnDemandGrid, Keyboard, Selection, selector, ColumnResizer, DijitRegistry,
+                _TabContainerWidget, WsAccess, ESPUtil, UserDetailsWidget, GroupDetailsWidget,
+                template) {
+    return declare("UserQueryWidget", [_TabContainerWidget], {
+        templateString: template,
+        baseClass: "UserQueryWidget",
+        i18n: lang.mixin(nlsCommon, nlsSpecific),
+
+        usersTab: null,
+        usersGrid: null,
+
+        postCreate: function (args) {
+            this.inherited(arguments);
+            this.addGroupForm = registry.byId(this.id + "AddGroupForm");
+            this.groupsTab = registry.byId(this.id + "_Groups");
+            this.addUserForm = registry.byId(this.id + "AddUserForm");
+            this.usersTab = registry.byId(this.id + "_Users");
+        },
+
+        //  Hitched actions  ---
+        //  Groups  ---
+        _onRefreshGroups: function () {
+            this.refreshGroupsGrid();
+        },
+
+        _onEditGroup: function (event) {
+            var selections = this.groupsGrid.getSelected();
+            var firstTab = null;
+            for (var i = selections.length - 1; i >= 0; --i) {
+                var tab = this.ensureGroupPane(this.id + "_Group" + selections[i].name, {
+                    Name: selections[i].name
+                });
+                if (i == 0) {
+                    firstTab = tab;
+                }
+            }
+            if (firstTab) {
+                this.selectChild(firstTab);
+            }
+        },
+
+        _onDeleteGroup: function (params) {
+            if (confirm(this.i18n.DeleteSelectedGroups)) {
+                var selections = this.groupsGrid.getSelected();
+                var request = {
+                    ActionType: "delete"
+                };
+                arrayUtil.forEach(selections, function (item, idx) {
+                    request["groupnames_i" + idx] = item.name;
+                }, this);
+
+                var context = this;
+                WsAccess.GroupAction({
+                    request: request
+                }).then(function (response) {
+                    context.refreshGroupsGrid(true);
+                });
+            }
+        },
+
+        _onGroupsRowDblClick: function (name) {
+            var groupTab = this.ensureGroupPane(this.id + "_Group" + name, {
+                Name: name
+            });
+            this.selectChild(groupTab);
+        },
+
+        _onAddGroupSubmit: function () {
+            if (this.addGroupForm.validate()) {
+                var context = this;
+                var request = domForm.toObject(this.addGroupForm.id);
+                WsAccess.GroupAdd({
+                    request: request
+                }).then(function (response) {
+                    context.refreshGroupsGrid();
+                });
+                registry.byId(this.id + "AddGroupsDropDown").closeDropDown();
+            }
+        },
+
+        //  Users  ---
+        _onRefreshUsers: function () {
+            this.refreshUsersGrid();
+        },
+
+        _onEditUser: function (event) {
+            var selections = this.usersGrid.getSelected();
+            var firstTab = null;
+            for (var i = selections.length - 1; i >= 0; --i) {
+                var tab = this.ensureUserPane(this.id + "_User" + selections[i].username, {
+                    Username: selections[i].username,
+                    Fullname: selections[i].fullname,
+                    Passwordexpiration: selections[i].passwordexpiration
+                });
+                if (i == 0) {
+                    firstTab = tab;
+                }
+            }
+            if (firstTab) {
+                this.selectChild(firstTab);
+            }
+        },
+
+        _onDeleteUser: function (params) {
+            var selections = this.usersGrid.getSelected();
+            if (confirm(this.i18n.DeleteSelectedUsers)) {
+                request = {
+                    ActionType: "delete"
+                };
+                arrayUtil.forEach(selections, function (item, idx) {
+                    request["usernames_i" + idx] = item.username;
+                }, this);
+                var context = this;
+                WsAccess.UserAction({
+                    request: request
+                }).then(function (response) {
+                    context.refreshUsersGrid(true);
+                });
+            }
+        },
+
+        _onUsersRowDblClick: function (username, fullname, passwordexpiration) {
+            var userTab = this.ensureUserPane(this.id + "_" + username, {
+                Username: username,
+                Fullname: fullname,
+                Passwordexpiration: passwordexpiration
+            });
+            this.selectChild(userTab);
+        },
+
+        _onSubmitAddUserDialog: function (event) {
+            if (this.addUserForm.validate()) {
+                var context = this;
+                var request = domForm.toObject(this.addUserForm.id);
+                lang.mixin(request, {
+                    password1: request.password,
+                    password2: request.password
+                })
+                WsAccess.AddUser({
+                    request: request
+                }).then(function (response) {
+                    context.refreshUsersGrid();
+                });
+                registry.byId(this.id + "AddUsersDropDown").closeDropDown();
+            }
+        },
+
+        //  Implementation  ---
+        init: function (params) {
+            if (this.inherited(arguments))
+                return;
+
+            this.initGroupsGrid();
+            this.initUsersGrid();
+            this.refreshActionState();
+        },
+
+        //  Groups  ---
+        initGroupsGrid: function () {
+            this.initGroupsContextMenu();
+            var store = WsAccess.CreateGroupsStore();
+            this.groupsGrid = declare([OnDemandGrid, Keyboard, Selection, ColumnResizer, DijitRegistry, ESPUtil.GridHelper])({
+                allowSelectAll: true,
+                deselectOnRefresh: false,
+                store: store,
+                columns: {
+                    check: selector({
+                        width: 27,
+                        label: " "
+                    }, "checkbox"),
+                    name: {
+                        label: this.i18n.GroupName
+                    }
+                },
+            }, this.id + "GroupsGrid");
+            this.groupsGrid.noDataMessage = "<span class='dojoxGridNoData'>" + this.i18n.noDataMessage + "</span>";
+            var context = this;
+            on(document, ".WuidClick:click", function (evt) {
+                if (context._onGroupsRowDblClick) {
+                    var item = context.groupsGrid.row(evt).data;
+                    context._onGroupsRowDblClick(item.name);
+                }
+            });
+            this.groupsGrid.on(".dgrid-row:dblclick", function (evt) {
+                if (context._onGroupsRowDblClick) {
+                    var item = context.groupsGrid.row(evt).data;
+                    context._onGroupsRowDblClick(item.name);
+                }
+            });
+            this.groupsGrid.onSelectionChanged(function (event) {
+                context.refreshActionState();
+            });
+            this.groupsGrid.onContentChanged(function (event) {
+                context.refreshActionState();
+            });
+            this.groupsGrid.startup();
+        },
+
+        initGroupsContextMenu: function () {
+            var context = this;
+            var pMenu = new Menu({
+                targetNodeIds: [this.id + "GroupsGrid"]
+            });
+            pMenu.addChild(new MenuItem({
+                label: this.i18n.Add,
+                onClick: function (args) {
+                    registry.byId(context.id + "AddGroupsDropDown").openDropDown();
+                }
+            }));
+            pMenu.addChild(new MenuItem({
+                label: this.i18n.Edit,
+                onClick: function (args) { context._onEditGroup(); }
+            }));
+            pMenu.addChild(new MenuItem({
+                label: this.i18n.Delete,
+                onClick: function (args) { context._onDeleteGroup(); }
+            }));
+            pMenu.addChild(new MenuSeparator());
+            pMenu.addChild(new MenuItem({
+                label: this.i18n.Refresh,
+                onClick: function (args) { context._onRefreshGroups(); }
+            }));
+        },
+
+        refreshGroupsGrid: function (clearSelection) {
+            this.groupsGrid.set("query", {
+                id: "*"
+            });
+            if (clearSelection) {
+                this.groupsGrid.clearSelection();
+            }
+        },
+
+        ensureGroupPane: function (id, params) {
+            var retVal = registry.byId(id);
+            if (!retVal) {
+                retVal = new GroupDetailsWidget({
+                    id: id,
+                    title: params.Name,
+                    closable: true,
+                    params: params
+                });
+                this.addChild(retVal, 2);
+            }
+            return retVal;
+        },
+        //  Users  ---
+        initUsersGrid: function () {
+            this.initUsersContextMenu();
+            var store = WsAccess.CreateUsersStore();
+            this.usersGrid = declare([OnDemandGrid, Keyboard, Selection, ColumnResizer, DijitRegistry, ESPUtil.GridHelper])({
+                allowSelectAll: true,
+                deselectOnRefresh: false,
+                store: store,
+                columns: {
+                    check: selector({
+                        width: 27,
+                        label: " "
+                    },"checkbox"),
+                    username: {
+                        width: 180,
+                        label: this.i18n.Username
+                    },
+                    fullname: {
+                        label: this.i18n.FullName
+                    },
+                    passwordexpiration: {
+                        width: 180,
+                        label: this.i18n.PasswordExpiration
+                    }
+                },
+            }, this.id + "UsersGrid");
+            this.usersGrid.noDataMessage = "<span class='dojoxGridNoData'>" + this.i18n.noDataMessage + "</span>";
+            var context = this;
+            on(document, ".WuidClick:click", function (evt) {
+                if (context._onUsersRowDblClick) {
+                    var item = context.usersGrid.row(evt).data;
+                    context._onUsersRowDblClick(item.username,item.fullname,item.passwordexpiration);
+                }
+            });
+            this.usersGrid.on(".dgrid-row:dblclick", function (evt) {
+                if (context._onUsersRowDblClick) {
+                    var item = context.usersGrid.row(evt).data;
+                    context._onUsersRowDblClick(item.username,item.fullname,item.passwordexpiration);
+                }
+            });
+            this.usersGrid.onSelectionChanged(function (event) {
+                context.refreshActionState();
+            });
+            this.usersGrid.onContentChanged(function (event) {
+                context.refreshActionState();
+            });
+            this.usersGrid.startup();
+        },
+
+        initUsersContextMenu: function () {
+            var context = this;
+            var pMenu = new Menu({
+                targetNodeIds: [this.id + "UsersGrid"]
+            });
+            pMenu.addChild(new MenuItem({
+                label: this.i18n.Add,
+                onClick: function (args) {
+                    registry.byId(context.id + "AddUsersDropDown").openDropDown();
+                }
+            }));
+            pMenu.addChild(new MenuItem({
+                label: this.i18n.Edit,
+                onClick: function (args) { context._onEditUser(); }
+            }));
+            pMenu.addChild(new MenuItem({
+                label: this.i18n.Delete,
+                onClick: function (args) { context._onDeleteUser(); }
+            }));
+            pMenu.addChild(new MenuSeparator());
+            pMenu.addChild(new MenuItem({
+                label: this.i18n.Refresh,
+                onClick: function (args) { context._onRefreshUsers(); }
+            }));
+        },
+
+        ensureUserPane: function (id, params) {
+            var retVal = registry.byId(id);
+            if (!retVal) {
+                retVal = new UserDetailsWidget({
+                    id: id,
+                    title: params.Username,
+                    closable: true,
+                    params: params
+                });
+                this.addChild(retVal, 2);
+            }
+            return retVal;
+        },
+
+        refreshUsersGrid: function (clearSelection) {
+            this.usersGrid.set("query",{
+               id: "*"
+            });
+            if (clearSelection) {
+                this.usersGrid.clearSelection();
+            }
+        },
+
+        //  ---  ---
+        initTab: function () {
+            var currSel = this.getSelectedChild();
+            if (currSel && !currSel.initalized) {
+                if (currSel.id === this.groupsTab.id) {
+                } else if (currSel.id === this.usersTab.id) {
+                } else {
+                    if (!currSel.initalized) {
+                        currSel.init(currSel.params);
+                    }
+                }
+            }
+        },
+
+        refreshActionState: function () {
+            var userSelection = this.usersGrid.getSelected();
+            var hasUserSelection = userSelection.length;
+            registry.byId(this.id + "EditUsers").set("disabled", !hasUserSelection);
+            registry.byId(this.id + "DeleteUsers").set("disabled", !hasUserSelection);
+
+            var groupSelection = this.groupsGrid.getSelected();
+            var hasGroupSelection = groupSelection.length;
+            registry.byId(this.id + "EditGroups").set("disabled", !hasGroupSelection);
+            registry.byId(this.id + "DeleteGroups").set("disabled", !hasGroupSelection);
+        }
+    });
+});

+ 29 - 2
esp/files/scripts/_Widget.js

@@ -1,13 +1,15 @@
 define([
     "dojo/_base/declare", // declare
     "dojo/io-query",
+    "dojo/dom",
+    "dojo/dom-attr",
 
     "dijit/layout/_LayoutWidget",
     "dijit/_TemplatedMixin",
     "dijit/_WidgetsInTemplateMixin",
     "dijit/registry"
 
-], function (declare, ioQuery,
+], function (declare, ioQuery, dom, domAttr,
     _LayoutWidget, _TemplatedMixin, _WidgetsInTemplateMixin, registry) {
 
     return declare("_Widget", [_LayoutWidget, _TemplatedMixin, _WidgetsInTemplateMixin], {
@@ -34,7 +36,7 @@ define([
             return false;
         },
 
-        //  Usefull functions  ---
+        //  DOM functions  ---
         setDisabled: function (id, disabled, icon, disabledIcon) {
             var target = registry.byId(id);
             if (target) {
@@ -43,6 +45,31 @@ define([
             }
         },
 
+        updateInput: function (name, oldValue, newValue) {
+            var registryNode = registry.byId(this.id + name);
+            if (registryNode) {
+                registryNode.set("value", newValue);
+            } else {
+                var domElem = dom.byId(this.id + name);
+                if (domElem) {
+                    switch (domElem.tagName) {
+                        case "SPAN":
+                        case "DIV":
+                            domAttr.set(this.id + name, "innerHTML", newValue);
+                            break;
+                        case "INPUT":
+                        case "TEXTAREA":
+                            domAttr.set(this.id + name, "value", newValue);
+                            break;
+                        default:
+                            throw new Error("Unknown DOM element:  " + domElem.tagName);
+                    }
+                }
+            }
+        },
+
+
+        //  String functions  ---
         endsWith: function (str, suffix) {
             return str.indexOf(suffix, str.length - suffix.length) !== -1;
         }

+ 1 - 0
esp/files/scripts/nls/HPCCPlatformWidget.js

@@ -9,6 +9,7 @@ define({ root:
     LoggedInAs: "Logged in as",
     OpenLegacyECLWatch: "Open Legacy ECL Watch",
     Operations: "Operations",
+    Permissions: "Permissions",
     PlaceholderFindText: "Wuid, User, More...",
     PublishedQueries: "Published Queries",
     ReleaseNotes: "Release Notes",

+ 56 - 0
esp/files/scripts/nls/UserQueryWidget.js

@@ -0,0 +1,56 @@
+define({ root:
+//begin v1.x content
+({
+    title: "Permissions",
+
+    Add: "Add",
+    AddGroup: "Add Group",
+    AddUser: "Add User",
+    AllowAccess: "<center>Allow<br>Access",
+    AllowFull: "<center>Allow<br>Full</center>",
+    AllowRead: "<center>Allow<br>Read</center>",
+    AllowWrite: "<center>Allow<br>Write</center>",
+    Cancel: "Cancel",
+    ConfirmPassword: "Confirm Password",
+    DeleteSelectedGroups: "Delete selected group(s)?",
+    DeleteSelectedUsers: "Delete selected user(s)?",
+    DenyAccess: "<center>Deny<br>Access",
+    DenyFull: "<center>Deny<br>Full</center>",
+    DenyRead: "<center>Deny<br>Read</center>",
+    DenyWrite: "<center>Deny<br>Write</center>",
+    Edit: "Edit",
+    FirstName: "First Name",
+    FullName: "Full Name",
+    GroupDetails: "Group Details",
+    GroupName: "Group Name",
+    GroupPermissions: "Group Permissions",
+    Groups: "Groups",
+    LastName: "Last Name",
+    MemberOf: "Member Of",
+    Members: "Members",
+    MustContainUppercaseAndSymbol: "Must contain uppercase and symbol",
+    Name: "Name",
+    NewPassword: "New Password",
+    Open: "Open",
+    Password: "Password",
+    PasswordsDoNotMatch: "Passwords do not match.",
+    PasswordExpiration: "Password Expiration",
+    PlaceholderFirstName: "John",
+    PlaceholderLastName: "Smith",
+    Resource: "Resource",
+    RetypePassword: "Retype Password",
+    Save: "Save",
+    Summary: "Summary",
+    UserDetails: "User Details",
+    UserID: "User ID",
+    UserName: "User Name",
+    Username: "Username",
+    UserPermissions: "User Permissions",
+    Users: "Users"
+})
+//end v1.x content
+,
+"es": true,
+"zh": true,
+"hu": true
+});

+ 414 - 6
esp/files/scripts/ws_access.js

@@ -14,13 +14,421 @@
 #    limitations under the License.
 ############################################################################## */
 define([
-    "hpcc/ESPRequest"
-], function (
-    ESPRequest) {
-    return {
+    "dojo/_base/declare",
+    "dojo/_base/lang",
+    "dojo/_base/array",
+    "dojo/Deferred",
+    "dojo/promise/all",
+    "dojo/store/Memory",
+    "dojo/store/Observable",
+    "dojo/store/util/QueryResults",
+
+    "hpcc/ESPRequest",
+    "hpcc/ESPUtil"
+
+], function (declare, lang, arrayUtil, Deferred, all, Memory, Observable, QueryResults,
+    ESPRequest, ESPUtil) {
+
+    var UsersStore = declare([Memory], {
+
+        constructor: function () {
+            this.idProperty = "__hpcc_id";
+        },
+
+        put: function (object, options) {
+            var retVal = this.inherited(arguments);
+            self.UserGroupEdit({
+                request: {
+                    username: object.username,
+                    action: object.isMember ? "add" : "delete",
+                    groupnames_i1: object.__hpcc_groupname
+                }
+            });
+            return retVal;
+        },
+
+        query: function (query, options) {
+            var results = all([
+                this.refreshUsers(),
+                this.refreshGroupUsers()
+            ]).then(lang.hitch(this, function (response) {
+                var groupUsers = {};
+                arrayUtil.forEach(response[1], function (item, idx) {
+                    groupUsers[item.username] = true;
+                }, this);
+
+                var data = [];
+                arrayUtil.forEach(response[0], function (item, idx) {
+                    data.push(lang.mixin(item, {
+                        __hpcc_groupname: this.groupname,
+                        __hpcc_id: item.username,
+                        isMember: groupUsers[item.username] ? true : false
+                    }));
+                }, this);
+                this.setData(data);
+                return this.data;
+            }));
+            return QueryResults(results);
+        },
+
+        refreshUsers: function () {
+            return self.Users().then(function (response) {
+                if (lang.exists("UserResponse.Users.User", response)) {
+                    return response.UserResponse.Users.User;
+                }
+                return [];
+            });
+        },
+
+        refreshGroupUsers: function (query) {
+            if (!this.groupname) {
+                var deferred = new Deferred;
+                deferred.resolve([]);
+                return deferred.promise;
+            };
+            return self.GroupEdit({
+                request: {
+                    groupname: this.groupname
+                }
+            }).then(function (response) {
+                if (lang.exists("GroupEditResponse.Users.User", response)) {
+                    return response.GroupEditResponse.Users.User;
+                }
+                return [];
+            });
+        }
+    });
+
+    var GroupsStore = declare([Memory], {
+
+        constructor: function () {
+            this.idProperty = "__hpcc_id";
+        },
+
+        put: function (object, options) {
+            var retVal = this.inherited(arguments);
+            self.UserGroupEdit({
+                request: {
+                    username: object.__hpcc_username,
+                    action: object.isMember ? "add" : "delete",
+                    groupnames_i1: object.name
+                }
+            });
+            return retVal;
+        },
+
+        query: function (query, options) {
+            var results = all([
+                this.refreshGroups(),
+                this.refreshUserGroups()
+            ]).then(lang.hitch(this, function (response) {
+                var userGroups = {};
+                arrayUtil.forEach(response[1], function (item, idx) {
+                    userGroups[item.name] = true;
+                }, this);
+
+                var data = [];
+                arrayUtil.forEach(response[0], function (item, idx) {
+                    if (item.name !== "Authenticated Users") {
+                        data.push(lang.mixin(item, {
+                            __hpcc_id: item.name,
+                            __hpcc_username: this.username,
+                            isMember: userGroups[item.name] ? true : false
+                        }));
+                    }
+                }, this);
+                this.setData(data);
+                return this.data;
+            }));
+            return QueryResults(results);
+        },
+
+        refreshGroups: function () {
+            return self.Groups().then(function (response) {
+                if (lang.exists("GroupResponse.Groups.Group", response)) {
+                    return response.GroupResponse.Groups.Group;
+                }
+                return [];
+            });
+        },
+
+        refreshUserGroups: function (query) {
+            return self.UserEdit({
+                request: {
+                    username: this.username
+                }
+            }).then(function (response) {
+                if (lang.exists("UserEditResponse.Groups.Group", response)) {
+                    return response.UserEditResponse.Groups.Group;
+                }
+                return [];
+            });
+        }
+    });
+
+    var CONCAT_SYMBOL = ":";
+    var ResourcesStore = declare([Memory], {
+
+        constructor: function () {
+            this.idProperty = "__hpcc_id";
+        },
+
+        put: function (row) {
+            var item = this.get(row.__hpcc_id);
+            var retVal = this.inherited(arguments);
+            var request = {
+                account_name: this.groupname ? this.groupname : this.username,
+                account_type: this.groupname ? 1 : 0,
+                basedn: row.__hpcc_parent.basedn,
+                rname: row.name,
+                action: "update"
+            };
+            lang.mixin(request, row);
+            self.PermissionAction({
+                request: request
+            });
+            return retVal;
+        },
+
+        query: function (query, options) {
+            var results = all([
+                this.refreshResources(query),
+                this.refreshAccountPermissions(query)
+            ]).then(lang.hitch(this, function (response) {
+                var accountPermissions = {};
+                arrayUtil.forEach(response[1], function (item, idx) {
+                    accountPermissions[item.PermissionName] = item;
+                }, this);
+
+                var data = [];
+                arrayUtil.forEach(response[0], function (item, idx) {
+                    var accountPermission = accountPermissions[item.name];
+                    data.push(lang.mixin(item, {
+                        __hpcc_type: "Resources",
+                        __hpcc_id: this.parentRow.__hpcc_id + CONCAT_SYMBOL + item.name,
+                        __hpcc_parent: this.parentRow,
+                        DisplayName: item.description ? item.description : item.name,
+                        allow_access: accountPermission ? accountPermission.allow_access : false,
+                        allow_read: accountPermission ? accountPermission.allow_read : false,
+                        allow_write: accountPermission ? accountPermission.allow_write : false,
+                        allow_full: accountPermission ? accountPermission.allow_full : false,
+                        deny_access: accountPermission ? accountPermission.deny_access : false,
+                        deny_read: accountPermission ? accountPermission.deny_read : false,
+                        deny_write: accountPermission ? accountPermission.deny_write : false,
+                        deny_full: accountPermission ? accountPermission.deny_full : false
+                    }));
+                }, this);
+                this.setData(data);
+                return data;
+            }));
+            return QueryResults(results);
+        },
+
+        refreshResources: function (query) {
+            return self.Resources({
+                request: {
+                    basedn: this.basedn,
+                }
+            }).then(lang.hitch(this, function (response) {
+                if (lang.exists("ResourcesResponse.Resources.Resource", response)) {
+                    return response.ResourcesResponse.Resources.Resource;
+                }
+                return [];
+            }));
+        },
+
+        refreshAccountPermissions: function () {
+            return self.AccountPermissions({
+                request: {
+                    AccountName: this.groupname ? this.groupname : this.username,
+                    IsGroup: this.groupname ? true : false,
+                    IncludeGroup: false
+                }
+            }).then(lang.hitch(this, function (response) {
+                if (lang.exists("AccountPermissionsResponse.Permissions.Permission", response)) {
+                    return response.AccountPermissionsResponse.Permissions.Permission;
+                }
+                return [];
+            }));
+        }
+    });
+
+    var PermissionsStore = declare([Memory], {
+        service: "ws_access",
+        action: "Permissions",
+        responseQualifier: "BasednsResponse.Basedns.Basedn",
+        idProperty: "__hpcc_id",
+
+        constructor: function () {
+            this.idProperty = "__hpcc_id";
+        },
+
+        get: function (id) {
+            var tmp = id.split(CONCAT_SYMBOL);
+            var retVal = null;
+            if (tmp.length > 0) {
+                var parentID = tmp[0];
+                var parent = this.inherited(arguments, [parentID]);
+                if (tmp.length === 1) {
+                    return parent;
+                }
+                var child = parent.children.get(id);
+                if (child) {
+                    return child;
+                }
+                return parent;
+            }
+            return null;
+        },
+
+        putChild: function (row) {
+            var parent = row.__hpcc_parent;
+            return parent.children.put(row);
+        },
+
+        getChildren: function (parent, options) {
+            return parent.children.query();
+        },
+
+        mayHaveChildren: function (object) {
+            return object.__hpcc_type === "Permission";
+        },
+
+        query: function (query, options) {
+            var results = self.Permissions().then(lang.hitch(this, function (response) {
+                var data = [];
+                if (lang.exists("BasednsResponse.Basedns.Basedn", response)) {
+                    arrayUtil.forEach(response.BasednsResponse.Basedns.Basedn, function (item, idx) {
+                        data.push(lang.mixin(item, {
+                            __hpcc_type: "Permission",
+                            __hpcc_id: item.basedn,
+                            DisplayName: item.name,
+                            children: lang.mixin(self.CreateResourcesStore(this.groupname, this.username, item.basedn), {
+                                parent: this,
+                                parentRow: item
+                            })
+                        }));
+                    }, this);
+                }
+                this.setData(data);
+                return data;
+            }));
+            return QueryResults(results);
+        }
+    });
+
+    var self = {
+        checkError: function (response, sourceMethod) {
+            var retCode = lang.getObject(sourceMethod + "Response.retcode", false, response);
+            var retMsg = lang.getObject(sourceMethod + "Response.retmsg", false, response);
+            if (retCode) {
+                dojo.publish("hpcc/brToaster", {
+                    Severity: "Error",
+                    Source: "WsAccess." + sourceMethod,
+                    Exceptions: [{ Message: retMsg }]
+                });
+            }
+        },
+
+        _doCall: function (action, params) {
+            var context = this;
+            return ESPRequest.send("ws_access", action, params).then(function (response) {
+                context.checkError(response, action);
+                return response;
+            });
+        },
+
+        Users: function (params) {
+            return this._doCall("Users", params);
+        },
+
+        UserAction: function (params) {
+            return this._doCall("UserAction", params);
+        },
+
+        AddUser: function (params) {
+            return this._doCall("AddUser", params);
+        },
+
         UserEdit: function (params) {
-            return ESPRequest.send("ws_access", "UserEdit", params);
+            return this._doCall("UserEdit", params);
+        },
+
+        UserInfoEditInput: function (params) {
+            return this._doCall("UserInfoEditInput", params);
+        },
+
+        UserInfoEdit: function (params) {
+            return this._doCall("UserInfoEdit", params);
+        },
+
+        UserResetPass: function (params) {
+            return this._doCall("UserResetPass", params);
+        },
+
+        UserGroupEdit: function (params) {
+            return this._doCall("UserGroupEdit", params);
+        },
+
+        GroupAdd: function (params) {
+            return this._doCall("GroupAdd", params);
+        },
+
+        GroupAction: function (params) {
+            return this._doCall("GroupAction", params);
+        },
+
+        GroupEdit: function (params) {
+            return this._doCall("GroupEdit", params);
+        },
+
+        Groups: function (params) {
+            return this._doCall("Groups", params);
+        },
+
+        Permissions: function (params) {
+            return this._doCall("Permissions", params);
+        },
+
+        Resources: function (params) {
+            return this._doCall("Resources", params);
+        },
+
+        AccountPermissions: function (params) {
+            return this._doCall("AccountPermissions", params);
+        },
+
+        PermissionAction: function (params) {
+            return this._doCall("PermissionAction", params);
+        },
+
+        CreateUsersStore: function (groupname) {
+            var store = new UsersStore();
+            store.groupname = groupname;
+            return Observable(store);
+        },
+
+        CreateGroupsStore: function (username) {
+            var store = new GroupsStore();
+            store.username = username;
+            return Observable(store);
+        },
+
+        CreatePermissionsStore: function (groupname, username) {
+            var store = new PermissionsStore();
+            store.groupname = groupname;
+            store.username = username;
+            return Observable(store);
+        },
+
+        CreateResourcesStore: function (groupname, username, basedn) {
+            var store = new ResourcesStore();
+            store.groupname = groupname;
+            store.username = username;
+            store.basedn = basedn;
+            return Observable(store);
         }
     };
-});
 
+    return self;
+});

esp/files/scripts/WsAccount.js → esp/files/scripts/ws_account.js


+ 28 - 0
esp/files/templates/GroupDetailsWidget.html

@@ -0,0 +1,28 @@
+<div class="${baseClass}">
+    <div id="${id}BorderContainer" class="${baseClass}BorderContainer" style="width: 100%; height: 100%;" data-dojo-type="dijit.layout.BorderContainer">
+        <div id="${id}TabContainer" data-dojo-props="region: 'center', tabPosition: 'top'" style="width: 100%; height: 100%" data-dojo-type="dijit.layout.TabContainer">
+            <div id="${id}_Summary" style="width: 100%; height: 100%" data-dojo-props='title:"${i18n.Summary}", iconClass:"iconUsers"' data-dojo-type="dijit.layout.BorderContainer">
+                <div id="${id}Toolbar" class="topPanel" data-dojo-props="region: 'top'" data-dojo-type="dijit.Toolbar">
+                    <div id="${id}SaveGroup" data-dojo-attach-event="onClick:_onSave" data-dojo-props="disabled: true" data-dojo-type="dijit.form.Button">${i18n.Save}</div>
+                    <span data-dojo-type="dijit.ToolbarSeparator"></span>
+                    <div id="${id}NewPage" class="right" data-dojo-attach-event="onClick:_onNewPage" data-dojo-props="iconClass:'iconNewPage', showLabel:false" data-dojo-type="dijit.form.Button">${i18n.OpenInNewPage}</div>
+                </div>
+                <div data-dojo-props="region: 'center'" data-dojo-type="dijit.layout.ContentPane">
+                    <h2>
+                        <span id="${id}Group" class="bold">${i18n.GroupName}</span>
+                    </h2>
+                    <form>
+                        <ul>
+                            <li>
+                                <label class="Prompt" for="${id}Name">${i18n.Name}:</label>
+                                <input id="${id}Name" name="name" data-dojo-props="trim: true, disabled: true" data-dojo-type="dijit.form.TextBox" />
+                            </li>
+                        </ul>
+                    </form>
+                </div>
+            </div>
+            <div id="${id}_Members" data-dojo-props='title:"${i18n.Members}", iconClass:"iconGroups"' data-dojo-type="MembersWidget"></div>
+            <div id="${id}_Permissions" data-dojo-props='title:"${i18n.GroupPermissions}", iconClass:"iconFolder"' data-dojo-type="PermissionsWidget"></div>
+        </div>
+    </div>
+</div>

+ 1 - 5
esp/files/templates/HPCCPlatformOpsWidget.html

@@ -10,11 +10,7 @@
             </div>
             <div id="${id}_SystemServers" title="${i18n.SystemServers}" style="padding: 0px; border:0px; border-color:none; overflow: hidden" data-dojo-type="dijit.layout.ContentPane">
             </div>
-            <div id="${id}_Users" title="${i18n.Users}" style="padding: 0px; border:0px; border-color:none; overflow: hidden" data-dojo-type="dijit.layout.ContentPane">
-            </div>
-            <div id="${id}_Groups" title="${i18n.Groups}" style="padding: 0px; border:0px; border-color:none; overflow: hidden" data-dojo-type="dijit.layout.ContentPane">
-            </div>
-            <div id="${id}_Permissions" title="${i18n.Permissions}" style="padding: 0px; border:0px; border-color:none; overflow: hidden" data-dojo-type="dijit.layout.ContentPane">
+            <div id="${id}_Permissions" title="${i18n.Permissions}" data-dojo-type="UserQueryWidget">
             </div>
             <div id="${id}_Resources" title="${i18n.Resources}" style="padding: 0px; border:0px; border-color:none; overflow: hidden" data-dojo-type="dijit.layout.ContentPane">
             </div>

+ 50 - 0
esp/files/templates/UserDetailsWidget.html

@@ -0,0 +1,50 @@
+<div class="${baseClass}">
+    <div id="${id}BorderContainer" class="${baseClass}BorderContainer" style="width: 100%; height: 100%;" data-dojo-type="dijit.layout.BorderContainer">
+        <div id="${id}TabContainer" data-dojo-props="region: 'center', tabPosition: 'top'" style="width: 100%; height: 100%" data-dojo-type="dijit.layout.TabContainer">
+            <div id="${id}_Summary" style="width: 100%; height: 100%" data-dojo-props='title:"${i18n.Summary}", iconClass:"iconUsers"' data-dojo-type="dijit.layout.BorderContainer">
+                <div id="${id}Toolbar" class="topPanel" data-dojo-props="region: 'top'" data-dojo-type="dijit.Toolbar">
+                    <div id="${id}SaveUsers" data-dojo-attach-event="onClick:_onSave" data-dojo-type="dijit.form.Button">${i18n.Save}</div>
+                    <span data-dojo-type="dijit.ToolbarSeparator"></span>
+                    <div id="${id}NewPage" class="right" data-dojo-attach-event="onClick:_onNewPage" data-dojo-props="iconClass:'iconNewPage', showLabel:false" data-dojo-type="dijit.form.Button">${i18n.OpenInNewPage}</div>
+                </div>
+                <div data-dojo-props="region: 'center'" data-dojo-type="dijit.layout.ContentPane">
+                    <h2>
+                        <span id="${id}User" class="bold">${i18n.UserName}</span>
+                    </h2>
+                    <form id="${id}UserForm" data-dojo-type="dijit.form.Form">
+                        <ul>
+                            <li>
+                                <label class="Prompt" for="${id}Username">${i18n.Username}:</label>
+                                <div id="${id}Username"></div>
+                            </li>
+                            <li>
+                                <label class="Prompt" for="${id}FirstName">${i18n.FirstName}:</label>
+                                <input id="${id}FirstName" name="firstname" data-dojo-props="trim: true, placeHolder:'${i18n.PlaceholderFirstName}'" data-dojo-type="dijit.form.TextBox" />
+                            </li>
+                            <li>
+                                <label class="Prompt" for="${id}LastName">${i18n.LastName}:</label>
+                                <input id="${id}LastName" name="lastname" data-dojo-props="trim: true, placeHolder:'${i18n.PlaceholderLastName}'" data-dojo-type="dijit.form.TextBox" />
+                            </li>
+                            <div name="newPassword" data-dojo-props="required: false" data-dojo-type="dojox.form.PasswordValidator">
+                                <li>
+                                    <label class="Prompt" for="${id}NewPassword">${i18n.NewPassword}:</label>
+                                    <input name="newPassword" type="password" pwtype="new" data-dojo-props="invalidMessage:'${i18n.PasswordsDoNotMatch}', placeHolder:'${i18n.MustContainUppercaseAndSymbol}'" data-dojo-type="dijit.form.ValidationTextBox" />
+                                </li>
+                                <li>
+                                    <label class="Prompt" for="${id}VerifyPassword">${i18n.ConfirmPassword}:</label>
+                                    <input name="newPasswordRetype" type="password" pwtype="verify" data-dojo-props="invalidMessage:'${i18n.PasswordsDoNotMatch}', placeHolder:'${i18n.MustContainUppercaseAndSymbol}'" data-dojo-type="dijit.form.ValidationTextBox" />
+                                </li>
+                            </div>
+                            <li>
+                                <label class="Prompt" for="${id}PasswordExpiration">${i18n.PasswordExpiration}:</label>
+                                <div id="${id}PasswordExpiration"></div>
+                            </li>
+                        </ul>
+                    </form>
+                </div>
+            </div>
+            <div id="${id}_MemberOf" data-dojo-props='title:"${i18n.MemberOf}", iconClass:"iconGroups"' data-dojo-type="MemberOfWidget"></div>
+            <div id="${id}_UserPermissions" data-dojo-props='title:"${i18n.UserPermissions}", iconClass:"iconFolder"' data-dojo-type="PermissionsWidget"></div>
+        </div>
+    </div>
+</div>

+ 81 - 0
esp/files/templates/UserQueryWidget.html

@@ -0,0 +1,81 @@
+<div class="${baseClass}">
+    <div id="${id}BorderContainer" class="${baseClass}BorderContainer" style="width: 100%; height: 100%" data-dojo-type="dijit.layout.BorderContainer">
+        <div id="${id}TabContainer" data-dojo-props="region: 'center', tabPosition: 'top'" style="width: 100%; height: 100%" data-dojo-type="dijit.layout.TabContainer">
+            <div id="${id}_Users" style="width: 100%; height: 100%" data-dojo-props='title:"${i18n.Users}", iconClass:"iconUser"' data-dojo-type="dijit.layout.BorderContainer">
+                <div id="${id}Toolbar" class="topPanel" style="padding: 0px; overflow: hidden" data-dojo-props="region: 'top'" data-dojo-type="dijit.Toolbar">
+                    <div id="${id}UsersRefresh" data-dojo-attach-event="onClick:_onRefreshUsers" data-dojo-props="iconClass:'iconRefresh'" data-dojo-type="dijit.form.Button">${i18n.Refresh}</div>
+                    <span data-dojo-type="dijit.ToolbarSeparator"></span>
+                    <div id="${id}AddUsersDropDown" data-dojo-type="dijit.form.DropDownButton">
+                        <span>${i18n.Add}</span>
+                        <div data-dojo-type="dijit.TooltipDialog">
+                            <div id="${id}AddUserForm" onsubmit="return false;" style="width:400px" data-dojo-type="dijit.form.Form">
+                                <div name="password" data-dojo-type="dojox.form.PasswordValidator">
+                                    <table cellspacing="10">
+                                        <tr>
+                                            <td><label for="name">${i18n.UserID}:</label></td>
+                                            <td><input id="${id}AddUsername" name="username" data-dojo-props="trim: true, required: true" data-dojo-type="dijit.form.ValidationTextBox" /></td>
+                                        </tr>
+                                        <tr>
+                                            <td><label for="name">${i18n.FirstName}:</label></td>
+                                            <td><input id="${id}AddUserFirstName" name="firstname" data-dojo-props="trim: true, placeHolder:'${i18n.PlaceholderFirstName}'" data-dojo-type="dijit.form.ValidationTextBox" /></td>
+                                        </tr>
+                                        <tr>
+                                            <td><label for="name">${i18n.LastName}:</label></td>
+                                            <td><input id="${id}AddUserLastName" name="lastname" data-dojo-props="trim: true, placeHolder:'${i18n.PlaceholderLastName}'" data-dojo-type="dijit.form.ValidationTextBox" /></td>
+                                        </tr>
+                                        <tr>
+                                            <td><label for="name">${i18n.Password}:</label></td>
+                                            <td><input name="password1" type="password" pwtype="new" data-dojo-props="trim: true, required: true, invalidMessage:'${i18n.PasswordsDoNotMatch}', placeHolder:'${i18n.MustContainUppercaseAndSymbol}'" data-dojo-type="dijit.form.ValidationTextBox" /></td>
+                                        </tr>
+                                        <tr>
+                                            <td><label for="name">${i18n.RetypePassword}:</label></td>
+                                            <td><input name="password2" type="password" pwtype="verify" data-dojo-props="trim: true, required: true, invalidMessage:'${i18n.PasswordsDoNotMatch}', placeHolder:'${i18n.MustContainUppercaseAndSymbol}'" data-dojo-type="dijit.form.ValidationTextBox" /></td>
+                                        </tr>
+                                    </table>
+                                </div>
+                                <div data-dojo-type="hpcc.TableContainer">
+                                </div>
+                                <div class="dijitDialogPaneActionBar">
+                                    <button id="${id}_onAddSubmit" type="submit" data-dojo-attach-event="onClick:_onSubmitAddUserDialog" data-dojo-type="dijit/form/Button">${i18n.Add}</button>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                    <div id="${id}EditUsers" data-dojo-attach-event="onClick:_onEditUser" data-dojo-type="dijit.form.Button">${i18n.Edit}</div>
+                    <div id="${id}DeleteUsers" data-dojo-attach-event="onClick:_onDeleteUser" data-dojo-type="dijit.form.Button">${i18n.Delete}</div>
+                    <span data-dojo-type="dijit.ToolbarSeparator"></span>
+                    <div class="right" data-dojo-attach-event="onClick:_onNewPage" data-dojo-props="iconClass:'iconNewPage', showLabel:false" data-dojo-type="dijit.form.Button">${i18n.OpenInNewPage}</div>
+                </div>
+                <div id="${id}GridCP" style="padding: 0px; border:0px; border-color:none; overflow: hidden" data-dojo-props="region: 'center'" data-dojo-type="dijit.layout.ContentPane">
+                    <div id="${id}UsersGrid"></div>
+                </div>
+            </div>
+            <div id="${id}_Groups" style="width: 100%; height: 100%" data-dojo-props='title:"${i18n.Groups}", iconClass:"iconGroup"' data-dojo-type="dijit.layout.BorderContainer">
+                <div id="${id}GroupsToolbar" class="topPanel" style="padding: 0px; overflow: hidden" data-dojo-props="region: 'top'" data-dojo-type="dijit.Toolbar">
+                    <div id="${id}GroupsRefresh" data-dojo-attach-event="onClick:_onRefreshGroups" data-dojo-props="iconClass:'iconRefresh'" data-dojo-type="dijit.form.Button">${i18n.Refresh}</div>
+                    <span data-dojo-type="dijit.ToolbarSeparator"></span>
+                    <div id="${id}AddGroupsDropDown" data-dojo-type="dijit.form.DropDownButton">
+                        <span>${i18n.Add}</span>
+                        <div data-dojo-type="dijit.TooltipDialog">
+                            <div id="${id}AddGroupForm" onsubmit="return false;" style="width:400px" data-dojo-type="dijit.form.Form">
+                                <div data-dojo-type="hpcc.TableContainer">
+                                    <input id="${id}AddGroupName" title="${i18n.GroupName}:" name="groupname" data-dojo-props="trim: true, required: true" data-dojo-type="dijit.form.ValidationTextBox" />
+                                </div>
+                                <div class="dijitDialogPaneActionBar">
+                                    <button type="submit" data-dojo-attach-event="onClick:_onAddGroupSubmit" data-dojo-type="dijit.form.Button">${i18n.Add}</button>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                    <div id="${id}EditGroups" data-dojo-attach-event="onClick:_onEditGroup" data-dojo-type="dijit.form.Button">${i18n.Edit}</div>
+                    <div id="${id}DeleteGroups" data-dojo-attach-event="onClick:_onDeleteGroup" data-dojo-type="dijit.form.Button">${i18n.Delete}</div>
+                    <span data-dojo-type="dijit.ToolbarSeparator"></span>
+                    <div class="right" data-dojo-attach-event="onClick:_onNewPage" data-dojo-props="iconClass:'iconNewPage', showLabel:false" data-dojo-type="dijit.form.Button">${i18n.OpenInNewPage}</div>
+                </div>
+                <div id="${id}GroupsGridCP" style="padding: 0px; border:0px; border-color:none; overflow: hidden" data-dojo-props="region: 'center'" data-dojo-type="dijit.layout.ContentPane">
+                    <div id="${id}GroupsGrid"></div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>