From 4945ae17d3692f089ce6c996f6585a5e5b308e4d Mon Sep 17 00:00:00 2001
From: Baker Linden <baker@lindenlab.com>
Date: Thu, 29 Aug 2013 11:36:45 -0700
Subject: [PATCH] Initial commit for GroupBan - Lots of crap isn't working as
 intended yet.

---
 indra/llcommon/roles_constants.h              | 100 +--
 indra/llcommon/stdenums.h                     |   1 +
 indra/newview/CMakeLists.txt                  |   7 +
 indra/newview/llfloatergroupbulkban.cpp       | 134 ++++
 indra/newview/llfloatergroupbulkban.h         |  48 ++
 indra/newview/llgroupmgr.cpp                  | 172 ++++-
 indra/newview/llgroupmgr.h                    |  83 ++-
 indra/newview/llpanelgroupbulk.cpp            | 403 +++++++++++
 indra/newview/llpanelgroupbulk.h              |  74 ++
 indra/newview/llpanelgroupbulkban.cpp         | 156 ++++
 indra/newview/llpanelgroupbulkban.h           |  47 ++
 indra/newview/llpanelgroupbulkimpl.h          |  87 +++
 indra/newview/llpanelgroupinvite.cpp          | 673 ++++--------------
 indra/newview/llpanelgroupinvite.h            |  29 +-
 indra/newview/llpanelgrouproles.cpp           | 512 +++++++++++--
 indra/newview/llpanelgrouproles.h             |  57 ++
 indra/newview/llviewerregion.cpp              |   1 +
 .../default/xui/en/panel_group_bulk_ban.xml   |  78 ++
 .../default/xui/en/panel_group_invite.xml     |   2 +-
 .../default/xui/en/panel_group_roles.xml      |  73 ++
 .../skins/default/xui/en/role_actions.xml     |   7 +
 .../newview/skins/default/xui/en/strings.xml  |   1 +
 22 files changed, 2072 insertions(+), 673 deletions(-)
 create mode 100644 indra/newview/llfloatergroupbulkban.cpp
 create mode 100644 indra/newview/llfloatergroupbulkban.h
 create mode 100644 indra/newview/llpanelgroupbulk.cpp
 create mode 100644 indra/newview/llpanelgroupbulk.h
 create mode 100644 indra/newview/llpanelgroupbulkban.cpp
 create mode 100644 indra/newview/llpanelgroupbulkban.h
 create mode 100644 indra/newview/llpanelgroupbulkimpl.h
 create mode 100644 indra/newview/skins/default/xui/en/panel_group_bulk_ban.xml

diff --git a/indra/llcommon/roles_constants.h b/indra/llcommon/roles_constants.h
index effd15ea72e..ee3e8b6b740 100755
--- a/indra/llcommon/roles_constants.h
+++ b/indra/llcommon/roles_constants.h
@@ -53,7 +53,7 @@ enum LLRoleChangeType
 
 // KNOWN HOLES: use these for any single bit powers you need
 // bit 0x1 << 46
-// bit 0x1 << 49 and above
+// bit 0x1 << 50 and above
 
 // These powers were removed to make group roles simpler
 // bit 0x1 << 41 (GP_ACCOUNTING_VIEW)
@@ -63,88 +63,90 @@ const U64 GP_NO_POWERS = 0x0;
 const U64 GP_ALL_POWERS = 0xFFFFFFFFFFFFFFFFLL;
 
 // Membership
-const U64 GP_MEMBER_INVITE		= 0x1 << 1;		// Invite member
-const U64 GP_MEMBER_EJECT			= 0x1 << 2;		// Eject member from group
-const U64 GP_MEMBER_OPTIONS		= 0x1 << 3;		// Toggle "Open enrollment" and change "Signup Fee"
-const U64 GP_MEMBER_VISIBLE_IN_DIR = 0x1LL << 47;
+const U64 GP_MEMBER_INVITE				= 0x1LL << 1;	// Invite member
+const U64 GP_MEMBER_EJECT				= 0x1LL << 2;	// Eject member from group
+const U64 GP_MEMBER_OPTIONS				= 0x1LL << 3;	// Toggle "Open enrollment" and change "Signup Fee"
+const U64 GP_MEMBER_VISIBLE_IN_DIR		= 0x1LL << 47;
 
 // Roles
-const U64 GP_ROLE_CREATE			= 0x1 << 4;		// Create new roles
-const U64 GP_ROLE_DELETE			= 0x1 << 5;		// Delete roles
-const U64 GP_ROLE_PROPERTIES		= 0x1 << 6;		// Change Role Names, Titles, and Descriptions (Of roles the user is in, only, or any role in group?)
-const U64 GP_ROLE_ASSIGN_MEMBER_LIMITED = 0x1 << 7; // Assign Member to a Role that the assigner is in
-const U64 GP_ROLE_ASSIGN_MEMBER	= 0x1 << 8;		// Assign Member to Role
-const U64 GP_ROLE_REMOVE_MEMBER	= 0x1 << 9;		// Remove Member from Role
-const U64 GP_ROLE_CHANGE_ACTIONS	= 0x1 << 10;	// Change actions a role can perform
+const U64 GP_ROLE_CREATE				= 0x1LL << 4;	// Create new roles
+const U64 GP_ROLE_DELETE				= 0x1LL << 5;	// Delete roles
+const U64 GP_ROLE_PROPERTIES			= 0x1LL << 6;	// Change Role Names, Titles, and Descriptions (Of roles the user is in, only, or any role in group?)
+const U64 GP_ROLE_ASSIGN_MEMBER_LIMITED	= 0x1LL << 7;	// Assign Member to a Role that the assigner is in
+const U64 GP_ROLE_ASSIGN_MEMBER			= 0x1LL << 8;	// Assign Member to Role
+const U64 GP_ROLE_REMOVE_MEMBER			= 0x1LL << 9;	// Remove Member from Role
+const U64 GP_ROLE_CHANGE_ACTIONS		= 0x1LL << 10;	// Change actions a role can perform
 
 // Group Identity
-const U64 GP_GROUP_CHANGE_IDENTITY = 0x1 << 11;	// Charter, insignia, 'Show In Group List', 'Publish on the web', 'Mature', all 'Show Member In Group Profile' checkboxes
+const U64 GP_GROUP_CHANGE_IDENTITY		= 0x1LL << 11;	// Charter, insignia, 'Show In Group List', 'Publish on the web', 'Mature', all 'Show Member In Group Profile' checkboxes
 
 // Parcel Management
-const U64 GP_LAND_DEED			= 0x1 << 12;	// Deed Land and Buy Land for Group
-const U64 GP_LAND_RELEASE			= 0x1 << 13;	// Release Land (to Gov. Linden)
-const U64 GP_LAND_SET_SALE_INFO	= 0x1 << 14;	// Set for sale info (Toggle "For Sale", Set Price, Set Target, Toggle "Sell objects with the land")
-const U64 GP_LAND_DIVIDE_JOIN		= 0x1 << 15;	// Divide and Join Parcels
+const U64 GP_LAND_DEED					= 0x1LL << 12;	// Deed Land and Buy Land for Group
+const U64 GP_LAND_RELEASE				= 0x1LL << 13;	// Release Land (to Gov. Linden)
+const U64 GP_LAND_SET_SALE_INFO			= 0x1LL << 14;	// Set for sale info (Toggle "For Sale", Set Price, Set Target, Toggle "Sell objects with the land")
+const U64 GP_LAND_DIVIDE_JOIN			= 0x1LL << 15;	// Divide and Join Parcels
 
 // Parcel Identity
-const U64 GP_LAND_FIND_PLACES		= 0x1 << 17;	// Toggle "Show in Find Places" and Set Category.
-const U64 GP_LAND_CHANGE_IDENTITY = 0x1 << 18;	// Change Parcel Identity: Parcel Name, Parcel Description, Snapshot, 'Publish on the web', and 'Mature' checkbox
-const U64 GP_LAND_SET_LANDING_POINT = 0x1 << 19;	// Set Landing Point
+const U64 GP_LAND_FIND_PLACES			= 0x1LL << 17;	// Toggle "Show in Find Places" and Set Category.
+const U64 GP_LAND_CHANGE_IDENTITY		= 0x1LL << 18;	// Change Parcel Identity: Parcel Name, Parcel Description, Snapshot, 'Publish on the web', and 'Mature' checkbox
+const U64 GP_LAND_SET_LANDING_POINT		= 0x1LL << 19;	// Set Landing Point
 
 // Parcel Settings
-const U64 GP_LAND_CHANGE_MEDIA	= 0x1 << 20;	// Change Media Settings
-const U64 GP_LAND_EDIT			= 0x1 << 21;	// Toggle Edit Land
-const U64 GP_LAND_OPTIONS			= 0x1 << 22;	// Toggle Set Home Point, Fly, Outside Scripts, Create/Edit Objects, Landmark, and Damage checkboxes
+const U64 GP_LAND_CHANGE_MEDIA			= 0x1LL << 20;	// Change Media Settings
+const U64 GP_LAND_EDIT					= 0x1LL << 21;	// Toggle Edit Land
+const U64 GP_LAND_OPTIONS				= 0x1LL << 22;	// Toggle Set Home Point, Fly, Outside Scripts, Create/Edit Objects, Landmark, and Damage checkboxes
 
 // Parcel Powers
-const U64 GP_LAND_ALLOW_EDIT_LAND = 0x1 << 23;	// Bypass Edit Land Restriction
-const U64 GP_LAND_ALLOW_FLY		= 0x1 << 24;	// Bypass Fly Restriction
-const U64 GP_LAND_ALLOW_CREATE	= 0x1 << 25;	// Bypass Create/Edit Objects Restriction
-const U64 GP_LAND_ALLOW_LANDMARK	= 0x1 << 26;	// Bypass Landmark Restriction
-const U64 GP_LAND_ALLOW_SET_HOME	= 0x1 << 28;	// Bypass Set Home Point Restriction
-const U64 GP_LAND_ALLOW_HOLD_EVENT	= 0x1LL << 41;	// Allowed to hold events on group-owned land
-
+const U64 GP_LAND_ALLOW_EDIT_LAND		= 0x1LL << 23;	// Bypass Edit Land Restriction
+const U64 GP_LAND_ALLOW_FLY				= 0x1LL << 24;	// Bypass Fly Restriction
+const U64 GP_LAND_ALLOW_CREATE			= 0x1LL << 25;	// Bypass Create/Edit Objects Restriction
+const U64 GP_LAND_ALLOW_LANDMARK		= 0x1LL << 26;	// Bypass Landmark Restriction
+const U64 GP_LAND_ALLOW_SET_HOME		= 0x1LL << 28;	// Bypass Set Home Point Restriction
+const U64 GP_LAND_ALLOW_HOLD_EVENT		= 0x1LL << 41;	// Allowed to hold events on group-owned land
 
 // Parcel Access
-const U64 GP_LAND_MANAGE_ALLOWED	= 0x1 << 29;	// Manage Allowed List
-const U64 GP_LAND_MANAGE_BANNED	= 0x1 << 30;	// Manage Banned List
-const U64 GP_LAND_MANAGE_PASSES	= 0x1LL << 31;	// Change Sell Pass Settings
-const U64 GP_LAND_ADMIN			= 0x1LL << 32;	// Eject and Freeze Users on the land
+const U64 GP_LAND_MANAGE_ALLOWED		= 0x1LL << 29;	// Manage Allowed List
+const U64 GP_LAND_MANAGE_BANNED			= 0x1LL << 30;	// Manage Banned List
+const U64 GP_LAND_MANAGE_PASSES			= 0x1LL << 31;	// Change Sell Pass Settings
+const U64 GP_LAND_ADMIN					= 0x1LL << 32;	// Eject and Freeze Users on the land
 
 // Parcel Content
-const U64 GP_LAND_RETURN_GROUP_SET	= 0x1LL << 33;	// Return objects on parcel that are set to group
-const U64 GP_LAND_RETURN_NON_GROUP	= 0x1LL << 34;	// Return objects on parcel that are not set to group
-const U64 GP_LAND_RETURN_GROUP_OWNED= 0x1LL << 48;	// Return objects on parcel that are owned by the group
+const U64 GP_LAND_RETURN_GROUP_SET		= 0x1LL << 33;	// Return objects on parcel that are set to group
+const U64 GP_LAND_RETURN_NON_GROUP		= 0x1LL << 34;	// Return objects on parcel that are not set to group
+const U64 GP_LAND_RETURN_GROUP_OWNED	= 0x1LL << 48;	// Return objects on parcel that are owned by the group
 
 // Select a power-bit based on an object's relationship to a parcel.
 const U64 GP_LAND_RETURN		= GP_LAND_RETURN_GROUP_OWNED 
 								| GP_LAND_RETURN_GROUP_SET	
 								| GP_LAND_RETURN_NON_GROUP;
 
-const U64 GP_LAND_GARDENING		= 0x1LL << 35;	// Parcel Gardening - plant and move linden trees
+const U64 GP_LAND_GARDENING				= 0x1LL << 35;	// Parcel Gardening - plant and move linden trees
 
 // Object Management
-const U64 GP_OBJECT_DEED			= 0x1LL << 36;	// Deed Object
-const U64 GP_OBJECT_MANIPULATE		= 0x1LL << 38;	// Manipulate Group Owned Objects (Move, Copy, Mod)
-const U64 GP_OBJECT_SET_SALE		= 0x1LL << 39;	// Set Group Owned Object for Sale
+const U64 GP_OBJECT_DEED				= 0x1LL << 36;	// Deed Object
+const U64 GP_OBJECT_MANIPULATE			= 0x1LL << 38;	// Manipulate Group Owned Objects (Move, Copy, Mod)
+const U64 GP_OBJECT_SET_SALE			= 0x1LL << 39;	// Set Group Owned Object for Sale
 
 // Accounting
-const U64 GP_ACCOUNTING_ACCOUNTABLE = 0x1LL << 40;	// Pay Group Liabilities and Receive Group Dividends
+const U64 GP_ACCOUNTING_ACCOUNTABLE		= 0x1LL << 40;	// Pay Group Liabilities and Receive Group Dividends
 
 // Notices
-const U64 GP_NOTICES_SEND			= 0x1LL << 42;	// Send Notices
-const U64 GP_NOTICES_RECEIVE		= 0x1LL << 43;	// Receive Notices and View Notice History
+const U64 GP_NOTICES_SEND				= 0x1LL << 42;	// Send Notices
+const U64 GP_NOTICES_RECEIVE			= 0x1LL << 43;	// Receive Notices and View Notice History
 
 // Proposals
 // TODO: _DEPRECATED suffix as part of vote removal - DEV-24856:
-const U64 GP_PROPOSAL_START		= 0x1LL << 44;	// Start Proposal
+const U64 GP_PROPOSAL_START				= 0x1LL << 44;	// Start Proposal
 // TODO: _DEPRECATED suffix as part of vote removal - DEV-24856:
-const U64 GP_PROPOSAL_VOTE		= 0x1LL << 45;	// Vote on Proposal
+const U64 GP_PROPOSAL_VOTE				= 0x1LL << 45;	// Vote on Proposal
 
 // Group chat moderation related
-const U64 GP_SESSION_JOIN = 0x1LL << 16; //can join session
-const U64 GP_SESSION_VOICE = 0x1LL << 27; //can hear/talk
-const U64 GP_SESSION_MODERATOR = 0x1LL << 37; //can mute people's session
+const U64 GP_SESSION_JOIN				= 0x1LL << 16;	//can join session
+const U64 GP_SESSION_VOICE				= 0x1LL << 27;	//can hear/talk
+const U64 GP_SESSION_MODERATOR			= 0x1LL << 37;	//can mute people's session
+
+// Group Banning
+const U64 GP_GROUP_BAN_ACCESS			= 0x1LL << 49;	// Allows access to ban / un-ban agents from a group.
 
 const U64 GP_DEFAULT_MEMBER = GP_ACCOUNTING_ACCOUNTABLE
 								| GP_LAND_ALLOW_SET_HOME
diff --git a/indra/llcommon/stdenums.h b/indra/llcommon/stdenums.h
index efcbe767958..b505ef0b4b2 100755
--- a/indra/llcommon/stdenums.h
+++ b/indra/llcommon/stdenums.h
@@ -127,6 +127,7 @@ enum LLGroupChange
 	GC_ROLE_DATA,
 	GC_ROLE_MEMBER_DATA,
 	GC_TITLES,
+	GC_BANLIST,
 	GC_ALL
 };
 
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index d06cee5ee63..6a9f91ffcd5 100755
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -229,6 +229,7 @@ set(viewer_SOURCE_FILES
     llfloaterfonttest.cpp
     llfloatergesture.cpp
     llfloatergodtools.cpp
+    llfloatergroupbulkban.cpp
     llfloatergroupinvite.cpp
     llfloatergroups.cpp
     llfloaterhandler.cpp
@@ -398,6 +399,8 @@ set(viewer_SOURCE_FILES
     llpanelface.cpp
     llpanelgenerictip.cpp
     llpanelgroup.cpp
+    llpanelgroupbulk.cpp
+    llpanelgroupbulkban.cpp
     llpanelgroupgeneral.cpp
     llpanelgroupinvite.cpp
     llpanelgrouplandmoney.cpp
@@ -812,6 +815,7 @@ set(viewer_HEADER_FILES
     llfloaterfonttest.h
     llfloatergesture.h
     llfloatergodtools.h
+    llfloatergroupbulkban.h
     llfloatergroupinvite.h
     llfloatergroups.h
     llfloaterhandler.h
@@ -974,6 +978,9 @@ set(viewer_HEADER_FILES
     llpanelface.h
     llpanelgenerictip.h
     llpanelgroup.h
+    llpanelgroupbulk.h
+    llpanelgroupbulkimpl.h
+    llpanelgroupbulkban.h
     llpanelgroupgeneral.h
     llpanelgroupinvite.h
     llpanelgrouplandmoney.h
diff --git a/indra/newview/llfloatergroupbulkban.cpp b/indra/newview/llfloatergroupbulkban.cpp
new file mode 100644
index 00000000000..54a2283b132
--- /dev/null
+++ b/indra/newview/llfloatergroupbulkban.cpp
@@ -0,0 +1,134 @@
+/** 
+* @file llfloatergroupbulkban.cpp
+* @brief Floater to ban Residents from a group.
+* 
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2013, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+* $/LicenseInfo$
+*/
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatergroupbulkban.h"
+#include "llpanelgroupbulkban.h"
+#include "lltrans.h"
+#include "lldraghandle.h"
+
+
+class LLFloaterGroupBulkBan::impl
+{
+public:
+	impl(const LLUUID& group_id) : mGroupID(group_id), mBulkBanPanelp(NULL) {}
+	~impl() {}
+
+	static void closeFloater(void* data);
+
+public:
+	LLUUID mGroupID;
+	LLPanelGroupBulkBan* mBulkBanPanelp;
+
+	static std::map<LLUUID, LLFloaterGroupBulkBan*> sInstances;
+};
+
+//
+// Globals
+//
+std::map<LLUUID, LLFloaterGroupBulkBan*> LLFloaterGroupBulkBan::impl::sInstances;
+
+void LLFloaterGroupBulkBan::impl::closeFloater(void* data)
+{
+	LLFloaterGroupBulkBan* floaterp = (LLFloaterGroupBulkBan*)data;
+	if(floaterp)
+		floaterp->closeFloater();
+}
+
+//-----------------------------------------------------------------------------
+// Implementation
+//-----------------------------------------------------------------------------
+LLFloaterGroupBulkBan::LLFloaterGroupBulkBan(const LLUUID& group_id/*=LLUUID::null*/)
+	: LLFloater(group_id)
+{
+	S32 floater_header_size = getHeaderHeight();
+	LLRect contents;
+
+	mImpl = new impl(group_id);
+	mImpl->mBulkBanPanelp = new LLPanelGroupBulkBan(group_id);
+
+	contents = mImpl->mBulkBanPanelp->getRect();
+	contents.mTop -= floater_header_size;
+
+	setTitle(mImpl->mBulkBanPanelp->getString("GroupBulkBan"));
+	mImpl->mBulkBanPanelp->setCloseCallback(impl::closeFloater, this);
+	mImpl->mBulkBanPanelp->setRect(contents);
+
+	addChild(mImpl->mBulkBanPanelp);
+}
+
+LLFloaterGroupBulkBan::~LLFloaterGroupBulkBan()
+{
+	if(mImpl->mGroupID.notNull())
+	{
+		impl::sInstances.erase(mImpl->mGroupID);
+	}
+
+	delete mImpl->mBulkBanPanelp;
+	delete mImpl;
+}
+
+void LLFloaterGroupBulkBan::showForGroup(const LLUUID& group_id, uuid_vec_t* agent_ids)
+{
+	const LLFloater::Params& floater_params = LLFloater::getDefaultParams();
+	S32 floater_header_size = floater_params.header_height;
+	LLRect contents;
+
+	// Make sure group_id isn't null
+	if (group_id.isNull())
+	{
+		llwarns << "LLFloaterGroupInvite::showForGroup with null group_id!" << llendl;
+		return;
+	}
+
+	// If we don't have a floater for this group, create one.
+	LLFloaterGroupBulkBan* fgb = get_if_there(impl::sInstances,
+		group_id,
+		(LLFloaterGroupBulkBan*)NULL);
+	if (!fgb)
+	{
+		fgb = new LLFloaterGroupBulkBan(group_id);
+		contents = fgb->mImpl->mBulkBanPanelp->getRect();
+		contents.mTop += floater_header_size;
+		fgb->setRect(contents);
+		fgb->getDragHandle()->setRect(contents);
+		fgb->getDragHandle()->setTitle(fgb->mImpl->mBulkBanPanelp->getString("GroupBulkBan"));
+
+		impl::sInstances[group_id] = fgb;
+
+		fgb->mImpl->mBulkBanPanelp->clear();
+	}
+
+	if (agent_ids != NULL)
+	{
+		fgb->mImpl->mBulkBanPanelp->addUsers(*agent_ids);
+	}
+
+	fgb->center();
+	fgb->openFloater();
+	fgb->mImpl->mBulkBanPanelp->update();
+}
diff --git a/indra/newview/llfloatergroupbulkban.h b/indra/newview/llfloatergroupbulkban.h
new file mode 100644
index 00000000000..5b680a1ba49
--- /dev/null
+++ b/indra/newview/llfloatergroupbulkban.h
@@ -0,0 +1,48 @@
+/** 
+* @file   llfloatergroupbulkban.h
+* @brief  This floater is a wrapper for LLPanelGroupBulkBan, which
+* is used to ban Residents from a specific group.
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2013, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+* $/LicenseInfo$
+*/
+
+#ifndef LL_LLFLOATERGROUPBULKBAN_H
+#define LL_LLFLOATERGROUPBULKBAN_H
+
+#include "llfloater.h"
+#include "lluuid.h"
+
+class LLFloaterGroupBulkBan : public LLFloater
+{
+public:
+	virtual ~LLFloaterGroupBulkBan();
+
+	static void showForGroup(const LLUUID& group_id, uuid_vec_t* agent_ids = NULL);
+
+protected:
+	LLFloaterGroupBulkBan(const LLUUID& group_id = LLUUID::null);
+
+	class impl;
+	impl* mImpl;
+};
+
+#endif // LL_LLFLOATERGROUPBULKBAN_H
diff --git a/indra/newview/llgroupmgr.cpp b/indra/newview/llgroupmgr.cpp
index cbd844cdac0..0d4b678019e 100755
--- a/indra/newview/llgroupmgr.cpp
+++ b/indra/newview/llgroupmgr.cpp
@@ -231,11 +231,13 @@ LLGroupMgrGroupData::LLGroupMgrGroupData(const LLUUID& id) :
 	mMemberCount(0),
 	mRoleCount(0),
 	mReceivedRoleMemberPairs(0),
-	mMemberDataComplete(FALSE),
-	mRoleDataComplete(FALSE),
-	mRoleMemberDataComplete(FALSE),
-	mGroupPropertiesDataComplete(FALSE),
-	mPendingRoleMemberRequest(FALSE),
+	mMemberDataComplete(false),
+	mRoleDataComplete(false),
+	mRoleMemberDataComplete(false),
+	mGroupPropertiesDataComplete(false),
+	mGroupBanStatus(STATUS_INIT),
+	mGroupBanDataComplete(false),
+	mPendingRoleMemberRequest(false),
 	mAccessTime(0.0f)
 {
 	mMemberVersion.generate();
@@ -424,7 +426,7 @@ void LLGroupMgrGroupData::removeMemberData()
 		delete mi->second;
 	}
 	mMembers.clear();
-	mMemberDataComplete = FALSE;
+	mMemberDataComplete = false;
 	mMemberVersion.generate();
 }
 
@@ -446,8 +448,8 @@ void LLGroupMgrGroupData::removeRoleData()
 	}
 	mRoles.clear();
 	mReceivedRoleMemberPairs = 0;
-	mRoleDataComplete = FALSE;
-	mRoleMemberDataComplete = FALSE;
+	mRoleDataComplete = false;
+	mRoleMemberDataComplete= false;
 }
 
 void LLGroupMgrGroupData::removeRoleMemberData()
@@ -471,7 +473,7 @@ void LLGroupMgrGroupData::removeRoleMemberData()
 	}
 
 	mReceivedRoleMemberPairs = 0;
-	mRoleMemberDataComplete = FALSE;
+	mRoleMemberDataComplete= false;
 }
 
 LLGroupMgrGroupData::~LLGroupMgrGroupData()
@@ -742,6 +744,22 @@ void LLGroupMgrGroupData::cancelRoleChanges()
 	// Clear out all changes!
 	mRoleChanges.clear();
 }
+
+void LLGroupMgrGroupData::createBanEntry(const LLUUID& ban_id, const LLGroupBanData& ban_data)
+{ 
+	mBanList[ban_id] = ban_data;
+}
+
+bool LLGroupMgrGroupData::removeBanEntry(const LLUUID& ban_id)
+{
+	// Once we get this hooked up to the backend, we want to confirm the create or delete worked.
+	mBanList.erase(ban_id);
+	return true;
+}
+
+
+
+
 //
 // LLGroupMgr
 //
@@ -951,12 +969,12 @@ void LLGroupMgr::processGroupMembersReply(LLMessageSystem* msg, void** data)
 
 	if (group_datap->mMembers.size() ==  (U32)group_datap->mMemberCount)
 	{
-		group_datap->mMemberDataComplete = TRUE;
+		group_datap->mMemberDataComplete = true;
 		group_datap->mMemberRequestID.setNull();
 		// We don't want to make role-member data requests until we have all the members
 		if (group_datap->mPendingRoleMemberRequest)
 		{
-			group_datap->mPendingRoleMemberRequest = FALSE;
+			group_datap->mPendingRoleMemberRequest = false;
 			LLGroupMgr::getInstance()->sendGroupRoleMembersRequest(group_datap->mID);
 		}
 	}
@@ -1026,7 +1044,7 @@ void LLGroupMgr::processGroupPropertiesReply(LLMessageSystem* msg, void** data)
 	group_datap->mMemberCount = num_group_members;
 	group_datap->mRoleCount = num_group_roles + 1; // Add the everyone role.
 	
-	group_datap->mGroupPropertiesDataComplete = TRUE;
+	group_datap->mGroupPropertiesDataComplete = true;
 	group_datap->mChanged = TRUE;
 
 	LLGroupMgr::getInstance()->notifyObservers(GC_PROPERTIES);
@@ -1103,12 +1121,12 @@ void LLGroupMgr::processGroupRoleDataReply(LLMessageSystem* msg, void** data)
 
 	if (group_datap->mRoles.size() == (U32)group_datap->mRoleCount)
 	{
-		group_datap->mRoleDataComplete = TRUE;
+		group_datap->mRoleDataComplete = true;
 		group_datap->mRoleDataRequestID.setNull();
 		// We don't want to make role-member data requests until we have all the role data
 		if (group_datap->mPendingRoleMemberRequest)
 		{
-			group_datap->mPendingRoleMemberRequest = FALSE;
+			group_datap->mPendingRoleMemberRequest = false;
 			LLGroupMgr::getInstance()->sendGroupRoleMembersRequest(group_datap->mID);
 		}
 	}
@@ -1217,7 +1235,7 @@ void LLGroupMgr::processGroupRoleMembersReply(LLMessageSystem* msg, void** data)
 			}
 		}
 		
-        group_datap->mRoleMemberDataComplete = TRUE;
+        group_datap->mRoleMemberDataComplete= true;
 		group_datap->mRoleMembersRequestID.setNull();
 	}
 
@@ -1543,7 +1561,7 @@ void LLGroupMgr::sendGroupRoleMembersRequest(const LLUUID& group_id)
 			llinfos << " Pending: " << (group_datap->mPendingRoleMemberRequest ? "Y" : "N")
 				<< " MemberDataComplete: " << (group_datap->mMemberDataComplete ? "Y" : "N")
 				<< " RoleDataComplete: " << (group_datap->mRoleDataComplete ? "Y" : "N") << llendl;
-			group_datap->mPendingRoleMemberRequest = TRUE;
+			group_datap->mPendingRoleMemberRequest = true;
 			return;
 		}
 
@@ -1840,6 +1858,122 @@ void LLGroupMgr::sendGroupMemberEjects(const LLUUID& group_id,
 }
 
 
+// Responder class for capability group management
+class GroupBanDataResponder : public LLHTTPClient::Responder
+{
+public:
+	GroupBanDataResponder() {}
+	virtual ~GroupBanDataResponder() {}
+	virtual void result(const LLSD& pContent);
+	virtual void errorWithContent(U32 pStatus, const std::string& pReason, const LLSD& pContent);
+};
+
+void GroupBanDataResponder::errorWithContent(U32 pStatus, const std::string& pReason, const LLSD& pContent)
+{
+	LL_WARNS("GrpMgr") << "Error receiving group member data [status:" 
+		<< pStatus << "]: " << pContent << LL_ENDL;
+}
+
+void GroupBanDataResponder::result(const LLSD& content)
+{
+	LL_INFOS("GrpMgr") << "[BAKER] Received ban data!" << LL_ENDL;
+	LLGroupMgr::processGroupBanRequest(content);
+}
+
+void LLGroupMgr::sendGroupBanRequest(EBanRequestType request_type, const LLUUID& group_id, const std::vector<LLUUID> ban_list /* = std::vector<LLUUID>() */)
+{
+	LLViewerRegion* currentRegion = gAgent.getRegion();
+	if(!currentRegion)
+	{
+		LL_WARNS("GrpMgr") << "Agent does not have a current region. Uh-oh!" << LL_ENDL;
+		return;
+	}
+
+	// Check to make sure we have our capabilities
+	if(!currentRegion->capabilitiesReceived())
+	{
+		LL_WARNS("GrpMgr") << " Capabilities not received!" << LL_ENDL;
+		return;
+	}
+
+	// Get our capability
+	std::string cap_url =  currentRegion->getCapability("GroupBan");
+	if(cap_url.empty())
+	{
+		return;
+	}
+
+	LLHTTPClient::ResponderPtr grp_ban_responder = new GroupBanDataResponder();
+	// PUT to our service.  Add a body containing the group_id and list of agents to ban.
+	LLSD ban_ids = LLSD::emptyMap();
+	ban_ids["group_id"] = group_id;
+	// Add our list of potential banned agents to the list
+	ban_ids["ban_ids"]	= LLSD::emptyArray();
+	LLSD ban_entry;
+	std::vector<LLUUID>::const_iterator iter = ban_list.cbegin();
+	for(;iter != ban_list.end(); ++iter)
+	{
+		ban_entry = (*iter);
+		ban_ids["ban_ids"].append(ban_entry);
+	}
+
+	switch(request_type)
+	{
+	case REQUEST_GET:
+		cap_url += "?group_id=" + group_id.asString();
+		LLHTTPClient::get(cap_url, grp_ban_responder);
+		break;
+	case REQUEST_PUT:
+		// BAKER TODO: Figure out which 'body' is correct.
+		LLHTTPClient::put(cap_url, ban_ids, grp_ban_responder, LLSD(), 60);
+		break;
+	case REQUEST_DEL:
+		LLHTTPClient::del(cap_url, grp_ban_responder, ban_ids, 60);
+		break;
+	}
+
+	LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(group_id);
+	if (gdatap)
+		gdatap->setGroupBanStatus(LLGroupMgrGroupData::STATUS_REQUESTING);
+}
+
+
+void LLGroupMgr::processGroupBanRequest(const LLSD& content)
+{
+	// Did we get anything in content?
+	if(!content.size())
+	{
+		LL_DEBUGS("GrpMgr") << "No group member data received." << LL_ENDL;
+		return;
+	}
+
+	LLUUID group_id = content["group_id"].asUUID();
+	
+	LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(group_id);
+	if (!gdatap)
+		return;
+	
+	LLSD banlist = LLSD::emptyMap();
+
+
+	LLSD::map_const_iterator i		= content["ban_list"].beginMap();
+	LLSD::map_const_iterator iEnd	= content["ban_list"].endMap();
+	for(;i != iEnd; ++i)
+	{
+		const LLUUID ban_id(i->first);
+		// We have nothing right now inside our banlist map.
+		// Once ban_date is implemented, set that here!
+		// 
+		gdatap->createBanEntry(ban_id, LLGroupBanData());
+	}
+
+	gdatap->mChanged = TRUE;
+	gdatap->setGroupBanStatus(LLGroupMgrGroupData::STATUS_COMPLETE);
+	LLGroupMgr::getInstance()->notifyObservers(GC_BANLIST);
+}
+
+
+
 // Responder class for capability group management
 class GroupMemberDataResponder : public LLHTTPClient::Responder
 {
@@ -1925,7 +2059,7 @@ void LLGroupMgr::processCapGroupMembersRequest(const LLSD& content)
 	if(num_members < 1)
 		return;
 	
-	LLUUID	group_id = content["group_id"].asUUID();
+	LLUUID group_id = content["group_id"].asUUID();
 
 	LLGroupMgrGroupData* group_datap = LLGroupMgr::getInstance()->getGroupData(group_id);
 	if(!group_datap)
@@ -2008,12 +2142,12 @@ void LLGroupMgr::processCapGroupMembersRequest(const LLSD& content)
 		LLGroupMgr::getInstance()->sendGroupTitlesRequest(group_id);
 
 
-	group_datap->mMemberDataComplete = TRUE;
+	group_datap->mMemberDataComplete = true;
 	group_datap->mMemberRequestID.setNull();
 	// Make the role-member data request
 	if (group_datap->mPendingRoleMemberRequest)
 	{
-		group_datap->mPendingRoleMemberRequest = FALSE;
+		group_datap->mPendingRoleMemberRequest = false;
 		LLGroupMgr::getInstance()->sendGroupRoleMembersRequest(group_id);
 	}
 
diff --git a/indra/newview/llgroupmgr.h b/indra/newview/llgroupmgr.h
index d8c1ab7ef5f..876883e87e5 100755
--- a/indra/newview/llgroupmgr.h
+++ b/indra/newview/llgroupmgr.h
@@ -33,7 +33,11 @@
 #include <string>
 #include <map>
 
+// Forward Declarations
 class LLMessageSystem;
+class LLGroupRoleData;
+class LLGroupMgr;
+
 
 class LLGroupMgrObserver
 {
@@ -54,8 +58,6 @@ class LLParticularGroupObserver
 	virtual void changed(const LLUUID& group_id, LLGroupChange gc) = 0;
 };
 
-class LLGroupRoleData;
-
 class LLGroupMemberData
 {
 friend class LLGroupMgrGroupData;
@@ -190,6 +192,16 @@ struct lluuid_pair_less
 	}
 };
 
+
+struct LLGroupBanData
+{
+	LLGroupBanData()	{ mBanDate = "00/00/0000"; }
+	~LLGroupBanData()	{}
+	
+	std::string mBanDate; // Just store something here to ensure it works.
+};
+
+
 struct LLGroupTitle
 {
 	std::string mTitle;
@@ -197,11 +209,17 @@ struct LLGroupTitle
 	BOOL		mSelected;
 };
 
-class LLGroupMgr;
-
 class LLGroupMgrGroupData
 {
 friend class LLGroupMgr;
+public:
+	enum EGroupDataStatus
+	{
+		STATUS_NONE,
+		STATUS_REQUESTING,
+		STATUS_COMPLETE
+	};
+
 
 public:
 	LLGroupMgrGroupData(const LLUUID& id);
@@ -228,27 +246,46 @@ friend class LLGroupMgr;
 	void recalcAllAgentPowers();
 	void recalcAgentPowers(const LLUUID& agent_id);
 
-	BOOL isMemberDataComplete() { return mMemberDataComplete; }
-	BOOL isRoleDataComplete() { return mRoleDataComplete; }
-	BOOL isRoleMemberDataComplete() { return mRoleMemberDataComplete; }
-	BOOL isGroupPropertiesDataComplete() { return mGroupPropertiesDataComplete; }
+	bool isMemberDataComplete() { return mMemberDataComplete; }
+	bool isRoleDataComplete() { return mRoleDataComplete; }
+	bool isRoleMemberDataComplete() { return mRoleMemberDataComplete; }
+	bool isGroupPropertiesDataComplete() { return mGroupPropertiesDataComplete; }
+	bool isGroupBanDataComplete() { return mGroupBanDataComplete; }
+	
+	EGroupDataStatus getGroupBanStatus() { return mGroupBanStatus; }
+	void setGroupBanStatus(EGroupDataStatus status) { mGroupBanStatus = status; }
 
 	F32 getAccessTime() const { return mAccessTime; }
 	void setAccessed();
 
 	const LLUUID& getMemberVersion() const { return mMemberVersion; }
 
+	//////////////////////////////////////////////////////////////////////////
+	// BAN LIST
+	//////////////////////////////////////////////////////////////////////////
+	void clearBanList() { mBanList.clear(); }
+
+	void getBanList(const LLUUID& group_id, LLGroupBanData& ban_data);
+	const LLGroupBanData& getBanEntry(const LLUUID& ban_id) { return mBanList[ban_id]; }
+	
+	void createBanEntry(const LLUUID& ban_id, const LLGroupBanData& ban_data = LLGroupBanData());
+	bool removeBanEntry(const LLUUID& ban_id);
+	
+
+
+
 public:
 	typedef	std::map<LLUUID,LLGroupMemberData*> member_list_t;
 	typedef	std::map<LLUUID,LLGroupRoleData*> role_list_t;
 	typedef std::map<lluuid_pair,LLRoleMemberChange,lluuid_pair_less> change_map_t;
 	typedef std::map<LLUUID,LLRoleData> role_data_map_t;
+	typedef std::map<LLUUID,LLGroupBanData> ban_list_t;
+
 	member_list_t		mMembers;
 	role_list_t			mRoles;
-
-	
 	change_map_t		mRoleMemberChanges;
 	role_data_map_t		mRoleChanges;
+	ban_list_t			mBanList;
 
 	std::vector<LLGroupTitle> mTitles;
 
@@ -279,12 +316,16 @@ friend class LLGroupMgr;
 	LLUUID				mTitlesRequestID;
 	U32					mReceivedRoleMemberPairs;
 
-	BOOL				mMemberDataComplete;
-	BOOL				mRoleDataComplete;
-	BOOL				mRoleMemberDataComplete;
-	BOOL				mGroupPropertiesDataComplete;
+	bool				mMemberDataComplete;
+	bool				mRoleDataComplete;
+	bool				mRoleMemberDataComplete;
+	bool				mGroupPropertiesDataComplete;
+	
+	EGroupDataStatus	mGroupBanStatus;
+	bool				mGroupBanDataComplete;
+	bool				mGroupBanDataPending;
 
-	BOOL				mPendingRoleMemberRequest;
+	bool				mPendingRoleMemberRequest;
 	F32					mAccessTime;
 
 	// Generate a new ID every time mMembers
@@ -311,6 +352,14 @@ class LLGroupMgr : public LLSingleton<LLGroupMgr>
 {
 	LOG_CLASS(LLGroupMgr);
 	
+public:
+	enum EBanRequestType
+	{
+		REQUEST_GET = 0,
+		REQUEST_PUT,
+		REQUEST_DEL
+	};
+
 public:
 	LLGroupMgr();
 	~LLGroupMgr();
@@ -344,6 +393,9 @@ class LLGroupMgr : public LLSingleton<LLGroupMgr>
 	static void sendGroupMemberInvites(const LLUUID& group_id, std::map<LLUUID,LLUUID>& role_member_pairs);
 	static void sendGroupMemberEjects(const LLUUID& group_id,
 									  uuid_vec_t& member_ids);
+	// BAKER - Group Ban
+	static void sendGroupBanRequest(EBanRequestType request_type, const LLUUID& group_id, const std::vector<LLUUID> ban_list = std::vector<LLUUID>());
+	static void processGroupBanRequest(const LLSD& content);
 
 	// BAKER
 	void sendCapGroupMembersRequest(const LLUUID& group_id);
@@ -390,4 +442,3 @@ class LLGroupMgr : public LLSingleton<LLGroupMgr>
 
 
 #endif
-
diff --git a/indra/newview/llpanelgroupbulk.cpp b/indra/newview/llpanelgroupbulk.cpp
new file mode 100644
index 00000000000..6c6fd8cfe01
--- /dev/null
+++ b/indra/newview/llpanelgroupbulk.cpp
@@ -0,0 +1,403 @@
+/** 
+* @file llpanelgroupbulk.cpp
+* @brief Implementation of llpanelgroupbulk
+* @author Baker@lindenlab.com
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2013, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+* $/LicenseInfo$
+*/
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanelgroupbulk.h"
+#include "llpanelgroupbulkimpl.h"
+
+#include "llagent.h"
+#include "llavatarnamecache.h"
+#include "llfloateravatarpicker.h"
+#include "llbutton.h"
+#include "llcallingcard.h"
+#include "llcombobox.h"
+#include "llgroupactions.h"
+#include "llgroupmgr.h"
+#include "llnamelistctrl.h"
+#include "llnotificationsutil.h"
+#include "llscrolllistitem.h"
+#include "llspinctrl.h"
+#include "lltextbox.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "lluictrlfactory.h"
+#include "llviewerwindow.h"
+
+
+//////////////////////////////////////////////////////////////////////////
+// Implementation of llpanelgroupbulkimpl.h functions
+//////////////////////////////////////////////////////////////////////////
+LLPanelGroupBulkImpl::LLPanelGroupBulkImpl(const LLUUID& group_id) :
+	mGroupID(group_id),
+	mBulkAgentList(NULL),
+	mOKButton(NULL),
+	mRemoveButton(NULL),
+	mGroupName(NULL),
+	mLoadingText(),
+	mTooManySelected(),
+	mCloseCallback(NULL),
+	mCloseCallbackUserData(NULL),
+	mAvatarNameCacheConnection(),
+	mRoleNames(NULL),
+	mOwnerWarning(),
+	mAlreadyInGroup(),
+	mConfirmedOwnerInvite(false)
+{}
+
+LLPanelGroupBulkImpl::~LLPanelGroupBulkImpl()
+{
+	if(mAvatarNameCacheConnection.connected())
+	{
+		mAvatarNameCacheConnection.disconnect();
+	}
+}
+
+void LLPanelGroupBulkImpl::callbackClickAdd(void* userdata)
+{
+	LLPanelGroupBulk* panelp = (LLPanelGroupBulk*)userdata;
+
+	if(panelp)
+	{
+		//Right now this is hard coded with some knowledge that it is part
+		//of a floater since the avatar picker needs to be added as a dependent
+		//floater to the parent floater.
+		//Soon the avatar picker will be embedded into this panel
+		//instead of being it's own separate floater.  But that is next week.
+		//This will do for now. -jwolk May 10, 2006
+		LLView* button = panelp->findChild<LLButton>("add_button");
+		LLFloater* root_floater = gFloaterView->getParentFloater(panelp);
+		LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(
+			boost::bind(callbackAddUsers, _1, panelp->mImplementation), TRUE, FALSE, FALSE, root_floater->getName(), button);
+		if(picker)
+		{
+			root_floater->addDependentFloater(picker);
+		}
+	}
+}
+
+void LLPanelGroupBulkImpl::callbackClickRemove(void* userdata)
+{
+	LLPanelGroupBulkImpl* selfp = (LLPanelGroupBulkImpl*)userdata;
+	if (selfp) 
+		selfp->handleRemove();
+}
+
+void LLPanelGroupBulkImpl::callbackClickCancel(void* userdata)
+{
+	LLPanelGroupBulkImpl* selfp = (LLPanelGroupBulkImpl*)userdata;
+	if(selfp) 
+		(*(selfp->mCloseCallback))(selfp->mCloseCallbackUserData);
+}
+
+void LLPanelGroupBulkImpl::callbackSelect(LLUICtrl* ctrl, void* userdata)
+{
+	LLPanelGroupBulkImpl* selfp = (LLPanelGroupBulkImpl*)userdata;
+	if (selfp) 
+		selfp->handleSelection();
+}
+
+void LLPanelGroupBulkImpl::callbackAddUsers(const uuid_vec_t& agent_ids, void* user_data)
+{
+	std::vector<std::string> names;
+	for (S32 i = 0; i < (S32)agent_ids.size(); i++)
+	{
+		LLAvatarName av_name;
+		if (LLAvatarNameCache::get(agent_ids[i], &av_name))
+		{
+			onAvatarNameCache(agent_ids[i], av_name, user_data);
+		}
+		else 
+		{
+			LLPanelGroupBulkImpl* selfp = (LLPanelGroupBulkImpl*) user_data;
+			if (selfp)
+			{
+				if (selfp->mAvatarNameCacheConnection.connected())
+				{
+					selfp->mAvatarNameCacheConnection.disconnect();
+				}
+				// *TODO : Add a callback per avatar name being fetched.
+				selfp->mAvatarNameCacheConnection = LLAvatarNameCache::get(agent_ids[i],boost::bind(onAvatarNameCache, _1, _2, user_data));
+			}
+		}
+	}
+}
+
+void LLPanelGroupBulkImpl::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name, void* user_data)
+{
+	LLPanelGroupBulkImpl* selfp = (LLPanelGroupBulkImpl*) user_data;
+
+	if (selfp)
+	{
+		if (selfp->mAvatarNameCacheConnection.connected())
+		{
+			selfp->mAvatarNameCacheConnection.disconnect();
+		}
+		std::vector<std::string> names;
+		uuid_vec_t agent_ids;
+		agent_ids.push_back(agent_id);
+		names.push_back(av_name.getCompleteName());
+
+		selfp->addUsers(names, agent_ids);
+	}
+}
+
+void LLPanelGroupBulkImpl::handleRemove()
+{
+	std::vector<LLScrollListItem*> selection = mBulkAgentList->getAllSelected();
+	if (selection.empty()) 
+		return;
+
+	mBulkAgentList->deleteSelectedItems();
+	mRemoveButton->setEnabled(FALSE);
+
+	if(	mOKButton && mOKButton->getEnabled() &&
+		mBulkAgentList->isEmpty())
+	{
+		mOKButton->setEnabled(FALSE);
+	}
+}
+
+void LLPanelGroupBulkImpl::handleSelection()
+{
+	std::vector<LLScrollListItem*> selection = mBulkAgentList->getAllSelected();
+	if (selection.empty())
+		mRemoveButton->setEnabled(FALSE);
+	else
+		mRemoveButton->setEnabled(TRUE);
+}
+
+void LLPanelGroupBulkImpl::addUsers(const std::vector<std::string>& names, const uuid_vec_t& agent_ids)
+{
+	std::string name;
+	LLUUID id;
+
+	for (S32 i = 0; i < (S32)names.size(); ++i)
+	{
+		name = names[i];
+		id = agent_ids[i];
+
+		// Make sure this agent isn't already in the list.
+		bool already_in_list = false;
+		std::vector<LLScrollListItem*> items = mBulkAgentList->getAllData();
+		std::vector<LLScrollListItem*>::iterator iter = items.begin();
+		for (; iter != items.end(); ++iter)
+		{
+			LLScrollListItem* item = *iter;
+			if (item->getUUID() == id)
+			{
+				already_in_list = true;
+				break;
+			}
+		}
+		if (already_in_list)
+		{
+			continue;
+		}
+
+		//add the name to the names list
+		LLSD row;
+		row["id"] = id;
+		row["columns"][0]["value"] = name;
+
+		mBulkAgentList->addElement(row);
+		
+		// We've successfully added someone to the list.
+		if(mOKButton && !mOKButton->getEnabled())
+			mOKButton->setEnabled(TRUE);
+	}
+}
+
+void LLPanelGroupBulkImpl::setGroupName(std::string name)
+{
+	if(mGroupName)
+		mGroupName->setText(name);
+}
+
+
+LLPanelGroupBulk::LLPanelGroupBulk(const LLUUID& group_id) : 
+	LLPanel(),
+	mImplementation(new LLPanelGroupBulkImpl(group_id)),
+	mPendingGroupPropertiesUpdate(false),
+	mPendingRoleDataUpdate(false),
+	mPendingMemberDataUpdate(false)
+{}
+
+LLPanelGroupBulk::~LLPanelGroupBulk()
+{
+	delete mImplementation;
+}
+
+void LLPanelGroupBulk::clear()
+{
+	if(mImplementation->mBulkAgentList)
+		mImplementation->mBulkAgentList->deleteAllItems();
+	
+	if(mImplementation->mOKButton)
+		mImplementation->mOKButton->setEnabled(FALSE);
+}
+
+void LLPanelGroupBulk::update()
+{
+	updateGroupName();
+	updateGroupData();
+}
+
+void LLPanelGroupBulk::draw()
+{
+	LLPanel::draw();
+	update();
+}
+
+void LLPanelGroupBulk::updateGroupName()
+{
+	LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mImplementation->mGroupID);
+
+	if(	gdatap &&
+		gdatap->isGroupPropertiesDataComplete())
+	{
+		// Only do work if the current group name differs
+		if(mImplementation->mGroupName->getText().compare(gdatap->mName) != 0)
+			mImplementation->setGroupName(gdatap->mName);
+	}
+	else
+	{
+		mImplementation->setGroupName(mImplementation->mLoadingText);
+	}
+}
+
+void LLPanelGroupBulk::updateGroupData()
+{
+	LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mImplementation->mGroupID);
+	if(!gdatap)
+		return;
+
+	if(gdatap->isGroupPropertiesDataComplete())
+		mPendingGroupPropertiesUpdate = false;
+	else
+	{
+		if(!mPendingGroupPropertiesUpdate)
+		{
+			mPendingGroupPropertiesUpdate = true;
+			LLGroupMgr::getInstance()->sendGroupPropertiesRequest(mImplementation->mGroupID);
+		}
+	}
+
+	if(gdatap->isRoleDataComplete())
+		mPendingRoleDataUpdate = false;
+	else
+	{
+		if(!mPendingRoleDataUpdate)
+		{
+			mPendingRoleDataUpdate = true;
+			LLGroupMgr::getInstance()->sendGroupRoleDataRequest(mImplementation->mGroupID);
+		}
+	}
+
+	if(gdatap->isMemberDataComplete())
+		mPendingMemberDataUpdate = false;
+	else
+	{
+		if(!mPendingMemberDataUpdate)
+		{
+			mPendingMemberDataUpdate = true;
+			LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mImplementation->mGroupID);
+		}
+	}
+}
+
+void LLPanelGroupBulk::addUserCallback(const LLUUID& id, const LLAvatarName& av_name)
+{
+	std::vector<std::string> names;
+	uuid_vec_t agent_ids;
+	agent_ids.push_back(id);
+	names.push_back(av_name.getAccountName());
+
+	mImplementation->addUsers(names, agent_ids);
+}
+
+void LLPanelGroupBulk::setCloseCallback(void (*close_callback)(void*), void* data)
+{
+	mImplementation->mCloseCallback         = close_callback;
+	mImplementation->mCloseCallbackUserData = data;
+}
+
+void LLPanelGroupBulk::addUsers(uuid_vec_t& agent_ids)
+{
+	std::vector<std::string> names;
+	for (S32 i = 0; i < (S32)agent_ids.size(); i++)
+	{
+		std::string fullname;
+		LLUUID agent_id = agent_ids[i];
+		LLViewerObject* dest = gObjectList.findObject(agent_id);
+		if(dest && dest->isAvatar())
+		{
+			LLNameValue* nvfirst = dest->getNVPair("FirstName");
+			LLNameValue* nvlast = dest->getNVPair("LastName");
+			if(nvfirst && nvlast)
+			{
+				fullname = LLCacheName::buildFullName(
+					nvfirst->getString(), nvlast->getString());
+
+			}
+			if (!fullname.empty())
+			{
+				names.push_back(fullname);
+			} 
+			else 
+			{
+				llwarns << "llPanelGroupBulk: Selected avatar has no name: " << dest->getID() << llendl;
+				names.push_back("(Unknown)");
+			}
+		}
+		else
+		{
+			//looks like user try to invite offline friend
+			//for offline avatar_id gObjectList.findObject() will return null
+			//so we need to do this additional search in avatar tracker, see EXT-4732
+			if (LLAvatarTracker::instance().isBuddy(agent_id))
+			{
+				LLAvatarName av_name;
+				if (!LLAvatarNameCache::get(agent_id, &av_name))
+				{
+					// actually it should happen, just in case
+					LLAvatarNameCache::get(LLUUID(agent_id), boost::bind(&LLPanelGroupBulk::addUserCallback, this, _1, _2));
+					// for this special case!
+					//when there is no cached name we should remove resident from agent_ids list to avoid breaking of sequence
+					// removed id will be added in callback
+					agent_ids.erase(agent_ids.begin() + i);
+				}
+				else
+				{
+					names.push_back(av_name.getAccountName());
+				}
+			}
+		}
+	}
+	mImplementation->addUsers(names, agent_ids);
+}
+
diff --git a/indra/newview/llpanelgroupbulk.h b/indra/newview/llpanelgroupbulk.h
new file mode 100644
index 00000000000..222931eabc6
--- /dev/null
+++ b/indra/newview/llpanelgroupbulk.h
@@ -0,0 +1,74 @@
+/** 
+* @file   llpanelgroupbulk.h
+* @brief  Header file for llpanelgroupbulk
+* @author Baker@lindenlab.com
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2013, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+* $/LicenseInfo$
+*/
+#ifndef LL_LLPANELGROUPBULK_H
+#define LL_LLPANELGROUPBULK_H
+
+#include "llpanel.h"
+#include "lluuid.h"
+
+class LLAvatarName;
+class LLGroupMgrGroupData;
+class LLPanelGroupBulkImpl;
+
+// Base panel class for bulk group invite / ban floaters
+class LLPanelGroupBulk : public LLPanel
+{
+public:
+	LLPanelGroupBulk(const LLUUID& group_id);
+	~LLPanelGroupBulk();
+
+public:	
+	static void callbackClickSubmit(void* userdata) {}
+	virtual void submit() = 0;
+
+public:
+	virtual void clear();
+	virtual void update();
+	virtual void draw();
+
+protected:
+	virtual void updateGroupName();
+	virtual void updateGroupData();
+
+public:
+	// this callback is being used to add a user whose fullname isn't been loaded before invoking of addUsers().
+	virtual void addUserCallback(const LLUUID& id, const LLAvatarName& av_name);
+	virtual void setCloseCallback(void (*close_callback)(void*), void* data);
+
+	virtual void addUsers(uuid_vec_t& agent_ids);
+
+public:
+	LLPanelGroupBulkImpl* mImplementation;
+
+protected:
+	bool mPendingGroupPropertiesUpdate;
+	bool mPendingRoleDataUpdate;
+	bool mPendingMemberDataUpdate;
+};
+
+#endif // LL_LLPANELGROUPBULK_H
+
diff --git a/indra/newview/llpanelgroupbulkban.cpp b/indra/newview/llpanelgroupbulkban.cpp
new file mode 100644
index 00000000000..57bab0c8132
--- /dev/null
+++ b/indra/newview/llpanelgroupbulkban.cpp
@@ -0,0 +1,156 @@
+/** 
+* @file llpanelgroupbulkban.cpp
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2013, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+* $/LicenseInfo$
+*/
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanelgroupbulkban.h"
+#include "llpanelgroupbulk.h"
+#include "llpanelgroupbulkimpl.h"
+
+#include "llagent.h"
+#include "llavatarnamecache.h"
+#include "llfloateravatarpicker.h"
+#include "llbutton.h"
+#include "llcallingcard.h"
+#include "llcombobox.h"
+#include "llgroupactions.h"
+#include "llgroupmgr.h"
+#include "llnamelistctrl.h"
+#include "llnotificationsutil.h"
+#include "llscrolllistitem.h"
+#include "llspinctrl.h"
+#include "lltextbox.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "lluictrlfactory.h"
+#include "llviewerwindow.h"
+
+
+LLPanelGroupBulkBan::LLPanelGroupBulkBan(const LLUUID& group_id) : LLPanelGroupBulk(group_id)
+{
+	// Pass on construction of this panel to the control factory.
+	buildFromFile( "panel_group_bulk_ban.xml");
+}
+
+BOOL LLPanelGroupBulkBan::postBuild()
+{
+	BOOL recurse = TRUE;
+
+	mImplementation->mLoadingText = getString("loading");
+	mImplementation->mGroupName = getChild<LLTextBox>("group_name_text", recurse);
+	mImplementation->mBulkAgentList = getChild<LLNameListCtrl>("banned_agent_list", recurse);
+	if ( mImplementation->mBulkAgentList )
+	{
+		mImplementation->mBulkAgentList->setCommitOnSelectionChange(TRUE);
+		mImplementation->mBulkAgentList->setCommitCallback(LLPanelGroupBulkImpl::callbackSelect, mImplementation);
+	}
+
+	LLButton* button = getChild<LLButton>("add_button", recurse);
+	if ( button )
+	{
+		// default to opening avatarpicker automatically
+		// (*impl::callbackClickAdd)((void*)this);
+		button->setClickedCallback(LLPanelGroupBulkImpl::callbackClickAdd, this);
+	}
+
+	mImplementation->mRemoveButton = 
+		getChild<LLButton>("remove_button", recurse);
+	if ( mImplementation->mRemoveButton )
+	{
+		mImplementation->mRemoveButton->setClickedCallback(LLPanelGroupBulkImpl::callbackClickRemove, mImplementation);
+		mImplementation->mRemoveButton->setEnabled(FALSE);
+	}
+
+	mImplementation->mOKButton = 
+		getChild<LLButton>("ban_button", recurse);
+	if ( mImplementation->mOKButton )
+	{
+		mImplementation->mOKButton->setClickedCallback(LLPanelGroupBulkBan::callbackClickSubmit, this);
+		mImplementation->mOKButton->setEnabled(FALSE);
+	}
+
+	button = getChild<LLButton>("cancel_button", recurse);
+	if ( button )
+	{
+		button->setClickedCallback(LLPanelGroupBulkImpl::callbackClickCancel, mImplementation);
+	}
+
+	mImplementation->mTooManySelected = getString("ban_selection_too_large");
+
+	update();
+
+	// 	return (mImplementation->mRoleNames &&
+	// 		mImplementation->mBannedAgents &&
+	// 		mImplementation->mRemoveButton);
+
+	return (mImplementation->mBulkAgentList &&
+		mImplementation->mRemoveButton);
+}
+
+
+void LLPanelGroupBulkBan::callbackClickSubmit(void* userdata)
+{
+	LLPanelGroupBulkBan* selfp = (LLPanelGroupBulkBan*)userdata;
+
+	if(selfp)
+		selfp->submit();
+}
+
+
+void LLPanelGroupBulkBan::submit()
+{
+	std::vector<LLUUID> banned_agent_list;	
+	std::vector<LLScrollListItem*> agents = mImplementation->mBulkAgentList->getAllData();
+	std::vector<LLScrollListItem*>::iterator iter = agents.begin();
+	for(;iter != agents.end(); ++iter)
+	{
+		LLScrollListItem* agent = *iter;
+		banned_agent_list.push_back(agent->getUUID());
+	}
+
+	const S32 MAX_GROUP_BANS = 100; // Max invites per request. 100 to match server cap.
+	if (banned_agent_list.size() > MAX_GROUP_BANS)
+	{
+		// Fail!
+		LLSD msg;
+		msg["MESSAGE"] = mImplementation->mTooManySelected;
+		LLNotificationsUtil::add("GenericAlert", msg);
+		(*(mImplementation->mCloseCallback))(mImplementation->mCloseCallbackUserData);
+		return;
+	}
+
+	LLGroupMgr::getInstance()->sendGroupBanRequest(LLGroupMgr::REQUEST_PUT, mImplementation->mGroupID, banned_agent_list);
+
+	// BAKER TEMP:
+	//	For now, don't close, but clear the list.
+	mImplementation->mBulkAgentList->deleteAllItems();
+
+	//then close
+	//(*(mImplementation->mCloseCallback))(mImplementation->mCloseCallbackUserData);
+}
+
+
+
+
diff --git a/indra/newview/llpanelgroupbulkban.h b/indra/newview/llpanelgroupbulkban.h
new file mode 100644
index 00000000000..0684f365a0d
--- /dev/null
+++ b/indra/newview/llpanelgroupbulkban.h
@@ -0,0 +1,47 @@
+/** 
+* @file   llpanelgroupbulkban.h
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2013, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+* $/LicenseInfo$
+*/
+
+#ifndef LL_LLPANELGROUPBULKBAN_H
+#define LL_LLPANELGROUPBULKBAN_H
+
+#include "llpanel.h"
+#include "lluuid.h"
+#include "llpanelgroupbulk.h"
+
+class LLAvatarName;
+
+class LLPanelGroupBulkBan : public LLPanelGroupBulk
+{
+public:
+	LLPanelGroupBulkBan(const LLUUID& group_id);
+	~LLPanelGroupBulkBan() {}
+
+	virtual BOOL postBuild();
+
+	static void callbackClickSubmit(void* userdata);
+	virtual void submit();
+};
+
+#endif // LL_LLPANELGROUPBULKBAN_H
diff --git a/indra/newview/llpanelgroupbulkimpl.h b/indra/newview/llpanelgroupbulkimpl.h
new file mode 100644
index 00000000000..74da542eb8c
--- /dev/null
+++ b/indra/newview/llpanelgroupbulkimpl.h
@@ -0,0 +1,87 @@
+/** 
+* @file   llpanelgroupbulkimpl.h
+* @brief  Header file for llpanelgroupbulkimpl
+* @author Baker@lindenlab.com
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2013, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+* $/LicenseInfo$
+*/
+#ifndef LL_LLPANELGROUPBULKIMPL_H
+#define LL_LLPANELGROUPBULKIMPL_H
+
+#include "llpanel.h"
+#include "lluuid.h"
+
+class LLAvatarName;
+class LLNameListCtrl;
+class LLTextBox;
+class LLComboBox;
+
+class LLPanelGroupBulkImpl
+{
+public:
+	LLPanelGroupBulkImpl(const LLUUID& group_id);
+	~LLPanelGroupBulkImpl();
+
+	static void callbackClickAdd(void* userdata);
+	static void callbackClickRemove(void* userdata);
+
+	static void callbackClickCancel(void* userdata);
+
+	static void callbackSelect(LLUICtrl* ctrl, void* userdata);
+	static void callbackAddUsers(const uuid_vec_t& agent_ids, void* user_data);
+
+	static void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name, void* user_data);
+
+	void handleRemove();
+	void handleSelection();
+
+	void addUsers(const std::vector<std::string>& names, const uuid_vec_t& agent_ids);
+	void setGroupName(std::string name);
+
+
+public:
+	LLUUID			mGroupID;
+
+	LLNameListCtrl*	mBulkAgentList;
+	LLButton*		mOKButton;
+	LLButton*		mRemoveButton;
+	LLTextBox*		mGroupName;
+
+	std::string		mLoadingText;
+	std::string		mTooManySelected;
+
+	void (*mCloseCallback)(void* data);
+	void* mCloseCallbackUserData;
+	boost::signals2::connection mAvatarNameCacheConnection;
+
+	// The following are for the LLPanelGroupInvite subclass only.  
+	// These aren't needed for LLPanelGroupBulkBan, but if we have to add another 
+	// group bulk floater for some reason, we'll have these objects too.
+public:
+	LLComboBox*		mRoleNames;
+	std::string		mOwnerWarning;
+	std::string		mAlreadyInGroup;
+	bool			mConfirmedOwnerInvite;
+};
+
+#endif // LL_LLPANELGROUPBULKIMPL_H
+
diff --git a/indra/newview/llpanelgroupinvite.cpp b/indra/newview/llpanelgroupinvite.cpp
index 133b269c112..c990584d221 100755
--- a/indra/newview/llpanelgroupinvite.cpp
+++ b/indra/newview/llpanelgroupinvite.cpp
@@ -26,6 +26,8 @@
 #include "llviewerprecompiledheaders.h"
 
 #include "llpanelgroupinvite.h"
+#include "llpanelgroupbulk.h"
+#include "llpanelgroupbulkimpl.h"
 
 #include "llagent.h"
 #include "llavatarnamecache.h"
@@ -45,203 +47,216 @@
 #include "lluictrlfactory.h"
 #include "llviewerwindow.h"
 
-class LLPanelGroupInvite::impl
+
+// BAKER TODO:
+// Figure out how to use LLHandle<LLPanel> to make this safer
+//bool invite_owner_callback(LLPanelGroupInvite panel, const LLSD& notification, const LLSD& response)
+bool invite_owner_callback(LLHandle<LLPanel> panel_handle, const LLSD& notification, const LLSD& response)
 {
-public:
-	impl(const LLUUID& group_id);
-	~impl();
-
-	void addUsers(const std::vector<std::string>& names,
-				  const uuid_vec_t& agent_ids);
-	void submitInvitations();
-	void addRoleNames(LLGroupMgrGroupData* gdatap);
-	void handleRemove();
-	void handleSelection();
-
-	static void callbackClickCancel(void* userdata);
-	static void callbackClickOK(void* userdata);
-	static void callbackClickAdd(void* userdata);
-	static void callbackClickRemove(void* userdata);
-	static void callbackSelect(LLUICtrl* ctrl, void* userdata);
-	static void callbackAddUsers(const uuid_vec_t& agent_ids,
-								 void* user_data);
-	
-	static void onAvatarNameCache(const LLUUID& agent_id,
-											 const LLAvatarName& av_name,
-											 void* user_data);
-
-	bool inviteOwnerCallback(const LLSD& notification, const LLSD& response);
-
-public:
-	LLUUID mGroupID;
-
-	std::string		mLoadingText;
-	LLNameListCtrl	*mInvitees;
-	LLComboBox      *mRoleNames;
-	LLButton		*mOKButton;
- 	LLButton		*mRemoveButton;
-	LLTextBox		*mGroupName;
-	std::string		mOwnerWarning;
-	std::string		mAlreadyInGroup;
-	std::string		mTooManySelected;
-	bool		mConfirmedOwnerInvite;
-
-	void (*mCloseCallback)(void* data);
-
-	void* mCloseCallbackUserData;
-
-	boost::signals2::connection mAvatarNameCacheConnection;
-};
-
-
-LLPanelGroupInvite::impl::impl(const LLUUID& group_id):
-	mGroupID( group_id ),
-	mLoadingText (),
-	mInvitees ( NULL ),
-	mRoleNames( NULL ),
-	mOKButton ( NULL ),
-	mRemoveButton( NULL ),
-	mGroupName( NULL ),
-	mConfirmedOwnerInvite( false ),
-	mCloseCallback( NULL ),
-	mCloseCallbackUserData( NULL ),
-	mAvatarNameCacheConnection()
+	LLPanelGroupInvite* panel = dynamic_cast<LLPanelGroupInvite*>(panel_handle.get());
+	if(!panel)
+		return false;
+
+	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+	switch(option)
+	{
+	case 0:
+		// user confirmed that they really want a new group owner
+		panel->mImplementation->mConfirmedOwnerInvite = true;
+		panel->submit();
+		break;
+	case 1:
+		// fall through
+	default:
+		break;
+	}
+ 	return false;
+}
+
+
+LLPanelGroupInvite::LLPanelGroupInvite(const LLUUID& group_id) : LLPanelGroupBulk(group_id)
 {
+	// Pass on construction of this panel to the control factory.
+	buildFromFile( "panel_group_invite.xml");
 }
 
-LLPanelGroupInvite::impl::~impl()
+void LLPanelGroupInvite::clear()
 {
-	if (mAvatarNameCacheConnection.connected())
+	LLPanelGroupBulk::clear();
+
+	if(mImplementation->mRoleNames)
 	{
-		mAvatarNameCacheConnection.disconnect();
+		mImplementation->mRoleNames->clear();
+		mImplementation->mRoleNames->removeall();
+		mImplementation->mRoleNames->setCurrentByID(LLUUID::null);
 	}
 }
 
-void LLPanelGroupInvite::impl::addUsers(const std::vector<std::string>& names,
-										const uuid_vec_t& agent_ids)
+void LLPanelGroupInvite::update()
 {
-	std::string name;
-	LLUUID id;
+	LLPanelGroupBulk::update();
 
-	for (S32 i = 0; i < (S32)names.size(); i++)
+	if(mImplementation->mRoleNames)
 	{
-		name = names[i];
-		id = agent_ids[i];
-
-		// Make sure this agent isn't already in the list.
-		bool already_in_list = false;
-		std::vector<LLScrollListItem*> items = mInvitees->getAllData();
-		for (std::vector<LLScrollListItem*>::iterator iter = items.begin();
-			 iter != items.end(); ++iter)
+		LLUUID store_selected_role = mImplementation->mRoleNames->getCurrentID();
+		mImplementation->mRoleNames->clear();
+		mImplementation->mRoleNames->removeall();
+		mImplementation->mRoleNames->setCurrentByID(LLUUID::null);
+
+		if(!mPendingRoleDataUpdate && !mPendingMemberDataUpdate)
 		{
-			LLScrollListItem* item = *iter;
-			if (item->getUUID() == id)
-			{
-				already_in_list = true;
-				break;
-			}
+			//////////////////////////////////////////////////////////////////////////
+			// Add role names
+			 addRoleNames();
+			////////////////////////////////////////////////////////////////////////////
+			mImplementation->mRoleNames->setCurrentByID(store_selected_role);
 		}
-		if (already_in_list)
+		else
 		{
-			continue;
+			mImplementation->mRoleNames->add(mImplementation->mLoadingText, LLUUID::null, ADD_BOTTOM);
 		}
+		
+	}
+}
+
+BOOL LLPanelGroupInvite::postBuild()
+{
+	BOOL recurse = TRUE;
 
-		//add the name to the names list
-		LLSD row;
-		row["id"] = id;
-		row["columns"][0]["value"] = name;
+	mImplementation->mLoadingText = getString("loading");
+	mImplementation->mRoleNames = getChild<LLComboBox>("role_name",
+															   recurse);
+	mImplementation->mGroupName = getChild<LLTextBox>("group_name_text", recurse);
+	mImplementation->mBulkAgentList = getChild<LLNameListCtrl>("invitee_list", recurse);
+	if ( mImplementation->mBulkAgentList )
+	{
+		mImplementation->mBulkAgentList->setCommitOnSelectionChange(TRUE);
+		mImplementation->mBulkAgentList->setCommitCallback(LLPanelGroupBulkImpl::callbackSelect, mImplementation);
+	}
 
-		mInvitees->addElement(row);
+	LLButton* button = getChild<LLButton>("add_button", recurse);
+	if ( button )
+	{
+		// default to opening avatarpicker automatically
+		// (*impl::callbackClickAdd)((void*)this);
+		button->setClickedCallback(LLPanelGroupBulkImpl::callbackClickAdd, this);
 	}
+
+	mImplementation->mRemoveButton = 
+			getChild<LLButton>("remove_button", recurse);
+	if ( mImplementation->mRemoveButton )
+	{
+		mImplementation->mRemoveButton->setClickedCallback(LLPanelGroupBulkImpl::callbackClickRemove, mImplementation);
+		mImplementation->mRemoveButton->setEnabled(FALSE);
+	}
+
+	mImplementation->mOKButton = getChild<LLButton>("invite_button", recurse);
+	if ( mImplementation->mOKButton )
+ 	{
+		mImplementation->mOKButton->setClickedCallback(LLPanelGroupInvite::callbackClickSubmit, this);
+		mImplementation->mOKButton->setEnabled(FALSE);
+ 	}
+
+	button = getChild<LLButton>("cancel_button", recurse);
+	if ( button )
+	{
+		button->setClickedCallback(LLPanelGroupBulkImpl::callbackClickCancel, mImplementation);
+	}
+
+	mImplementation->mOwnerWarning = getString("confirm_invite_owner_str");
+	mImplementation->mAlreadyInGroup = getString("already_in_group");
+	mImplementation->mTooManySelected = getString("invite_selection_too_large");
+
+	update();
+	
+	return (mImplementation->mRoleNames &&
+			mImplementation->mBulkAgentList &&
+			mImplementation->mRemoveButton);
+}
+
+void LLPanelGroupInvite::callbackClickSubmit(void* userdata)
+{
+	LLPanelGroupInvite* selfp = (LLPanelGroupInvite*)userdata;
+
+	if(selfp)
+		selfp->submit();
 }
 
-void LLPanelGroupInvite::impl::submitInvitations()
+void LLPanelGroupInvite::submit()
 {
 	std::map<LLUUID, LLUUID> role_member_pairs;
 
-	LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
+	LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mImplementation->mGroupID);
 
 	// Default to everyone role.
 	LLUUID role_id = LLUUID::null;
 
-	if (mRoleNames)
+	if (mImplementation->mRoleNames)
 	{
-		role_id = mRoleNames->getCurrentID();
-		
+		role_id = mImplementation->mRoleNames->getCurrentID();
+
+		//LLUUID t_ownerUUID = gdatap->mOwnerRole;
+		//bool t_confirmInvite = mImplementation->mConfirmedOwnerInvite;
+
 		// owner role: display confirmation and wait for callback
-		if ((role_id == gdatap->mOwnerRole) && (!mConfirmedOwnerInvite))
+		if ((role_id == gdatap->mOwnerRole) && (!mImplementation->mConfirmedOwnerInvite))
 		{
 			LLSD args;
-			args["MESSAGE"] = mOwnerWarning;
-			LLNotificationsUtil::add("GenericAlertYesCancel", args, LLSD(), boost::bind(&LLPanelGroupInvite::impl::inviteOwnerCallback, this, _1, _2));
+			args["MESSAGE"] = mImplementation->mOwnerWarning;
+			LLNotificationsUtil::add(	"GenericAlertYesCancel", 
+										args, 
+										LLSD(), 
+										boost::bind(invite_owner_callback, 
+													this->getHandle(), 
+													_1, _2));
 			return; // we'll be called again if user confirms
 		}
 	}
 
 	bool already_in_group = false;
 	//loop over the users
-	std::vector<LLScrollListItem*> items = mInvitees->getAllData();
+	std::vector<LLScrollListItem*> items = mImplementation->mBulkAgentList->getAllData();
 	for (std::vector<LLScrollListItem*>::iterator iter = items.begin();
-		 iter != items.end(); ++iter)
+		iter != items.end(); ++iter)
 	{
 		LLScrollListItem* item = *iter;
-		if(LLGroupActions::isAvatarMemberOfGroup(mGroupID, item->getUUID()))
+		if(LLGroupActions::isAvatarMemberOfGroup(mImplementation->mGroupID, item->getUUID()))
 		{
 			already_in_group = true;
 			continue;
 		}
 		role_member_pairs[item->getUUID()] = role_id;
 	}
-	
+
 	const S32 MAX_GROUP_INVITES = 100; // Max invites per request. 100 to match server cap.
 	if (role_member_pairs.size() > MAX_GROUP_INVITES)
 	{
 		// Fail!
 		LLSD msg;
-		msg["MESSAGE"] = mTooManySelected;
+		msg["MESSAGE"] = mImplementation->mTooManySelected;
 		LLNotificationsUtil::add("GenericAlert", msg);
-		(*mCloseCallback)(mCloseCallbackUserData);
+		(*(mImplementation->mCloseCallback))(mImplementation->mCloseCallbackUserData);
 		return;
 	}
 
-	LLGroupMgr::getInstance()->sendGroupMemberInvites(mGroupID, role_member_pairs);
-	
+	LLGroupMgr::getInstance()->sendGroupMemberInvites(mImplementation->mGroupID, role_member_pairs);
+
 	if(already_in_group)
 	{
 		LLSD msg;
-		msg["MESSAGE"] = mAlreadyInGroup;
+		msg["MESSAGE"] = mImplementation->mAlreadyInGroup;
 		LLNotificationsUtil::add("GenericAlert", msg);
 	}
 
 	//then close
-	(*mCloseCallback)(mCloseCallbackUserData);
+	(*(mImplementation->mCloseCallback))(mImplementation->mCloseCallbackUserData);
 }
 
-bool LLPanelGroupInvite::impl::inviteOwnerCallback(const LLSD& notification, const LLSD& response)
+void LLPanelGroupInvite::addRoleNames()
 {
-	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
-
-	switch(option)
-	{
-	case 0:
-		// user confirmed that they really want a new group owner
-		mConfirmedOwnerInvite = true;
-		submitInvitations();
-		break;
-	case 1:
-		// fall through
-	default:
-		break;
-	}
-	return false;
-}
-
+	LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mImplementation->mGroupID);
 
-
-void LLPanelGroupInvite::impl::addRoleNames(LLGroupMgrGroupData* gdatap)
-{
-	LLGroupMgrGroupData::member_list_t::iterator agent_iter =
-		gdatap->mMembers.find(gAgent.getID());
+	LLGroupMgrGroupData::member_list_t::iterator agent_iter = gdatap->mMembers.find(gAgent.getID());
 
 	//get the member data for the agent if it exists
 	if ( agent_iter != gdatap->mMembers.end() )
@@ -251,7 +266,7 @@ void LLPanelGroupInvite::impl::addRoleNames(LLGroupMgrGroupData* gdatap)
 		//loop over the agent's roles in the group
 		//then add those roles to the list of roles that the agent
 		//can invite people to be
-		if ( member_data && mRoleNames)
+		if ( member_data && mImplementation->mRoleNames)
 		{
 			//if the user is the owner then we add
 			//all of the roles in the group
@@ -261,10 +276,10 @@ void LLPanelGroupInvite::impl::addRoleNames(LLGroupMgrGroupData* gdatap)
 			//we add every role the user is in
 			//else we just add to everyone
 			bool is_owner   = member_data->isInRole(gdatap->mOwnerRole);
-			bool can_assign_any = gAgent.hasPowerInGroup(mGroupID,
-												 GP_ROLE_ASSIGN_MEMBER);
-			bool can_assign_limited = gAgent.hasPowerInGroup(mGroupID,
-												 GP_ROLE_ASSIGN_MEMBER_LIMITED);
+			bool can_assign_any = gAgent.hasPowerInGroup(mImplementation->mGroupID,
+				GP_ROLE_ASSIGN_MEMBER);
+			bool can_assign_limited = gAgent.hasPowerInGroup(mImplementation->mGroupID,
+				GP_ROLE_ASSIGN_MEMBER_LIMITED);
 
 			LLGroupMgrGroupData::role_list_t::iterator rit = gdatap->mRoles.begin();
 			LLGroupMgrGroupData::role_list_t::iterator end = gdatap->mRoles.end();
@@ -279,394 +294,18 @@ void LLPanelGroupInvite::impl::addRoleNames(LLGroupMgrGroupData* gdatap)
 					// Owners can add any role.
 					if ( is_owner 
 						// Even 'can_assign_any' can't add owner role.
-						 || (can_assign_any && role_id != gdatap->mOwnerRole)
+						|| (can_assign_any && role_id != gdatap->mOwnerRole)
 						// Add all roles user is in
-						 || (can_assign_limited && member_data->isInRole(role_id))
+						|| (can_assign_limited && member_data->isInRole(role_id))
 						// Everyone role.
-						 || role_id == LLUUID::null )
+						|| role_id == LLUUID::null )
 					{
-							mRoleNames->add(rd.mRoleName,
-											role_id,
-											ADD_BOTTOM);
+						mImplementation->mRoleNames->add(rd.mRoleName,
+							role_id,
+							ADD_BOTTOM);
 					}
 				}
 			}
 		}//end if member data is not null
 	}//end if agent is in the group
 }
-
-//static
-void LLPanelGroupInvite::impl::callbackClickAdd(void* userdata)
-{
-	LLPanelGroupInvite* panelp = (LLPanelGroupInvite*) userdata;
-
-	if ( panelp )
-	{
-		//Right now this is hard coded with some knowledge that it is part
-		//of a floater since the avatar picker needs to be added as a dependent
-		//floater to the parent floater.
-		//Soon the avatar picker will be embedded into this panel
-		//instead of being it's own separate floater.  But that is next week.
-		//This will do for now. -jwolk May 10, 2006
-        LLView * button = panelp->findChild<LLButton>("add_button");
-        LLFloater * root_floater = gFloaterView->getParentFloater(panelp);
-		LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(
-			boost::bind(impl::callbackAddUsers, _1, panelp->mImplementation), TRUE, FALSE, FALSE, root_floater->getName(), button);
-		if (picker)
-		{
-			root_floater->addDependentFloater(picker);
-		}
-	}
-}
-
-//static
-void LLPanelGroupInvite::impl::callbackClickRemove(void* userdata)
-{
-	impl* selfp = (impl*) userdata;
-
-	if ( selfp ) selfp->handleRemove();
-}
-
-void LLPanelGroupInvite::impl::handleRemove()
-{
-	// Check if there is anything selected.
-	std::vector<LLScrollListItem*> selection = 
-			mInvitees->getAllSelected();
-	if (selection.empty()) return;
-
-	// Remove all selected invitees.
-	mInvitees->deleteSelectedItems();
-	mRemoveButton->setEnabled(FALSE);
-}
-
-// static
-void LLPanelGroupInvite::impl::callbackSelect(
-									LLUICtrl* ctrl, void* userdata)
-{
-	impl* selfp = (impl*) userdata;
-	if ( selfp ) selfp->handleSelection();
-}
-
-void LLPanelGroupInvite::impl::handleSelection()
-{
-	// Check if there is anything selected.
-	std::vector<LLScrollListItem*> selection = 
-			mInvitees->getAllSelected();
-	if (selection.empty())
-	{
-		mRemoveButton->setEnabled(FALSE);
-	}
-	else
-	{
-		mRemoveButton->setEnabled(TRUE);
-	}
-}
-
-void LLPanelGroupInvite::impl::callbackClickCancel(void* userdata)
-{
-	impl* selfp = (impl*) userdata;
-
-	if ( selfp ) 
-	{
-		(*(selfp->mCloseCallback))(selfp->mCloseCallbackUserData);
-	}
-}
-
-void LLPanelGroupInvite::impl::callbackClickOK(void* userdata)
-{
-	impl* selfp = (impl*) userdata;
-
-	if ( selfp ) selfp->submitInvitations();
-}
-
-
-
-//static
-void LLPanelGroupInvite::impl::callbackAddUsers(const uuid_vec_t& agent_ids, void* user_data)
-{	
-	std::vector<std::string> names;
-	for (S32 i = 0; i < (S32)agent_ids.size(); i++)
-	{
-		LLAvatarName av_name;
-		if (LLAvatarNameCache::get(agent_ids[i], &av_name))
-		{
-			LLPanelGroupInvite::impl::onAvatarNameCache(agent_ids[i], av_name, user_data);
-		}
-		else 
-		{
-			impl* selfp = (impl*) user_data;
-			if (selfp)
-			{
-				if (selfp->mAvatarNameCacheConnection.connected())
-				{
-					selfp->mAvatarNameCacheConnection.disconnect();
-				}
-				// *TODO : Add a callback per avatar name being fetched.
-				selfp->mAvatarNameCacheConnection = LLAvatarNameCache::get(agent_ids[i],boost::bind(&LLPanelGroupInvite::impl::onAvatarNameCache, _1, _2, user_data));
-			}
-		}
-	}	
-	
-}
-
-void LLPanelGroupInvite::impl::onAvatarNameCache(const LLUUID& agent_id,
-											 const LLAvatarName& av_name,
-											 void* user_data)
-{
-	impl* selfp = (impl*) user_data;
-
-	if (selfp)
-	{
-		if (selfp->mAvatarNameCacheConnection.connected())
-		{
-			selfp->mAvatarNameCacheConnection.disconnect();
-		}
-		std::vector<std::string> names;
-		uuid_vec_t agent_ids;
-		agent_ids.push_back(agent_id);
-		names.push_back(av_name.getCompleteName());
-		
-		selfp->addUsers(names, agent_ids);
-	}
-}
-
-
-LLPanelGroupInvite::LLPanelGroupInvite(const LLUUID& group_id)
-	: LLPanel(),
-	  mImplementation(new impl(group_id)),
-	  mPendingUpdate(FALSE)
-{
-	// Pass on construction of this panel to the control factory.
-	buildFromFile( "panel_group_invite.xml");
-}
-
-LLPanelGroupInvite::~LLPanelGroupInvite()
-{
-	delete mImplementation;
-}
-
-void LLPanelGroupInvite::setCloseCallback(void (*close_callback)(void*),
-										  void* data)
-{
-	mImplementation->mCloseCallback         = close_callback;
-	mImplementation->mCloseCallbackUserData = data;
-}
-
-void LLPanelGroupInvite::clear()
-{
-	mStoreSelected = LLUUID::null;
-	mImplementation->mInvitees->deleteAllItems();
-	mImplementation->mRoleNames->clear();
-	mImplementation->mRoleNames->removeall();
-	mImplementation->mOKButton->setEnabled(FALSE);
-}
-
-void LLPanelGroupInvite::addUsers(uuid_vec_t& agent_ids)
-{
-	std::vector<std::string> names;
-	for (S32 i = 0; i < (S32)agent_ids.size(); i++)
-	{
-		std::string fullname;
-		LLUUID agent_id = agent_ids[i];
-		LLViewerObject* dest = gObjectList.findObject(agent_id);
-		if(dest && dest->isAvatar())
-		{
-			LLNameValue* nvfirst = dest->getNVPair("FirstName");
-			LLNameValue* nvlast = dest->getNVPair("LastName");
-			if(nvfirst && nvlast)
-			{
-				fullname = LLCacheName::buildFullName(
-					nvfirst->getString(), nvlast->getString());
-
-			}
-			if (!fullname.empty())
-			{
-				names.push_back(fullname);
-			} 
-			else 
-			{
-				llwarns << "llPanelGroupInvite: Selected avatar has no name: " << dest->getID() << llendl;
-				names.push_back("(Unknown)");
-			}
-		}
-		else
-		{
-			//looks like user try to invite offline friend
-			//for offline avatar_id gObjectList.findObject() will return null
-			//so we need to do this additional search in avatar tracker, see EXT-4732
-			if (LLAvatarTracker::instance().isBuddy(agent_id))
-			{
-				LLAvatarName av_name;
-				if (!LLAvatarNameCache::get(agent_id, &av_name))
-				{
-					// actually it should happen, just in case
-					//LLAvatarNameCache::get(LLUUID(agent_id), boost::bind(&LLPanelGroupInvite::addUserCallback, this, _1, _2));
-					// for this special case!
-					//when there is no cached name we should remove resident from agent_ids list to avoid breaking of sequence
-					// removed id will be added in callback
-					agent_ids.erase(agent_ids.begin() + i);
-				}
-				else
-				{
-					names.push_back(av_name.getAccountName());
-				}
-			}
-		}
-	}
-	mImplementation->addUsers(names, agent_ids);
-}
-
-void LLPanelGroupInvite::addUserCallback(const LLUUID& id, const LLAvatarName& av_name)
-{
-	std::vector<std::string> names;
-	uuid_vec_t agent_ids;
-	agent_ids.push_back(id);
-	names.push_back(av_name.getAccountName());
-
-	mImplementation->addUsers(names, agent_ids);
-}
-
-void LLPanelGroupInvite::draw()
-{
-	LLPanel::draw();
-	if (mPendingUpdate)
-	{
-		updateLists();
-	}
-}
- 
-void LLPanelGroupInvite::update()
-{
-	mPendingUpdate = FALSE;
-	if (mImplementation->mGroupName) 
-	{
-		mImplementation->mGroupName->setText(mImplementation->mLoadingText);
-	}
-	if ( mImplementation->mRoleNames ) 
-	{
-		mStoreSelected = mImplementation->mRoleNames->getCurrentID();
-		mImplementation->mRoleNames->clear();
-		mImplementation->mRoleNames->removeall();
-		mImplementation->mRoleNames->add(mImplementation->mLoadingText, LLUUID::null, ADD_BOTTOM);
-		mImplementation->mRoleNames->setCurrentByID(LLUUID::null);
-	}
-
-	updateLists();
-}
-
-void LLPanelGroupInvite::updateLists()
-{
-	LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mImplementation->mGroupID);
-	bool waiting = false;
-
-	if (gdatap) 
-	{
-		if (gdatap->isGroupPropertiesDataComplete()) 
-		{
-			if (mImplementation->mGroupName) 
-			{
-				mImplementation->mGroupName->setText(gdatap->mName);
-			}
-		} 
-		else 
-		{
-			waiting = true;
-		}
-		if (gdatap->isRoleDataComplete() && gdatap->isMemberDataComplete()) 
-		{
-			if ( mImplementation->mRoleNames )
-			{
-				mImplementation->mRoleNames->clear();
-				mImplementation->mRoleNames->removeall();
-
-				//add the role names and select the everybody role by default
-				mImplementation->addRoleNames(gdatap);
-				mImplementation->mRoleNames->setCurrentByID(mStoreSelected);
-			}
-		} 
-		else 
-		{
-			waiting = true;
-		}
-	} 
-	else 
-	{
-		waiting = true;
-	}
-
-	if (waiting) 
-	{
-		if (!mPendingUpdate) 
-		{
-			LLGroupMgr::getInstance()->sendGroupPropertiesRequest(mImplementation->mGroupID);
-			LLGroupMgr::getInstance()->sendGroupRoleDataRequest(mImplementation->mGroupID);
-			LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mImplementation->mGroupID);
-		}
-		mPendingUpdate = TRUE;
-	} 
-	else
-	{
-		mPendingUpdate = FALSE;
-		if (mImplementation->mOKButton && mImplementation->mRoleNames->getItemCount()) 
-		{
-			mImplementation->mOKButton->setEnabled(TRUE);
-		}
-	}
-}
-
-BOOL LLPanelGroupInvite::postBuild()
-{
-	BOOL recurse = TRUE;
-
-	mImplementation->mLoadingText = getString("loading");
-	mImplementation->mRoleNames = getChild<LLComboBox>("role_name",
-															   recurse);
-	mImplementation->mGroupName = getChild<LLTextBox>("group_name_text", recurse);
-	mImplementation->mInvitees = 
-		getChild<LLNameListCtrl>("invitee_list", recurse);
-	if ( mImplementation->mInvitees )
-	{
-		mImplementation->mInvitees->setCommitOnSelectionChange(TRUE);
-		mImplementation->mInvitees->setCommitCallback(impl::callbackSelect, mImplementation);
-	}
-
-	LLButton* button = getChild<LLButton>("add_button", recurse);
-	if ( button )
-	{
-		// default to opening avatarpicker automatically
-		// (*impl::callbackClickAdd)((void*)this);
-		button->setClickedCallback(impl::callbackClickAdd, this);
-	}
-
-	mImplementation->mRemoveButton = 
-			getChild<LLButton>("remove_button", recurse);
-	if ( mImplementation->mRemoveButton )
-	{
-		mImplementation->mRemoveButton->setClickedCallback(impl::callbackClickRemove, mImplementation);
-		mImplementation->mRemoveButton->setEnabled(FALSE);
-	}
-
-	mImplementation->mOKButton = 
-		getChild<LLButton>("ok_button", recurse);
-	if ( mImplementation->mOKButton )
- 	{
-		mImplementation->mOKButton->setClickedCallback(impl::callbackClickOK, mImplementation);
-		mImplementation->mOKButton->setEnabled(FALSE);
- 	}
-
-	button = getChild<LLButton>("cancel_button", recurse);
-	if ( button )
-	{
-		button->setClickedCallback(impl::callbackClickCancel, mImplementation);
-	}
-
-	mImplementation->mOwnerWarning = getString("confirm_invite_owner_str");
-	mImplementation->mAlreadyInGroup = getString("already_in_group");
-	mImplementation->mTooManySelected = getString("invite_selection_too_large");
-
-	update();
-	
-	return (mImplementation->mRoleNames &&
-			mImplementation->mInvitees &&
-			mImplementation->mRemoveButton);
-}
diff --git a/indra/newview/llpanelgroupinvite.h b/indra/newview/llpanelgroupinvite.h
index 9f7b5ae9be1..b87a5883b8c 100755
--- a/indra/newview/llpanelgroupinvite.h
+++ b/indra/newview/llpanelgroupinvite.h
@@ -27,36 +27,27 @@
 #define LL_LLPANELGROUPINVITE_H
 
 #include "llpanel.h"
+#include "llpanelgroupbulk.h"
 #include "lluuid.h"
 
 class LLAvatarName;
 
-class LLPanelGroupInvite
-: public LLPanel
+class LLPanelGroupInvite : public LLPanelGroupBulk
 {
 public:
 	LLPanelGroupInvite(const LLUUID& group_id);
-	~LLPanelGroupInvite();
+	~LLPanelGroupInvite() {};
 	
-	void addUsers(uuid_vec_t& agent_ids);
-	/**
-	 * this callback is being used to add a user whose fullname isn't been loaded before invoking of addUsers().
-	 */  
-	void addUserCallback(const LLUUID& id, const LLAvatarName& av_name);
-	void clear();
-	void update();
+	virtual void clear();
+	virtual void update();
 
-	void setCloseCallback(void (*close_callback)(void*), void* data);
-
-	virtual void draw();
 	virtual BOOL postBuild();
-protected:
-	class impl;
-	impl* mImplementation;
 
-	BOOL mPendingUpdate;
-	LLUUID mStoreSelected;
-	void updateLists();
+	static void callbackClickSubmit(void* userdata);
+	virtual void submit();
+
+private:
+	void addRoleNames();
 };
 
 #endif
diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp
index cfdac11d26b..8d7b3c38eea 100755
--- a/indra/newview/llpanelgrouproles.cpp
+++ b/indra/newview/llpanelgrouproles.cpp
@@ -32,6 +32,7 @@
 #include "llavatarnamecache.h"
 #include "llbutton.h"
 #include "llfiltereditor.h"
+#include "llfloatergroupbulkban.h"
 #include "llfloatergroupinvite.h"
 #include "llavataractions.h"
 #include "lliconctrl.h"
@@ -109,8 +110,9 @@ bool agentCanAddToRole(const LLUUID& group_id,
 	return false;
 }
 
-// static
+// LLPanelGroupRoles /////////////////////////////////////////////////////
 
+// static
 LLPanelGroupRoles::LLPanelGroupRoles()
 :	LLPanelGroupTab(),
 	mCurrentTab(NULL),
@@ -412,6 +414,35 @@ BOOL LLPanelGroupRoles::hasModal()
 	return panelp->hasModal();
 }
 
+// BAKER -- Moved this from all the way at the bottom
+void LLPanelGroupRoles::setGroupID(const LLUUID& id)
+{
+	LLPanelGroupTab::setGroupID(id);
+
+	LLPanelGroupMembersSubTab* group_members_tab = findChild<LLPanelGroupMembersSubTab>("members_sub_tab");
+	LLPanelGroupRolesSubTab*  group_roles_tab = findChild<LLPanelGroupRolesSubTab>("roles_sub_tab");
+	LLPanelGroupActionsSubTab* group_actions_tab = findChild<LLPanelGroupActionsSubTab>("actions_sub_tab");
+	LLPanelGroupBanListSubTab* group_ban_tab = findChild<LLPanelGroupBanListSubTab>("banlist_sub_tab");
+
+	if(group_members_tab) group_members_tab->setGroupID(id);
+	if(group_roles_tab) group_roles_tab->setGroupID(id);
+	if(group_actions_tab) group_actions_tab->setGroupID(id);
+	if(group_ban_tab) group_ban_tab->setGroupID(id);
+
+	LLButton* button = getChild<LLButton>("member_invite");
+	if ( button )
+		button->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_MEMBER_INVITE));
+
+	if(mSubTabContainer)
+		mSubTabContainer->selectTab(0);
+
+	activate();
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+
+// LLPanelGroupSubTab ////////////////////////////////////////////////////
 
 ////////////////////////////
 // LLPanelGroupSubTab
@@ -728,11 +759,14 @@ void LLPanelGroupSubTab::setFooterEnabled(BOOL enable)
 	}
 }
 
+//////////////////////////////////////////////////////////////////////////
+
+
+// LLPanelGroupMembersSubTab /////////////////////////////////////////////
+
 ////////////////////////////
 // LLPanelGroupMembersSubTab
 ////////////////////////////
-
-
 static LLRegisterPanelClassWrapper<LLPanelGroupMembersSubTab> t_panel_group_members_subtab("panel_group_members_subtab");
 
 LLPanelGroupMembersSubTab::LLPanelGroupMembersSubTab()
@@ -810,6 +844,14 @@ BOOL LLPanelGroupMembersSubTab::postBuildSubTab(LLView* root)
 		mEjectBtn->setEnabled(FALSE);
 	}
 
+	mBanBtn = parent->getChild<LLButton>("member_ban", recurse);
+	if(mBanBtn)
+	{
+		mBanBtn->setClickedCallback(onBanMember, this);
+		mBanBtn->setEnabled(FALSE);
+	}
+
+
 	return TRUE;
 }
 
@@ -823,34 +865,6 @@ void LLPanelGroupMembersSubTab::setGroupID(const LLUUID& id)
 	LLPanelGroupSubTab::setGroupID(id);
 }
 
-void LLPanelGroupRolesSubTab::setGroupID(const LLUUID& id)
-{
-	if(mRolesList) mRolesList->deleteAllItems();
-	if(mAssignedMembersList) mAssignedMembersList->deleteAllItems();
-	if(mAllowedActionsList) mAllowedActionsList->deleteAllItems();
-
-	if(mRoleName) mRoleName->clear();
-	if(mRoleDescription) mRoleDescription->clear();
-	if(mRoleTitle) mRoleTitle->clear();
-
-	mHasRoleChange = FALSE;
-
-	setFooterEnabled(FALSE);
-
-	LLPanelGroupSubTab::setGroupID(id);
-}
-void LLPanelGroupActionsSubTab::setGroupID(const LLUUID& id)
-{
-	if(mActionList) mActionList->deleteAllItems();
-	if(mActionRoles) mActionRoles->deleteAllItems();
-	if(mActionMembers) mActionMembers->deleteAllItems();
-
-	if(mActionDescription) mActionDescription->clear();
-
-	LLPanelGroupSubTab::setGroupID(id);
-}
-
-
 // static
 void LLPanelGroupMembersSubTab::onMemberSelect(LLUICtrl* ctrl, void* user_data)
 {
@@ -916,6 +930,12 @@ void LLPanelGroupMembersSubTab::handleMemberSelect()
 	LLGroupMgrGroupData::role_list_t::iterator iter = gdatap->mRoles.begin();
 	LLGroupMgrGroupData::role_list_t::iterator end  = gdatap->mRoles.end();
 
+	//////////////////////////////////////////////////////////////////////////
+	// BAKER STUB:
+	// Check if the member has the power to ban (just like the eject below)
+	// Right now, just give it to them (for testing)
+	BOOL can_ban_members = gAgent.hasPowerInGroup(mGroupID,	GP_GROUP_BAN_ACCESS);
+	//////////////////////////////////////////////////////////////////////////
 	BOOL can_eject_members = gAgent.hasPowerInGroup(mGroupID,
 													GP_MEMBER_EJECT);
 	BOOL member_is_owner = FALSE;
@@ -986,6 +1006,7 @@ void LLPanelGroupMembersSubTab::handleMemberSelect()
 			if (role_id.notNull() && (count > 0))
 			{
 				can_eject_members = FALSE;
+				can_ban_members = FALSE;
 				if (role_id == gdatap->mOwnerRole)
 				{
 					member_is_owner = TRUE;
@@ -1047,7 +1068,10 @@ void LLPanelGroupMembersSubTab::handleMemberSelect()
 	mAssignedRolesList->setEnabled(TRUE);
 
 	if (gAgent.isGodlike())
+	{
 		can_eject_members = TRUE;
+		can_ban_members = TRUE;
+	}
 
 	if (!can_eject_members && !member_is_owner)
 	{
@@ -1060,10 +1084,12 @@ void LLPanelGroupMembersSubTab::handleMemberSelect()
 			if ( member_data && member_data->isInRole(gdatap->mOwnerRole) )
 			{
 				can_eject_members = TRUE;
+				can_ban_members = TRUE;
 			}
 		}
 	}
 
+	mBanBtn->setEnabled(can_ban_members);
 	mEjectBtn->setEnabled(can_eject_members);
 }
 
@@ -1231,7 +1257,6 @@ void LLPanelGroupMembersSubTab::handleRoleCheck(const LLUUID& role_id,
 					 FALSE);
 }
 
-
 // static 
 void LLPanelGroupMembersSubTab::onRoleCheck(LLUICtrl* ctrl, void* user_data)
 {
@@ -1653,7 +1678,7 @@ void LLPanelGroupMembersSubTab::updateMembers()
 		return;
 	}
 
-	//cleanup list only for first iretation
+	//cleanup list only for first iteration
 	if(mMemberProgress == gdatap->mMembers.begin())
 	{
 		mMembersList->deleteAllItems();
@@ -1712,12 +1737,55 @@ void LLPanelGroupMembersSubTab::updateMembers()
 	handleMemberSelect();
 }
 
+// BAKER
+void LLPanelGroupMembersSubTab::onBanMember(void* user_data)
+{
+	LLPanelGroupMembersSubTab* self = static_cast<LLPanelGroupMembersSubTab*>(user_data);
+	self->handleBanMember();
+}
 
+void LLPanelGroupMembersSubTab::handleBanMember()
+{
+	LL_INFOS("BAKER") << "[BAKER] LLPanelGroupMembersSubTab::handleBanMember()" << LL_ENDL;
+
+	LLGroupMgrGroupData* gdatap	= LLGroupMgr::getInstance()->getGroupData(mGroupID);
+	if(!gdatap) 
+	{
+		llwarns << "LLPanelGroupMembersSubTab::handleMemberSelect() "
+			<< "-- No group data!" << llendl;
+		return;
+	}
+
+	std::vector<LLScrollListItem*> selection = mMembersList->getAllSelected();
+	if(selection.empty())
+	{
+		LL_WARNS("BAKER") << "[BAKER] Empty selection!" << LL_ENDL;
+		return;
+	}
+
+	std::vector<LLScrollListItem*>::iterator itor;
+	for(itor = selection.begin(); itor != selection.end(); ++itor)
+	{
+		LLUUID ban_id = (*itor)->getUUID();
+		LLGroupBanData ban_data;
+
+		// DEL to People API somewhere in this chain...
+		gdatap->createBanEntry(ban_id, ban_data);
+		mMembersList->removeNameItem(ban_id);
+
+	}	
+
+
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+
+// LLPanelGroupRolesSubTab ///////////////////////////////////////////////
 
 ////////////////////////////
 // LLPanelGroupRolesSubTab
 ////////////////////////////
-
 static LLRegisterPanelClassWrapper<LLPanelGroupRolesSubTab> t_panel_group_roles_subtab("panel_group_roles_subtab");
 
 LLPanelGroupRolesSubTab::LLPanelGroupRolesSubTab()
@@ -1962,7 +2030,7 @@ void LLPanelGroupRolesSubTab::update(LLGroupChange gc)
 		mRolesList->sortByColumn(std::string("name"), TRUE);
 
 		if ( (gdatap->mRoles.size() < (U32)MAX_ROLES)
-			&& gAgent.hasPowerInGroup(mGroupID, GP_ROLE_CREATE) )
+			&& gAgent.hasPowerInGroup(mGroupID, GP_GROUP_BAN_ACCESS) )
 		{
 			mCreateRoleButton->setEnabled(TRUE);
 		}
@@ -2010,6 +2078,9 @@ void LLPanelGroupRolesSubTab::update(LLGroupChange gc)
 void LLPanelGroupRolesSubTab::onRoleSelect(LLUICtrl* ctrl, void* user_data)
 {
 	LLPanelGroupRolesSubTab* self = static_cast<LLPanelGroupRolesSubTab*>(user_data);
+	if (!self) 
+		return;
+
 	self->handleRoleSelect();
 }
 
@@ -2250,7 +2321,6 @@ bool LLPanelGroupRolesSubTab::addActionCB(const LLSD& notification, const LLSD&
 	return false;
 }
 
-
 // static
 void LLPanelGroupRolesSubTab::onPropertiesKey(LLLineEditor* ctrl, void* user_data)
 {
@@ -2428,13 +2498,34 @@ void LLPanelGroupRolesSubTab::saveRoleChanges(bool select_saved_role)
 		mHasRoleChange = FALSE;
 	}
 }
+
+void LLPanelGroupRolesSubTab::setGroupID(const LLUUID& id)
+{
+	if(mRolesList) mRolesList->deleteAllItems();
+	if(mAssignedMembersList) mAssignedMembersList->deleteAllItems();
+	if(mAllowedActionsList) mAllowedActionsList->deleteAllItems();
+
+	if(mRoleName) mRoleName->clear();
+	if(mRoleDescription) mRoleDescription->clear();
+	if(mRoleTitle) mRoleTitle->clear();
+
+	mHasRoleChange = FALSE;
+
+	setFooterEnabled(FALSE);
+
+	LLPanelGroupSubTab::setGroupID(id);
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+
+// LLPanelGroupActionsSubTab /////////////////////////////////////////////
+
 ////////////////////////////
 // LLPanelGroupActionsSubTab
 ////////////////////////////
-
 static LLRegisterPanelClassWrapper<LLPanelGroupActionsSubTab> t_panel_group_actions_subtab("panel_group_actions_subtab");
 
-
 LLPanelGroupActionsSubTab::LLPanelGroupActionsSubTab()
 : LLPanelGroupSubTab()
 {
@@ -2607,26 +2698,343 @@ void LLPanelGroupActionsSubTab::handleActionSelect()
 	}
 }
 
-void LLPanelGroupRoles::setGroupID(const LLUUID& id)
+void LLPanelGroupActionsSubTab::setGroupID(const LLUUID& id)
 {
-	LLPanelGroupTab::setGroupID(id);
+	if(mActionList) mActionList->deleteAllItems();
+	if(mActionRoles) mActionRoles->deleteAllItems();
+	if(mActionMembers) mActionMembers->deleteAllItems();
+
+	if(mActionDescription) mActionDescription->clear();
+
+	LLPanelGroupSubTab::setGroupID(id);
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////
+// LLPanelGroupBanListSubTab
+////////////////////////////
+static LLRegisterPanelClassWrapper<LLPanelGroupBanListSubTab> t_panel_group_ban_subtab("panel_group_banlist_subtab");
+
+LLPanelGroupBanListSubTab::LLPanelGroupBanListSubTab()
+	: LLPanelGroupSubTab(),
+	  mBanList(NULL),
+	  mCreateBanButton(NULL),
+	  mDeleteBanButton(NULL),
+	  mUpdateBanList(true)
+{
+	LL_INFOS("BAKER") << "[BAKER] LLPanelGroupBanListSubTab::ctor()" << LL_ENDL;
+}
+
+BOOL LLPanelGroupBanListSubTab::postBuildSubTab(LLView* root)
+{
+	LL_INFOS("BAKER") << "[BAKER] LLPanelGroupBanListSubTab::postBuildSubTab()" << LL_ENDL;
+
+	LLPanelGroupSubTab::postBuildSubTab(root);
+
+	// Upcast parent so we can ask it for sibling controls.
+	LLPanelGroupRoles* parent = (LLPanelGroupRoles*)root;
+
+	// Look recursively from the parent to find all our widgets.
+	bool recurse = true;
 	
-	LLPanelGroupMembersSubTab* group_members_tab = findChild<LLPanelGroupMembersSubTab>("members_sub_tab");
-	LLPanelGroupRolesSubTab*  group_roles_tab = findChild<LLPanelGroupRolesSubTab>("roles_sub_tab");
-	LLPanelGroupActionsSubTab* group_actions_tab = findChild<LLPanelGroupActionsSubTab>("actions_sub_tab");
+	// BAKER TODO:
+	//	What are these? Looks like something inhereted from LLPanelGroupSubTab
+	mHeader = parent->getChild<LLPanel>("banlist_header", recurse);
+	mFooter = parent->getChild<LLPanel>("banlist_footer", recurse);
+	//////////////////////////////////////////////////////////////////////////
+	
+	mBanList = parent->getChild<LLNameListCtrl>("ban_list", recurse);
+	mCreateBanButton = parent->getChild<LLButton>("ban_create", recurse);
+	mDeleteBanButton = parent->getChild<LLButton>("ban_delete", recurse);
 
-	if(group_members_tab) group_members_tab->setGroupID(id);
-	if(group_roles_tab) group_roles_tab->setGroupID(id);
-	if(group_actions_tab) group_actions_tab->setGroupID(id);
+	if(!mBanList || !mCreateBanButton || !mDeleteBanButton)
+		return FALSE;
 
-	LLButton* button = getChild<LLButton>("member_invite");
-	if ( button )
-		button->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_MEMBER_INVITE));
+	mBanList->setCommitOnSelectionChange(TRUE);
+	mBanList->setCommitCallback(onBanEntrySelect, this);
 
-	if(mSubTabContainer)
-		mSubTabContainer->selectTab(0);
+	mCreateBanButton->setClickedCallback(onCreateBanEntry, this);
+	mCreateBanButton->setEnabled(FALSE);
+	
+	mDeleteBanButton->setClickedCallback(onDeleteBanEntry, this);
+	mDeleteBanButton->setEnabled(FALSE);
+	
+	setFooterEnabled(FALSE);
 
-	activate();
+	return TRUE;
+}
+
+void LLPanelGroupBanListSubTab::activate()
+{
+	LL_INFOS("BAKER") << "[BAKER] LLPanelGroupBanListSubTab::activate()" << LL_ENDL;
+	
+	LLPanelGroupSubTab::activate();
+
+	mBanList->deselectAllItems();
+	mDeleteBanButton->setEnabled(FALSE);
+
+	setFooterEnabled(FALSE);
+	update(GC_ALL);
+}
+
+void LLPanelGroupBanListSubTab::deactivate()
+{
+	LL_INFOS("BAKER") << "[BAKER] LLPanelGroupBanListSubTab::deactivate()" << LL_ENDL;
+	
+	LLPanelGroupSubTab::deactivate();
+}
+
+bool LLPanelGroupBanListSubTab::needsApply(std::string& mesg)
+{
+	LL_INFOS("BAKER") << "LLPanelGroupBanListSubTab::needsApply()" << LL_ENDL;
+
+	// STUB
+	return false;
+}
+
+bool LLPanelGroupBanListSubTab::apply(std::string& mesg)
+{
+	LL_INFOS("BAKER") << "[BAKER] LLPanelGroupBanListSubTab::apply()" << LL_ENDL;
+	
+	
+
+
+	// STUB
+	return true;
 }
 
+void LLPanelGroupBanListSubTab::update(LLGroupChange gc)
+{
+	LL_INFOS("BAKER") << "[BAKER] LLPanelGroupBanListSubTab::update()" << LL_ENDL;
+
+	if (gc != GC_ALL || gc != GC_BANLIST)
+		return;
+
+	LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
+	if(!gdatap) 
+	{
+		LL_INFOS("BAKER") << "[BAKER] No group data!" << LL_ENDL;
+		return;
+	}
+
+	switch(gdatap->getGroupBanStatus())
+	{
+	// Must be initial update [	Check if I should request this at panel creation
+	//							with everything else -- might as well]
+	// Request our ban list!
+	case LLGroupMgrGroupData::STATUS_INIT:
+		LLGroupMgr::getInstance()->sendGroupBanRequest(LLGroupMgr::REQUEST_GET, mGroupID);
+		break;
+	
+	// Already have a request out -- don't bother sending another one 
+	// Repeat sending won't make a difference, as it'll be behind a load balancer
+	case LLGroupMgrGroupData::STATUS_REQUESTING:
+		break;
+	
+	//	See if the list needs updating -- if we call update, but nothing changed,
+	//	there's no reason to send another request.
+	//		[NOTHING CHANGED]	- Do Nothing!
+	//		[SOMETHING CHANGED]	- Don't panic! Just repopulate the ban list! 
+	case LLGroupMgrGroupData::STATUS_COMPLETE:
+		populateBanList();
+
+		
+		break;
+	}
+}
+
+
+void LLPanelGroupBanListSubTab::populateBanList()
+{
+	//if(gdatap->getGroupBanStatus() == )
+
+
+
+// 	mBanList->deleteAllItems();
+// 	LLGroupMgr::getInstance()->sendGroupBanRequest(LLGroupMgr::REQUEST_GET, mGroupID);
+// 	
+// 	std::map<LLUUID,LLGroupBanData>::const_iterator entry = gdatap->mBanList.begin();
+// 	for(; entry != gdatap->mBanList.end(); entry++)
+// 	{
+// 		LLNameListCtrl::NameItem ban_entry;
+// 		ban_entry.value = entry->first;
+// 		mBanList->addNameItemRow(ban_entry);
+// 	}
+	
+	mUpdateBanList = false;
+}
+
+
+void LLPanelGroupBanListSubTab::onBanEntrySelect(LLUICtrl* ctrl, void* user_data)
+{
+	LL_INFOS("BAKER") << "[BAKER] LLPanelGroupBanListSubTab::onBanEntrySelect()" << LL_ENDL;
+	
+	LLPanelGroupBanListSubTab* self = static_cast<LLPanelGroupBanListSubTab*>(user_data);
+	if (!self) 
+		return;
+
+	self->handleBanEntrySelect();
+}
+
+void LLPanelGroupBanListSubTab::handleBanEntrySelect()
+{
+	LL_INFOS("BAKER") << "[BAKER] LLPanelGroupBanListSubTab::handleBanEntrySelect()" << LL_ENDL;
+
+	// BAKER TODO: -- MOVE TO SELECT BAN ENTRY
+	// Make sure only authorized people have access to adding / deleting bans
+	//if (gAgent.hasPowerInGroup(mGroupID, GP_GROUP_BAN_ACCESS))
+	mCreateBanButton->setEnabled(TRUE);
+
+	// Check if the agent has the ability to unban this person
+	//if (gAgent.hasPowerInGroup(mGroupID, GP_GROUP_BAN_ACCESS))
+		mDeleteBanButton->setEnabled(TRUE);
+}
+
+
+void LLPanelGroupBanListSubTab::onBanGroupMember(void* user_data)
+{
+	LL_INFOS("BAKER") << "[BAKER] LLPanelGroupBanListSubTab::onBanGroupMember()" << LL_ENDL;
+
+	LLPanelGroupBanListSubTab* self = static_cast<LLPanelGroupBanListSubTab*>(user_data);
+	if (!self) 
+		return;
+
+	self->handleBanGroupMember();
+}
+
+void LLPanelGroupBanListSubTab::handleBanGroupMember()
+{
+	LL_INFOS("BAKER") << "[BAKER] LLPanelGroupBanListSubTab::handleBanGroupMember()" << LL_ENDL;
+
+	//////////////////////////////////////////////////////////////////////////
+	// BAKER TEMP
+	//	Getting viewer functionality working, so I'm gonna cheat a bit here for now
+	//	Assume everything worked on the back end
+	//	
+	//	First, get the entries added to the ban list
+	//////////////////////////////////////////////////////////////////////////
+	
+}
+
+
+void LLPanelGroupBanListSubTab::onCreateBanEntry(void* user_data)
+{
+	LL_INFOS("BAKER") << "[BAKER] LLPanelGroupBanListSubTab::onCreateBanEntry()" << LL_ENDL;
+	
+	LLPanelGroupBanListSubTab* self = static_cast<LLPanelGroupBanListSubTab*>(user_data);
+	if (!self) 
+		return;
+
+	self->handleCreateBanEntry();
+}
+
+void LLPanelGroupBanListSubTab::handleCreateBanEntry()
+{
+	LL_INFOS("BAKER") << "[BAKER] LLPanelGroupBanListSubTab::handleCreateBanEntry()" << LL_ENDL;
+	
+	// STUB
+	// Attempt to add an entry into the database
+	// If there was a problem, don't add the entry to the local list
+	// Otherwise, add it
+	// 
+	// For now, let's just add it to the local list for testing.  We can hook it up
+	// at the end.
+
+
+	LLFloaterGroupBulkBan::showForGroup(mGroupID);
+
+}
+
+
+void LLPanelGroupBanListSubTab::onDeleteBanEntry(void* user_data)
+{
+	LL_INFOS("BAKER") << "[BAKER] LLPanelGroupBanListSubTab::onDeleteBanEntry()" << LL_ENDL;
+	
+	LLPanelGroupBanListSubTab* self = static_cast<LLPanelGroupBanListSubTab*>(user_data);
+	if (!self) 
+		return;
+
+	self->handleDeleteBanEntry();
+}
+
+void LLPanelGroupBanListSubTab::handleDeleteBanEntry()
+{
+	LL_INFOS("BAKER") << "[BAKER] LLPanelGroupBanListSubTab::handleDeleteBanEntry()" << LL_ENDL;
+	
+	LLGroupMgrGroupData* gdatap	= LLGroupMgr::getInstance()->getGroupData(mGroupID);
+	if(!gdatap) 
+	{
+		llwarns << "LLPanelGroupMembersSubTab::handleMemberSelect() "
+			<< "-- No group data!" << llendl;
+		return;
+	}
+
+	std::vector<LLScrollListItem*> selection = mBanList->getAllSelected();
+	if(selection.empty())
+	{
+		LL_WARNS("BAKER") << "[BAKER] Empty selection!" << LL_ENDL;
+		return;
+	}
+
+	//////////////////////////////////////////////////////////////////////////
+	// BAKER STUB:
+	// Check if the member has the power to ban (just like the eject below)
+	bool can_ban_members = false;
+	if (gAgent.isGodlike() ||
+		gAgent.hasPowerInGroup(mGroupID, GP_GROUP_BAN_ACCESS))
+	{
+		can_ban_members	= true;
+	}
+	//////////////////////////////////////////////////////////////////////////
+	
+	// Owners can ban anyone in the group.
+	LLGroupMgrGroupData::member_list_t::iterator mi = gdatap->mMembers.find(gAgent.getID());
+	if (mi != gdatap->mMembers.end())
+	{
+		LLGroupMemberData* member_data = (*mi).second;
+		if ( member_data && member_data->isInRole(gdatap->mOwnerRole) )
+		{
+			can_ban_members	= true;
+		}
+	}
+
+	std::vector<LLScrollListItem*>::iterator itor;
+	for(itor = selection.begin(); itor != selection.end(); ++itor)
+	{
+		// STUB
+		// Attempt to remove entry from the database
+		// If there was a problem with the delete, don't remove it from the list yet!
+		// Otherwise, remove it from our local list.
+		// 
+
+		LLUUID ban_id = (*itor)->getUUID();
+		if(gdatap->removeBanEntry(ban_id))
+		{
+			mBanList->removeNameItem(ban_id);
+			// Removing an item removes the selection, we shouldn't be able to click
+			// the button anymore until we reselect another entry.
+			mDeleteBanButton->setEnabled(FALSE);
+		}
+	}
+	
+
+}
+
+
+void LLPanelGroupBanListSubTab::setGroupID(const LLUUID& id)
+{
+	LL_INFOS("BAKER") << "[BAKER] LLPanelGroupBanListSubTab::setGroupID()" << LL_ENDL;
+	
+	if(mBanList)
+		mBanList->deleteAllItems();
+
+	setFooterEnabled(FALSE);
+	LLPanelGroupSubTab::setGroupID(id);
+}
+
+
+
+
 
diff --git a/indra/newview/llpanelgrouproles.h b/indra/newview/llpanelgrouproles.h
index 78bb3c57a16..1695097fc5a 100755
--- a/indra/newview/llpanelgrouproles.h
+++ b/indra/newview/llpanelgrouproles.h
@@ -172,6 +172,10 @@ class LLPanelGroupMembersSubTab : public LLPanelGroupSubTab
 	void handleRoleCheck(const LLUUID& role_id,
 						 LLRoleMemberChangeType type);
 
+	static void onBanMember(void* user_data);
+	void handleBanMember();
+
+
 	void applyMemberChanges();
 	bool addOwnerCB(const LLSD& notification, const LLSD& response);
 
@@ -205,6 +209,7 @@ class LLPanelGroupMembersSubTab : public LLPanelGroupSubTab
 	LLScrollListCtrl*	mAssignedRolesList;
 	LLScrollListCtrl*	mAllowedActionsList;
 	LLButton*           mEjectBtn;
+	LLButton*			mBanBtn;
 
 	BOOL mChanged;
 	BOOL mPendingMemberUpdate;
@@ -305,5 +310,57 @@ class LLPanelGroupActionsSubTab : public LLPanelGroupSubTab
 	LLTextEditor*	mActionDescription;
 };
 
+class LLPanelGroupBanListSubTab : public LLPanelGroupSubTab
+{
+public:
+	LLPanelGroupBanListSubTab();
+	virtual ~LLPanelGroupBanListSubTab() {}
+
+	virtual BOOL postBuildSubTab(LLView* root);
+
+	// Triggered when the tab becomes active.
+	virtual void activate();
+	
+	// Triggered when the tab becomes inactive.
+	virtual void deactivate();
+	
+	// Asks if something needs to be applied.
+	// If returning true, this function should modify the message to the user.
+	virtual bool needsApply(std::string& mesg);
+	
+	// Request to apply current data.
+	// If returning fail, this function should modify the message to the user.
+	virtual bool apply(std::string& mesg);
+	
+	// Triggered when group information changes in the group manager.
+	virtual void update(LLGroupChange gc);
+
+
+	static void onBanEntrySelect(LLUICtrl* ctrl, void* user_data);
+	void handleBanEntrySelect();
+
+	static void onBanGroupMember(void* user_data);
+	void handleBanGroupMember();
+	
+	static void onCreateBanEntry(void* user_data);
+	void handleCreateBanEntry();
+	
+	static void onDeleteBanEntry(void* user_data);
+	void handleDeleteBanEntry();
+	
+	virtual void setGroupID(const LLUUID& id);
+
+protected:
+	void populateBanList();
+
+
+protected:
+	LLNameListCtrl* mBanList;
+	LLButton* mCreateBanButton;
+	LLButton* mDeleteBanButton;
+
+	bool mUpdateBanList;
+	
+};
 
 #endif // LL_LLPANELGROUPROLES_H
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index 8422708addd..cfbfc983fbf 100755
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -1600,6 +1600,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
 	capabilityNames.append("GetObjectCost");
 	capabilityNames.append("GetObjectPhysicsData");
 	capabilityNames.append("GetTexture");
+	capabilityNames.append("GroupBan");
 	capabilityNames.append("GroupMemberData");
 	capabilityNames.append("GroupProposalBallot");
 	capabilityNames.append("HomeLocation");
diff --git a/indra/newview/skins/default/xui/en/panel_group_bulk_ban.xml b/indra/newview/skins/default/xui/en/panel_group_bulk_ban.xml
new file mode 100644
index 00000000000..3baed595da7
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_group_bulk_ban.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ height="330"
+ label="Ban Residents"
+ layout="topleft"
+ left="0"
+ name="bulk_ban_panel"
+ top="330"
+ width="210">
+  <panel.string
+   name="loading">
+    (loading...)
+  </panel.string>
+  <panel.string
+     name="ban_selection_too_large">
+    Group Invitations not sent: too many Residents selected. Group Invitations are limited to 100 per request.
+  </panel.string>
+  <text
+   type="string"
+   length="1"
+   height="54"
+   layout="topleft"
+   left="7"
+   name="help_text"
+   top="28"
+   word_wrap="true"
+   width="200">
+    You can select multiple Residents to ban from your group. Click &apos;Open Resident Chooser&apos; to start.
+  </text>
+  <button
+   height="20"
+   label="Open Resident Chooser"
+   layout="topleft"
+   left_delta="-2"
+   name="add_button"
+   top_delta="44"
+   width="200" />
+  <name_list
+   allow_calling_card_drop="true"
+   column_padding="0"
+   height="174"
+   layout="topleft"
+   left_delta="0"
+   multi_select="true"
+   name="banned_agent_list"
+   tool_tip="Hold the Ctrl key and click Resident names to multi-select"
+   top_pad="4"
+   width="200" />
+  <button
+   height="20"
+   label="Remove Selected from List"
+   layout="topleft"
+   left_delta="0"
+   name="remove_button"
+   tool_tip="Removes the Residents selected above from the ban list"
+   top_pad="4"
+   width="200" />
+  <button
+   height="20"
+   label="BAN THIS SICK FILTH"
+   layout="topleft"
+   left="4"
+   name="ban_button"
+   top_delta="30"
+   width="135" />
+  <button
+   height="20"
+   label="Cancel"
+   layout="topleft"
+   left_pad="2"
+   name="cancel_button"
+   top_delta="0"
+   width="65" />
+  <string
+  name="GroupBulkBan">
+    Group Ban
+  </string>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_group_invite.xml b/indra/newview/skins/default/xui/en/panel_group_invite.xml
index 124c0596c3d..944a496fba3 100755
--- a/indra/newview/skins/default/xui/en/panel_group_invite.xml
+++ b/indra/newview/skins/default/xui/en/panel_group_invite.xml
@@ -88,7 +88,7 @@
      label="Send Invitations"
      layout="topleft"
      left="4"
-     name="ok_button"
+     name="invite_button"
      top="356"
      width="135" />
     <button
diff --git a/indra/newview/skins/default/xui/en/panel_group_roles.xml b/indra/newview/skins/default/xui/en/panel_group_roles.xml
index df91ad8b5e5..9817f70ac74 100755
--- a/indra/newview/skins/default/xui/en/panel_group_roles.xml
+++ b/indra/newview/skins/default/xui/en/panel_group_roles.xml
@@ -114,6 +114,13 @@ clicking on their names.
              left_pad="10"
              name="member_eject"
              width="100" />
+            <button
+             height="23"
+             label="Ban"
+             follows="top|left"
+             left_pad="10"
+             name="member_ban"
+             width="100" />
         </panel>
         <panel
          border="false"
@@ -277,6 +284,72 @@ things in this group. There&apos;s a broad variety of Abilities.
              width="270" />
         </scroll_list>
         </panel>
+        <panel
+         border="false"
+         height="303"
+         label="BANNED AGENTS"
+         layout="topleft"
+         left="0"
+         right="-1"
+         help_topic="roles_banlist_tab"
+         name="banlist_sub_tab"
+         class="panel_group_banlist_subtab"
+         tool_tip="View the banned agents from this group."
+         width="310">
+        <panel.string
+         name="help_text">
+            Any resident on the ban list will be unable to join the group.
+        </panel.string>
+        <filter_editor
+        layout="topleft"
+        top="5"
+        left="5"
+        right="-5"
+        height="22"
+        search_button_visible="false"
+        follows="left|top|right"
+        label="Filter Bans"
+        name="filter_bans" />
+          <name_list
+               column_padding="0"
+               draw_heading="true"
+               height="240"
+               follows="left|top|right"
+               layout="topleft"
+               left="0"
+               right="-1"
+               multi_select="true"
+               name="ban_list"
+               short_names="false"
+               top_pad="5">
+            <name_list.columns
+             label="Resident"
+             name="name"
+             font.name="SANSSERIF_SMALL"
+             font.style="NORMAL"
+           relative_width="0.65" />
+            <name_list.columns
+             label="Date Banned"
+             name="date_banned"
+     relative_width="0.35" />
+          </name_list>
+		  <button
+            follows="top|left"
+             height="23"
+             label="Ban!"
+             layout="topleft"
+             left="3"
+             name="ban_create"
+             width="120" />
+            <button
+             height="23"
+             follows="top|left"
+             label="Unban!"
+             layout="topleft"
+             left_pad="10"
+             name="ban_delete"
+             width="120" />
+      </panel>
     </tab_container>
     <panel
      height="350"
diff --git a/indra/newview/skins/default/xui/en/role_actions.xml b/indra/newview/skins/default/xui/en/role_actions.xml
index 89aef57ccae..79789fcd7b5 100755
--- a/indra/newview/skins/default/xui/en/role_actions.xml
+++ b/indra/newview/skins/default/xui/en/role_actions.xml
@@ -1,5 +1,12 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
 <role_actions>
+  <action_set
+      description="These Abilities include powers to ban and un-ban residents from the group."
+      name="Bans">
+    <action description="Manage ban list."
+            longdescription="Allows the group member to ban / un-ban Residents from this group."
+            name="allow ban" value="49" />
+  </action_set>
 	<action_set
 	     description="These Abilities include powers to add and remove group Members, and allow new Members to join without an invitation."
 	     name="Membership">
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index f7b33b0a4ad..aa29a887869 100755
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -3672,6 +3672,7 @@ Abuse Report</string>
   <string name="LocalEstimateUSD">US$ [AMOUNT]</string>
 
   <!-- Group Profile roles and powers -->
+  <string name="Group Ban">Group Ban</string>
   <string name="Membership">Membership</string>
   <string name="Roles">Roles</string>
   <string name="Group Identity">Group Identity</string>
-- 
GitLab