Repository: incubator-ranger
Updated Branches:
  refs/heads/master b99525389 -> ebcf9dd5e


RANGER-1237:Ranger permissions do not load when there are bulk users

Signed-off-by: Gautam Borad <gau...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/incubator-ranger/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ranger/commit/ebcf9dd5
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ranger/tree/ebcf9dd5
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ranger/diff/ebcf9dd5

Branch: refs/heads/master
Commit: ebcf9dd5e8c35a12b84405067bb7db0c5c3596c6
Parents: b995253
Author: pradeep agrawal <pradeep.agra...@freestoneinfotech.com>
Authored: Tue Dec 13 12:39:58 2016 +0530
Committer: Gautam Borad <gau...@apache.org>
Committed: Wed Dec 14 15:04:38 2016 +0530

----------------------------------------------------------------------
 .../java/org/apache/ranger/biz/XUserMgr.java    |  24 +-
 .../ranger/service/XGroupPermissionService.java |  26 ++
 .../apache/ranger/service/XGroupService.java    |  17 +-
 .../ranger/service/XModuleDefService.java       |  43 ++--
 .../ranger/service/XUserPermissionService.java  |  27 ++
 .../org/apache/ranger/service/XUserService.java |  24 ++
 .../webapp/scripts/controllers/Controller.js    |   2 +-
 .../main/webapp/scripts/models/XABaseModel.js   |   4 +-
 .../scripts/modules/globalize/message/en.js     |   9 +-
 .../src/main/webapp/scripts/utils/XAUtils.js    |  14 +-
 .../views/permissions/ModulePermissionCreate.js |  22 +-
 .../views/permissions/ModulePermissionForm.js   | 251 +++++++++++--------
 security-admin/src/main/webapp/styles/xa.css    |  21 ++
 .../permissions/ModulePermissionForm_tmpl.html  |  41 ++-
 14 files changed, 354 insertions(+), 171 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/ebcf9dd5/security-admin/src/main/java/org/apache/ranger/biz/XUserMgr.java
----------------------------------------------------------------------
diff --git a/security-admin/src/main/java/org/apache/ranger/biz/XUserMgr.java 
b/security-admin/src/main/java/org/apache/ranger/biz/XUserMgr.java
index e3f7223..189a254 100644
--- a/security-admin/src/main/java/org/apache/ranger/biz/XUserMgr.java
+++ b/security-admin/src/main/java/org/apache/ranger/biz/XUserMgr.java
@@ -851,18 +851,26 @@ public class XUserMgr extends XUserMgrBase {
                VXModuleDef vModuleDefPopulateOld = 
xModuleDefService.populateViewBean(xModuleDef);
 
                List<XXGroupPermission> xgroupPermissionList = 
daoManager.getXXGroupPermission().findByModuleId(vXModuleDef.getId(), true);
-
-               for (XXGroupPermission xGrpPerm : xgroupPermissionList) {
-                       VXGroupPermission vXGrpPerm = 
xGroupPermissionService.populateViewBean(xGrpPerm);
-                       groupPermListOld.add(vXGrpPerm);
+                Map<Long, XXGroup> 
xXGroupMap=xGroupService.getXXGroupIdXXGroupMap();
+                if(xXGroupMap==null || xXGroupMap.isEmpty()){
+                        for (XXGroupPermission xGrpPerm : 
xgroupPermissionList) {
+                                VXGroupPermission vXGrpPerm = 
xGroupPermissionService.populateViewBean(xGrpPerm);
+                                groupPermListOld.add(vXGrpPerm);
+                        }
+                }else{
+                        
groupPermListOld=xGroupPermissionService.getPopulatedVXGroupPermissionList(xgroupPermissionList,xXGroupMap,vModuleDefPopulateOld);
                }
                vModuleDefPopulateOld.setGroupPermList(groupPermListOld);
 
                List<XXUserPermission> xuserPermissionList = 
daoManager.getXXUserPermission().findByModuleId(vXModuleDef.getId(), true);
-
-               for (XXUserPermission xUserPerm : xuserPermissionList) {
-                       VXUserPermission vUserPerm = 
xUserPermissionService.populateViewBean(xUserPerm);
-                       userPermListOld.add(vUserPerm);
+                Map<Long, XXUser> 
xXPortalUserIdXXUserMap=xUserService.getXXPortalUserIdXXUserMap();
+                if(xXPortalUserIdXXUserMap==null || 
xXPortalUserIdXXUserMap.isEmpty()){
+                        for (XXUserPermission xUserPerm : xuserPermissionList) 
{
+                                VXUserPermission vUserPerm = 
xUserPermissionService.populateViewBean(xUserPerm);
+                                userPermListOld.add(vUserPerm);
+                        }
+                }else{
+                        
userPermListOld=xUserPermissionService.getPopulatedVXUserPermissionList(xuserPermissionList,xXPortalUserIdXXUserMap,vModuleDefPopulateOld);
                }
                vModuleDefPopulateOld.setUserPermList(userPermListOld);
 

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/ebcf9dd5/security-admin/src/main/java/org/apache/ranger/service/XGroupPermissionService.java
----------------------------------------------------------------------
diff --git 
a/security-admin/src/main/java/org/apache/ranger/service/XGroupPermissionService.java
 
b/security-admin/src/main/java/org/apache/ranger/service/XGroupPermissionService.java
index 20c3b67..3df5233 100644
--- 
a/security-admin/src/main/java/org/apache/ranger/service/XGroupPermissionService.java
+++ 
b/security-admin/src/main/java/org/apache/ranger/service/XGroupPermissionService.java
@@ -17,12 +17,17 @@
 
 package org.apache.ranger.service;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
 import org.apache.ranger.common.MessageEnums;
 import org.apache.ranger.common.SearchField;
 import org.apache.ranger.db.RangerDaoManager;
 import org.apache.ranger.entity.XXGroup;
 import org.apache.ranger.entity.XXGroupPermission;
 import org.apache.ranger.view.VXGroupPermission;
+import org.apache.ranger.view.VXModuleDef;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Service;
@@ -77,4 +82,25 @@ public class XGroupPermissionService extends 
XGroupPermissionServiceBase<XXGroup
                vObj.setGroupName(xGroup.getName());
                return vObj;
        }
+
+        public List<VXGroupPermission> 
getPopulatedVXGroupPermissionList(List<XXGroupPermission> 
xgroupPermissionList,Map<Long, XXGroup> xXGroupMap,VXModuleDef vModuleDef){
+                List<VXGroupPermission> vXGroupPermissionList = new 
ArrayList<VXGroupPermission>();
+                XXGroup xXGroup=null;
+                for(XXGroupPermission xgroupPermission:xgroupPermissionList){
+                        
if(xXGroupMap.containsKey(xgroupPermission.getGroupId())){
+                                xXGroup 
=xXGroupMap.get(xgroupPermission.getGroupId());
+                                VXGroupPermission vXGrpPerm=new 
VXGroupPermission();
+                                vXGrpPerm.setId(xgroupPermission.getId());
+                                
vXGrpPerm.setGroupId(xgroupPermission.getGroupId());
+                                
vXGrpPerm.setModuleId(xgroupPermission.getModuleId());
+                                
vXGrpPerm.setIsAllowed(xgroupPermission.getIsAllowed());
+                                
vXGrpPerm.setCreateDate(xgroupPermission.getCreateTime());
+                                
vXGrpPerm.setUpdateDate(xgroupPermission.getUpdateTime());
+                                vXGrpPerm.setGroupName(xXGroup.getName());
+                                
vXGrpPerm.setModuleName(vModuleDef.getModule());
+                                vXGroupPermissionList.add(vXGrpPerm);
+                        }
+                }
+                return vXGroupPermissionList;
+        }
 }

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/ebcf9dd5/security-admin/src/main/java/org/apache/ranger/service/XGroupService.java
----------------------------------------------------------------------
diff --git 
a/security-admin/src/main/java/org/apache/ranger/service/XGroupService.java 
b/security-admin/src/main/java/org/apache/ranger/service/XGroupService.java
index d74d2bf..e259eae 100644
--- a/security-admin/src/main/java/org/apache/ranger/service/XGroupService.java
+++ b/security-admin/src/main/java/org/apache/ranger/service/XGroupService.java
@@ -23,6 +23,7 @@ import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.ranger.common.AppConstants;
 import org.apache.ranger.common.MessageEnums;
@@ -41,6 +42,7 @@ import org.apache.ranger.view.VXGroup;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
 
 @Service
 @Scope("singleton")
@@ -253,5 +255,18 @@ public class XGroupService extends 
XGroupServiceBase<XXGroup, VXGroup> {
        protected VXGroup mapEntityToViewBean(VXGroup vObj, XXGroup mObj) {
                super.mapEntityToViewBean(vObj, mObj);
                return vObj;
-       }       
+        }
+
+        public Map<Long, XXGroup> getXXGroupIdXXGroupMap(){
+                Map<Long, XXGroup> xXGroupMap=new HashMap<Long, XXGroup>();
+                try{
+                        List<XXGroup> 
xXGroupList=rangerDaoManager.getXXGroup().getAll();
+                        if(!CollectionUtils.isEmpty(xXGroupList)){
+                                for(XXGroup xXGroup:xXGroupList){
+                                        xXGroupMap.put(xXGroup.getId(), 
xXGroup);
+                                }
+                        }
+                }catch(Exception ex){}
+                return xXGroupMap;
+        }
 }

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/ebcf9dd5/security-admin/src/main/java/org/apache/ranger/service/XModuleDefService.java
----------------------------------------------------------------------
diff --git 
a/security-admin/src/main/java/org/apache/ranger/service/XModuleDefService.java 
b/security-admin/src/main/java/org/apache/ranger/service/XModuleDefService.java
index 2e00643..b8403e8 100644
--- 
a/security-admin/src/main/java/org/apache/ranger/service/XModuleDefService.java
+++ 
b/security-admin/src/main/java/org/apache/ranger/service/XModuleDefService.java
@@ -19,12 +19,14 @@ package org.apache.ranger.service;
 
 import java.util.ArrayList;
 import java.util.List;
-
+import java.util.Map;
 import org.apache.ranger.common.RangerConstants;
 import org.apache.ranger.common.SearchField;
 import org.apache.ranger.db.RangerDaoManager;
+import org.apache.ranger.entity.XXGroup;
 import org.apache.ranger.entity.XXGroupPermission;
 import org.apache.ranger.entity.XXModuleDef;
+import org.apache.ranger.entity.XXUser;
 import org.apache.ranger.entity.XXUserPermission;
 import org.apache.ranger.view.VXGroupPermission;
 import org.apache.ranger.view.VXModuleDef;
@@ -32,6 +34,7 @@ import org.apache.ranger.view.VXUserPermission;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
 
 @Service
 @Scope("singleton")
@@ -49,6 +52,11 @@ public class XModuleDefService extends
        @Autowired
        XGroupPermissionService xGrpPermService;
 
+        @Autowired
+        XUserService xUserService;
+
+        @Autowired
+        XGroupService xGroupService;
        public XModuleDefService() {
                searchFields.add(new SearchField("module", "obj.module",
                                SearchField.DATA_TYPE.STRING, 
SearchField.SEARCH_TYPE.PARTIAL));
@@ -84,34 +92,33 @@ public class XModuleDefService extends
 
        @Override
        public VXModuleDef populateViewBean(XXModuleDef xObj) {
-
                VXModuleDef vModuleDef = super.populateViewBean(xObj);
+                Map<Long, XXUser> 
xXPortalUserIdXXUserMap=xUserService.getXXPortalUserIdXXUserMap();
+                Map<Long, XXGroup> 
xXGroupMap=xGroupService.getXXGroupIdXXGroupMap();
                List<VXUserPermission> vXUserPermissionList = new 
ArrayList<VXUserPermission>();
                List<VXGroupPermission> vXGroupPermissionList = new 
ArrayList<VXGroupPermission>();
-
                List<XXUserPermission> xuserPermissionList = rangerDaoManager
                                
.getXXUserPermission().findByModuleId(xObj.getId(), false);
                List<XXGroupPermission> xgroupPermissionList = rangerDaoManager
                                
.getXXGroupPermission().findByModuleId(xObj.getId(), false);
-               for (XXUserPermission xUserPerm : xuserPermissionList) {
-
-                       VXUserPermission vXUserPerm = xUserPermService
-                                       .populateViewBean(xUserPerm);
-                       vXUserPermissionList.add(vXUserPerm);
-
+                if(CollectionUtils.isEmpty(xXPortalUserIdXXUserMap)){
+                        for (XXUserPermission xUserPerm : xuserPermissionList) 
{
+                                VXUserPermission vXUserPerm = 
xUserPermService.populateViewBean(xUserPerm);
+                                vXUserPermissionList.add(vXUserPerm);
+                        }
+                }else{
+                        
vXUserPermissionList=xUserPermService.getPopulatedVXUserPermissionList(xuserPermissionList,xXPortalUserIdXXUserMap,vModuleDef);
                }
-
-               for (XXGroupPermission xGrpPerm : xgroupPermissionList) {
-
-                       VXGroupPermission vXGrpPerm = xGrpPermService
-                                       .populateViewBean(xGrpPerm);
-                       vXGroupPermissionList.add(vXGrpPerm);
-
+                if(CollectionUtils.isEmpty(xXGroupMap)){
+                        for (XXGroupPermission xGrpPerm : 
xgroupPermissionList) {
+                                VXGroupPermission vXGrpPerm = 
xGrpPermService.populateViewBean(xGrpPerm);
+                                vXGroupPermissionList.add(vXGrpPerm);
+                        }
+                }else{
+                        
vXGroupPermissionList=xGrpPermService.getPopulatedVXGroupPermissionList(xgroupPermissionList,xXGroupMap,vModuleDef);
                }
-
                vModuleDef.setUserPermList(vXUserPermissionList);
                vModuleDef.setGroupPermList(vXGroupPermissionList);
                return vModuleDef;
        }
-
 }

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/ebcf9dd5/security-admin/src/main/java/org/apache/ranger/service/XUserPermissionService.java
----------------------------------------------------------------------
diff --git 
a/security-admin/src/main/java/org/apache/ranger/service/XUserPermissionService.java
 
b/security-admin/src/main/java/org/apache/ranger/service/XUserPermissionService.java
index bd3a50d..3a97ef8 100644
--- 
a/security-admin/src/main/java/org/apache/ranger/service/XUserPermissionService.java
+++ 
b/security-admin/src/main/java/org/apache/ranger/service/XUserPermissionService.java
@@ -17,11 +17,17 @@
 
 package org.apache.ranger.service;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
 import org.apache.ranger.common.SearchField;
 import org.apache.ranger.db.RangerDaoManager;
 import org.apache.ranger.entity.XXModuleDef;
 import org.apache.ranger.entity.XXPortalUser;
+import org.apache.ranger.entity.XXUser;
 import org.apache.ranger.entity.XXUserPermission;
+import org.apache.ranger.view.VXModuleDef;
 import org.apache.ranger.view.VXUserPermission;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Scope;
@@ -71,4 +77,25 @@ public class XUserPermissionService extends 
XUserPermissionServiceBase<XXUserPer
                return vObj;
        }
 
+        public List<VXUserPermission> 
getPopulatedVXUserPermissionList(List<XXUserPermission> 
xuserPermissionList,Map<Long, XXUser> xXPortalUserIdXXUserMap,VXModuleDef 
vModuleDef){
+                List<VXUserPermission> vXUserPermissionList = new 
ArrayList<VXUserPermission>();
+                XXUser xXUser=null;
+                for(XXUserPermission xuserPermission:xuserPermissionList){
+                        
if(xXPortalUserIdXXUserMap.containsKey(xuserPermission.getUserId())){
+                                xXUser 
=xXPortalUserIdXXUserMap.get(xuserPermission.getUserId());
+                                VXUserPermission vXUserPerm=new 
VXUserPermission();
+                                vXUserPerm.setId(xuserPermission.getId());
+                                vXUserPerm.setUserId(xXUser.getId());
+                                
vXUserPerm.setModuleId(xuserPermission.getModuleId());
+                                
vXUserPerm.setIsAllowed(xuserPermission.getIsAllowed());
+                                
vXUserPerm.setCreateDate(xuserPermission.getCreateTime());
+                                
vXUserPerm.setUpdateDate(xuserPermission.getUpdateTime());
+                                
vXUserPerm.setModuleName(vModuleDef.getModule());
+                                vXUserPerm.setLoginId(xXUser.getName());
+                                vXUserPerm.setUserName(xXUser.getName());
+                                vXUserPermissionList.add(vXUserPerm);
+                        }
+                }
+                return vXUserPermissionList;
+        }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/ebcf9dd5/security-admin/src/main/java/org/apache/ranger/service/XUserService.java
----------------------------------------------------------------------
diff --git 
a/security-admin/src/main/java/org/apache/ranger/service/XUserService.java 
b/security-admin/src/main/java/org/apache/ranger/service/XUserService.java
index 8ba9ef6..0d07982 100644
--- a/security-admin/src/main/java/org/apache/ranger/service/XUserService.java
+++ b/security-admin/src/main/java/org/apache/ranger/service/XUserService.java
@@ -24,6 +24,7 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import org.apache.ranger.biz.RangerBizUtil;
@@ -47,6 +48,7 @@ import org.apache.ranger.view.VXUser;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
 
 @Service
 @Scope("singleton")
@@ -383,5 +385,27 @@ public class XUserService extends XUserServiceBase<XXUser, 
VXUser> {
 
                return trxLogList;
        }
+        public Map<Long, XXUser> getXXPortalUserIdXXUserMap(){
+                Map<Long, XXUser> xXPortalUserIdXXUserMap=new HashMap<Long, 
XXUser>();
+                try{
+                        Map<String, XXUser> xXUserMap=new HashMap<String, 
XXUser>();
+                        List<XXUser> 
xXUserList=daoManager.getXXUser().getAll();
+                        if(!CollectionUtils.isEmpty(xXUserList)){
+                                for(XXUser xxUser:xXUserList){
+                                        xXUserMap.put(xxUser.getName(), 
xxUser);
+                                }
+                        }
+                        xXUserList=null;
+                        List<XXPortalUser> 
xXPortalUserList=daoManager.getXXPortalUser().getAll();
+                        if(!CollectionUtils.isEmpty(xXPortalUserList)){
+                                for(XXPortalUser 
xXPortalUser:xXPortalUserList){
+                                        
if(xXUserMap.containsKey(xXPortalUser.getLoginId())){
+                                                
xXPortalUserIdXXUserMap.put(xXPortalUser.getId(),xXUserMap.get(xXPortalUser.getLoginId()));
+                                        }
+                                }
+                        }
+                }catch(Exception ex){}
+                return xXPortalUserIdXXUserMap;
+        }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/ebcf9dd5/security-admin/src/main/webapp/scripts/controllers/Controller.js
----------------------------------------------------------------------
diff --git a/security-admin/src/main/webapp/scripts/controllers/Controller.js 
b/security-admin/src/main/webapp/scripts/controllers/Controller.js
index fc56496..041c6b7 100755
--- a/security-admin/src/main/webapp/scripts/controllers/Controller.js
+++ b/security-admin/src/main/webapp/scripts/controllers/Controller.js
@@ -346,7 +346,7 @@ define(function(require) {
                   var modulePermission         = new ModulePermission({id : 
moduleId});
                   var that = this
                   modulePermission.collection = new ModulePermissionList();
-                  modulePermission.fetch({cache : true}).done(function(){
+                   modulePermission.fetch({cache : false}).done(function(){
                           App.rContent.show(new view({
                                   model : modulePermission,
                                   groupList : that.groupList,

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/ebcf9dd5/security-admin/src/main/webapp/scripts/models/XABaseModel.js
----------------------------------------------------------------------
diff --git a/security-admin/src/main/webapp/scripts/models/XABaseModel.js 
b/security-admin/src/main/webapp/scripts/models/XABaseModel.js
index 03b4e13..6083210 100644
--- a/security-admin/src/main/webapp/scripts/models/XABaseModel.js
+++ b/security-admin/src/main/webapp/scripts/models/XABaseModel.js
@@ -41,9 +41,9 @@ define(function(require){
                },
                bindErrorEvents :function(){
                        //Moved require inside fuctn expression due to ie issue
-                       this.bind("error", function(){
+                        this.bind("error", function(e){
                                var XAUtils = require('utils/XAUtils');
-                               XAUtils.defaultErrorHandler();
+                                XAUtils.defaultErrorHandler(undefined, e);
                        });
                },
                /**

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/ebcf9dd5/security-admin/src/main/webapp/scripts/modules/globalize/message/en.js
----------------------------------------------------------------------
diff --git 
a/security-admin/src/main/webapp/scripts/modules/globalize/message/en.js 
b/security-admin/src/main/webapp/scripts/modules/globalize/message/en.js
index 99a5424..63a5b0f 100644
--- a/security-admin/src/main/webapp/scripts/modules/globalize/message/en.js
+++ b/security-admin/src/main/webapp/scripts/modules/globalize/message/en.js
@@ -237,7 +237,9 @@ define(function(require) {
                                selectMaskingOption             : 'Select 
Masking Option',
                                versionTime                     : 'Version / 
Time',
                                serviceType                     : 'Service 
Type',
-                               hostName                                        
        : 'Host Name'
+                                hostName                                       
        : 'Host Name',
+                                selectAndAddUser                               
 : 'Select and Add User',
+                                selectAndAddGroup                              
 : 'Select and Add Group',
                        },
                        btn : {
                                add                                             
        : 'Add',
@@ -373,7 +375,10 @@ define(function(require) {
                 resultMsg               :'Search by access result i.e 
Allowed/Denied logs.',
                 statusMsg               :'Status of Policy Enable/Disable.',
                 columnfamily            :'Hbase column-family',
-                searchForPluginStatus   : "Search for Plugin Status...."
+                searchForPluginStatus   : "Search for Plugin Status....",
+                pleaseSelectUser        : 'Please select user.',
+                pleaseSelectGroup       : 'Please select group.',
+                addSelectedUserGroup   : 'Please add selected user/group to 
permissions else user/group will not be added.'
 
                        },
                        plcHldr : {

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/ebcf9dd5/security-admin/src/main/webapp/scripts/utils/XAUtils.js
----------------------------------------------------------------------
diff --git a/security-admin/src/main/webapp/scripts/utils/XAUtils.js 
b/security-admin/src/main/webapp/scripts/utils/XAUtils.js
index 14ac70b..c026b52 100644
--- a/security-admin/src/main/webapp/scripts/utils/XAUtils.js
+++ b/security-admin/src/main/webapp/scripts/utils/XAUtils.js
@@ -537,19 +537,10 @@ define(function(require) {
        };
 
        XAUtils.showGroupsOrUsers = function(rawValue, model, userOrGroups) {
-               var showMoreLess = false, objArr = [];
+                var showMoreLess = false, objArr;
                if (!_.isArray(rawValue) && rawValue.length == 0)
                        return '--';
-               if (userOrGroups == 'groups') {
-                       _.each(rawValue, function(perm) {
-                               objArr = _.union(objArr, 
_.escape(perm.groupName))
-                       });
-               } else if (userOrGroups == 'users') {
-                       _.each(rawValue, function(perm) {
-                               objArr = _.union(objArr, 
_.escape(perm.userName))
-                       });
-               }
-
+                objArr = (userOrGroups == 'groups') ? _.pluck(rawValue, 
'groupName') : _.pluck(rawValue, 'userName');
                var newObjArr = _.map(objArr, function(name, i) {
                        if (i >= 4) {
                                return '<span class="label label-info 
float-left-margin-2" policy-' + userOrGroups
@@ -579,7 +570,6 @@ define(function(require) {
                newObjArr.unshift('<div data-id="groupsDiv">');
                newObjArr.push('</div>');
                return newObjArr.length ? newObjArr.join(' ') : '--';
-
        };
 
        XAUtils.defaultErrorHandler = function(model, error) {

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/ebcf9dd5/security-admin/src/main/webapp/scripts/views/permissions/ModulePermissionCreate.js
----------------------------------------------------------------------
diff --git 
a/security-admin/src/main/webapp/scripts/views/permissions/ModulePermissionCreate.js
 
b/security-admin/src/main/webapp/scripts/views/permissions/ModulePermissionCreate.js
index 02b879d..0d1c92f 100644
--- 
a/security-admin/src/main/webapp/scripts/views/permissions/ModulePermissionCreate.js
+++ 
b/security-admin/src/main/webapp/scripts/views/permissions/ModulePermissionCreate.js
@@ -92,29 +92,9 @@ define(function(require){
                initializePlugins: function(){
                },
                renderForm : function(){
-                       var VXGroupList         = 
require('collections/VXGroupList');
-                       var VXUserList          = 
require('collections/VXUserList');
-                   var params = {sortBy : 'name'};
-                   this.userList = new VXUserList();
-                   this.userList.setPageSize(100,{fetch:true});
-                   this.userList.fetch({
-                       cache :false,
-                               data: params,
-                               async : false
-                  });
-                  this.groupList = new VXGroupList();
-                  this.groupList.setPageSize(100,{fetch:true});
-                  this.groupList.fetch({
-                          cache :false,
-                          data : params,
-                          async : false
-                  });
-                  var that = this;
                   this.form = new ModulePermissionForm({
                           template  : 
require('hbs!tmpl/permissions/ModulePermissionForm_tmpl'),
-                          model          : that.model,
-                          groupList : that.groupList,
-                          userList  : that.userList
+                           model         : this.model,
                   });
                   this.rForm.show(this.form);
                },

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/ebcf9dd5/security-admin/src/main/webapp/scripts/views/permissions/ModulePermissionForm.js
----------------------------------------------------------------------
diff --git 
a/security-admin/src/main/webapp/scripts/views/permissions/ModulePermissionForm.js
 
b/security-admin/src/main/webapp/scripts/views/permissions/ModulePermissionForm.js
index 1756955..aa4c332 100644
--- 
a/security-admin/src/main/webapp/scripts/views/permissions/ModulePermissionForm.js
+++ 
b/security-admin/src/main/webapp/scripts/views/permissions/ModulePermissionForm.js
@@ -48,20 +48,35 @@ define(function(require) {
                templateHelpers :function(){
                },
                templateData : function(){
-                       return { 'id' : this.model.id, 'permHeaders' : 
this.getPermHeaders() };
+                        return {
+                                'id' : this.model.id,
+                                'permHeaders' : this.getPermHeaders(),
+                                'userList' : this.model.get('userPermList'),
+                                'groupList' : this.model.get('groupPermList')
+                        };
                },
                initialize : function(options) {
-                       _.extend(this, _.pick(options, 'groupList','userList'));
+                        _.extend(this, _.pick(options));
+
                        if (!this.model.isNew()){
                                this.setupFieldsforEditModule();
                        }
                        Backbone.Form.prototype.initialize.call(this, options);
                },
                ui : {
-                       /*selectGroups  : 'div[data-fields="selectGroups"]',
-                       selectUsers             : 
'div[data-fields="selectUsers"]',*/
+                        selectGroups   : 'div[data-editors="selectGroups"]',
+                        selectUsers            : 
'div[data-editors="selectUsers"]',
+                        addGroupBtn            : '[data-id="addGroupBtn"]',
+                        addUserBtn             : '[data-id="addUserBtn"]',
                },
-               events : {
+                events : function(){
+                        var events = {};
+                        events['click ' + this.ui.addGroupBtn ] = 'onAddGroup';
+                        events['click ' + this.ui.addUserBtn ] = 'onAddUser';
+                        events['click ' + '[data-js="addUser"]']  = 
'onAddUserClick';
+                        events['click ' + '[data-js="selectedGroupSpan"]']  = 
'onRemovedGroupClick';
+
+                        return events;
                },
                /** fields for the form
                */
@@ -81,13 +96,13 @@ define(function(require) {
                                selectGroups : {
                                        type : 'Select2Remote',
                                        editorAttrs  : {'placeholder' :'Select 
Group','tokenSeparators': [",", " "],multiple:true},
-                                       pluginAttr: 
this.getPlugginAttr(true,{'lookupURL':"service/xusers/groups",'permList':that.model.get('groupPermList'),'idKey':'groupId','textKey':'groupName'}),
+                                        pluginAttr: 
this.getPlugginAttr(true,{'lookupURL':"service/xusers/groups",'idKey':'groupId','textKey':'groupName'}),
                                        title : 
localization.tt('lbl.selectGroup')+' *'
                                },
                                selectUsers : {
                                        type : 'Select2Remote',
                                        editorAttrs  : {'placeholder' :'Select 
User','tokenSeparators': [",", " "],multiple:true},
-                                       pluginAttr: 
this.getPlugginAttr(true,{'lookupURL':"service/xusers/users",'permList':that.model.get('userPermList'),'idKey':'userId','textKey':'userName'}),
+                                        pluginAttr: 
this.getPlugginAttr(true,{'lookupURL':"service/xusers/users",'idKey':'userId','textKey':'userName'}),
                                        title : 
localization.tt('lbl.selectUser')+' *',
                                },
                                isAllowed : {
@@ -100,27 +115,28 @@ define(function(require) {
                render: function(options) {
                        var that = this;
                        Backbone.Form.prototype.render.call(this, options);
+                        this.$el.find('[data-js="selectedGroupList"] span 
i').on('click', this.removeGroup.bind(this));
+                        this.$el.find('[data-js="selectedUserList"] span 
i').on('click', this.removeUser.bind(this));
                        
+                        if(this.model.get('groupPermList').length <= 0){
+                                this.$el.find('.emptySelectedGroups').show();
+                        }else{
+                                this.$el.find('.emptySelectedGroups').hide();
+                        }
+                        if(this.model.get('userPermList').length <= 0){
+                                this.$el.find('.emptySelectedUsers').show();
+                        }else{
+                                this.$el.find('.emptySelectedUsers').hide();
+                        }
                },
                setupFieldsforEditModule : function(){
-                       var groupsNVList=[],usersNVList =[];
-                       groupsNVList = 
_.map(this.model.get('groupPermList'),function(gPerm){
-                               return {'id': Number(gPerm.groupId), 
'text':_.escape(gPerm.groupName)};
-                       });
-                       this.model.set('selectGroups', groupsNVList);
-
-                       usersNVList = 
_.map(this.model.get('userPermList'),function(uPerm){
-                               return {'id': Number(uPerm.userId), 
'text':_.escape(uPerm.userName)};
-                       });
-                       this.model.set('selectUsers', usersNVList);
-
+                        this.addedGroups = 
_.map(this.model.get('groupPermList'), function(g){ return { 'id' : g.groupId, 
'text' : g.groupName} });
+                        this.addedUsers = 
_.map(this.model.get('userPermList'), function(u){ return { 'id' : u.userId, 
'text' : u.userName} });
                },
                getPermHeaders : function(){
                        var permList = [];
-                       permList.unshift(localization.tt('lbl.allowAccess'));
-                       permList.unshift(localization.tt('lbl.selectUser'));
-                       permList.unshift(localization.tt('lbl.selectGroup'));
-//                     permList.push("");
+                        
permList.unshift(localization.tt('lbl.selectAndAddUser'));
+                        
permList.unshift(localization.tt('lbl.selectAndAddGroup'));
                        return permList;
                },
                getPlugginAttr :function(autocomplete, options){
@@ -131,25 +147,7 @@ define(function(require) {
                                return {
                                        closeOnSelect : true,
                                        multiple: true,
-                                       minimumInputLength: 0,
                                        tokenSeparators: [",", " "],
-                                       initSelection : function (element, 
callback) {
-                                               var data = [];
-                                               
_.each(options.permList,function (elem) {
-                                                       data.push({id: 
elem[options.idKey], text: _.escape(elem[options.textKey])});
-                                               });
-                                               callback(data);
-                                       },
-                                       createSearchChoice: function(term, 
data) {
-                                               if ($(data).filter(function() {
-                                                       return 
this.text.localeCompare(term) === 0;
-                                               }).length === 0) {
-                                                       return {
-                                                               id : term,
-                                                               text: term
-                                                       };
-                                               }
-                                       },
                                        ajax: {
                                                url: options.lookupURL,
                                                type : 'GET',
@@ -169,12 +167,12 @@ define(function(require) {
                                                        selectedVals = 
that.getSelectedValues(options);
                                                        if(data.resultSize != 
"0"){
                                                                
if(!_.isUndefined(data.vXGroups)){
-                                                                       results 
= data.vXGroups.map(function(m, i){     return {id : m.id+"", text: 
_.escape(m.name) }; });
+                                                                        
results = data.vXGroups.map(function(m, i){    return {id : m.id, text: 
_.escape(m.name) };    });
                                                                } else 
if(!_.isUndefined(data.vXUsers)){
-                                                                       results 
= data.vXUsers.map(function(m, i){      return {id : m.id+"", text: 
_.escape(m.name) }; });
-                                                                       
if(!_.isEmpty(selectedVals)){
+                                                                        
results = data.vXUsers.map(function(m, i){     return {id : m.id, text: 
_.escape(m.name) };    });
+                                                                }
+                                                                
if(!_.isEmpty(selectedVals)){
                                                                                
results = XAUtil.filterResultByText(results, selectedVals);
-                                                                       }
                                                                }
                                                        }
                                                        return { results : 
results};
@@ -202,75 +200,130 @@ define(function(require) {
                },
                getSelectedValues : function(options){
                        var vals = [],selectedVals = [];
-                       var type = options.textKey == 'groupName' ? 
'selectGroups' : 'selectUsers';
-                       var $select = this.$('[name="'+type+'"]');
-                       if(!_.isEmpty($select.select2('data'))){
-                               selectedVals = 
_.map($select.select2('data'),function(obj){ return obj.text; });
+                        var added = options.textKey == 'groupName' ? 
this.addedGroups : this.addedUsers;
+                        if(!_.isEmpty(added)){
+                                selectedVals = _.map(added, function(obj){ 
return obj.text; });
                        }
                        vals.push.apply(vals , selectedVals);
-                       vals = $.unique(vals);
                        return vals;
                },
                beforeSaveModulePermissions : function(){
-                       if(this.model.get('module') != ''){
-                               var groupValStr = 
this.fields.selectGroups.getValue();
-                               var userValStr = 
this.fields.selectUsers.getValue();
-                               
this.compareAndUpdateObj(groupValStr,{'mode':'groups','permList':this.model.get('groupPermList'),'idKey':'groupId','textKey':'groupName'});
-                               
this.compareAndUpdateObj(userValStr,{'mode':'users','permList':this.model.get('userPermList'),'idKey':'userId','textKey':'userName'});
+                        
if(!_.isEmpty(this.fields.selectGroups.editor.$el.select2('data'))
+                                        || 
!_.isEmpty(this.fields.selectUsers.editor.$el.select2('data'))){
+                                XAUtil.alertPopup({
+                                        msg 
:localization.tt('msg.addSelectedUserGroup'),
+                                });
+                                return false;
                        }
+                        this.model.unset('selectUsers');
+                        this.model.unset('selectGroups');
+
                        return true;
                },
-               compareAndUpdateObj: function(objValsStr,options){
+                onAddGroup : function(e){
+                        var that = this, newPerms = [];
+                        var selectedGroups = 
this.fields.selectGroups.editor.$el.select2('data');
+                        _.each(selectedGroups, function(obj){
+                                var self = that;
+                                
this.$el.find('[data-js="selectedGroupList"]').append('<span 
class="selected-widget"  ><i class="icon remove icon-remove" 
data-js="selectedGroupIcon" data-id="'+obj.id+'"></i>&nbsp;'+obj.text+'</span>')
+                                this.addedGroups.push(obj)
+                                this.$el.find('[data-js="selectedGroupList"] 
:last').on('click',this.removeGroup.bind(this));
+                                
this.fields.selectGroups.editor.$el.select2('data',[]);
+                                var addedGroupPerm 
=_.findWhere(this.model.get('groupPermList'), {'groupId': parseInt(obj.id) });
+                                if(!_.isUndefined(addedGroupPerm)){
+                                        addedGroupPerm.isAllowed = 
XAEnums.AccessResult.ACCESS_RESULT_ALLOWED.value;
+                                }else{
+                                        var perm = {};
+                                        perm['moduleId'] = 
that.model.get('id');
+                                        perm['groupId'] = obj['id'];
+                                        perm.isAllowed = 
XAEnums.AccessResult.ACCESS_RESULT_ALLOWED.value;
+                                        newPerms.push(perm);
+                                }
+                        }, this);
+                        if(!_.isEmpty(newPerms)){
+                                var permissions = 
this.model.get('groupPermList');
+                                this.model.set('groupPermList', 
permissions.concat(newPerms));
+                        }
+
+                        this.emptyCheck();
+                        if(_.isEmpty(selectedGroups))  
alert(localization.tt("msg.pleaseSelectGroup"));
+                        return false;
 
-                       var selectedVals = (!_.isNull(objValsStr)) ? 
objValsStr.toString().split(',') : [];
-                       var selectedIdList=[];
-                       selectedVals = _.each(selectedVals, function(eachVal){
-                               //Ignoring any non existing Group Name
-                               if(_.isNumber(parseInt(eachVal))  && 
!_.isNaN(parseInt(eachVal))){
-                                       selectedIdList.push(Number(eachVal));
+                },
+
+                removeGroup : function(e){
+                        var ele = $(e.currentTarget);
+                        var id = ele.attr('data-id');
+                        ele.parent().remove();
+                        this.addedGroups = _.reject(this.addedGroups, 
function(d){ return d.id == id; });
+                        var removedGroupPerm 
=_.findWhere(this.model.get('groupPermList'), {'groupId': parseInt(id) });
+                        if(!_.isUndefined(removedGroupPerm)){
+                                if(!_.has(removedGroupPerm, 'id')){
+                                        this.model.set('groupPermList', 
_.reject(this.model.get('groupPermList'), function(perm){ return perm.groupId 
== removedGroupPerm.groupId }));
+                                }else{
+                                        removedGroupPerm.isAllowed = 
XAEnums.AccessResult.ACCESS_RESULT_DENIED.value;
                                }
-                       });
-                       var modelPermList = options.permList;
-                       var modelPerms = _.unique(_.pluck(options.permList, 
options.idKey));
-                       if(!_.isEmpty(selectedIdList)){
-                               //Look for equals
-                               if(_.isEqual(selectedIdList,modelPerms)) {
-                                       //No changes in Selected Users
-                               } else {
-                                       //look for new values -
-                                       //loop through each new element and 
check if it has any non matching ids
-                                       var diff = _.filter(selectedIdList, 
function(value){ return !_.contains(modelPerms, value); });
-                                       var that = this;
-                                       if(!_.isEmpty(diff)){
-                                               //push new elements to model 
groupPermList
-                                               _.each(diff, function(newEl){
-                                                       var newObj = {};
-                                                       newObj[options.idKey] = 
newEl;
-                                                       newObj['moduleId'] = 
that.model.get('id');
-                                                       newObj['isAllowed'] = 1;
-                                                       
options.permList.push(newObj);
-                                               });
-                                       }
-                                       //Look for removed users/groups
-                                       //loop through each model element and 
check new selected groups is missing from any original list  of group ids
-                                       var updDiff = _.filter(modelPerms, 
function(value){ return !_.contains(selectedIdList, value); });
-                                       if(!_.isEmpty(updDiff)){
-                                               _.each(options.permList, 
function(origElem){
-                                                       if(_.contains(updDiff, 
origElem[options.idKey]))
-                                                               
origElem.isAllowed = 0;
-                                               });
-                                       }
+                        }
+                        this.emptyCheck();
+                },
+                onAddUser : function(e){
+                        var that = this, newPerms = [];
+                        var selectedUsers = 
this.fields.selectUsers.editor.$el.select2('data');
+                        _.each(selectedUsers, function(obj){
+                                var self = that;
+                                
this.$el.find('[data-js="selectedUserList"]').append('<span 
class="selected-widget"  ><i class="icon remove icon-remove" 
data-js="selectedUserIcon" data-id="'+obj.id+'"></i>&nbsp;'+obj.text+'</span>')
+                                this.addedUsers.push(obj)
+                                this.$el.find('[data-js="selectedUserList"] 
:last').on('click',this.removeUser.bind(this));
+                                
this.fields.selectUsers.editor.$el.select2('data', []);
+                                var addedUserPerm 
=_.findWhere(this.model.get('userPermList'), {'userId': parseInt(obj.id) });
+                                if(!_.isUndefined(addedUserPerm)){
+                                        addedUserPerm.isAllowed = 
XAEnums.AccessResult.ACCESS_RESULT_ALLOWED.value;
+                                }else{
+                                        var perm = {};
+                                        perm['moduleId'] = 
that.model.get('id');
+                                        perm['userId'] = obj['id'];
+                                        perm.isAllowed = 
XAEnums.AccessResult.ACCESS_RESULT_ALLOWED.value;
+                                        newPerms.push(perm);
                                }
-
-                       } else {
-                               //Remove permissions from all objects which 
earlier had permission
-                               _.each(options.permList, function(perm){
-                                       perm.isAllowed = 0;
-                               });
+                        }, this);
+                        if(!_.isEmpty(newPerms)){
+                                var permissions = 
this.model.get('userPermList');
+                                this.model.set('userPermList', 
permissions.concat(newPerms));
+                        }
+                        this.emptyCheck();
+                        if(_.isEmpty(selectedUsers))   
alert(localization.tt("msg.pleaseSelectUser"));
+                        return false;
+                },
+                removeUser : function(e){
+                        var ele = $(e.currentTarget);
+                        var id = ele.attr('data-id');
+                        ele.parent().remove();
+                        this.addedUsers = _.reject(this.addedUsers, 
function(d){ return d.id == id; });
+                        var removedUserPerm 
=_.findWhere(this.model.get('userPermList'), {'userId': parseInt(id) });
+                        if(!_.isUndefined(removedUserPerm)){
+                                if(!_.has(removedUserPerm, 'id')){
+                                        this.model.set('userPermList', 
_.reject(this.model.get('userPermList'), function(perm){ return perm.userId == 
removedUserPerm.userId }));
+                                }else{
+                                        removedUserPerm.isAllowed = 
XAEnums.AccessResult.ACCESS_RESULT_DENIED.value;
+                                }
+                        }
+                        this.emptyCheck();
+                },
+                emptyCheck : function(type){
+                        if(this.$el.find('[data-js="selectedGroupList"] 
span').length > 0){
+                                this.$el.find('.emptySelectedGroups').hide();
+                        }else{
+                                this.$el.find('.emptySelectedGroups').show();
                        }
 
+                        if(this.$el.find('[data-js="selectedUserList"] 
span').length > 0){
+                                this.$el.find('.emptySelectedUsers').hide();
+                        }else{
+                                this.$el.find('.emptySelectedUsers').show();
+                        }
                }
+
        });
        
        return ModulePermissionForm;
-});
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/ebcf9dd5/security-admin/src/main/webapp/styles/xa.css
----------------------------------------------------------------------
diff --git a/security-admin/src/main/webapp/styles/xa.css 
b/security-admin/src/main/webapp/styles/xa.css
index 264e0ea..7d86dfa 100644
--- a/security-admin/src/main/webapp/styles/xa.css
+++ b/security-admin/src/main/webapp/styles/xa.css
@@ -2054,4 +2054,25 @@ td.subgrid-custom-cell{
 }
 .VS-search .search_facet_input_container{
        margin-top: 1px;
+}
+.selected-widget {
+    padding: 3px 5px 3px 4px;
+    border-radius: 3px;
+    border: 1px solid #aaaaaa;
+    background-color: #e4e4e4;
+    margin: 3px 3px;
+    float: left;
+}
+.selected-list {
+    margin: 5px;
+    max-height: 270px;
+    overflow: auto;
+}
+
+.inline {
+    display: inline;
+}
+.vertAlignInitial{
+        vertical-align: initial !important;
+        text-align: left;
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/ebcf9dd5/security-admin/src/main/webapp/templates/permissions/ModulePermissionForm_tmpl.html
----------------------------------------------------------------------
diff --git 
a/security-admin/src/main/webapp/templates/permissions/ModulePermissionForm_tmpl.html
 
b/security-admin/src/main/webapp/templates/permissions/ModulePermissionForm_tmpl.html
index 1bf096b..909d2fc 100644
--- 
a/security-admin/src/main/webapp/templates/permissions/ModulePermissionForm_tmpl.html
+++ 
b/security-admin/src/main/webapp/templates/permissions/ModulePermissionForm_tmpl.html
@@ -16,7 +16,7 @@
 --}}
 <form class="form-horizontal">
        <fieldset>
-               <p class="formHeader"> Policy Details : </p>
+                <p class="formHeader"> Module Details : </p>
        <div class="clearfix"></div>
        <b class="policy-form">
                <fieldset>
@@ -35,21 +35,48 @@
                                        <thead>
                                                <tr>
                                                        {{#each permHeaders}}
-                                                       <th>{{./this}}</th>
+                                                        <th 
width="49%">{{./this}}</th>
                                                        {{/each}}
                                                </tr>
                                        </thead>
                                        <tbody class="js-formInput">
                                                <tr>
                                                        <td>
-                                                               <div 
data-editors="selectGroups"></div>
+                                                                <div 
data-editors="selectGroups" class="inline"></div>
+                                                                <button 
data-id="addGroupBtn" class="icon icon-plus icon-2x btn btn-default" title="Add 
Group"></button>
                                                        </td>
                                                        <td>
-                                                               <div 
data-editors="selectUsers"></div>
-                                                       </td>
-                                                       <td>
-                                                               <div 
data-editors="isAllowed"></div>
+                                                                <div 
data-editors="selectUsers" class="inline"></div>
+                                                                <button 
data-id="addUserBtn" class="icon icon-plus icon-2x btn btn-default" title="Add 
User"></button>
+
                                                        </td>
+                                                        <!--<td>
+                                                                 <div 
data-editors="isAllowed"></div>
+                                                        </td>-->
+                                                </tr>
+                                                <tr>
+                                                        <td  
class="vertAlignInitial">
+                                                                <div 
class="selected-list clearfix" data-js="selectedGroupList">
+                                                                <label 
class="editable-empty emptySelectedGroups">No Selected Groups</label>
+                                                                {{#each 
groupList}}
+                                                                        <span 
class="selected-widget">
+                                                                               
 <i class="icon remove icon-remove" data-id="{{./this.groupId}}"></i>&nbsp;
+                                                                               
 {{./this.groupName}}
+                                                                        </span>
+                                                                {{/each}}
+
+                                                                </div></td>
+                                                                <td 
class="vertAlignInitial" width="100%">
+                                                                <div 
class="selected-list clearfix" data-js="selectedUserList">
+                                                                        <label 
class="editable-empty emptySelectedUsers" style="text-align: center;">No 
Selected Users</label>
+
+                                                                        
{{#each userList}}
+                                                                        <span 
class="selected-widget">
+                                                                               
 <i class="icon remove icon-remove" data-id="{{./this.userId}}"></i>&nbsp;
+                                                                               
 {{./this.userName}}
+                                                                        </span>
+                                                                {{/each}}
+                                                                </div></td>
                                                </tr>
                                        </tbody>
                                </table>

Reply via email to