Repository: incubator-ranger Updated Branches: refs/heads/master 6ba785079 -> b15105336
RANGER-204 : Update show/hide users/groups functionality Signed-off-by: Velmurugan Periasamy <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/incubator-ranger/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ranger/commit/b1510533 Tree: http://git-wip-us.apache.org/repos/asf/incubator-ranger/tree/b1510533 Diff: http://git-wip-us.apache.org/repos/asf/incubator-ranger/diff/b1510533 Branch: refs/heads/master Commit: b15105336769622148e9cc81e2be48b48e46dda9 Parents: 6ba7850 Author: Gautam Borad <[email protected]> Authored: Wed Apr 1 23:54:53 2015 +0530 Committer: Velmurugan Periasamy <[email protected]> Committed: Fri Apr 3 17:50:33 2015 -0400 ---------------------------------------------------------------------- .../scripts/collection_bases/VXGroupListBase.js | 2 + .../scripts/collection_bases/VXUserListBase.js | 2 + .../src/main/webapp/scripts/models/VXGroup.js | 2 + .../src/main/webapp/scripts/models/VXUser.js | 2 + .../main/webapp/scripts/modules/XAOverrides.js | 208 ++++++++++++++++- .../scripts/modules/globalize/message/en.js | 1 + .../scripts/views/users/UserTableLayout.js | 231 ++++--------------- .../templates/users/UserTableLayout_tmpl.html | 28 +-- 8 files changed, 277 insertions(+), 199 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/b1510533/security-admin/src/main/webapp/scripts/collection_bases/VXGroupListBase.js ---------------------------------------------------------------------- diff --git a/security-admin/src/main/webapp/scripts/collection_bases/VXGroupListBase.js b/security-admin/src/main/webapp/scripts/collection_bases/VXGroupListBase.js index 4b86d08..20edf0b 100644 --- a/security-admin/src/main/webapp/scripts/collection_bases/VXGroupListBase.js +++ b/security-admin/src/main/webapp/scripts/collection_bases/VXGroupListBase.js @@ -40,6 +40,8 @@ define(function(require){ initialize : function() { this.modelName = 'VXGroup'; this.modelAttrName = 'vXGroups'; + var multiSelect = new Backbone.Picky.MultiSelect(this); + _.extend(this, multiSelect); this.bindErrorEvents(); this._changes = { }; this.on('change', this._onChange); http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/b1510533/security-admin/src/main/webapp/scripts/collection_bases/VXUserListBase.js ---------------------------------------------------------------------- diff --git a/security-admin/src/main/webapp/scripts/collection_bases/VXUserListBase.js b/security-admin/src/main/webapp/scripts/collection_bases/VXUserListBase.js index 2c7f5fc..3745bc0 100644 --- a/security-admin/src/main/webapp/scripts/collection_bases/VXUserListBase.js +++ b/security-admin/src/main/webapp/scripts/collection_bases/VXUserListBase.js @@ -40,6 +40,8 @@ define(function(require){ initialize : function() { this.modelName = 'VXUser'; this.modelAttrName = 'vXUsers'; + var multiSelect = new Backbone.Picky.MultiSelect(this); + _.extend(this, multiSelect); this.bindErrorEvents(); this._changes = { }; this.on('change', this._onChange); http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/b1510533/security-admin/src/main/webapp/scripts/models/VXGroup.js ---------------------------------------------------------------------- diff --git a/security-admin/src/main/webapp/scripts/models/VXGroup.js b/security-admin/src/main/webapp/scripts/models/VXGroup.js index fdad8aa..3aa3615 100644 --- a/security-admin/src/main/webapp/scripts/models/VXGroup.js +++ b/security-admin/src/main/webapp/scripts/models/VXGroup.js @@ -35,6 +35,8 @@ define(function(require){ */ initialize: function() { this.modelName = 'VXGroup'; + var selectable = new Backbone.Picky.Selectable(this); + _.extend(this, selectable); this.bindErrorEvents(); this.toView(); }, http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/b1510533/security-admin/src/main/webapp/scripts/models/VXUser.js ---------------------------------------------------------------------- diff --git a/security-admin/src/main/webapp/scripts/models/VXUser.js b/security-admin/src/main/webapp/scripts/models/VXUser.js index b74ed5a..875b828 100644 --- a/security-admin/src/main/webapp/scripts/models/VXUser.js +++ b/security-admin/src/main/webapp/scripts/models/VXUser.js @@ -34,6 +34,8 @@ define(function(require){ */ initialize: function() { this.modelName = 'VXUser'; + var selectable = new Backbone.Picky.Selectable(this); + _.extend(this, selectable); this.bindErrorEvents(); this.toView(); }, http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/b1510533/security-admin/src/main/webapp/scripts/modules/XAOverrides.js ---------------------------------------------------------------------- diff --git a/security-admin/src/main/webapp/scripts/modules/XAOverrides.js b/security-admin/src/main/webapp/scripts/modules/XAOverrides.js index f312065..11085d5 100644 --- a/security-admin/src/main/webapp/scripts/modules/XAOverrides.js +++ b/security-admin/src/main/webapp/scripts/modules/XAOverrides.js @@ -762,7 +762,11 @@ onChange: function () { var checked = this.checkbox().prop("checked"); this.$el.parent().toggleClass("selected", checked); - this.model.set('isVisible', checked) + if(checked){ + this.model.select(); + }else{ + this.model.deselect(); + } this.model.trigger("backgrid:selected", this.model, checked); }, @@ -936,4 +940,206 @@ } }; + Backbone.Picky = (function(Backbone, _) { + var Picky = {}; + + // Picky.SingleSelect + // ------------------ + // A single-select mixin for Backbone.Collection, allowing a single + // model to be selected within a collection. Selection of another + // model within the collection causes the previous model to be + // deselected. + + Picky.SingleSelect = function(collection) { + this.collection = collection; + }; + + _.extend(Picky.SingleSelect.prototype, { + + // Select a model, deselecting any previously + // selected model + select: function(model) { + if (model && this.selected === model) { + return; + } + + this.deselect(); + + this.selected = model; + this.selected.select(); + this.trigger("select:one", model); + }, + + // Deselect a model, resulting in no model + // being selected + deselect: function(model) { + if (!this.selected) { + return; + } + + model = model || this.selected; + if (this.selected !== model) { + return; + } + + this.selected.deselect(); + this.trigger("deselect:one", this.selected); + delete this.selected; + } + + }); + + // Picky.MultiSelect + // ----------------- + // A mult-select mixin for Backbone.Collection, allowing a collection to + // have multiple items selected, including `selectAll` and `selectNone` + // capabilities. + + Picky.MultiSelect = function(collection) { + this.collection = collection; + this.selected = {}; + }; + + _.extend(Picky.MultiSelect.prototype, { + + // Select a specified model, make sure the + // model knows it's selected, and hold on to + // the selected model. + select: function(model) { + if (this.selected[model.id]) { + return; + } + + this.selected[model.id] = model; + model.select(); + calculateSelectedLength(this); + }, + + // Deselect a specified model, make sure the + // model knows it has been deselected, and remove + // the model from the selected list. + deselect: function(model) { + if (!this.selected[model.id]) { + return; + } + + delete this.selected[model.id]; + model.deselect(); + calculateSelectedLength(this); + }, + + // Select all models in this collection + selectAll: function() { + this.each(function(model) { + model.select(); + }); + calculateSelectedLength(this); + }, + + // Deselect all models in this collection + selectNone: function() { + if (this.selectedLength === 0) { + return; + } + this.each(function(model) { + model.deselect(); + }); + calculateSelectedLength(this); + }, + + // Toggle select all / none. If some are selected, it + // will select all. If all are selected, it will select + // none. If none are selected, it will select all. + toggleSelectAll: function() { + if (this.selectedLength === this.length) { + this.selectNone(); + } else { + this.selectAll(); + } + } + }); + + // Picky.Selectable + // ---------------- + // A selectable mixin for Backbone.Model, allowing a model to be selected, + // enabling it to work with Picky.MultiSelect or on it's own + + Picky.Selectable = function(model) { + this.model = model; + }; + + _.extend(Picky.Selectable.prototype, { + + // Select this model, and tell our + // collection that we're selected + select: function() { + if (this.selected) { + return; + } + + this.selected = true; + this.trigger("selected", this); + + if (this.collection) { + this.collection.select(this); + } + }, + + // Deselect this model, and tell our + // collection that we're deselected + deselect: function() { + if (!this.selected) { + return; + } + + this.selected = false; + this.trigger("deselected", this); + + if (this.collection) { + this.collection.deselect(this); + } + }, + + // Change selected to the opposite of what + // it currently is + toggleSelected: function() { + if (this.selected) { + this.deselect(); + } else { + this.select(); + } + } + }); + + // Helper Methods + // -------------- + + // Calculate the number of selected items in a collection + // and update the collection with that length. Trigger events + // from the collection based on the number of selected items. + var calculateSelectedLength = function(collection) { + collection.selectedLength = _.size(collection.selected); + + var selectedLength = collection.selectedLength; + var length = collection.length; + + if (selectedLength === length) { + collection.trigger("select:all", collection); + return; + } + + if (selectedLength === 0) { + collection.trigger("select:none", collection); + return; + } + + if (selectedLength > 0 && selectedLength < length) { + collection.trigger("select:some", collection); + return; + } + }; + + return Picky; + })(Backbone, _); + }); http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/b1510533/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 9b5b5e8..843d70c 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 @@ -142,6 +142,7 @@ define(function(require) { accountStatus : 'Account Status', ActiveStatus_STATUS_ENABLED : 'Enabled', ActiveStatus_STATUS_DISABLED : 'Disabled', + visibility : 'Visibility', VisibilityStatus_IS_VISIBLE : 'Visible', VisibilityStatus_IS_HIDDEN : 'Hidden', commonNameForCertificate : 'Common Name For Certificate', http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/b1510533/security-admin/src/main/webapp/scripts/views/users/UserTableLayout.js ---------------------------------------------------------------------- diff --git a/security-admin/src/main/webapp/scripts/views/users/UserTableLayout.js b/security-admin/src/main/webapp/scripts/views/users/UserTableLayout.js index 161a09a..74ecdbe 100644 --- a/security-admin/src/main/webapp/scripts/views/users/UserTableLayout.js +++ b/security-admin/src/main/webapp/scripts/views/users/UserTableLayout.js @@ -57,7 +57,9 @@ define(function(require){ visualSearch: '.visual_search', btnShowMore : '[data-id="showMore"]', btnShowLess : '[data-id="showLess"]', - btnSave : '[data-id="save"]' + btnSave : '[data-id="save"]', + btnShowHide : '[data-action="showHide"]', + visibilityDropdown : '[data-id="visibilityDropdown"]' }, /** ui events hash */ @@ -67,6 +69,8 @@ define(function(require){ events['click ' + this.ui.btnShowMore] = 'onShowMore'; events['click ' + this.ui.btnShowLess] = 'onShowLess'; events['click ' + this.ui.btnSave] = 'onSave'; + events['click ' + this.ui.visibilityDropdown +' li a'] = 'onVisibilityChange'; + return events; }, @@ -93,16 +97,8 @@ define(function(require){ /** all events binding here */ bindEvents : function(){ var that = this; - this.listenTo(this.collection,"backgrid:selected", this.onModelChange); - this.listenTo(this.groupList,"backgrid:selected", this.onModelChange); /*this.listenTo(this.model, "change:foo", this.modelChanged, this);*/ /*this.listenTo(communicator.vent,'someView:someEvent', this.someEventHandler, this)'*/ - this.collection.on('change', function(){ - that.preventNavigation(); - }); - this.groupList.on('change', function(){ - that.preventNavigation(); - }); }, /** on render callback */ @@ -114,33 +110,6 @@ define(function(require){ this.renderUserTab(); this.addVisualSearch(); }, - - preventNavigation : function(){ - XAUtil.preventNavigation(localization.tt('dialogMsg.preventNavUserList'),this.rTableList.$el); - this.rTableList.$el.find('table').addClass('dirtyField'); - //this.ui.btnSave.removeClass('disabled'); - }, - - allowNavigation : function(){ - XAUtil.allowNavigation(); - this.rTableList.$el.find('table').removeClass('dirtyField'); - //this.ui.btnSave.addClass('disabled'); - }, - - onModelChange : function(model, selected){ - /*var that = this; - if(! (model.id in that.chgFlags)){ - that.chgFlags[model.id] = true; - } else { - that.chgFlags[model.id] ^= true; - } - - if (_.some(that.chgFlags)){ - that.preventNavigation(); - } else { - that.allowNavigation(); - }*/ - }, onTabChange : function(e){ var that = this; this.chgFlags = []; @@ -155,14 +124,18 @@ define(function(require){ } $(this.rUserDetail.el).hide(); }, - onSave : function(e){ + onVisibilityChange : function(e){ var that = this; + var status = $(e.currentTarget).attr('data-id') == 'visible' ? true : false; var updateReq = {}; var collection = this.showUsers ? this.collection : this.groupList; - collection.changed_models().each(function(m){ - m.toServer(); - updateReq[m.get('id')] = m.get('isVisible'); + _.each(collection.selected, function(m){ + if( m.get('isVisible') != status ){ + m.set('isVisible', status); + m.toServer(); + updateReq[m.get('id')] = m.get('isVisible'); + } }); var clearCache = function(coll){ @@ -178,7 +151,6 @@ define(function(require){ collection.setUsersVisibility(updateReq, { success : function(){ that.chgFlags = []; - that.allowNavigation(); clearCache(collection); } }); @@ -186,18 +158,17 @@ define(function(require){ collection.setGroupsVisibility(updateReq, { success : function(){ that.chgFlags = []; - that.allowNavigation(); clearCache(collection); } }); } - }, renderUserTab : function(){ var that = this; if(_.isUndefined(this.collection)){ this.collection = new VXUserList(); } + this.collection.selectNone(); this.renderUserListTable(); this.collection.fetch({ cache:true @@ -214,6 +185,7 @@ define(function(require){ if(_.isUndefined(this.groupList)){ this.groupList = new VXGroupList(); } + this.groupList.selectNone(); this.renderGroupListTable(); this.groupList.fetch({ cache:true @@ -228,61 +200,13 @@ define(function(require){ renderUserListTable : function(){ var that = this; var tableRow = Backgrid.Row.extend({ - render: function () { tableRow.__super__.render.apply(this, arguments); - if (this.model.get("isVisible") == 0) { - this.el.classList.add("tr-inactive"); + if(!this.model.get('isVisible')){ + this.$el.addClass('tr-inactive'); } return this; - }, - events: { - 'change' : 'onClickCheckbox' - }, - onClickCheckbox: function(e) { - if (this.model.get("isVisible") == 0) { - this.el.classList.add("tr-inactive"); - } else { - this.el.classList.remove("tr-inactive"); - }; }, - /* - initialize : function(){ - var that = this; - var args = Array.prototype.slice.apply(arguments); - Backgrid.Row.prototype.initialize.apply(this, args); - this.listenTo(this.model, 'model:highlightBackgridRow', function(){ - that.$el.addClass("alert"); - $("html, body").animate({scrollTop: that.$el.position().top},"linear"); - setTimeout(function () { - that.$el.removeClass("alert"); - }, 1500); - }, this); - }, - onClick: function (e) { - if($(e.target).is('a')) - return; - this.$el.parent('tbody').find('tr').removeClass('tr-active'); - this.$el.toggleClass('tr-active'); - var groupList = new VXGroupList(); - var userModel = this.model; - groupList.getGroupsForUser(this.model.id,{ - cache : false, - success :function(msResponse){ - var groupColl = msResponse.vXGroups ? msResponse.vXGroups : null; - if(that.rUserDetail){ - $(that.rUserDetail.el).hide(); - $(that.rUserDetail.el).html(new vUserInfo({ - model : userModel, - groupList : new VXGroupList(groupColl) - }).render().$el).slideDown(); - } - }, - error : function(){ - console.log('error..'); - } - }); - } */ }); this.rTableList.show(new XATableLayout({ columns: this.getColumns(), @@ -300,7 +224,7 @@ define(function(require){ getColumns : function(){ var cols = { - isVisible : { + select : { label : localization.tt("lbl.isVisible"), //cell : Backgrid.SelectCell.extend({className: 'cellWidth-1'}), cell: "select-row", @@ -374,41 +298,26 @@ define(function(require){ return '--'; } }), - /*cell : Backgrid.HtmlCell.extend({className: 'cellWidth-1'}), - label : localization.tt("lbl.groups"), - formatter: _.extend({}, Backgrid.CellFormatter.prototype, { - fromRaw: function (rawValue) { - var html = ''; - if(!_.isUndefined(rawValue)){ - _.each(rawValue,function(name){ - html += '<span class="label label-info">'+name+'</span>'; - }); - return html; - }else - return '--'; - } - }),*/ editable : false, sortable : false }, - /* status : { - label : localization.tt("lbl.status"), + isVisible : { + label : localization.tt("lbl.visibility"), cell : Backgrid.HtmlCell.extend({className: 'cellWidth-1'}), formatter: _.extend({}, Backgrid.CellFormatter.prototype, { fromRaw: function (rawValue, model) { - var status = model.has('status') ? 'Active' : 'Deactive'; - if(model.has('status')) - return '<span class="label label-success">' +status + '</span>'; - else - return '<span class="label label-important">' +status + '</span>'; - // return model.has('status') ? 'On' : 'Off'; + if(!_.isUndefined(rawValue)){ + if(rawValue) + return '<span class="label label-success">'+XAEnums.VisibilityStatus.STATUS_VISIBLE.label+'</span>'; + else + return '<span class="label label-green">'+XAEnums.VisibilityStatus.STATUS_HIDDEN.label+'</span>'; + }else + return '--'; } }), - click : false, - drag : false, editable:false, - sortable:false, - },*/ + sortable:false + }, }; return this.collection.constructor.getTableCols(cols, this.collection); @@ -416,55 +325,22 @@ define(function(require){ renderGroupListTable : function(){ var that = this; - /*var tableRow = Backgrid.Row.extend({ - events: { - 'click' : 'onClick' - }, - initialize : function(){ - var that = this; - var args = Array.prototype.slice.apply(arguments); - Backgrid.Row.prototype.initialize.apply(this, args); - this.listenTo(this.model, 'model:highlightBackgridRow1', function(){ - that.$el.addClass("alert"); - $("html, body").animate({scrollTop: that.$el.position().top},"linear"); - setTimeout(function () { - that.$el.removeClass("alert"); - }, 1500); - }, this); + + var tableRow = Backgrid.Row.extend({ + render: function () { + tableRow.__super__.render.apply(this, arguments); + if(!this.model.get('isVisible')){ + this.$el.addClass('tr-inactive'); + } + return this; }, - onClick: function (e) { - if($(e.target).is('a')) - return; - this.$el.parent('tbody').find('tr').removeClass('tr-active'); - this.$el.toggleClass('tr-active'); - var userList = new VXUserList(); - var gid = this.model.id; - var groupModel = new VXGroup({id:gid}); - groupModel.fetch().done(function(msResponse){ - userList.getUsersOfGroup(gid,{ - success :function(msResponse){ - var userColl = msResponse.vXUsers ? msResponse.vXUsers : null; - if(that.rUserDetail){ - $(that.rUserDetail.el).hide(); - $(that.rUserDetail.el).html(new vUserInfo({ - model : groupModel, - userList: new VXUserList(userColl) - }).render().$el).slideDown(); - } - }, - error : function(){ - console.log('error..'); - } - }); - }); - } - });*/ + }); this.rTableList.show(new XATableLayout({ columns: this.getGroupColumns(), collection: this.groupList, includeFilter : false, gridOpts : { -// row: tableRow, + row: tableRow, header : XABackgrid, emptyText : 'No Groups found!' } @@ -475,7 +351,7 @@ define(function(require){ getGroupColumns : function(){ var cols = { - isVisible : { + select : { label : localization.tt("lbl.isVisible"), //cell : Backgrid.SelectCell.extend({className: 'cellWidth-1'}), cell: "select-row", @@ -514,23 +390,23 @@ define(function(require){ editable:false, sortable:false, }, - /*status : { - label : localization.tt("lbl.status"), + isVisible : { + label : localization.tt("lbl.visibility"), cell : Backgrid.HtmlCell.extend({className: 'cellWidth-1'}), formatter: _.extend({}, Backgrid.CellFormatter.prototype, { fromRaw: function (rawValue, model) { - var status = model.has('status') ? 'Active' : 'Deactive'; - if(model.has('status')) - return '<span class="label label-success">' +status + '</span>'; - else - return '<span class="label label-important">' +status + '</span>'; + if(!_.isUndefined(rawValue)){ + if(rawValue) + return '<span class="label label-success">'+XAEnums.VisibilityStatus.STATUS_VISIBLE.label+'</span>'; + else + return '<span class="label label-green">'+XAEnums.VisibilityStatus.STATUS_HIDDEN.label+'</span>'; + }else + return '--'; } }), - click : false, - drag : false, editable:false, - sortable:false, - }, */ + sortable:false + } }; return this.groupList.constructor.getTableCols(cols, this.groupList); }, @@ -548,8 +424,6 @@ define(function(require){ {text : "Visibility", label :"isVisible", 'multiple' : true, 'optionsArr' : XAUtil.enumToSelectLabelValuePairs(XAEnums.VisibilityStatus)}, {text : "User Source", label :"userSource", 'multiple' : true, 'optionsArr' : XAUtil.enumToSelectLabelValuePairs(XAEnums.UserTypes)}, ]; - // {text : 'Start Date',label :'startDate'},{text : 'End Date',label :'endDate'}, - // {text : 'Today',label :'today'}]; }else{ placeholder = localization.tt('h.searchForYourGroup'); coll = this.groupList; @@ -557,9 +431,6 @@ define(function(require){ serverAttrName = [{text : "Group Name", label :"name"}, {text : "Visibility", label :"isVisible", 'multiple' : true, 'optionsArr' : XAUtil.enumToSelectLabelValuePairs(XAEnums.VisibilityStatus)}, {text : "Group Source", label :"groupSource", 'multiple' : true, 'optionsArr' : XAUtil.enumToSelectLabelValuePairs(XAEnums.GroupTypes)},]; - - //{text : 'Start Date',label :'startDate'},{text : 'End Date',label :'endDate'}, - //{text : 'Today',label :'today'}]; } var query = (!_.isUndefined(coll.VSQuery)) ? coll.VSQuery : ''; http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/b1510533/security-admin/src/main/webapp/templates/users/UserTableLayout_tmpl.html ---------------------------------------------------------------------- diff --git a/security-admin/src/main/webapp/templates/users/UserTableLayout_tmpl.html b/security-admin/src/main/webapp/templates/users/UserTableLayout_tmpl.html index 21006b1..3dbefd4 100644 --- a/security-admin/src/main/webapp/templates/users/UserTableLayout_tmpl.html +++ b/security-admin/src/main/webapp/templates/users/UserTableLayout_tmpl.html @@ -14,14 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. --}} -<!-- <ul class="nav nav-tabs"> - <li class="active" data-js="users"> - <a data-toggle="tab" href="#users">Users</a> - </li> - <li data="groups" data-js="groups"> - <a data-toggle="tab" href="#groups">Groups</a> - </li> -</ul> --> <ul class="nav nav-tabs tabs clearfix"> <li data="groups" data-js="groups"> <a data-toggle="tab" href="#groups">Groups</a> @@ -31,13 +23,8 @@ </li> </ul> - - - <h3 class="wrap-header bold"> {{tt 'lbl.userListing'}} </h3> - <div class="wrap non-collapsible m-height "> - <div> <div class="span9"> <div class="visual_search"></div> @@ -45,14 +32,19 @@ <div class="clearfix"> <a href="#!/user/create" class="btn btn-primary btn-right" type="button" data-id="addNewUser"> {{tt 'lbl.addNewUser'}} </a> <a href="#!/group/create" class="btn btn-primary btn-right" type="button" data-id="addNewGroup" style="display:none;"> {{tt 'lbl.addNewGroup'}} </a> - <a href="javascript:void(0)" class="_allowNav btn btn-primary btn-right" type="button" data-id="save" style="display:block;" > {{tt 'btn.setVisibility'}} </a> + <div class="btn-group btn-right"> + <a class="btn btn-primary dropdown-toggle" data-toggle="dropdown" href="#"> + {{tt 'btn.setVisibility'}} + <span class="caret"></span> + </a> + <ul class="dropdown-menu" data-id="visibilityDropdown"> + <li><a href="javascript:void(0);" data-id="visible">{{tt 'lbl.VisibilityStatus_IS_VISIBLE'}}</a></li> + <li><a href="javascript:void(0);" data-id="hidden">{{tt 'lbl.VisibilityStatus_IS_HIDDEN'}}</a></li> + </ul> + </div> </div> <div data-id="r_tableList" class="clickable"> <b class="_prevNav"></b> </div> </div> - - <!-- <div class="span3"> - <div id="userDetail"></div> - </div> --> </div>
