summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMerov Linden <merov@lindenlab.com>2014-08-12 20:35:17 -0700
committerMerov Linden <merov@lindenlab.com>2014-08-12 20:35:17 -0700
commit30972fca1d1048a1dbfcb28d58489cd687820ec0 (patch)
tree6291a9f0435024c8f60729c1d11d4b4698e98f96
parent231572cfed6d178658bc3333eb97aff34746cb81 (diff)
parent1346949e4d601e8c945fa30593ba1d405e9ed80d (diff)
Pull merge from lindenlab/viewer-release
-rwxr-xr-x.hgtags1
-rwxr-xr-xindra/llui/lltooltip.cpp3
-rwxr-xr-xindra/newview/CMakeLists.txt7
-rw-r--r--indra/newview/VIEWER_VERSION.txt2
-rwxr-xr-xindra/newview/app_settings/settings.xml20
-rwxr-xr-xindra/newview/llconversationmodel.cpp9
-rw-r--r--indra/newview/llconversationmodel.h2
-rw-r--r--indra/newview/llfloatergroupbulkban.cpp134
-rw-r--r--indra/newview/llfloatergroupbulkban.h48
-rwxr-xr-xindra/newview/llfloaterimcontainer.cpp118
-rwxr-xr-xindra/newview/llfloaterimcontainer.h4
-rwxr-xr-xindra/newview/llgroupactions.cpp5
-rwxr-xr-xindra/newview/llgroupmgr.cpp200
-rwxr-xr-xindra/newview/llgroupmgr.h81
-rwxr-xr-xindra/newview/llnamelistctrl.cpp33
-rwxr-xr-xindra/newview/llnamelistctrl.h13
-rw-r--r--indra/newview/llpanelgroupbulk.cpp421
-rw-r--r--indra/newview/llpanelgroupbulk.h74
-rw-r--r--indra/newview/llpanelgroupbulkban.cpp259
-rw-r--r--indra/newview/llpanelgroupbulkban.h49
-rw-r--r--indra/newview/llpanelgroupbulkimpl.h99
-rwxr-xr-xindra/newview/llpanelgroupinvite.cpp9
-rwxr-xr-xindra/newview/llpanelgrouproles.cpp811
-rwxr-xr-xindra/newview/llpanelgrouproles.h65
-rwxr-xr-xindra/newview/llpanelplaceprofile.cpp5
-rwxr-xr-xindra/newview/llviewerregion.cpp1
-rw-r--r--[-rwxr-xr-x]indra/newview/roles_constants.h102
-rwxr-xr-xindra/newview/skins/default/xui/en/floater_about.xml2
-rwxr-xr-xindra/newview/skins/default/xui/en/menu_conversation.xml9
-rwxr-xr-xindra/newview/skins/default/xui/en/notifications.xml52
-rw-r--r--indra/newview/skins/default/xui/en/panel_group_bulk_ban.xml108
-rwxr-xr-xindra/newview/skins/default/xui/en/panel_group_invite.xml4
-rwxr-xr-xindra/newview/skins/default/xui/en/panel_group_roles.xml946
-rwxr-xr-xindra/newview/skins/default/xui/en/role_actions.xml5
-rwxr-xr-xindra/newview/skins/default/xui/en/strings.xml1
35 files changed, 2966 insertions, 736 deletions
diff --git a/.hgtags b/.hgtags
index 9b426adee4..4d2aec7673 100755
--- a/.hgtags
+++ b/.hgtags
@@ -485,3 +485,4 @@ a9f2d0cb11f73b06858e6083bb50083becc3f9cd 3.7.9-release
91dae9494b4d147541c7a01902334ba19a7ec05e 3.7.10-release
64799eb298834073a3e9992cd8d27c3cb9d30b10 3.7.11-release
3b44ea8988cb902f0dda8429e8d5e4569e304532 3.7.12-release
+d86a7e1bc96d27b683f951d3701d5b7042158c68 3.7.13-release
diff --git a/indra/llui/lltooltip.cpp b/indra/llui/lltooltip.cpp
index 5e1f12996e..7f2224870d 100755
--- a/indra/llui/lltooltip.cpp
+++ b/indra/llui/lltooltip.cpp
@@ -477,7 +477,8 @@ void LLToolTipMgr::show(const std::string& msg)
void LLToolTipMgr::show(const LLToolTip::Params& params)
{
if (!params.styled_message.isProvided()
- && (!params.message.isProvided() || params.message().empty())) return;
+ && (!params.message.isProvided() || params.message().empty())
+ && !params.image.isProvided()) return;
// fill in default tooltip params from tool_tip.xml
LLToolTip::Params params_with_defaults(params);
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index c70e0697a1..7dbd92283e 100755
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -236,6 +236,7 @@ set(viewer_SOURCE_FILES
llfloatergesture.cpp
llfloatergodtools.cpp
llfloatergotoline.cpp
+ llfloatergroupbulkban.cpp
llfloatergroupinvite.cpp
llfloatergroups.cpp
llfloaterhandler.cpp
@@ -411,6 +412,8 @@ set(viewer_SOURCE_FILES
llpanelface.cpp
llpanelgenerictip.cpp
llpanelgroup.cpp
+ llpanelgroupbulk.cpp
+ llpanelgroupbulkban.cpp
llpanelgroupgeneral.cpp
llpanelgroupinvite.cpp
llpanelgrouplandmoney.cpp
@@ -838,6 +841,7 @@ set(viewer_HEADER_FILES
llfloatergesture.h
llfloatergodtools.h
llfloatergotoline.h
+ llfloatergroupbulkban.h
llfloatergroupinvite.h
llfloatergroups.h
llfloaterhandler.h
@@ -1006,6 +1010,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/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt
index 214b521fe2..35c6ac5179 100644
--- a/indra/newview/VIEWER_VERSION.txt
+++ b/indra/newview/VIEWER_VERSION.txt
@@ -1 +1 @@
-3.7.13
+3.7.14
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 15e19d3b21..730f685d42 100755
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -6129,16 +6129,16 @@
<integer>0</integer>
</map>
<key>MemoryLogFrequency</key>
- <map>
- <key>Comment</key>
- <string>Seconds between display of Memory in log (0 for never)</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>F32</string>
- <key>Value</key>
- <real>600.0</real>
- </map>
+ <map>
+ <key>Comment</key>
+ <string>Seconds between display of Memory in log (0 for never)</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>30.0</real>
+ </map>
<key>MemoryPrivatePoolEnabled</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/llconversationmodel.cpp b/indra/newview/llconversationmodel.cpp
index 74b77f760d..6e32ce60ec 100755
--- a/indra/newview/llconversationmodel.cpp
+++ b/indra/newview/llconversationmodel.cpp
@@ -51,6 +51,7 @@ LLConversationItem::LLConversationItem(std::string display_name, const LLUUID& u
mConvType(CONV_UNKNOWN),
mLastActiveTime(0.0),
mDisplayModeratorOptions(false),
+ mDisplayGroupBanOptions(false),
mAvatarNameCacheConnection()
{
}
@@ -63,6 +64,7 @@ LLConversationItem::LLConversationItem(const LLUUID& uuid, LLFolderViewModelInte
mConvType(CONV_UNKNOWN),
mLastActiveTime(0.0),
mDisplayModeratorOptions(false),
+ mDisplayGroupBanOptions(false),
mAvatarNameCacheConnection()
{
}
@@ -75,6 +77,7 @@ LLConversationItem::LLConversationItem(LLFolderViewModelInterface& root_view_mod
mConvType(CONV_UNKNOWN),
mLastActiveTime(0.0),
mDisplayModeratorOptions(false),
+ mDisplayGroupBanOptions(false),
mAvatarNameCacheConnection()
{
}
@@ -159,6 +162,12 @@ void LLConversationItem::buildParticipantMenuOptions(menuentry_vec_t& items, U32
items.push_back(std::string("ModerateVoiceMute"));
items.push_back(std::string("ModerateVoiceUnmute"));
}
+
+ if ((getType() != CONV_SESSION_1_ON_1) && mDisplayGroupBanOptions)
+ {
+ items.push_back(std::string("Group Ban Separator"));
+ items.push_back(std::string("BanMember"));
+ }
}
}
diff --git a/indra/newview/llconversationmodel.h b/indra/newview/llconversationmodel.h
index d3d9fbd109..3d69cd6f5b 100644
--- a/indra/newview/llconversationmodel.h
+++ b/indra/newview/llconversationmodel.h
@@ -143,6 +143,7 @@ protected:
bool mNeedsRefresh; // Flag signaling to the view that something changed for this item
F64 mLastActiveTime;
bool mDisplayModeratorOptions;
+ bool mDisplayGroupBanOptions;
boost::signals2::connection mAvatarNameCacheConnection;
};
@@ -206,6 +207,7 @@ public:
void dumpDebugData();
void setModeratorOptionsVisible(bool visible) { mDisplayModeratorOptions = visible; }
void setDisplayModeratorRole(bool displayRole);
+ void setGroupBanVisible(bool visible) { mDisplayGroupBanOptions = visible; }
private:
void onAvatarNameCache(const LLAvatarName& av_name); // callback used by fetchAvatarName
diff --git a/indra/newview/llfloatergroupbulkban.cpp b/indra/newview/llfloatergroupbulkban.cpp
new file mode 100644
index 0000000000..54a2283b13
--- /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 0000000000..5b680a1ba4
--- /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/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp
index ebb44561da..be8195b5ee 100755
--- a/indra/newview/llfloaterimcontainer.cpp
+++ b/indra/newview/llfloaterimcontainer.cpp
@@ -532,6 +532,7 @@ void LLFloaterIMContainer::draw()
{
LLConversationItemParticipant* participant_model = dynamic_cast<LLConversationItemParticipant*>(*current_participant_model);
participant_model->setModeratorOptionsVisible(isGroupModerator() && participant_model->getUUID() != gAgentID);
+ participant_model->setGroupBanVisible(haveAbilityToBan() && participant_model->getUUID() != gAgentID);
current_participant_model++;
}
@@ -1150,6 +1151,10 @@ void LLFloaterIMContainer::doToParticipants(const std::string& command, uuid_vec
{
toggleAllowTextChat(userID);
}
+ else if ("ban_member" == command)
+ {
+ banSelectedMember(userID);
+ }
}
else if (selectedIDS.size() > 1)
{
@@ -1271,6 +1276,22 @@ bool LLFloaterIMContainer::enableContextMenuItem(const LLSD& userdata)
uuid_vec_t uuids;
getParticipantUUIDs(uuids);
+
+ //If there is group or ad-hoc chat in multiselection, everything needs to be disabled
+ if(uuids.size() > 1)
+ {
+ const std::set<LLFolderViewItem*> selectedItems = mConversationsRoot->getSelectionList();
+ LLConversationItem * conversationItem;
+ for(std::set<LLFolderViewItem*>::const_iterator it = selectedItems.begin(); it != selectedItems.end(); ++it)
+ {
+ conversationItem = static_cast<LLConversationItem *>((*it)->getViewModelItem());
+ if((conversationItem->getType() == LLConversationItem::CONV_SESSION_GROUP) || (conversationItem->getType() == LLConversationItem::CONV_SESSION_AD_HOC))
+ {
+ return false;
+ }
+ }
+ }
+
if ("conversation_log" == item)
{
return gSavedPerAccountSettings.getS32("KeepConversationLogTranscripts") > 0;
@@ -1376,6 +1397,10 @@ bool LLFloaterIMContainer::enableContextMenuItem(const std::string& item, uuid_v
{
return LLAvatarActions::canCall();
}
+ else if ("can_open_voice_conversation" == item)
+ {
+ return is_single_select && LLAvatarActions::canCall();
+ }
else if ("can_zoom_in" == item)
{
return is_single_select && gObjectList.findObject(single_id);
@@ -1388,6 +1413,10 @@ bool LLFloaterIMContainer::enableContextMenuItem(const std::string& item, uuid_v
{
return LLAvatarActions::canOfferTeleport(uuids);
}
+ else if ("can_ban_member" == item)
+ {
+ return canBanSelectedMember(single_id);
+ }
else if (("can_moderate_voice" == item) || ("can_allow_text_chat" == item) || ("can_mute" == item) || ("can_unmute" == item))
{
// *TODO : get that out of here...
@@ -1810,6 +1839,95 @@ bool LLFloaterIMContainer::isGroupModerator()
return false;
}
+bool LLFloaterIMContainer::haveAbilityToBan()
+{
+ LLSpeakerMgr * speaker_manager = getSpeakerMgrForSelectedParticipant();
+ if (NULL == speaker_manager)
+ {
+ LL_WARNS() << "Speaker manager is missing" << LL_ENDL;
+ return false;
+ }
+ LLUUID group_uuid = speaker_manager->getSessionID();
+
+ return gAgent.isInGroup(group_uuid) && gAgent.hasPowerInGroup(group_uuid, GP_GROUP_BAN_ACCESS);
+}
+
+bool LLFloaterIMContainer::canBanSelectedMember(const LLUUID& participant_uuid)
+{
+ LLSpeakerMgr * speaker_manager = getSpeakerMgrForSelectedParticipant();
+ if (NULL == speaker_manager)
+ {
+ LL_WARNS() << "Speaker manager is missing" << LL_ENDL;
+ return false;
+ }
+ LLUUID group_uuid = speaker_manager->getSessionID();
+ LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(group_uuid);
+ if(!gdatap)
+ {
+ LL_WARNS("Groups") << "Unable to get group data for group " << group_uuid << LL_ENDL;
+ return false;
+ }
+
+ if (!gdatap->mMembers.size())
+ {
+ return false;
+ }
+
+ LLGroupMgrGroupData::member_list_t::iterator mi = gdatap->mMembers.find((participant_uuid));
+ if (mi == gdatap->mMembers.end())
+ {
+ return false;
+ }
+
+ LLGroupMemberData* member_data = (*mi).second;
+ // Is the member an owner?
+ if ( member_data && member_data->isInRole(gdatap->mOwnerRole) )
+ {
+ return false;
+ }
+
+ if( gAgent.hasPowerInGroup(group_uuid, GP_ROLE_REMOVE_MEMBER) &&
+ gAgent.hasPowerInGroup(group_uuid, GP_GROUP_BAN_ACCESS) )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+void LLFloaterIMContainer::banSelectedMember(const LLUUID& participant_uuid)
+{
+ LLSpeakerMgr * speaker_manager = getSpeakerMgrForSelectedParticipant();
+ if (NULL == speaker_manager)
+ {
+ LL_WARNS() << "Speaker manager is missing" << LL_ENDL;
+ return;
+ }
+
+ LLUUID group_uuid = speaker_manager->getSessionID();
+ LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(group_uuid);
+ if(!gdatap)
+ {
+ LL_WARNS("Groups") << "Unable to get group data for group " << group_uuid << LL_ENDL;
+ return;
+ }
+ std::vector<LLUUID> ids;
+ ids.push_back(participant_uuid);
+
+ LLGroupBanData ban_data;
+ gdatap->createBanEntry(participant_uuid, ban_data);
+ LLGroupMgr::getInstance()->sendGroupBanRequest(LLGroupMgr::REQUEST_POST, group_uuid, LLGroupMgr::BAN_CREATE, ids);
+ LLGroupMgr::getInstance()->sendGroupMemberEjects(group_uuid, ids);
+ LLGroupMgr::getInstance()->sendGroupMembersRequest(group_uuid);
+ LLSD args;
+ std::string name;
+ gCacheName->getFullName(participant_uuid, name);
+ args["AVATAR_NAME"] = name;
+ args["GROUP_NAME"] = gdatap->mName;
+ LLNotifications::instance().add(LLNotification::Params("EjectAvatarFromGroup").substitutions(args));
+
+}
+
void LLFloaterIMContainer::moderateVoice(const std::string& command, const LLUUID& userID)
{
if (!gAgent.getRegion()) return;
diff --git a/indra/newview/llfloaterimcontainer.h b/indra/newview/llfloaterimcontainer.h
index f6d973b9b3..5ea9fd399b 100755
--- a/indra/newview/llfloaterimcontainer.h
+++ b/indra/newview/llfloaterimcontainer.h
@@ -167,12 +167,16 @@ private:
LLSpeaker * getSpeakerOfSelectedParticipant(LLSpeakerMgr * speaker_managerp);
LLSpeakerMgr * getSpeakerMgrForSelectedParticipant();
bool isGroupModerator();
+ bool haveAbilityToBan();
+ bool canBanSelectedMember(const LLUUID& participant_uuid);
+ LLUUID getGroupUIIDForSelectedParticipant();
bool isMuted(const LLUUID& avatar_id);
void moderateVoice(const std::string& command, const LLUUID& userID);
void moderateVoiceAllParticipants(bool unmute);
void moderateVoiceParticipant(const LLUUID& avatar_id, bool unmute);
void toggleAllowTextChat(const LLUUID& participant_uuid);
void toggleMute(const LLUUID& participant_id, U32 flags);
+ void banSelectedMember(const LLUUID& participant_uuid);
void openNearbyChat();
bool isParticipantListExpanded();
diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp
index f514729aa1..913efd6434 100755
--- a/indra/newview/llgroupactions.cpp
+++ b/indra/newview/llgroupactions.cpp
@@ -149,7 +149,7 @@ public:
void changed(LLGroupChange gc)
{
- if (gc == GC_MEMBER_DATA && !mRequestProcessed)
+ if (gc == GC_PROPERTIES && !mRequestProcessed)
{
LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupId);
if (!gdatap)
@@ -159,9 +159,6 @@ public:
else if (!gdatap->isMemberDataComplete())
{
LL_WARNS() << "LLGroupMgr::getInstance()->getGroupData()->isMemberDataComplete() was FALSE" << LL_ENDL;
- }
- else
- {
processGroupData();
mRequestProcessed = true;
}
diff --git a/indra/newview/llgroupmgr.cpp b/indra/newview/llgroupmgr.cpp
index d5b817ce76..56e671d902 100755
--- a/indra/newview/llgroupmgr.cpp
+++ b/indra/newview/llgroupmgr.cpp
@@ -234,11 +234,11 @@ 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),
+ mPendingRoleMemberRequest(false),
mAccessTime(0.0f)
{
mMemberVersion.generate();
@@ -427,7 +427,7 @@ void LLGroupMgrGroupData::removeMemberData()
delete mi->second;
}
mMembers.clear();
- mMemberDataComplete = FALSE;
+ mMemberDataComplete = false;
mMemberVersion.generate();
}
@@ -449,8 +449,8 @@ void LLGroupMgrGroupData::removeRoleData()
}
mRoles.clear();
mReceivedRoleMemberPairs = 0;
- mRoleDataComplete = FALSE;
- mRoleMemberDataComplete = FALSE;
+ mRoleDataComplete = false;
+ mRoleMemberDataComplete= false;
}
void LLGroupMgrGroupData::removeRoleMemberData()
@@ -474,7 +474,7 @@ void LLGroupMgrGroupData::removeRoleMemberData()
}
mReceivedRoleMemberPairs = 0;
- mRoleMemberDataComplete = FALSE;
+ mRoleMemberDataComplete= false;
}
LLGroupMgrGroupData::~LLGroupMgrGroupData()
@@ -750,6 +750,20 @@ 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;
+}
+
+void LLGroupMgrGroupData::removeBanEntry(const LLUUID& ban_id)
+{
+ mBanList.erase(ban_id);
+}
+
+
+
+
//
// LLGroupMgr
//
@@ -959,12 +973,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);
}
}
@@ -1034,7 +1048,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);
@@ -1111,12 +1125,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);
}
}
@@ -1225,7 +1239,7 @@ void LLGroupMgr::processGroupRoleMembersReply(LLMessageSystem* msg, void** data)
}
}
- group_datap->mRoleMemberDataComplete = TRUE;
+ group_datap->mRoleMemberDataComplete= true;
group_datap->mRoleMembersRequestID.setNull();
}
@@ -1849,6 +1863,138 @@ void LLGroupMgr::sendGroupMemberEjects(const LLUUID& group_id,
// Responder class for capability group management
+class GroupBanDataResponder : public LLHTTPClient::Responder
+{
+public:
+ GroupBanDataResponder(const LLUUID& gropup_id, BOOL force_refresh=false);
+ virtual ~GroupBanDataResponder() {}
+ virtual void httpSuccess();
+ virtual void httpFailure();
+private:
+ LLUUID mGroupID;
+ BOOL mForceRefresh;
+};
+
+GroupBanDataResponder::GroupBanDataResponder(const LLUUID& gropup_id, BOOL force_refresh) :
+ mGroupID(gropup_id),
+ mForceRefresh(force_refresh)
+{}
+
+void GroupBanDataResponder::httpFailure()
+{
+ LL_WARNS("GrpMgr") << "Error receiving group member data [status:"
+ << mStatus << "]: " << mContent << LL_ENDL;
+}
+
+void GroupBanDataResponder::httpSuccess()
+{
+ if (mContent.has("ban_list"))
+ {
+ // group ban data received
+ LLGroupMgr::processGroupBanRequest(mContent);
+ }
+ else if (mForceRefresh)
+ {
+ // no ban data received, refreshing data after successful operation
+ LLGroupMgr::getInstance()->sendGroupBanRequest(LLGroupMgr::REQUEST_GET, mGroupID);
+ }
+}
+
+void LLGroupMgr::sendGroupBanRequest( EBanRequestType request_type,
+ const LLUUID& group_id,
+ U32 ban_action, /* = BAN_NO_ACTION */
+ 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("GroupAPIv1");
+ if(cap_url.empty())
+ {
+ return;
+ }
+ cap_url += "?group_id=" + group_id.asString();
+
+ LLSD body = LLSD::emptyMap();
+ body["ban_action"] = (LLSD::Integer)(ban_action & ~BAN_UPDATE);
+ // Add our list of potential banned residents to the list
+ body["ban_ids"] = LLSD::emptyArray();
+ LLSD ban_entry;
+
+ uuid_vec_t::const_iterator iter = ban_list.begin();
+ for(;iter != ban_list.end(); ++iter)
+ {
+ ban_entry = (*iter);
+ body["ban_ids"].append(ban_entry);
+ }
+
+ LLHTTPClient::ResponderPtr grp_ban_responder = new GroupBanDataResponder(group_id, ban_action & BAN_UPDATE);
+ switch(request_type)
+ {
+ case REQUEST_GET:
+ LLHTTPClient::get(cap_url, grp_ban_responder);
+ break;
+ case REQUEST_POST:
+ LLHTTPClient::post(cap_url, body, grp_ban_responder);
+ break;
+ case REQUEST_PUT:
+ case REQUEST_DEL:
+ break;
+ }
+}
+
+
+void LLGroupMgr::processGroupBanRequest(const LLSD& content)
+{
+ // Did we get anything in content?
+ if(!content.size())
+ {
+ LL_WARNS("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::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);
+ LLSD ban_entry(i->second);
+
+ LLGroupBanData ban_data;
+ if(ban_entry.has("ban_date"))
+ {
+ ban_data.mBanDate = ban_entry["ban_date"].asDate();
+ // TODO: Ban Reason
+ }
+
+ gdatap->createBanEntry(ban_id, ban_data);
+ }
+
+ gdatap->mChanged = TRUE;
+ LLGroupMgr::getInstance()->notifyObservers(GC_BANLIST);
+}
+
+
+
+// Responder class for capability group management
class GroupMemberDataResponder : public LLHTTPClient::Responder
{
LOG_CLASS(GroupMemberDataResponder);
@@ -1941,7 +2087,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,6 +2154,22 @@ void LLGroupMgr::processCapGroupMembersRequest(const LLSD& content)
online_status,
is_owner);
+ LLGroupMemberData* member_old = group_datap->mMembers[member_id];
+ if (member_old && group_datap->mRoleMemberDataComplete)
+ {
+ LLGroupMemberData::role_list_t::iterator rit = member_old->roleBegin();
+ LLGroupMemberData::role_list_t::iterator end = member_old->roleEnd();
+
+ for ( ; rit != end; ++rit)
+ {
+ data->addRole((*rit).first,(*rit).second);
+ }
+ }
+ else
+ {
+ group_datap->mRoleMemberDataComplete = false;
+ }
+
group_datap->mMembers[member_id] = data;
}
@@ -2024,12 +2186,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)
+ if (group_datap->mPendingRoleMemberRequest || !group_datap->mRoleMemberDataComplete)
{
- 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 9b62ecac48..2e94e8d9a0 100755
--- a/indra/newview/llgroupmgr.h
+++ b/indra/newview/llgroupmgr.h
@@ -33,8 +33,10 @@
#include <string>
#include <map>
+// Forward Declarations
class LLMessageSystem;
-
+class LLGroupRoleData;
+class LLGroupMgr;
enum LLGroupChange
{
@@ -43,9 +45,12 @@ enum LLGroupChange
GC_ROLE_DATA,
GC_ROLE_MEMBER_DATA,
GC_TITLES,
+ GC_BANLIST,
GC_ALL
};
+const U32 GB_MAX_BANNED_AGENTS = 500;
+
class LLGroupMgrObserver
{
public:
@@ -65,8 +70,6 @@ public:
virtual void changed(const LLUUID& group_id, LLGroupChange gc) = 0;
};
-class LLGroupRoleData;
-
class LLGroupMemberData
{
friend class LLGroupMgrGroupData;
@@ -201,6 +204,17 @@ struct lluuid_pair_less
}
};
+
+struct LLGroupBanData
+{
+ LLGroupBanData(): mBanDate() {}
+ ~LLGroupBanData() {}
+
+ LLDate mBanDate;
+ // TODO: std:string ban_reason;
+};
+
+
struct LLGroupTitle
{
std::string mTitle;
@@ -208,8 +222,6 @@ struct LLGroupTitle
BOOL mSelected;
};
-class LLGroupMgr;
-
class LLGroupMgrGroupData
{
friend class LLGroupMgr;
@@ -239,11 +251,11 @@ public:
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 isSingleMemberNotOwner();
F32 getAccessTime() const { return mAccessTime; }
@@ -251,17 +263,26 @@ public:
const LLUUID& getMemberVersion() const { return mMemberVersion; }
+ 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());
+ void 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;
@@ -292,12 +313,12 @@ private:
LLUUID mTitlesRequestID;
U32 mReceivedRoleMemberPairs;
- BOOL mMemberDataComplete;
- BOOL mRoleDataComplete;
- BOOL mRoleMemberDataComplete;
- BOOL mGroupPropertiesDataComplete;
+ bool mMemberDataComplete;
+ bool mRoleDataComplete;
+ bool mRoleMemberDataComplete;
+ bool mGroupPropertiesDataComplete;
- BOOL mPendingRoleMemberRequest;
+ bool mPendingRoleMemberRequest;
F32 mAccessTime;
// Generate a new ID every time mMembers
@@ -325,6 +346,23 @@ class LLGroupMgr : public LLSingleton<LLGroupMgr>
LOG_CLASS(LLGroupMgr);
public:
+ enum EBanRequestType
+ {
+ REQUEST_GET = 0,
+ REQUEST_POST,
+ REQUEST_PUT,
+ REQUEST_DEL
+ };
+
+ enum EBanRequestAction
+ {
+ BAN_NO_ACTION = 0,
+ BAN_CREATE = 1,
+ BAN_DELETE = 2,
+ BAN_UPDATE = 4
+ };
+
+public:
LLGroupMgr();
~LLGroupMgr();
@@ -357,8 +395,14 @@ public:
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);
+
+ static void sendGroupBanRequest(EBanRequestType request_type,
+ const LLUUID& group_id,
+ U32 ban_action = BAN_NO_ACTION,
+ const uuid_vec_t ban_list = uuid_vec_t());
+
+ static void processGroupBanRequest(const LLSD& content);
- // BAKER
void sendCapGroupMembersRequest(const LLUUID& group_id);
static void processCapGroupMembersRequest(const LLSD& content);
@@ -403,4 +447,3 @@ private:
#endif
-
diff --git a/indra/newview/llnamelistctrl.cpp b/indra/newview/llnamelistctrl.cpp
index 91866e5ca2..54e4c6c1da 100755
--- a/indra/newview/llnamelistctrl.cpp
+++ b/indra/newview/llnamelistctrl.cpp
@@ -64,7 +64,8 @@ LLNameListCtrl::LLNameListCtrl(const LLNameListCtrl::Params& p)
mNameColumnIndex(p.name_column.column_index),
mNameColumn(p.name_column.column_name),
mAllowCallingCardDrop(p.allow_calling_card_drop),
- mShortNames(p.short_names)
+ mShortNames(p.short_names),
+ mPendingLookupsRemaining(0)
{}
// public
@@ -337,6 +338,17 @@ LLScrollListItem* LLNameListCtrl::addNameItemRow(
mAvatarNameCacheConnections.erase(it);
}
mAvatarNameCacheConnections[id] = LLAvatarNameCache::get(id,boost::bind(&LLNameListCtrl::onAvatarNameCache,this, _1, _2, suffix, item->getHandle()));
+
+ if(mPendingLookupsRemaining <= 0)
+ {
+ // BAKER TODO:
+ // We might get into a state where mPendingLookupsRemaining might
+ // go negative. So just reset it right now and figure out if it's
+ // possible later :)
+ mPendingLookupsRemaining = 0;
+ mNameListCompleteSignal(false);
+ }
+ mPendingLookupsRemaining++;
}
break;
}
@@ -388,6 +400,8 @@ void LLNameListCtrl::removeNameItem(const LLUUID& agent_id)
{
selectNthItem(idx); // not sure whether this is needed, taken from previous implementation
deleteSingleItem(idx);
+
+ mPendingLookupsRemaining--;
}
}
@@ -429,6 +443,23 @@ void LLNameListCtrl::onAvatarNameCache(const LLUUID& agent_id,
}
}
+ //////////////////////////////////////////////////////////////////////////
+ // BAKER - FIX NameListCtrl
+ //if (mPendingLookupsRemaining <= 0)
+ {
+ // We might get into a state where mPendingLookupsRemaining might
+ // go negative. So just reset it right now and figure out if it's
+ // possible later :)
+ //mPendingLookupsRemaining = 0;
+
+ mNameListCompleteSignal(true);
+ }
+ //else
+ {
+ // mPendingLookupsRemaining--;
+ }
+ //////////////////////////////////////////////////////////////////////////
+
dirtyColumns();
}
diff --git a/indra/newview/llnamelistctrl.h b/indra/newview/llnamelistctrl.h
index 4ed260d847..2c40eeaaca 100755
--- a/indra/newview/llnamelistctrl.h
+++ b/indra/newview/llnamelistctrl.h
@@ -67,6 +67,7 @@ class LLNameListCtrl
: public LLScrollListCtrl, public LLInstanceTracker<LLNameListCtrl>
{
public:
+ typedef boost::signals2::signal<void(bool)> namelist_complete_signal_t;
typedef enum e_name_type
{
@@ -156,7 +157,7 @@ public:
/*virtual*/ void updateColumns(bool force_update);
- /*virtual*/ void mouseOverHighlightNthItem( S32 index );
+ /*virtual*/ void mouseOverHighlightNthItem( S32 index );
private:
void showInspector(const LLUUID& avatar_id, bool is_group);
void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name, std::string suffix, LLHandle<LLNameListItem> item);
@@ -168,6 +169,16 @@ private:
bool mShortNames; // display name only, no SLID
typedef std::map<LLUUID, boost::signals2::connection> avatar_name_cache_connection_map_t;
avatar_name_cache_connection_map_t mAvatarNameCacheConnections;
+
+ S32 mPendingLookupsRemaining;
+ namelist_complete_signal_t mNameListCompleteSignal;
+
+public:
+ boost::signals2::connection setOnNameListCompleteCallback(boost::function<void(bool)> onNameListCompleteCallback)
+ {
+ return mNameListCompleteSignal.connect(onNameListCompleteCallback);
+ }
+
};
diff --git a/indra/newview/llpanelgroupbulk.cpp b/indra/newview/llpanelgroupbulk.cpp
new file mode 100644
index 0000000000..1eafc5bd64
--- /dev/null
+++ b/indra/newview/llpanelgroupbulk.cpp
@@ -0,0 +1,421 @@
+/**
+* @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),
+ mListFullNotificationSent(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;
+
+ std::vector<LLScrollListItem*>::iterator iter;
+ for(iter = selection.begin(); iter != selection.end(); ++iter)
+ {
+ mInviteeIDs.erase((*iter)->getUUID());
+ }
+
+ 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;
+
+ if(mListFullNotificationSent)
+ {
+ return;
+ }
+
+ if( !mListFullNotificationSent &&
+ (names.size() + mInviteeIDs.size() > MAX_GROUP_INVITES))
+ {
+ mListFullNotificationSent = true;
+
+ // Fail! Show a warning and don't add any names.
+ LLSD msg;
+ msg["MESSAGE"] = mTooManySelected;
+ LLNotificationsUtil::add("GenericAlert", msg);
+ return;
+ }
+
+ for (S32 i = 0; i < (S32)names.size(); ++i)
+ {
+ name = names[i];
+ id = agent_ids[i];
+
+ if(mInviteeIDs.find(id) != mInviteeIDs.end())
+ {
+ continue;
+ }
+
+ //add the name to the names list
+ LLSD row;
+ row["id"] = id;
+ row["columns"][0]["value"] = name;
+
+ mBulkAgentList->addElement(row);
+ mInviteeIDs.insert(id);
+
+ // 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()
+{
+ mImplementation->mInviteeIDs.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 && gdatap->isGroupPropertiesDataComplete())
+ {
+ mPendingGroupPropertiesUpdate = false;
+ }
+ else
+ {
+ if(!mPendingGroupPropertiesUpdate)
+ {
+ mPendingGroupPropertiesUpdate = true;
+ LLGroupMgr::getInstance()->sendGroupPropertiesRequest(mImplementation->mGroupID);
+ }
+ }
+
+ if(gdatap && gdatap->isRoleDataComplete())
+ {
+ mPendingRoleDataUpdate = false;
+ }
+ else
+ {
+ if(!mPendingRoleDataUpdate)
+ {
+ mPendingRoleDataUpdate = true;
+ LLGroupMgr::getInstance()->sendGroupRoleDataRequest(mImplementation->mGroupID);
+ }
+ }
+
+ if(gdatap && 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->mListFullNotificationSent = false;
+ mImplementation->addUsers(names, agent_ids);
+}
+
diff --git a/indra/newview/llpanelgroupbulk.h b/indra/newview/llpanelgroupbulk.h
new file mode 100644
index 0000000000..25ae71ab86
--- /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);
+ /*virtual*/ ~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 0000000000..cf1f0bc32f
--- /dev/null
+++ b/indra/newview/llpanelgroupbulkban.cpp
@@ -0,0 +1,259 @@
+/**
+* @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 "llavataractions.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 "llslurl.h"
+#include "llspinctrl.h"
+#include "lltextbox.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "lluictrlfactory.h"
+#include "llviewerwindow.h"
+
+#include <boost/foreach.hpp>
+
+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");
+ mImplementation->mBanNotPermitted = getString("ban_not_permitted");
+ mImplementation->mBanLimitFail = getString("ban_limit_fail");
+ mImplementation->mCannotBanYourself = getString("cant_ban_yourself");
+
+ update();
+ return TRUE;
+}
+
+// TODO: Refactor the shitty callback functions with void* -- just use boost::bind to call submit() instead.
+void LLPanelGroupBulkBan::callbackClickSubmit(void* userdata)
+{
+ LLPanelGroupBulkBan* selfp = (LLPanelGroupBulkBan*)userdata;
+
+ if(selfp)
+ selfp->submit();
+}
+
+
+void LLPanelGroupBulkBan::submit()
+{
+ if (!gAgent.hasPowerInGroup(mImplementation->mGroupID, GP_GROUP_BAN_ACCESS))
+ {
+ // Fail! Agent no longer have ban rights. Permissions could have changed after button was pressed.
+ LLSD msg;
+ msg["MESSAGE"] = mImplementation->mBanNotPermitted;
+ LLNotificationsUtil::add("GenericAlert", msg);
+ (*(mImplementation->mCloseCallback))(mImplementation->mCloseCallbackUserData);
+ return;
+ }
+ LLGroupMgrGroupData * group_datap = LLGroupMgr::getInstance()->getGroupData(mImplementation->mGroupID);
+ if (group_datap && group_datap->mBanList.size() >= GB_MAX_BANNED_AGENTS)
+ {
+ // Fail! Size limit exceeded. List could have updated after button was pressed.
+ LLSD msg;
+ msg["MESSAGE"] = mImplementation->mBanLimitFail;
+ LLNotificationsUtil::add("GenericAlert", msg);
+ (*(mImplementation->mCloseCallback))(mImplementation->mCloseCallbackUserData);
+ return;
+ }
+ 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_BANS_PER_REQUEST = 100; // Max bans per request. 100 to match server cap.
+ if (banned_agent_list.size() > MAX_BANS_PER_REQUEST)
+ {
+ // Fail!
+ LLSD msg;
+ msg["MESSAGE"] = mImplementation->mTooManySelected;
+ LLNotificationsUtil::add("GenericAlert", msg);
+ (*(mImplementation->mCloseCallback))(mImplementation->mCloseCallbackUserData);
+ return;
+ }
+
+ // remove already banned users and yourself from request.
+ std::vector<LLAvatarName> banned_avatar_names;
+ std::vector<LLAvatarName> out_of_limit_names;
+ bool banning_self = FALSE;
+ std::vector<LLUUID>::iterator conflict = std::find(banned_agent_list.begin(), banned_agent_list.end(), gAgent.getID());
+ if (conflict != banned_agent_list.end())
+ {
+ banned_agent_list.erase(conflict);
+ banning_self = TRUE;
+ }
+ if (group_datap)
+ {
+ BOOST_FOREACH(const LLGroupMgrGroupData::ban_list_t::value_type& group_ban_pair, group_datap->mBanList)
+ {
+ const LLUUID& group_ban_agent_id = group_ban_pair.first;
+ std::vector<LLUUID>::iterator conflict = std::find(banned_agent_list.begin(), banned_agent_list.end(), group_ban_agent_id);
+ if (conflict != banned_agent_list.end())
+ {
+ LLAvatarName av_name;
+ LLAvatarNameCache::get(group_ban_agent_id, &av_name);
+ banned_avatar_names.push_back(av_name);
+
+ banned_agent_list.erase(conflict);
+ if (banned_agent_list.size() == 0)
+ {
+ break;
+ }
+ }
+ }
+ // this check should always be the last one before we send the request.
+ // Otherwise we have a possibility of cutting more then we need to.
+ if (banned_agent_list.size() > GB_MAX_BANNED_AGENTS - group_datap->mBanList.size())
+ {
+ std::vector<LLUUID>::iterator exeedes_limit = banned_agent_list.begin() + GB_MAX_BANNED_AGENTS - group_datap->mBanList.size();
+ for (std::vector<LLUUID>::iterator itor = exeedes_limit ;
+ itor != banned_agent_list.end(); ++itor)
+ {
+ LLAvatarName av_name;
+ LLAvatarNameCache::get(*itor, &av_name);
+ out_of_limit_names.push_back(av_name);
+ }
+ banned_agent_list.erase(exeedes_limit,banned_agent_list.end());
+ }
+ }
+
+ // sending request and ejecting members
+ if (banned_agent_list.size() != 0)
+ {
+ LLGroupMgr::getInstance()->sendGroupBanRequest(LLGroupMgr::REQUEST_POST, mImplementation->mGroupID, LLGroupMgr::BAN_CREATE | LLGroupMgr::BAN_UPDATE, banned_agent_list);
+ LLGroupMgr::getInstance()->sendGroupMemberEjects(mImplementation->mGroupID, banned_agent_list);
+ }
+
+ // building notification
+ if (banned_avatar_names.size() > 0 || banning_self || out_of_limit_names.size() > 0)
+ {
+ std::string reasons;
+ if(banned_avatar_names.size() > 0)
+ {
+ reasons = "\n " + buildResidentsArgument(banned_avatar_names, "residents_already_banned");
+ }
+
+ if(banning_self)
+ {
+ reasons += "\n " + mImplementation->mCannotBanYourself;
+ }
+
+ if(out_of_limit_names.size() > 0)
+ {
+ reasons += "\n " + buildResidentsArgument(out_of_limit_names, "ban_limit_reached");
+ }
+
+ LLStringUtil::format_map_t msg_args;
+ msg_args["[REASONS]"] = reasons;
+ LLSD msg;
+ if (banned_agent_list.size() == 0)
+ {
+ msg["MESSAGE"] = getString("ban_failed", msg_args);
+ }
+ else
+ {
+ msg["MESSAGE"] = getString("partial_ban", msg_args);
+ }
+ LLNotificationsUtil::add("GenericAlert", msg);
+ }
+
+ //then close
+ (*(mImplementation->mCloseCallback))(mImplementation->mCloseCallbackUserData);
+}
+
+std::string LLPanelGroupBulkBan::buildResidentsArgument(std::vector<LLAvatarName> avatar_names, const std::string &format)
+{
+ std::string names_string;
+ LLAvatarActions::buildResidentsString(avatar_names, names_string);
+ LLStringUtil::format_map_t args;
+ args["[RESIDENTS]"] = names_string;
+ return getString(format, args);
+}
diff --git a/indra/newview/llpanelgroupbulkban.h b/indra/newview/llpanelgroupbulkban.h
new file mode 100644
index 0000000000..9060d275f9
--- /dev/null
+++ b/indra/newview/llpanelgroupbulkban.h
@@ -0,0 +1,49 @@
+/**
+* @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();
+private:
+ std::string buildResidentsArgument(std::vector<LLAvatarName> avatar_names, const std::string &format);
+};
+
+#endif // LL_LLPANELGROUPBULKBAN_H
diff --git a/indra/newview/llpanelgroupbulkimpl.h b/indra/newview/llpanelgroupbulkimpl.h
new file mode 100644
index 0000000000..d3a48e5a9a
--- /dev/null
+++ b/indra/newview/llpanelgroupbulkimpl.h
@@ -0,0 +1,99 @@
+/**
+* @file llpanelgroupbulkimpl.h
+* @brief Class definition for implementation class 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$
+*/
+#ifndef LL_LLPANELGROUPBULKIMPL_H
+#define LL_LLPANELGROUPBULKIMPL_H
+
+#include "llpanel.h"
+#include "lluuid.h"
+
+class LLAvatarName;
+class LLNameListCtrl;
+class LLTextBox;
+class LLComboBox;
+
+//////////////////////////////////////////////////////////////////////////
+// Implementation found in llpanelgroupbulk.cpp
+//////////////////////////////////////////////////////////////////////////
+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:
+ static const S32 MAX_GROUP_INVITES = 100; // Max invites per request. 100 to match server cap.
+
+
+ LLUUID mGroupID;
+
+ LLNameListCtrl* mBulkAgentList;
+ LLButton* mOKButton;
+ LLButton* mRemoveButton;
+ LLTextBox* mGroupName;
+
+ std::string mLoadingText;
+ std::string mTooManySelected;
+ std::string mBanNotPermitted;
+ std::string mBanLimitFail;
+ std::string mCannotBanYourself;
+
+ std::set<LLUUID> mInviteeIDs;
+
+ 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;
+ bool mListFullNotificationSent;
+};
+
+#endif // LL_LLPANELGROUPBULKIMPL_H
+
diff --git a/indra/newview/llpanelgroupinvite.cpp b/indra/newview/llpanelgroupinvite.cpp
index dd13e8abf4..236ad861a5 100755
--- a/indra/newview/llpanelgroupinvite.cpp
+++ b/indra/newview/llpanelgroupinvite.cpp
@@ -260,7 +260,7 @@ void LLPanelGroupInvite::impl::addRoleNames(LLGroupMgrGroupData* gdatap)
//else if they have the limited add to roles power
//we add every role the user is in
//else we just add to everyone
- bool is_owner = member_data->isInRole(gdatap->mOwnerRole);
+ bool is_owner = member_data->isOwner();
bool can_assign_any = gAgent.hasPowerInGroup(mGroupID,
GP_ROLE_ASSIGN_MEMBER);
bool can_assign_limited = gAgent.hasPowerInGroup(mGroupID,
@@ -492,7 +492,7 @@ void LLPanelGroupInvite::addUsers(uuid_vec_t& agent_ids)
}
else
{
- LL_WARNS() << "llPanelGroupInvite: Selected avatar has no name: " << dest->getID() << LL_ENDL;
+ llwarns << "llPanelGroupInvite: Selected avatar has no name: " << dest->getID() << llendl;
names.push_back("(Unknown)");
}
}
@@ -579,7 +579,7 @@ void LLPanelGroupInvite::updateLists()
{
waiting = true;
}
- if (gdatap->isRoleDataComplete() && gdatap->isMemberDataComplete())
+ if (gdatap->isRoleDataComplete() && gdatap->isMemberDataComplete() && gdatap->isRoleMemberDataComplete())
{
if ( mImplementation->mRoleNames )
{
@@ -607,6 +607,7 @@ void LLPanelGroupInvite::updateLists()
{
LLGroupMgr::getInstance()->sendGroupPropertiesRequest(mImplementation->mGroupID);
LLGroupMgr::getInstance()->sendGroupRoleDataRequest(mImplementation->mGroupID);
+ LLGroupMgr::getInstance()->sendGroupRoleMembersRequest(mImplementation->mGroupID);
LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mImplementation->mGroupID);
}
mPendingUpdate = TRUE;
@@ -654,7 +655,7 @@ BOOL LLPanelGroupInvite::postBuild()
}
mImplementation->mOKButton =
- getChild<LLButton>("ok_button", recurse);
+ getChild<LLButton>("invite_button", recurse);
if ( mImplementation->mOKButton )
{
mImplementation->mOKButton->setClickedCallback(impl::callbackClickOK, mImplementation);
diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp
index 9ca3d7e96e..1d7ba4d741 100755
--- a/indra/newview/llpanelgrouproles.cpp
+++ b/indra/newview/llpanelgrouproles.cpp
@@ -1,4 +1,4 @@
-/**
+/**
* @file llpanelgrouproles.cpp
* @brief Panel for roles information about a particular group.
*
@@ -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,10 @@ bool agentCanAddToRole(const LLUUID& group_id,
return false;
}
-// static
+// LLPanelGroupRoles /////////////////////////////////////////////////////
+
+// static
LLPanelGroupRoles::LLPanelGroupRoles()
: LLPanelGroupTab(),
mCurrentTab(NULL),
@@ -297,7 +300,6 @@ bool LLPanelGroupRoles::onModalClose(const LLSD& notification, const LLSD& respo
return false;
}
-
bool LLPanelGroupRoles::apply(std::string& mesg)
{
// Pass this along to the currently visible sub tab.
@@ -334,7 +336,6 @@ void LLPanelGroupRoles::update(LLGroupChange gc)
{
if (mGroupID.isNull()) return;
-
LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
if (panelp)
{
@@ -351,39 +352,33 @@ void LLPanelGroupRoles::activate()
{
// Start requesting member and role data if needed.
LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
- //if (!gdatap || mFirstUse)
+ if (!gdatap || !gdatap->isMemberDataComplete() )
{
- // Check member data.
-
- if (!gdatap || !gdatap->isMemberDataComplete() )
- {
- LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mGroupID);
- }
-
- // Check role data.
- if (!gdatap || !gdatap->isRoleDataComplete() )
- {
- // Mildly hackish - clear all pending changes
- cancel();
+ LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mGroupID);
+ }
- LLGroupMgr::getInstance()->sendGroupRoleDataRequest(mGroupID);
- }
+ if (!gdatap || !gdatap->isRoleDataComplete() )
+ {
+ // Mildly hackish - clear all pending changes
+ cancel();
- // Check role-member mapping data.
- if (!gdatap || !gdatap->isRoleMemberDataComplete() )
- {
- LLGroupMgr::getInstance()->sendGroupRoleMembersRequest(mGroupID);
- }
+ LLGroupMgr::getInstance()->sendGroupRoleDataRequest(mGroupID);
+ }
- // Need this to get base group member powers
- if (!gdatap || !gdatap->isGroupPropertiesDataComplete() )
- {
- LLGroupMgr::getInstance()->sendGroupPropertiesRequest(mGroupID);
- }
+ // Check role-member mapping data.
+ if (!gdatap || !gdatap->isRoleMemberDataComplete() )
+ {
+ LLGroupMgr::getInstance()->sendGroupRoleMembersRequest(mGroupID);
+ }
- mFirstUse = FALSE;
+ // Need this to get base group member powers
+ if (!gdatap || !gdatap->isGroupPropertiesDataComplete() )
+ {
+ LLGroupMgr::getInstance()->sendGroupPropertiesRequest(mGroupID);
}
+ mFirstUse = FALSE;
+
LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
if (panelp) panelp->activate();
}
@@ -412,15 +407,38 @@ BOOL LLPanelGroupRoles::hasModal()
return panelp->hasModal();
}
+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(1);
+ group_roles_tab->mFirstOpen = TRUE;
+ activate();
+}
+
-////////////////////////////
-// LLPanelGroupSubTab
-////////////////////////////
+// LLPanelGroupSubTab ////////////////////////////////////////////////////
LLPanelGroupSubTab::LLPanelGroupSubTab()
: LLPanelGroupTab(),
mHeader(NULL),
mFooter(NULL),
mActivated(false),
+ mHasGroupBanPower(false),
mSearchEditor(NULL)
{
}
@@ -542,9 +560,10 @@ void LLPanelGroupSubTab::buildActionsList(LLScrollListCtrl* ctrl,
return;
}
+ mHasGroupBanPower = false;
+
std::vector<LLRoleActionSet*>::iterator ras_it = LLGroupMgr::getInstance()->mRoleActionSets.begin();
std::vector<LLRoleActionSet*>::iterator ras_end = LLGroupMgr::getInstance()->mRoleActionSets.end();
-
for ( ; ras_it != ras_end; ++ras_it)
{
buildActionCategory(ctrl,
@@ -674,6 +693,31 @@ void LLPanelGroupSubTab::buildActionCategory(LLScrollListCtrl* ctrl,
row["columns"][column_index]["value"] = (*ra_it)->mDescription;
row["columns"][column_index]["font"] = "SANSSERIF_SMALL";
+ if(mHasGroupBanPower)
+ {
+ // The ban ability is being set. Prevent these abilities from being manipulated
+ if((*ra_it)->mPowerBit == GP_MEMBER_EJECT)
+ {
+ row["enabled"] = false;
+ }
+ else if((*ra_it)->mPowerBit == GP_ROLE_REMOVE_MEMBER)
+ {
+ row["enabled"] = false;
+ }
+ }
+ else
+ {
+ // The ban ability is not set. Allow these abilities to be manipulated
+ if((*ra_it)->mPowerBit == GP_MEMBER_EJECT)
+ {
+ row["enabled"] = true;
+ }
+ else if((*ra_it)->mPowerBit == GP_ROLE_REMOVE_MEMBER)
+ {
+ row["enabled"] = true;
+ }
+ }
+
LLScrollListItem* item = ctrl->addElement(row, ADD_BOTTOM, (*ra_it));
if (-1 != check_box_index)
@@ -709,6 +753,15 @@ void LLPanelGroupSubTab::buildActionCategory(LLScrollListCtrl* ctrl,
check->setTentative(TRUE);
}
}
+
+ // Regardless of whether or not this ability is allowed by all or some, we want to prevent
+ // the group managers from accidentally disabling either of the two additional abilities
+ // tied with GP_GROUP_BAN_ACCESS.
+ if( (allowed_by_all & GP_GROUP_BAN_ACCESS) == GP_GROUP_BAN_ACCESS ||
+ (allowed_by_some & GP_GROUP_BAN_ACCESS) == GP_GROUP_BAN_ACCESS)
+ {
+ mHasGroupBanPower = true;
+ }
}
}
@@ -728,11 +781,8 @@ void LLPanelGroupSubTab::setFooterEnabled(BOOL enable)
}
}
-////////////////////////////
-// LLPanelGroupMembersSubTab
-////////////////////////////
-
+// LLPanelGroupMembersSubTab /////////////////////////////////////////////
static LLPanelInjector<LLPanelGroupMembersSubTab> t_panel_group_members_subtab("panel_group_members_subtab");
LLPanelGroupMembersSubTab::LLPanelGroupMembersSubTab()
@@ -813,6 +863,13 @@ 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;
}
@@ -826,34 +883,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)
{
@@ -882,7 +911,7 @@ void LLPanelGroupMembersSubTab::handleMemberSelect()
// Build a vector of all selected members, and gather allowed actions.
uuid_vec_t selected_members;
- U64 allowed_by_all = 0xffffffffffffLL;
+ U64 allowed_by_all = GP_ALL_POWERS; //0xFFFFffffFFFFffffLL;
U64 allowed_by_some = 0;
std::vector<LLScrollListItem*>::iterator itor;
@@ -919,8 +948,8 @@ void LLPanelGroupMembersSubTab::handleMemberSelect()
LLGroupMgrGroupData::role_list_t::iterator iter = gdatap->mRoles.begin();
LLGroupMgrGroupData::role_list_t::iterator end = gdatap->mRoles.end();
- BOOL can_eject_members = gAgent.hasPowerInGroup(mGroupID,
- GP_MEMBER_EJECT);
+ 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;
for( ; iter != end; ++iter)
@@ -967,6 +996,7 @@ void LLPanelGroupMembersSubTab::handleMemberSelect()
{
// Can't remove other owners.
cb_enable = FALSE;
+ can_ban_members = FALSE;
break;
}
}
@@ -986,14 +1016,14 @@ void LLPanelGroupMembersSubTab::handleMemberSelect()
}
// If anyone selected is in any role besides 'Everyone' then they can't be ejected.
- if (role_id.notNull() && (count > 0))
- {
+ if (role_id.notNull() && (count > 0))
+ {
can_eject_members = FALSE;
- if (role_id == gdatap->mOwnerRole)
- {
- member_is_owner = TRUE;
- }
- }
+ if (role_id == gdatap->mOwnerRole)
+ {
+ member_is_owner = TRUE;
+ }
+ }
LLRoleData rd;
if (gdatap->getRoleData(role_id,rd))
@@ -1050,7 +1080,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)
{
@@ -1063,10 +1096,41 @@ void LLPanelGroupMembersSubTab::handleMemberSelect()
if ( member_data && member_data->isInRole(gdatap->mOwnerRole) )
{
can_eject_members = TRUE;
+ //can_ban_members = TRUE;
}
}
+
+ }
+
+ // ... or we can eject them because we have all the requisite powers...
+ if( gAgent.hasPowerInGroup(mGroupID, GP_ROLE_REMOVE_MEMBER) &&
+ !member_is_owner)
+ {
+ if( gAgent.hasPowerInGroup(mGroupID, GP_MEMBER_EJECT))
+ {
+ can_eject_members = TRUE;
+ }
+
+ if( gAgent.hasPowerInGroup(mGroupID, GP_GROUP_BAN_ACCESS))
+ {
+ can_ban_members = TRUE;
+ }
}
+
+ uuid_vec_t::const_iterator member_iter = selected_members.begin();
+ uuid_vec_t::const_iterator member_end = selected_members.end();
+ for ( ; member_iter != member_end; ++member_iter)
+ {
+ // Don't count the agent.
+ if ((*member_iter) == gAgent.getID())
+ {
+ can_eject_members = FALSE;
+ can_ban_members = FALSE;
+ }
+ }
+
+ mBanBtn->setEnabled(can_ban_members);
mEjectBtn->setEnabled(can_eject_members);
}
@@ -1104,61 +1168,26 @@ void LLPanelGroupMembersSubTab::onEjectMembers(void *userdata)
}
void LLPanelGroupMembersSubTab::handleEjectMembers()
-{
+{
+ //send down an eject message
+ uuid_vec_t selected_members;
+
std::vector<LLScrollListItem*> selection = mMembersList->getAllSelected();
if (selection.empty()) return;
-
- S32 selection_count = selection.size();
- if (selection_count == 1)
- {
- LLSD args;
- LLUUID selected_avatar = mMembersList->getValue().asUUID();
- std::string fullname = LLSLURL("agent", selected_avatar, "inspect").getSLURLString();
- args["AVATAR_NAME"] = fullname;
- LLSD payload;
- LLNotificationsUtil::add("EjectGroupMemberWarning",
- args,
- payload,
- boost::bind(&LLPanelGroupMembersSubTab::handleEjectCallback, this, _1, _2));
- }
- else
- {
- LLSD args;
- args["COUNT"] = llformat("%d", selection_count);
- LLSD payload;
- LLNotificationsUtil::add("EjectGroupMembersWarning",
- args,
- payload,
- boost::bind(&LLPanelGroupMembersSubTab::handleEjectCallback, this, _1, _2));
- }
-}
-bool LLPanelGroupMembersSubTab::handleEjectCallback(const LLSD& notification, const LLSD& response)
-{
- S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
- if (0 == option) // Eject button
+ std::vector<LLScrollListItem*>::iterator itor;
+ for (itor = selection.begin() ;
+ itor != selection.end(); ++itor)
{
- //send down an eject message
- uuid_vec_t selected_members;
-
- std::vector<LLScrollListItem*> selection = mMembersList->getAllSelected();
- if (selection.empty()) return false;
-
- std::vector<LLScrollListItem*>::iterator itor;
- for (itor = selection.begin() ;
- itor != selection.end(); ++itor)
- {
- LLUUID member_id = (*itor)->getUUID();
- selected_members.push_back( member_id );
- }
-
- mMembersList->deleteSelectedItems();
-
- sendEjectNotifications(mGroupID, selected_members);
-
- LLGroupMgr::getInstance()->sendGroupMemberEjects(mGroupID, selected_members);
+ LLUUID member_id = (*itor)->getUUID();
+ selected_members.push_back( member_id );
}
- return false;
+
+ mMembersList->deleteSelectedItems();
+
+ sendEjectNotifications(mGroupID, selected_members);
+
+ LLGroupMgr::getInstance()->sendGroupMemberEjects(mGroupID, selected_members);
}
void LLPanelGroupMembersSubTab::sendEjectNotifications(const LLUUID& group_id, const uuid_vec_t& selected_members)
@@ -1186,12 +1215,11 @@ void LLPanelGroupMembersSubTab::handleRoleCheck(const LLUUID& role_id,
//add that the user is requesting to change the roles for selected
//members
- U64 powers_all_have = 0xffffffffffffLL;
+ U64 powers_all_have = GP_ALL_POWERS;
U64 powers_some_have = 0;
BOOL is_owner_role = ( gdatap->mOwnerRole == role_id );
LLUUID member_id;
-
std::vector<LLScrollListItem*> selection = mMembersList->getAllSelected();
if (selection.empty())
@@ -1202,7 +1230,6 @@ void LLPanelGroupMembersSubTab::handleRoleCheck(const LLUUID& role_id,
for (std::vector<LLScrollListItem*>::iterator itor = selection.begin() ;
itor != selection.end(); ++itor)
{
-
member_id = (*itor)->getUUID();
//see if we requested a change for this member before
@@ -1268,7 +1295,6 @@ void LLPanelGroupMembersSubTab::handleRoleCheck(const LLUUID& role_id,
FALSE);
}
-
// static
void LLPanelGroupMembersSubTab::onRoleCheck(LLUICtrl* ctrl, void* user_data)
{
@@ -1307,6 +1333,15 @@ void LLPanelGroupMembersSubTab::activate()
update(GC_ALL);
mActivated = true;
}
+ else
+ {
+ LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
+ // Members can be removed outside of this tab, checking changes
+ if (!gdatap || (gdatap->isMemberDataComplete() && gdatap->mMembers.size() != mMembersList->getItemCount()))
+ {
+ update(GC_MEMBER_DATA);
+ }
+ }
}
void LLPanelGroupMembersSubTab::deactivate()
@@ -1701,13 +1736,12 @@ void LLPanelGroupMembersSubTab::updateMembers()
return;
}
- //cleanup list only for first iretation
+ //cleanup list only for first iteration
if(mMemberProgress == gdatap->mMembers.begin())
{
mMembersList->deleteAllItems();
}
-
LLGroupMgrGroupData::member_list_t::iterator end = gdatap->mMembers.end();
LLTimer update_time;
@@ -1764,12 +1798,44 @@ void LLPanelGroupMembersSubTab::updateMembers()
handleMemberSelect();
}
+void LLPanelGroupMembersSubTab::onBanMember(void* user_data)
+{
+ LLPanelGroupMembersSubTab* self = static_cast<LLPanelGroupMembersSubTab*>(user_data);
+ self->handleBanMember();
+}
+void LLPanelGroupMembersSubTab::handleBanMember()
+{
+ LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
+ if(!gdatap)
+ {
+ LL_WARNS("Groups") << "Unable to get group data for group " << mGroupID << LL_ENDL;
+ return;
+ }
-////////////////////////////
-// LLPanelGroupRolesSubTab
-////////////////////////////
+ std::vector<LLScrollListItem*> selection = mMembersList->getAllSelected();
+ if(selection.empty())
+ {
+ return;
+ }
+ uuid_vec_t ban_ids;
+ std::vector<LLScrollListItem*>::iterator itor;
+ for(itor = selection.begin(); itor != selection.end(); ++itor)
+ {
+ LLUUID ban_id = (*itor)->getUUID();
+ ban_ids.push_back(ban_id);
+
+ LLGroupBanData ban_data;
+ gdatap->createBanEntry(ban_id, ban_data);
+ }
+
+ LLGroupMgr::getInstance()->sendGroupBanRequest(LLGroupMgr::REQUEST_POST, mGroupID, LLGroupMgr::BAN_CREATE, ban_ids);
+ handleEjectMembers();
+}
+
+
+// LLPanelGroupRolesSubTab ///////////////////////////////////////////////
static LLPanelInjector<LLPanelGroupRolesSubTab> t_panel_group_roles_subtab("panel_group_roles_subtab");
LLPanelGroupRolesSubTab::LLPanelGroupRolesSubTab()
@@ -1783,7 +1849,7 @@ LLPanelGroupRolesSubTab::LLPanelGroupRolesSubTab()
mMemberVisibleCheck(NULL),
mDeleteRoleButton(NULL),
mCreateRoleButton(NULL),
-
+ mFirstOpen(TRUE),
mHasRoleChange(FALSE)
{
}
@@ -1885,6 +1951,7 @@ void LLPanelGroupRolesSubTab::deactivate()
LL_DEBUGS() << "LLPanelGroupRolesSubTab::deactivate()" << LL_ENDL;
LLPanelGroupSubTab::deactivate();
+ mFirstOpen = FALSE;
}
bool LLPanelGroupRolesSubTab::needsApply(std::string& mesg)
@@ -1892,6 +1959,12 @@ bool LLPanelGroupRolesSubTab::needsApply(std::string& mesg)
LL_DEBUGS() << "LLPanelGroupRolesSubTab::needsApply()" << LL_ENDL;
LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
+ if(!gdatap)
+ {
+ llwarns << "Unable to get group data for group " << mGroupID << llendl;
+ return false;
+ }
+
return (mHasRoleChange // Text changed in current role
|| (gdatap && gdatap->pendingRoleChanges())); // Pending role changes in the group
@@ -1902,7 +1975,7 @@ bool LLPanelGroupRolesSubTab::apply(std::string& mesg)
LL_DEBUGS() << "LLPanelGroupRolesSubTab::apply()" << LL_ENDL;
saveRoleChanges(true);
-
+ mFirstOpen = FALSE;
LLGroupMgr::getInstance()->sendGroupRoleChanges(mGroupID);
notifyObservers();
@@ -2039,14 +2112,17 @@ void LLPanelGroupRolesSubTab::update(LLGroupChange gc)
}
}
- if (!gdatap || !gdatap->isMemberDataComplete())
+ if(!mFirstOpen)
{
- LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mGroupID);
- }
-
- if (!gdatap || !gdatap->isRoleMemberDataComplete())
- {
- LLGroupMgr::getInstance()->sendGroupRoleMembersRequest(mGroupID);
+ if (!gdatap || !gdatap->isMemberDataComplete())
+ {
+ LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mGroupID);
+ }
+
+ if (!gdatap || !gdatap->isRoleMemberDataComplete())
+ {
+ LLGroupMgr::getInstance()->sendGroupRoleMembersRequest(mGroupID);
+ }
}
if ((GC_ROLE_MEMBER_DATA == gc || GC_MEMBER_DATA == gc)
@@ -2062,6 +2138,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();
}
@@ -2241,40 +2320,115 @@ void LLPanelGroupRolesSubTab::handleActionCheck(LLUICtrl* ctrl, bool force)
LLRoleAction* rap = (LLRoleAction*)action_item->getUserdata();
U64 power = rap->mPowerBit;
- if (check->get())
+ bool isEnablingAbility = check->get();
+ LLRoleData rd;
+ LLSD args;
+
+ if (isEnablingAbility &&
+ !force &&
+ ((GP_ROLE_ASSIGN_MEMBER == power) || (GP_ROLE_CHANGE_ACTIONS == power) ))
{
- if (!force && ( (GP_ROLE_ASSIGN_MEMBER == power)
- || (GP_ROLE_CHANGE_ACTIONS == power) ))
- {
- // Uncheck the item, for now. It will be
- // checked if they click 'Yes', below.
- check->set(FALSE);
+ // Uncheck the item, for now. It will be
+ // checked if they click 'Yes', below.
+ check->set(FALSE);
- LLRoleData rd;
- LLSD args;
+ LLRoleData rd;
+ LLSD args;
- if ( gdatap->getRoleData(role_id, rd) )
+ if ( gdatap->getRoleData(role_id, rd) )
+ {
+ args["ACTION_NAME"] = rap->mDescription;
+ args["ROLE_NAME"] = rd.mRoleName;
+ mHasModal = TRUE;
+ std::string warning = "AssignDangerousActionWarning";
+ if (GP_ROLE_CHANGE_ACTIONS == power)
{
- args["ACTION_NAME"] = rap->mDescription;
- args["ROLE_NAME"] = rd.mRoleName;
- mHasModal = TRUE;
- std::string warning = "AssignDangerousActionWarning";
- if (GP_ROLE_CHANGE_ACTIONS == power)
- {
- warning = "AssignDangerousAbilityWarning";
- }
- LLNotificationsUtil::add(warning, args, LLSD(), boost::bind(&LLPanelGroupRolesSubTab::addActionCB, this, _1, _2, check));
+ warning = "AssignDangerousAbilityWarning";
}
- else
- {
+ LLNotificationsUtil::add(warning, args, LLSD(), boost::bind(&LLPanelGroupRolesSubTab::addActionCB, this, _1, _2, check));
+ }
+ else
+ {
LL_WARNS() << "Unable to look up role information for role id: "
<< role_id << LL_ENDL;
+ }
+ }
+
+ if(GP_GROUP_BAN_ACCESS == power)
+ {
+ std::string warning = isEnablingAbility ? "AssignBanAbilityWarning" : "RemoveBanAbilityWarning";
+
+ //////////////////////////////////////////////////////////////////////////
+ // Get role data for both GP_ROLE_REMOVE_MEMBER and GP_MEMBER_EJECT
+ // Add description and role name to LLSD
+ // Pop up dialog saying "Yo, you also granted these other abilities when you did this!"
+ if ( gdatap->getRoleData(role_id, rd) )
+ {
+ args["ACTION_NAME"] = rap->mDescription;
+ args["ROLE_NAME"] = rd.mRoleName;
+ mHasModal = TRUE;
+
+ std::vector<LLScrollListItem*> all_data = mAllowedActionsList->getAllData();
+ std::vector<LLScrollListItem*>::iterator ad_it = all_data.begin();
+ std::vector<LLScrollListItem*>::iterator ad_end = all_data.end();
+ LLRoleAction* adp;
+ for( ; ad_it != ad_end; ++ad_it)
+ {
+ adp = (LLRoleAction*)(*ad_it)->getUserdata();
+ if(adp->mPowerBit == GP_MEMBER_EJECT)
+ {
+ args["ACTION_NAME_2"] = adp->mDescription;
+ }
+ else if(adp->mPowerBit == GP_ROLE_REMOVE_MEMBER)
+ {
+ args["ACTION_NAME_3"] = adp->mDescription;
+ }
}
+
+ LLNotificationsUtil::add(warning, args);
}
else
{
- gdatap->addRolePower(role_id,power);
+ llwarns << "Unable to look up role information for role id: "
+ << role_id << llendl;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+
+ LLGroupMgrGroupData::role_list_t::iterator rit = gdatap->mRoles.find(role_id);
+ U64 current_role_powers = GP_NO_POWERS;
+ if (rit != gdatap->mRoles.end())
+ {
+ current_role_powers = ((*rit).second->getRoleData().mRolePowers);
+ }
+
+ if(isEnablingAbility)
+ {
+ power |= (GP_ROLE_REMOVE_MEMBER | GP_MEMBER_EJECT);
+ current_role_powers |= power;
}
+ else
+ {
+ current_role_powers &= ~GP_GROUP_BAN_ACCESS;
+ }
+
+ mAllowedActionsList->deleteAllItems();
+ buildActionsList( mAllowedActionsList,
+ current_role_powers,
+ current_role_powers,
+ boost::bind(&LLPanelGroupRolesSubTab::handleActionCheck, this, _1, false),
+ TRUE,
+ FALSE,
+ FALSE);
+
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Adding non-specific ability to role
+ //////////////////////////////////////////////////////////////////////////
+ if(isEnablingAbility)
+ {
+ gdatap->addRolePower(role_id, power);
}
else
{
@@ -2283,6 +2437,7 @@ void LLPanelGroupRolesSubTab::handleActionCheck(LLUICtrl* ctrl, bool force)
mHasRoleChange = TRUE;
notifyObservers();
+
}
bool LLPanelGroupRolesSubTab::addActionCB(const LLSD& notification, const LLSD& response, LLCheckBoxCtrl* check)
@@ -2302,7 +2457,6 @@ bool LLPanelGroupRolesSubTab::addActionCB(const LLSD& notification, const LLSD&
return false;
}
-
// static
void LLPanelGroupRolesSubTab::onPropertiesKey(LLLineEditor* ctrl, void* user_data)
{
@@ -2480,13 +2634,28 @@ void LLPanelGroupRolesSubTab::saveRoleChanges(bool select_saved_role)
mHasRoleChange = FALSE;
}
}
-////////////////////////////
-// LLPanelGroupActionsSubTab
-////////////////////////////
-static LLPanelInjector<LLPanelGroupActionsSubTab> t_panel_group_actions_subtab("panel_group_actions_subtab");
+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 /////////////////////////////////////////////
+static LLPanelInjector<LLPanelGroupActionsSubTab> t_panel_group_actions_subtab("panel_group_actions_subtab");
+
LLPanelGroupActionsSubTab::LLPanelGroupActionsSubTab()
: LLPanelGroupSubTab()
{
@@ -2659,26 +2828,298 @@ 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 LLPanelInjector<LLPanelGroupBanListSubTab> t_panel_group_ban_subtab("panel_group_banlist_subtab");
+
+LLPanelGroupBanListSubTab::LLPanelGroupBanListSubTab()
+ : LLPanelGroupSubTab(),
+ mBanList(NULL),
+ mCreateBanButton(NULL),
+ mDeleteBanButton(NULL)
+{}
+
+BOOL LLPanelGroupBanListSubTab::postBuildSubTab(LLView* root)
+{
+ 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");
+ 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);
+ mRefreshBanListButton = parent->getChild<LLButton>("ban_refresh", recurse);
+ mBanCountText = parent->getChild<LLTextBase>("ban_count", 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 || !mRefreshBanListButton || !mBanCountText)
+ 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(1);
+ mCreateBanButton->setClickedCallback(onCreateBanEntry, this);
+ mCreateBanButton->setEnabled(FALSE);
- activate();
+ mDeleteBanButton->setClickedCallback(onDeleteBanEntry, this);
+ mDeleteBanButton->setEnabled(FALSE);
+
+ mRefreshBanListButton->setClickedCallback(onRefreshBanList, this);
+ mRefreshBanListButton->setEnabled(FALSE);
+
+ setBanCount(0);
+
+ mBanList->setOnNameListCompleteCallback(boost::bind(&LLPanelGroupBanListSubTab::onBanListCompleted, this, _1));
+
+ populateBanList();
+
+ setFooterEnabled(FALSE);
+ return TRUE;
}
+void LLPanelGroupBanListSubTab::activate()
+{
+ LLPanelGroupSubTab::activate();
+
+ mBanList->deselectAllItems();
+ mDeleteBanButton->setEnabled(FALSE);
+ LLGroupMgrGroupData * group_datap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
+ if (group_datap)
+ {
+ mCreateBanButton->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_GROUP_BAN_ACCESS) &&
+ group_datap->mBanList.size() < GB_MAX_BANNED_AGENTS);
+ setBanCount(group_datap->mBanList.size());
+ }
+ else
+ {
+ mCreateBanButton->setEnabled(FALSE);
+ setBanCount(0);
+ }
+
+ // BAKER: Should I really request everytime activate() is called?
+ // Perhaps I should only do it on a force refresh, or if an action on the list happens...
+ // Because it's not going to live-update the list anyway... You'd have to refresh if you
+ // wanted to see someone else's additions anyway...
+ //
+ LLGroupMgr::getInstance()->sendGroupBanRequest(LLGroupMgr::REQUEST_GET, mGroupID);
+
+ setFooterEnabled(FALSE);
+ update(GC_ALL);
+}
+
+void LLPanelGroupBanListSubTab::update(LLGroupChange gc)
+{
+ populateBanList();
+}
+
+void LLPanelGroupBanListSubTab::draw()
+{
+ LLPanelGroupSubTab::draw();
+
+ // BAKER: Might be good to put it here instead of update, maybe.. See how often draw gets hit.
+ //if(
+ // populateBanList();
+}
+
+void LLPanelGroupBanListSubTab::onBanEntrySelect(LLUICtrl* ctrl, void* user_data)
+{
+ LLPanelGroupBanListSubTab* self = static_cast<LLPanelGroupBanListSubTab*>(user_data);
+ if (!self)
+ return;
+
+ self->handleBanEntrySelect();
+}
+
+void LLPanelGroupBanListSubTab::handleBanEntrySelect()
+{
+ if (gAgent.hasPowerInGroup(mGroupID, GP_GROUP_BAN_ACCESS))
+ {
+ mDeleteBanButton->setEnabled(TRUE);
+ }
+}
+
+void LLPanelGroupBanListSubTab::onCreateBanEntry(void* user_data)
+{
+ LLPanelGroupBanListSubTab* self = static_cast<LLPanelGroupBanListSubTab*>(user_data);
+ if (!self)
+ return;
+
+ self->handleCreateBanEntry();
+}
+
+void LLPanelGroupBanListSubTab::handleCreateBanEntry()
+{
+ LLFloaterGroupBulkBan::showForGroup(mGroupID);
+ //populateBanList();
+}
+
+void LLPanelGroupBanListSubTab::onDeleteBanEntry(void* user_data)
+{
+ LLPanelGroupBanListSubTab* self = static_cast<LLPanelGroupBanListSubTab*>(user_data);
+ if (!self)
+ return;
+
+ self->handleDeleteBanEntry();
+}
+
+void LLPanelGroupBanListSubTab::handleDeleteBanEntry()
+{
+ LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
+ if(!gdatap)
+ {
+ LL_WARNS("Groups") << "Unable to get group data for group " << mGroupID << LL_ENDL;
+ return;
+ }
+
+ std::vector<LLScrollListItem*> selection = mBanList->getAllSelected();
+ if(selection.empty())
+ {
+ return;
+ }
+
+ 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;
+ }
+ }
+
+ if(!can_ban_members)
+ return;
+
+ std::vector<LLUUID> ban_ids;
+ std::vector<LLScrollListItem*>::iterator itor;
+ for(itor = selection.begin(); itor != selection.end(); ++itor)
+ {
+ LLUUID ban_id = (*itor)->getUUID();
+ ban_ids.push_back(ban_id);
+
+ 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);
+ }
+
+ // update ban-count related elements
+ mCreateBanButton->setEnabled(TRUE);
+ setBanCount(gdatap->mBanList.size());
+
+ LLGroupMgr::getInstance()->sendGroupBanRequest(LLGroupMgr::REQUEST_POST, mGroupID, LLGroupMgr::BAN_DELETE, ban_ids);
+}
+
+void LLPanelGroupBanListSubTab::onRefreshBanList(void* user_data)
+{
+ LLPanelGroupBanListSubTab* self = static_cast<LLPanelGroupBanListSubTab*>(user_data);
+ if (!self)
+ return;
+
+ self->handleRefreshBanList();
+}
+
+void LLPanelGroupBanListSubTab::handleRefreshBanList()
+{
+ mRefreshBanListButton->setEnabled(FALSE);
+ LLGroupMgr::getInstance()->sendGroupBanRequest(LLGroupMgr::REQUEST_GET, mGroupID);
+}
+
+void LLPanelGroupBanListSubTab::onBanListCompleted(bool isComplete)
+{
+ if(isComplete)
+ {
+ mRefreshBanListButton->setEnabled(TRUE);
+ populateBanList();
+ }
+}
+
+void LLPanelGroupBanListSubTab::setBanCount(U32 ban_count)
+{
+ LLStringUtil::format_map_t args;
+ args["[COUNT]"] = llformat("%d", ban_count);
+ args["[LIMIT]"] = llformat("%d", GB_MAX_BANNED_AGENTS);
+ mBanCountText->setText(getString("ban_count_template", args));
+}
+
+void LLPanelGroupBanListSubTab::populateBanList()
+{
+ LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
+ if(!gdatap)
+ {
+ LL_WARNS("Groups") << "Unable to get group data for group " << mGroupID << LL_ENDL;
+ return;
+ }
+
+ mBanList->deleteAllItems();
+ 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;
+ LLGroupBanData bd = entry->second;
+
+ ban_entry.columns.add().column("name").font.name("SANSSERIF_SMALL").style("NORMAL");
+
+ // Baker TODO: MAINT-
+ // Check out utc_to_pacific_time()
+
+ std::string ban_date_str = bd.mBanDate.toHTTPDateString("%Y/%m/%d");
+// time_t utc_time;
+// utc_time = time_corrected();
+// LLSD substitution;
+// substitution["datetime"] = (S32) utc_time;
+// LLStringUtil::format (ban_date_str, substitution);
+
+ //LL_INFOS("BAKER") << "[BAKER] BAN_DATE: " << bd.mBanDate.toHTTPDateString("%Y/%m/%d") << LL_ENDL;
+ //LL_INFOS("BAKER") << "[BAKER] BAN_DATE_MODIFIED: " << ban_date_str << LL_ENDL;
+
+ //ban_entry.columns.add().column("ban_date").value(ban_date_str.font.name("SANSSERIF_SMALL").style("NORMAL");
+ ban_entry.columns.add().column("ban_date").value(bd.mBanDate.toHTTPDateString("%Y/%m/%d")).font.name("SANSSERIF_SMALL").style("NORMAL");
+
+ mBanList->addNameItemRow(ban_entry);
+ }
+
+ mRefreshBanListButton->setEnabled(TRUE);
+ mCreateBanButton->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_GROUP_BAN_ACCESS) &&
+ gdatap->mBanList.size() < GB_MAX_BANNED_AGENTS);
+ setBanCount(gdatap->mBanList.size());
+}
+
+void LLPanelGroupBanListSubTab::setGroupID(const LLUUID& id)
+{
+ if(mBanList)
+ mBanList->deleteAllItems();
+
+ setFooterEnabled(FALSE);
+ LLPanelGroupSubTab::setGroupID(id);
+}
diff --git a/indra/newview/llpanelgrouproles.h b/indra/newview/llpanelgrouproles.h
index baa2d40c7e..540b24ada6 100755
--- a/indra/newview/llpanelgrouproles.h
+++ b/indra/newview/llpanelgrouproles.h
@@ -39,11 +39,9 @@ class LLScrollListCtrl;
class LLScrollListItem;
class LLTextEditor;
-// Forward declare for friend usage.
-//virtual BOOL LLPanelGroupSubTab::postBuildSubTab(LLView*);
-
typedef std::map<std::string,std::string> icon_map_t;
+
class LLPanelGroupRoles : public LLPanelGroupTab
{
public:
@@ -92,6 +90,7 @@ protected:
std::string mWantApplyMesg;
};
+
class LLPanelGroupSubTab : public LLPanelGroupTab
{
public:
@@ -143,10 +142,14 @@ protected:
icon_map_t mActionIcons;
bool mActivated;
-
+
+ bool mHasGroupBanPower; // Used to communicate between action sets due to the dependency between
+ // GP_GROUP_BAN_ACCESS and GP_EJECT_MEMBER and GP_ROLE_REMOVE_MEMBER
+
void setOthersVisible(BOOL b);
};
+
class LLPanelGroupMembersSubTab : public LLPanelGroupSubTab
{
public:
@@ -173,6 +176,10 @@ public:
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);
@@ -206,6 +213,7 @@ protected:
LLScrollListCtrl* mAssignedRolesList;
LLScrollListCtrl* mAllowedActionsList;
LLButton* mEjectBtn;
+ LLButton* mBanBtn;
BOOL mChanged;
BOOL mPendingMemberUpdate;
@@ -219,6 +227,7 @@ protected:
avatar_name_cache_connection_map_t mAvatarNameCacheConnections;
};
+
class LLPanelGroupRolesSubTab : public LLPanelGroupSubTab
{
public:
@@ -241,7 +250,7 @@ public:
static void onActionCheck(LLUICtrl*, void*);
bool addActionCB(const LLSD& notification, const LLSD& response, LLCheckBoxCtrl* check);
-
+
static void onPropertiesKey(LLLineEditor*, void*);
void onDescriptionKeyStroke(LLTextEditor* caller);
@@ -260,6 +269,9 @@ public:
void saveRoleChanges(bool select_saved_role);
virtual void setGroupID(const LLUUID& id);
+
+ BOOL mFirstOpen;
+
protected:
void handleActionCheck(LLUICtrl* ctrl, bool force);
LLSD createRoleItem(const LLUUID& role_id, std::string name, std::string title, S32 members);
@@ -281,6 +293,7 @@ protected:
std::string mRemoveEveryoneTxt;
};
+
class LLPanelGroupActionsSubTab : public LLPanelGroupSubTab
{
public:
@@ -308,4 +321,46 @@ protected:
};
+class LLPanelGroupBanListSubTab : public LLPanelGroupSubTab
+{
+public:
+ LLPanelGroupBanListSubTab();
+ virtual ~LLPanelGroupBanListSubTab() {}
+
+ virtual BOOL postBuildSubTab(LLView* root);
+
+ virtual void activate();
+ virtual void update(LLGroupChange gc);
+ virtual void draw();
+
+ static void onBanEntrySelect(LLUICtrl* ctrl, void* user_data);
+ void handleBanEntrySelect();
+
+ static void onCreateBanEntry(void* user_data);
+ void handleCreateBanEntry();
+
+ static void onDeleteBanEntry(void* user_data);
+ void handleDeleteBanEntry();
+
+ static void onRefreshBanList(void* user_data);
+ void handleRefreshBanList();
+
+ void onBanListCompleted(bool isComplete);
+
+protected:
+ void setBanCount(U32 ban_count);
+ void populateBanList();
+
+public:
+ virtual void setGroupID(const LLUUID& id);
+
+protected:
+ LLNameListCtrl* mBanList;
+ LLButton* mCreateBanButton;
+ LLButton* mDeleteBanButton;
+ LLButton* mRefreshBanListButton;
+ LLTextBase* mBanCountText;
+
+};
+
#endif // LL_LLPANELGROUPROLES_H
diff --git a/indra/newview/llpanelplaceprofile.cpp b/indra/newview/llpanelplaceprofile.cpp
index 0817b677bc..ed91d277dd 100755
--- a/indra/newview/llpanelplaceprofile.cpp
+++ b/indra/newview/llpanelplaceprofile.cpp
@@ -488,8 +488,9 @@ void LLPanelPlaceProfile::displaySelectedParcelInfo(LLParcel* parcel,
gCacheName->getGroup(parcel->getGroupID(),
boost::bind(&LLPanelPlaceInfo::onNameCache, mRegionGroupText, _2));
- gCacheName->getGroup(parcel->getGroupID(),
- boost::bind(&LLPanelPlaceInfo::onNameCache, mParcelOwner, _2));
+ std::string owner =
+ LLSLURL("group", parcel->getGroupID(), "inspect").getSLURLString();
+ mParcelOwner->setText(owner);
}
else
{
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index 32686a10c9..0cde354af9 100755
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -2709,6 +2709,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
capabilityNames.append("GetObjectCost");
capabilityNames.append("GetObjectPhysicsData");
capabilityNames.append("GetTexture");
+ capabilityNames.append("GroupAPIv1");
capabilityNames.append("GroupMemberData");
capabilityNames.append("GroupProposalBallot");
capabilityNames.append("HomeLocation");
diff --git a/indra/newview/roles_constants.h b/indra/newview/roles_constants.h
index effd15ea72..8fd7978fa1 100755..100644
--- a/indra/newview/roles_constants.h
+++ b/indra/newview/roles_constants.h
@@ -53,98 +53,100 @@ enum LLRoleChangeType
// KNOWN HOLES: use these for any single bit powers you need
// bit 0x1 << 46
-// bit 0x1 << 49 and above
+// bit 0x1 << 52 and above
// These powers were removed to make group roles simpler
// bit 0x1 << 41 (GP_ACCOUNTING_VIEW)
// bit 0x1 << 46 (GP_PROPOSAL_VIEW)
const U64 GP_NO_POWERS = 0x0;
-const U64 GP_ALL_POWERS = 0xFFFFFFFFFFFFFFFFLL;
+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 << 51; // 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/newview/skins/default/xui/en/floater_about.xml b/indra/newview/skins/default/xui/en/floater_about.xml
index 9668cfa526..ef2f158a86 100755
--- a/indra/newview/skins/default/xui/en/floater_about.xml
+++ b/indra/newview/skins/default/xui/en/floater_about.xml
@@ -72,7 +72,7 @@ Second Life is brought to you by the Lindens:
top_pad="10"
width="435"
word_wrap="true">
-Philip, Andrew, Doug, Richard, Phoenix, Ian, Mark, Robin, Dan, Char, Ryan, Eric, Jim, Lee, Jeff, Michael, Kelly, Steve, Catherine, Bub, Ramzi, Jill, Jeska, Don, Kona, Callum, Charity, Jack, Shawn, babbage, James, Lauren, Blue, Brent, Reuben, Pathfinder, Jesse, Patsy, Torley, Bo, Cyn, Jonathan, Gia, Annette, Ginsu, Harry, Lex, Runitai, Guy, Cornelius, Beth, Swiss, Thumper, Wendy, Teeple, Seth, Dee, Mia, Sally, Liana, Aura, Beez, Milo, Red, Gulliver, Marius, Joe, Jose, Dore, Justin, Nora, Morpheus, Lexie, Amber, Chris, Xan, Leyla, Walker, Sabin, Joshua, Hiromi, Tofu, Fritz, June, Jean, Ivy, Dez, Ken, Betsy, Which, Spike, Rob, Zee, Dustin, George, Claudia, del, Matthew, jane, jay, Adrian, Yool, Rika, Yoz, siobhan, Qarl, Benjamin, Beast, Everett, madhavi, Christopher, Izzy, stephany, Jeremy, sean, adreanne, Pramod, Tobin, sejong, Iridium, maurice, kj, Meta, kari, JP, bert, kyle, Jon, Socrates, Bridie, Ivan, maria, Aric, Coco, Periapse, sandy, Storrs, Lotte, Colossus, Brad, Pastrami, Zen, BigPapi, Banzai, Sardonyx, Mani, Garry, Jaime, Neuro, Samuel, Niko, CeeLo, Austin, Soft, Poppy, emma, tessa, angelo, kurz, alexa, Sue, CG, Blake, Erica, Brett, Bevis, kristen, Q, simon, Enus, MJ, laurap, Kip, Scouse, Ron, Ram, kend, Marty, Prospero, melissa, kraft, Nat, Seraph, Hamilton, Lordan, Green, miz, Ashlei, Trinity, Ekim, Echo, Charlie, Rowan, Rome, Jt, Doris, benoc, Christy, Bao, Kate, Tj, Patch, Cheah, Johan, Brandy, Angela, Oreh, Cogsworth, Lan, Mitchell, Space, Bambers, Einstein, Bender, Malbers, Matias, Maggie, Rothman, Milton, Niall, Marin, Allison, Mango, Andrea, Katt, Yi, Ambroff, Rico, Raymond, Gail, Christa, William, Dawn, Usi, Dynamike, M, Corr, Dante, Molly, kaylee, Danica, Kelv, Lil, jacob, Nya, Rodney, elsie, Blondin, Grant, Nyx, Devin, Monty, Minerva, Keira, Katie, Jenn, Makai, Clare, Joy, Cody, Gayathri, FJ, spider, Oskar, Landon, Jarv, Noelle, Al, Doc, Gray, Vir, t, Maestro, Simone, Shannon, yang, Courtney, Scott, charlene, Quixote, Susan, Zed, Amanda, Katelin, Esbee, JoRoan, Enkidu, roxie, Scarlet, Merov, Kevin, Judy, Rand, Newell, Les, Dessie, Galen, Michon, Geo, Siz, Calyle, Pete, Praveen, Callen, Sheldon, Pink, Nelson, jenelle, Terrence, Nathan, Juan, Sascha, Huseby, Karina, Kaye, Kotler, Lis, Darv, Charrell, Dakota, Kimmora, Theeba, Taka, Mae, Perry, Ducot, dana, Esther, Dough, gisele, Doten, Viale, Fisher, jessieann, ashley, Torres, delby, rountree, kurt, Slaton, Madison, Rue, Gino, Wen, Casssandra, Brodesky, Squid, Gez, Rakesh, Gecko, Ladan, Tony, Tatem, Squire, Falcon, BK, Crimp, Tiggs, Bacon, Coyot, Carmilla, Webb, Sea, Arch, Jillian, Jason, Bernard, Vogt, Peggy, dragon, Pup, xandix, Wallace, Bewest, Inoshiro, Rhett, AG, Aimee, Ghengis, Itiaes, Eli, Steffan, Epic, Grapes, Stone, Prep, Scobu, Robert, Alain, Carla, Vicky, Tia, Alec, Taras, Lisa, Oz, Ariane, Log, House, Kazu, Kim, Drofnas, Tyler, Campbell, Michele, Madeline, Nelly, Baron, Thor, Lori, Hele, Fredrik, Teddy, Pixie, Berry, Gabrielle, Alfonso, Brooke, Wolf, Ringo, Cru, Charlar, Rodvik, Gibson, Elise, Bagman, Greger, Leonidas, Jerm, Leslie, CB, Brenda, Durian, Carlo, mm, Zeeshan, Caleb, Max, Elikak, Mercille, Steph, Chase
+Philip, Andrew, Doug, Richard, Phoenix, Ian, Mark, Robin, Dan, Char, Ryan, Eric, Jim, Lee, Jeff, Michael, Kelly, Steve, Catherine, Bub, Ramzi, Jill, Jeska, Don, Kona, Callum, Charity, Jack, Shawn, babbage, James, Lauren, Blue, Brent, Reuben, Pathfinder, Jesse, Patsy, Torley, Bo, Cyn, Jonathan, Gia, Annette, Ginsu, Harry, Lex, Runitai, Guy, Cornelius, Beth, Swiss, Thumper, Wendy, Teeple, Seth, Dee, Mia, Sally, Liana, Aura, Beez, Milo, Red, Gulliver, Marius, Joe, Jose, Dore, Justin, Nora, Morpheus, Lexie, Amber, Chris, Xan, Leyla, Walker, Sabin, Joshua, Hiromi, Tofu, Fritz, June, Jean, Ivy, Dez, Ken, Betsy, Which, Spike, Rob, Zee, Dustin, George, Claudia, del, Matthew, jane, jay, Adrian, Yool, Rika, Yoz, siobhan, Qarl, Benjamin, Beast, Everett, madhavi, Christopher, Izzy, stephany, Jeremy, sean, adreanne, Pramod, Tobin, sejong, Iridium, maurice, kj, Meta, kari, JP, bert, kyle, Jon, Socrates, Bridie, Ivan, maria, Aric, Coco, Periapse, sandy, Storrs, Lotte, Colossus, Brad, Pastrami, Zen, BigPapi, Banzai, Sardonyx, Mani, Garry, Jaime, Neuro, Samuel, Niko, CeeLo, Austin, Soft, Poppy, emma, tessa, angelo, kurz, alexa, Sue, CG, Blake, Erica, Brett, Bevis, kristen, Q, simon, Enus, MJ, laurap, Kip, Scouse, Ron, Ram, kend, Marty, Prospero, melissa, kraft, Nat, Seraph, Hamilton, Lordan, Green, miz, Ashlei, Trinity, Ekim, Echo, Charlie, Rowan, Rome, Jt, Doris, benoc, Christy, Bao, Kate, Tj, Patch, Cheah, Johan, Brandy, Angela, Oreh, Cogsworth, Lan, Mitchell, Space, Bambers, Einstein, Bender, Malbers, Matias, Maggie, Rothman, Milton, Niall, Marin, Allison, Mango, Andrea, Katt, Yi, Ambroff, Rico, Raymond, Gail, Christa, William, Dawn, Usi, Dynamike, M, Corr, Dante, Molly, kaylee, Danica, Kelv, Lil, jacob, Nya, Rodney, elsie, Blondin, Grant, Nyx, Devin, Monty, Minerva, Keira, Katie, Jenn, Makai, Clare, Joy, Cody, Gayathri, FJ, spider, Oskar, Landon, Jarv, Noelle, Al, Doc, Gray, Vir, t, Maestro, Simone, Shannon, yang, Courtney, Scott, charlene, Quixote, Susan, Zed, Amanda, Katelin, Esbee, JoRoan, Enkidu, roxie, Scarlet, Merov, Kevin, Judy, Rand, Newell, Les, Dessie, Galen, Michon, Geo, Siz, Calyle, Pete, Praveen, Callen, Sheldon, Pink, Nelson, jenelle, Terrence, Nathan, Juan, Sascha, Huseby, Karina, Kaye, Kotler, Lis, Darv, Charrell, Dakota, Kimmora, Theeba, Taka, Mae, Perry, Ducot, dana, Esther, Dough, gisele, Doten, Viale, Fisher, jessieann, ashley, Torres, delby, rountree, kurt, Slaton, Madison, Rue, Gino, Wen, Casssandra, Brodesky, Squid, Gez, Rakesh, Gecko, Ladan, Tony, Tatem, Squire, Falcon, BK, Crimp, Tiggs, Bacon, Coyot, Carmilla, Webb, Sea, Arch, Jillian, Jason, Bernard, Vogt, Peggy, dragon, Pup, xandix, Wallace, Bewest, Inoshiro, Rhett, AG, Aimee, Ghengis, Itiaes, Eli, Steffan, Epic, Grapes, Stone, Prep, Scobu, Robert, Alain, Carla, Vicky, Tia, Alec, Taras, Lisa, Oz, Ariane, Log, House, Kazu, Kim, Drofnas, Tyler, Campbell, Michele, Madeline, Nelly, Baron, Thor, Lori, Hele, Fredrik, Teddy, Pixie, Berry, Gabrielle, Alfonso, Brooke, Wolf, Ringo, Cru, Charlar, Rodvik, Gibson, Elise, Bagman, Greger, Leonidas, Jerm, Leslie, CB, Brenda, Durian, Carlo, mm, Zeeshan, Caleb, Max, Elikak, Mercille, Steph, Chase, Baker
</text_editor>
<text
follows="top|left"
diff --git a/indra/newview/skins/default/xui/en/menu_conversation.xml b/indra/newview/skins/default/xui/en/menu_conversation.xml
index 31b1d091ee..ed362b36e5 100755
--- a/indra/newview/skins/default/xui/en/menu_conversation.xml
+++ b/indra/newview/skins/default/xui/en/menu_conversation.xml
@@ -17,6 +17,7 @@
layout="topleft"
name="open_voice_conversation">
<on_click function="Avatar.DoToSelected" parameter="open_voice_conversation"/>
+ <on_enable function="Avatar.EnableItem" parameter="can_open_voice_conversation"/>
</menu_item_call>
<menu_item_call
label="Disconnect from voice"
@@ -213,4 +214,12 @@
<on_enable function="Avatar.EnableItem" parameter="can_moderate_voice" />
</menu_item_call>
</context_menu>
+ <menu_item_separator layout="topleft" name="Group Ban Separator"/>
+ <menu_item_call
+ label="Ban member"
+ layout="topleft"
+ name="BanMember">
+ <on_click function="Avatar.DoToSelected" parameter="ban_member" />
+ <on_enable function="Avatar.EnableItem" parameter="can_ban_member" />
+ </menu_item_call>
</toggleable_menu>
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 062a93f3cf..02009e63e0 100755
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -589,7 +589,35 @@ Add this Ability to &apos;[ROLE_NAME]&apos;?
notext="No"
yestext="Yes"/>
</notification>
-
+
+ <notification
+ icon="alertmodal.tga"
+ name="AssignBanAbilityWarning"
+ type="alertmodal">
+You are about to add the Ability &apos;[ACTION_NAME]&apos; to the Role &apos;[ROLE_NAME]&apos;.
+
+ *WARNING*
+Any Member in a Role with this Ability will also be granted the Abilities &apos;[ACTION_NAME_2]&apos; and &apos;[ACTION_NAME_3]&apos;
+ <usetemplate
+ name="okbutton"
+ yestext="OK"/>
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="RemoveBanAbilityWarning"
+ type="alertmodal">
+You are removing the Ability &apos;[ACTION_NAME]&apos; to the Role &apos;[ROLE_NAME]&apos;.
+
+ *WARNING*
+Removing this ability will NOT remove the Abilities &apos;[ACTION_NAME_2]&apos; and &apos;[ACTION_NAME_3]&apos;.
+
+If you no longer wish to have these abilities granted to this role, disable them immediately!
+ <usetemplate
+ name="okbutton"
+ yestext="OK"/>
+ </notification>
+
<notification
icon="alertmodal.tga"
name="EjectGroupMemberWarning"
@@ -3830,6 +3858,28 @@ Leave Group?
</notification>
<notification
+ icon="aler.tga"
+ name="GroupDepartError"
+ type="alert">
+Unable to leave group: [reason].
+ <tag>reason</tag>
+ <usetemplate
+ name="okbutton"
+ yestext="OK"/>
+ </notification>
+
+ <notification
+ icon="alert.tga"
+ name="GroupDepart"
+ type="alert">
+You have left the group [group_name].
+ <tag>group_name</tag>
+ <usetemplate
+ name="okbutton"
+ yestext="OK"/>
+ </notification>
+
+ <notification
icon="alert.tga"
name="ConfirmKick"
type="alert">
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 0000000000..509dcf354e
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_group_bulk_ban.xml
@@ -0,0 +1,108 @@
+<?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 bans not sent: too many Residents selected. Group bans are limited to 100 per request.
+ </panel.string>
+ <panel.string
+ name="ban_not_permitted">
+ Group ban not sent: you do not have 'Manage ban list' ability.
+ </panel.string>
+ <panel.string
+ name="ban_limit_fail">
+ Group ban not sent: your group have reached limit of allowed ban records.
+ </panel.string>
+ <panel.string
+ name="partial_ban">
+ Some group bans were not sent:
+[REASONS]
+ </panel.string>
+ <panel.string
+ name="ban_failed">
+ Group bans were not sent:
+[REASONS]
+ </panel.string>
+ <panel.string
+ name="residents_already_banned">
+ - The following resident(s) are already banned: [RESIDENTS].
+ </panel.string>
+ <panel.string
+ name="ban_limit_reached">
+ - Ban limit reached, following agents not banned: [RESIDENTS].
+ </panel.string>
+ <panel.string
+ name="cant_ban_yourself">
+ - You cannot ban yourself from a group.
+ </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 Residents"
+ 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 124c0596c3..ebb460deb0 100755
--- a/indra/newview/skins/default/xui/en/panel_group_invite.xml
+++ b/indra/newview/skins/default/xui/en/panel_group_invite.xml
@@ -76,7 +76,7 @@
Choose what Role to assign them to:
</text>
<combo_box
- height="16"
+ height="20"
layout="topleft"
left_delta="0"
name="role_name"
@@ -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 9ac5b8800e..dac4371a38 100755
--- a/indra/newview/skins/default/xui/en/panel_group_roles.xml
+++ b/indra/newview/skins/default/xui/en/panel_group_roles.xml
@@ -118,6 +118,13 @@ clicking on their names.
left_pad="10"
name="member_eject"
width="100" />
+ <button
+ height="23"
+ label="Ban Member(s)"
+ follows="top|left"
+ left_pad="10"
+ name="member_ban"
+ width="100" />
</panel>
<panel
border="false"
@@ -138,454 +145,535 @@ clicking on their names.
right="-5"
name="show_all_button"
width="100" />-->
- <panel.string
- name="help_text">
- Roles have a title and an allowed list of Abilities
-that Members can perform. Members can belong to
-one or more Roles. A group can have up to 10 Roles,
-including the Everyone and Owner Roles.
- </panel.string>
- <panel.string
- name="cant_delete_role">
- The &apos;Everyone&apos; and &apos;Owners&apos; Roles are special and can't be deleted.
- </panel.string>
- <panel.string
- name="power_folder_icon" translate="false">
- Inv_FolderClosed
- </panel.string>
- <panel.string
- name="power_all_have_icon" translate="false">
- Checkbox_On
- </panel.string>
- <panel.string
- name="power_partial_icon" translate="false">
- Checkbox_Off
- </panel.string>
- <filter_editor
- layout="topleft"
- top="5"
- left="5"
- right="-5"
- height="22"
- search_button_visible="false"
- follows="left|top|right"
- label="Filter Roles"
- name="filter_input" />
- <scroll_list
- column_padding="0"
- draw_heading="true"
- draw_stripes="false"
- heading_height="23"
- height="132"
- layout="topleft"
- search_column="1"
- left="0"
- follows="left|top|right"
- right="-1"
- name="role_list"
- top_pad="2"
- width="310">
- <scroll_list.columns
- label="Role"
- name="name"
- relative_width="0.45" />
- <scroll_list.columns
- label="Title"
- name="title"
- relative_width="0.45" />
- <scroll_list.columns
- label="#"
- name="members"
- relative_width="0.15" />
- </scroll_list>
- <button
- follows="top|left"
- height="23"
- label="New Role"
- layout="topleft"
- left="0"
- name="role_create"
- width="120" />
- <button
- height="23"
- follows="top|left"
- label="Delete Role"
- layout="topleft"
- left_pad="10"
- name="role_delete"
- width="120" />
- </panel>
- <panel
- border="false"
- height="303"
- label="ABILITIES"
- layout="topleft"
- left="0"
- right="-1"
- help_topic="roles_actions_tab"
- name="actions_sub_tab"
- class="panel_group_actions_subtab"
- tool_tip="You can view an Ability&apos;s Description and which Roles and Members can execute the Ability."
- width="310">
- <panel.string
- name="help_text">
- Abilities allow Members in Roles to do specific
-things in this group. There&apos;s a broad variety of Abilities.
- </panel.string>
- <panel.string
- name="power_folder_icon" translate="false">
- Inv_FolderClosed
- </panel.string>
- <panel.string
- name="power_all_have_icon" translate="false">
- Checkbox_On
- </panel.string>
- <panel.string
- name="power_partial_icon" translate="false">
- Checkbox_Off
- </panel.string>
- <filter_editor
- layout="topleft"
- top="5"
- left="5"
- right="-5"
- height="22"
- search_button_visible="false"
- follows="left|top|right"
- label="Filter Abilities"
- name="filter_input" />
- <scroll_list
- column_padding="0"
- draw_stripes="true"
- height="200"
- follows="left|top|right"
- layout="topleft"
- left="0"
- right="-1"
- name="action_list"
- search_column="2"
- tool_tip="Select an Ability to view more details"
- top_pad="5"
- width="300">
- <scroll_list.columns
- label=""
- name="icon"
- width="2" />
- <scroll_list.columns
- label=""
- name="checkbox"
- width="20" />
- <scroll_list.columns
- label=""
- name="action"
- width="270" />
- </scroll_list>
- </panel>
- </tab_container>
+ <panel.string
+ name="help_text">
+ Roles have a title and an allowed list of Abilities
+ that Members can perform. Members can belong to
+ one or more Roles. A group can have up to 10 Roles,
+ including the Everyone and Owner Roles.
+ </panel.string>
+ <panel.string
+ name="cant_delete_role">
+ The &apos;Everyone&apos; and &apos;Owners&apos; Roles are special and can't be deleted.
+ </panel.string>
+ <panel.string
+ name="power_folder_icon" translate="false">
+ Inv_FolderClosed
+ </panel.string>
+ <panel.string
+ name="power_all_have_icon" translate="false">
+ Checkbox_On
+ </panel.string>
+ <panel.string
+ name="power_partial_icon" translate="false">
+ Checkbox_Off
+ </panel.string>
+ <filter_editor
+ layout="topleft"
+ top="5"
+ left="5"
+ right="-5"
+ height="22"
+ search_button_visible="false"
+ follows="left|top|right"
+ label="Filter Roles"
+ name="filter_input" />
+ <scroll_list
+ column_padding="0"
+ draw_heading="true"
+ draw_stripes="false"
+ heading_height="23"
+ height="132"
+ layout="topleft"
+ search_column="1"
+ left="0"
+ follows="left|top|right"
+ right="-1"
+ name="role_list"
+ top_pad="2"
+ width="310">
+ <scroll_list.columns
+ label="Role"
+ name="name"
+ relative_width="0.45" />
+ <scroll_list.columns
+ label="Title"
+ name="title"
+ relative_width="0.45" />
+ <scroll_list.columns
+ label="#"
+ name="members"
+ relative_width="0.15" />
+ </scroll_list>
+ <button
+ follows="top|left"
+ height="23"
+ label="New Role"
+ layout="topleft"
+ left="0"
+ name="role_create"
+ width="120" />
+ <button
+ height="23"
+ follows="top|left"
+ label="Delete Role"
+ layout="topleft"
+ left_pad="10"
+ name="role_delete"
+ width="120" />
+ </panel>
<panel
- height="350"
- background_visible="false"
- bg_alpha_color="FloaterUnfocusBorderColor"
+ border="false"
+ height="303"
+ label="ABILITIES"
layout="topleft"
- follows="top|left|right"
left="0"
right="-1"
- width="313"
- mouse_opaque="false"
- name="members_footer"
- top="325"
- visible="false">
- <text
- type="string"
- height="16"
- layout="topleft"
- follows="left|top"
- left="5"
- top="8"
- text_color="EmphasisColor"
- name="static"
- width="300">
- Assigned Roles
- </text>
- <scroll_list
- draw_stripes="true"
- follows="left|top|right"
- height="150"
- layout="topleft"
- left="0"
- right="-1"
- name="member_assigned_roles"
- top_pad="0">
- <scroll_list.columns
- label=""
- name="checkbox"
- width="20" />
- <scroll_list.columns
- label=""
- name="role"
- width="270" />
- </scroll_list>
- <text
- type="string"
- height="16"
- layout="topleft"
- follows="left|top"
- left="5"
- top_pad="5"
- text_color="EmphasisColor"
- name="static2"
- width="285">
- Allowed Abilities
- </text>
- <scroll_list
- draw_stripes="true"
- follows="left|top|right"
- height="150"
- layout="topleft"
- left="0"
- right="-1"
- name="member_allowed_actions"
- search_column="2"
- tool_tip="For details of each allowed ability see the abilities tab"
- top_pad="0">
- <scroll_list.columns
- label=""
- name="icon"
- width="2" />
- <scroll_list.columns
- label=""
- name="checkbox"
- width="20" />
- <scroll_list.columns
- label=""
- name="action"
- width="270" />
- </scroll_list>
+ help_topic="roles_actions_tab"
+ name="actions_sub_tab"
+ class="panel_group_actions_subtab"
+ tool_tip="You can view an Ability&apos;s Description and which Roles and Members can execute the Ability."
+ width="310">
+ <panel.string
+ name="help_text">
+ Abilities allow Members in Roles to do specific
+ things in this group. There&apos;s a broad variety of Abilities.
+ </panel.string>
+ <panel.string
+ name="power_folder_icon" translate="false">
+ Inv_FolderClosed
+ </panel.string>
+ <panel.string
+ name="power_all_have_icon" translate="false">
+ Checkbox_On
+ </panel.string>
+ <panel.string
+ name="power_partial_icon" translate="false">
+ Checkbox_Off
+ </panel.string>
+ <filter_editor
+ layout="topleft"
+ top="5"
+ left="5"
+ right="-5"
+ height="22"
+ search_button_visible="false"
+ follows="left|top|right"
+ label="Filter Abilities"
+ name="filter_input" />
+ <scroll_list
+ column_padding="0"
+ draw_stripes="true"
+ height="200"
+ follows="left|top|right"
+ layout="topleft"
+ left="0"
+ right="-1"
+ name="action_list"
+ search_column="2"
+ tool_tip="Select an Ability to view more details"
+ top_pad="5"
+ width="300">
+ <scroll_list.columns
+ label=""
+ name="icon"
+ width="2" />
+ <scroll_list.columns
+ label=""
+ name="checkbox"
+ width="20" />
+ <scroll_list.columns
+ label=""
+ name="action"
+ width="270" />
+ </scroll_list>
</panel>
<panel
- height="550"
- background_visible="false"
- bg_alpha_color="FloaterUnfocusBorderColor"
+ border="false"
+ height="303"
+ label="BANNED RESIDENTS"
layout="topleft"
- follows="top|left|right"
left="0"
right="-1"
- width="313"
- mouse_opaque="false"
- name="roles_footer"
- top_delta="0"
- top="209"
- visible="false">
- <text
- type="string"
- height="16"
- layout="topleft"
- follows="left|top"
- left="5"
- top="5"
- name="static"
- width="300">
- Role Name
- </text>
- <line_editor
- type="string"
- height="20"
- layout="topleft"
- left="0"
- follows="left|top|right"
- right="-1"
- max_length_bytes="20"
- name="role_name"
- top_pad="0"
- width="300">
- </line_editor>
- <text
- type="string"
- height="16"
- layout="topleft"
- follows="left|top"
- left="5"
- name="static3"
- top_pad="5"
- width="300">
- Role Title
- </text>
- <line_editor
- type="string"
- height="20"
- layout="topleft"
- left="0"
- follows="left|top|right"
- right="-1"
- max_length_bytes="20"
- name="role_title"
- top_pad="0"
- width="300">
- </line_editor>
- <text
- type="string"
- height="16"
- layout="topleft"
- follows="left|top"
- left="5"
- name="static2"
- top_pad="5"
- width="200">
- Description
- </text>
- <text_editor
- type="string"
- layout="topleft"
- left="0"
- follows="left|top|right"
- right="-1"
- max_length="295"
- height="35"
- name="role_description"
- top_pad="0"
- width="300"
- word_wrap="true">
- </text_editor>
- <text
- type="string"
- height="16"
- layout="topleft"
- follows="left|top"
- left="5"
- text_color="EmphasisColor"
- name="static4"
- top_pad="5"
- width="300">
- Assigned Members
- </text>
- <name_list
- draw_stripes="true"
- height="128"
- layout="topleft"
- left="0"
- follows="left|top|right"
- right="-1"
- name="role_assigned_members"
- top_pad="0"
- width="300" />
- <check_box
- height="15"
- label="Reveal members"
- left="5"
- layout="topleft"
- name="role_visible_in_list"
- tool_tip="Sets whether members of this role are visible in the General tab to people outside of the group."
- top_pad="4"
- width="300" />
- <text
- type="string"
- height="16"
- layout="topleft"
- follows="left|top"
- left="5"
- text_color="EmphasisColor"
- name="static5"
- top_pad="2"
- width="300">
- Allowed Abilities
- </text>
- <scroll_list
- draw_stripes="true"
- height="140"
- layout="topleft"
- left="0"
- follows="left|top|right"
- right="-1"
- name="role_allowed_actions"
- search_column="2"
- tool_tip="For details of each allowed ability see the abilities tab"
- top_pad="0"
- width="300">
- <scroll_list.columns
- label=""
- name="icon"
- width="2" />
- <scroll_list.columns
- label=""
- name="checkbox"
- width="20" />
- <scroll_list.columns
- label=""
- name="action"
- width="270" />
- </scroll_list>
+ help_topic="roles_banlist_tab"
+ name="banlist_sub_tab"
+ class="panel_group_banlist_subtab"
+ tool_tip="View the banned residents 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>
+ <panel.string
+ name="ban_count_template">
+ Ban count: [COUNT]/[LIMIT]
+ </panel.string>
+ <name_list
+ column_padding="0"
+ draw_heading="true"
+ height="400"
+ 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.7" />
+ <name_list.columns
+ label="Date Banned"
+ name="ban_date"
+ relative_width="0.3" />
+ </name_list>
+ <button
+ follows="top|left"
+ height="23"
+ label="Ban Resident(s)"
+ layout="topleft"
+ left="3"
+ name="ban_create"
+ tool_tip="Ban residents from your group"
+ width="120" />
+ <button
+ follows="top|left"
+ height="23"
+ label="Remove Ban(s)"
+ layout="topleft"
+ left_pad="5"
+ name="ban_delete"
+ tool_tip="Unban selected residents from your group"
+ width="120" />
+ <button
+ follows="top|left"
+ height="23"
+ width="23"
+ image_overlay="Refresh_Off"
+ layout="topleft"
+ left_pad="5"
+ name="ban_refresh"
+ tool_tip="Refresh the ban list"
+ />
+ <text
+ type="string"
+ height="18"
+ left_pad="5"
+ follows="top|left"
+ layout="topleft"
+ name="ban_count"
+ width="100">
+ </text>
</panel>
- <panel
- height="424"
- background_visible="false"
- bg_alpha_color="FloaterUnfocusBorderColor"
+ </tab_container>
+ <panel
+ height="350"
+ background_visible="false"
+ bg_alpha_color="FloaterUnfocusBorderColor"
+ layout="topleft"
+ follows="top|left|right"
+ left="0"
+ right="-1"
+ width="313"
+ mouse_opaque="false"
+ name="members_footer"
+ top="325"
+ visible="false">
+ <text
+ type="string"
+ height="16"
+ layout="topleft"
+ follows="left|top"
+ left="5"
+ top="8"
+ text_color="EmphasisColor"
+ name="static"
+ width="300">
+ Assigned Roles
+ </text>
+ <scroll_list
+ draw_stripes="true"
+ follows="left|top|right"
+ height="150"
layout="topleft"
- follows="top|left|right"
left="0"
right="-1"
- width="313"
- mouse_opaque="false"
- name="actions_footer"
- top_delta="0"
- top="255"
- visible="false">
- <text_editor
- bg_readonly_color="Transparent"
- text_readonly_color="EmphasisColor"
- font="SansSerifSmall"
- type="string"
- enabled="false"
- halign="left"
- layout="topleft"
+ name="member_assigned_roles"
+ top_pad="0">
+ <scroll_list.columns
+ label=""
+ name="checkbox"
+ width="20" />
+ <scroll_list.columns
+ label=""
+ name="role"
+ width="270" />
+ </scroll_list>
+ <text
+ type="string"
+ height="16"
+ layout="topleft"
+ follows="left|top"
+ left="5"
+ top_pad="5"
+ text_color="EmphasisColor"
+ name="static2"
+ width="285">
+ Allowed Abilities
+ </text>
+ <scroll_list
+ draw_stripes="true"
follows="left|top|right"
- left="0"
- right="-1"
- height="90"
- max_length="512"
- name="action_description"
- top="0"
- word_wrap="true">
- This Ability is &apos;Eject Members from this Group&apos;. Only an Owner can eject another Owner.
- </text_editor>
- <text
+ height="150"
+ layout="topleft"
+ left="0"
+ right="-1"
+ name="member_allowed_actions"
+ search_column="2"
+ tool_tip="For details of each allowed ability see the abilities tab"
+ top_pad="0">
+ <scroll_list.columns
+ label=""
+ name="icon"
+ width="2" />
+ <scroll_list.columns
+ label=""
+ name="checkbox"
+ width="20" />
+ <scroll_list.columns
+ label=""
+ name="action"
+ width="270" />
+ </scroll_list>
+ </panel>
+ <panel
+ height="550"
+ background_visible="false"
+ bg_alpha_color="FloaterUnfocusBorderColor"
+ layout="topleft"
+ follows="top|left|right"
+ left="0"
+ right="-1"
+ width="313"
+ mouse_opaque="false"
+ name="roles_footer"
+ top_delta="0"
+ top="209"
+ visible="false">
+ <text
+ type="string"
+ height="16"
+ layout="topleft"
+ follows="left|top"
+ left="5"
+ top="5"
+ name="static"
+ width="300">
+ Role Name
+ </text>
+ <line_editor
+ type="string"
+ height="20"
+ layout="topleft"
+ left="0"
+ follows="left|top|right"
+ right="-1"
+ max_length_bytes="20"
+ name="role_name"
+ top_pad="0"
+ width="300">
+ </line_editor>
+ <text
+ type="string"
+ height="16"
+ layout="topleft"
+ follows="left|top"
+ left="5"
+ name="static3"
+ top_pad="5"
+ width="300">
+ Role Title
+ </text>
+ <line_editor
+ type="string"
+ height="20"
+ layout="topleft"
+ left="0"
+ follows="left|top|right"
+ right="-1"
+ max_length_bytes="20"
+ name="role_title"
+ top_pad="0"
+ width="300">
+ </line_editor>
+ <text
type="string"
height="16"
layout="topleft"
follows="left|top"
left="5"
name="static2"
- top_pad="1"
- width="300">
- Roles with this ability
- </text>
- <scroll_list
- height="172"
- layout="topleft"
- follows="left|top|right"
- left="5"
- right="-1"
- name="action_roles"
- top_pad="0"
- width="300" />
- <text
- type="string"
- height="16"
- layout="topleft"
- follows="left|top"
- left="5"
- name="static3"
top_pad="5"
- width="300">
- Members with this ability
- </text>
- <name_list
- height="122"
- follows="left|top|right"
- layout="topleft"
- left="5"
- right="-1"
- name="action_members"
- top_pad="0"
- width="300" />
- </panel>
+ width="200">
+ Description
+ </text>
+ <text_editor
+ type="string"
+ layout="topleft"
+ left="0"
+ follows="left|top|right"
+ right="-1"
+ max_length="295"
+ height="35"
+ name="role_description"
+ top_pad="0"
+ width="300"
+ word_wrap="true">
+ </text_editor>
+ <text
+ type="string"
+ height="16"
+ layout="topleft"
+ follows="left|top"
+ left="5"
+ text_color="EmphasisColor"
+ name="static4"
+ top_pad="5"
+ width="300">
+ Assigned Members
+ </text>
+ <name_list
+ draw_stripes="true"
+ height="128"
+ layout="topleft"
+ left="0"
+ follows="left|top|right"
+ right="-1"
+ name="role_assigned_members"
+ top_pad="0"
+ width="300" />
+ <check_box
+ height="15"
+ label="Reveal members"
+ left="5"
+ layout="topleft"
+ name="role_visible_in_list"
+ tool_tip="Sets whether members of this role are visible in the General tab to people outside of the group."
+ top_pad="4"
+ width="300" />
+ <text
+ type="string"
+ height="16"
+ layout="topleft"
+ follows="left|top"
+ left="5"
+ text_color="EmphasisColor"
+ name="static5"
+ top_pad="2"
+ width="300">
+ Allowed Abilities
+ </text>
+ <scroll_list
+ draw_stripes="true"
+ height="140"
+ layout="topleft"
+ left="0"
+ follows="left|top|right"
+ right="-1"
+ name="role_allowed_actions"
+ search_column="2"
+ tool_tip="For details of each allowed ability see the abilities tab"
+ top_pad="0"
+ width="300">
+ <scroll_list.columns
+ label=""
+ name="icon"
+ width="2" />
+ <scroll_list.columns
+ label=""
+ name="checkbox"
+ width="20" />
+ <scroll_list.columns
+ label=""
+ name="action"
+ width="270" />
+ </scroll_list>
+ </panel>
+ <panel
+ height="424"
+ background_visible="false"
+ bg_alpha_color="FloaterUnfocusBorderColor"
+ layout="topleft"
+ follows="top|left|right"
+ left="0"
+ right="-1"
+ width="313"
+ mouse_opaque="false"
+ name="actions_footer"
+ top_delta="0"
+ top="255"
+ visible="false">
+ <text_editor
+ bg_readonly_color="Transparent"
+ text_readonly_color="EmphasisColor"
+ font="SansSerifSmall"
+ type="string"
+ enabled="false"
+ halign="left"
+ layout="topleft"
+ follows="left|top|right"
+ left="0"
+ right="-1"
+ height="90"
+ max_length="512"
+ name="action_description"
+ top="0"
+ word_wrap="true">
+ This Ability is &apos;Eject Members from this Group&apos;. Only an Owner can eject another Owner.
+ </text_editor>
+ <text
+ type="string"
+ height="16"
+ layout="topleft"
+ follows="left|top"
+ left="5"
+ name="static2"
+ top_pad="1"
+ width="300">
+ Roles with this ability
+ </text>
+ <scroll_list
+ height="172"
+ layout="topleft"
+ follows="left|top|right"
+ left="5"
+ right="-1"
+ name="action_roles"
+ top_pad="0"
+ width="300" />
+ <text
+ type="string"
+ height="16"
+ layout="topleft"
+ follows="left|top"
+ left="5"
+ name="static3"
+ top_pad="5"
+ width="300">
+ Members with this ability
+ </text>
+ <name_list
+ height="122"
+ follows="left|top|right"
+ layout="topleft"
+ left="5"
+ right="-1"
+ name="action_members"
+ top_pad="0"
+ width="300" />
+ </panel>
</panel>
diff --git a/indra/newview/skins/default/xui/en/role_actions.xml b/indra/newview/skins/default/xui/en/role_actions.xml
index 0eeccbeac5..1044cbfd2e 100755
--- a/indra/newview/skins/default/xui/en/role_actions.xml
+++ b/indra/newview/skins/default/xui/en/role_actions.xml
@@ -9,7 +9,10 @@
<action description="Eject Members from this Group"
longdescription="Eject Members from this Group using the &apos;Eject&apos; button in the Roles section &gt; Members tab. An Owner can eject anyone except another Owner. If you&apos;re not an Owner, a Member can be ejected from a group if, and only if, they&apos;re only in the Everyone Role, and NO other Roles. To remove Members from Roles, you need to have the &apos;Remove Members from Roles&apos; Ability."
name="member eject" value="2" />
- <action
+ <action description="Manage ban list"
+ longdescription="Allows the group member to ban / un-ban Residents from this group."
+ name="allow ban" value="51" />
+ <action
description="Toggle &apos;Open Enrollment&apos; and change &apos;Enrollment fee&apos;"
longdescription="Toggle &apos;Open Enrollment&apos; to let new Members join without an invitation, and change the &apos;Enrollment fee&apos; in the General section."
name="member options" value="3" />
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index 3cf8ffae26..5fb964fba7 100755
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -3796,6 +3796,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>