diff --git a/indra/llui/lltooltip.cpp b/indra/llui/lltooltip.cpp index 5e1f12996e..bb7f615d51 100755 --- a/indra/llui/lltooltip.cpp +++ b/indra/llui/lltooltip.cpp @@ -476,9 +476,9 @@ 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; - + if (!params.styled_message.isProvided() + && (!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); params_with_defaults.fillFrom(LLUICtrlFactory::instance().getDefaultParams()); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 9271ef6f74..02859310c6 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -342,6 +342,7 @@ set(viewer_SOURCE_FILES llfloatergesture.cpp llfloatergodtools.cpp llfloatergotoline.cpp + llfloatergroupbulkban.cpp llfloatergroupinvite.cpp llfloatergroups.cpp llfloaterhandler.cpp @@ -515,6 +516,8 @@ set(viewer_SOURCE_FILES llpanelface.cpp llpanelgenerictip.cpp llpanelgroup.cpp + llpanelgroupbulk.cpp + llpanelgroupbulkban.cpp llpanelgroupgeneral.cpp llpanelgroupinvite.cpp llpanelgrouplandmoney.cpp @@ -1058,6 +1061,7 @@ set(viewer_HEADER_FILES llfloatergesture.h llfloatergodtools.h llfloatergotoline.h + llfloatergroupbulkban.h llfloatergroupinvite.h llfloatergroups.h llfloaterhandler.h @@ -1224,6 +1228,9 @@ set(viewer_HEADER_FILES llpanelface.h llpanelgenerictip.h llpanelgroup.h + llpanelgroupbulk.h + llpanelgroupbulkimpl.h + llpanelgroupbulkban.h llpanelgroupgeneral.h llpanelgroupinvite.h llpanelgrouplandmoney.h diff --git a/indra/newview/llfloatergroupbulkban.cpp b/indra/newview/llfloatergroupbulkban.cpp new file mode 100644 index 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 sInstances; +}; + +// +// Globals +// +std::map 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 eaf557d278..339d15f26e 100755 --- a/indra/newview/llfloaterimcontainer.cpp +++ b/indra/newview/llfloaterimcontainer.cpp @@ -1272,6 +1272,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 selectedItems = mConversationsRoot->getSelectionList(); + LLConversationItem * conversationItem; + for(std::set::const_iterator it = selectedItems.begin(); it != selectedItems.end(); ++it) + { + conversationItem = static_cast((*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 +1392,10 @@ bool LLFloaterIMContainer::enableContextMenuItem(const std::string& item, uuid_v else if ("can_call" == item) { return LLAvatarActions::canCall(); + } + else if ("can_open_voice_conversation" == item) + { + return is_single_select && LLAvatarActions::canCall(); } else if ("can_zoom_in" == item) { diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp index af7c630ee7..10a55dbc41 100755 --- a/indra/newview/llgroupactions.cpp +++ b/indra/newview/llgroupactions.cpp @@ -173,7 +173,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) @@ -183,9 +183,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 66d5f158f3..be1e4bfaff 100755 --- a/indra/newview/llgroupmgr.cpp +++ b/indra/newview/llgroupmgr.cpp @@ -238,11 +238,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(); @@ -431,7 +431,7 @@ void LLGroupMgrGroupData::removeMemberData() delete mi->second; } mMembers.clear(); - mMemberDataComplete = FALSE; + mMemberDataComplete = false; mMemberVersion.generate(); } @@ -453,8 +453,8 @@ void LLGroupMgrGroupData::removeRoleData() } mRoles.clear(); mReceivedRoleMemberPairs = 0; - mRoleDataComplete = FALSE; - mRoleMemberDataComplete = FALSE; + mRoleDataComplete = false; + mRoleMemberDataComplete= false; } void LLGroupMgrGroupData::removeRoleMemberData() @@ -478,7 +478,7 @@ void LLGroupMgrGroupData::removeRoleMemberData() } mReceivedRoleMemberPairs = 0; - mRoleMemberDataComplete = FALSE; + mRoleMemberDataComplete= false; } LLGroupMgrGroupData::~LLGroupMgrGroupData() @@ -754,6 +754,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 // @@ -963,12 +977,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); } } @@ -1038,7 +1052,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); @@ -1115,12 +1129,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); } } @@ -1229,7 +1243,7 @@ void LLGroupMgr::processGroupRoleMembersReply(LLMessageSystem* msg, void** data) } } - group_datap->mRoleMemberDataComplete = TRUE; + group_datap->mRoleMemberDataComplete= true; group_datap->mRoleMembersRequestID.setNull(); } @@ -1555,7 +1569,7 @@ void LLGroupMgr::sendGroupRoleMembersRequest(const LLUUID& group_id) LL_INFOS() << " Pending: " << (group_datap->mPendingRoleMemberRequest ? "Y" : "N") << " MemberDataComplete: " << (group_datap->mMemberDataComplete ? "Y" : "N") << " RoleDataComplete: " << (group_datap->mRoleDataComplete ? "Y" : "N") << LL_ENDL; - group_datap->mPendingRoleMemberRequest = TRUE; + group_datap->mPendingRoleMemberRequest = true; return; } @@ -1873,6 +1887,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 result(const LLSD& pContent); + virtual void errorWithContent(U32 pStatus, const std::string& pReason, const LLSD& pContent); +private: + LLUUID mGroupID; + BOOL mForceRefresh; +}; + +GroupBanDataResponder::GroupBanDataResponder(const LLUUID& gropup_id, BOOL force_refresh) : + mGroupID(gropup_id), + mForceRefresh(force_refresh) +{} + +void GroupBanDataResponder::errorWithContent(U32 pStatus, const std::string& pReason, const LLSD& pContent) +{ + LL_WARNS("GrpMgr") << "Error receiving group member data [status:" + << pStatus << "]: " << pContent << LL_ENDL; +} + +void GroupBanDataResponder::result(const LLSD& content) +{ + if (content.has("ban_list")) + { + // group ban data received + LLGroupMgr::processGroupBanRequest(content); + } + 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 ban_list) /* = std::vector() */ +{ + LLViewerRegion* currentRegion = gAgent.getRegion(); + if(!currentRegion) + { + LL_WARNS("GrpMgr") << "Agent does not have a current region." << 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 agents 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 { @@ -1958,7 +2104,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) @@ -2041,12 +2187,12 @@ void LLGroupMgr::processCapGroupMembersRequest(const LLSD& content) LLGroupMgr::getInstance()->sendGroupTitlesRequest(group_id); - group_datap->mMemberDataComplete = TRUE; + group_datap->mMemberDataComplete = true; group_datap->mMemberRequestID.setNull(); // Make the role-member data request if (group_datap->mPendingRoleMemberRequest) { - group_datap->mPendingRoleMemberRequest = FALSE; + group_datap->mPendingRoleMemberRequest = false; LLGroupMgr::getInstance()->sendGroupRoleMembersRequest(group_id); } diff --git a/indra/newview/llgroupmgr.h b/indra/newview/llgroupmgr.h index ece6a8cdbb..44a666ef0a 100755 --- a/indra/newview/llgroupmgr.h +++ b/indra/newview/llgroupmgr.h @@ -33,8 +33,10 @@ #include #include +// 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; @@ -205,6 +208,17 @@ struct lluuid_pair_less } }; + +struct LLGroupBanData +{ + LLGroupBanData(): mBanDate() {} + ~LLGroupBanData() {} + + LLDate mBanDate; + // TODO: std:string ban_reason; +}; + + struct LLGroupTitle { std::string mTitle; @@ -212,8 +226,6 @@ struct LLGroupTitle BOOL mSelected; }; -class LLGroupMgr; - class LLGroupMgrGroupData { friend class LLGroupMgr; @@ -244,16 +256,16 @@ public: void recalcAgentPowers(const LLUUID& agent_id); // [SL:KB] - Patch: Chat-GroupSessionEject | Checked: 2012-02-04 (Catznip-3.2.1) | Added: Catznip-3.2.1 - BOOL isMemberDataComplete() const { return mMemberDataComplete; } - BOOL isRoleDataComplete() const { return mRoleDataComplete; } - BOOL isRoleMemberDataComplete() const { return mRoleMemberDataComplete; } - BOOL isGroupPropertiesDataComplete() const { return mGroupPropertiesDataComplete; } + bool isMemberDataComplete() const { return mMemberDataComplete; } + bool isRoleDataComplete() const { return mRoleDataComplete; } + bool isRoleMemberDataComplete() const { return mRoleMemberDataComplete; } + bool isGroupPropertiesDataComplete() const { return mGroupPropertiesDataComplete; } // [/SL:KB] -// 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; } @@ -261,17 +273,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 member_list_t; typedef std::map role_list_t; typedef std::map change_map_t; typedef std::map role_data_map_t; + typedef std::map 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 mTitles; @@ -302,12 +323,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 @@ -334,6 +355,23 @@ class LLGroupMgr : public LLSingleton { 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(); @@ -367,8 +405,14 @@ public: static void sendGroupMemberInvites(const LLUUID& group_id, std::map& 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); @@ -413,4 +457,3 @@ private: #endif - diff --git a/indra/newview/llnamelistctrl.cpp b/indra/newview/llnamelistctrl.cpp index f1a1f70270..29d5440fa1 100755 --- a/indra/newview/llnamelistctrl.cpp +++ b/indra/newview/llnamelistctrl.cpp @@ -64,11 +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), - // FIRE-12347 / MAINT-3187: Name list not loading - //mShortNames(p.short_names), - mShortNames(p.short_names) - //mAvatarNameCacheConnection() - // + mShortNames(p.short_names), + mPendingLookupsRemaining(0) {} // public @@ -357,6 +354,17 @@ LLScrollListItem* LLNameListCtrl::addNameItemRow( } 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; } @@ -408,6 +416,8 @@ void LLNameListCtrl::removeNameItem(const LLUUID& agent_id) { selectNthItem(idx); // not sure whether this is needed, taken from previous implementation deleteSingleItem(idx); + + mPendingLookupsRemaining--; } } @@ -456,6 +466,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 aeeb5e9b86..c332f2b967 100755 --- a/indra/newview/llnamelistctrl.h +++ b/indra/newview/llnamelistctrl.h @@ -70,6 +70,7 @@ class LLNameListCtrl : public LLScrollListCtrl, public LLInstanceTracker { public: + typedef boost::signals2::signal namelist_complete_signal_t; typedef enum e_name_type { @@ -165,7 +166,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); // FIRE-12347 / MAINT-3187: Name list not loading @@ -183,6 +184,16 @@ private: typedef boost::unordered_map 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 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("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 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 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 selection = mBulkAgentList->getAllSelected(); + if (selection.empty()) + return; + + std::vector::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 selection = mBulkAgentList->getAllSelected(); + if (selection.empty()) + mRemoveButton->setEnabled(FALSE); + else + mRemoveButton->setEnabled(TRUE); +} + +void LLPanelGroupBulkImpl::addUsers(const std::vector& 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 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 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..222931eabc --- /dev/null +++ b/indra/newview/llpanelgroupbulk.h @@ -0,0 +1,74 @@ +/** +* @file llpanelgroupbulk.h +* @brief Header file for llpanelgroupbulk +* @author Baker@lindenlab.com +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2013, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ +#ifndef LL_LLPANELGROUPBULK_H +#define LL_LLPANELGROUPBULK_H + +#include "llpanel.h" +#include "lluuid.h" + +class LLAvatarName; +class LLGroupMgrGroupData; +class LLPanelGroupBulkImpl; + +// Base panel class for bulk group invite / ban floaters +class LLPanelGroupBulk : public LLPanel +{ +public: + LLPanelGroupBulk(const LLUUID& group_id); + ~LLPanelGroupBulk(); + +public: + static void callbackClickSubmit(void* userdata) {} + virtual void submit() = 0; + +public: + virtual void clear(); + virtual void update(); + virtual void draw(); + +protected: + virtual void updateGroupName(); + virtual void updateGroupData(); + +public: + // this callback is being used to add a user whose fullname isn't been loaded before invoking of addUsers(). + virtual void addUserCallback(const LLUUID& id, const LLAvatarName& av_name); + virtual void setCloseCallback(void (*close_callback)(void*), void* data); + + virtual void addUsers(uuid_vec_t& agent_ids); + +public: + LLPanelGroupBulkImpl* mImplementation; + +protected: + bool mPendingGroupPropertiesUpdate; + bool mPendingRoleDataUpdate; + bool mPendingMemberDataUpdate; +}; + +#endif // LL_LLPANELGROUPBULK_H + diff --git a/indra/newview/llpanelgroupbulkban.cpp b/indra/newview/llpanelgroupbulkban.cpp new file mode 100644 index 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 + +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("group_name_text", recurse); + mImplementation->mBulkAgentList = getChild("banned_agent_list", recurse); + if ( mImplementation->mBulkAgentList ) + { + mImplementation->mBulkAgentList->setCommitOnSelectionChange(TRUE); + mImplementation->mBulkAgentList->setCommitCallback(LLPanelGroupBulkImpl::callbackSelect, mImplementation); + } + + LLButton* button = getChild("add_button", recurse); + if ( button ) + { + // default to opening avatarpicker automatically + // (*impl::callbackClickAdd)((void*)this); + button->setClickedCallback(LLPanelGroupBulkImpl::callbackClickAdd, this); + } + + mImplementation->mRemoveButton = + getChild("remove_button", recurse); + if ( mImplementation->mRemoveButton ) + { + mImplementation->mRemoveButton->setClickedCallback(LLPanelGroupBulkImpl::callbackClickRemove, mImplementation); + mImplementation->mRemoveButton->setEnabled(FALSE); + } + + mImplementation->mOKButton = + getChild("ban_button", recurse); + if ( mImplementation->mOKButton ) + { + mImplementation->mOKButton->setClickedCallback(LLPanelGroupBulkBan::callbackClickSubmit, this); + mImplementation->mOKButton->setEnabled(FALSE); + } + + button = getChild("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 banned_agent_list; + std::vector agents = mImplementation->mBulkAgentList->getAllData(); + std::vector::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 banned_avatar_names; + std::vector out_of_limit_names; + bool banning_self = FALSE; + std::vector::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::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::iterator exeedes_limit = banned_agent_list.begin() + GB_MAX_BANNED_AGENTS - group_datap->mBanList.size(); + for (std::vector::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 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 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& 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 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 5dfa7f32b4..e662a05dfc 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, @@ -501,9 +501,7 @@ void LLPanelGroupInvite::addUsers(uuid_vec_t& agent_ids) //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 - // FIRE-4140: Group invite button on profile sometimes doesn't work - //if (LLAvatarTracker::instance().isBuddy(agent_id)) - // + if (LLAvatarTracker::instance().isBuddy(agent_id)) { LLAvatarName av_name; if (!LLAvatarNameCache::get(agent_id, &av_name)) @@ -581,7 +579,7 @@ void LLPanelGroupInvite::updateLists() { waiting = true; } - if (gdatap->isRoleDataComplete() && gdatap->isMemberDataComplete()) + if (gdatap->isRoleDataComplete() && gdatap->isMemberDataComplete() && gdatap->isRoleMemberDataComplete()) { if ( mImplementation->mRoleNames ) { @@ -609,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; @@ -656,7 +655,7 @@ BOOL LLPanelGroupInvite::postBuild() } mImplementation->mOKButton = - getChild("ok_button", recurse); + getChild("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 e6e06c59d2..b808c9167f 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" @@ -112,8 +113,10 @@ bool agentCanAddToRole(const LLUUID& group_id, return false; } -// static +// LLPanelGroupRoles ///////////////////////////////////////////////////// + +// static LLPanelGroupRoles::LLPanelGroupRoles() : LLPanelGroupTab(), mCurrentTab(NULL), @@ -300,7 +303,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. @@ -337,7 +339,6 @@ void LLPanelGroupRoles::update(LLGroupChange gc) { if (mGroupID.isNull()) return; - LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel(); if (panelp) { @@ -354,39 +355,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()->sendGroupRoleDataRequest(mGroupID); - } - - // Check role-member mapping data. - if (!gdatap || !gdatap->isRoleMemberDataComplete() ) - { - LLGroupMgr::getInstance()->sendGroupRoleMembersRequest(mGroupID); - } - - // Need this to get base group member powers - if (!gdatap || !gdatap->isGroupPropertiesDataComplete() ) - { - LLGroupMgr::getInstance()->sendGroupPropertiesRequest(mGroupID); - } - - mFirstUse = FALSE; + LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mGroupID); } + if (!gdatap || !gdatap->isRoleDataComplete() ) + { + // Mildly hackish - clear all pending changes + cancel(); + + LLGroupMgr::getInstance()->sendGroupRoleDataRequest(mGroupID); + } + + // Check role-member mapping data. + if (!gdatap || !gdatap->isRoleMemberDataComplete() ) + { + LLGroupMgr::getInstance()->sendGroupRoleMembersRequest(mGroupID); + } + + // 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(); } @@ -415,15 +410,42 @@ BOOL LLPanelGroupRoles::hasModal() return panelp->hasModal(); } +void LLPanelGroupRoles::setGroupID(const LLUUID& id) +{ + LLPanelGroupTab::setGroupID(id); -//////////////////////////// -// LLPanelGroupSubTab -//////////////////////////// + LLPanelGroupMembersSubTab* group_members_tab = findChild("members_sub_tab"); + LLPanelGroupRolesSubTab* group_roles_tab = findChild("roles_sub_tab"); + LLPanelGroupActionsSubTab* group_actions_tab = findChild("actions_sub_tab"); + LLPanelGroupBanListSubTab* group_ban_tab = findChild("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("member_invite"); + if ( button ) + button->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_MEMBER_INVITE)); + // [FS:CR] FIRE-12276 + button = getChild("export_list"); + if (button) + button->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_MEMBER_VISIBLE_IN_DIR)); + // [/FS:CR] + if(mSubTabContainer) + mSubTabContainer->selectTab(1); + group_roles_tab->mFirstOpen = TRUE; + activate(); +} + + +// LLPanelGroupSubTab //////////////////////////////////////////////////// LLPanelGroupSubTab::LLPanelGroupSubTab() : LLPanelGroupTab(), mHeader(NULL), mFooter(NULL), mActivated(false), + mHasGroupBanPower(false), mSearchEditor(NULL) { } @@ -545,9 +567,10 @@ void LLPanelGroupSubTab::buildActionsList(LLScrollListCtrl* ctrl, return; } + mHasGroupBanPower = false; + std::vector::iterator ras_it = LLGroupMgr::getInstance()->mRoleActionSets.begin(); std::vector::iterator ras_end = LLGroupMgr::getInstance()->mRoleActionSets.end(); - for ( ; ras_it != ras_end; ++ras_it) { buildActionCategory(ctrl, @@ -677,6 +700,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) @@ -712,6 +760,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; + } } } @@ -731,11 +788,8 @@ void LLPanelGroupSubTab::setFooterEnabled(BOOL enable) } } -//////////////////////////// -// LLPanelGroupMembersSubTab -//////////////////////////// - +// LLPanelGroupMembersSubTab ///////////////////////////////////////////// static LLPanelInjector t_panel_group_members_subtab("panel_group_members_subtab"); LLPanelGroupMembersSubTab::LLPanelGroupMembersSubTab() @@ -834,6 +888,13 @@ BOOL LLPanelGroupMembersSubTab::postBuildSubTab(LLView* root) mEjectBtn->setEnabled(FALSE); } + mBanBtn = parent->getChild("member_ban", recurse); + if(mBanBtn) + { + mBanBtn->setClickedCallback(onBanMember, this); + mBanBtn->setEnabled(FALSE); + } + return TRUE; } @@ -847,34 +908,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) { @@ -903,7 +936,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::iterator itor; @@ -940,8 +973,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) @@ -988,6 +1021,7 @@ void LLPanelGroupMembersSubTab::handleMemberSelect() { // Can't remove other owners. cb_enable = FALSE; + can_ban_members = FALSE; break; } } @@ -1007,14 +1041,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)) @@ -1071,7 +1105,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) { @@ -1084,10 +1121,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); } @@ -1125,61 +1193,26 @@ void LLPanelGroupMembersSubTab::onEjectMembers(void *userdata) } void LLPanelGroupMembersSubTab::handleEjectMembers() -{ +{ + //send down an eject message + uuid_vec_t selected_members; + std::vector 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::iterator itor; + for (itor = selection.begin() ; + itor != selection.end(); ++itor) { - //send down an eject message - uuid_vec_t selected_members; - - std::vector selection = mMembersList->getAllSelected(); - if (selection.empty()) return false; - - std::vector::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) @@ -1207,12 +1240,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 selection = mMembersList->getAllSelected(); if (selection.empty()) @@ -1223,7 +1255,6 @@ void LLPanelGroupMembersSubTab::handleRoleCheck(const LLUUID& role_id, for (std::vector::iterator itor = selection.begin() ; itor != selection.end(); ++itor) { - member_id = (*itor)->getUUID(); //see if we requested a change for this member before @@ -1289,7 +1320,6 @@ void LLPanelGroupMembersSubTab::handleRoleCheck(const LLUUID& role_id, FALSE); } - // static void LLPanelGroupMembersSubTab::onRoleCheck(LLUICtrl* ctrl, void* user_data) { @@ -1328,6 +1358,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() @@ -1731,13 +1770,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; @@ -1806,6 +1844,12 @@ void LLPanelGroupMembersSubTab::updateMembers() handleMemberSelect(); } +void LLPanelGroupMembersSubTab::onBanMember(void* user_data) +{ + LLPanelGroupMembersSubTab* self = static_cast(user_data); + self->handleBanMember(); +} + // [FS:CR] FIRE-12276 void LLPanelGroupMembersSubTab::onExportMembersToXML() { @@ -1845,11 +1889,38 @@ void LLPanelGroupMembersSubTab::onExportMembersToXML() } // [/FS:CR] FIRE-12276 +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 selection = mMembersList->getAllSelected(); + if(selection.empty()) + { + return; + } + uuid_vec_t ban_ids; + std::vector::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 t_panel_group_roles_subtab("panel_group_roles_subtab"); LLPanelGroupRolesSubTab::LLPanelGroupRolesSubTab() @@ -1863,7 +1934,7 @@ LLPanelGroupRolesSubTab::LLPanelGroupRolesSubTab() mMemberVisibleCheck(NULL), mDeleteRoleButton(NULL), mCreateRoleButton(NULL), - + mFirstOpen(TRUE), mHasRoleChange(FALSE) { } @@ -1965,6 +2036,7 @@ void LLPanelGroupRolesSubTab::deactivate() LL_DEBUGS() << "LLPanelGroupRolesSubTab::deactivate()" << LL_ENDL; LLPanelGroupSubTab::deactivate(); + mFirstOpen = FALSE; } bool LLPanelGroupRolesSubTab::needsApply(std::string& mesg) @@ -1972,6 +2044,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 @@ -1982,7 +2060,7 @@ bool LLPanelGroupRolesSubTab::apply(std::string& mesg) LL_DEBUGS() << "LLPanelGroupRolesSubTab::apply()" << LL_ENDL; saveRoleChanges(true); - + mFirstOpen = FALSE; LLGroupMgr::getInstance()->sendGroupRoleChanges(mGroupID); notifyObservers(); @@ -2119,14 +2197,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) @@ -2142,6 +2223,9 @@ void LLPanelGroupRolesSubTab::update(LLGroupChange gc) void LLPanelGroupRolesSubTab::onRoleSelect(LLUICtrl* ctrl, void* user_data) { LLPanelGroupRolesSubTab* self = static_cast(user_data); + if (!self) + return; + self->handleRoleSelect(); } @@ -2321,41 +2405,116 @@ 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); + + LLRoleData rd; + LLSD args; + + if ( gdatap->getRoleData(role_id, rd) ) { - // Uncheck the item, for now. It will be - // checked if they click 'Yes', below. - check->set(FALSE); - - LLRoleData rd; - LLSD args; - - 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)); - } - else - { - LL_WARNS() << "Unable to look up role information for role id: " - << role_id << LL_ENDL; + warning = "AssignDangerousAbilityWarning"; } + LLNotificationsUtil::add(warning, args, LLSD(), boost::bind(&LLPanelGroupRolesSubTab::addActionCB, this, _1, _2, check)); } else { - gdatap->addRolePower(role_id,power); + 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 all_data = mAllowedActionsList->getAllData(); + std::vector::iterator ad_it = all_data.begin(); + std::vector::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 + { + 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 { gdatap->removeRolePower(role_id,power); @@ -2363,6 +2522,7 @@ void LLPanelGroupRolesSubTab::handleActionCheck(LLUICtrl* ctrl, bool force) mHasRoleChange = TRUE; notifyObservers(); + } bool LLPanelGroupRolesSubTab::addActionCB(const LLSD& notification, const LLSD& response, LLCheckBoxCtrl* check) @@ -2382,7 +2542,6 @@ bool LLPanelGroupRolesSubTab::addActionCB(const LLSD& notification, const LLSD& return false; } - // static void LLPanelGroupRolesSubTab::onPropertiesKey(LLLineEditor* ctrl, void* user_data) { @@ -2560,13 +2719,28 @@ void LLPanelGroupRolesSubTab::saveRoleChanges(bool select_saved_role) mHasRoleChange = FALSE; } } -//////////////////////////// -// LLPanelGroupActionsSubTab -//////////////////////////// +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 t_panel_group_actions_subtab("panel_group_actions_subtab"); - LLPanelGroupActionsSubTab::LLPanelGroupActionsSubTab() : LLPanelGroupSubTab() { @@ -2739,34 +2913,296 @@ void LLPanelGroupActionsSubTab::handleActionSelect() } } -void LLPanelGroupRoles::setGroupID(const LLUUID& id) +void LLPanelGroupActionsSubTab::setGroupID(const LLUUID& id) { - LLPanelGroupTab::setGroupID(id); - - LLPanelGroupMembersSubTab* group_members_tab = findChild("members_sub_tab"); - LLPanelGroupRolesSubTab* group_roles_tab = findChild("roles_sub_tab"); - LLPanelGroupActionsSubTab* group_actions_tab = findChild("actions_sub_tab"); + if(mActionList) mActionList->deleteAllItems(); + if(mActionRoles) mActionRoles->deleteAllItems(); + if(mActionMembers) mActionMembers->deleteAllItems(); - 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(mActionDescription) mActionDescription->clear(); - LLButton* button = getChild("member_invite"); - if ( button ) - button->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_MEMBER_INVITE)); - // [FS:CR] FIRE-12276 - button = getChild("export_list"); - if (button) - button->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_MEMBER_VISIBLE_IN_DIR)); - // [/FS:CR] - - if(mSubTabContainer) - // FIRE-13501: Activate "Members" tab by default - //mSubTabContainer->selectTab(1); - mSubTabContainer->selectTab(0); - // - - activate(); + LLPanelGroupSubTab::setGroupID(id); } +// LLPanelGroupBanListSubTab ///////////////////////////////////////////// +static LLPanelInjector 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; + + mHeader = parent->getChild("banlist_header", recurse); + mFooter = parent->getChild("banlist_footer", recurse); + + mBanList = parent->getChild("ban_list", recurse); + + mCreateBanButton = parent->getChild("ban_create", recurse); + mDeleteBanButton = parent->getChild("ban_delete", recurse); + mRefreshBanListButton = parent->getChild("ban_refresh", recurse); + mBanCountText = parent->getChild("ban_count", recurse); + + if(!mBanList || !mCreateBanButton || !mDeleteBanButton || !mRefreshBanListButton || !mBanCountText) + return FALSE; + + mBanList->setCommitOnSelectionChange(TRUE); + mBanList->setCommitCallback(onBanEntrySelect, this); + + mCreateBanButton->setClickedCallback(onCreateBanEntry, this); + mCreateBanButton->setEnabled(FALSE); + + 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(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(user_data); + if (!self) + return; + + self->handleCreateBanEntry(); +} + +void LLPanelGroupBanListSubTab::handleCreateBanEntry() +{ + LLFloaterGroupBulkBan::showForGroup(mGroupID); + //populateBanList(); +} + +void LLPanelGroupBanListSubTab::onDeleteBanEntry(void* user_data) +{ + LLPanelGroupBanListSubTab* self = static_cast(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 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 ban_ids; + std::vector::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(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::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"); + + // 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 86499fa69f..e8c0ab82ea 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 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); @@ -209,6 +216,7 @@ protected: LLScrollListCtrl* mAssignedRolesList; LLScrollListCtrl* mAllowedActionsList; LLButton* mEjectBtn; + LLButton* mBanBtn; BOOL mChanged; BOOL mPendingMemberUpdate; @@ -229,6 +237,7 @@ private: void onExportMembersToXML(); }; + class LLPanelGroupRolesSubTab : public LLPanelGroupSubTab { public: @@ -251,7 +260,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); @@ -270,6 +279,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); @@ -291,6 +303,7 @@ protected: std::string mRemoveEveryoneTxt; }; + class LLPanelGroupActionsSubTab : public LLPanelGroupSubTab { public: @@ -318,4 +331,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 d4b6dfe503..7425a8d267 100755 --- a/indra/newview/llpanelplaceprofile.cpp +++ b/indra/newview/llpanelplaceprofile.cpp @@ -500,8 +500,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/lltoastnotifypanel.cpp b/indra/newview/lltoastnotifypanel.cpp index c7183a715c..aa3ca68980 100755 --- a/indra/newview/lltoastnotifypanel.cpp +++ b/indra/newview/lltoastnotifypanel.cpp @@ -45,6 +45,7 @@ //#include "llfloaterimsession.h" #include "fsfloaterim.h" // [FS communication UI] +#include "llavataractions.h" const S32 BOTTOM_PAD = VPAD * 3; const S32 IGNORE_BTN_TOP_DELTA = 3*VPAD;//additional ignore_btn padding diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index d2c9ef6b85..3111a5cc3c 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -2835,6 +2835,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 --- 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 6683b1e004..50438d966d 100755 --- a/indra/newview/skins/default/xui/en/floater_about.xml +++ b/indra/newview/skins/default/xui/en/floater_about.xml @@ -86,7 +86,7 @@ The Lindens are: top_pad="5" width="444" 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 + - + + +You are about to add the Ability '[ACTION_NAME]' to the Role '[ROLE_NAME]'. + + *WARNING* +Any Member in a Role with this Ability will also be granted the Abilities '[ACTION_NAME_2]' and '[ACTION_NAME_3]' + + + + +You are removing the Ability '[ACTION_NAME]' to the Role '[ROLE_NAME]'. + + *WARNING* +Removing this ability will NOT remove the Abilities '[ACTION_NAME_2]' and '[ACTION_NAME_3]'. + +If you no longer wish to have these abilities granted to this role, disable them immediately! + + + + +Unable to leave group: [reason]. + reason + + + + +You have left the group [group_name]. + group_name + + + + + + (loading...) + + + Group bans not sent: too many Residents selected. Group bans are limited to 100 per request. + + + Group ban not sent: you do not have 'Manage ban list' ability. + + + Group ban not sent: your group have reached limit of allowed ban records. + + + Some group bans were not sent: +[REASONS] + + + Group bans were not sent: +[REASONS] + + + - The following resident(s) are already banned: [RESIDENTS]. + + + - Ban limit reached, following agents not banned: [RESIDENTS]. + + + - You cannot ban yourself from a group. + + + You can select multiple Residents to ban from your group. Click 'Open Resident Chooser' to start. + +