summaryrefslogtreecommitdiff
path: root/indra/newview/llpanelprofile.cpp
diff options
context:
space:
mode:
authorAnsariel <ansariel.hiller@phoenixviewer.com>2024-05-22 19:04:52 +0200
committerAnsariel <ansariel.hiller@phoenixviewer.com>2024-05-22 19:04:52 +0200
commit1b67dd855c41f5a0cda7ec2a68d98071986ca703 (patch)
treeab243607f74f78200787bba5b9b88f07ef1b966f /indra/newview/llpanelprofile.cpp
parent6d6eabca44d08d5b97bfe3e941d2b9687c2246ea (diff)
parente1623bb276f83a43ce7a197e388720c05bdefe61 (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/llpanelprofile.cpp')
-rw-r--r--indra/newview/llpanelprofile.cpp5214
1 files changed, 2431 insertions, 2783 deletions
diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp
index 0fc2ad4d6d..adceee80d3 100644
--- a/indra/newview/llpanelprofile.cpp
+++ b/indra/newview/llpanelprofile.cpp
@@ -1,2783 +1,2431 @@
-/**
-* @file llpanelprofile.cpp
-* @brief Profile panel implementation
-*
-* $LicenseInfo:firstyear=2022&license=viewerlgpl$
-* Second Life Viewer Source Code
-* Copyright (C) 2022, 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 "llpanelprofile.h"
-
-// Common
-#include "llavatarnamecache.h"
-#include "llsdutil.h"
-#include "llslurl.h"
-#include "lldateutil.h" //ageFromDate
-
-// UI
-#include "llavatariconctrl.h"
-#include "llclipboard.h"
-#include "llcheckboxctrl.h"
-#include "llcombobox.h"
-#include "lllineeditor.h"
-#include "llloadingindicator.h"
-#include "llmenubutton.h"
-#include "lltabcontainer.h"
-#include "lltextbox.h"
-#include "lltexteditor.h"
-#include "lltexturectrl.h"
-#include "lltoggleablemenu.h"
-#include "lltooldraganddrop.h"
-#include "llgrouplist.h"
-#include "llurlaction.h"
-
-// Image
-#include "llimagej2c.h"
-
-// Newview
-#include "llagent.h" //gAgent
-#include "llagentpicksinfo.h"
-#include "llavataractions.h"
-#include "llavatarpropertiesprocessor.h"
-#include "llcallingcard.h"
-#include "llcommandhandler.h"
-#include "llfloaterprofiletexture.h"
-#include "llfloaterreg.h"
-#include "llfloaterreporter.h"
-#include "llfilepicker.h"
-#include "llfirstuse.h"
-#include "llgroupactions.h"
-#include "lllogchat.h"
-#include "llmutelist.h"
-#include "llnotificationsutil.h"
-#include "llpanelblockedlist.h"
-#include "llpanelprofileclassifieds.h"
-#include "llpanelprofilepicks.h"
-#include "llthumbnailctrl.h"
-#include "lltrans.h"
-#include "llviewercontrol.h"
-#include "llviewermenu.h" //is_agent_mappable
-#include "llviewermenufile.h"
-#include "llviewertexturelist.h"
-#include "llvoiceclient.h"
-#include "llweb.h"
-
-
-static LLPanelInjector<LLPanelProfileSecondLife> t_panel_profile_secondlife("panel_profile_secondlife");
-static LLPanelInjector<LLPanelProfileWeb> t_panel_web("panel_profile_web");
-static LLPanelInjector<LLPanelProfilePicks> t_panel_picks("panel_profile_picks");
-static LLPanelInjector<LLPanelProfileFirstLife> t_panel_firstlife("panel_profile_firstlife");
-static LLPanelInjector<LLPanelProfileNotes> t_panel_notes("panel_profile_notes");
-static LLPanelInjector<LLPanelProfile> t_panel_profile("panel_profile");
-
-static const std::string PANEL_SECONDLIFE = "panel_profile_secondlife";
-static const std::string PANEL_WEB = "panel_profile_web";
-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";
-static const std::string PROFILE_IMAGE_UPLOAD_CAP = "UploadAgentProfileImage";
-
-
-//////////////////////////////////////////////////////////////////////////
-
-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);
-
- LL_DEBUGS("AvatarProperties") << "Agent id: " << agent_id << " Result: " << httpResults << LL_ENDL;
-
- 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<LLPanel>(PANEL_PROFILE_VIEW, true);
- LLPanelProfile *panel_profile = dynamic_cast<LLPanelProfile*>(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();
- 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->customer_type = result["customer_type"].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;
- }
- if (result["identified"].asBoolean())
- {
- avatar_data->flags |= AVATAR_IDENTIFIED;
- }
- if (result["transacted"].asBoolean())
- {
- avatar_data->flags |= AVATAR_TRANSACTED;
- }
-
- 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<LLPanel>(PANEL_SECONDLIFE, true);
- LLPanelProfileSecondLife *panel_sl = dynamic_cast<LLPanelProfileSecondLife*>(panel);
- if (panel_sl)
- {
- panel_sl->processProfileProperties(avatar_data);
- }
-
- panel = floater_profile->findChild<LLPanel>(PANEL_WEB, true);
- LLPanelProfileWeb *panel_web = dynamic_cast<LLPanelProfileWeb*>(panel);
- if (panel_web)
- {
- panel_web->setLoaded();
- }
-
- panel = floater_profile->findChild<LLPanel>(PANEL_FIRSTLIFE, true);
- LLPanelProfileFirstLife *panel_first = dynamic_cast<LLPanelProfileFirstLife*>(panel);
- if (panel_first)
- {
- panel_first->processProperties(avatar_data);
- }
-
- // 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<LLPanel>(PANEL_PICKS, true);
- LLPanelProfilePicks *panel_picks = dynamic_cast<LLPanelProfilePicks*>(panel);
- if (panel_picks)
- {
- // Refresh pick limit before processing
- LLAgentPicksInfo::getInstance()->onServerRespond(&avatar_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<LLPanel>(PANEL_NOTES, true);
- LLPanelProfileNotes *panel_notes = dynamic_cast<LLPanelProfileNotes*>(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 own avatar, but notes
-// are for everybody (no onger an issue?)
-void put_avatar_properties_coro(std::string cap_url, LLUUID agent_id, LLSD data, std::function<void(bool)> callback)
-{
- LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
- LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
- httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("put_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 " << data << " for id " << agent_id << LL_ENDL;
- }
- else
- {
- LL_DEBUGS("AvatarProperties") << "Agent id: " << agent_id << " Data: " << data << " Result: " << httpResults << LL_ENDL;
- }
-
- if (callback)
- {
- callback(status);
- }
-}
-
-LLUUID post_profile_image(std::string cap_url, const LLSD &first_data, std::string path_to_image, LLHandle<LLPanel> *handle)
-{
- LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
- LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
- httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("post_profile_image_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);
-
- LLSD result = httpAdapter->postAndSuspend(httpRequest, cap_url, first_data, httpOpts, httpHeaders);
-
- LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
- LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
-
- if (!status)
- {
- // todo: notification?
- LL_WARNS("AvatarProperties") << "Failed to get uploader cap " << status.toString() << LL_ENDL;
- return LLUUID::null;
- }
- if (!result.has("uploader"))
- {
- // todo: notification?
- LL_WARNS("AvatarProperties") << "Failed to get uploader cap, response contains no data." << LL_ENDL;
- return LLUUID::null;
- }
- std::string uploader_cap = result["uploader"].asString();
- if (uploader_cap.empty())
- {
- LL_WARNS("AvatarProperties") << "Failed to get uploader cap, cap invalid." << LL_ENDL;
- return LLUUID::null;
- }
-
- // Upload the image
-
- LLCore::HttpRequest::ptr_t uploaderhttpRequest(new LLCore::HttpRequest);
- LLCore::HttpHeaders::ptr_t uploaderhttpHeaders(new LLCore::HttpHeaders);
- LLCore::HttpOptions::ptr_t uploaderhttpOpts(new LLCore::HttpOptions);
- S64 length;
-
- {
- llifstream instream(path_to_image.c_str(), std::iostream::binary | std::iostream::ate);
- if (!instream.is_open())
- {
- LL_WARNS("AvatarProperties") << "Failed to open file " << path_to_image << LL_ENDL;
- return LLUUID::null;
- }
- length = instream.tellg();
- }
-
- uploaderhttpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, "application/jp2"); // optional
- uploaderhttpHeaders->append(HTTP_OUT_HEADER_CONTENT_LENGTH, llformat("%d", length)); // required!
- uploaderhttpOpts->setFollowRedirects(true);
-
- result = httpAdapter->postFileAndSuspend(uploaderhttpRequest, uploader_cap, path_to_image, uploaderhttpOpts, uploaderhttpHeaders);
-
- httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
- status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
-
- LL_DEBUGS("AvatarProperties") << result << LL_ENDL;
-
- if (!status)
- {
- LL_WARNS("AvatarProperties") << "Failed to upload image " << status.toString() << LL_ENDL;
- return LLUUID::null;
- }
-
- if (result["state"].asString() != "complete")
- {
- if (result.has("message"))
- {
- LL_WARNS("AvatarProperties") << "Failed to upload image, state " << result["state"] << " message: " << result["message"] << LL_ENDL;
- }
- else
- {
- LL_WARNS("AvatarProperties") << "Failed to upload image " << result << LL_ENDL;
- }
- return LLUUID::null;
- }
-
- return result["new_asset"].asUUID();
-}
-
-enum EProfileImageType
-{
- PROFILE_IMAGE_SL,
- PROFILE_IMAGE_FL,
-};
-
-void post_profile_image_coro(std::string cap_url, EProfileImageType type, std::string path_to_image, LLHandle<LLPanel> *handle)
-{
- LLSD data;
- switch (type)
- {
- case PROFILE_IMAGE_SL:
- data["profile-image-asset"] = "sl_image_id";
- break;
- case PROFILE_IMAGE_FL:
- data["profile-image-asset"] = "fl_image_id";
- break;
- }
-
- LLUUID result = post_profile_image(cap_url, data, path_to_image, handle);
-
- // reset loading indicator
- if (!handle->isDead())
- {
- switch (type)
- {
- case PROFILE_IMAGE_SL:
- {
- LLPanelProfileSecondLife* panel = static_cast<LLPanelProfileSecondLife*>(handle->get());
- if (result.notNull())
- {
- panel->setProfileImageUploaded(result);
- }
- else
- {
- // failure, just stop progress indicator
- panel->setProfileImageUploading(false);
- }
- break;
- }
- case PROFILE_IMAGE_FL:
- {
- LLPanelProfileFirstLife* panel = static_cast<LLPanelProfileFirstLife*>(handle->get());
- if (result.notNull())
- {
- panel->setProfileImageUploaded(result);
- }
- else
- {
- // failure, just stop progress indicator
- panel->setProfileImageUploading(false);
- }
- break;
- }
- }
- }
-
- if (type == PROFILE_IMAGE_SL && result.notNull())
- {
- LLAvatarIconIDCache::getInstance()->add(gAgentID, result);
- // Should trigger callbacks in icon controls
- LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesRequest(gAgentID);
- }
-
- // Cleanup
- LLFile::remove(path_to_image);
- delete handle;
-}
-
-//////////////////////////////////////////////////////////////////////////
-// LLProfileHandler
-
-class LLProfileHandler : public LLCommandHandler
-{
-public:
- // requires trusted browser to trigger
- LLProfileHandler() : LLCommandHandler("profile", UNTRUSTED_THROTTLE) { }
-
- bool handle(const LLSD& params,
- const LLSD& query_map,
- const std::string& grid,
- LLMediaCtrl* web)
- {
- if (params.size() < 1) return false;
- std::string agent_name = params[0];
- LL_INFOS() << "Profile, agent_name " << agent_name << LL_ENDL;
- std::string url = getProfileURL(agent_name);
- LLWeb::loadURLInternal(url);
-
- return true;
- }
-};
-LLProfileHandler gProfileHandler;
-
-
-//////////////////////////////////////////////////////////////////////////
-// LLAgentHandler
-
-class LLAgentHandler : public LLCommandHandler
-{
-public:
- // requires trusted browser to trigger
- LLAgentHandler() : LLCommandHandler("agent", UNTRUSTED_THROTTLE) { }
-
- virtual bool canHandleUntrusted(
- const LLSD& params,
- const LLSD& query_map,
- LLMediaCtrl* web,
- const std::string& nav_type)
- {
- if (params.size() < 2)
- {
- return true; // don't block, will fail later
- }
-
- if (nav_type == NAV_TYPE_CLICKED
- || nav_type == NAV_TYPE_EXTERNAL)
- {
- return true;
- }
-
- const std::string verb = params[1].asString();
- if (verb == "about" || verb == "inspect" || verb == "reportAbuse")
- {
- return true;
- }
- return false;
- }
-
- bool handle(const LLSD& params,
- const LLSD& query_map,
- const std::string& grid,
- LLMediaCtrl* web)
- {
- if (params.size() < 2) return false;
- LLUUID avatar_id;
- if (!avatar_id.set(params[0], false))
- {
- return false;
- }
-
- const std::string verb = params[1].asString();
- if (verb == "about")
- {
- LLAvatarActions::showProfile(avatar_id);
- return true;
- }
-
- if (verb == "inspect")
- {
- LLFloaterReg::showInstance("inspect_avatar", LLSD().with("avatar_id", avatar_id));
- return true;
- }
-
- if (verb == "im")
- {
- LLAvatarActions::startIM(avatar_id);
- return true;
- }
-
- if (verb == "pay")
- {
- if (!LLUI::getInstance()->mSettingGroups["config"]->getBOOL("EnableAvatarPay"))
- {
- LLNotificationsUtil::add("NoAvatarPay", LLSD(), LLSD(), std::string("SwitchToStandardSkinAndQuit"));
- return true;
- }
-
- LLAvatarActions::pay(avatar_id);
- return true;
- }
-
- if (verb == "offerteleport")
- {
- LLAvatarActions::offerTeleport(avatar_id);
- return true;
- }
-
- if (verb == "requestfriend")
- {
- LLAvatarActions::requestFriendshipDialog(avatar_id);
- return true;
- }
-
- if (verb == "removefriend")
- {
- LLAvatarActions::removeFriendDialog(avatar_id);
- return true;
- }
-
- if (verb == "mute")
- {
- if (! LLAvatarActions::isBlocked(avatar_id))
- {
- LLAvatarActions::toggleBlock(avatar_id);
- }
- return true;
- }
-
- if (verb == "unmute")
- {
- if (LLAvatarActions::isBlocked(avatar_id))
- {
- LLAvatarActions::toggleBlock(avatar_id);
- }
- return true;
- }
-
- if (verb == "block")
- {
- if (params.size() > 2)
- {
- const std::string object_name = LLURI::unescape(params[2].asString());
- LLMute mute(avatar_id, object_name, LLMute::OBJECT);
- LLMuteList::getInstance()->add(mute);
- LLPanelBlockedList::showPanelAndSelect(mute.mID);
- }
- return true;
- }
-
- if (verb == "unblock")
- {
- if (params.size() > 2)
- {
- const std::string object_name = params[2].asString();
- LLMute mute(avatar_id, object_name, LLMute::OBJECT);
- LLMuteList::getInstance()->remove(mute);
- }
- return true;
- }
-
- // reportAbuse is here due to convoluted avatar handling
- // in LLScrollListCtrl and LLTextBase
- if (verb == "reportAbuse" && web == NULL)
- {
- LLAvatarName av_name;
- if (LLAvatarNameCache::get(avatar_id, &av_name))
- {
- LLFloaterReporter::showFromAvatar(avatar_id, av_name.getCompleteName());
- }
- else
- {
- LLFloaterReporter::showFromAvatar(avatar_id, "not avaliable");
- }
- return true;
- }
- return false;
- }
-};
-LLAgentHandler gAgentHandler;
-
-
-///----------------------------------------------------------------------------
-/// LLFloaterProfilePermissions
-///----------------------------------------------------------------------------
-
-class LLFloaterProfilePermissions
- : public LLFloater
- , public LLFriendObserver
-{
-public:
- LLFloaterProfilePermissions(LLView * owner, const LLUUID &avatar_id);
- ~LLFloaterProfilePermissions();
- bool postBuild() override;
- void onOpen(const LLSD& key) override;
- void draw() override;
- void changed(U32 mask) override; // LLFriendObserver
-
- void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name);
- bool hasUnsavedChanges() { return mHasUnsavedPermChanges; }
-
- void onApplyRights();
-
-private:
- void fillRightsData();
- void rightsConfirmationCallback(const LLSD& notification, const LLSD& response);
- void confirmModifyRights(bool grant);
- void onCommitSeeOnlineRights();
- void onCommitEditRights();
- void onCancel();
-
- LLTextBase* mDescription;
- LLCheckBoxCtrl* mOnlineStatus;
- LLCheckBoxCtrl* mMapRights;
- LLCheckBoxCtrl* mEditObjectRights;
- LLButton* mOkBtn;
- LLButton* mCancelBtn;
-
- LLUUID mAvatarID;
- F32 mContextConeOpacity;
- bool mHasUnsavedPermChanges;
- LLHandle<LLView> mOwnerHandle;
-
- boost::signals2::connection mAvatarNameCacheConnection;
-};
-
-LLFloaterProfilePermissions::LLFloaterProfilePermissions(LLView * owner, const LLUUID &avatar_id)
- : LLFloater(LLSD())
- , mAvatarID(avatar_id)
- , mContextConeOpacity(0.0f)
- , mHasUnsavedPermChanges(false)
- , mOwnerHandle(owner->getHandle())
-{
- buildFromFile("floater_profile_permissions.xml");
-}
-
-LLFloaterProfilePermissions::~LLFloaterProfilePermissions()
-{
- mAvatarNameCacheConnection.disconnect();
- if (mAvatarID.notNull())
- {
- LLAvatarTracker::instance().removeParticularFriendObserver(mAvatarID, this);
- }
-}
-
-bool LLFloaterProfilePermissions::postBuild()
-{
- mDescription = getChild<LLTextBase>("perm_description");
- mOnlineStatus = getChild<LLCheckBoxCtrl>("online_check");
- mMapRights = getChild<LLCheckBoxCtrl>("map_check");
- mEditObjectRights = getChild<LLCheckBoxCtrl>("objects_check");
- mOkBtn = getChild<LLButton>("perms_btn_ok");
- mCancelBtn = getChild<LLButton>("perms_btn_cancel");
-
- mOnlineStatus->setCommitCallback([this](LLUICtrl*, void*) { onCommitSeeOnlineRights(); }, nullptr);
- mMapRights->setCommitCallback([this](LLUICtrl*, void*) { mHasUnsavedPermChanges = true; }, nullptr);
- mEditObjectRights->setCommitCallback([this](LLUICtrl*, void*) { onCommitEditRights(); }, nullptr);
- mOkBtn->setCommitCallback([this](LLUICtrl*, void*) { onApplyRights(); }, nullptr);
- mCancelBtn->setCommitCallback([this](LLUICtrl*, void*) { onCancel(); }, nullptr);
-
- return true;
-}
-
-void LLFloaterProfilePermissions::onOpen(const LLSD& key)
-{
- if (LLAvatarActions::isFriend(mAvatarID))
- {
- LLAvatarTracker::instance().addParticularFriendObserver(mAvatarID, this);
- fillRightsData();
- }
-
- mCancelBtn->setFocus(true);
-
- mAvatarNameCacheConnection = LLAvatarNameCache::get(mAvatarID, boost::bind(&LLFloaterProfilePermissions::onAvatarNameCache, this, _1, _2));
-}
-
-void LLFloaterProfilePermissions::draw()
-{
- // drawFrustum
- LLView *owner = mOwnerHandle.get();
- static LLCachedControl<F32> max_opacity(gSavedSettings, "PickerContextOpacity", 0.4f);
- drawConeToOwner(mContextConeOpacity, max_opacity, owner);
- LLFloater::draw();
-}
-
-void LLFloaterProfilePermissions::changed(U32 mask)
-{
- if (mask != LLFriendObserver::ONLINE)
- {
- fillRightsData();
- }
-}
-
-void LLFloaterProfilePermissions::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name)
-{
- mAvatarNameCacheConnection.disconnect();
-
- LLStringUtil::format_map_t args;
- args["[AGENT_NAME]"] = av_name.getDisplayName();
- std::string descritpion = getString("description_string", args);
- mDescription->setValue(descritpion);
-}
-
-void LLFloaterProfilePermissions::fillRightsData()
-{
- const LLRelationship* relation = LLAvatarTracker::instance().getBuddyInfo(mAvatarID);
- // If true - we are viewing friend's profile, enable check boxes and set values.
- if (relation)
- {
- S32 rights = relation->getRightsGrantedTo();
-
- bool see_online = LLRelationship::GRANT_ONLINE_STATUS & rights;
- mOnlineStatus->setValue(see_online);
- mMapRights->setEnabled(see_online);
- mMapRights->setValue(LLRelationship::GRANT_MAP_LOCATION & rights);
- mEditObjectRights->setValue(LLRelationship::GRANT_MODIFY_OBJECTS & rights);
- }
- else
- {
- closeFloater();
- LL_INFOS("ProfilePermissions") << "Floater closing since agent is no longer a friend" << LL_ENDL;
- }
-}
-
-void LLFloaterProfilePermissions::rightsConfirmationCallback(const LLSD& notification,
- const LLSD& response)
-{
- S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
- if (option != 0) // canceled
- {
- mEditObjectRights->setValue(!mEditObjectRights->getValue().asBoolean());
- }
- else
- {
- mHasUnsavedPermChanges = true;
- }
-}
-
-void LLFloaterProfilePermissions::confirmModifyRights(bool grant)
-{
- LLSD args;
- args["NAME"] = LLSLURL("agent", mAvatarID, "completename").getSLURLString();
- LLNotificationsUtil::add(grant ? "GrantModifyRights" : "RevokeModifyRights", args, LLSD(),
- boost::bind(&LLFloaterProfilePermissions::rightsConfirmationCallback, this, _1, _2));
-}
-
-void LLFloaterProfilePermissions::onCommitSeeOnlineRights()
-{
- bool see_online = mOnlineStatus->getValue().asBoolean();
- mMapRights->setEnabled(see_online);
- if (see_online)
- {
- const LLRelationship* relation = LLAvatarTracker::instance().getBuddyInfo(mAvatarID);
- if (relation)
- {
- S32 rights = relation->getRightsGrantedTo();
- mMapRights->setValue(LLRelationship::GRANT_MAP_LOCATION & rights);
- }
- else
- {
- closeFloater();
- LL_INFOS("ProfilePermissions") << "Floater closing since agent is no longer a friend" << LL_ENDL;
- }
- }
- else
- {
- mMapRights->setValue(false);
- }
- mHasUnsavedPermChanges = true;
-}
-
-void LLFloaterProfilePermissions::onCommitEditRights()
-{
- const LLRelationship* buddy_relationship = LLAvatarTracker::instance().getBuddyInfo(mAvatarID);
-
- if (!buddy_relationship)
- {
- LL_WARNS("ProfilePermissions") << "Trying to modify rights for non-friend avatar. Closing floater." << LL_ENDL;
- closeFloater();
- return;
- }
-
- bool allow_modify_objects = mEditObjectRights->getValue().asBoolean();
-
- // if modify objects checkbox clicked
- if (buddy_relationship->isRightGrantedTo(
- LLRelationship::GRANT_MODIFY_OBJECTS) != allow_modify_objects)
- {
- confirmModifyRights(allow_modify_objects);
- }
-}
-
-void LLFloaterProfilePermissions::onApplyRights()
-{
- const LLRelationship* buddy_relationship = LLAvatarTracker::instance().getBuddyInfo(mAvatarID);
-
- if (!buddy_relationship)
- {
- LL_WARNS("ProfilePermissions") << "Trying to modify rights for non-friend avatar. Skipped." << LL_ENDL;
- return;
- }
-
- S32 rights = 0;
-
- if (mOnlineStatus->getValue().asBoolean())
- {
- rights |= LLRelationship::GRANT_ONLINE_STATUS;
- }
- if (mMapRights->getValue().asBoolean())
- {
- rights |= LLRelationship::GRANT_MAP_LOCATION;
- }
- if (mEditObjectRights->getValue().asBoolean())
- {
- rights |= LLRelationship::GRANT_MODIFY_OBJECTS;
- }
-
- LLAvatarPropertiesProcessor::getInstance()->sendFriendRights(mAvatarID, rights);
-
- closeFloater();
-}
-
-void LLFloaterProfilePermissions::onCancel()
-{
- closeFloater();
-}
-
-//////////////////////////////////////////////////////////////////////////
-// LLPanelProfileSecondLife
-
-LLPanelProfileSecondLife::LLPanelProfileSecondLife()
- : LLPanelProfileTab()
- , mAvatarNameCacheConnection()
- , mHasUnsavedDescriptionChanges(false)
- , mWaitingForImageUpload(false)
- , mAllowPublish(false)
-{
-}
-
-LLPanelProfileSecondLife::~LLPanelProfileSecondLife()
-{
- if (getAvatarId().notNull())
- {
- LLAvatarTracker::instance().removeParticularFriendObserver(getAvatarId(), this);
- }
-
- if (LLVoiceClient::instanceExists())
- {
- LLVoiceClient::getInstance()->removeObserver((LLVoiceClientStatusObserver*)this);
- }
-
- if (mAvatarNameCacheConnection.connected())
- {
- mAvatarNameCacheConnection.disconnect();
- }
-}
-
-bool LLPanelProfileSecondLife::postBuild()
-{
- mGroupList = getChild<LLGroupList>("group_list");
- mShowInSearchCombo = getChild<LLComboBox>("show_in_search");
- mSecondLifePic = getChild<LLThumbnailCtrl>("2nd_life_pic");
- mSecondLifePicLayout = getChild<LLPanel>("image_panel");
- mDescriptionEdit = getChild<LLTextEditor>("sl_description_edit");
- mAgentActionMenuButton = getChild<LLMenuButton>("agent_actions_menu");
- mSaveDescriptionChanges = getChild<LLButton>("save_description_changes");
- mDiscardDescriptionChanges = getChild<LLButton>("discard_description_changes");
- mCanSeeOnlineIcon = getChild<LLIconCtrl>("can_see_online");
- mCantSeeOnlineIcon = getChild<LLIconCtrl>("cant_see_online");
- mCanSeeOnMapIcon = getChild<LLIconCtrl>("can_see_on_map");
- mCantSeeOnMapIcon = getChild<LLIconCtrl>("cant_see_on_map");
- mCanEditObjectsIcon = getChild<LLIconCtrl>("can_edit_objects");
- mCantEditObjectsIcon = getChild<LLIconCtrl>("cant_edit_objects");
-
- mShowInSearchCombo->setCommitCallback([this](LLUICtrl*, void*) { onShowInSearchCallback(); }, nullptr);
- mGroupList->setDoubleClickCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { LLPanelProfileSecondLife::openGroupProfile(); });
- mGroupList->setReturnCallback([this](LLUICtrl*, const LLSD&) { LLPanelProfileSecondLife::openGroupProfile(); });
- mSaveDescriptionChanges->setCommitCallback([this](LLUICtrl*, void*) { onSaveDescriptionChanges(); }, nullptr);
- mDiscardDescriptionChanges->setCommitCallback([this](LLUICtrl*, void*) { onDiscardDescriptionChanges(); }, nullptr);
- mDescriptionEdit->setKeystrokeCallback([this](LLTextEditor* caller) { onSetDescriptionDirty(); });
-
- mCanSeeOnlineIcon->setMouseUpCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { onShowAgentPermissionsDialog(); });
- mCantSeeOnlineIcon->setMouseUpCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { onShowAgentPermissionsDialog(); });
- mCanSeeOnMapIcon->setMouseUpCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { onShowAgentPermissionsDialog(); });
- mCantSeeOnMapIcon->setMouseUpCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { onShowAgentPermissionsDialog(); });
- mCanEditObjectsIcon->setMouseUpCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { onShowAgentPermissionsDialog(); });
- mCantEditObjectsIcon->setMouseUpCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { onShowAgentPermissionsDialog(); });
- mSecondLifePic->setMouseUpCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { onShowAgentProfileTexture(); });
-
- return true;
-}
-
-void LLPanelProfileSecondLife::onOpen(const LLSD& key)
-{
- LLPanelProfileTab::onOpen(key);
-
- resetData();
-
- LLUUID avatar_id = getAvatarId();
-
- bool own_profile = getSelfProfile();
-
- mGroupList->setShowNone(!own_profile);
-
- childSetVisible("notes_panel", !own_profile);
- childSetVisible("settings_panel", own_profile);
- childSetVisible("about_buttons_panel", own_profile);
-
- if (own_profile)
- {
- // Group list control cannot toggle ForAgent loading
- // Less than ideal, but viewing own profile via search is edge case
- mGroupList->enableForAgent(false);
- }
-
- // Init menu, menu needs to be created in scope of a registar to work correctly.
- LLUICtrl::CommitCallbackRegistry::ScopedRegistrar commit;
- commit.add("Profile.Commit", [this](LLUICtrl*, const LLSD& userdata) { onCommitMenu(userdata); });
-
- LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable;
- enable.add("Profile.EnableItem", [this](LLUICtrl*, const LLSD& userdata) { return onEnableMenu(userdata); });
- enable.add("Profile.CheckItem", [this](LLUICtrl*, const LLSD& userdata) { return onCheckMenu(userdata); });
-
- if (own_profile)
- {
- mAgentActionMenuButton->setMenu("menu_profile_self.xml", LLMenuButton::MP_BOTTOM_RIGHT);
- }
- else
- {
- // Todo: use PeopleContextMenu instead?
- mAgentActionMenuButton->setMenu("menu_profile_other.xml", LLMenuButton::MP_BOTTOM_RIGHT);
- }
-
- mDescriptionEdit->setParseHTML(!own_profile);
-
- if (!own_profile)
- {
- mVoiceStatus = LLAvatarActions::canCall() && (LLAvatarActions::isFriend(avatar_id) ? LLAvatarTracker::instance().isBuddyOnline(avatar_id) : true);
- updateOnlineStatus();
- fillRightsData();
- }
-
- mAvatarNameCacheConnection = LLAvatarNameCache::get(getAvatarId(), boost::bind(&LLPanelProfileSecondLife::onAvatarNameCache, this, _1, _2));
-}
-
-
-bool LLPanelProfileSecondLife::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
- EDragAndDropType cargo_type,
- void* cargo_data,
- EAcceptance* accept,
- std::string& tooltip_msg)
-{
- // Try children first
- if (LLPanelProfileTab::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg)
- && *accept != ACCEPT_NO)
- {
- return true;
- }
-
- // No point sharing with own profile
- if (getSelfProfile())
- {
- return false;
- }
-
- // Exclude fields that look like they are editable.
- S32 child_x = 0;
- S32 child_y = 0;
- if (localPointToOtherView(x, y, &child_x, &child_y, mDescriptionEdit)
- && mDescriptionEdit->pointInView(child_x, child_y))
- {
- return false;
- }
-
- if (localPointToOtherView(x, y, &child_x, &child_y, mGroupList)
- && mGroupList->pointInView(child_x, child_y))
- {
- return false;
- }
-
- // Share
- LLToolDragAndDrop::handleGiveDragAndDrop(getAvatarId(),
- LLUUID::null,
- drop,
- cargo_type,
- cargo_data,
- accept);
- return true;
-}
-
-void LLPanelProfileSecondLife::updateData()
-{
- LLUUID avatar_id = getAvatarId();
- if (!getStarted() && avatar_id.notNull())
- {
- setIsLoading();
-
- 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;
- }
- }
-}
-
-void LLPanelProfileSecondLife::refreshName()
-{
- if (!mAvatarNameCacheConnection.connected())
- {
- mAvatarNameCacheConnection = LLAvatarNameCache::get(getAvatarId(), boost::bind(&LLPanelProfileSecondLife::onAvatarNameCache, this, _1, _2));
- }
-}
-
-void LLPanelProfileSecondLife::resetData()
-{
- resetLoading();
-
- // Set default image and 1:1 dimensions for it
- mSecondLifePic->setValue("Generic_Person_Large");
- mImageId = LLUUID::null;
-
- LLRect imageRect = mSecondLifePicLayout->getRect();
- mSecondLifePicLayout->reshape(imageRect.getHeight(), imageRect.getHeight());
-
- setDescriptionText(LLStringUtil::null);
- mGroups.clear();
- mGroupList->setGroups(mGroups);
-
- bool own_profile = getSelfProfile();
- mCanSeeOnlineIcon->setVisible(false);
- mCantSeeOnlineIcon->setVisible(!own_profile);
- mCanSeeOnMapIcon->setVisible(false);
- mCantSeeOnMapIcon->setVisible(!own_profile);
- mCanEditObjectsIcon->setVisible(false);
- mCantEditObjectsIcon->setVisible(!own_profile);
-
- mCanSeeOnlineIcon->setEnabled(false);
- mCantSeeOnlineIcon->setEnabled(false);
- mCanSeeOnMapIcon->setEnabled(false);
- mCantSeeOnMapIcon->setEnabled(false);
- mCanEditObjectsIcon->setEnabled(false);
- mCantEditObjectsIcon->setEnabled(false);
-
- childSetVisible("partner_layout", false);
- childSetVisible("badge_layout", false);
- childSetVisible("partner_spacer_layout", true);
-}
-
-void LLPanelProfileSecondLife::processProfileProperties(const LLAvatarData* avatar_data)
-{
- const LLRelationship* relationship = LLAvatarTracker::instance().getBuddyInfo(getAvatarId());
- if ((relationship != NULL || gAgent.isGodlike()) && !getSelfProfile())
- {
- // Relies onto friend observer to get information about online status updates.
- // Once SL-17506 gets implemented, condition might need to become:
- // (gAgent.isGodlike() || isRightGrantedFrom || flags & AVATAR_ONLINE)
- processOnlineStatus(relationship != NULL,
- gAgent.isGodlike() || relationship->isRightGrantedFrom(LLRelationship::GRANT_ONLINE_STATUS),
- (avatar_data->flags & AVATAR_ONLINE));
- }
-
- fillCommonData(avatar_data);
-
- fillPartnerData(avatar_data);
-
- fillAccountStatus(avatar_data);
-
- setLoaded();
-}
-
-void LLPanelProfileSecondLife::processGroupProperties(const LLAvatarGroups* avatar_groups)
-{
-
- LLAvatarGroups::group_list_t::const_iterator it = avatar_groups->group_list.begin();
- const LLAvatarGroups::group_list_t::const_iterator it_end = avatar_groups->group_list.end();
-
- for (; it_end != it; ++it)
- {
- LLAvatarGroups::LLGroupData group_data = *it;
- mGroups[group_data.group_name] = group_data.group_id;
- }
-
- mGroupList->setGroups(mGroups);
-}
-
-void LLPanelProfileSecondLife::openGroupProfile()
-{
- LLUUID group_id = mGroupList->getSelectedUUID();
- LLGroupActions::show(group_id);
-}
-
-void LLPanelProfileSecondLife::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name)
-{
- mAvatarNameCacheConnection.disconnect();
- getChild<LLUICtrl>("display_name")->setValue(av_name.getDisplayName());
- getChild<LLUICtrl>("user_name")->setValue(av_name.getAccountName());
-}
-
-void LLPanelProfileSecondLife::setProfileImageUploading(bool loading)
-{
- LLLoadingIndicator* indicator = getChild<LLLoadingIndicator>("image_upload_indicator");
- indicator->setVisible(loading);
- if (loading)
- {
- indicator->start();
- }
- else
- {
- indicator->stop();
- }
- mWaitingForImageUpload = loading;
-}
-
-void LLPanelProfileSecondLife::setProfileImageUploaded(const LLUUID &image_asset_id)
-{
- mSecondLifePic->setValue(image_asset_id);
- mImageId = image_asset_id;
-
- LLViewerFetchedTexture* imagep = LLViewerTextureManager::getFetchedTexture(image_asset_id);
- if (imagep->getFullHeight())
- {
- onImageLoaded(true, imagep);
- }
- else
- {
- imagep->setLoadedCallback(onImageLoaded,
- MAX_DISCARD_LEVEL,
- false,
- false,
- new LLHandle<LLPanel>(getHandle()),
- NULL,
- false);
- }
-
- LLFloater *floater = mFloaterProfileTextureHandle.get();
- if (floater)
- {
- LLFloaterProfileTexture * texture_view = dynamic_cast<LLFloaterProfileTexture*>(floater);
- if (mImageId.notNull())
- {
- texture_view->loadAsset(mImageId);
- }
- else
- {
- texture_view->resetAsset();
- }
- }
-
- setProfileImageUploading(false);
-}
-
-bool LLPanelProfileSecondLife::hasUnsavedChanges()
-{
- LLFloater *floater = mFloaterPermissionsHandle.get();
- if (floater)
- {
- LLFloaterProfilePermissions* perm = dynamic_cast<LLFloaterProfilePermissions*>(floater);
- if (perm && perm->hasUnsavedChanges())
- {
- return true;
- }
- }
- // if floater
- return mHasUnsavedDescriptionChanges;
-}
-
-void LLPanelProfileSecondLife::commitUnsavedChanges()
-{
- LLFloater *floater = mFloaterPermissionsHandle.get();
- if (floater)
- {
- LLFloaterProfilePermissions* perm = dynamic_cast<LLFloaterProfilePermissions*>(floater);
- if (perm && perm->hasUnsavedChanges())
- {
- perm->onApplyRights();
- }
- }
- if (mHasUnsavedDescriptionChanges)
- {
- onSaveDescriptionChanges();
- }
-}
-
-void LLPanelProfileSecondLife::fillCommonData(const LLAvatarData* avatar_data)
-{
- // Refresh avatar id in cache with new info to prevent re-requests
- // and to make sure icons in text will be up to date
- LLAvatarIconIDCache::getInstance()->add(avatar_data->avatar_id, avatar_data->image_id);
-
- fillAgeData(avatar_data->born_on);
-
- setDescriptionText(avatar_data->about_text);
-
- if (avatar_data->image_id.notNull())
- {
- mSecondLifePic->setValue(avatar_data->image_id);
- mImageId = avatar_data->image_id;
- }
- else
- {
- mSecondLifePic->setValue("Generic_Person_Large");
- mImageId = LLUUID::null;
- }
-
- // Will be loaded as a LLViewerFetchedTexture::BOOST_UI due to mSecondLifePic
- LLViewerFetchedTexture* imagep = LLViewerTextureManager::getFetchedTexture(avatar_data->image_id);
- if (imagep->getFullHeight())
- {
- onImageLoaded(true, imagep);
- }
- else
- {
- imagep->setLoadedCallback(onImageLoaded,
- MAX_DISCARD_LEVEL,
- false,
- false,
- new LLHandle<LLPanel>(getHandle()),
- NULL,
- false);
- }
-
- if (getSelfProfile())
- {
- mAllowPublish = avatar_data->flags & AVATAR_ALLOW_PUBLISH;
- mShowInSearchCombo->setValue((bool)mAllowPublish);
- }
-}
-
-void LLPanelProfileSecondLife::fillPartnerData(const LLAvatarData* avatar_data)
-{
- LLTextBox* partner_text_ctrl = getChild<LLTextBox>("partner_link");
- if (avatar_data->partner_id.notNull())
- {
- childSetVisible("partner_layout", true);
- LLStringUtil::format_map_t args;
- args["[LINK]"] = LLSLURL("agent", avatar_data->partner_id, "inspect").getSLURLString();
- std::string partner_text = getString("partner_text", args);
- partner_text_ctrl->setText(partner_text);
- }
- else
- {
- childSetVisible("partner_layout", false);
- }
-}
-
-void LLPanelProfileSecondLife::fillAccountStatus(const LLAvatarData* avatar_data)
-{
- LLStringUtil::format_map_t args;
- args["[ACCTTYPE]"] = LLAvatarPropertiesProcessor::accountType(avatar_data);
- args["[PAYMENTINFO]"] = LLAvatarPropertiesProcessor::paymentInfo(avatar_data);
-
- std::string caption_text = getString("CaptionTextAcctInfo", args);
- getChild<LLUICtrl>("account_info")->setValue(caption_text);
-
- const S32 LINDEN_EMPLOYEE_INDEX = 3;
- LLDate sl_release;
- sl_release.fromYMDHMS(2003, 6, 23, 0, 0, 0);
- std::string customer_lower = avatar_data->customer_type;
- LLStringUtil::toLower(customer_lower);
- if (avatar_data->caption_index == LINDEN_EMPLOYEE_INDEX)
- {
- getChild<LLUICtrl>("badge_icon")->setValue("Profile_Badge_Linden");
- getChild<LLUICtrl>("badge_text")->setValue(getString("BadgeLinden"));
- childSetVisible("badge_layout", true);
- childSetVisible("partner_spacer_layout", false);
- }
- else if (avatar_data->born_on < sl_release)
- {
- getChild<LLUICtrl>("badge_icon")->setValue("Profile_Badge_Beta");
- getChild<LLUICtrl>("badge_text")->setValue(getString("BadgeBeta"));
- childSetVisible("badge_layout", true);
- childSetVisible("partner_spacer_layout", false);
- }
- else if (customer_lower == "beta_lifetime")
- {
- getChild<LLUICtrl>("badge_icon")->setValue("Profile_Badge_Beta_Lifetime");
- getChild<LLUICtrl>("badge_text")->setValue(getString("BadgeBetaLifetime"));
- childSetVisible("badge_layout", true);
- childSetVisible("partner_spacer_layout", false);
- }
- else if (customer_lower == "lifetime")
- {
- getChild<LLUICtrl>("badge_icon")->setValue("Profile_Badge_Lifetime");
- getChild<LLUICtrl>("badge_text")->setValue(getString("BadgeLifetime"));
- childSetVisible("badge_layout", true);
- childSetVisible("partner_spacer_layout", false);
- }
- else if (customer_lower == "secondlifetime_premium")
- {
- getChild<LLUICtrl>("badge_icon")->setValue("Profile_Badge_Premium_Lifetime");
- getChild<LLUICtrl>("badge_text")->setValue(getString("BadgePremiumLifetime"));
- childSetVisible("badge_layout", true);
- childSetVisible("partner_spacer_layout", false);
- }
- else if (customer_lower == "secondlifetime_premium_plus")
- {
- getChild<LLUICtrl>("badge_icon")->setValue("Profile_Badge_Pplus_Lifetime");
- getChild<LLUICtrl>("badge_text")->setValue(getString("BadgePremiumPlusLifetime"));
- childSetVisible("badge_layout", true);
- childSetVisible("partner_spacer_layout", false);
- }
- else
- {
- childSetVisible("badge_layout", false);
- childSetVisible("partner_spacer_layout", true);
- }
-}
-
-void LLPanelProfileSecondLife::fillRightsData()
-{
- if (getSelfProfile())
- {
- return;
- }
-
- const LLRelationship* relation = LLAvatarTracker::instance().getBuddyInfo(getAvatarId());
- // If true - we are viewing friend's profile, enable check boxes and set values.
- if (relation)
- {
- S32 rights = relation->getRightsGrantedTo();
- bool can_see_online = LLRelationship::GRANT_ONLINE_STATUS & rights;
- bool can_see_on_map = LLRelationship::GRANT_MAP_LOCATION & rights;
- bool can_edit_objects = LLRelationship::GRANT_MODIFY_OBJECTS & rights;
-
- mCanSeeOnlineIcon->setVisible(can_see_online);
- mCantSeeOnlineIcon->setVisible(!can_see_online);
- mCanSeeOnMapIcon->setVisible(can_see_on_map);
- mCantSeeOnMapIcon->setVisible(!can_see_on_map);
- mCanEditObjectsIcon->setVisible(can_edit_objects);
- mCantEditObjectsIcon->setVisible(!can_edit_objects);
-
- mCanSeeOnlineIcon->setEnabled(true);
- mCantSeeOnlineIcon->setEnabled(true);
- mCanSeeOnMapIcon->setEnabled(true);
- mCantSeeOnMapIcon->setEnabled(true);
- mCanEditObjectsIcon->setEnabled(true);
- mCantEditObjectsIcon->setEnabled(true);
- }
- else
- {
- mCanSeeOnlineIcon->setVisible(false);
- mCantSeeOnlineIcon->setVisible(false);
- mCanSeeOnMapIcon->setVisible(false);
- mCantSeeOnMapIcon->setVisible(false);
- mCanEditObjectsIcon->setVisible(false);
- mCantEditObjectsIcon->setVisible(false);
- }
-}
-
-void LLPanelProfileSecondLife::fillAgeData(const LLDate &born_on)
-{
- // Date from server comes already converted to stl timezone,
- // so display it as an UTC + 0
- std::string name_and_date = getString("date_format");
- LLSD args_name;
- args_name["datetime"] = (S32)born_on.secondsSinceEpoch();
- LLStringUtil::format(name_and_date, args_name);
- getChild<LLUICtrl>("sl_birth_date")->setValue(name_and_date);
-
- std::string register_date = getString("age_format");
- LLSD args_age;
- args_age["[AGE]"] = LLDateUtil::ageFromDate(born_on, LLDate::now());
- LLStringUtil::format(register_date, args_age);
- getChild<LLUICtrl>("user_age")->setValue(register_date);
-}
-
-void LLPanelProfileSecondLife::onImageLoaded(bool success, LLViewerFetchedTexture *imagep)
-{
- LLRect imageRect = mSecondLifePicLayout->getRect();
- if (!success || imagep->getFullWidth() == imagep->getFullHeight())
- {
- mSecondLifePicLayout->reshape(imageRect.getWidth(), imageRect.getWidth());
- }
- else
- {
- // assume 3:4, for sake of firestorm
- mSecondLifePicLayout->reshape(imageRect.getWidth(), imageRect.getWidth() * 3 / 4);
- }
-}
-
-//static
-void LLPanelProfileSecondLife::onImageLoaded(bool success,
- LLViewerFetchedTexture *src_vi,
- LLImageRaw* src,
- LLImageRaw* aux_src,
- S32 discard_level,
- bool final,
- void* userdata)
-{
- if (!userdata) return;
-
- LLHandle<LLPanel>* handle = (LLHandle<LLPanel>*)userdata;
-
- if (!handle->isDead())
- {
- LLPanelProfileSecondLife* panel = static_cast<LLPanelProfileSecondLife*>(handle->get());
- if (panel)
- {
- panel->onImageLoaded(success, src_vi);
- }
- }
-
- if (final || !success)
- {
- delete handle;
- }
-}
-
-// virtual, called by LLAvatarTracker
-void LLPanelProfileSecondLife::changed(U32 mask)
-{
- updateOnlineStatus();
- if (mask != LLFriendObserver::ONLINE)
- {
- fillRightsData();
- }
-}
-
-// virtual, called by LLVoiceClient
-void LLPanelProfileSecondLife::onChange(EStatusType status, const std::string &channelURI, bool proximal)
-{
- if(status == STATUS_JOINING || status == STATUS_LEFT_CHANNEL)
- {
- return;
- }
-
- mVoiceStatus = LLAvatarActions::canCall() && (LLAvatarActions::isFriend(getAvatarId()) ? LLAvatarTracker::instance().isBuddyOnline(getAvatarId()) : true);
-}
-
-void LLPanelProfileSecondLife::setAvatarId(const LLUUID& avatar_id)
-{
- if (avatar_id.notNull())
- {
- if (getAvatarId().notNull())
- {
- LLAvatarTracker::instance().removeParticularFriendObserver(getAvatarId(), this);
- }
-
- LLPanelProfileTab::setAvatarId(avatar_id);
-
- if (LLAvatarActions::isFriend(getAvatarId()))
- {
- LLAvatarTracker::instance().addParticularFriendObserver(getAvatarId(), this);
- }
- }
-}
-
-// method was disabled according to EXT-2022. Re-enabled & improved according to EXT-3880
-void LLPanelProfileSecondLife::updateOnlineStatus()
-{
- const LLRelationship* relationship = LLAvatarTracker::instance().getBuddyInfo(getAvatarId());
- if (relationship != NULL)
- {
- // For friend let check if he allowed me to see his status
- bool online = relationship->isOnline();
- bool perm_granted = relationship->isRightGrantedFrom(LLRelationship::GRANT_ONLINE_STATUS);
- processOnlineStatus(true, perm_granted, online);
- }
- else
- {
- childSetVisible("friend_layout", false);
- childSetVisible("online_layout", false);
- childSetVisible("offline_layout", false);
- }
-}
-
-void LLPanelProfileSecondLife::processOnlineStatus(bool is_friend, bool show_online, bool online)
-{
- childSetVisible("friend_layout", is_friend);
- childSetVisible("online_layout", online && show_online);
- childSetVisible("offline_layout", !online && show_online);
-}
-
-void LLPanelProfileSecondLife::setLoaded()
-{
- LLPanelProfileTab::setLoaded();
-
- if (getSelfProfile())
- {
- mShowInSearchCombo->setEnabled(true);
- mDescriptionEdit->setEnabled(true);
- }
-}
-
-
-class LLProfileImagePicker : public LLFilePickerThread
-{
-public:
- LLProfileImagePicker(EProfileImageType type, LLHandle<LLPanel> *handle);
- ~LLProfileImagePicker();
- void notify(const std::vector<std::string>& filenames) override;
-
-private:
- LLHandle<LLPanel> *mHandle;
- EProfileImageType mType;
-};
-
-LLProfileImagePicker::LLProfileImagePicker(EProfileImageType type, LLHandle<LLPanel> *handle)
- : LLFilePickerThread(LLFilePicker::FFLOAD_IMAGE),
- mHandle(handle),
- mType(type)
-{
-}
-
-LLProfileImagePicker::~LLProfileImagePicker()
-{
- delete mHandle;
-}
-
-void LLProfileImagePicker::notify(const std::vector<std::string>& filenames)
-{
- if (mHandle->isDead())
- {
- return;
- }
- if (filenames.empty())
- {
- return;
- }
- std::string file_path = filenames[0];
- if (file_path.empty())
- {
- return;
- }
-
- // generate a temp texture file for coroutine
- std::string temp_file = gDirUtilp->getTempFilename();
- U32 codec = LLImageBase::getCodecFromExtension(gDirUtilp->getExtension(file_path));
- const S32 MAX_DIM = 256;
- if (!LLViewerTextureList::createUploadFile(file_path, temp_file, codec, MAX_DIM))
- {
- LLSD notif_args;
- notif_args["REASON"] = LLImage::getLastError().c_str();
- LLNotificationsUtil::add("CannotUploadTexture", notif_args);
- LL_WARNS("AvatarProperties") << "Failed to upload profile image of type " << (S32)mType << ", " << notif_args["REASON"].asString() << LL_ENDL;
- return;
- }
-
- std::string cap_url = gAgent.getRegionCapability(PROFILE_IMAGE_UPLOAD_CAP);
- if (cap_url.empty())
- {
- LLSD args;
- args["CAPABILITY"] = PROFILE_IMAGE_UPLOAD_CAP;
- LLNotificationsUtil::add("RegionCapabilityRequestError", args);
- LL_WARNS("AvatarProperties") << "Failed to upload profile image of type " << (S32)mType << ", no cap found" << LL_ENDL;
- return;
- }
-
- switch (mType)
- {
- case PROFILE_IMAGE_SL:
- {
- LLPanelProfileSecondLife* panel = static_cast<LLPanelProfileSecondLife*>(mHandle->get());
- panel->setProfileImageUploading(true);
- }
- break;
- case PROFILE_IMAGE_FL:
- {
- LLPanelProfileFirstLife* panel = static_cast<LLPanelProfileFirstLife*>(mHandle->get());
- panel->setProfileImageUploading(true);
- }
- break;
- }
-
- LLCoros::instance().launch("postAgentUserImageCoro",
- boost::bind(post_profile_image_coro, cap_url, mType, temp_file, mHandle));
-
- mHandle = nullptr; // transferred to post_profile_image_coro
-}
-
-void LLPanelProfileSecondLife::onCommitMenu(const LLSD& userdata)
-{
- const std::string item_name = userdata.asString();
- const LLUUID agent_id = getAvatarId();
- // todo: consider moving this into LLAvatarActions::onCommit(name, id)
- // and making all other flaoters, like people menu do the same
- if (item_name == "im")
- {
- LLAvatarActions::startIM(agent_id);
- }
- else if (item_name == "offer_teleport")
- {
- LLAvatarActions::offerTeleport(agent_id);
- }
- else if (item_name == "request_teleport")
- {
- LLAvatarActions::teleportRequest(agent_id);
- }
- else if (item_name == "voice_call")
- {
- LLAvatarActions::startCall(agent_id);
- }
- else if (item_name == "chat_history")
- {
- LLAvatarActions::viewChatHistory(agent_id);
- }
- else if (item_name == "add_friend")
- {
- LLAvatarActions::requestFriendshipDialog(agent_id);
- }
- else if (item_name == "remove_friend")
- {
- LLAvatarActions::removeFriendDialog(agent_id);
- }
- else if (item_name == "invite_to_group")
- {
- LLAvatarActions::inviteToGroup(agent_id);
- }
- else if (item_name == "can_show_on_map")
- {
- LLAvatarActions::showOnMap(agent_id);
- }
- else if (item_name == "share")
- {
- LLAvatarActions::share(agent_id);
- }
- else if (item_name == "pay")
- {
- LLAvatarActions::pay(agent_id);
- }
- else if (item_name == "toggle_block_agent")
- {
- LLAvatarActions::toggleBlock(agent_id);
- }
- else if (item_name == "copy_user_id")
- {
- LLWString wstr = utf8str_to_wstring(getAvatarId().asString());
- LLClipboard::instance().copyToClipboard(wstr, 0, wstr.size());
- }
- else if (item_name == "agent_permissions")
- {
- onShowAgentPermissionsDialog();
- }
- else if (item_name == "copy_display_name"
- || item_name == "copy_username")
- {
- LLAvatarName av_name;
- if (!LLAvatarNameCache::get(getAvatarId(), &av_name))
- {
- // shouldn't happen, option is supposed to be invisible while name is fetching
- LL_WARNS() << "Failed to get agent data" << LL_ENDL;
- return;
- }
- LLWString wstr;
- if (item_name == "copy_display_name")
- {
- wstr = utf8str_to_wstring(av_name.getDisplayName(true));
- }
- else if (item_name == "copy_username")
- {
- wstr = utf8str_to_wstring(av_name.getUserName());
- }
- LLClipboard::instance().copyToClipboard(wstr, 0, wstr.size());
- }
- else if (item_name == "edit_display_name")
- {
- LLAvatarNameCache::get(getAvatarId(), boost::bind(&LLPanelProfileSecondLife::onAvatarNameCacheSetName, this, _1, _2));
- LLFirstUse::setDisplayName(false);
- }
- else if (item_name == "edit_partner")
- {
- std::string url = "https://[GRID]/my/account/partners.php";
- LLSD subs;
- url = LLWeb::expandURLSubstitutions(url, subs);
- LLUrlAction::openURL(url);
- }
- else if (item_name == "upload_photo")
- {
- (new LLProfileImagePicker(PROFILE_IMAGE_SL, new LLHandle<LLPanel>(getHandle())))->getFile();
-
- LLFloater* floaterp = mFloaterTexturePickerHandle.get();
- if (floaterp)
- {
- floaterp->closeFloater();
- }
- }
- else if (item_name == "change_photo")
- {
- onShowTexturePicker();
- }
- else if (item_name == "remove_photo")
- {
- onCommitProfileImage(LLUUID::null);
-
- LLFloater* floaterp = mFloaterTexturePickerHandle.get();
- if (floaterp)
- {
- floaterp->closeFloater();
- }
- }
-}
-
-bool LLPanelProfileSecondLife::onEnableMenu(const LLSD& userdata)
-{
- const std::string item_name = userdata.asString();
- const LLUUID agent_id = getAvatarId();
- if (item_name == "offer_teleport" || item_name == "request_teleport")
- {
- return LLAvatarActions::canOfferTeleport(agent_id);
- }
- else if (item_name == "voice_call")
- {
- return mVoiceStatus;
- }
- else if (item_name == "chat_history")
- {
- return LLLogChat::isTranscriptExist(agent_id);
- }
- else if (item_name == "add_friend")
- {
- return !LLAvatarActions::isFriend(agent_id);
- }
- else if (item_name == "remove_friend")
- {
- return LLAvatarActions::isFriend(agent_id);
- }
- else if (item_name == "can_show_on_map")
- {
- return (LLAvatarTracker::instance().isBuddyOnline(agent_id) && is_agent_mappable(agent_id))
- || gAgent.isGodlike();
- }
- else if (item_name == "toggle_block_agent")
- {
- return LLAvatarActions::canBlock(agent_id);
- }
- else if (item_name == "agent_permissions")
- {
- return LLAvatarActions::isFriend(agent_id);
- }
- else if (item_name == "copy_display_name"
- || item_name == "copy_username")
- {
- return !mAvatarNameCacheConnection.connected();
- }
- else if (item_name == "upload_photo"
- || item_name == "change_photo")
- {
- std::string cap_url = gAgent.getRegionCapability(PROFILE_IMAGE_UPLOAD_CAP);
- return !cap_url.empty() && !mWaitingForImageUpload && getIsLoaded();
- }
- else if (item_name == "remove_photo")
- {
- std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP);
- return mImageId.notNull() && !cap_url.empty() && !mWaitingForImageUpload && getIsLoaded();
- }
-
- return false;
-}
-
-bool LLPanelProfileSecondLife::onCheckMenu(const LLSD& userdata)
-{
- const std::string item_name = userdata.asString();
- const LLUUID agent_id = getAvatarId();
- if (item_name == "toggle_block_agent")
- {
- return LLAvatarActions::isBlocked(agent_id);
- }
- return false;
-}
-
-void LLPanelProfileSecondLife::onAvatarNameCacheSetName(const LLUUID& agent_id, const LLAvatarName& av_name)
-{
- if (av_name.getDisplayName().empty())
- {
- // something is wrong, tell user to try again later
- LLNotificationsUtil::add("SetDisplayNameFailedGeneric");
- return;
- }
-
- LL_INFOS("LegacyProfile") << "name-change now " << LLDate::now() << " next_update "
- << LLDate(av_name.mNextUpdate) << LL_ENDL;
- F64 now_secs = LLDate::now().secondsSinceEpoch();
-
- if (now_secs < av_name.mNextUpdate)
- {
- // if the update time is more than a year in the future, it means updates have been blocked
- // show a more general message
- static const S32 YEAR = 60*60*24*365;
- if (now_secs + YEAR < av_name.mNextUpdate)
- {
- LLNotificationsUtil::add("SetDisplayNameBlocked");
- return;
- }
- }
-
- LLFloaterReg::showInstance("display_name");
-}
-
-void LLPanelProfileSecondLife::setDescriptionText(const std::string &text)
-{
- mSaveDescriptionChanges->setEnabled(false);
- mDiscardDescriptionChanges->setEnabled(false);
- mHasUnsavedDescriptionChanges = false;
-
- mDescriptionText = text;
- mDescriptionEdit->setValue(mDescriptionText);
-}
-
-void LLPanelProfileSecondLife::onSetDescriptionDirty()
-{
- mSaveDescriptionChanges->setEnabled(true);
- mDiscardDescriptionChanges->setEnabled(true);
- mHasUnsavedDescriptionChanges = true;
-}
-
-void LLPanelProfileSecondLife::onShowInSearchCallback()
-{
- S32 value = mShowInSearchCombo->getValue().asInteger();
- if (mAllowPublish == (bool)value)
- {
- return;
- }
- std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP);
- if (!cap_url.empty())
- {
- mAllowPublish = value;
- LLSD data;
- data["allow_publish"] = mAllowPublish;
- LLCoros::instance().launch("putAgentUserInfoCoro",
- boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), data, nullptr));
- }
- else
- {
- LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL;
- }
-}
-
-void LLPanelProfileSecondLife::onSaveDescriptionChanges()
-{
- mDescriptionText = mDescriptionEdit->getValue().asString();
- std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP);
- if (!cap_url.empty())
- {
- LLCoros::instance().launch("putAgentUserInfoCoro",
- boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), LLSD().with("sl_about_text", mDescriptionText), nullptr));
- }
- else
- {
- LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL;
- }
-
- mSaveDescriptionChanges->setEnabled(false);
- mDiscardDescriptionChanges->setEnabled(false);
- mHasUnsavedDescriptionChanges = false;
-}
-
-void LLPanelProfileSecondLife::onDiscardDescriptionChanges()
-{
- setDescriptionText(mDescriptionText);
-}
-
-void LLPanelProfileSecondLife::onShowAgentPermissionsDialog()
-{
- LLFloater *floater = mFloaterPermissionsHandle.get();
- if (!floater)
- {
- LLFloater* parent_floater = gFloaterView->getParentFloater(this);
- if (parent_floater)
- {
- LLFloaterProfilePermissions * perms = new LLFloaterProfilePermissions(parent_floater, getAvatarId());
- mFloaterPermissionsHandle = perms->getHandle();
- perms->openFloater();
- perms->setVisibleAndFrontmost(true);
-
- parent_floater->addDependentFloater(mFloaterPermissionsHandle);
- }
- }
- else // already open
- {
- floater->setMinimized(false);
- floater->setVisibleAndFrontmost(true);
- }
-}
-
-void LLPanelProfileSecondLife::onShowAgentProfileTexture()
-{
- if (!getIsLoaded())
- {
- return;
- }
-
- LLFloater *floater = mFloaterProfileTextureHandle.get();
- if (!floater)
- {
- LLFloater* parent_floater = gFloaterView->getParentFloater(this);
- if (parent_floater)
- {
- LLFloaterProfileTexture * texture_view = new LLFloaterProfileTexture(parent_floater);
- mFloaterProfileTextureHandle = texture_view->getHandle();
- if (mImageId.notNull())
- {
- texture_view->loadAsset(mImageId);
- }
- else
- {
- texture_view->resetAsset();
- }
- texture_view->openFloater();
- texture_view->setVisibleAndFrontmost(true);
-
- parent_floater->addDependentFloater(mFloaterProfileTextureHandle);
- }
- }
- else // already open
- {
- LLFloaterProfileTexture * texture_view = dynamic_cast<LLFloaterProfileTexture*>(floater);
- texture_view->setMinimized(false);
- texture_view->setVisibleAndFrontmost(true);
- if (mImageId.notNull())
- {
- texture_view->loadAsset(mImageId);
- }
- else
- {
- texture_view->resetAsset();
- }
- }
-}
-
-void LLPanelProfileSecondLife::onShowTexturePicker()
-{
- LLFloater* floaterp = mFloaterTexturePickerHandle.get();
-
- // Show the dialog
- if (!floaterp)
- {
- LLFloater* parent_floater = gFloaterView->getParentFloater(this);
- if (parent_floater)
- {
- // because inventory construction is somewhat slow
- getWindow()->setCursor(UI_CURSOR_WAIT);
- LLFloaterTexturePicker* texture_floaterp = new LLFloaterTexturePicker(
- this,
- mImageId,
- LLUUID::null,
- mImageId,
- false,
- false,
- "SELECT PHOTO",
- PERM_NONE,
- PERM_NONE,
- false,
- NULL,
- PICK_TEXTURE);
-
- mFloaterTexturePickerHandle = texture_floaterp->getHandle();
-
- texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLPickerSource source, const LLUUID& asset_id, const LLUUID&, const LLUUID&)
- {
- if (op == LLTextureCtrl::TEXTURE_SELECT)
- {
- onCommitProfileImage(asset_id);
- }
- });
- texture_floaterp->setLocalTextureEnabled(false);
- texture_floaterp->setBakeTextureEnabled(false);
- texture_floaterp->setCanApply(false, true, false);
-
- parent_floater->addDependentFloater(mFloaterTexturePickerHandle);
-
- texture_floaterp->openFloater();
- texture_floaterp->setFocus(true);
- }
- }
- else
- {
- floaterp->setMinimized(false);
- floaterp->setVisibleAndFrontmost(true);
- }
-}
-
-void LLPanelProfileSecondLife::onCommitProfileImage(const LLUUID& id)
-{
- if (mImageId == id)
- {
- return;
- }
-
- std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP);
- if (!cap_url.empty())
- {
- std::function<void(bool)> callback = [id](bool result)
- {
- if (result)
- {
- LLAvatarIconIDCache::getInstance()->add(gAgentID, id);
- // Should trigger callbacks in icon controls
- LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesRequest(gAgentID);
- }
- };
- LLSD params;
- params["sl_image_id"] = id;
- LLCoros::instance().launch("putAgentUserInfoCoro",
- boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), params, callback));
-
- mImageId = id;
- if (mImageId == LLUUID::null)
- {
- mSecondLifePic->setValue("Generic_Person_Large");
- }
- else
- {
- mSecondLifePic->setValue(mImageId);
- }
-
- LLFloater *floater = mFloaterProfileTextureHandle.get();
- if (floater)
- {
- LLFloaterProfileTexture * texture_view = dynamic_cast<LLFloaterProfileTexture*>(floater);
- if (mImageId == LLUUID::null)
- {
- texture_view->resetAsset();
- }
- else
- {
- texture_view->loadAsset(mImageId);
- }
- }
- }
- else
- {
- LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL;
- }
-}
-
-//////////////////////////////////////////////////////////////////////////
-// LLPanelProfileWeb
-
-LLPanelProfileWeb::LLPanelProfileWeb()
- : LLPanelProfileTab()
- , mWebBrowser(NULL)
- , mAvatarNameCacheConnection()
-{
-}
-
-LLPanelProfileWeb::~LLPanelProfileWeb()
-{
- if (mAvatarNameCacheConnection.connected())
- {
- mAvatarNameCacheConnection.disconnect();
- }
-}
-
-void LLPanelProfileWeb::onOpen(const LLSD& key)
-{
- LLPanelProfileTab::onOpen(key);
-
- resetData();
-
- mAvatarNameCacheConnection = LLAvatarNameCache::get(getAvatarId(), boost::bind(&LLPanelProfileWeb::onAvatarNameCache, this, _1, _2));
-}
-
-bool LLPanelProfileWeb::postBuild()
-{
- mWebBrowser = getChild<LLMediaCtrl>("profile_html");
- mWebBrowser->addObserver(this);
- mWebBrowser->setHomePageUrl("about:blank");
-
- return true;
-}
-
-void LLPanelProfileWeb::resetData()
-{
- mWebBrowser->navigateHome();
-}
-
-void LLPanelProfileWeb::updateData()
-{
- LLUUID avatar_id = getAvatarId();
- if (!getStarted() && avatar_id.notNull() && !mURLWebProfile.empty())
- {
- setIsLoading();
-
- mWebBrowser->setVisible(true);
- mPerformanceTimer.start();
- mWebBrowser->navigateTo(mURLWebProfile, HTTP_CONTENT_TEXT_HTML);
- }
-}
-
-void LLPanelProfileWeb::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name)
-{
- mAvatarNameCacheConnection.disconnect();
-
- std::string username = av_name.getAccountName();
- if (username.empty())
- {
- username = LLCacheName::buildUsername(av_name.getDisplayName());
- }
- else
- {
- LLStringUtil::replaceChar(username, ' ', '.');
- }
-
- mURLWebProfile = getProfileURL(username, true);
- if (mURLWebProfile.empty())
- {
- return;
- }
-
- //if the tab was opened before name was resolved, load the panel now
- updateData();
-}
-
-void LLPanelProfileWeb::onCommitLoad(LLUICtrl* ctrl)
-{
- if (!mURLHome.empty())
- {
- LLSD::String valstr = ctrl->getValue().asString();
- if (valstr.empty())
- {
- mWebBrowser->setVisible(true);
- mPerformanceTimer.start();
- mWebBrowser->navigateTo( mURLHome, HTTP_CONTENT_TEXT_HTML );
- }
- else if (valstr == "popout")
- {
- // open in viewer's browser, new window
- LLWeb::loadURLInternal(mURLHome);
- }
- else if (valstr == "external")
- {
- // open in external browser
- LLWeb::loadURLExternal(mURLHome);
- }
- }
-}
-
-void LLPanelProfileWeb::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event)
-{
- switch(event)
- {
- case MEDIA_EVENT_STATUS_TEXT_CHANGED:
- childSetValue("status_text", LLSD( self->getStatusText() ) );
- break;
-
- case MEDIA_EVENT_NAVIGATE_BEGIN:
- {
- if (mFirstNavigate)
- {
- mFirstNavigate = false;
- }
- else
- {
- mPerformanceTimer.start();
- }
- }
- break;
-
- case MEDIA_EVENT_NAVIGATE_COMPLETE:
- {
- LLStringUtil::format_map_t args;
- args["[TIME]"] = llformat("%.2f", mPerformanceTimer.getElapsedTimeF32());
- childSetValue("status_text", LLSD( getString("LoadTime", args)) );
- }
- break;
-
- default:
- // Having a default case makes the compiler happy.
- break;
- }
-}
-
-
-//////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////
-
-LLPanelProfileFirstLife::LLPanelProfileFirstLife()
- : LLPanelProfileTab()
- , mHasUnsavedChanges(false)
-{
-}
-
-LLPanelProfileFirstLife::~LLPanelProfileFirstLife()
-{
-}
-
-bool LLPanelProfileFirstLife::postBuild()
-{
- mDescriptionEdit = getChild<LLTextEditor>("fl_description_edit");
- mPicture = getChild<LLThumbnailCtrl>("real_world_pic");
-
- mUploadPhoto = getChild<LLButton>("fl_upload_image");
- mChangePhoto = getChild<LLButton>("fl_change_image");
- mRemovePhoto = getChild<LLButton>("fl_remove_image");
- mSaveChanges = getChild<LLButton>("fl_save_changes");
- mDiscardChanges = getChild<LLButton>("fl_discard_changes");
-
- mUploadPhoto->setCommitCallback([this](LLUICtrl*, void*) { onUploadPhoto(); }, nullptr);
- mChangePhoto->setCommitCallback([this](LLUICtrl*, void*) { onChangePhoto(); }, nullptr);
- mRemovePhoto->setCommitCallback([this](LLUICtrl*, void*) { onRemovePhoto(); }, nullptr);
- mSaveChanges->setCommitCallback([this](LLUICtrl*, void*) { onSaveDescriptionChanges(); }, nullptr);
- mDiscardChanges->setCommitCallback([this](LLUICtrl*, void*) { onDiscardDescriptionChanges(); }, nullptr);
- mDescriptionEdit->setKeystrokeCallback([this](LLTextEditor* caller) { onSetDescriptionDirty(); });
-
- return true;
-}
-
-void LLPanelProfileFirstLife::onOpen(const LLSD& key)
-{
- LLPanelProfileTab::onOpen(key);
-
- if (!getSelfProfile())
- {
- // Otherwise as the only focusable element it will be selected
- mDescriptionEdit->setTabStop(false);
- }
-
- resetData();
-}
-
-void LLPanelProfileFirstLife::setProfileImageUploading(bool loading)
-{
- mUploadPhoto->setEnabled(!loading);
- mChangePhoto->setEnabled(!loading);
- mRemovePhoto->setEnabled(!loading && mImageId.notNull());
-
- LLLoadingIndicator* indicator = getChild<LLLoadingIndicator>("image_upload_indicator");
- indicator->setVisible(loading);
- if (loading)
- {
- indicator->start();
- }
- else
- {
- indicator->stop();
- }
-}
-
-void LLPanelProfileFirstLife::setProfileImageUploaded(const LLUUID &image_asset_id)
-{
- mPicture->setValue(image_asset_id);
- mImageId = image_asset_id;
- setProfileImageUploading(false);
-}
-
-void LLPanelProfileFirstLife::commitUnsavedChanges()
-{
- if (mHasUnsavedChanges)
- {
- onSaveDescriptionChanges();
- }
-}
-
-void LLPanelProfileFirstLife::onUploadPhoto()
-{
- (new LLProfileImagePicker(PROFILE_IMAGE_FL, new LLHandle<LLPanel>(getHandle())))->getFile();
-
- LLFloater* floaterp = mFloaterTexturePickerHandle.get();
- if (floaterp)
- {
- floaterp->closeFloater();
- }
-}
-
-void LLPanelProfileFirstLife::onChangePhoto()
-{
- LLFloater* floaterp = mFloaterTexturePickerHandle.get();
-
- // Show the dialog
- if (!floaterp)
- {
- LLFloater* parent_floater = gFloaterView->getParentFloater(this);
- if (parent_floater)
- {
- // because inventory construction is somewhat slow
- getWindow()->setCursor(UI_CURSOR_WAIT);
- LLFloaterTexturePicker* texture_floaterp = new LLFloaterTexturePicker(
- this,
- mImageId,
- LLUUID::null,
- mImageId,
- false,
- false,
- "SELECT PHOTO",
- PERM_NONE,
- PERM_NONE,
- false,
- NULL,
- PICK_TEXTURE);
-
- mFloaterTexturePickerHandle = texture_floaterp->getHandle();
-
- texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLPickerSource source, const LLUUID& asset_id, const LLUUID&, const LLUUID&)
- {
- if (op == LLTextureCtrl::TEXTURE_SELECT)
- {
- onCommitPhoto(asset_id);
- }
- });
- texture_floaterp->setLocalTextureEnabled(false);
- texture_floaterp->setCanApply(false, true, false);
-
- parent_floater->addDependentFloater(mFloaterTexturePickerHandle);
-
- texture_floaterp->openFloater();
- texture_floaterp->setFocus(true);
- }
- }
- else
- {
- floaterp->setMinimized(false);
- floaterp->setVisibleAndFrontmost(true);
- }
-}
-
-void LLPanelProfileFirstLife::onRemovePhoto()
-{
- onCommitPhoto(LLUUID::null);
-
- LLFloater* floaterp = mFloaterTexturePickerHandle.get();
- if (floaterp)
- {
- floaterp->closeFloater();
- }
-}
-
-void LLPanelProfileFirstLife::onCommitPhoto(const LLUUID& id)
-{
- if (mImageId == id)
- {
- return;
- }
-
- std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP);
- if (!cap_url.empty())
- {
- LLSD params;
- params["fl_image_id"] = id;
- LLCoros::instance().launch("putAgentUserInfoCoro",
- boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), params, nullptr));
-
- mImageId = id;
- if (mImageId.notNull())
- {
- mPicture->setValue(mImageId);
- }
- else
- {
- mPicture->setValue("Generic_Person_Large");
- }
-
- mRemovePhoto->setEnabled(mImageId.notNull());
- }
- else
- {
- LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL;
- }
-}
-
-void LLPanelProfileFirstLife::setDescriptionText(const std::string &text)
-{
- mSaveChanges->setEnabled(false);
- mDiscardChanges->setEnabled(false);
- mHasUnsavedChanges = false;
-
- mCurrentDescription = text;
- mDescriptionEdit->setValue(mCurrentDescription);
-}
-
-void LLPanelProfileFirstLife::onSetDescriptionDirty()
-{
- mSaveChanges->setEnabled(true);
- mDiscardChanges->setEnabled(true);
- mHasUnsavedChanges = true;
-}
-
-void LLPanelProfileFirstLife::onSaveDescriptionChanges()
-{
- mCurrentDescription = mDescriptionEdit->getValue().asString();
- std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP);
- if (!cap_url.empty())
- {
- LLCoros::instance().launch("putAgentUserInfoCoro",
- boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), LLSD().with("fl_about_text", mCurrentDescription), nullptr));
- }
- else
- {
- LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL;
- }
-
- mSaveChanges->setEnabled(false);
- mDiscardChanges->setEnabled(false);
- mHasUnsavedChanges = false;
-}
-
-void LLPanelProfileFirstLife::onDiscardDescriptionChanges()
-{
- setDescriptionText(mCurrentDescription);
-}
-
-void LLPanelProfileFirstLife::processProperties(const LLAvatarData* avatar_data)
-{
- setDescriptionText(avatar_data->fl_about_text);
-
- mImageId = avatar_data->fl_image_id;
-
- if (mImageId.notNull())
- {
- mPicture->setValue(mImageId);
- }
- else
- {
- mPicture->setValue("Generic_Person_Large");
- }
-
- setLoaded();
-}
-
-void LLPanelProfileFirstLife::resetData()
-{
- setDescriptionText(std::string());
- mPicture->setValue("Generic_Person_Large");
- mImageId = LLUUID::null;
-
- mUploadPhoto->setVisible(getSelfProfile());
- mChangePhoto->setVisible(getSelfProfile());
- mRemovePhoto->setVisible(getSelfProfile());
- mSaveChanges->setVisible(getSelfProfile());
- mDiscardChanges->setVisible(getSelfProfile());
-}
-
-void LLPanelProfileFirstLife::setLoaded()
-{
- LLPanelProfileTab::setLoaded();
-
- if (getSelfProfile())
- {
- mDescriptionEdit->setEnabled(true);
- mPicture->setEnabled(true);
- mRemovePhoto->setEnabled(mImageId.notNull());
- }
-}
-
-//////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////
-
-LLPanelProfileNotes::LLPanelProfileNotes()
-: LLPanelProfileTab()
- , mHasUnsavedChanges(false)
-{
-
-}
-
-LLPanelProfileNotes::~LLPanelProfileNotes()
-{
-}
-
-void LLPanelProfileNotes::updateData()
-{
- LLUUID avatar_id = getAvatarId();
- if (!getStarted() && avatar_id.notNull())
- {
- setIsLoading();
-
- 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));
- }
- }
-}
-
-void LLPanelProfileNotes::commitUnsavedChanges()
-{
- if (mHasUnsavedChanges)
- {
- onSaveNotesChanges();
- }
-}
-
-bool LLPanelProfileNotes::postBuild()
-{
- mNotesEditor = getChild<LLTextEditor>("notes_edit");
- mSaveChanges = getChild<LLButton>("notes_save_changes");
- mDiscardChanges = getChild<LLButton>("notes_discard_changes");
-
- mSaveChanges->setCommitCallback([this](LLUICtrl*, void*) { onSaveNotesChanges(); }, nullptr);
- mDiscardChanges->setCommitCallback([this](LLUICtrl*, void*) { onDiscardNotesChanges(); }, nullptr);
- mNotesEditor->setKeystrokeCallback([this](LLTextEditor* caller) { onSetNotesDirty(); });
-
- return true;
-}
-
-void LLPanelProfileNotes::onOpen(const LLSD& key)
-{
- LLPanelProfileTab::onOpen(key);
-
- resetData();
-}
-
-void LLPanelProfileNotes::setNotesText(const std::string &text)
-{
- mSaveChanges->setEnabled(false);
- mDiscardChanges->setEnabled(false);
- mHasUnsavedChanges = false;
-
- mCurrentNotes = text;
- mNotesEditor->setValue(mCurrentNotes);
-}
-
-void LLPanelProfileNotes::onSetNotesDirty()
-{
- mSaveChanges->setEnabled(true);
- mDiscardChanges->setEnabled(true);
- mHasUnsavedChanges = true;
-}
-
-void LLPanelProfileNotes::onSaveNotesChanges()
-{
- mCurrentNotes = mNotesEditor->getValue().asString();
- std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP);
- if (!cap_url.empty())
- {
- LLCoros::instance().launch("putAgentUserInfoCoro",
- boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), LLSD().with("notes", mCurrentNotes), nullptr));
- }
- else
- {
- LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL;
- }
-
- mSaveChanges->setEnabled(false);
- mDiscardChanges->setEnabled(false);
- mHasUnsavedChanges = false;
-}
-
-void LLPanelProfileNotes::onDiscardNotesChanges()
-{
- setNotesText(mCurrentNotes);
-}
-
-void LLPanelProfileNotes::processProperties(LLAvatarNotes* avatar_notes)
-{
- setNotesText(avatar_notes->notes);
- mNotesEditor->setEnabled(true);
- setLoaded();
-}
-
-void LLPanelProfileNotes::resetData()
-{
- resetLoading();
- setNotesText(std::string());
-}
-
-void LLPanelProfileNotes::setAvatarId(const LLUUID& avatar_id)
-{
- if (avatar_id.notNull())
- {
- LLPanelProfileTab::setAvatarId(avatar_id);
- }
-}
-
-
-//////////////////////////////////////////////////////////////////////////
-// LLPanelProfile
-
-LLPanelProfile::LLPanelProfile()
- : LLPanelProfileTab()
-{
-}
-
-LLPanelProfile::~LLPanelProfile()
-{
-}
-
-bool LLPanelProfile::postBuild()
-{
- return true;
-}
-
-void LLPanelProfile::onTabChange()
-{
- LLPanelProfileTab* active_panel = dynamic_cast<LLPanelProfileTab*>(mTabContainer->getCurrentPanel());
- if (active_panel)
- {
- active_panel->updateData();
- }
-}
-
-void LLPanelProfile::onOpen(const LLSD& key)
-{
- LLUUID avatar_id = key["id"].asUUID();
-
- // Don't reload the same profile
- if (getAvatarId() == avatar_id)
- {
- return;
- }
-
- LLPanelProfileTab::onOpen(avatar_id);
-
- mTabContainer = getChild<LLTabContainer>("panel_profile_tabs");
- mPanelSecondlife = findChild<LLPanelProfileSecondLife>(PANEL_SECONDLIFE);
- mPanelWeb = findChild<LLPanelProfileWeb>(PANEL_WEB);
- mPanelPicks = findChild<LLPanelProfilePicks>(PANEL_PICKS);
- mPanelClassifieds = findChild<LLPanelProfileClassifieds>(PANEL_CLASSIFIEDS);
- mPanelFirstlife = findChild<LLPanelProfileFirstLife>(PANEL_FIRSTLIFE);
- mPanelNotes = findChild<LLPanelProfileNotes>(PANEL_NOTES);
-
- mPanelSecondlife->onOpen(avatar_id);
- mPanelWeb->onOpen(avatar_id);
- mPanelPicks->onOpen(avatar_id);
- mPanelClassifieds->onOpen(avatar_id);
- mPanelFirstlife->onOpen(avatar_id);
- mPanelNotes->onOpen(avatar_id);
-
- // Always request the base profile info
- resetLoading();
- updateData();
-
- // Some tabs only request data when opened
- mTabContainer->setCommitCallback(boost::bind(&LLPanelProfile::onTabChange, this));
-}
-
-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 (!getStarted() && avatar_id.notNull())
- {
- setIsLoading();
-
- mPanelSecondlife->setIsLoading();
- mPanelPicks->setIsLoading();
- mPanelFirstlife->setIsLoading();
- mPanelNotes->setIsLoading();
-
- 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));
- }
- }
-}
-
-void LLPanelProfile::refreshName()
-{
- mPanelSecondlife->refreshName();
-}
-
-void LLPanelProfile::createPick(const LLPickData &data)
-{
- mTabContainer->selectTabPanel(mPanelPicks);
- mPanelPicks->createPick(data);
-}
-
-void LLPanelProfile::showPick(const LLUUID& pick_id)
-{
- if (pick_id.notNull())
- {
- mPanelPicks->selectPick(pick_id);
- }
- mTabContainer->selectTabPanel(mPanelPicks);
-}
-
-bool LLPanelProfile::isPickTabSelected()
-{
- return (mTabContainer->getCurrentPanel() == mPanelPicks);
-}
-
-bool LLPanelProfile::isNotesTabSelected()
-{
- return (mTabContainer->getCurrentPanel() == mPanelNotes);
-}
-
-bool LLPanelProfile::hasUnsavedChanges()
-{
- return mPanelSecondlife->hasUnsavedChanges()
- || mPanelPicks->hasUnsavedChanges()
- || mPanelClassifieds->hasUnsavedChanges()
- || mPanelFirstlife->hasUnsavedChanges()
- || mPanelNotes->hasUnsavedChanges();
-}
-
-bool LLPanelProfile::hasUnpublishedClassifieds()
-{
- return mPanelClassifieds->hasNewClassifieds();
-}
-
-void LLPanelProfile::commitUnsavedChanges()
-{
- mPanelSecondlife->commitUnsavedChanges();
- mPanelPicks->commitUnsavedChanges();
- mPanelClassifieds->commitUnsavedChanges();
- mPanelFirstlife->commitUnsavedChanges();
- mPanelNotes->commitUnsavedChanges();
-}
-
-void LLPanelProfile::showClassified(const LLUUID& classified_id, bool edit)
-{
- if (classified_id.notNull())
- {
- mPanelClassifieds->selectClassified(classified_id, edit);
- }
- mTabContainer->selectTabPanel(mPanelClassifieds);
-}
-
-void LLPanelProfile::createClassified()
-{
- mPanelClassifieds->createClassified();
- mTabContainer->selectTabPanel(mPanelClassifieds);
-}
-
+/**
+* @file llpanelprofile.cpp
+* @brief Profile panel implementation
+*
+* $LicenseInfo:firstyear=2022&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2022, 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 "llpanelprofile.h"
+
+// Common
+#include "llavatarnamecache.h"
+#include "llsdutil.h"
+#include "llslurl.h"
+#include "lldateutil.h" //ageFromDate
+
+// UI
+#include "llavatariconctrl.h"
+#include "llclipboard.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "lllineeditor.h"
+#include "llloadingindicator.h"
+#include "llmenubutton.h"
+#include "lltabcontainer.h"
+#include "lltextbox.h"
+#include "lltexteditor.h"
+#include "lltexturectrl.h"
+#include "lltoggleablemenu.h"
+#include "lltooldraganddrop.h"
+#include "llgrouplist.h"
+#include "llurlaction.h"
+
+// Image
+#include "llimagej2c.h"
+
+// Newview
+#include "llagent.h" //gAgent
+#include "llagentpicksinfo.h"
+#include "llavataractions.h"
+#include "llavatarpropertiesprocessor.h"
+#include "llcallingcard.h"
+#include "llcommandhandler.h"
+#include "llfloaterprofiletexture.h"
+#include "llfloaterreg.h"
+#include "llfloaterreporter.h"
+#include "llfilepicker.h"
+#include "llfirstuse.h"
+#include "llgroupactions.h"
+#include "lllogchat.h"
+#include "llmutelist.h"
+#include "llnotificationsutil.h"
+#include "llpanelblockedlist.h"
+#include "llpanelprofileclassifieds.h"
+#include "llpanelprofilepicks.h"
+#include "lltrans.h"
+#include "llviewercontrol.h"
+#include "llviewermenu.h" //is_agent_mappable
+#include "llviewermenufile.h"
+#include "llviewertexturelist.h"
+#include "llvoiceclient.h"
+#include "llweb.h"
+
+
+static LLPanelInjector<LLPanelProfileSecondLife> t_panel_profile_secondlife("panel_profile_secondlife");
+static LLPanelInjector<LLPanelProfileWeb> t_panel_web("panel_profile_web");
+static LLPanelInjector<LLPanelProfilePicks> t_panel_picks("panel_profile_picks");
+static LLPanelInjector<LLPanelProfileFirstLife> t_panel_firstlife("panel_profile_firstlife");
+static LLPanelInjector<LLPanelProfileNotes> t_panel_notes("panel_profile_notes");
+static LLPanelInjector<LLPanelProfile> t_panel_profile("panel_profile");
+
+static const std::string PANEL_SECONDLIFE = "panel_profile_secondlife";
+static const std::string PANEL_WEB = "panel_profile_web";
+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";
+static const std::string PROFILE_IMAGE_UPLOAD_CAP = "UploadAgentProfileImage";
+
+
+//////////////////////////////////////////////////////////////////////////
+
+LLUUID post_profile_image(std::string cap_url, const LLSD &first_data, std::string path_to_image, LLHandle<LLPanel> *handle)
+{
+ LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+ LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
+ httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("post_profile_image_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);
+
+ LLSD result = httpAdapter->postAndSuspend(httpRequest, cap_url, first_data, httpOpts, httpHeaders);
+
+ LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+ LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+
+ if (!status)
+ {
+ // todo: notification?
+ LL_WARNS("AvatarProperties") << "Failed to get uploader cap " << status.toString() << LL_ENDL;
+ return LLUUID::null;
+ }
+ if (!result.has("uploader"))
+ {
+ // todo: notification?
+ LL_WARNS("AvatarProperties") << "Failed to get uploader cap, response contains no data." << LL_ENDL;
+ return LLUUID::null;
+ }
+ std::string uploader_cap = result["uploader"].asString();
+ if (uploader_cap.empty())
+ {
+ LL_WARNS("AvatarProperties") << "Failed to get uploader cap, cap invalid." << LL_ENDL;
+ return LLUUID::null;
+ }
+
+ // Upload the image
+ LLCore::HttpRequest::ptr_t uploaderhttpRequest(new LLCore::HttpRequest);
+ LLCore::HttpHeaders::ptr_t uploaderhttpHeaders(new LLCore::HttpHeaders);
+ LLCore::HttpOptions::ptr_t uploaderhttpOpts(new LLCore::HttpOptions);
+ S64 length;
+
+ {
+ llifstream instream(path_to_image.c_str(), std::iostream::binary | std::iostream::ate);
+ if (!instream.is_open())
+ {
+ LL_WARNS("AvatarProperties") << "Failed to open file " << path_to_image << LL_ENDL;
+ return LLUUID::null;
+ }
+ length = instream.tellg();
+ }
+
+ uploaderhttpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, "application/jp2"); // optional
+ uploaderhttpHeaders->append(HTTP_OUT_HEADER_CONTENT_LENGTH, llformat("%d", length)); // required!
+ uploaderhttpOpts->setFollowRedirects(true);
+
+ result = httpAdapter->postFileAndSuspend(uploaderhttpRequest, uploader_cap, path_to_image, uploaderhttpOpts, uploaderhttpHeaders);
+
+ httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+ status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+
+ LL_DEBUGS("AvatarProperties") << result << LL_ENDL;
+
+ if (!status)
+ {
+ LL_WARNS("AvatarProperties") << "Failed to upload image " << status.toString() << LL_ENDL;
+ return LLUUID::null;
+ }
+
+ if (result["state"].asString() != "complete")
+ {
+ if (result.has("message"))
+ {
+ LL_WARNS("AvatarProperties") << "Failed to upload image, state " << result["state"] << " message: " << result["message"] << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS("AvatarProperties") << "Failed to upload image " << result << LL_ENDL;
+ }
+ return LLUUID::null;
+ }
+
+ return result["new_asset"].asUUID();
+}
+
+enum EProfileImageType
+{
+ PROFILE_IMAGE_SL,
+ PROFILE_IMAGE_FL,
+};
+
+void post_profile_image_coro(std::string cap_url, EProfileImageType type, std::string path_to_image, LLHandle<LLPanel> *handle)
+{
+ LLSD data;
+ switch (type)
+ {
+ case PROFILE_IMAGE_SL:
+ data["profile-image-asset"] = "sl_image_id";
+ break;
+ case PROFILE_IMAGE_FL:
+ data["profile-image-asset"] = "fl_image_id";
+ break;
+ }
+
+ LLUUID result = post_profile_image(cap_url, data, path_to_image, handle);
+
+ // reset loading indicator
+ if (!handle->isDead())
+ {
+ switch (type)
+ {
+ case PROFILE_IMAGE_SL:
+ {
+ LLPanelProfileSecondLife* panel = static_cast<LLPanelProfileSecondLife*>(handle->get());
+ if (result.notNull())
+ {
+ panel->setProfileImageUploaded(result);
+ }
+ else
+ {
+ // failure, just stop progress indicator
+ panel->setProfileImageUploading(false);
+ }
+ break;
+ }
+ case PROFILE_IMAGE_FL:
+ {
+ LLPanelProfileFirstLife* panel = static_cast<LLPanelProfileFirstLife*>(handle->get());
+ if (result.notNull())
+ {
+ panel->setProfileImageUploaded(result);
+ }
+ else
+ {
+ // failure, just stop progress indicator
+ panel->setProfileImageUploading(false);
+ }
+ break;
+ }
+ }
+ }
+
+ if (type == PROFILE_IMAGE_SL && result.notNull())
+ {
+ LLAvatarIconIDCache::getInstance()->add(gAgentID, result);
+ // Should trigger callbacks in icon controls
+ LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesRequest(gAgentID);
+ }
+
+ // Cleanup
+ LLFile::remove(path_to_image);
+ delete handle;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// LLProfileHandler
+
+class LLProfileHandler : public LLCommandHandler
+{
+public:
+ // requires trusted browser to trigger
+ LLProfileHandler() : LLCommandHandler("profile", UNTRUSTED_THROTTLE) { }
+
+ bool handle(const LLSD& params,
+ const LLSD& query_map,
+ const std::string& grid,
+ LLMediaCtrl* web)
+ {
+ if (params.size() < 1) return false;
+ std::string agent_name = params[0];
+ LL_INFOS() << "Profile, agent_name " << agent_name << LL_ENDL;
+ std::string url = getProfileURL(agent_name);
+ LLWeb::loadURLInternal(url);
+
+ return true;
+ }
+};
+LLProfileHandler gProfileHandler;
+
+
+//////////////////////////////////////////////////////////////////////////
+// LLAgentHandler
+
+class LLAgentHandler : public LLCommandHandler
+{
+public:
+ // requires trusted browser to trigger
+ LLAgentHandler() : LLCommandHandler("agent", UNTRUSTED_THROTTLE) { }
+
+ virtual bool canHandleUntrusted(
+ const LLSD& params,
+ const LLSD& query_map,
+ LLMediaCtrl* web,
+ const std::string& nav_type)
+ {
+ if (params.size() < 2)
+ {
+ return true; // don't block, will fail later
+ }
+
+ if (nav_type == NAV_TYPE_CLICKED
+ || nav_type == NAV_TYPE_EXTERNAL)
+ {
+ return true;
+ }
+
+ const std::string verb = params[1].asString();
+ if (verb == "about" || verb == "inspect" || verb == "reportAbuse")
+ {
+ return true;
+ }
+ return false;
+ }
+
+ bool handle(const LLSD& params,
+ const LLSD& query_map,
+ const std::string& grid,
+ LLMediaCtrl* web)
+ {
+ if (params.size() < 2) return false;
+ LLUUID avatar_id;
+ if (!avatar_id.set(params[0], false))
+ {
+ return false;
+ }
+
+ const std::string verb = params[1].asString();
+ if (verb == "about")
+ {
+ LLAvatarActions::showProfile(avatar_id);
+ return true;
+ }
+
+ if (verb == "inspect")
+ {
+ LLFloaterReg::showInstance("inspect_avatar", LLSD().with("avatar_id", avatar_id));
+ return true;
+ }
+
+ if (verb == "im")
+ {
+ LLAvatarActions::startIM(avatar_id);
+ return true;
+ }
+
+ if (verb == "pay")
+ {
+ if (!LLUI::getInstance()->mSettingGroups["config"]->getBOOL("EnableAvatarPay"))
+ {
+ LLNotificationsUtil::add("NoAvatarPay", LLSD(), LLSD(), std::string("SwitchToStandardSkinAndQuit"));
+ return true;
+ }
+
+ LLAvatarActions::pay(avatar_id);
+ return true;
+ }
+
+ if (verb == "offerteleport")
+ {
+ LLAvatarActions::offerTeleport(avatar_id);
+ return true;
+ }
+
+ if (verb == "requestfriend")
+ {
+ LLAvatarActions::requestFriendshipDialog(avatar_id);
+ return true;
+ }
+
+ if (verb == "removefriend")
+ {
+ LLAvatarActions::removeFriendDialog(avatar_id);
+ return true;
+ }
+
+ if (verb == "mute")
+ {
+ if (! LLAvatarActions::isBlocked(avatar_id))
+ {
+ LLAvatarActions::toggleBlock(avatar_id);
+ }
+ return true;
+ }
+
+ if (verb == "unmute")
+ {
+ if (LLAvatarActions::isBlocked(avatar_id))
+ {
+ LLAvatarActions::toggleBlock(avatar_id);
+ }
+ return true;
+ }
+
+ if (verb == "block")
+ {
+ if (params.size() > 2)
+ {
+ const std::string object_name = LLURI::unescape(params[2].asString());
+ LLMute mute(avatar_id, object_name, LLMute::OBJECT);
+ LLMuteList::getInstance()->add(mute);
+ LLPanelBlockedList::showPanelAndSelect(mute.mID);
+ }
+ return true;
+ }
+
+ if (verb == "unblock")
+ {
+ if (params.size() > 2)
+ {
+ const std::string object_name = params[2].asString();
+ LLMute mute(avatar_id, object_name, LLMute::OBJECT);
+ LLMuteList::getInstance()->remove(mute);
+ }
+ return true;
+ }
+
+ // reportAbuse is here due to convoluted avatar handling
+ // in LLScrollListCtrl and LLTextBase
+ if (verb == "reportAbuse" && web == NULL)
+ {
+ LLAvatarName av_name;
+ if (LLAvatarNameCache::get(avatar_id, &av_name))
+ {
+ LLFloaterReporter::showFromAvatar(avatar_id, av_name.getCompleteName());
+ }
+ else
+ {
+ LLFloaterReporter::showFromAvatar(avatar_id, "not avaliable");
+ }
+ return true;
+ }
+ return false;
+ }
+};
+LLAgentHandler gAgentHandler;
+
+
+///----------------------------------------------------------------------------
+/// LLFloaterProfilePermissions
+///----------------------------------------------------------------------------
+
+class LLFloaterProfilePermissions
+ : public LLFloater
+ , public LLFriendObserver
+{
+public:
+ LLFloaterProfilePermissions(LLView * owner, const LLUUID &avatar_id);
+ ~LLFloaterProfilePermissions();
+ bool postBuild() override;
+ void onOpen(const LLSD& key) override;
+ void draw() override;
+ void changed(U32 mask) override; // LLFriendObserver
+
+ void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name);
+ bool hasUnsavedChanges() { return mHasUnsavedPermChanges; }
+
+ void onApplyRights();
+
+private:
+ void fillRightsData();
+ void rightsConfirmationCallback(const LLSD& notification, const LLSD& response);
+ void confirmModifyRights(bool grant);
+ void onCommitSeeOnlineRights();
+ void onCommitEditRights();
+ void onCancel();
+
+ LLTextBase* mDescription;
+ LLCheckBoxCtrl* mOnlineStatus;
+ LLCheckBoxCtrl* mMapRights;
+ LLCheckBoxCtrl* mEditObjectRights;
+ LLButton* mOkBtn;
+ LLButton* mCancelBtn;
+
+ LLUUID mAvatarID;
+ F32 mContextConeOpacity;
+ bool mHasUnsavedPermChanges;
+ LLHandle<LLView> mOwnerHandle;
+
+ boost::signals2::connection mAvatarNameCacheConnection;
+};
+
+LLFloaterProfilePermissions::LLFloaterProfilePermissions(LLView * owner, const LLUUID &avatar_id)
+ : LLFloater(LLSD())
+ , mAvatarID(avatar_id)
+ , mContextConeOpacity(0.0f)
+ , mHasUnsavedPermChanges(false)
+ , mOwnerHandle(owner->getHandle())
+{
+ buildFromFile("floater_profile_permissions.xml");
+}
+
+LLFloaterProfilePermissions::~LLFloaterProfilePermissions()
+{
+ mAvatarNameCacheConnection.disconnect();
+ if (mAvatarID.notNull())
+ {
+ LLAvatarTracker::instance().removeParticularFriendObserver(mAvatarID, this);
+ }
+}
+
+bool LLFloaterProfilePermissions::postBuild()
+{
+ mDescription = getChild<LLTextBase>("perm_description");
+ mOnlineStatus = getChild<LLCheckBoxCtrl>("online_check");
+ mMapRights = getChild<LLCheckBoxCtrl>("map_check");
+ mEditObjectRights = getChild<LLCheckBoxCtrl>("objects_check");
+ mOkBtn = getChild<LLButton>("perms_btn_ok");
+ mCancelBtn = getChild<LLButton>("perms_btn_cancel");
+
+ mOnlineStatus->setCommitCallback([this](LLUICtrl*, void*) { onCommitSeeOnlineRights(); }, nullptr);
+ mMapRights->setCommitCallback([this](LLUICtrl*, void*) { mHasUnsavedPermChanges = true; }, nullptr);
+ mEditObjectRights->setCommitCallback([this](LLUICtrl*, void*) { onCommitEditRights(); }, nullptr);
+ mOkBtn->setCommitCallback([this](LLUICtrl*, void*) { onApplyRights(); }, nullptr);
+ mCancelBtn->setCommitCallback([this](LLUICtrl*, void*) { onCancel(); }, nullptr);
+
+ return true;
+}
+
+void LLFloaterProfilePermissions::onOpen(const LLSD& key)
+{
+ if (LLAvatarActions::isFriend(mAvatarID))
+ {
+ LLAvatarTracker::instance().addParticularFriendObserver(mAvatarID, this);
+ fillRightsData();
+ }
+
+ mCancelBtn->setFocus(true);
+
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(mAvatarID, boost::bind(&LLFloaterProfilePermissions::onAvatarNameCache, this, _1, _2));
+}
+
+void LLFloaterProfilePermissions::draw()
+{
+ // drawFrustum
+ LLView *owner = mOwnerHandle.get();
+ static LLCachedControl<F32> max_opacity(gSavedSettings, "PickerContextOpacity", 0.4f);
+ drawConeToOwner(mContextConeOpacity, max_opacity, owner);
+ LLFloater::draw();
+}
+
+void LLFloaterProfilePermissions::changed(U32 mask)
+{
+ if (mask != LLFriendObserver::ONLINE)
+ {
+ fillRightsData();
+ }
+}
+
+void LLFloaterProfilePermissions::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name)
+{
+ mAvatarNameCacheConnection.disconnect();
+
+ LLStringUtil::format_map_t args;
+ args["[AGENT_NAME]"] = av_name.getDisplayName();
+ std::string descritpion = getString("description_string", args);
+ mDescription->setValue(descritpion);
+}
+
+void LLFloaterProfilePermissions::fillRightsData()
+{
+ const LLRelationship* relation = LLAvatarTracker::instance().getBuddyInfo(mAvatarID);
+ // If true - we are viewing friend's profile, enable check boxes and set values.
+ if (relation)
+ {
+ S32 rights = relation->getRightsGrantedTo();
+
+ bool see_online = LLRelationship::GRANT_ONLINE_STATUS & rights;
+ mOnlineStatus->setValue(see_online);
+ mMapRights->setEnabled(see_online);
+ mMapRights->setValue(LLRelationship::GRANT_MAP_LOCATION & rights);
+ mEditObjectRights->setValue(LLRelationship::GRANT_MODIFY_OBJECTS & rights);
+ }
+ else
+ {
+ closeFloater();
+ LL_INFOS("ProfilePermissions") << "Floater closing since agent is no longer a friend" << LL_ENDL;
+ }
+}
+
+void LLFloaterProfilePermissions::rightsConfirmationCallback(const LLSD& notification,
+ const LLSD& response)
+{
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+ if (option != 0) // canceled
+ {
+ mEditObjectRights->setValue(!mEditObjectRights->getValue().asBoolean());
+ }
+ else
+ {
+ mHasUnsavedPermChanges = true;
+ }
+}
+
+void LLFloaterProfilePermissions::confirmModifyRights(bool grant)
+{
+ LLSD args;
+ args["NAME"] = LLSLURL("agent", mAvatarID, "completename").getSLURLString();
+ LLNotificationsUtil::add(grant ? "GrantModifyRights" : "RevokeModifyRights", args, LLSD(),
+ boost::bind(&LLFloaterProfilePermissions::rightsConfirmationCallback, this, _1, _2));
+}
+
+void LLFloaterProfilePermissions::onCommitSeeOnlineRights()
+{
+ bool see_online = mOnlineStatus->getValue().asBoolean();
+ mMapRights->setEnabled(see_online);
+ if (see_online)
+ {
+ const LLRelationship* relation = LLAvatarTracker::instance().getBuddyInfo(mAvatarID);
+ if (relation)
+ {
+ S32 rights = relation->getRightsGrantedTo();
+ mMapRights->setValue(LLRelationship::GRANT_MAP_LOCATION & rights);
+ }
+ else
+ {
+ closeFloater();
+ LL_INFOS("ProfilePermissions") << "Floater closing since agent is no longer a friend" << LL_ENDL;
+ }
+ }
+ else
+ {
+ mMapRights->setValue(false);
+ }
+ mHasUnsavedPermChanges = true;
+}
+
+void LLFloaterProfilePermissions::onCommitEditRights()
+{
+ const LLRelationship* buddy_relationship = LLAvatarTracker::instance().getBuddyInfo(mAvatarID);
+
+ if (!buddy_relationship)
+ {
+ LL_WARNS("ProfilePermissions") << "Trying to modify rights for non-friend avatar. Closing floater." << LL_ENDL;
+ closeFloater();
+ return;
+ }
+
+ bool allow_modify_objects = mEditObjectRights->getValue().asBoolean();
+
+ // if modify objects checkbox clicked
+ if (buddy_relationship->isRightGrantedTo(
+ LLRelationship::GRANT_MODIFY_OBJECTS) != allow_modify_objects)
+ {
+ confirmModifyRights(allow_modify_objects);
+ }
+}
+
+void LLFloaterProfilePermissions::onApplyRights()
+{
+ const LLRelationship* buddy_relationship = LLAvatarTracker::instance().getBuddyInfo(mAvatarID);
+
+ if (!buddy_relationship)
+ {
+ LL_WARNS("ProfilePermissions") << "Trying to modify rights for non-friend avatar. Skipped." << LL_ENDL;
+ return;
+ }
+
+ S32 rights = 0;
+
+ if (mOnlineStatus->getValue().asBoolean())
+ {
+ rights |= LLRelationship::GRANT_ONLINE_STATUS;
+ }
+ if (mMapRights->getValue().asBoolean())
+ {
+ rights |= LLRelationship::GRANT_MAP_LOCATION;
+ }
+ if (mEditObjectRights->getValue().asBoolean())
+ {
+ rights |= LLRelationship::GRANT_MODIFY_OBJECTS;
+ }
+
+ LLAvatarPropertiesProcessor::getInstance()->sendFriendRights(mAvatarID, rights);
+
+ closeFloater();
+}
+
+void LLFloaterProfilePermissions::onCancel()
+{
+ closeFloater();
+}
+
+//////////////////////////////////////////////////////////////////////////
+// LLPanelProfileSecondLife
+
+LLPanelProfileSecondLife::LLPanelProfileSecondLife()
+ : LLPanelProfilePropertiesProcessorTab()
+ , mAvatarNameCacheConnection()
+ , mHasUnsavedDescriptionChanges(false)
+ , mWaitingForImageUpload(false)
+ , mAllowPublish(false)
+ , mHideAge(false)
+{
+}
+
+LLPanelProfileSecondLife::~LLPanelProfileSecondLife()
+{
+ if (getAvatarId().notNull())
+ {
+ LLAvatarTracker::instance().removeParticularFriendObserver(getAvatarId(), this);
+ }
+
+ if (LLVoiceClient::instanceExists())
+ {
+ LLVoiceClient::getInstance()->removeObserver((LLVoiceClientStatusObserver*)this);
+ }
+
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+}
+
+bool LLPanelProfileSecondLife::postBuild()
+{
+ mGroupList = getChild<LLGroupList>("group_list");
+ mShowInSearchCombo = getChild<LLComboBox>("show_in_search");
+ mHideAgeCombo = getChild<LLComboBox>("hide_age");
+ mSecondLifePic = getChild<LLProfileImageCtrl>("2nd_life_pic");
+ mSecondLifePicLayout = getChild<LLPanel>("image_panel");
+ mDescriptionEdit = getChild<LLTextEditor>("sl_description_edit");
+ mAgentActionMenuButton = getChild<LLMenuButton>("agent_actions_menu");
+ mSaveDescriptionChanges = getChild<LLButton>("save_description_changes");
+ mDiscardDescriptionChanges = getChild<LLButton>("discard_description_changes");
+ mCanSeeOnlineIcon = getChild<LLIconCtrl>("can_see_online");
+ mCantSeeOnlineIcon = getChild<LLIconCtrl>("cant_see_online");
+ mCanSeeOnMapIcon = getChild<LLIconCtrl>("can_see_on_map");
+ mCantSeeOnMapIcon = getChild<LLIconCtrl>("cant_see_on_map");
+ mCanEditObjectsIcon = getChild<LLIconCtrl>("can_edit_objects");
+ mCantEditObjectsIcon = getChild<LLIconCtrl>("cant_edit_objects");
+
+ mShowInSearchCombo->setCommitCallback([this](LLUICtrl*, void*) { onShowInSearchCallback(); }, nullptr);
+ mHideAgeCombo->setCommitCallback([this](LLUICtrl*, void*) { onHideAgeCallback(); }, nullptr);
+ mGroupList->setDoubleClickCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { LLPanelProfileSecondLife::openGroupProfile(); });
+ mGroupList->setReturnCallback([this](LLUICtrl*, const LLSD&) { LLPanelProfileSecondLife::openGroupProfile(); });
+ mSaveDescriptionChanges->setCommitCallback([this](LLUICtrl*, void*) { onSaveDescriptionChanges(); }, nullptr);
+ mDiscardDescriptionChanges->setCommitCallback([this](LLUICtrl*, void*) { onDiscardDescriptionChanges(); }, nullptr);
+ mDescriptionEdit->setKeystrokeCallback([this](LLTextEditor* caller) { onSetDescriptionDirty(); });
+
+ mCanSeeOnlineIcon->setMouseUpCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { onShowAgentPermissionsDialog(); });
+ mCantSeeOnlineIcon->setMouseUpCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { onShowAgentPermissionsDialog(); });
+ mCanSeeOnMapIcon->setMouseUpCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { onShowAgentPermissionsDialog(); });
+ mCantSeeOnMapIcon->setMouseUpCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { onShowAgentPermissionsDialog(); });
+ mCanEditObjectsIcon->setMouseUpCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { onShowAgentPermissionsDialog(); });
+ mCantEditObjectsIcon->setMouseUpCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { onShowAgentPermissionsDialog(); });
+ mSecondLifePic->setMouseUpCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { onShowAgentProfileTexture(); });
+
+ return true;
+}
+
+void LLPanelProfileSecondLife::onOpen(const LLSD& key)
+{
+ LLPanelProfileTab::onOpen(key);
+
+ resetData();
+
+ LLUUID avatar_id = getAvatarId();
+
+ bool own_profile = getSelfProfile();
+
+ mGroupList->setShowNone(!own_profile);
+
+ childSetVisible("notes_panel", !own_profile);
+ childSetVisible("settings_panel", own_profile);
+ childSetVisible("about_buttons_panel", own_profile);
+
+ if (own_profile)
+ {
+ // Group list control cannot toggle ForAgent loading
+ // Less than ideal, but viewing own profile via search is edge case
+ mGroupList->enableForAgent(false);
+ }
+
+ // Init menu, menu needs to be created in scope of a registar to work correctly.
+ LLUICtrl::CommitCallbackRegistry::ScopedRegistrar commit;
+ commit.add("Profile.Commit", [this](LLUICtrl*, const LLSD& userdata) { onCommitMenu(userdata); });
+
+ LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable;
+ enable.add("Profile.EnableItem", [this](LLUICtrl*, const LLSD& userdata) { return onEnableMenu(userdata); });
+ enable.add("Profile.CheckItem", [this](LLUICtrl*, const LLSD& userdata) { return onCheckMenu(userdata); });
+
+ if (own_profile)
+ {
+ mAgentActionMenuButton->setMenu("menu_profile_self.xml", LLMenuButton::MP_BOTTOM_RIGHT);
+ }
+ else
+ {
+ // Todo: use PeopleContextMenu instead?
+ mAgentActionMenuButton->setMenu("menu_profile_other.xml", LLMenuButton::MP_BOTTOM_RIGHT);
+ }
+
+ mDescriptionEdit->setParseHTML(!own_profile);
+
+ if (!own_profile)
+ {
+ mVoiceStatus = LLAvatarActions::canCall() && (LLAvatarActions::isFriend(avatar_id) ? LLAvatarTracker::instance().isBuddyOnline(avatar_id) : true);
+ updateOnlineStatus();
+ fillRightsData();
+ }
+
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(getAvatarId(), boost::bind(&LLPanelProfileSecondLife::onAvatarNameCache, this, _1, _2));
+}
+
+
+bool LLPanelProfileSecondLife::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+ // Try children first
+ if (LLPanelProfileTab::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg)
+ && *accept != ACCEPT_NO)
+ {
+ return true;
+ }
+
+ // No point sharing with own profile
+ if (getSelfProfile())
+ {
+ return false;
+ }
+
+ // Exclude fields that look like they are editable.
+ S32 child_x = 0;
+ S32 child_y = 0;
+ if (localPointToOtherView(x, y, &child_x, &child_y, mDescriptionEdit)
+ && mDescriptionEdit->pointInView(child_x, child_y))
+ {
+ return false;
+ }
+
+ if (localPointToOtherView(x, y, &child_x, &child_y, mGroupList)
+ && mGroupList->pointInView(child_x, child_y))
+ {
+ return false;
+ }
+
+ // Share
+ LLToolDragAndDrop::handleGiveDragAndDrop(getAvatarId(),
+ LLUUID::null,
+ drop,
+ cargo_type,
+ cargo_data,
+ accept);
+ return true;
+}
+
+void LLPanelProfileSecondLife::refreshName()
+{
+ if (!mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(getAvatarId(), boost::bind(&LLPanelProfileSecondLife::onAvatarNameCache, this, _1, _2));
+ }
+}
+
+void LLPanelProfileSecondLife::resetData()
+{
+ resetLoading();
+
+ // Set default image and 1:1 dimensions for it
+ mSecondLifePic->setValue("Generic_Person_Large");
+
+ LLRect imageRect = mSecondLifePicLayout->getRect();
+ mSecondLifePicLayout->reshape(imageRect.getWidth(), imageRect.getWidth());
+
+ setDescriptionText(LLStringUtil::null);
+ mGroups.clear();
+ mGroupList->setGroups(mGroups);
+
+ bool own_profile = getSelfProfile();
+ mCanSeeOnlineIcon->setVisible(false);
+ mCantSeeOnlineIcon->setVisible(!own_profile);
+ mCanSeeOnMapIcon->setVisible(false);
+ mCantSeeOnMapIcon->setVisible(!own_profile);
+ mCanEditObjectsIcon->setVisible(false);
+ mCantEditObjectsIcon->setVisible(!own_profile);
+
+ mCanSeeOnlineIcon->setEnabled(false);
+ mCantSeeOnlineIcon->setEnabled(false);
+ mCanSeeOnMapIcon->setEnabled(false);
+ mCantSeeOnMapIcon->setEnabled(false);
+ mCanEditObjectsIcon->setEnabled(false);
+ mCantEditObjectsIcon->setEnabled(false);
+
+ childSetVisible("partner_layout", false);
+ childSetVisible("badge_layout", false);
+ childSetVisible("partner_spacer_layout", true);
+}
+
+void LLPanelProfileSecondLife::processProperties(void* data, EAvatarProcessorType type)
+{
+ if (APT_PROPERTIES == type)
+ {
+ LLAvatarData* avatar_data = static_cast<LLAvatarData*>(data);
+ if (avatar_data && getAvatarId() == avatar_data->avatar_id)
+ {
+ processProfileProperties(avatar_data);
+ }
+ }
+}
+
+void LLPanelProfileSecondLife::processProfileProperties(const LLAvatarData* avatar_data)
+{
+ const LLRelationship* relationship = LLAvatarTracker::instance().getBuddyInfo(getAvatarId());
+ if ((relationship != NULL || gAgent.isGodlike()) && !getSelfProfile())
+ {
+ // Relies onto friend observer to get information about online status updates.
+ // Once SL-17506 gets implemented, condition might need to become:
+ // (gAgent.isGodlike() || isRightGrantedFrom || flags & AVATAR_ONLINE)
+ processOnlineStatus(relationship != NULL,
+ gAgent.isGodlike() || relationship->isRightGrantedFrom(LLRelationship::GRANT_ONLINE_STATUS),
+ (avatar_data->flags & AVATAR_ONLINE));
+ }
+
+ fillCommonData(avatar_data);
+
+ fillPartnerData(avatar_data);
+
+ fillAccountStatus(avatar_data);
+
+ LLAvatarData::group_list_t::const_iterator it = avatar_data->group_list.begin();
+ const LLAvatarData::group_list_t::const_iterator it_end = avatar_data->group_list.end();
+
+ for (; it_end != it; ++it)
+ {
+ LLAvatarData::LLGroupData group_data = *it;
+ mGroups[group_data.group_name] = group_data.group_id;
+ }
+
+ mGroupList->setGroups(mGroups);
+
+ setLoaded();
+}
+
+void LLPanelProfileSecondLife::openGroupProfile()
+{
+ LLUUID group_id = mGroupList->getSelectedUUID();
+ LLGroupActions::show(group_id);
+}
+
+void LLPanelProfileSecondLife::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name)
+{
+ mAvatarNameCacheConnection.disconnect();
+ getChild<LLUICtrl>("display_name")->setValue(av_name.getDisplayName());
+ getChild<LLUICtrl>("user_name")->setValue(av_name.getAccountName());
+}
+
+void LLPanelProfileSecondLife::setProfileImageUploading(bool loading)
+{
+ LLLoadingIndicator* indicator = getChild<LLLoadingIndicator>("image_upload_indicator");
+ indicator->setVisible(loading);
+ if (loading)
+ {
+ indicator->start();
+ }
+ else
+ {
+ indicator->stop();
+ }
+ mWaitingForImageUpload = loading;
+}
+
+void LLPanelProfileSecondLife::setProfileImageUploaded(const LLUUID &image_asset_id)
+{
+ mSecondLifePic->setValue(image_asset_id);
+
+ LLFloater *floater = mFloaterProfileTextureHandle.get();
+ if (floater)
+ {
+ LLFloaterProfileTexture * texture_view = dynamic_cast<LLFloaterProfileTexture*>(floater);
+ texture_view->loadAsset(mSecondLifePic->getImageAssetId());
+ }
+
+ setProfileImageUploading(false);
+}
+
+bool LLPanelProfileSecondLife::hasUnsavedChanges()
+{
+ LLFloater *floater = mFloaterPermissionsHandle.get();
+ if (floater)
+ {
+ LLFloaterProfilePermissions* perm = dynamic_cast<LLFloaterProfilePermissions*>(floater);
+ if (perm && perm->hasUnsavedChanges())
+ {
+ return true;
+ }
+ }
+ // if floater
+ return mHasUnsavedDescriptionChanges;
+}
+
+void LLPanelProfileSecondLife::commitUnsavedChanges()
+{
+ LLFloater *floater = mFloaterPermissionsHandle.get();
+ if (floater)
+ {
+ LLFloaterProfilePermissions* perm = dynamic_cast<LLFloaterProfilePermissions*>(floater);
+ if (perm && perm->hasUnsavedChanges())
+ {
+ perm->onApplyRights();
+ }
+ }
+ if (mHasUnsavedDescriptionChanges)
+ {
+ onSaveDescriptionChanges();
+ }
+}
+
+void LLPanelProfileSecondLife::fillCommonData(const LLAvatarData* avatar_data)
+{
+ // Refresh avatar id in cache with new info to prevent re-requests
+ // and to make sure icons in text will be up to date
+ LLAvatarIconIDCache::getInstance()->add(avatar_data->avatar_id, avatar_data->image_id);
+
+ fillAgeData(avatar_data);
+
+ setDescriptionText(avatar_data->about_text);
+
+ mSecondLifePic->setValue(avatar_data->image_id);
+
+ if (getSelfProfile())
+ {
+ mAllowPublish = avatar_data->flags & AVATAR_ALLOW_PUBLISH;
+ mShowInSearchCombo->setValue(mAllowPublish);
+ }
+}
+
+void LLPanelProfileSecondLife::fillPartnerData(const LLAvatarData* avatar_data)
+{
+ LLTextBox* partner_text_ctrl = getChild<LLTextBox>("partner_link");
+ if (avatar_data->partner_id.notNull())
+ {
+ childSetVisible("partner_layout", true);
+ LLStringUtil::format_map_t args;
+ args["[LINK]"] = LLSLURL("agent", avatar_data->partner_id, "inspect").getSLURLString();
+ std::string partner_text = getString("partner_text", args);
+ partner_text_ctrl->setText(partner_text);
+ }
+ else
+ {
+ childSetVisible("partner_layout", false);
+ }
+}
+
+void LLPanelProfileSecondLife::fillAccountStatus(const LLAvatarData* avatar_data)
+{
+ LLStringUtil::format_map_t args;
+ args["[ACCTTYPE]"] = LLAvatarPropertiesProcessor::accountType(avatar_data);
+ args["[PAYMENTINFO]"] = LLAvatarPropertiesProcessor::paymentInfo(avatar_data);
+
+ std::string caption_text = getString("CaptionTextAcctInfo", args);
+ getChild<LLUICtrl>("account_info")->setValue(caption_text);
+
+ const S32 LINDEN_EMPLOYEE_INDEX = 3;
+ LLDate sl_release;
+ sl_release.fromYMDHMS(2003, 6, 23, 0, 0, 0);
+ std::string customer_lower = avatar_data->customer_type;
+ LLStringUtil::toLower(customer_lower);
+ if (avatar_data->caption_index == LINDEN_EMPLOYEE_INDEX)
+ {
+ getChild<LLUICtrl>("badge_icon")->setValue("Profile_Badge_Linden");
+ getChild<LLUICtrl>("badge_text")->setValue(getString("BadgeLinden"));
+ childSetVisible("badge_layout", true);
+ childSetVisible("partner_spacer_layout", false);
+ }
+ else if (avatar_data->born_on < sl_release)
+ {
+ getChild<LLUICtrl>("badge_icon")->setValue("Profile_Badge_Beta");
+ getChild<LLUICtrl>("badge_text")->setValue(getString("BadgeBeta"));
+ childSetVisible("badge_layout", true);
+ childSetVisible("partner_spacer_layout", false);
+ }
+ else if (customer_lower == "beta_lifetime")
+ {
+ getChild<LLUICtrl>("badge_icon")->setValue("Profile_Badge_Beta_Lifetime");
+ getChild<LLUICtrl>("badge_text")->setValue(getString("BadgeBetaLifetime"));
+ childSetVisible("badge_layout", true);
+ childSetVisible("partner_spacer_layout", false);
+ }
+ else if (customer_lower == "lifetime")
+ {
+ getChild<LLUICtrl>("badge_icon")->setValue("Profile_Badge_Lifetime");
+ getChild<LLUICtrl>("badge_text")->setValue(getString("BadgeLifetime"));
+ childSetVisible("badge_layout", true);
+ childSetVisible("partner_spacer_layout", false);
+ }
+ else if (customer_lower == "secondlifetime_premium")
+ {
+ getChild<LLUICtrl>("badge_icon")->setValue("Profile_Badge_Premium_Lifetime");
+ getChild<LLUICtrl>("badge_text")->setValue(getString("BadgePremiumLifetime"));
+ childSetVisible("badge_layout", true);
+ childSetVisible("partner_spacer_layout", false);
+ }
+ else if (customer_lower == "secondlifetime_premium_plus")
+ {
+ getChild<LLUICtrl>("badge_icon")->setValue("Profile_Badge_Pplus_Lifetime");
+ getChild<LLUICtrl>("badge_text")->setValue(getString("BadgePremiumPlusLifetime"));
+ childSetVisible("badge_layout", true);
+ childSetVisible("partner_spacer_layout", false);
+ }
+ else
+ {
+ childSetVisible("badge_layout", false);
+ childSetVisible("partner_spacer_layout", true);
+ }
+}
+
+void LLPanelProfileSecondLife::fillRightsData()
+{
+ if (getSelfProfile())
+ {
+ return;
+ }
+
+ const LLRelationship* relation = LLAvatarTracker::instance().getBuddyInfo(getAvatarId());
+ // If true - we are viewing friend's profile, enable check boxes and set values.
+ if (relation)
+ {
+ S32 rights = relation->getRightsGrantedTo();
+ bool can_see_online = LLRelationship::GRANT_ONLINE_STATUS & rights;
+ bool can_see_on_map = LLRelationship::GRANT_MAP_LOCATION & rights;
+ bool can_edit_objects = LLRelationship::GRANT_MODIFY_OBJECTS & rights;
+
+ mCanSeeOnlineIcon->setVisible(can_see_online);
+ mCantSeeOnlineIcon->setVisible(!can_see_online);
+ mCanSeeOnMapIcon->setVisible(can_see_on_map);
+ mCantSeeOnMapIcon->setVisible(!can_see_on_map);
+ mCanEditObjectsIcon->setVisible(can_edit_objects);
+ mCantEditObjectsIcon->setVisible(!can_edit_objects);
+
+ mCanSeeOnlineIcon->setEnabled(true);
+ mCantSeeOnlineIcon->setEnabled(true);
+ mCanSeeOnMapIcon->setEnabled(true);
+ mCantSeeOnMapIcon->setEnabled(true);
+ mCanEditObjectsIcon->setEnabled(true);
+ mCantEditObjectsIcon->setEnabled(true);
+ }
+ else
+ {
+ mCanSeeOnlineIcon->setVisible(false);
+ mCantSeeOnlineIcon->setVisible(false);
+ mCanSeeOnMapIcon->setVisible(false);
+ mCantSeeOnMapIcon->setVisible(false);
+ mCanEditObjectsIcon->setVisible(false);
+ mCantEditObjectsIcon->setVisible(false);
+ }
+}
+
+void LLPanelProfileSecondLife::fillAgeData(const LLAvatarData* avatar_data)
+{
+ // Date from server comes already converted to stl timezone,
+ // so display it as an UTC + 0
+ bool hide_age = avatar_data->hide_age && !getSelfProfile();
+ std::string name_and_date = getString(hide_age ? "date_format_short" : "date_format_full");
+ LLSD args_name;
+ args_name["datetime"] = (S32)avatar_data->born_on.secondsSinceEpoch();
+ LLStringUtil::format(name_and_date, args_name);
+ getChild<LLUICtrl>("sl_birth_date")->setValue(name_and_date);
+
+ LLUICtrl* userAgeCtrl = getChild<LLUICtrl>("user_age");
+ if (hide_age)
+ {
+ userAgeCtrl->setVisible(false);
+ }
+ else
+ {
+ std::string register_date = getString("age_format");
+ LLSD args_age;
+ args_age["[AGE]"] = LLDateUtil::ageFromDate(avatar_data->born_on, LLDate::now());
+ LLStringUtil::format(register_date, args_age);
+ userAgeCtrl->setValue(register_date);
+ }
+
+ bool showHideAgeCombo = false;
+ if (getSelfProfile())
+ {
+ if (LLAvatarPropertiesProcessor::getInstance()->isHideAgeSupportedByServer())
+ {
+ F64 birth = avatar_data->born_on.secondsSinceEpoch();
+ F64 now = LLDate::now().secondsSinceEpoch();
+ if (now - birth > 365 * 24 * 60 * 60)
+ {
+ mHideAge = avatar_data->hide_age;
+ mHideAgeCombo->setValue(mHideAge);
+ showHideAgeCombo = true;
+ }
+ }
+ }
+ mHideAgeCombo->setVisible(showHideAgeCombo);
+}
+
+void LLPanelProfileSecondLife::onImageLoaded(bool success, LLViewerFetchedTexture *imagep)
+{
+ LLRect imageRect = mSecondLifePicLayout->getRect();
+ if (!success || imagep->getFullWidth() == imagep->getFullHeight())
+ {
+ mSecondLifePicLayout->reshape(imageRect.getWidth(), imageRect.getWidth());
+ }
+ else
+ {
+ // assume 3:4, for sake of firestorm
+ mSecondLifePicLayout->reshape(imageRect.getWidth(), imageRect.getWidth() * 3 / 4);
+ }
+}
+
+// virtual, called by LLAvatarTracker
+void LLPanelProfileSecondLife::changed(U32 mask)
+{
+ updateOnlineStatus();
+ if (mask != LLFriendObserver::ONLINE)
+ {
+ fillRightsData();
+ }
+}
+
+// virtual, called by LLVoiceClient
+void LLPanelProfileSecondLife::onChange(EStatusType status, const std::string &channelURI, bool proximal)
+{
+ if(status == STATUS_JOINING || status == STATUS_LEFT_CHANNEL)
+ {
+ return;
+ }
+
+ mVoiceStatus = LLAvatarActions::canCall() && (LLAvatarActions::isFriend(getAvatarId()) ? LLAvatarTracker::instance().isBuddyOnline(getAvatarId()) : true);
+}
+
+void LLPanelProfileSecondLife::setAvatarId(const LLUUID& avatar_id)
+{
+ if (avatar_id.notNull())
+ {
+ if (getAvatarId().notNull())
+ {
+ LLAvatarTracker::instance().removeParticularFriendObserver(getAvatarId(), this);
+ }
+
+ LLPanelProfilePropertiesProcessorTab::setAvatarId(avatar_id);
+
+ if (LLAvatarActions::isFriend(getAvatarId()))
+ {
+ LLAvatarTracker::instance().addParticularFriendObserver(getAvatarId(), this);
+ }
+ }
+}
+
+// method was disabled according to EXT-2022. Re-enabled & improved according to EXT-3880
+void LLPanelProfileSecondLife::updateOnlineStatus()
+{
+ const LLRelationship* relationship = LLAvatarTracker::instance().getBuddyInfo(getAvatarId());
+ if (relationship != NULL)
+ {
+ // For friend let check if he allowed me to see his status
+ bool online = relationship->isOnline();
+ bool perm_granted = relationship->isRightGrantedFrom(LLRelationship::GRANT_ONLINE_STATUS);
+ processOnlineStatus(true, perm_granted, online);
+ }
+ else
+ {
+ childSetVisible("friend_layout", false);
+ childSetVisible("online_layout", false);
+ childSetVisible("offline_layout", false);
+ }
+}
+
+void LLPanelProfileSecondLife::processOnlineStatus(bool is_friend, bool show_online, bool online)
+{
+ childSetVisible("friend_layout", is_friend);
+ childSetVisible("online_layout", online && show_online);
+ childSetVisible("offline_layout", !online && show_online);
+}
+
+void LLPanelProfileSecondLife::setLoaded()
+{
+ LLPanelProfileTab::setLoaded();
+
+ if (getSelfProfile())
+ {
+ mShowInSearchCombo->setEnabled(true);
+ if (mHideAgeCombo->getVisible())
+ {
+ mHideAgeCombo->setEnabled(true);
+ }
+ mDescriptionEdit->setEnabled(true);
+ }
+}
+
+
+class LLProfileImagePicker : public LLFilePickerThread
+{
+public:
+ LLProfileImagePicker(EProfileImageType type, LLHandle<LLPanel> *handle);
+ ~LLProfileImagePicker();
+ void notify(const std::vector<std::string>& filenames) override;
+
+private:
+ LLHandle<LLPanel> *mHandle;
+ EProfileImageType mType;
+};
+
+LLProfileImagePicker::LLProfileImagePicker(EProfileImageType type, LLHandle<LLPanel> *handle)
+ : LLFilePickerThread(LLFilePicker::FFLOAD_IMAGE),
+ mHandle(handle),
+ mType(type)
+{
+}
+
+LLProfileImagePicker::~LLProfileImagePicker()
+{
+ delete mHandle;
+}
+
+void LLProfileImagePicker::notify(const std::vector<std::string>& filenames)
+{
+ if (mHandle->isDead())
+ {
+ return;
+ }
+ if (filenames.empty())
+ {
+ return;
+ }
+ std::string file_path = filenames[0];
+ if (file_path.empty())
+ {
+ return;
+ }
+
+ // generate a temp texture file for coroutine
+ std::string temp_file = gDirUtilp->getTempFilename();
+ U32 codec = LLImageBase::getCodecFromExtension(gDirUtilp->getExtension(file_path));
+ const S32 MAX_DIM = 256;
+ if (!LLViewerTextureList::createUploadFile(file_path, temp_file, codec, MAX_DIM))
+ {
+ LLSD notif_args;
+ notif_args["REASON"] = LLImage::getLastThreadError().c_str();
+ LLNotificationsUtil::add("CannotUploadTexture", notif_args);
+ LL_WARNS("AvatarProperties") << "Failed to upload profile image of type " << (S32)mType << ", " << notif_args["REASON"].asString() << LL_ENDL;
+ return;
+ }
+
+ std::string cap_url = gAgent.getRegionCapability(PROFILE_IMAGE_UPLOAD_CAP);
+ if (cap_url.empty())
+ {
+ LLSD args;
+ args["CAPABILITY"] = PROFILE_IMAGE_UPLOAD_CAP;
+ LLNotificationsUtil::add("RegionCapabilityRequestError", args);
+ LL_WARNS("AvatarProperties") << "Failed to upload profile image of type " << (S32)mType << ", no cap found" << LL_ENDL;
+ return;
+ }
+
+ switch (mType)
+ {
+ case PROFILE_IMAGE_SL:
+ {
+ LLPanelProfileSecondLife* panel = static_cast<LLPanelProfileSecondLife*>(mHandle->get());
+ panel->setProfileImageUploading(true);
+ }
+ break;
+ case PROFILE_IMAGE_FL:
+ {
+ LLPanelProfileFirstLife* panel = static_cast<LLPanelProfileFirstLife*>(mHandle->get());
+ panel->setProfileImageUploading(true);
+ }
+ break;
+ }
+
+ LLCoros::instance().launch("postAgentUserImageCoro",
+ boost::bind(post_profile_image_coro, cap_url, mType, temp_file, mHandle));
+
+ mHandle = nullptr; // transferred to post_profile_image_coro
+}
+
+void LLPanelProfileSecondLife::onCommitMenu(const LLSD& userdata)
+{
+ const std::string item_name = userdata.asString();
+ const LLUUID agent_id = getAvatarId();
+ // todo: consider moving this into LLAvatarActions::onCommit(name, id)
+ // and making all other flaoters, like people menu do the same
+ if (item_name == "im")
+ {
+ LLAvatarActions::startIM(agent_id);
+ }
+ else if (item_name == "offer_teleport")
+ {
+ LLAvatarActions::offerTeleport(agent_id);
+ }
+ else if (item_name == "request_teleport")
+ {
+ LLAvatarActions::teleportRequest(agent_id);
+ }
+ else if (item_name == "voice_call")
+ {
+ LLAvatarActions::startCall(agent_id);
+ }
+ else if (item_name == "chat_history")
+ {
+ LLAvatarActions::viewChatHistory(agent_id);
+ }
+ else if (item_name == "add_friend")
+ {
+ LLAvatarActions::requestFriendshipDialog(agent_id);
+ }
+ else if (item_name == "remove_friend")
+ {
+ LLAvatarActions::removeFriendDialog(agent_id);
+ }
+ else if (item_name == "invite_to_group")
+ {
+ LLAvatarActions::inviteToGroup(agent_id);
+ }
+ else if (item_name == "can_show_on_map")
+ {
+ LLAvatarActions::showOnMap(agent_id);
+ }
+ else if (item_name == "share")
+ {
+ LLAvatarActions::share(agent_id);
+ }
+ else if (item_name == "pay")
+ {
+ LLAvatarActions::pay(agent_id);
+ }
+ else if (item_name == "toggle_block_agent")
+ {
+ LLAvatarActions::toggleBlock(agent_id);
+ }
+ else if (item_name == "copy_user_id")
+ {
+ LLWString wstr = utf8str_to_wstring(getAvatarId().asString());
+ LLClipboard::instance().copyToClipboard(wstr, 0, wstr.size());
+ }
+ else if (item_name == "agent_permissions")
+ {
+ onShowAgentPermissionsDialog();
+ }
+ else if (item_name == "copy_display_name"
+ || item_name == "copy_username")
+ {
+ LLAvatarName av_name;
+ if (!LLAvatarNameCache::get(getAvatarId(), &av_name))
+ {
+ // shouldn't happen, option is supposed to be invisible while name is fetching
+ LL_WARNS() << "Failed to get agent data" << LL_ENDL;
+ return;
+ }
+ LLWString wstr;
+ if (item_name == "copy_display_name")
+ {
+ wstr = utf8str_to_wstring(av_name.getDisplayName(true));
+ }
+ else if (item_name == "copy_username")
+ {
+ wstr = utf8str_to_wstring(av_name.getUserName());
+ }
+ LLClipboard::instance().copyToClipboard(wstr, 0, wstr.size());
+ }
+ else if (item_name == "edit_display_name")
+ {
+ LLAvatarNameCache::get(getAvatarId(), boost::bind(&LLPanelProfileSecondLife::onAvatarNameCacheSetName, this, _1, _2));
+ LLFirstUse::setDisplayName(false);
+ }
+ else if (item_name == "edit_partner")
+ {
+ std::string url = "https://[GRID]/my/account/partners.php";
+ LLSD subs;
+ url = LLWeb::expandURLSubstitutions(url, subs);
+ LLUrlAction::openURL(url);
+ }
+ else if (item_name == "upload_photo")
+ {
+ (new LLProfileImagePicker(PROFILE_IMAGE_SL, new LLHandle<LLPanel>(LLPanel::getHandle())))->getFile();
+
+ LLFloater* floaterp = mFloaterTexturePickerHandle.get();
+ if (floaterp)
+ {
+ floaterp->closeFloater();
+ }
+ }
+ else if (item_name == "change_photo")
+ {
+ onShowTexturePicker();
+ }
+ else if (item_name == "remove_photo")
+ {
+ onCommitProfileImage(LLUUID::null);
+
+ LLFloater* floaterp = mFloaterTexturePickerHandle.get();
+ if (floaterp)
+ {
+ floaterp->closeFloater();
+ }
+ }
+}
+
+bool LLPanelProfileSecondLife::onEnableMenu(const LLSD& userdata)
+{
+ const std::string item_name = userdata.asString();
+ const LLUUID agent_id = getAvatarId();
+ if (item_name == "offer_teleport" || item_name == "request_teleport")
+ {
+ return LLAvatarActions::canOfferTeleport(agent_id);
+ }
+ else if (item_name == "voice_call")
+ {
+ return mVoiceStatus;
+ }
+ else if (item_name == "chat_history")
+ {
+ return LLLogChat::isTranscriptExist(agent_id);
+ }
+ else if (item_name == "add_friend")
+ {
+ return !LLAvatarActions::isFriend(agent_id);
+ }
+ else if (item_name == "remove_friend")
+ {
+ return LLAvatarActions::isFriend(agent_id);
+ }
+ else if (item_name == "can_show_on_map")
+ {
+ return (LLAvatarTracker::instance().isBuddyOnline(agent_id) && is_agent_mappable(agent_id))
+ || gAgent.isGodlike();
+ }
+ else if (item_name == "toggle_block_agent")
+ {
+ return LLAvatarActions::canBlock(agent_id);
+ }
+ else if (item_name == "agent_permissions")
+ {
+ return LLAvatarActions::isFriend(agent_id);
+ }
+ else if (item_name == "copy_display_name"
+ || item_name == "copy_username")
+ {
+ return !mAvatarNameCacheConnection.connected();
+ }
+ else if (item_name == "upload_photo"
+ || item_name == "change_photo")
+ {
+ std::string cap_url = gAgent.getRegionCapability(PROFILE_IMAGE_UPLOAD_CAP);
+ return !cap_url.empty() && !mWaitingForImageUpload && getIsLoaded();
+ }
+ else if (item_name == "remove_photo")
+ {
+ std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP);
+ return mSecondLifePic->getImageAssetId().notNull() && !cap_url.empty() && !mWaitingForImageUpload && getIsLoaded();
+ }
+
+ return false;
+}
+
+bool LLPanelProfileSecondLife::onCheckMenu(const LLSD& userdata)
+{
+ const std::string item_name = userdata.asString();
+ const LLUUID agent_id = getAvatarId();
+ if (item_name == "toggle_block_agent")
+ {
+ return LLAvatarActions::isBlocked(agent_id);
+ }
+ return false;
+}
+
+void LLPanelProfileSecondLife::onAvatarNameCacheSetName(const LLUUID& agent_id, const LLAvatarName& av_name)
+{
+ if (av_name.getDisplayName().empty())
+ {
+ // something is wrong, tell user to try again later
+ LLNotificationsUtil::add("SetDisplayNameFailedGeneric");
+ return;
+ }
+
+ LL_INFOS("LegacyProfile") << "name-change now " << LLDate::now() << " next_update "
+ << LLDate(av_name.mNextUpdate) << LL_ENDL;
+ F64 now_secs = LLDate::now().secondsSinceEpoch();
+
+ if (now_secs < av_name.mNextUpdate)
+ {
+ // if the update time is more than a year in the future, it means updates have been blocked
+ // show a more general message
+ static const S32 YEAR = 60*60*24*365;
+ if (now_secs + YEAR < av_name.mNextUpdate)
+ {
+ LLNotificationsUtil::add("SetDisplayNameBlocked");
+ return;
+ }
+ }
+
+ LLFloaterReg::showInstance("display_name");
+}
+
+void LLPanelProfileSecondLife::setDescriptionText(const std::string &text)
+{
+ mSaveDescriptionChanges->setEnabled(false);
+ mDiscardDescriptionChanges->setEnabled(false);
+ mHasUnsavedDescriptionChanges = false;
+
+ mDescriptionText = text;
+ mDescriptionEdit->setValue(mDescriptionText);
+}
+
+void LLPanelProfileSecondLife::onSetDescriptionDirty()
+{
+ mSaveDescriptionChanges->setEnabled(true);
+ mDiscardDescriptionChanges->setEnabled(true);
+ mHasUnsavedDescriptionChanges = true;
+}
+
+void LLPanelProfileSecondLife::onShowInSearchCallback()
+{
+ bool value = mShowInSearchCombo->getValue().asInteger();
+ if (value == mAllowPublish)
+ return;
+
+ mAllowPublish = value;
+ saveAgentUserInfoCoro("allow_publish", value);
+}
+
+void LLPanelProfileSecondLife::onHideAgeCallback()
+{
+ bool value = mHideAgeCombo->getValue().asInteger();
+ if (value == mHideAge)
+ return;
+
+ mHideAge = value;
+ saveAgentUserInfoCoro("hide_age", value);
+}
+
+void LLPanelProfileSecondLife::onSaveDescriptionChanges()
+{
+ mDescriptionText = mDescriptionEdit->getValue().asString();
+ saveAgentUserInfoCoro("sl_about_text", mDescriptionText);
+
+ mSaveDescriptionChanges->setEnabled(false);
+ mDiscardDescriptionChanges->setEnabled(false);
+ mHasUnsavedDescriptionChanges = false;
+}
+
+void LLPanelProfileSecondLife::onDiscardDescriptionChanges()
+{
+ setDescriptionText(mDescriptionText);
+}
+
+void LLPanelProfileSecondLife::onShowAgentPermissionsDialog()
+{
+ LLFloater *floater = mFloaterPermissionsHandle.get();
+ if (!floater)
+ {
+ LLFloater* parent_floater = gFloaterView->getParentFloater(this);
+ if (parent_floater)
+ {
+ LLFloaterProfilePermissions * perms = new LLFloaterProfilePermissions(parent_floater, getAvatarId());
+ mFloaterPermissionsHandle = perms->getHandle();
+ perms->openFloater();
+ perms->setVisibleAndFrontmost(true);
+
+ parent_floater->addDependentFloater(mFloaterPermissionsHandle);
+ }
+ }
+ else // already open
+ {
+ floater->setMinimized(false);
+ floater->setVisibleAndFrontmost(true);
+ }
+}
+
+void LLPanelProfileSecondLife::onShowAgentProfileTexture()
+{
+ if (!getIsLoaded())
+ {
+ return;
+ }
+
+ LLFloater *floater = mFloaterProfileTextureHandle.get();
+ if (!floater)
+ {
+ LLFloater* parent_floater = gFloaterView->getParentFloater(this);
+ if (parent_floater)
+ {
+ LLFloaterProfileTexture * texture_view = new LLFloaterProfileTexture(parent_floater);
+ mFloaterProfileTextureHandle = texture_view->getHandle();
+ if (mSecondLifePic->getImageAssetId().notNull())
+ {
+ texture_view->loadAsset(mSecondLifePic->getImageAssetId());
+ }
+ else
+ {
+ texture_view->resetAsset();
+ }
+ texture_view->openFloater();
+ texture_view->setVisibleAndFrontmost(true);
+
+ parent_floater->addDependentFloater(mFloaterProfileTextureHandle);
+ }
+ }
+ else // already open
+ {
+ LLFloaterProfileTexture * texture_view = dynamic_cast<LLFloaterProfileTexture*>(floater);
+ texture_view->setMinimized(false);
+ texture_view->setVisibleAndFrontmost(true);
+ if (mSecondLifePic->getImageAssetId().notNull())
+ {
+ texture_view->loadAsset(mSecondLifePic->getImageAssetId());
+ }
+ else
+ {
+ texture_view->resetAsset();
+ }
+ }
+}
+
+void LLPanelProfileSecondLife::onShowTexturePicker()
+{
+ LLFloater* floaterp = mFloaterTexturePickerHandle.get();
+
+ // Show the dialog
+ if (!floaterp)
+ {
+ LLFloater* parent_floater = gFloaterView->getParentFloater(this);
+ if (parent_floater)
+ {
+ // because inventory construction is somewhat slow
+ getWindow()->setCursor(UI_CURSOR_WAIT);
+ LLFloaterTexturePicker* texture_floaterp = new LLFloaterTexturePicker(
+ this,
+ mSecondLifePic->getImageAssetId(),
+ LLUUID::null,
+ mSecondLifePic->getImageAssetId(),
+ false,
+ false,
+ "SELECT PHOTO",
+ PERM_NONE,
+ PERM_NONE,
+ false,
+ NULL,
+ PICK_TEXTURE);
+
+ mFloaterTexturePickerHandle = texture_floaterp->getHandle();
+
+ texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLPickerSource source, const LLUUID& asset_id, const LLUUID&, const LLUUID&)
+ {
+ if (op == LLTextureCtrl::TEXTURE_SELECT)
+ {
+ onCommitProfileImage(asset_id);
+ }
+ });
+ texture_floaterp->setLocalTextureEnabled(false);
+ texture_floaterp->setBakeTextureEnabled(false);
+ texture_floaterp->setCanApply(false, true, false);
+
+ parent_floater->addDependentFloater(mFloaterTexturePickerHandle);
+
+ texture_floaterp->openFloater();
+ texture_floaterp->setFocus(true);
+ }
+ }
+ else
+ {
+ floaterp->setMinimized(false);
+ floaterp->setVisibleAndFrontmost(true);
+ }
+}
+
+void LLPanelProfileSecondLife::onCommitProfileImage(const LLUUID& id)
+{
+ if (mSecondLifePic->getImageAssetId() == id)
+ return;
+
+ std::function<void(bool)> callback = [id](bool result)
+ {
+ if (result)
+ {
+ LLAvatarIconIDCache::getInstance()->add(gAgentID, id);
+ // Should trigger callbacks in icon controls (or request Legacy)
+ LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesRequest(gAgentID);
+ }
+ };
+
+ if (!saveAgentUserInfoCoro("sl_image_id", id, callback))
+ return;
+
+ mSecondLifePic->setValue(id);
+
+ LLFloater *floater = mFloaterProfileTextureHandle.get();
+ if (floater)
+ {
+ LLFloaterProfileTexture * texture_view = dynamic_cast<LLFloaterProfileTexture*>(floater);
+ if (id == LLUUID::null)
+ {
+ texture_view->resetAsset();
+ }
+ else
+ {
+ texture_view->loadAsset(id);
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// LLPanelProfileWeb
+
+LLPanelProfileWeb::LLPanelProfileWeb()
+ : LLPanelProfileTab()
+ , mWebBrowser(NULL)
+ , mAvatarNameCacheConnection()
+{
+}
+
+LLPanelProfileWeb::~LLPanelProfileWeb()
+{
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+}
+
+void LLPanelProfileWeb::onOpen(const LLSD& key)
+{
+ LLPanelProfileTab::onOpen(key);
+
+ resetData();
+
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(getAvatarId(), boost::bind(&LLPanelProfileWeb::onAvatarNameCache, this, _1, _2));
+}
+
+bool LLPanelProfileWeb::postBuild()
+{
+ mWebBrowser = getChild<LLMediaCtrl>("profile_html");
+ mWebBrowser->addObserver(this);
+ mWebBrowser->setHomePageUrl("about:blank");
+
+ return true;
+}
+
+void LLPanelProfileWeb::resetData()
+{
+ mWebBrowser->navigateHome();
+}
+
+void LLPanelProfileWeb::updateData()
+{
+ LLUUID avatar_id = getAvatarId();
+ if (!getStarted() && avatar_id.notNull() && !mURLWebProfile.empty())
+ {
+ setIsLoading();
+
+ mWebBrowser->setVisible(true);
+ mPerformanceTimer.start();
+ mWebBrowser->navigateTo(mURLWebProfile, HTTP_CONTENT_TEXT_HTML);
+ }
+}
+
+void LLPanelProfileWeb::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name)
+{
+ mAvatarNameCacheConnection.disconnect();
+
+ std::string username = av_name.getAccountName();
+ if (username.empty())
+ {
+ username = LLCacheName::buildUsername(av_name.getDisplayName());
+ }
+ else
+ {
+ LLStringUtil::replaceChar(username, ' ', '.');
+ }
+
+ mURLWebProfile = getProfileURL(username, true);
+ if (mURLWebProfile.empty())
+ {
+ return;
+ }
+
+ //if the tab was opened before name was resolved, load the panel now
+ updateData();
+}
+
+void LLPanelProfileWeb::onCommitLoad(LLUICtrl* ctrl)
+{
+ if (!mURLHome.empty())
+ {
+ LLSD::String valstr = ctrl->getValue().asString();
+ if (valstr.empty())
+ {
+ mWebBrowser->setVisible(true);
+ mPerformanceTimer.start();
+ mWebBrowser->navigateTo( mURLHome, HTTP_CONTENT_TEXT_HTML );
+ }
+ else if (valstr == "popout")
+ {
+ // open in viewer's browser, new window
+ LLWeb::loadURLInternal(mURLHome);
+ }
+ else if (valstr == "external")
+ {
+ // open in external browser
+ LLWeb::loadURLExternal(mURLHome);
+ }
+ }
+}
+
+void LLPanelProfileWeb::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event)
+{
+ switch(event)
+ {
+ case MEDIA_EVENT_STATUS_TEXT_CHANGED:
+ childSetValue("status_text", LLSD( self->getStatusText() ) );
+ break;
+
+ case MEDIA_EVENT_NAVIGATE_BEGIN:
+ {
+ if (mFirstNavigate)
+ {
+ mFirstNavigate = false;
+ }
+ else
+ {
+ mPerformanceTimer.start();
+ }
+ }
+ break;
+
+ case MEDIA_EVENT_NAVIGATE_COMPLETE:
+ {
+ LLStringUtil::format_map_t args;
+ args["[TIME]"] = llformat("%.2f", mPerformanceTimer.getElapsedTimeF32());
+ childSetValue("status_text", LLSD( getString("LoadTime", args)) );
+
+ setLoaded();
+ }
+ break;
+
+ default:
+ // Having a default case makes the compiler happy.
+ break;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+LLPanelProfileFirstLife::LLPanelProfileFirstLife()
+ : LLPanelProfilePropertiesProcessorTab()
+ , mHasUnsavedChanges(false)
+{
+}
+
+LLPanelProfileFirstLife::~LLPanelProfileFirstLife()
+{
+}
+
+bool LLPanelProfileFirstLife::postBuild()
+{
+ mDescriptionEdit = getChild<LLTextEditor>("fl_description_edit");
+ mPicture = getChild<LLProfileImageCtrl>("real_world_pic");
+
+ mUploadPhoto = getChild<LLButton>("fl_upload_image");
+ mChangePhoto = getChild<LLButton>("fl_change_image");
+ mRemovePhoto = getChild<LLButton>("fl_remove_image");
+ mSaveChanges = getChild<LLButton>("fl_save_changes");
+ mDiscardChanges = getChild<LLButton>("fl_discard_changes");
+
+ mUploadPhoto->setCommitCallback([this](LLUICtrl*, void*) { onUploadPhoto(); }, nullptr);
+ mChangePhoto->setCommitCallback([this](LLUICtrl*, void*) { onChangePhoto(); }, nullptr);
+ mRemovePhoto->setCommitCallback([this](LLUICtrl*, void*) { onRemovePhoto(); }, nullptr);
+ mSaveChanges->setCommitCallback([this](LLUICtrl*, void*) { onSaveDescriptionChanges(); }, nullptr);
+ mDiscardChanges->setCommitCallback([this](LLUICtrl*, void*) { onDiscardDescriptionChanges(); }, nullptr);
+ mDescriptionEdit->setKeystrokeCallback([this](LLTextEditor* caller) { onSetDescriptionDirty(); });
+
+ return true;
+}
+
+void LLPanelProfileFirstLife::onOpen(const LLSD& key)
+{
+ LLPanelProfileTab::onOpen(key);
+
+ if (!getSelfProfile())
+ {
+ // Otherwise as the only focusable element it will be selected
+ mDescriptionEdit->setTabStop(false);
+ }
+
+ resetData();
+}
+
+void LLPanelProfileFirstLife::setProfileImageUploading(bool loading)
+{
+ mUploadPhoto->setEnabled(!loading);
+ mChangePhoto->setEnabled(!loading);
+ mRemovePhoto->setEnabled(!loading && mPicture->getImageAssetId().notNull());
+
+ LLLoadingIndicator* indicator = getChild<LLLoadingIndicator>("image_upload_indicator");
+ indicator->setVisible(loading);
+ if (loading)
+ {
+ indicator->start();
+ }
+ else
+ {
+ indicator->stop();
+ }
+}
+
+void LLPanelProfileFirstLife::setProfileImageUploaded(const LLUUID &image_asset_id)
+{
+ mPicture->setValue(image_asset_id);
+ setProfileImageUploading(false);
+}
+
+void LLPanelProfileFirstLife::commitUnsavedChanges()
+{
+ if (mHasUnsavedChanges)
+ {
+ onSaveDescriptionChanges();
+ }
+}
+
+void LLPanelProfileFirstLife::onUploadPhoto()
+{
+ (new LLProfileImagePicker(PROFILE_IMAGE_FL, new LLHandle<LLPanel>(LLPanel::getHandle())))->getFile();
+
+ LLFloater* floaterp = mFloaterTexturePickerHandle.get();
+ if (floaterp)
+ {
+ floaterp->closeFloater();
+ }
+}
+
+void LLPanelProfileFirstLife::onChangePhoto()
+{
+ LLFloater* floaterp = mFloaterTexturePickerHandle.get();
+
+ // Show the dialog
+ if (!floaterp)
+ {
+ LLFloater* parent_floater = gFloaterView->getParentFloater(this);
+ if (parent_floater)
+ {
+ // because inventory construction is somewhat slow
+ getWindow()->setCursor(UI_CURSOR_WAIT);
+ LLFloaterTexturePicker* texture_floaterp = new LLFloaterTexturePicker(
+ this,
+ mPicture->getImageAssetId(),
+ LLUUID::null,
+ mPicture->getImageAssetId(),
+ false,
+ false,
+ "SELECT PHOTO",
+ PERM_NONE,
+ PERM_NONE,
+ false,
+ NULL,
+ PICK_TEXTURE);
+
+ mFloaterTexturePickerHandle = texture_floaterp->getHandle();
+
+ texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLPickerSource source, const LLUUID& asset_id, const LLUUID&, const LLUUID&)
+ {
+ if (op == LLTextureCtrl::TEXTURE_SELECT)
+ {
+ onCommitPhoto(asset_id);
+ }
+ });
+ texture_floaterp->setLocalTextureEnabled(false);
+ texture_floaterp->setCanApply(false, true, false);
+
+ parent_floater->addDependentFloater(mFloaterTexturePickerHandle);
+
+ texture_floaterp->openFloater();
+ texture_floaterp->setFocus(true);
+ }
+ }
+ else
+ {
+ floaterp->setMinimized(false);
+ floaterp->setVisibleAndFrontmost(true);
+ }
+}
+
+void LLPanelProfileFirstLife::onRemovePhoto()
+{
+ onCommitPhoto(LLUUID::null);
+
+ LLFloater* floaterp = mFloaterTexturePickerHandle.get();
+ if (floaterp)
+ {
+ floaterp->closeFloater();
+ }
+}
+
+void LLPanelProfileFirstLife::onCommitPhoto(const LLUUID& id)
+{
+ if (mPicture->getImageAssetId() == id)
+ return;
+
+ if (!saveAgentUserInfoCoro("fl_image_id", id))
+ return;
+
+ mPicture->setValue(id);
+
+ mRemovePhoto->setEnabled(id.notNull());
+}
+
+void LLPanelProfileFirstLife::setDescriptionText(const std::string &text)
+{
+ mSaveChanges->setEnabled(false);
+ mDiscardChanges->setEnabled(false);
+ mHasUnsavedChanges = false;
+
+ mCurrentDescription = text;
+ mDescriptionEdit->setValue(mCurrentDescription);
+}
+
+void LLPanelProfileFirstLife::onSetDescriptionDirty()
+{
+ mSaveChanges->setEnabled(true);
+ mDiscardChanges->setEnabled(true);
+ mHasUnsavedChanges = true;
+}
+
+void LLPanelProfileFirstLife::onSaveDescriptionChanges()
+{
+ mCurrentDescription = mDescriptionEdit->getValue().asString();
+ saveAgentUserInfoCoro("fl_about_text", mCurrentDescription);
+
+ mSaveChanges->setEnabled(false);
+ mDiscardChanges->setEnabled(false);
+ mHasUnsavedChanges = false;
+}
+
+void LLPanelProfileFirstLife::onDiscardDescriptionChanges()
+{
+ setDescriptionText(mCurrentDescription);
+}
+
+void LLPanelProfileFirstLife::processProperties(void* data, EAvatarProcessorType type)
+{
+ if (APT_PROPERTIES == type)
+ {
+ LLAvatarData* avatar_data = static_cast<LLAvatarData*>(data);
+ if (avatar_data && getAvatarId() == avatar_data->avatar_id)
+ {
+ processProperties(avatar_data);
+ }
+ }
+}
+
+void LLPanelProfileFirstLife::processProperties(const LLAvatarData* avatar_data)
+{
+ setDescriptionText(avatar_data->fl_about_text);
+
+ mPicture->setValue(avatar_data->fl_image_id);
+
+ setLoaded();
+}
+
+void LLPanelProfileFirstLife::resetData()
+{
+ setDescriptionText(std::string());
+ mPicture->setValue(LLUUID::null);
+
+ mUploadPhoto->setVisible(getSelfProfile());
+ mChangePhoto->setVisible(getSelfProfile());
+ mRemovePhoto->setVisible(getSelfProfile());
+ mSaveChanges->setVisible(getSelfProfile());
+ mDiscardChanges->setVisible(getSelfProfile());
+}
+
+void LLPanelProfileFirstLife::setLoaded()
+{
+ LLPanelProfileTab::setLoaded();
+
+ if (getSelfProfile())
+ {
+ mDescriptionEdit->setEnabled(true);
+ mPicture->setEnabled(true);
+ mRemovePhoto->setEnabled(mPicture->getImageAssetId().notNull());
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+LLPanelProfileNotes::LLPanelProfileNotes()
+: LLPanelProfilePropertiesProcessorTab()
+ , mHasUnsavedChanges(false)
+{
+
+}
+
+LLPanelProfileNotes::~LLPanelProfileNotes()
+{
+}
+
+void LLPanelProfileNotes::commitUnsavedChanges()
+{
+ if (mHasUnsavedChanges)
+ {
+ onSaveNotesChanges();
+ }
+}
+
+bool LLPanelProfileNotes::postBuild()
+{
+ mNotesEditor = getChild<LLTextEditor>("notes_edit");
+ mSaveChanges = getChild<LLButton>("notes_save_changes");
+ mDiscardChanges = getChild<LLButton>("notes_discard_changes");
+
+ mSaveChanges->setCommitCallback([this](LLUICtrl*, void*) { onSaveNotesChanges(); }, nullptr);
+ mDiscardChanges->setCommitCallback([this](LLUICtrl*, void*) { onDiscardNotesChanges(); }, nullptr);
+ mNotesEditor->setKeystrokeCallback([this](LLTextEditor* caller) { onSetNotesDirty(); });
+
+ return true;
+}
+
+void LLPanelProfileNotes::onOpen(const LLSD& key)
+{
+ LLPanelProfileTab::onOpen(key);
+
+ resetData();
+}
+
+void LLPanelProfileNotes::setNotesText(const std::string &text)
+{
+ mSaveChanges->setEnabled(false);
+ mDiscardChanges->setEnabled(false);
+ mHasUnsavedChanges = false;
+
+ mCurrentNotes = text;
+ mNotesEditor->setValue(mCurrentNotes);
+}
+
+void LLPanelProfileNotes::onSetNotesDirty()
+{
+ mSaveChanges->setEnabled(true);
+ mDiscardChanges->setEnabled(true);
+ mHasUnsavedChanges = true;
+}
+
+void LLPanelProfileNotes::onSaveNotesChanges()
+{
+ mCurrentNotes = mNotesEditor->getValue().asString();
+ saveAgentUserInfoCoro("notes", mCurrentNotes);
+
+ mSaveChanges->setEnabled(false);
+ mDiscardChanges->setEnabled(false);
+ mHasUnsavedChanges = false;
+}
+
+void LLPanelProfileNotes::onDiscardNotesChanges()
+{
+ setNotesText(mCurrentNotes);
+}
+
+void LLPanelProfileNotes::processProperties(void* data, EAvatarProcessorType type)
+{
+ if (APT_PROPERTIES == type)
+ {
+ LLAvatarData* avatar_data = static_cast<LLAvatarData*>(data);
+ if (avatar_data && getAvatarId() == avatar_data->avatar_id)
+ {
+ processProperties(avatar_data);
+ }
+ }
+}
+
+void LLPanelProfileNotes::processProperties(const LLAvatarData* avatar_data)
+{
+ setNotesText(avatar_data->notes);
+ mNotesEditor->setEnabled(true);
+ setLoaded();
+}
+
+void LLPanelProfileNotes::resetData()
+{
+ resetLoading();
+ setNotesText(std::string());
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// LLPanelProfile
+
+LLPanelProfile::LLPanelProfile()
+ : LLPanelProfileTab()
+{
+}
+
+LLPanelProfile::~LLPanelProfile()
+{
+}
+
+bool LLPanelProfile::postBuild()
+{
+ return true;
+}
+
+void LLPanelProfile::onTabChange()
+{
+ LLPanelProfileTab* active_panel = dynamic_cast<LLPanelProfileTab*>(mTabContainer->getCurrentPanel());
+ if (active_panel)
+ {
+ active_panel->updateData();
+ }
+}
+
+void LLPanelProfile::onOpen(const LLSD& key)
+{
+ LLUUID avatar_id = key["id"].asUUID();
+
+ // Don't reload the same profile
+ if (getAvatarId() == avatar_id)
+ {
+ return;
+ }
+
+ LLPanelProfileTab::onOpen(avatar_id);
+
+ mTabContainer = getChild<LLTabContainer>("panel_profile_tabs");
+ mPanelSecondlife = findChild<LLPanelProfileSecondLife>(PANEL_SECONDLIFE);
+ mPanelWeb = findChild<LLPanelProfileWeb>(PANEL_WEB);
+ mPanelPicks = findChild<LLPanelProfilePicks>(PANEL_PICKS);
+ mPanelClassifieds = findChild<LLPanelProfileClassifieds>(PANEL_CLASSIFIEDS);
+ mPanelFirstlife = findChild<LLPanelProfileFirstLife>(PANEL_FIRSTLIFE);
+ mPanelNotes = findChild<LLPanelProfileNotes>(PANEL_NOTES);
+
+ mPanelSecondlife->onOpen(avatar_id);
+ mPanelWeb->onOpen(avatar_id);
+ mPanelPicks->onOpen(avatar_id);
+ mPanelClassifieds->onOpen(avatar_id);
+ mPanelFirstlife->onOpen(avatar_id);
+ mPanelNotes->onOpen(avatar_id);
+
+ // Always request the base profile info
+ resetLoading();
+ updateData();
+
+ // Some tabs only request data when opened
+ mTabContainer->setCommitCallback(boost::bind(&LLPanelProfile::onTabChange, this));
+}
+
+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 (!getStarted() && avatar_id.notNull())
+ {
+ setIsLoading();
+
+ mPanelSecondlife->setIsLoading();
+ mPanelPicks->setIsLoading();
+ mPanelFirstlife->setIsLoading();
+ mPanelNotes->setIsLoading();
+
+ LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesRequest(getAvatarId());
+ }
+}
+
+void LLPanelProfile::refreshName()
+{
+ mPanelSecondlife->refreshName();
+}
+
+void LLPanelProfile::createPick(const LLPickData &data)
+{
+ mTabContainer->selectTabPanel(mPanelPicks);
+ mPanelPicks->createPick(data);
+}
+
+void LLPanelProfile::showPick(const LLUUID& pick_id)
+{
+ if (pick_id.notNull())
+ {
+ mPanelPicks->selectPick(pick_id);
+ }
+ mTabContainer->selectTabPanel(mPanelPicks);
+}
+
+bool LLPanelProfile::isPickTabSelected()
+{
+ return (mTabContainer->getCurrentPanel() == mPanelPicks);
+}
+
+bool LLPanelProfile::isNotesTabSelected()
+{
+ return (mTabContainer->getCurrentPanel() == mPanelNotes);
+}
+
+bool LLPanelProfile::hasUnsavedChanges()
+{
+ return mPanelSecondlife->hasUnsavedChanges()
+ || mPanelPicks->hasUnsavedChanges()
+ || mPanelClassifieds->hasUnsavedChanges()
+ || mPanelFirstlife->hasUnsavedChanges()
+ || mPanelNotes->hasUnsavedChanges();
+}
+
+bool LLPanelProfile::hasUnpublishedClassifieds()
+{
+ return mPanelClassifieds->hasNewClassifieds();
+}
+
+void LLPanelProfile::commitUnsavedChanges()
+{
+ mPanelSecondlife->commitUnsavedChanges();
+ mPanelPicks->commitUnsavedChanges();
+ mPanelClassifieds->commitUnsavedChanges();
+ mPanelFirstlife->commitUnsavedChanges();
+ mPanelNotes->commitUnsavedChanges();
+}
+
+void LLPanelProfile::showClassified(const LLUUID& classified_id, bool edit)
+{
+ if (classified_id.notNull())
+ {
+ mPanelClassifieds->selectClassified(classified_id, edit);
+ }
+ mTabContainer->selectTabPanel(mPanelClassifieds);
+}
+
+void LLPanelProfile::createClassified()
+{
+ mPanelClassifieds->createClassified();
+ mTabContainer->selectTabPanel(mPanelClassifieds);
+}
+