diff --git a/indra/newview/llavatarpropertiesprocessor.cpp b/indra/newview/llavatarpropertiesprocessor.cpp index e48b50ea09..f06051370e 100644 --- a/indra/newview/llavatarpropertiesprocessor.cpp +++ b/indra/newview/llavatarpropertiesprocessor.cpp @@ -36,6 +36,7 @@ #include "llstartup.h" // Linden library includes +#include "llavataractions.h" // for getProfileUrl #include "lldate.h" #include "lltrans.h" #include "llui.h" // LLUI::getLanguage() @@ -94,54 +95,115 @@ void LLAvatarPropertiesProcessor::removeObserver(const LLUUID& avatar_id, LLAvat } } - -void LLAvatarPropertiesProcessor::sendGenericRequest(const LLUUID& avatar_id, EAvatarProcessorType type, const std::string method) +void LLAvatarPropertiesProcessor::sendRequest(const LLUUID& avatar_id, EAvatarProcessorType type, const std::string &method) { + // this is the startup state when send_complete_agent_movement() message is sent. + // Before this messages won't work so don't bother trying + if (LLStartUp::getStartupState() <= STATE_AGENT_SEND) + { + return; + } + + if (avatar_id.isNull()) + { + return; + } + // Suppress duplicate requests while waiting for a response from the network if (isPendingRequest(avatar_id, type)) { // waiting for a response, don't re-request return; } - // indicate we're going to make a request - addPendingRequest(avatar_id, type); - std::vector strings; - strings.push_back( avatar_id.asString() ); - send_generic_message(method, strings); + // Cap is not ready for global use + //std::string cap = gAgent.getRegionCapability("AgentProfile"); + std::string cap; + + switch (type) + { + case APT_PROPERTIES: + if (cap.empty()) + { + // indicate we're going to make a request + sendAvatarPropertiesRequestMessage(avatar_id); + } + else + { + initAgentProfileCapRequest(avatar_id, cap); + } + break; + case APT_PICKS: + case APT_GROUPS: + case APT_NOTES: + if (cap.empty()) + { + // indicate we're going to make a request + sendGenericRequest(avatar_id, type, method); + } + else + { + initAgentProfileCapRequest(avatar_id, cap); + } + break; + default: + sendGenericRequest(avatar_id, type, method); + break; + } +} + +void LLAvatarPropertiesProcessor::sendGenericRequest(const LLUUID& avatar_id, EAvatarProcessorType type, const std::string &method) +{ + // indicate we're going to make a request + addPendingRequest(avatar_id, type); + + std::vector strings; + strings.push_back(avatar_id.asString()); + send_generic_message(method, strings); +} + +void LLAvatarPropertiesProcessor::sendAvatarPropertiesRequestMessage(const LLUUID& avatar_id) +{ + addPendingRequest(avatar_id, APT_PROPERTIES); + + LLMessageSystem *msg = gMessageSystem; + + msg->newMessageFast(_PREHASH_AvatarPropertiesRequest); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->addUUIDFast(_PREHASH_AvatarID, avatar_id); + gAgent.sendReliableMessage(); +} + +void LLAvatarPropertiesProcessor::initAgentProfileCapRequest(const LLUUID& avatar_id, const std::string& cap_url) +{ + addPendingRequest(avatar_id, APT_PROPERTIES); + addPendingRequest(avatar_id, APT_PICKS); + addPendingRequest(avatar_id, APT_GROUPS); + addPendingRequest(avatar_id, APT_NOTES); + LLCoros::instance().launch("requestAgentUserInfoCoro", + boost::bind(requestAvatarPropertiesCoro, cap_url, avatar_id)); } void LLAvatarPropertiesProcessor::sendAvatarPropertiesRequest(const LLUUID& avatar_id) { - // this is the startup state when send_complete_agent_movement() message is sent. - // Before this, the AvatarPropertiesRequest message - // won't work so don't bother trying - if (LLStartUp::getStartupState() <= STATE_AGENT_SEND) - { - return; - } - - if (isPendingRequest(avatar_id, APT_PROPERTIES)) - { - // waiting for a response, don't re-request - return; - } - // indicate we're going to make a request - addPendingRequest(avatar_id, APT_PROPERTIES); - - LLMessageSystem *msg = gMessageSystem; - - msg->newMessageFast(_PREHASH_AvatarPropertiesRequest); - msg->nextBlockFast( _PREHASH_AgentData); - msg->addUUIDFast( _PREHASH_AgentID, gAgent.getID() ); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->addUUIDFast( _PREHASH_AvatarID, avatar_id); - gAgent.sendReliableMessage(); + sendRequest(avatar_id, APT_PROPERTIES, "AvatarPropertiesRequest"); } void LLAvatarPropertiesProcessor::sendAvatarPicksRequest(const LLUUID& avatar_id) { - sendGenericRequest(avatar_id, APT_PICKS, "avatarpicksrequest"); + std::string cap = gAgent.getRegionCapability("AgentProfile"); + + if (!cap.empty()) + { + // AgentProfile capability covers picks + sendAvatarPropertiesRequest(avatar_id); + } + else + { + sendGenericRequest(avatar_id, APT_PICKS, "avatarpicksrequest"); + } } void LLAvatarPropertiesProcessor::sendAvatarNotesRequest(const LLUUID& avatar_id) @@ -266,6 +328,113 @@ bool LLAvatarPropertiesProcessor::hasPaymentInfoOnFile(const LLAvatarData* avata return ((avatar_data->flags & AVATAR_TRANSACTED) || (avatar_data->flags & AVATAR_IDENTIFIED)); } +// static +void LLAvatarPropertiesProcessor::requestAvatarPropertiesCoro(std::string cap_url, LLUUID agent_id) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("requestAvatarPropertiesCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpHeaders::ptr_t httpHeaders; + + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + httpOpts->setFollowRedirects(true); + + std::string finalUrl = cap_url + "/" + agent_id.asString(); + + LLSD result = httpAdapter->getAndSuspend(httpRequest, finalUrl, httpOpts, httpHeaders); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status + || !result.has("id") + || agent_id != result["id"].asUUID()) + { + LL_WARNS("AvatarProperties") << "Failed to get agent information for id " << agent_id << LL_ENDL; + LLAvatarPropertiesProcessor* self = getInstance(); + self->removePendingRequest(agent_id, APT_PROPERTIES); + self->removePendingRequest(agent_id, APT_PICKS); + self->removePendingRequest(agent_id, APT_GROUPS); + self->removePendingRequest(agent_id, APT_NOTES); + return; + } + + // Avatar Data + + LLAvatarData avatar_data; + std::string birth_date; + + avatar_data.agent_id = agent_id; + avatar_data.avatar_id = agent_id; + avatar_data.image_id = result["sl_image_id"].asUUID(); + avatar_data.fl_image_id = result["fl_image_id"].asUUID(); + avatar_data.partner_id = result["partner_id"].asUUID(); + avatar_data.about_text = result["sl_about_text"].asString(); + avatar_data.fl_about_text = result["fl_about_text"].asString(); + avatar_data.born_on = result["member_since"].asDate(); + avatar_data.profile_url = getProfileURL(agent_id.asString()); + + avatar_data.flags = 0; + avatar_data.caption_index = 0; + + LLAvatarPropertiesProcessor* self = getInstance(); + // Request processed, no longer pending + self->removePendingRequest(agent_id, APT_PROPERTIES); + self->notifyObservers(agent_id, &avatar_data, APT_PROPERTIES); + + // Picks + + LLSD picks_array = result["picks"]; + LLAvatarPicks avatar_picks; + avatar_picks.agent_id = agent_id; // Not in use? + avatar_picks.target_id = agent_id; + + for (LLSD::array_const_iterator it = picks_array.beginArray(); it != picks_array.endArray(); ++it) + { + const LLSD& pick_data = *it; + avatar_picks.picks_list.emplace_back(pick_data["id"].asUUID(), pick_data["name"].asString()); + } + + // Request processed, no longer pending + self->removePendingRequest(agent_id, APT_PICKS); + self->notifyObservers(agent_id, &avatar_picks, APT_PICKS); + + // Groups + + LLSD groups_array = result["groups"]; + LLAvatarGroups avatar_groups; + avatar_groups.agent_id = agent_id; // Not in use? + avatar_groups.avatar_id = agent_id; // target_id + + for (LLSD::array_const_iterator it = groups_array.beginArray(); it != groups_array.endArray(); ++it) + { + const LLSD& group_info = *it; + LLAvatarGroups::LLGroupData group_data; + group_data.group_powers = 0; // Not in use? + group_data.group_title = group_info["name"].asString(); // Missing data, not in use? + group_data.group_id = group_info["id"].asUUID(); + group_data.group_name = group_info["name"].asString(); + group_data.group_insignia_id = group_info["image_id"].asUUID(); + + avatar_groups.group_list.push_back(group_data); + } + + self->removePendingRequest(agent_id, APT_GROUPS); + self->notifyObservers(agent_id, &avatar_groups, APT_GROUPS); + + // Notes + LLAvatarNotes avatar_notes; + + avatar_notes.agent_id = agent_id; + avatar_notes.target_id = agent_id; + avatar_notes.notes = result["notes"].asString(); + + // Request processed, no longer pending + self->removePendingRequest(agent_id, APT_NOTES); + self->notifyObservers(agent_id, &avatar_notes, APT_NOTES); +} + void LLAvatarPropertiesProcessor::processAvatarPropertiesReply(LLMessageSystem* msg, void**) { LLAvatarData avatar_data; @@ -404,7 +573,7 @@ void LLAvatarPropertiesProcessor::processAvatarNotesReply(LLMessageSystem* msg, void LLAvatarPropertiesProcessor::processAvatarPicksReply(LLMessageSystem* msg, void**) { LLAvatarPicks avatar_picks; - msg->getUUID(_PREHASH_AgentData, _PREHASH_AgentID, avatar_picks.target_id); + msg->getUUID(_PREHASH_AgentData, _PREHASH_AgentID, avatar_picks.agent_id); msg->getUUID(_PREHASH_AgentData, _PREHASH_TargetID, avatar_picks.target_id); S32 block_count = msg->getNumberOfBlocks(_PREHASH_Data); diff --git a/indra/newview/llavatarpropertiesprocessor.h b/indra/newview/llavatarpropertiesprocessor.h index ad7327728c..f778634d25 100644 --- a/indra/newview/llavatarpropertiesprocessor.h +++ b/indra/newview/llavatarpropertiesprocessor.h @@ -248,6 +248,8 @@ public: static bool hasPaymentInfoOnFile(const LLAvatarData* avatar_data); + static void requestAvatarPropertiesCoro(std::string cap_url, LLUUID agent_id); + static void processAvatarPropertiesReply(LLMessageSystem* msg, void**); static void processAvatarInterestsReply(LLMessageSystem* msg, void**); @@ -266,7 +268,10 @@ public: protected: - void sendGenericRequest(const LLUUID& avatar_id, EAvatarProcessorType type, const std::string method); + void sendRequest(const LLUUID& avatar_id, EAvatarProcessorType type, const std::string &method); + void sendGenericRequest(const LLUUID& avatar_id, EAvatarProcessorType type, const std::string &method); + void sendAvatarPropertiesRequestMessage(const LLUUID& avatar_id); + void initAgentProfileCapRequest(const LLUUID& avatar_id, const std::string& cap_url); void notifyObservers(const LLUUID& id,void* data, EAvatarProcessorType type); diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp index 53f3341338..606ea97f82 100644 --- a/indra/newview/llpanelprofile.cpp +++ b/indra/newview/llpanelprofile.cpp @@ -29,6 +29,7 @@ // Common #include "llavatarnamecache.h" +#include "llsdutil.h" #include "llslurl.h" #include "lldateutil.h" //ageFromDate @@ -85,7 +86,206 @@ static const std::string PANEL_PICKS = "panel_profile_picks"; static const std::string PANEL_CLASSIFIEDS = "panel_profile_classifieds"; static const std::string PANEL_FIRSTLIFE = "panel_profile_firstlife"; static const std::string PANEL_NOTES = "panel_profile_notes"; +static const std::string PANEL_PROFILE_VIEW = "panel_profile_view"; +static const std::string PROFILE_PROPERTIES_CAP = "AgentProfile"; + + +////////////////////////////////////////////////////////////////////////// + +void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("request_avatar_properties_coro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpHeaders::ptr_t httpHeaders; + + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + httpOpts->setFollowRedirects(true); + + std::string finalUrl = cap_url + "/" + agent_id.asString(); + + LLSD result = httpAdapter->getAndSuspend(httpRequest, finalUrl, httpOpts, httpHeaders); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status + || !result.has("id") + || agent_id != result["id"].asUUID()) + { + LL_WARNS("AvatarProperties") << "Failed to get agent information for id " << agent_id << LL_ENDL; + return; + } + + LLFloater* floater_profile = LLFloaterReg::findInstance("profile", LLSD().with("id", agent_id)); + if (!floater_profile) + { + // floater is dead, so panels are dead as well + return; + } + + LLPanel *panel = floater_profile->findChild(PANEL_PROFILE_VIEW, TRUE); + LLPanelProfile *panel_profile = dynamic_cast(panel); + if (!panel_profile) + { + LL_WARNS() << PANEL_PROFILE_VIEW << " not found" << LL_ENDL; + return; + } + + + // Avatar Data + + LLAvatarData *avatar_data = &panel_profile->mAvatarData; + std::string birth_date; + + avatar_data->agent_id = agent_id; + avatar_data->avatar_id = agent_id; + avatar_data->image_id = result["sl_image_id"].asUUID(); + avatar_data->fl_image_id = result["fl_image_id"].asUUID(); + avatar_data->partner_id = result["partner_id"].asUUID(); + // Todo: new descriptio size is 65536, check if it actually fits or has scroll + avatar_data->about_text = result["sl_about_text"].asString(); + // Todo: new descriptio size is 65536, check if it actually fits or has scroll + avatar_data->fl_about_text = result["fl_about_text"].asString(); + avatar_data->born_on = result["member_since"].asDate(); + avatar_data->profile_url = getProfileURL(agent_id.asString()); + + avatar_data->flags = 0; + + if (result["online"].asBoolean()) + { + avatar_data->flags |= AVATAR_ONLINE; + } + if (result["allow_publish"].asBoolean()) + { + avatar_data->flags |= AVATAR_ALLOW_PUBLISH; + } + + avatar_data->caption_index = 0; + if (result.has("charter_member")) // won't be present if "caption" is set + { + avatar_data->caption_index = result["charter_member"].asInteger(); + } + else if (result.has("caption")) + { + avatar_data->caption_text = result["caption"].asString(); + } + + panel = floater_profile->findChild(PANEL_SECONDLIFE, TRUE); + LLPanelProfileSecondLife *panel_sl = dynamic_cast(panel); + if (panel_sl) + { + panel_sl->processProfileProperties(avatar_data); + } + + panel = floater_profile->findChild(PANEL_WEB, TRUE); + LLPanelProfileWeb *panel_web = dynamic_cast(panel); + if (panel_web) + { + panel_web->updateButtons(); + } + + panel = floater_profile->findChild(PANEL_FIRSTLIFE, TRUE); + LLPanelProfileFirstLife *panel_first = dynamic_cast(panel); + if (panel_first) + { + panel_first->mCurrentDescription = avatar_data->fl_about_text; + panel_first->mDescriptionEdit->setValue(panel_first->mCurrentDescription); + panel_first->mPicture->setValue(avatar_data->fl_image_id); + panel_first->updateButtons(); + } + + // Picks + + LLSD picks_array = result["picks"]; + LLAvatarPicks avatar_picks; + avatar_picks.agent_id = agent_id; // Not in use? + avatar_picks.target_id = agent_id; + + for (LLSD::array_const_iterator it = picks_array.beginArray(); it != picks_array.endArray(); ++it) + { + const LLSD& pick_data = *it; + avatar_picks.picks_list.emplace_back(pick_data["id"].asUUID(), pick_data["name"].asString()); + } + + panel = floater_profile->findChild(PANEL_PICKS, TRUE); + LLPanelProfilePicks *panel_picks = dynamic_cast(panel); + if (panel_picks) + { + panel_picks->processProperties(&avatar_picks); + } + + // Groups + + LLSD groups_array = result["groups"]; + LLAvatarGroups avatar_groups; + avatar_groups.agent_id = agent_id; // Not in use? + avatar_groups.avatar_id = agent_id; // target_id + + for (LLSD::array_const_iterator it = groups_array.beginArray(); it != groups_array.endArray(); ++it) + { + const LLSD& group_info = *it; + LLAvatarGroups::LLGroupData group_data; + group_data.group_powers = 0; // Not in use? + group_data.group_title = group_info["name"].asString(); // Missing data, not in use? + group_data.group_id = group_info["id"].asUUID(); + group_data.group_name = group_info["name"].asString(); + group_data.group_insignia_id = group_info["image_id"].asUUID(); + + avatar_groups.group_list.push_back(group_data); + } + + if (panel_sl) + { + panel_sl->processGroupProperties(&avatar_groups); + } + + // Notes + LLAvatarNotes avatar_notes; + + avatar_notes.agent_id = agent_id; + avatar_notes.target_id = agent_id; + avatar_notes.notes = result["notes"].asString(); + + panel = floater_profile->findChild(PANEL_NOTES, TRUE); + LLPanelProfileNotes *panel_notes = dynamic_cast(panel); + if (panel_notes) + { + panel_notes->processProperties(&avatar_notes); + } +} + +//TODO: changes take two minutes to propagate! +// Add some storage that holds updated data for two minutes +// for new instances to reuse the data +// Profile data is only relevant to won avatar, but notes +// are for everybody +void put_avatar_properties_coro(std::string cap_url, LLUUID agent_id, LLSD data) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("request_avatar_properties_coro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpHeaders::ptr_t httpHeaders; + + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + httpOpts->setFollowRedirects(true); + + std::string finalUrl = cap_url + "/" + agent_id.asString(); + + LLSD result = httpAdapter->putAndSuspend(httpRequest, finalUrl, data, httpOpts, httpHeaders); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + LL_WARNS("AvatarProperties") << "Failed to put agent information for id " << agent_id << LL_ENDL; + return; + } +} ////////////////////////////////////////////////////////////////////////// // LLProfileHandler @@ -419,11 +619,34 @@ void LLPanelProfileSecondLife::apply(LLAvatarData* data) { if (getIsLoaded() && getSelfProfile()) { - data->image_id = mSecondLifePic->getImageAssetID(); - data->about_text = mDescriptionEdit->getValue().asString(); - data->allow_publish = mShowInSearchCheckbox->getValue(); - - LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesUpdate(data); + // Might be a better idea to accumulate changes in floater + // instead of sending a request per tab + std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP); + if (!cap_url.empty()) + { + LLSD params = LLSDMap(); + if (data->image_id != mSecondLifePic->getImageAssetID()) + { + params["sl_image_id"] = mSecondLifePic->getImageAssetID(); + } + if (data->about_text != mDescriptionEdit->getValue().asString()) + { + params["sl_about_text"] = mDescriptionEdit->getValue().asString(); + } + if ((bool)data->allow_publish != mShowInSearchCheckbox->getValue().asBoolean()) + { + params["allow_publish"] = mShowInSearchCheckbox->getValue().asBoolean(); + } + if (!params.emptyMap()) + { + LLCoros::instance().launch("putAgentUserInfoCoro", + boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), params)); + } + } + else + { + LL_WARNS() << "Failed to update profile data, no cap found" << LL_ENDL; + } } } @@ -433,7 +656,17 @@ void LLPanelProfileSecondLife::updateData() if (!getIsLoading() && avatar_id.notNull() && !(getSelfProfile() && !getEmbedded())) { setIsLoading(); - LLAvatarPropertiesProcessor::getInstance()->sendAvatarGroupsRequest(avatar_id); + + std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP); + if (!cap_url.empty()) + { + LLCoros::instance().launch("requestAgentUserInfoCoro", + boost::bind(request_avatar_properties_coro, cap_url, avatar_id)); + } + else + { + LL_WARNS() << "Failed to update profile data, no cap found" << LL_ENDL; + } } } @@ -446,15 +679,6 @@ void LLPanelProfileSecondLife::processProperties(void* data, EAvatarProcessorTyp if(avatar_data && getAvatarId() == avatar_data->avatar_id) { processProfileProperties(avatar_data); - updateButtons(); - } - } - else if (APT_GROUPS == type) - { - LLAvatarGroups* avatar_groups = static_cast(data); - if(avatar_groups && getAvatarId() == avatar_groups->avatar_id) - { - processGroupProperties(avatar_groups); } } } @@ -498,6 +722,8 @@ void LLPanelProfileSecondLife::processProfileProperties(const LLAvatarData* avat fillPartnerData(avatar_data); fillAccountStatus(avatar_data); + + updateButtons(); } void LLPanelProfileSecondLife::processGroupProperties(const LLAvatarGroups* avatar_groups) @@ -1360,6 +1586,14 @@ void LLPanelProfileFirstLife::processProperties(void* data, EAvatarProcessorType } } +void LLPanelProfileFirstLife::processProperties(const LLAvatarData* avatar_data) +{ + mCurrentDescription = avatar_data->fl_about_text; + mDescriptionEdit->setValue(mCurrentDescription); + mPicture->setValue(avatar_data->fl_image_id); + updateButtons(); +} + void LLPanelProfileFirstLife::resetData() { mDescriptionEdit->setValue(LLStringUtil::null); @@ -1368,6 +1602,26 @@ void LLPanelProfileFirstLife::resetData() void LLPanelProfileFirstLife::apply(LLAvatarData* data) { + + std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP); + if (getIsLoaded() && !cap_url.empty()) + { + LLSD params = LLSDMap(); + if (data->fl_image_id != mPicture->getImageAssetID()) + { + params["fl_image_id"] = mPicture->getImageAssetID(); + } + if (data->fl_about_text != mDescriptionEdit->getValue().asString()) + { + params["fl_about_text"] = mDescriptionEdit->getValue().asString(); + } + if (!params.emptyMap()) + { + LLCoros::instance().launch("putAgentUserInfoCoro", + boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), params)); + } + } + data->fl_image_id = mPicture->getImageAssetID(); data->fl_about_text = mDescriptionEdit->getValue().asString(); } @@ -1413,7 +1667,13 @@ void LLPanelProfileNotes::updateData() if (!getIsLoading() && avatar_id.notNull()) { setIsLoading(); - LLAvatarPropertiesProcessor::getInstance()->sendAvatarNotesRequest(avatar_id); + + std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP); + if (!cap_url.empty()) + { + LLCoros::instance().launch("requestAgentUserInfoCoro", + boost::bind(request_avatar_properties_coro, cap_url, avatar_id)); + } } } @@ -1423,7 +1683,6 @@ BOOL LLPanelProfileNotes::postBuild() mMapRights = getChild("map_check"); mEditObjectRights = getChild("objects_check"); mNotesEditor = getChild("notes_edit"); - mCharacterLimitWarning = getChild("character_limit_warning"); mEditObjectRights->setCommitCallback(boost::bind(&LLPanelProfileNotes::onCommitRights, this)); @@ -1471,10 +1730,19 @@ void LLPanelProfileNotes::fillRightsData() void LLPanelProfileNotes::onCommitNotes() { + std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP); if (getIsLoaded()) { - std::string notes = mNotesEditor->getValue().asString(); - LLAvatarPropertiesProcessor::getInstance()->sendNotes(getAvatarId(),notes); + if (!cap_url.empty()) + { + std::string notes = mNotesEditor->getValue().asString(); + LLCoros::instance().launch("putAgentUserInfoCoro", + boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), LLSD().with("notes", notes))); + } + else + { + LL_WARNS() << "Failed to update notes, no cap found" << LL_ENDL; + } } } @@ -1548,27 +1816,6 @@ void LLPanelProfileNotes::applyRights() LLAvatarPropertiesProcessor::getInstance()->sendFriendRights(getAvatarId(), rights); } -void LLPanelProfileNotes::updateWarning() -{ - mCharacterLimitWarning->setText(std::string()); - - std::string str = getString("header_symbol_limit"); - mCharacterLimitWarning->appendText(str, false, LLStyle::Params().color(LLColor4::yellow)); - mCharacterLimitWarning->appendText(" ", false, LLStyle::Params()); - - LLStringUtil::format_map_t args; - if (!mURLWebProfile.empty()) - { - args["[PROFILE_URL]"] = mURLWebProfile; - } - else - { - args["[PROFILE_URL]"] = getProfileURL(getAvatarId().asString()); - } - str = getString("body_symbol_limit", args); - mCharacterLimitWarning->appendText(str, false, LLStyle::Params()); -} - void LLPanelProfileNotes::processProperties(void* data, EAvatarProcessorType type) { if (APT_NOTES == type) @@ -1576,28 +1823,22 @@ void LLPanelProfileNotes::processProperties(void* data, EAvatarProcessorType typ LLAvatarNotes* avatar_notes = static_cast(data); if (avatar_notes && getAvatarId() == avatar_notes->target_id) { - mNotesEditor->setValue(avatar_notes->notes); - // Don't enable editor in embedded mode since there is no way to save them - //mNotesEditor->setEnabled(TRUE); - mNotesEditor->setEnabled(!getEmbedded()); - // - updateButtons(); - - if (avatar_notes->notes.size() > 1000) - { - mCharacterLimitWarning->setVisible(TRUE); - updateWarning(); - } - else - { - mCharacterLimitWarning->setVisible(FALSE); - } - + processProperties(avatar_notes); LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(),this); } } } +void LLPanelProfileNotes::processProperties(LLAvatarNotes* avatar_notes) +{ + mNotesEditor->setValue(avatar_notes->notes); + // Don't enable editor in embedded mode since there is no way to save them + //mNotesEditor->setEnabled(TRUE); + mNotesEditor->setEnabled(!getEmbedded()); + // + updateButtons(); +} + void LLPanelProfileNotes::resetData() { resetLoading(); @@ -1605,7 +1846,6 @@ void LLPanelProfileNotes::resetData() mOnlineStatus->setValue(FALSE); mMapRights->setValue(FALSE); mEditObjectRights->setValue(FALSE); - mCharacterLimitWarning->setVisible(FALSE); mURLWebProfile.clear(); } @@ -1652,11 +1892,6 @@ void LLPanelProfileNotes::onAvatarNameCache(const LLUUID& agent_id, const LLAvat } mURLWebProfile = getProfileURL(username, false); - - if (mCharacterLimitWarning->getVisible()) - { - updateWarning(); - } } @@ -1753,10 +1988,18 @@ void LLPanelProfile::onOpen(const LLSD& key) void LLPanelProfile::updateData() { LLUUID avatar_id = getAvatarId(); + // Todo: getIsloading functionality needs to be expanded to + // include 'inited' or 'data_provided' state to not rerequest if (!getIsLoading() && avatar_id.notNull()) { setIsLoading(); - LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesRequest(avatar_id); + + std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP); + if (!cap_url.empty()) + { + LLCoros::instance().launch("requestAgentUserInfoCoro", + boost::bind(request_avatar_properties_coro, cap_url, avatar_id)); + } } } @@ -1766,11 +2009,9 @@ void LLPanelProfile::apply() { //KC - AvatarData is spread over 3 different panels // collect data from the last 2 and give to the first to save - LLAvatarData data = LLAvatarData(); - data.avatar_id = gAgentID; - mPanelFirstlife->apply(&data); - mPanelWeb->apply(&data); - mPanelSecondlife->apply(&data); + mPanelFirstlife->apply(&mAvatarData); + mPanelWeb->apply(&mAvatarData); + mPanelSecondlife->apply(&mAvatarData); mPanelInterests->apply(); mPanelPicks->apply(); diff --git a/indra/newview/llpanelprofile.h b/indra/newview/llpanelprofile.h index 2146eee9c1..20eeb0d6a2 100644 --- a/indra/newview/llpanelprofile.h +++ b/indra/newview/llpanelprofile.h @@ -104,6 +104,8 @@ public: void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); + friend void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id); + protected: /** * Process profile related data received from server. @@ -251,6 +253,8 @@ public: void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); + friend void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id); + protected: /*virtual*/ void updateButtons(); void onCommitLoad(LLUICtrl* ctrl); @@ -316,6 +320,7 @@ public: /*virtual*/ BOOL postBuild(); /*virtual*/ void processProperties(void* data, EAvatarProcessorType type); + void processProperties(const LLAvatarData* avatar_data); void resetData(); @@ -324,6 +329,8 @@ public: */ void apply(LLAvatarData* data); + friend void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id); + protected: /*virtual*/ void updateButtons(); void onDescriptionFocusReceived(); @@ -358,6 +365,7 @@ public: /*virtual*/ BOOL postBuild(); /*virtual*/ void processProperties(void* data, EAvatarProcessorType type); + void processProperties(LLAvatarNotes* avatar_notes); void resetData(); @@ -383,13 +391,11 @@ protected: void enableCheckboxes(bool enable); void applyRights(); - void updateWarning(); LLCheckBoxCtrl* mOnlineStatus; LLCheckBoxCtrl* mMapRights; LLCheckBoxCtrl* mEditObjectRights; LLTextEditor* mNotesEditor; - LLTextBox* mCharacterLimitWarning; std::string mURLWebProfile; @@ -428,6 +434,10 @@ public: void showClassified(const LLUUID& classified_id = LLUUID::null, bool edit = false); + LLAvatarData getAvatarData() { return mAvatarData; }; + + friend void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id); + private: void onTabChange(); @@ -439,6 +449,13 @@ private: LLPanelProfileFirstLife* mPanelFirstlife; LLPanelProfileNotes* mPanelNotes; LLTabContainer* mTabContainer; + + // Todo: due to server taking minutes to update this needs a more long term storage + // to reuse recently saved values if user opens floater again + // Storage implementation depends onto how a cap will be implemented, if cap will be + // enought to fully update LLAvatarPropertiesProcessor, then this storage can be + // implemented there. + LLAvatarData mAvatarData; }; #endif //LL_LLPANELPROFILE_H diff --git a/indra/newview/llpanelprofilepicks.cpp b/indra/newview/llpanelprofilepicks.cpp index 06381d264a..8f76fef2b6 100644 --- a/indra/newview/llpanelprofilepicks.cpp +++ b/indra/newview/llpanelprofilepicks.cpp @@ -258,68 +258,73 @@ void LLPanelProfilePicks::processProperties(void* data, EAvatarProcessorType typ LLAvatarPicks* avatar_picks = static_cast(data); if (avatar_picks && getAvatarId() == avatar_picks->target_id) { - LLUUID selected_id = mPickToSelectOnLoad; - if (mPickToSelectOnLoad.isNull()) - { - if (mTabContainer->getTabCount() > 0) - { - LLPanelProfilePick* active_pick_panel = dynamic_cast(mTabContainer->getCurrentPanel()); - if (active_pick_panel) - { - selected_id = active_pick_panel->getPickId(); - } - } - } - - mTabContainer->deleteAllTabs(); - - LLAvatarPicks::picks_list_t::const_iterator it = avatar_picks->picks_list.begin(); - for (; avatar_picks->picks_list.end() != it; ++it) - { - LLUUID pick_id = it->first; - std::string pick_name = it->second; - - LLPanelProfilePick* pick_panel = LLPanelProfilePick::create(); - - pick_panel->setPickId(pick_id); - pick_panel->setPickName(pick_name); - pick_panel->setAvatarId(getAvatarId()); - - mTabContainer->addTabPanel( - LLTabContainer::TabPanelParams(). - panel(pick_panel). - select_tab(selected_id == pick_id). - label(pick_name)); - - if (selected_id == pick_id) - { - mPickToSelectOnLoad = LLUUID::null; - } - } - - BOOL no_data = !mTabContainer->getTabCount(); - mNoItemsLabel->setVisible(no_data); - if (no_data) - { - if(getSelfProfile()) - { - mNoItemsLabel->setValue(LLTrans::getString("NoPicksText")); - } - else - { - mNoItemsLabel->setValue(LLTrans::getString("NoAvatarPicksText")); - } - } - else if (selected_id.isNull()) - { - mTabContainer->selectFirstTab(); - } - - updateButtons(); + processProperties(avatar_picks); } } } +void LLPanelProfilePicks::processProperties(const LLAvatarPicks* avatar_picks) +{ + LLUUID selected_id = mPickToSelectOnLoad; + if (mPickToSelectOnLoad.isNull()) + { + if (mTabContainer->getTabCount() > 0) + { + LLPanelProfilePick* active_pick_panel = dynamic_cast(mTabContainer->getCurrentPanel()); + if (active_pick_panel) + { + selected_id = active_pick_panel->getPickId(); + } + } + } + + mTabContainer->deleteAllTabs(); + + LLAvatarPicks::picks_list_t::const_iterator it = avatar_picks->picks_list.begin(); + for (; avatar_picks->picks_list.end() != it; ++it) + { + LLUUID pick_id = it->first; + std::string pick_name = it->second; + + LLPanelProfilePick* pick_panel = LLPanelProfilePick::create(); + + pick_panel->setPickId(pick_id); + pick_panel->setPickName(pick_name); + pick_panel->setAvatarId(getAvatarId()); + + mTabContainer->addTabPanel( + LLTabContainer::TabPanelParams(). + panel(pick_panel). + select_tab(selected_id == pick_id). + label(pick_name)); + + if (selected_id == pick_id) + { + mPickToSelectOnLoad = LLUUID::null; + } + } + + BOOL no_data = !mTabContainer->getTabCount(); + mNoItemsLabel->setVisible(no_data); + if (no_data) + { + if (getSelfProfile()) + { + mNoItemsLabel->setValue(LLTrans::getString("NoPicksText")); + } + else + { + mNoItemsLabel->setValue(LLTrans::getString("NoAvatarPicksText")); + } + } + else if (selected_id.isNull()) + { + mTabContainer->selectFirstTab(); + } + + updateButtons(); +} + void LLPanelProfilePicks::resetData() { resetLoading(); diff --git a/indra/newview/llpanelprofilepicks.h b/indra/newview/llpanelprofilepicks.h index ca00a263c1..d0232a18ce 100644 --- a/indra/newview/llpanelprofilepicks.h +++ b/indra/newview/llpanelprofilepicks.h @@ -59,6 +59,7 @@ public: void selectPick(const LLUUID& pick_id); /*virtual*/ void processProperties(void* data, EAvatarProcessorType type); + void processProperties(const LLAvatarPicks* avatar_picks); /*virtual*/ void resetData(); @@ -74,6 +75,8 @@ public: */ /*virtual*/ void updateData(); + friend void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id); + private: void onClickNewBtn(); void onClickDelete(); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 73996178ba..50eac89ccf 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -7776,7 +7776,7 @@ class LLAvatarTogglePicks : public view_listener_t { bool handleEvent(const LLSD& userdata) { - LLFloater* instance = LLAvatarActions::getProfileFloater(gAgent.getID()); + LLFloater * instance = LLAvatarActions::getProfileFloater(gAgent.getID()); if (LLFloater::isMinimized(instance) || (instance && !instance->hasFocus() && !instance->getIsChrome())) { instance->setMinimized(FALSE); diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index a0ae4a9f9b..d4fd772070 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -3156,6 +3156,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("AcceptFriendship"); capabilityNames.append("AcceptGroupInvite"); // ReadOfflineMsgs recieved messages only!!! capabilityNames.append("AgentPreferences"); + capabilityNames.append("AgentProfile"); capabilityNames.append("AgentState"); capabilityNames.append("AttachmentResources"); capabilityNames.append("AvatarPickerSearch"); diff --git a/indra/newview/skins/default/xui/en/panel_edit_profile.xml b/indra/newview/skins/default/xui/en/panel_edit_profile.xml index a9239cafce..48c57cdd18 100644 --- a/indra/newview/skins/default/xui/en/panel_edit_profile.xml +++ b/indra/newview/skins/default/xui/en/panel_edit_profile.xml @@ -252,7 +252,7 @@ layout="topleft" left="4" top_pad="-14" - max_length="511" + max_length="65000" name="sl_description_edit" right="-4" word_wrap="true" /> @@ -432,7 +432,7 @@ layout="topleft" left="4" top_pad="-14" - max_length="511" + max_length="65000" name="fl_description_edit" right="-4" word_wrap="true" /> diff --git a/indra/newview/skins/default/xui/en/panel_profile_firstlife.xml b/indra/newview/skins/default/xui/en/panel_profile_firstlife.xml index 7bda6d1718..108eb9443d 100644 --- a/indra/newview/skins/default/xui/en/panel_profile_firstlife.xml +++ b/indra/newview/skins/default/xui/en/panel_profile_firstlife.xml @@ -44,7 +44,7 @@ layout="topleft" bg_readonly_color="Transparent" border_visible="true" - max_length="253" + max_length="65000" parse_urls="true" word_wrap="true" /> diff --git a/indra/newview/skins/default/xui/en/panel_profile_notes.xml b/indra/newview/skins/default/xui/en/panel_profile_notes.xml index 179fa0136c..c7dfaa1e67 100644 --- a/indra/newview/skins/default/xui/en/panel_profile_notes.xml +++ b/indra/newview/skins/default/xui/en/panel_profile_notes.xml @@ -9,14 +9,6 @@ follows="all" layout="topleft" > - - -Warning: Your notes contain more than 1000 characters. - - -Only first 1000 characters are shown here. If you edit your notes and click OK, the extra characters will be lost. To preserve the extra characters, you must edit it [[PROFILE_URL] on the web] - - - - Placeholder: Your notes contain more than 1000 characters. Only first 1000 characters are shown here. If you edit your notes and click OK, the extra characters will be lost. To preserve the extra characters, you must edit it [https://my.secondlife.com/settings/profile on the web] -