diff options
author | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
---|---|---|
committer | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
commit | 1b67dd855c41f5a0cda7ec2a68d98071986ca703 (patch) | |
tree | ab243607f74f78200787bba5b9b88f07ef1b966f /indra/newview/llpanelgroupinvite.cpp | |
parent | 6d6eabca44d08d5b97bfe3e941d2b9687c2246ea (diff) | |
parent | e1623bb276f83a43ce7a197e388720c05bdefe61 (diff) |
Merge remote-tracking branch 'origin/main' into DRTVWR-600-maint-A
# Conflicts:
# autobuild.xml
# indra/cmake/CMakeLists.txt
# indra/cmake/GoogleMock.cmake
# indra/llaudio/llaudioengine_fmodstudio.cpp
# indra/llaudio/llaudioengine_fmodstudio.h
# indra/llaudio/lllistener_fmodstudio.cpp
# indra/llaudio/lllistener_fmodstudio.h
# indra/llaudio/llstreamingaudio_fmodstudio.cpp
# indra/llaudio/llstreamingaudio_fmodstudio.h
# indra/llcharacter/llmultigesture.cpp
# indra/llcharacter/llmultigesture.h
# indra/llimage/llimage.cpp
# indra/llimage/llimagepng.cpp
# indra/llimage/llimageworker.cpp
# indra/llimage/tests/llimageworker_test.cpp
# indra/llmessage/tests/llmockhttpclient.h
# indra/llprimitive/llgltfmaterial.h
# indra/llrender/llfontfreetype.cpp
# indra/llui/llcombobox.cpp
# indra/llui/llfolderview.cpp
# indra/llui/llfolderviewmodel.h
# indra/llui/lllineeditor.cpp
# indra/llui/lllineeditor.h
# indra/llui/lltextbase.cpp
# indra/llui/lltextbase.h
# indra/llui/lltexteditor.cpp
# indra/llui/lltextvalidate.cpp
# indra/llui/lltextvalidate.h
# indra/llui/lluictrl.h
# indra/llui/llview.cpp
# indra/llwindow/llwindowmacosx.cpp
# indra/newview/app_settings/settings.xml
# indra/newview/llappearancemgr.cpp
# indra/newview/llappearancemgr.h
# indra/newview/llavatarpropertiesprocessor.cpp
# indra/newview/llavatarpropertiesprocessor.h
# indra/newview/llbreadcrumbview.cpp
# indra/newview/llbreadcrumbview.h
# indra/newview/llbreastmotion.cpp
# indra/newview/llbreastmotion.h
# indra/newview/llconversationmodel.h
# indra/newview/lldensityctrl.cpp
# indra/newview/lldensityctrl.h
# indra/newview/llface.inl
# indra/newview/llfloatereditsky.cpp
# indra/newview/llfloatereditwater.cpp
# indra/newview/llfloateremojipicker.h
# indra/newview/llfloaterimsessiontab.cpp
# indra/newview/llfloaterprofiletexture.cpp
# indra/newview/llfloaterprofiletexture.h
# indra/newview/llgesturemgr.cpp
# indra/newview/llgesturemgr.h
# indra/newview/llimpanel.cpp
# indra/newview/llimpanel.h
# indra/newview/llinventorybridge.cpp
# indra/newview/llinventorybridge.h
# indra/newview/llinventoryclipboard.cpp
# indra/newview/llinventoryclipboard.h
# indra/newview/llinventoryfunctions.cpp
# indra/newview/llinventoryfunctions.h
# indra/newview/llinventorygallery.cpp
# indra/newview/lllistbrowser.cpp
# indra/newview/lllistbrowser.h
# indra/newview/llpanelobjectinventory.cpp
# indra/newview/llpanelprofile.cpp
# indra/newview/llpanelprofile.h
# indra/newview/llpreviewgesture.cpp
# indra/newview/llsavedsettingsglue.cpp
# indra/newview/llsavedsettingsglue.h
# indra/newview/lltooldraganddrop.cpp
# indra/newview/llurllineeditorctrl.cpp
# indra/newview/llvectorperfoptions.cpp
# indra/newview/llvectorperfoptions.h
# indra/newview/llviewerparceloverlay.cpp
# indra/newview/llviewertexlayer.cpp
# indra/newview/llviewertexturelist.cpp
# indra/newview/macmain.h
# indra/test/test.cpp
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 34307c9951..b169bfb623 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);
+}
|