Explorar o código

Merge remote-tracking branch 'origin/closedown-5.0.x'

Signed-off-by: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman %!s(int64=11) %!d(string=hai) anos
pai
achega
ccbcb18853

+ 8 - 5
common/dllserver/dllserver.cpp

@@ -555,8 +555,6 @@ IDllEntry * DllServer::createEntry(IPropertyTree *owner, IPropertyTree *entry)
 
 void DllServer::doRegisterDll(const char * name, const char * kind, const char * dllPath, const char * libPath)
 {
-    Owned<IRemoteConnection> lock = querySDS().connect("/GeneratedDlls", myProcessSession(), RTM_LOCK_WRITE, CONNECTION_TIMEOUT);
-
     RemoteFilename dllRemote;
     StringBuffer ipText, dllText;
     dllRemote.setRemotePath(dllPath);
@@ -566,7 +564,9 @@ void DllServer::doRegisterDll(const char * name, const char * kind, const char *
     Owned<IRemoteConnection> conn = getEntryConnection(name, RTM_LOCK_WRITE);
     if (conn)
     {
-        //check the entry doesn't exist already....
+        /* check the entry doesn't exist already....
+         * Ideally the connection above would be a RTM_LOCK_READ and be changed to a RTM_LOCK_WRITE only when 'location' not found
+         */
         Owned<IPropertyTreeIterator> iter = conn->queryRoot()->getElements("location");
         ForEach(*iter)
         {
@@ -578,12 +578,15 @@ void DllServer::doRegisterDll(const char * name, const char * kind, const char *
     }
     else
     {
+        /* in theory, two clients/threads could get here at the same time
+         * in practice only one client/thread will be adding a unique named generated dll
+         */
         StringBuffer xpath;
         xpath.append("/GeneratedDlls/");
         getMangledTag(xpath, name);
 
-        conn.setown(querySDS().connect(xpath, myProcessSession(), RTM_LOCK_WRITE|RTM_CREATE_ADD, CONNECTION_TIMEOUT));
-        assertex(conn); // RTM_CREATE_ADD will create GeneratedDlls parent node if it doesn't exist.
+        conn.setown(querySDS().connect(xpath, myProcessSession(), RTM_LOCK_WRITE|RTM_CREATE_QUERY, CONNECTION_TIMEOUT));
+        assertex(conn); // RTM_CREATE_QUERY will create GeneratedDlls parent node if it doesn't exist.
 
         IPropertyTree * entry = conn->queryRoot();
         entry->setProp("@name", name);

+ 6 - 2
common/workunit/referencedfilelist.cpp

@@ -288,7 +288,9 @@ void ReferencedFile::processRemoteFileTree(IPropertyTree *tree, const char *srcC
 
 void ReferencedFile::resolveLocal(const char *dstCluster, const char *srcCluster, IUserDescriptor *user, StringArray *subfiles)
 {
-    if (noDfsResolution || (flags & RefFileInPackage))
+    if (flags & RefFileInPackage)
+        return;
+    if (noDfsResolution)
     {
         flags |= RefFileNotFound;
         return;
@@ -334,7 +336,9 @@ void ReferencedFile::resolveRemote(IUserDescriptor *user, INode *remote, const c
 {
     if ((flags & RefFileForeign) && !resolveForeign)
         return;
-    if (noDfsResolution || (flags & RefFileInPackage))
+    if (flags & RefFileInPackage)
+        return;
+    if (noDfsResolution)
     {
         flags |= RefFileNotFound;
         return;

+ 12 - 24
dali/base/dacsds.cpp

@@ -47,10 +47,10 @@ static ISDSManager *SDSManager=NULL;
 
 static CriticalSection SDScrit;
 
-#define CHECK_ORPHANED(XSTR)                                                                                        \
-    if (orphaned)                                                                                                   \
+#define CHECK_CONNECTED(XSTR)                                                                                        \
+    if (!connected)                                                                                                   \
     {                                                                                                               \
-        LOG(MCerror, unknownJob, XSTR": Orphaned connection (xpath=%s, sessionId=%"I64F"d)", xpath.get(), sessionId);       \
+        LOG(MCerror, unknownJob, XSTR": Closed connection (xpath=%s, sessionId=%"I64F"d)", xpath.get(), sessionId);       \
         return;                                                                                                     \
     }
 
@@ -168,7 +168,6 @@ CRemoteConnection::CRemoteConnection(ISDSConnectionManager &manager, ConnectionI
     lazyFetch = true;
     stateChanges = true;
     connected = true;
-    orphaned = false;
     serverIterAvailable = querySDS().queryProperties().getPropBool("Client/@serverIterAvailable");
     serverIter =  querySDS().queryProperties().getPropBool("Client/@serverIter");
     useAppendOpt = querySDS().queryProperties().getPropBool("Client/@useAppendOpt");
@@ -195,20 +194,14 @@ void CRemoteConnection::getDetails(MemoryBuffer &mb)
 // IRemoteConnection impl.
 void CRemoteConnection::changeMode(unsigned mode, unsigned timeout, bool suppressReloads)
 {
-    if (orphaned)
-    {
-        LOG(MCerror, unknownJob, "changeMode: Orphaned connection (xpath=%s, sessionId=%"I64F"d)", xpath.get(), sessionId);
-        throw MakeSDSException(SDSExcpt_OrphanedNode, "%s", queryXPath());
-    }
-    assertex(connected);
+    CHECK_CONNECTED("changeMode");
     manager.changeMode(*this, mode, timeout, suppressReloads);
 }
 
 void CRemoteConnection::rollback()
 {
     CConnectionLock b(*this);
-    CHECK_ORPHANED("rollback");
-    assertex(connected);
+    CHECK_CONNECTED("rollback");
     CDisableFetchChangeBlock block(*this);
     if (((CClientRemoteTree *)root.get())->queryState())
         reload(); // all
@@ -274,8 +267,7 @@ void CRemoteConnection::_rollbackChildren(IPropertyTree *_parent, bool force)
 void CRemoteConnection::rollbackChildren(IPropertyTree *parent, bool force)
 {
     CConnectionLock b(*this);
-    CHECK_ORPHANED("rollbackChildren");
-    assertex(connected);
+    CHECK_CONNECTED("rollbackChildren");
     CDisableFetchChangeBlock block(*this);
 
     _rollbackChildren(parent, force);
@@ -284,8 +276,7 @@ void CRemoteConnection::rollbackChildren(IPropertyTree *parent, bool force)
 void CRemoteConnection::rollbackChildren(const char *_xpath, bool force)
 {
     CConnectionLock b(*this);
-    CHECK_ORPHANED("rollbackChildren");
-    assertex(connected);
+    CHECK_CONNECTED("rollbackChildren");
     CDisableFetchChangeBlock block(*this);
 
     Owned<IPropertyTreeIterator> iter = root->getElements(_xpath);
@@ -296,8 +287,7 @@ void CRemoteConnection::rollbackChildren(const char *_xpath, bool force)
 void CRemoteConnection::reload(const char *_xpath)
 {
     CConnectionLock b(*this);
-    CHECK_ORPHANED("close");
-    assertex(connected);
+    CHECK_CONNECTED("close");
     CDisableFetchChangeBlock block(*this);
     // NB: any linked client trees will still be active.
     if (_xpath == NULL || '\0' == *_xpath)
@@ -350,17 +340,15 @@ void CRemoteConnection::reload(const char *_xpath)
 void CRemoteConnection::commit()
 {
     CConnectionLock b(*this);
-    CHECK_ORPHANED("commit");
-    assertex(connected);
+    CHECK_CONNECTED("commit");
     manager.commit(*this, NULL);
 }
 
 void CRemoteConnection::close(bool deleteRoot)
 {
     CConnectionLock b(*this);
-    CHECK_ORPHANED("close");
-    if (connected)
-        manager.commit(*this, &deleteRoot);
+    CHECK_CONNECTED("close");
+    manager.commit(*this, &deleteRoot);
     connected=false;
 }
 
@@ -1301,7 +1289,7 @@ CClientSDSManager::~CClientSDSManager()
     ForEach(iter)
     {
         CRemoteConnection &conn = (CRemoteConnection &) iter.query();
-        conn.setOrphaned();
+        conn.setConnected(false);
     }
     ::Release(properties);
 }

+ 1 - 2
dali/base/dacsds.ipp

@@ -101,7 +101,6 @@ public:
     inline ISDSConnectionManager &queryManager() { return manager; }
     inline bool queryConnected() { return connected; }
     inline void setConnected(bool _connected) { connected = _connected; }
-    inline void setOrphaned() { orphaned = true; }
     inline bool queryServerIter() const { return serverIter; }
     inline bool queryServerIterAvailable() const { return serverIterAvailable; }
     inline bool queryServerGetIdsAvailable() const { return serverGetIdsAvailable; }
@@ -134,7 +133,7 @@ private:
     unsigned hash;
     bool lazyFetch;
     bool stateChanges;      // =false when client applying server received changes
-    bool connected, orphaned, serverIterAvailable, serverIter, useAppendOpt, serverGetIdsAvailable;
+    bool connected, serverIterAvailable, serverIter, useAppendOpt, serverGetIdsAvailable;
 
 friend class CConnectionLock;
 friend class CSetServerIterBlock;

+ 14 - 12
esp/services/ws_workunits/ws_workunitsQuerySets.cpp

@@ -67,6 +67,8 @@ IClientWUQuerySetDetailsResponse *fetchQueryDetails(IEspContext &context, IClien
         VStringBuffer url("http://%s:%d/WsWorkunits", host.str(), port);
         ws.setown(createWsWorkunitsClient());
         ws->addServiceUrl(url.str());
+        if (context.queryUserId() && *context.queryUserId())
+            ws->setUsernameToken(context.queryUserId(), context.queryPassword(), NULL);
     }
     //using existing WUQuerysetDetails rather than extending WUQueryDetails, to support copying query meta data from prior releases
     Owned<IClientWUQuerySetDetailsRequest> reqQueryInfo = ws->createWUQuerysetDetailsRequest();
@@ -389,26 +391,25 @@ void QueryFilesInUse::loadTargets(IPropertyTree *t, unsigned flags)
 
 IPropertyTreeIterator *QueryFilesInUse::findAllQueriesUsingFile(const char *lfn)
 {
-    CriticalBlock b(crit);
-
     if (!lfn || !*lfn)
         return NULL;
 
+    Owned<IPropertyTree> t = getTree();
     VStringBuffer xpath("*/Query[File/@lfn='%s']", lfn);
-    return tree->getElements(xpath);
+    return t->getElements(xpath);
 }
 
-IPropertyTreeIterator *QueryFilesInUse::findQueriesUsingFile(const char *target, const char *lfn)
+IPropertyTreeIterator *QueryFilesInUse::findQueriesUsingFile(const char *target, const char *lfn, StringAttr &pmid)
 {
-    CriticalBlock b(crit);
-
     if (!lfn || !*lfn)
         return NULL;
     if (!target || !*target)
         return findAllQueriesUsingFile(lfn);
-    IPropertyTree *targetTree = tree->queryPropTree(target);
+    Owned<IPropertyTree> t = getTree();
+    IPropertyTree *targetTree = t->queryPropTree(target);
     if (!targetTree)
         return NULL;
+    pmid.set(targetTree->queryProp("@pmid"));
 
     VStringBuffer xpath("Query[File/@lfn='%s']", lfn);
     return targetTree->getElements(xpath);
@@ -1265,7 +1266,8 @@ bool CWsWorkunitsEx::onWUListQueries(IEspContext &context, IEspWUListQueriesRequ
     if (lfn && *lfn)
     {
         queriesUsingFileMap.setown(new MapStringTo<bool>());
-        Owned<IPropertyTreeIterator> queriesUsingFile = filesInUse.findQueriesUsingFile(clusterReq, lfn);
+        StringAttr dummy;
+        Owned<IPropertyTreeIterator> queriesUsingFile = filesInUse.findQueriesUsingFile(clusterReq, lfn, dummy);
         ForEach (*queriesUsingFile)
         {
             IPropertyTree &queryUsingFile = queriesUsingFile->query();
@@ -1373,11 +1375,11 @@ bool CWsWorkunitsEx::onWUListQueriesUsingFile(IEspContext &context, IEspWUListQu
         target = targets.item(i);
         Owned<IEspTargetQueriesUsingFile> respTarget = createTargetQueriesUsingFile();
         respTarget->setTarget(target);
-        const char *pmid = filesInUse.getPackageMap(target);
-        if (pmid && *pmid)
-            respTarget->setPackageMap(pmid);
 
-        Owned<IPropertyTreeIterator> queries = filesInUse.findQueriesUsingFile(target, lfn);
+        StringAttr pmid;
+        Owned<IPropertyTreeIterator> queries = filesInUse.findQueriesUsingFile(target, lfn, pmid);
+        if (!pmid.isEmpty())
+            respTarget->setPackageMap(pmid);
         if (queries)
         {
             IArrayOf<IEspQueryUsingFile> respQueries;

+ 0 - 3
esp/services/ws_workunits/ws_workunitsService.cpp

@@ -420,9 +420,6 @@ void CWsWorkunitsEx::init(IPropertyTree *cfg, const char *process, const char *s
 
     m_sched.start();
     filesInUse.subscribe();
-
-    QueryFilesInUseUpdateThread *updateFilesInUse = new QueryFilesInUseUpdateThread(filesInUse);
-    updateFilesInUse->startRelease();
 }
 
 void CWsWorkunitsEx::refreshValidClusters()

+ 37 - 45
esp/services/ws_workunits/ws_workunitsService.hpp

@@ -26,9 +26,10 @@
 #include "zcrypt.hpp"
 #endif
 
-#define UFO_RELOAD_TARGETS_CHANGED_PMID          0x01
-#define UFO_RELOAD_MAPPED_QUERIES                0x02
-#define UFO_REMOVE_QUERIES_NOT_IN_QUERYSET       0x04
+#define UFO_DIRTY                                0x01
+#define UFO_RELOAD_TARGETS_CHANGED_PMID          0x02
+#define UFO_RELOAD_MAPPED_QUERIES                0x04
+#define UFO_REMOVE_QUERIES_NOT_IN_QUERYSET       0x08
 
 class QueryFilesInUse : public CInterface, implements ISDSSubscription
 {
@@ -40,14 +41,17 @@ class QueryFilesInUse : public CInterface, implements ISDSSubscription
     SubscriptionId qsChange;
     SubscriptionId pmChange;
     SubscriptionId psChange;
+    mutable CriticalSection dirtyCrit; //if there were an atomic_or I would just use atomic
+    unsigned dirty;
     bool aborting;
-
-public:
-    IMPLEMENT_IINTERFACE;
-    QueryFilesInUse() : aborting(false), qsChange(0), pmChange(0), psChange(0)
+private:
+    void loadTarget(IPropertyTree *tree, const char *target, unsigned flags);
+    void loadTargets(IPropertyTree *tree, unsigned flags);
+    void load(unsigned flags)
     {
-        tree.setown(createPTree("QueryFilesInUse"));
-        updateUsers();
+        Owned<IPropertyTree> t = createPTreeFromIPT(tree);
+        loadTargets(t, flags);
+        tree.setown(t.getClear());
     }
 
     void updateUsers()
@@ -66,31 +70,24 @@ public:
         }
     }
 
-    const char *getPackageMap(const char *target)
-    {
-        VStringBuffer xpath("%s/@pmid", target);
-        return tree->queryProp(xpath);
-    }
-
-    void loadTarget(IPropertyTree *tree, const char *target, unsigned flags);
-    void loadTargets(IPropertyTree *tree, unsigned flags);
-    void reload(unsigned flags)
+public:
+    IMPLEMENT_IINTERFACE;
+    QueryFilesInUse() : aborting(false), qsChange(0), pmChange(0), psChange(0), dirty(UFO_DIRTY)
     {
-        Owned<IPropertyTree> t = createPTreeFromIPT(tree);
-        loadTargets(t, flags);
-        CriticalBlock b(crit);
-        tree.setown(t.getClear());
+        tree.setown(createPTree("QueryFilesInUse"));
+        updateUsers();
     }
 
     virtual void notify(SubscriptionId subid, const char *xpath, SDSNotifyFlags flags, unsigned valueLen, const void *valueData)
     {
         Linked<QueryFilesInUse> me = this;  // Ensure that I am not released by the notify call (which would then access freed memory to release the critsec)
+        CriticalBlock b(dirtyCrit);
         if (subid == qsChange)
-            reload(UFO_REMOVE_QUERIES_NOT_IN_QUERYSET);
+            dirty |= UFO_REMOVE_QUERIES_NOT_IN_QUERYSET;
         else if (subid == pmChange)
-            reload(UFO_RELOAD_MAPPED_QUERIES);
+            dirty |= UFO_RELOAD_MAPPED_QUERIES;
         else if (subid == psChange)
-            reload(UFO_RELOAD_TARGETS_CHANGED_PMID);
+            dirty |= UFO_RELOAD_TARGETS_CHANGED_PMID;
     }
     virtual void subscribe()
     {
@@ -133,31 +130,26 @@ public:
         aborting=true;
         CriticalBlock b(crit);
     }
-    IPropertyTreeIterator *findQueriesUsingFile(const char *target, const char *lfn);
-    IPropertyTreeIterator *findAllQueriesUsingFile(const char *lfn);
-    StringBuffer &toStr(StringBuffer &s)
+    IPropertyTree *getTree()
     {
         CriticalBlock b(crit);
-        return toXML(tree, s);
+        unsigned flags;
+        {
+            CriticalBlock b(dirtyCrit);
+            flags = dirty;
+            dirty = 0;
+        }
+        if (flags)
+            load(flags);
+        return LINK(tree);
     }
 
-};
-
-class QueryFilesInUseUpdateThread : public Thread
-{
-    QueryFilesInUse &filesInUse;
-
-public:
-    QueryFilesInUseUpdateThread(QueryFilesInUse &_filesInUse) : filesInUse(_filesInUse) {}
-
-    virtual int run()
-    {
-        filesInUse.reload(0);
-        return 0;
-    }
-    virtual void start()
+    IPropertyTreeIterator *findAllQueriesUsingFile(const char *lfn);
+    IPropertyTreeIterator *findQueriesUsingFile(const char *target, const char *lfn, StringAttr &pmid);
+    StringBuffer &toStr(StringBuffer &s)
     {
-        Thread::start();
+        Owned<IPropertyTree> t = getTree();
+        return toXML(t, s);
     }
 };
 

+ 3 - 1
esp/src/eclwatch/ActivityWidget.js

@@ -296,7 +296,9 @@ define([
                         label: this.i18n.Graph, width: 90, sortable: true,
                         formatter: function (_gid, row) {
                             if (context.activity.isInstanceOfWorkunit(row)) {
-                                return "<a href='#' class='" + context.id + "GraphClick'>" + row.GraphName + "-" + row.GID + "</a>";
+                                if (row.GraphName) {
+                                    return "<a href='#' class='" + context.id + "GraphClick'>" + row.GraphName + "-" + row.GID + "</a>";
+                                }
                             }
                             return "";
                         }

+ 2 - 3
esp/src/eclwatch/GroupDetailsWidget.js

@@ -32,8 +32,7 @@ define([
 
     "hpcc/_TabContainerWidget",
     "hpcc/ws_access",
-    "hpcc/MembersWidget",
-    "hpcc/PermissionsWidget",
+    "hpcc/DelayLoadWidget",
 
     "dojo/text!../templates/GroupDetailsWidget.html",
 
@@ -48,7 +47,7 @@ define([
 ], function (declare, lang, i18n, nlsHPCC, dom, domAttr,
                 registry,
                 OnDemandGrid, Keyboard, Selection, selector, ColumnResizer, DijitRegistry,
-                _TabContainerWidget, WsAccess, MembersWidget, PermissionsWidget,
+                _TabContainerWidget, WsAccess, DelayLoadWidget,
                 template) {
     return declare("GroupDetailsWidget", [_TabContainerWidget], {
         templateString: template,

+ 1 - 1
esp/src/eclwatch/QuerySetQueryWidget.js

@@ -338,7 +338,7 @@ define([
                     QuerySetId:{
                         width: 140,
                         label: this.i18n.Target,
-                        sortable: false
+                        sortable: true
                     },
                     Wuid: {
                         width: 180,

+ 2 - 3
esp/src/eclwatch/UserDetailsWidget.js

@@ -33,8 +33,7 @@ define([
 
     "hpcc/_TabContainerWidget",
     "hpcc/ws_access",
-    "hpcc/MemberOfWidget",
-    "hpcc/PermissionsWidget",
+    "hpcc/DelayLoadWidget",
 
     "dojo/text!../templates/UserDetailsWidget.html",
 
@@ -52,7 +51,7 @@ define([
 ], function (declare, lang, i18n, nlsHPCC, dom, domAttr, domForm,
                 registry,
                 OnDemandGrid, Keyboard, Selection, selector, ColumnResizer, DijitRegistry,
-                _TabContainerWidget, WsAccess, MemberOfWidget, PermissionsWidget,
+                _TabContainerWidget, WsAccess, DelayLoadWidget,
                 template) {
     return declare("UserDetailsWidget", [_TabContainerWidget], {
         templateString: template,

+ 18 - 2
esp/src/eclwatch/UserQueryWidget.js

@@ -137,6 +137,7 @@ define([
                     request: request
                 }).then(function (response) {
                     context.refreshGroupsGrid(true);
+                    return response;
                 });
             }
         },
@@ -155,7 +156,11 @@ define([
                 WsAccess.GroupAdd({
                     request: request
                 }).then(function (response) {
-                    context.refreshGroupsGrid();
+                    if (lang.exists("GroupAddResponse.retcode", response) && response.GroupAddResponse.retcode === 0) {
+                        context.refreshGroupsGrid();
+                        context._onGroupsRowDblClick(response.GroupAddResponse.groupname);
+                    }
+                    return response;
                 });
                 registry.byId(this.id + "AddGroupsDropDown").closeDropDown();
             }
@@ -222,7 +227,11 @@ define([
                 WsAccess.AddUser({
                     request: request
                 }).then(function (response) {
-                    context.refreshUsersGrid();
+                    if (lang.exists("AddUserResponse.retcode", response) && response.AddUserResponse.retcode === 0) {
+                        context.refreshUsersGrid();
+                        context._onUsersRowDblClick(request.username);
+                    }
+                    return response;
                 });
                 registry.byId(this.id + "AddUsersDropDown").closeDropDown();
             }
@@ -242,6 +251,7 @@ define([
                     request: request
                 }).then(function (response) {
                     context.refreshPermissionsGrid();
+                    return response;
                 });
                 registry.byId(this.id + "AddPermissionsDropDown").closeDropDown();
             }
@@ -304,6 +314,7 @@ define([
                         label: " "
                     }, "checkbox"),
                     name: {
+                        sortable: false,
                         label: this.i18n.GroupName
                     }
                 }
@@ -390,13 +401,16 @@ define([
                     },"checkbox"),
                     username: {
                         width: 180,
+                        sortable: false,
                         label: this.i18n.Username
                     },
                     fullname: {
+                        sortable: false,
                         label: this.i18n.FullName
                     },
                     passwordexpiration: {
                         width: 180,
+                        sortable: false,
                         label: this.i18n.PasswordExpiration
                     }
                 }
@@ -507,9 +521,11 @@ define([
                     }, "checkbox"),
                     DisplayName: tree({
                         width: 360,
+                        sortable: false,
                         label: this.i18n.Name
                     }),
                     basedn: {
+                        sortable: false,
                         label: "basedn"
                     }
                 }

+ 2 - 1
esp/src/eclwatch/css/hpcc.css

@@ -142,6 +142,7 @@ hr.dashedLine {
 
 .claro .glow :hover{
     background-color:rgb(118, 157, 192);
+    border-radius: 4px;
     cursor: pointer;
 }
 
@@ -311,7 +312,7 @@ hr.dashedLine {
 
 .iconLogo{
     background-image: url("../img/hpcc_systems_logo.png");
-    width: 180px;
+    width: 212px;
     height: 32px;
     cursor: pointer;
 }

BIN=BIN
esp/src/eclwatch/img/hpcc_systems_logo.png


+ 1 - 0
esp/src/eclwatch/nls/hpcc.js

@@ -370,6 +370,7 @@ define({root:
     Scope: "Scope",
     SearchResults: "Search Results",
     SecondsRemaining: "Seconds Remaining",
+    Security: "Security",
     SelectPackageFile: "Select Package File",
     Separators: "Separators",
     SetBanner: "Set Banner",

+ 2 - 2
esp/src/eclwatch/templates/GroupDetailsWidget.html

@@ -21,8 +21,8 @@
                     </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 id="${id}_Members" data-dojo-props="delayWidget: 'MembersWidget', title:'${i18n.Members}', iconClass:'iconGroups'" data-dojo-type="DelayLoadWidget"></div>
+            <div id="${id}_Permissions" data-dojo-props="delayWidget: 'PermissionsWidget', title:'${i18n.GroupPermissions}', iconClass:'iconFolder'" data-dojo-type="DelayLoadWidget"></div>
         </div>
     </div>
 </div>

+ 1 - 1
esp/src/eclwatch/templates/HPCCPlatformOpsWidget.html

@@ -12,7 +12,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}_Permissions" title="${i18n.Permissions}" data-dojo-type="UserQueryWidget">
+            <div id="${id}_Permissions" title="${i18n.Security}" 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>

+ 2 - 2
esp/src/eclwatch/templates/UserDetailsWidget.html

@@ -43,8 +43,8 @@
                     </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 id="${id}_MemberOf" data-dojo-props="delayWidget: 'MemberOfWidget', title:'${i18n.MemberOf}', iconClass:'iconGroups'" data-dojo-type="DelayLoadWidget"></div>
+            <div id="${id}_UserPermissions" data-dojo-props="delayWidget: 'PermissionsWidget', title:'${i18n.UserPermissions}', iconClass:'iconFolder'" data-dojo-type="DelayLoadWidget"></div>
         </div>
     </div>
 </div>