diff options
Diffstat (limited to 'indra/newview/llpanelgroupinvite.cpp')
-rw-r--r-- | indra/newview/llpanelgroupinvite.cpp | 1400 |
1 files changed, 700 insertions, 700 deletions
diff --git a/indra/newview/llpanelgroupinvite.cpp b/indra/newview/llpanelgroupinvite.cpp index b169bfb623..8a6876f8fc 100644 --- a/indra/newview/llpanelgroupinvite.cpp +++ b/indra/newview/llpanelgroupinvite.cpp @@ -1,700 +1,700 @@ -/**
- * @file llpanelgroupinvite.cpp
- *
- * $LicenseInfo:firstyear=2006&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, 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 "llpanelgroupinvite.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"
-
-class LLPanelGroupInvite::impl
-{
-public:
- impl(const LLUUID& group_id);
- ~impl();
-
- void addUsers(const std::vector<std::string>& names,
- const uuid_vec_t& agent_ids);
- void submitInvitations();
- void addRoleNames(LLGroupMgrGroupData* gdatap);
- void handleRemove();
- void handleSelection();
-
- static void callbackClickCancel(void* userdata);
- static void callbackClickOK(void* userdata);
- static void callbackClickAdd(void* userdata);
- static void callbackClickRemove(void* userdata);
- static void callbackSelect(LLUICtrl* ctrl, void* userdata);
- static void callbackAddUsers(const uuid_vec_t& agent_ids,
- void* user_data);
-
- static void onAvatarNameCache(const LLUUID& agent_id,
- const LLAvatarName& av_name,
- void* user_data);
-
- bool inviteOwnerCallback(const LLSD& notification, const LLSD& response);
-
-public:
- LLUUID mGroupID;
-
- std::string mLoadingText;
- LLNameListCtrl *mInvitees;
- LLComboBox *mRoleNames;
- LLButton *mOKButton;
- LLButton *mRemoveButton;
- LLTextBox *mGroupName;
- std::string mOwnerWarning;
- std::string mAlreadyInGroup;
- std::string mTooManySelected;
- bool mConfirmedOwnerInvite;
- std::set<LLUUID> mInviteeIDs;
-
- void (*mCloseCallback)(void* data);
-
- void* mCloseCallbackUserData;
-
- boost::signals2::connection mAvatarNameCacheConnection;
-};
-
-
-LLPanelGroupInvite::impl::impl(const LLUUID& group_id):
- mGroupID( group_id ),
- mLoadingText (),
- mInvitees ( NULL ),
- mRoleNames( NULL ),
- mOKButton ( NULL ),
- mRemoveButton( NULL ),
- mGroupName( NULL ),
- mConfirmedOwnerInvite( false ),
- mCloseCallback( NULL ),
- mCloseCallbackUserData( NULL ),
- mAvatarNameCacheConnection()
-{
-}
-
-LLPanelGroupInvite::impl::~impl()
-{
- if (mAvatarNameCacheConnection.connected())
- {
- mAvatarNameCacheConnection.disconnect();
- }
-}
-
-const S32 MAX_GROUP_INVITES = 100; // Max invites per request. 100 to match server cap.
-
-void LLPanelGroupInvite::impl::addUsers(const std::vector<std::string>& names,
- const uuid_vec_t& agent_ids)
-{
- std::string name;
- LLUUID id;
-
- if (names.size() + mInviteeIDs.size() > MAX_GROUP_INVITES)
- {
- // 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];
-
- // Make sure this agent isn't already in the list.
- if (mInviteeIDs.find(id) != mInviteeIDs.end())
- {
- continue;
- }
-
- //add the name to the names list
- LLSD row;
- row["id"] = id;
- row["columns"][0]["value"] = name;
-
- mInvitees->addElement(row);
- mInviteeIDs.insert(id);
- }
-}
-
-void LLPanelGroupInvite::impl::submitInvitations()
-{
- std::map<LLUUID, LLUUID> role_member_pairs;
-
- LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
-
- // Default to everyone role.
- LLUUID role_id = LLUUID::null;
-
- if (mRoleNames)
- {
- role_id = mRoleNames->getCurrentID();
-
- // owner role: display confirmation and wait for callback
- if ((role_id == gdatap->mOwnerRole) && (!mConfirmedOwnerInvite))
- {
- LLSD args;
- args["MESSAGE"] = mOwnerWarning;
- LLNotificationsUtil::add("GenericAlertYesCancel", args, LLSD(), boost::bind(&LLPanelGroupInvite::impl::inviteOwnerCallback, this, _1, _2));
- return; // we'll be called again if user confirms
- }
- }
-
- bool already_in_group = false;
- //loop over the users
- std::vector<LLScrollListItem*> items = mInvitees->getAllData();
- for (std::vector<LLScrollListItem*>::iterator iter = items.begin();
- iter != items.end(); ++iter)
- {
- LLScrollListItem* item = *iter;
- if(LLGroupActions::isAvatarMemberOfGroup(mGroupID, item->getUUID()))
- {
- already_in_group = true;
- continue;
- }
- role_member_pairs[item->getUUID()] = role_id;
- }
-
- if (role_member_pairs.size() > MAX_GROUP_INVITES)
- {
- // Fail!
- LLSD msg;
- msg["MESSAGE"] = mTooManySelected;
- LLNotificationsUtil::add("GenericAlert", msg);
- (*mCloseCallback)(mCloseCallbackUserData);
- return;
- }
-
- LLGroupMgr::getInstance()->sendGroupMemberInvites(mGroupID, role_member_pairs);
-
- if(already_in_group)
- {
- LLSD msg;
- msg["MESSAGE"] = mAlreadyInGroup;
- LLNotificationsUtil::add("GenericAlert", msg);
- }
-
- //then close
- (*mCloseCallback)(mCloseCallbackUserData);
-}
-
-bool LLPanelGroupInvite::impl::inviteOwnerCallback(const LLSD& notification, const LLSD& response)
-{
- S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
-
- switch(option)
- {
- case 0:
- // user confirmed that they really want a new group owner
- mConfirmedOwnerInvite = true;
- submitInvitations();
- break;
- case 1:
- // fall through
- default:
- break;
- }
- return false;
-}
-
-
-
-void LLPanelGroupInvite::impl::addRoleNames(LLGroupMgrGroupData* gdatap)
-{
- LLGroupMgrGroupData::member_list_t::iterator agent_iter =
- gdatap->mMembers.find(gAgent.getID());
-
- //loop over the agent's roles in the group
- //then add those roles to the list of roles that the agent
- //can invite people to be.
- //if the user is the owner then we add
- //all of the roles in the group,
- //else if they have the add to roles power
- //we add every role but owner,
- //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 = false;
- bool can_assign_any = gAgent.hasPowerInGroup(mGroupID,
- GP_ROLE_ASSIGN_MEMBER);
- bool can_assign_limited = gAgent.hasPowerInGroup(mGroupID,
- GP_ROLE_ASSIGN_MEMBER_LIMITED);
- LLGroupMemberData* member_data = NULL;
- //get the member data for the agent if it exists
- if (agent_iter != gdatap->mMembers.end())
- {
- member_data = (*agent_iter).second;
- if (member_data && mRoleNames)
- {
- is_owner = member_data->isOwner();
- }//end if member data is not null
- }//end if agent is in the group
-
-
-
- LLGroupMgrGroupData::role_list_t::iterator rit = gdatap->mRoles.begin();
- LLGroupMgrGroupData::role_list_t::iterator end = gdatap->mRoles.end();
-
- //populate the role list:
- for ( ; rit != end; ++rit)
- {
- LLUUID role_id = (*rit).first;
- LLRoleData rd;
- if ( gdatap->getRoleData(role_id,rd) )
- {
- // Owners can add any role.
- if ( is_owner
- // Even 'can_assign_any' can't add owner role.
- || (can_assign_any && role_id != gdatap->mOwnerRole)
- // Add all roles user is in
- || (can_assign_limited && member_data && member_data->isInRole(role_id))
- // Everyone role.
- || role_id == LLUUID::null )
- {
- mRoleNames->add(rd.mRoleName,
- role_id,
- ADD_BOTTOM);
- }
- }
- }
-}
-
-//static
-void LLPanelGroupInvite::impl::callbackClickAdd(void* userdata)
-{
- LLPanelGroupInvite* panelp = (LLPanelGroupInvite*) userdata;
-
- if ( panelp )
- {
- //Right now this is hard coded with some knowledge that it is part
- //of a floater since the avatar picker needs to be added as a dependent
- //floater to the parent floater.
- //Soon the avatar picker will be embedded into this panel
- //instead of being it's own separate floater. But that is next week.
- //This will do for now. -jwolk May 10, 2006
- LLView * button = panelp->findChild<LLButton>("add_button");
- LLFloater * root_floater = gFloaterView->getParentFloater(panelp);
- LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(
- boost::bind(impl::callbackAddUsers, _1, panelp->mImplementation), true, false, false, root_floater->getName(), button);
- if (picker)
- {
- root_floater->addDependentFloater(picker);
- }
- }
-}
-
-//static
-void LLPanelGroupInvite::impl::callbackClickRemove(void* userdata)
-{
- impl* selfp = (impl*) userdata;
-
- if ( selfp ) selfp->handleRemove();
-}
-
-void LLPanelGroupInvite::impl::handleRemove()
-{
- // Check if there is anything selected.
- std::vector<LLScrollListItem*> selection =
- mInvitees->getAllSelected();
- if (selection.empty()) return;
-
- std::vector<LLScrollListItem*>::iterator iter;
- for(iter = selection.begin(); iter != selection.end(); ++iter)
- {
- mInviteeIDs.erase( (*iter)->getUUID() );
- }
-
- // Remove all selected invitees.
- mInvitees->deleteSelectedItems();
- mRemoveButton->setEnabled(false);
-}
-
-// static
-void LLPanelGroupInvite::impl::callbackSelect(
- LLUICtrl* ctrl, void* userdata)
-{
- impl* selfp = (impl*) userdata;
- if ( selfp ) selfp->handleSelection();
-}
-
-void LLPanelGroupInvite::impl::handleSelection()
-{
- // Check if there is anything selected.
- std::vector<LLScrollListItem*> selection =
- mInvitees->getAllSelected();
- if (selection.empty())
- {
- mRemoveButton->setEnabled(false);
- }
- else
- {
- mRemoveButton->setEnabled(true);
- }
-}
-
-void LLPanelGroupInvite::impl::callbackClickCancel(void* userdata)
-{
- impl* selfp = (impl*) userdata;
-
- if ( selfp )
- {
- (*(selfp->mCloseCallback))(selfp->mCloseCallbackUserData);
- }
-}
-
-void LLPanelGroupInvite::impl::callbackClickOK(void* userdata)
-{
- impl* selfp = (impl*) userdata;
-
- if ( selfp ) selfp->submitInvitations();
-}
-
-
-
-//static
-void LLPanelGroupInvite::impl::callbackAddUsers(const uuid_vec_t& agent_ids, void* user_data)
-{
- std::vector<std::string> names;
- for (S32 i = 0; i < (S32)agent_ids.size(); i++)
- {
- LLAvatarName av_name;
- if (LLAvatarNameCache::get(agent_ids[i], &av_name))
- {
- LLPanelGroupInvite::impl::onAvatarNameCache(agent_ids[i], av_name, user_data);
- }
- else
- {
- impl* selfp = (impl*) user_data;
- if (selfp)
- {
- if (selfp->mAvatarNameCacheConnection.connected())
- {
- selfp->mAvatarNameCacheConnection.disconnect();
- }
- // *TODO : Add a callback per avatar name being fetched.
- selfp->mAvatarNameCacheConnection = LLAvatarNameCache::get(agent_ids[i],boost::bind(&LLPanelGroupInvite::impl::onAvatarNameCache, _1, _2, user_data));
- }
- }
- }
-
-}
-
-void LLPanelGroupInvite::impl::onAvatarNameCache(const LLUUID& agent_id,
- const LLAvatarName& av_name,
- void* user_data)
-{
- impl* selfp = (impl*) user_data;
-
- if (selfp)
- {
- if (selfp->mAvatarNameCacheConnection.connected())
- {
- selfp->mAvatarNameCacheConnection.disconnect();
- }
- std::vector<std::string> names;
- uuid_vec_t agent_ids;
- agent_ids.push_back(agent_id);
- names.push_back(av_name.getCompleteName());
-
- selfp->addUsers(names, agent_ids);
- }
-}
-
-
-LLPanelGroupInvite::LLPanelGroupInvite(const LLUUID& group_id)
- : LLPanel(),
- mImplementation(new impl(group_id)),
- mPendingUpdate(false)
-{
- // Pass on construction of this panel to the control factory.
- buildFromFile( "panel_group_invite.xml");
-}
-
-LLPanelGroupInvite::~LLPanelGroupInvite()
-{
- delete mImplementation;
-}
-
-void LLPanelGroupInvite::setCloseCallback(void (*close_callback)(void*),
- void* data)
-{
- mImplementation->mCloseCallback = close_callback;
- mImplementation->mCloseCallbackUserData = data;
-}
-
-void LLPanelGroupInvite::clear()
-{
- mStoreSelected = LLUUID::null;
- mImplementation->mInvitees->deleteAllItems();
- mImplementation->mRoleNames->clear();
- mImplementation->mRoleNames->removeall();
- mImplementation->mOKButton->setEnabled(false);
- mImplementation->mInviteeIDs.clear();
-}
-
-void LLPanelGroupInvite::addUsers(uuid_vec_t& agent_ids)
-{
- std::vector<std::string> names;
- for (S32 i = 0; i < (S32)agent_ids.size(); i++)
- {
- std::string fullname;
- LLUUID agent_id = agent_ids[i];
- LLViewerObject* dest = gObjectList.findObject(agent_id);
- if(dest && dest->isAvatar())
- {
- LLNameValue* nvfirst = dest->getNVPair("FirstName");
- LLNameValue* nvlast = dest->getNVPair("LastName");
- if(nvfirst && nvlast)
- {
- fullname = LLCacheName::buildFullName(
- nvfirst->getString(), nvlast->getString());
-
- }
- if (!fullname.empty())
- {
- names.push_back(fullname);
- }
- else
- {
- LL_WARNS() << "llPanelGroupInvite: Selected avatar has no name: " << dest->getID() << LL_ENDL;
- names.push_back("(Unknown)");
- }
- }
- else
- {
- //looks like user try to invite offline avatar (or the avatar from the other region)
- //for offline avatar_id gObjectList.findObject() will return null
- //so we need to do this additional search in avatar tracker, see EXT-4732
- LLAvatarName av_name;
- if (!LLAvatarNameCache::get(agent_id, &av_name))
- {
- // actually it should happen, just in case
- //LLAvatarNameCache::get(LLUUID(agent_id), boost::bind(&LLPanelGroupInvite::addUserCallback, this, _1, _2));
- // for this special case!
- //when there is no cached name we should remove resident from agent_ids list to avoid breaking of sequence
- // removed id will be added in callback
- agent_ids.erase(agent_ids.begin() + i);
- }
- else
- {
- names.push_back(av_name.getAccountName());
- }
- }
- }
- mImplementation->addUsers(names, agent_ids);
-}
-
-void LLPanelGroupInvite::addUserCallback(const LLUUID& id, const LLAvatarName& av_name)
-{
- std::vector<std::string> names;
- uuid_vec_t agent_ids;
- agent_ids.push_back(id);
- names.push_back(av_name.getAccountName());
-
- mImplementation->addUsers(names, agent_ids);
-}
-
-void LLPanelGroupInvite::draw()
-{
- LLPanel::draw();
- if (mPendingUpdate)
- {
- updateLists();
- }
-}
-
-void LLPanelGroupInvite::update()
-{
- mPendingUpdate = false;
- if (mImplementation->mGroupName)
- {
- mImplementation->mGroupName->setText(mImplementation->mLoadingText);
- }
- if ( mImplementation->mRoleNames )
- {
- mStoreSelected = mImplementation->mRoleNames->getCurrentID();
- mImplementation->mRoleNames->clear();
- mImplementation->mRoleNames->removeall();
- mImplementation->mRoleNames->add(mImplementation->mLoadingText, LLUUID::null, ADD_BOTTOM);
- mImplementation->mRoleNames->setCurrentByID(LLUUID::null);
- }
-
- updateLists();
-}
-
-void LLPanelGroupInvite::updateLists()
-{
- LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mImplementation->mGroupID);
- bool waiting = false;
-
- if (gdatap)
- {
- if (gdatap->isGroupPropertiesDataComplete())
- {
- if (mImplementation->mGroupName)
- {
- mImplementation->mGroupName->setText(gdatap->mName);
- }
- }
- else
- {
- waiting = true;
- }
- if (gdatap->isRoleDataComplete() && gdatap->isMemberDataComplete()
- && (gdatap->isRoleMemberDataComplete() || !gdatap->mMembers.size())) // MAINT-5270: large groups receives an empty members list without some powers, so RoleMemberData wouldn't be complete for them
- {
- if ( mImplementation->mRoleNames )
- {
- mImplementation->mRoleNames->clear();
- mImplementation->mRoleNames->removeall();
-
- //add the role names and select the everybody role by default
- mImplementation->addRoleNames(gdatap);
- mImplementation->mRoleNames->setCurrentByID(mStoreSelected);
- }
- }
- else
- {
- waiting = true;
- }
- }
- else
- {
- waiting = true;
- }
-
- if (waiting)
- {
- if (!mPendingUpdate)
- {
- // Note: this will partially fail if some requests are already in progress
- LLGroupMgr::getInstance()->sendGroupPropertiesRequest(mImplementation->mGroupID);
- LLGroupMgr::getInstance()->sendGroupRoleDataRequest(mImplementation->mGroupID);
- LLGroupMgr::getInstance()->sendGroupRoleMembersRequest(mImplementation->mGroupID);
- LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mImplementation->mGroupID);
- }
- else if (gdatap)
- {
- // restart requests that were interrupted/dropped/failed to start
- if (!gdatap->isRoleDataPending() && !gdatap->isRoleDataComplete())
- {
- LLGroupMgr::getInstance()->sendGroupRoleDataRequest(mImplementation->mGroupID);
- }
- if (!gdatap->isRoleMemberDataPending() && !gdatap->isRoleMemberDataComplete())
- {
- LLGroupMgr::getInstance()->sendGroupRoleMembersRequest(mImplementation->mGroupID);
- }
- // sendCapGroupMembersRequest has a per frame send limitation that could have
- // interrupted previous request
- if (!gdatap->isMemberDataPending() && !gdatap->isMemberDataComplete())
- {
- LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mImplementation->mGroupID);
- }
- }
- mPendingUpdate = true;
- }
- else
- {
- mPendingUpdate = false;
- if (mImplementation->mOKButton && mImplementation->mRoleNames->getItemCount())
- {
- mImplementation->mOKButton->setEnabled(true);
- }
- }
-}
-
-bool LLPanelGroupInvite::postBuild()
-{
- constexpr bool recurse = true;
-
- mImplementation->mLoadingText = getString("loading");
- mImplementation->mRoleNames = getChild<LLComboBox>("role_name",
- recurse);
- mImplementation->mGroupName = getChild<LLTextBox>("group_name_text", recurse);
- mImplementation->mInvitees =
- getChild<LLNameListCtrl>("invitee_list", recurse);
- if ( mImplementation->mInvitees )
- {
- mImplementation->mInvitees->setCommitOnSelectionChange(true);
- mImplementation->mInvitees->setCommitCallback(impl::callbackSelect, mImplementation);
- }
-
- LLButton* button = getChild<LLButton>("add_button", recurse);
- if ( button )
- {
- // default to opening avatarpicker automatically
- // (*impl::callbackClickAdd)((void*)this);
- button->setClickedCallback(impl::callbackClickAdd, this);
- }
-
- mImplementation->mRemoveButton =
- getChild<LLButton>("remove_button", recurse);
- if ( mImplementation->mRemoveButton )
- {
- mImplementation->mRemoveButton->setClickedCallback(impl::callbackClickRemove, mImplementation);
- mImplementation->mRemoveButton->setEnabled(false);
- }
-
- mImplementation->mOKButton =
- getChild<LLButton>("invite_button", recurse);
- if ( mImplementation->mOKButton )
- {
- mImplementation->mOKButton->setClickedCallback(impl::callbackClickOK, mImplementation);
- mImplementation->mOKButton->setEnabled(false);
- }
-
- button = getChild<LLButton>("cancel_button", recurse);
- if ( button )
- {
- button->setClickedCallback(impl::callbackClickCancel, mImplementation);
- }
-
- mImplementation->mOwnerWarning = getString("confirm_invite_owner_str");
- mImplementation->mAlreadyInGroup = getString("already_in_group");
- mImplementation->mTooManySelected = getString("invite_selection_too_large");
-
- update();
-
- return (mImplementation->mRoleNames &&
- mImplementation->mInvitees &&
- mImplementation->mRemoveButton);
-}
+/** + * @file llpanelgroupinvite.cpp + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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 "llpanelgroupinvite.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" + +class LLPanelGroupInvite::impl +{ +public: + impl(const LLUUID& group_id); + ~impl(); + + void addUsers(const std::vector<std::string>& names, + const uuid_vec_t& agent_ids); + void submitInvitations(); + void addRoleNames(LLGroupMgrGroupData* gdatap); + void handleRemove(); + void handleSelection(); + + static void callbackClickCancel(void* userdata); + static void callbackClickOK(void* userdata); + static void callbackClickAdd(void* userdata); + static void callbackClickRemove(void* userdata); + static void callbackSelect(LLUICtrl* ctrl, void* userdata); + static void callbackAddUsers(const uuid_vec_t& agent_ids, + void* user_data); + + static void onAvatarNameCache(const LLUUID& agent_id, + const LLAvatarName& av_name, + void* user_data); + + bool inviteOwnerCallback(const LLSD& notification, const LLSD& response); + +public: + LLUUID mGroupID; + + std::string mLoadingText; + LLNameListCtrl *mInvitees; + LLComboBox *mRoleNames; + LLButton *mOKButton; + LLButton *mRemoveButton; + LLTextBox *mGroupName; + std::string mOwnerWarning; + std::string mAlreadyInGroup; + std::string mTooManySelected; + bool mConfirmedOwnerInvite; + std::set<LLUUID> mInviteeIDs; + + void (*mCloseCallback)(void* data); + + void* mCloseCallbackUserData; + + boost::signals2::connection mAvatarNameCacheConnection; +}; + + +LLPanelGroupInvite::impl::impl(const LLUUID& group_id): + mGroupID( group_id ), + mLoadingText (), + mInvitees ( NULL ), + mRoleNames( NULL ), + mOKButton ( NULL ), + mRemoveButton( NULL ), + mGroupName( NULL ), + mConfirmedOwnerInvite( false ), + mCloseCallback( NULL ), + mCloseCallbackUserData( NULL ), + mAvatarNameCacheConnection() +{ +} + +LLPanelGroupInvite::impl::~impl() +{ + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } +} + +const S32 MAX_GROUP_INVITES = 100; // Max invites per request. 100 to match server cap. + +void LLPanelGroupInvite::impl::addUsers(const std::vector<std::string>& names, + const uuid_vec_t& agent_ids) +{ + std::string name; + LLUUID id; + + if (names.size() + mInviteeIDs.size() > MAX_GROUP_INVITES) + { + // 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]; + + // Make sure this agent isn't already in the list. + if (mInviteeIDs.find(id) != mInviteeIDs.end()) + { + continue; + } + + //add the name to the names list + LLSD row; + row["id"] = id; + row["columns"][0]["value"] = name; + + mInvitees->addElement(row); + mInviteeIDs.insert(id); + } +} + +void LLPanelGroupInvite::impl::submitInvitations() +{ + std::map<LLUUID, LLUUID> role_member_pairs; + + LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID); + + // Default to everyone role. + LLUUID role_id = LLUUID::null; + + if (mRoleNames) + { + role_id = mRoleNames->getCurrentID(); + + // owner role: display confirmation and wait for callback + if ((role_id == gdatap->mOwnerRole) && (!mConfirmedOwnerInvite)) + { + LLSD args; + args["MESSAGE"] = mOwnerWarning; + LLNotificationsUtil::add("GenericAlertYesCancel", args, LLSD(), boost::bind(&LLPanelGroupInvite::impl::inviteOwnerCallback, this, _1, _2)); + return; // we'll be called again if user confirms + } + } + + bool already_in_group = false; + //loop over the users + std::vector<LLScrollListItem*> items = mInvitees->getAllData(); + for (std::vector<LLScrollListItem*>::iterator iter = items.begin(); + iter != items.end(); ++iter) + { + LLScrollListItem* item = *iter; + if(LLGroupActions::isAvatarMemberOfGroup(mGroupID, item->getUUID())) + { + already_in_group = true; + continue; + } + role_member_pairs[item->getUUID()] = role_id; + } + + if (role_member_pairs.size() > MAX_GROUP_INVITES) + { + // Fail! + LLSD msg; + msg["MESSAGE"] = mTooManySelected; + LLNotificationsUtil::add("GenericAlert", msg); + (*mCloseCallback)(mCloseCallbackUserData); + return; + } + + LLGroupMgr::getInstance()->sendGroupMemberInvites(mGroupID, role_member_pairs); + + if(already_in_group) + { + LLSD msg; + msg["MESSAGE"] = mAlreadyInGroup; + LLNotificationsUtil::add("GenericAlert", msg); + } + + //then close + (*mCloseCallback)(mCloseCallbackUserData); +} + +bool LLPanelGroupInvite::impl::inviteOwnerCallback(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + + switch(option) + { + case 0: + // user confirmed that they really want a new group owner + mConfirmedOwnerInvite = true; + submitInvitations(); + break; + case 1: + // fall through + default: + break; + } + return false; +} + + + +void LLPanelGroupInvite::impl::addRoleNames(LLGroupMgrGroupData* gdatap) +{ + LLGroupMgrGroupData::member_list_t::iterator agent_iter = + gdatap->mMembers.find(gAgent.getID()); + + //loop over the agent's roles in the group + //then add those roles to the list of roles that the agent + //can invite people to be. + //if the user is the owner then we add + //all of the roles in the group, + //else if they have the add to roles power + //we add every role but owner, + //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 = false; + bool can_assign_any = gAgent.hasPowerInGroup(mGroupID, + GP_ROLE_ASSIGN_MEMBER); + bool can_assign_limited = gAgent.hasPowerInGroup(mGroupID, + GP_ROLE_ASSIGN_MEMBER_LIMITED); + LLGroupMemberData* member_data = NULL; + //get the member data for the agent if it exists + if (agent_iter != gdatap->mMembers.end()) + { + member_data = (*agent_iter).second; + if (member_data && mRoleNames) + { + is_owner = member_data->isOwner(); + }//end if member data is not null + }//end if agent is in the group + + + + LLGroupMgrGroupData::role_list_t::iterator rit = gdatap->mRoles.begin(); + LLGroupMgrGroupData::role_list_t::iterator end = gdatap->mRoles.end(); + + //populate the role list: + for ( ; rit != end; ++rit) + { + LLUUID role_id = (*rit).first; + LLRoleData rd; + if ( gdatap->getRoleData(role_id,rd) ) + { + // Owners can add any role. + if ( is_owner + // Even 'can_assign_any' can't add owner role. + || (can_assign_any && role_id != gdatap->mOwnerRole) + // Add all roles user is in + || (can_assign_limited && member_data && member_data->isInRole(role_id)) + // Everyone role. + || role_id == LLUUID::null ) + { + mRoleNames->add(rd.mRoleName, + role_id, + ADD_BOTTOM); + } + } + } +} + +//static +void LLPanelGroupInvite::impl::callbackClickAdd(void* userdata) +{ + LLPanelGroupInvite* panelp = (LLPanelGroupInvite*) userdata; + + if ( panelp ) + { + //Right now this is hard coded with some knowledge that it is part + //of a floater since the avatar picker needs to be added as a dependent + //floater to the parent floater. + //Soon the avatar picker will be embedded into this panel + //instead of being it's own separate floater. But that is next week. + //This will do for now. -jwolk May 10, 2006 + LLView * button = panelp->findChild<LLButton>("add_button"); + LLFloater * root_floater = gFloaterView->getParentFloater(panelp); + LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show( + boost::bind(impl::callbackAddUsers, _1, panelp->mImplementation), true, false, false, root_floater->getName(), button); + if (picker) + { + root_floater->addDependentFloater(picker); + } + } +} + +//static +void LLPanelGroupInvite::impl::callbackClickRemove(void* userdata) +{ + impl* selfp = (impl*) userdata; + + if ( selfp ) selfp->handleRemove(); +} + +void LLPanelGroupInvite::impl::handleRemove() +{ + // Check if there is anything selected. + std::vector<LLScrollListItem*> selection = + mInvitees->getAllSelected(); + if (selection.empty()) return; + + std::vector<LLScrollListItem*>::iterator iter; + for(iter = selection.begin(); iter != selection.end(); ++iter) + { + mInviteeIDs.erase( (*iter)->getUUID() ); + } + + // Remove all selected invitees. + mInvitees->deleteSelectedItems(); + mRemoveButton->setEnabled(false); +} + +// static +void LLPanelGroupInvite::impl::callbackSelect( + LLUICtrl* ctrl, void* userdata) +{ + impl* selfp = (impl*) userdata; + if ( selfp ) selfp->handleSelection(); +} + +void LLPanelGroupInvite::impl::handleSelection() +{ + // Check if there is anything selected. + std::vector<LLScrollListItem*> selection = + mInvitees->getAllSelected(); + if (selection.empty()) + { + mRemoveButton->setEnabled(false); + } + else + { + mRemoveButton->setEnabled(true); + } +} + +void LLPanelGroupInvite::impl::callbackClickCancel(void* userdata) +{ + impl* selfp = (impl*) userdata; + + if ( selfp ) + { + (*(selfp->mCloseCallback))(selfp->mCloseCallbackUserData); + } +} + +void LLPanelGroupInvite::impl::callbackClickOK(void* userdata) +{ + impl* selfp = (impl*) userdata; + + if ( selfp ) selfp->submitInvitations(); +} + + + +//static +void LLPanelGroupInvite::impl::callbackAddUsers(const uuid_vec_t& agent_ids, void* user_data) +{ + std::vector<std::string> names; + for (S32 i = 0; i < (S32)agent_ids.size(); i++) + { + LLAvatarName av_name; + if (LLAvatarNameCache::get(agent_ids[i], &av_name)) + { + LLPanelGroupInvite::impl::onAvatarNameCache(agent_ids[i], av_name, user_data); + } + else + { + impl* selfp = (impl*) user_data; + if (selfp) + { + if (selfp->mAvatarNameCacheConnection.connected()) + { + selfp->mAvatarNameCacheConnection.disconnect(); + } + // *TODO : Add a callback per avatar name being fetched. + selfp->mAvatarNameCacheConnection = LLAvatarNameCache::get(agent_ids[i],boost::bind(&LLPanelGroupInvite::impl::onAvatarNameCache, _1, _2, user_data)); + } + } + } + +} + +void LLPanelGroupInvite::impl::onAvatarNameCache(const LLUUID& agent_id, + const LLAvatarName& av_name, + void* user_data) +{ + impl* selfp = (impl*) user_data; + + if (selfp) + { + if (selfp->mAvatarNameCacheConnection.connected()) + { + selfp->mAvatarNameCacheConnection.disconnect(); + } + std::vector<std::string> names; + uuid_vec_t agent_ids; + agent_ids.push_back(agent_id); + names.push_back(av_name.getCompleteName()); + + selfp->addUsers(names, agent_ids); + } +} + + +LLPanelGroupInvite::LLPanelGroupInvite(const LLUUID& group_id) + : LLPanel(), + mImplementation(new impl(group_id)), + mPendingUpdate(false) +{ + // Pass on construction of this panel to the control factory. + buildFromFile( "panel_group_invite.xml"); +} + +LLPanelGroupInvite::~LLPanelGroupInvite() +{ + delete mImplementation; +} + +void LLPanelGroupInvite::setCloseCallback(void (*close_callback)(void*), + void* data) +{ + mImplementation->mCloseCallback = close_callback; + mImplementation->mCloseCallbackUserData = data; +} + +void LLPanelGroupInvite::clear() +{ + mStoreSelected = LLUUID::null; + mImplementation->mInvitees->deleteAllItems(); + mImplementation->mRoleNames->clear(); + mImplementation->mRoleNames->removeall(); + mImplementation->mOKButton->setEnabled(false); + mImplementation->mInviteeIDs.clear(); +} + +void LLPanelGroupInvite::addUsers(uuid_vec_t& agent_ids) +{ + std::vector<std::string> names; + for (S32 i = 0; i < (S32)agent_ids.size(); i++) + { + std::string fullname; + LLUUID agent_id = agent_ids[i]; + LLViewerObject* dest = gObjectList.findObject(agent_id); + if(dest && dest->isAvatar()) + { + LLNameValue* nvfirst = dest->getNVPair("FirstName"); + LLNameValue* nvlast = dest->getNVPair("LastName"); + if(nvfirst && nvlast) + { + fullname = LLCacheName::buildFullName( + nvfirst->getString(), nvlast->getString()); + + } + if (!fullname.empty()) + { + names.push_back(fullname); + } + else + { + LL_WARNS() << "llPanelGroupInvite: Selected avatar has no name: " << dest->getID() << LL_ENDL; + names.push_back("(Unknown)"); + } + } + else + { + //looks like user try to invite offline avatar (or the avatar from the other region) + //for offline avatar_id gObjectList.findObject() will return null + //so we need to do this additional search in avatar tracker, see EXT-4732 + LLAvatarName av_name; + if (!LLAvatarNameCache::get(agent_id, &av_name)) + { + // actually it should happen, just in case + //LLAvatarNameCache::get(LLUUID(agent_id), boost::bind(&LLPanelGroupInvite::addUserCallback, this, _1, _2)); + // for this special case! + //when there is no cached name we should remove resident from agent_ids list to avoid breaking of sequence + // removed id will be added in callback + agent_ids.erase(agent_ids.begin() + i); + } + else + { + names.push_back(av_name.getAccountName()); + } + } + } + mImplementation->addUsers(names, agent_ids); +} + +void LLPanelGroupInvite::addUserCallback(const LLUUID& id, const LLAvatarName& av_name) +{ + std::vector<std::string> names; + uuid_vec_t agent_ids; + agent_ids.push_back(id); + names.push_back(av_name.getAccountName()); + + mImplementation->addUsers(names, agent_ids); +} + +void LLPanelGroupInvite::draw() +{ + LLPanel::draw(); + if (mPendingUpdate) + { + updateLists(); + } +} + +void LLPanelGroupInvite::update() +{ + mPendingUpdate = false; + if (mImplementation->mGroupName) + { + mImplementation->mGroupName->setText(mImplementation->mLoadingText); + } + if ( mImplementation->mRoleNames ) + { + mStoreSelected = mImplementation->mRoleNames->getCurrentID(); + mImplementation->mRoleNames->clear(); + mImplementation->mRoleNames->removeall(); + mImplementation->mRoleNames->add(mImplementation->mLoadingText, LLUUID::null, ADD_BOTTOM); + mImplementation->mRoleNames->setCurrentByID(LLUUID::null); + } + + updateLists(); +} + +void LLPanelGroupInvite::updateLists() +{ + LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mImplementation->mGroupID); + bool waiting = false; + + if (gdatap) + { + if (gdatap->isGroupPropertiesDataComplete()) + { + if (mImplementation->mGroupName) + { + mImplementation->mGroupName->setText(gdatap->mName); + } + } + else + { + waiting = true; + } + if (gdatap->isRoleDataComplete() && gdatap->isMemberDataComplete() + && (gdatap->isRoleMemberDataComplete() || !gdatap->mMembers.size())) // MAINT-5270: large groups receives an empty members list without some powers, so RoleMemberData wouldn't be complete for them + { + if ( mImplementation->mRoleNames ) + { + mImplementation->mRoleNames->clear(); + mImplementation->mRoleNames->removeall(); + + //add the role names and select the everybody role by default + mImplementation->addRoleNames(gdatap); + mImplementation->mRoleNames->setCurrentByID(mStoreSelected); + } + } + else + { + waiting = true; + } + } + else + { + waiting = true; + } + + if (waiting) + { + if (!mPendingUpdate) + { + // Note: this will partially fail if some requests are already in progress + LLGroupMgr::getInstance()->sendGroupPropertiesRequest(mImplementation->mGroupID); + LLGroupMgr::getInstance()->sendGroupRoleDataRequest(mImplementation->mGroupID); + LLGroupMgr::getInstance()->sendGroupRoleMembersRequest(mImplementation->mGroupID); + LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mImplementation->mGroupID); + } + else if (gdatap) + { + // restart requests that were interrupted/dropped/failed to start + if (!gdatap->isRoleDataPending() && !gdatap->isRoleDataComplete()) + { + LLGroupMgr::getInstance()->sendGroupRoleDataRequest(mImplementation->mGroupID); + } + if (!gdatap->isRoleMemberDataPending() && !gdatap->isRoleMemberDataComplete()) + { + LLGroupMgr::getInstance()->sendGroupRoleMembersRequest(mImplementation->mGroupID); + } + // sendCapGroupMembersRequest has a per frame send limitation that could have + // interrupted previous request + if (!gdatap->isMemberDataPending() && !gdatap->isMemberDataComplete()) + { + LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mImplementation->mGroupID); + } + } + mPendingUpdate = true; + } + else + { + mPendingUpdate = false; + if (mImplementation->mOKButton && mImplementation->mRoleNames->getItemCount()) + { + mImplementation->mOKButton->setEnabled(true); + } + } +} + +bool LLPanelGroupInvite::postBuild() +{ + constexpr bool recurse = true; + + mImplementation->mLoadingText = getString("loading"); + mImplementation->mRoleNames = getChild<LLComboBox>("role_name", + recurse); + mImplementation->mGroupName = getChild<LLTextBox>("group_name_text", recurse); + mImplementation->mInvitees = + getChild<LLNameListCtrl>("invitee_list", recurse); + if ( mImplementation->mInvitees ) + { + mImplementation->mInvitees->setCommitOnSelectionChange(true); + mImplementation->mInvitees->setCommitCallback(impl::callbackSelect, mImplementation); + } + + LLButton* button = getChild<LLButton>("add_button", recurse); + if ( button ) + { + // default to opening avatarpicker automatically + // (*impl::callbackClickAdd)((void*)this); + button->setClickedCallback(impl::callbackClickAdd, this); + } + + mImplementation->mRemoveButton = + getChild<LLButton>("remove_button", recurse); + if ( mImplementation->mRemoveButton ) + { + mImplementation->mRemoveButton->setClickedCallback(impl::callbackClickRemove, mImplementation); + mImplementation->mRemoveButton->setEnabled(false); + } + + mImplementation->mOKButton = + getChild<LLButton>("invite_button", recurse); + if ( mImplementation->mOKButton ) + { + mImplementation->mOKButton->setClickedCallback(impl::callbackClickOK, mImplementation); + mImplementation->mOKButton->setEnabled(false); + } + + button = getChild<LLButton>("cancel_button", recurse); + if ( button ) + { + button->setClickedCallback(impl::callbackClickCancel, mImplementation); + } + + mImplementation->mOwnerWarning = getString("confirm_invite_owner_str"); + mImplementation->mAlreadyInGroup = getString("already_in_group"); + mImplementation->mTooManySelected = getString("invite_selection_too_large"); + + update(); + + return (mImplementation->mRoleNames && + mImplementation->mInvitees && + mImplementation->mRemoveButton); +} |