summaryrefslogtreecommitdiff
path: root/indra/newview/llpanelprofile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llpanelprofile.cpp')
-rw-r--r--indra/newview/llpanelprofile.cpp2190
1 files changed, 1962 insertions, 228 deletions
diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp
index 5f13b223fb..38308f2498 100644
--- a/indra/newview/llpanelprofile.cpp
+++ b/indra/newview/llpanelprofile.cpp
@@ -1,25 +1,25 @@
-/**
+/**
* @file llpanelprofile.cpp
* @brief Profile panel implementation
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
-*
+*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
-*
+*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
-*
+*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-*
+*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -27,32 +27,427 @@
#include "llviewerprecompiledheaders.h"
#include "llpanelprofile.h"
-#include "llagent.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 "llgrouplist.h"
+#include "llurlaction.h"
+
+// Image
+#include "llimagej2c.h"
+
+// Newview
+#include "llagent.h" //gAgent
+#include "llagentpicksinfo.h"
#include "llavataractions.h"
-#include "llfloaterreg.h"
+#include "llavatarpropertiesprocessor.h"
+#include "llcallingcard.h"
#include "llcommandhandler.h"
-#include "llnotificationsutil.h"
-#include "llpanelpicks.h"
-#include "lltabcontainer.h"
-#include "llviewercontrol.h"
-#include "llviewernetwork.h"
+#include "llfloaterreg.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 const std::string PANEL_PICKS = "panel_picks";
-std::string getProfileURL(const std::string& agent_name)
+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)
{
- std::string url = "[WEB_PROFILE_URL][AGENT_NAME]";
- LLSD subs;
- subs["WEB_PROFILE_URL"] = LLGridManager::getInstance()->getWebProfileURL();
- subs["AGENT_NAME"] = agent_name;
- url = LLWeb::expandURLSubstitutions(url, subs);
- LLStringUtil::toLower(url);
- return url;
+ 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->flags = 0;
+
+ if (result["online"].asBoolean())
+ {
+ avatar_data->flags |= AVATAR_ONLINE;
+ }
+ if (result["allow_publish"].asBoolean())
+ {
+ avatar_data->flags |= AVATAR_ALLOW_PUBLISH;
+ }
+
+ avatar_data->caption_index = 0;
+ if (result.has("charter_member")) // won't be present if "caption" is set
+ {
+ avatar_data->caption_index = result["charter_member"].asInteger();
+ }
+ else if (result.has("caption"))
+ {
+ avatar_data->caption_text = result["caption"].asString();
+ }
+
+ panel = floater_profile->findChild<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);
+ }
+ if (panel_sl)
+ {
+ panel_sl->processNotesProperties(&avatar_notes);
+ }
}
+//TODO: changes take two minutes to propagate!
+// Add some storage that holds updated data for two minutes
+// for new instances to reuse the data
+// Profile data is only relevant to won avatar, but notes
+// are for everybody
+void put_avatar_properties_coro(std::string cap_url, LLUUID agent_id, LLSD data)
+{
+ LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+ LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
+ httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("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;
+ return;
+ }
+
+ LL_DEBUGS("AvatarProperties") << "Agent id: " << agent_id << " Data: " << data << " Result: " << httpResults << LL_ENDL;
+}
+
+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_WARNS("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;
+ }
+ }
+ }
+
+ // Cleanup
+ LLFile::remove(path_to_image);
+ delete handle;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// LLProfileHandler
+
class LLProfileHandler : public LLCommandHandler
{
public:
@@ -73,6 +468,10 @@ public:
};
LLProfileHandler gProfileHandler;
+
+//////////////////////////////////////////////////////////////////////////
+// LLAgentHandler
+
class LLAgentHandler : public LLCommandHandler
{
public:
@@ -184,273 +583,1608 @@ public:
LLAgentHandler gAgentHandler;
-//-- LLPanelProfile::ChildStack begins ----------------------------------------
-LLPanelProfile::ChildStack::ChildStack()
-: mParent(NULL)
+///----------------------------------------------------------------------------
+/// LLFloaterInventoryFinder
+///----------------------------------------------------------------------------
+
+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);
+
+private:
+ void fillRightsData();
+ void rightsConfirmationCallback(const LLSD& notification, const LLSD& response);
+ void confirmModifyRights(bool grant);
+ void onCommitRights();
+
+ void onApplyRights();
+ void onCancel();
+
+ LLTextBase* mDescription;
+ LLCheckBoxCtrl* mOnlineStatus;
+ LLCheckBoxCtrl* mMapRights;
+ LLCheckBoxCtrl* mEditObjectRights;
+ LLButton* mOkBtn;
+ LLButton* mCancelBtn;
+
+ LLUUID mAvatarID;
+ F32 mContextConeOpacity;
+ LLHandle<LLView> mOwnerHandle;
+
+ boost::signals2::connection mAvatarNameCacheConnection;
+};
+
+LLFloaterProfilePermissions::LLFloaterProfilePermissions(LLView * owner, const LLUUID &avatar_id)
+ : LLFloater(LLSD())
+ , mAvatarID(avatar_id)
+ , mContextConeOpacity(0.0f)
+ , mOwnerHandle(owner->getHandle())
{
+ buildFromFile("floater_profile_permissions.xml");
}
-LLPanelProfile::ChildStack::~ChildStack()
+LLFloaterProfilePermissions::~LLFloaterProfilePermissions()
{
- while (mStack.size() != 0)
- {
- view_list_t& top = mStack.back();
- for (view_list_t::const_iterator it = top.begin(); it != top.end(); ++it)
- {
- LLView* viewp = *it;
- if (viewp)
- {
- viewp->die();
- }
- }
- mStack.pop_back();
- }
+ mAvatarNameCacheConnection.disconnect();
+ if (mAvatarID.notNull())
+ {
+ LLAvatarTracker::instance().removeParticularFriendObserver(mAvatarID, this);
+ }
}
-void LLPanelProfile::ChildStack::setParent(LLPanel* parent)
+BOOL LLFloaterProfilePermissions::postBuild()
{
- llassert_always(parent != NULL);
- mParent = parent;
+ 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");
+
+ mEditObjectRights->setCommitCallback([this](LLUICtrl*, void*) { onCommitRights(); }, nullptr);
+ mOkBtn->setCommitCallback([this](LLUICtrl*, void*) { onApplyRights(); }, nullptr);
+ mCancelBtn->setCommitCallback([this](LLUICtrl*, void*) { onCancel(); }, nullptr);
+
+ return TRUE;
}
-/// Save current parent's child views and remove them from the child list.
-bool LLPanelProfile::ChildStack::push()
+void LLFloaterProfilePermissions::onOpen(const LLSD& key)
{
- view_list_t vlist = *mParent->getChildList();
+ if (LLAvatarActions::isFriend(mAvatarID))
+ {
+ LLAvatarTracker::instance().addParticularFriendObserver(mAvatarID, this);
+ fillRightsData();
+ }
- for (view_list_t::const_iterator it = vlist.begin(); it != vlist.end(); ++it)
- {
- LLView* viewp = *it;
- mParent->removeChild(viewp);
- }
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(mAvatarID, boost::bind(&LLFloaterProfilePermissions::onAvatarNameCache, this, _1, _2));
+}
- mStack.push_back(vlist);
- dump();
- return true;
+void LLFloaterProfilePermissions::draw()
+{
+ // drawFrustum
+ LLView *owner = mOwnerHandle.get();
+ static LLCachedControl<F32> max_opacity(gSavedSettings, "PickerContextOpacity", 0.4f);
+ drawConeToOwner(mContextConeOpacity, max_opacity, owner);
+ LLFloater::draw();
}
-/// Restore saved children (adding them back to the child list).
-bool LLPanelProfile::ChildStack::pop()
+void LLFloaterProfilePermissions::changed(U32 mask)
{
- if (mStack.size() == 0)
- {
- LL_WARNS() << "Empty stack" << LL_ENDL;
- llassert(mStack.size() == 0);
- return false;
- }
+ if (mask != LLFriendObserver::ONLINE)
+ {
+ fillRightsData();
+ }
+}
- view_list_t& top = mStack.back();
- for (view_list_t::const_iterator it = top.begin(); it != top.end(); ++it)
- {
- LLView* viewp = *it;
- mParent->addChild(viewp);
- }
+void LLFloaterProfilePermissions::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name)
+{
+ mAvatarNameCacheConnection.disconnect();
- mStack.pop_back();
- dump();
- return true;
+ LLStringUtil::format_map_t args;
+ args["[AGENT_NAME]"] = av_name.getDisplayName();
+ std::string descritpion = getString("description_string", args);
+ mDescription->setValue(descritpion);
}
-/// Temporarily add all saved children back.
-void LLPanelProfile::ChildStack::preParentReshape()
+void LLFloaterProfilePermissions::fillRightsData()
{
- mSavedStack = mStack;
- while(mStack.size() > 0)
- {
- pop();
- }
+ 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();
+
+ mOnlineStatus->setValue(LLRelationship::GRANT_ONLINE_STATUS & rights ? TRUE : FALSE);
+ mMapRights->setValue(LLRelationship::GRANT_MAP_LOCATION & rights ? TRUE : FALSE);
+ mEditObjectRights->setValue(LLRelationship::GRANT_MODIFY_OBJECTS & rights ? TRUE : FALSE);
+ }
+ else
+ {
+ closeFloater();
+ LL_INFOS("ProfilePermissions") << "Floater closing since agent is no longer a friend" << LL_ENDL;
+ }
}
-/// Add the temporarily saved children back.
-void LLPanelProfile::ChildStack::postParentReshape()
+void LLFloaterProfilePermissions::rightsConfirmationCallback(const LLSD& notification,
+ const LLSD& response)
{
- mStack = mSavedStack;
- mSavedStack = stack_t();
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+ if (option != 0)
+ {
+ mEditObjectRights->setValue(mEditObjectRights->getValue().asBoolean() ? FALSE : TRUE);
+ }
+}
- for (stack_t::const_iterator stack_it = mStack.begin(); stack_it != mStack.end(); ++stack_it)
- {
- const view_list_t& vlist = (*stack_it);
- for (view_list_t::const_iterator list_it = vlist.begin(); list_it != vlist.end(); ++list_it)
- {
- LLView* viewp = *list_it;
- LL_DEBUGS() << "removing " << viewp->getName() << LL_ENDL;
- mParent->removeChild(viewp);
- }
- }
+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 LLPanelProfile::ChildStack::dump()
+void LLFloaterProfilePermissions::onCommitRights()
{
- unsigned lvl = 0;
- LL_DEBUGS() << "child stack dump:" << LL_ENDL;
- for (stack_t::const_iterator stack_it = mStack.begin(); stack_it != mStack.end(); ++stack_it, ++lvl)
- {
- std::ostringstream dbg_line;
- dbg_line << "lvl #" << lvl << ":";
- const view_list_t& vlist = (*stack_it);
- for (view_list_t::const_iterator list_it = vlist.begin(); list_it != vlist.end(); ++list_it)
- {
- dbg_line << " " << (*list_it)->getName();
- }
- LL_DEBUGS() << dbg_line.str() << LL_ENDL;
- }
+ 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;
+ }
+
+ 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);
+ }
}
-//-- LLPanelProfile::ChildStack ends ------------------------------------------
+void LLFloaterProfilePermissions::onApplyRights()
+{
+ const LLRelationship* buddy_relationship = LLAvatarTracker::instance().getBuddyInfo(mAvatarID);
-LLPanelProfile::LLPanelProfile()
- : LLPanel()
- , mAvatarId(LLUUID::null)
+ 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()
{
- mChildStack.setParent(this);
+ closeFloater();
}
-BOOL LLPanelProfile::postBuild()
+//////////////////////////////////////////////////////////////////////////
+// LLPanelProfileSecondLife
+
+LLPanelProfileSecondLife::LLPanelProfileSecondLife()
+ : LLPanelProfileTab()
+ , mAvatarNameCacheConnection()
+ , mWaitingForImageUpload(false)
+ , mAllowPublish(false)
+{
+}
+
+LLPanelProfileSecondLife::~LLPanelProfileSecondLife()
{
- LLPanelPicks* panel_picks = findChild<LLPanelPicks>(PANEL_PICKS);
- panel_picks->setProfilePanel(this);
+ if (getAvatarId().notNull())
+ {
+ LLAvatarTracker::instance().removeParticularFriendObserver(getAvatarId(), this);
+ }
- getTabContainer()[PANEL_PICKS] = panel_picks;
+ if (LLVoiceClient::instanceExists())
+ {
+ LLVoiceClient::getInstance()->removeObserver((LLVoiceClientStatusObserver*)this);
+ }
- return TRUE;
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
}
-// virtual
-void LLPanelProfile::reshape(S32 width, S32 height, BOOL called_from_parent)
+BOOL LLPanelProfileSecondLife::postBuild()
{
- // Temporarily add saved children back and reshape them.
- mChildStack.preParentReshape();
- LLPanel::reshape(width, height, called_from_parent);
- mChildStack.postParentReshape();
+ mGroupList = getChild<LLGroupList>("group_list");
+ mShowInSearchCombo = getChild<LLComboBox>("show_in_search");
+ mSecondLifePic = getChild<LLIconCtrl>("2nd_life_pic");
+ mSecondLifePicLayout = getChild<LLPanel>("image_panel");
+ mDescriptionEdit = getChild<LLTextEditor>("sl_description_edit");
+ mNotesSnippet = getChild<LLTextEditor>("notes_snippet");
+ 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(); });
+
+ getChild<LLButton>("open_notes")->setCommitCallback([this](LLUICtrl*, void*) { onOpenNotes(); }, nullptr);
+
+ return TRUE;
}
-void LLPanelProfile::onOpen(const LLSD& key)
+void LLPanelProfileSecondLife::onOpen(const LLSD& key)
{
- getTabContainer()[PANEL_PICKS]->onOpen(getAvatarId());
+ LLPanelProfileTab::onOpen(key);
- // support commands to open further pieces of UI
- if (key.has("show_tab_panel"))
- {
- std::string panel = key["show_tab_panel"].asString();
- if (panel == "create_classified")
- {
- LLPanelPicks* picks = dynamic_cast<LLPanelPicks *>(getTabContainer()[PANEL_PICKS]);
- if (picks)
- {
- picks->createNewClassified();
- }
- }
- else if (panel == "classified_details")
- {
- LLPanelPicks* picks = dynamic_cast<LLPanelPicks *>(getTabContainer()[PANEL_PICKS]);
- if (picks)
- {
- LLSD params = key;
- params.erase("show_tab_panel");
- params.erase("open_tab_name");
- picks->openClassifiedInfo(params);
- }
- }
- else if (panel == "edit_classified")
- {
- LLPanelPicks* picks = dynamic_cast<LLPanelPicks *>(getTabContainer()[PANEL_PICKS]);
- if (picks)
- {
- LLSD params = key;
- params.erase("show_tab_panel");
- params.erase("open_tab_name");
- picks->openClassifiedEdit(params);
- }
- }
- else if (panel == "create_pick")
- {
- LLPanelPicks* picks = dynamic_cast<LLPanelPicks *>(getTabContainer()[PANEL_PICKS]);
- if (picks)
- {
- picks->createNewPick();
- }
- }
- else if (panel == "edit_pick")
- {
- LLPanelPicks* picks = dynamic_cast<LLPanelPicks *>(getTabContainer()[PANEL_PICKS]);
- if (picks)
- {
- LLSD params = key;
- params.erase("show_tab_panel");
- params.erase("open_tab_name");
- picks->openPickEdit(params);
- }
- }
- }
+ 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));
}
-void LLPanelProfile::onTabSelected(const LLSD& param)
+void LLPanelProfileSecondLife::updateData()
{
- std::string tab_name = param.asString();
- if (NULL != getTabContainer()[tab_name])
- {
- getTabContainer()[tab_name]->onOpen(getAvatarId());
- }
+ 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 LLPanelProfile::openPanel(LLPanel* panel, const LLSD& params)
+void LLPanelProfileSecondLife::resetData()
{
- // Hide currently visible panel (STORM-690).
- mChildStack.push();
+ resetLoading();
- // Add the panel or bring it to front.
- if (panel->getParent() != this)
- {
- addChild(panel);
- }
- else
- {
- sendChildToFront(panel);
- }
+ // Set default image and 1:1 dimensions for it
+ mSecondLifePic->setValue("Generic_Person_Large");
+ LLRect imageRect = mSecondLifePicLayout->getRect();
+ mSecondLifePicLayout->reshape(imageRect.getHeight(), imageRect.getHeight());
+
+ setDescriptionText(LLStringUtil::null);
+ mGroups.clear();
+ mGroupList->setGroups(mGroups);
- panel->setVisible(TRUE);
- panel->setFocus(TRUE); // prevent losing focus by the floater
- panel->onOpen(params);
+ mCanSeeOnlineIcon->setVisible(false);
+ mCantSeeOnlineIcon->setVisible(true);
+ mCanSeeOnMapIcon->setVisible(false);
+ mCantSeeOnMapIcon->setVisible(true);
+ mCanEditObjectsIcon->setVisible(false);
+ mCantEditObjectsIcon->setVisible(true);
- LLRect new_rect = getRect();
- panel->reshape(new_rect.getWidth(), new_rect.getHeight());
- new_rect.setLeftTopAndSize(0, new_rect.getHeight(), new_rect.getWidth(), new_rect.getHeight());
- panel->setRect(new_rect);
+ childSetVisible("permissions_panel", false);
}
-void LLPanelProfile::closePanel(LLPanel* panel)
+void LLPanelProfileSecondLife::processProfileProperties(const LLAvatarData* avatar_data)
{
- panel->setVisible(FALSE);
+ LLUUID avatar_id = getAvatarId();
+ if (!LLAvatarActions::isFriend(avatar_id) && !getSelfProfile())
+ {
+ // subscribe observer to get online status. Request will be sent by LLPanelProfileSecondLife itself.
+ // do not subscribe for friend avatar because online status can be wrong overridden
+ // via LLAvatarData::flags if Preferences: "Only Friends & Groups can see when I am online" is set.
+ processOnlineStatus(avatar_data->flags & AVATAR_ONLINE);
+ }
- if (panel->getParent() == this)
- {
- removeChild(panel);
+ fillCommonData(avatar_data);
- // Make the underlying panel visible.
- mChildStack.pop();
+ fillPartnerData(avatar_data);
- // Prevent losing focus by the floater
- const child_list_t* child_list = getChildList();
- if (child_list->size() > 0)
- {
- child_list->front()->setFocus(TRUE);
- }
- else
- {
- LL_WARNS() << "No underlying panel to focus." << LL_ENDL;
- }
- }
+ fillAccountStatus(avatar_data);
+
+ setLoaded();
}
-S32 LLPanelProfile::notifyParent(const LLSD& info)
+void LLPanelProfileSecondLife::processGroupProperties(const LLAvatarGroups* avatar_groups)
{
- std::string action = info["action"];
- // lets update Picks list after Pick was saved
- if("save_new_pick" == action)
- {
- onOpen(info);
- return 1;
- }
- return LLPanel::notifyParent(info);
+ 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::processNotesProperties(LLAvatarNotes* avatar_notes)
+{
+ mNotesSnippet->setValue(avatar_notes->notes);
+}
+
+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();
+ // Should be possible to get this from AgentProfile capability
+ getChild<LLUICtrl>("display_name")->setValue( av_name.getDisplayName() );
+ getChild<LLUICtrl>("user_name")->setValue(av_name.getAccountName());
+}
+
+void LLPanelProfileSecondLife::setNotesSnippet(std::string &notes)
+{
+ mNotesSnippet->setValue(notes);
+}
+
+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);
+
+ 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);
+ }
+
+ setProfileImageUploading(false);
+}
+
+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);
+
+ LLStringUtil::format_map_t args;
+ args["[AGE]"] = LLDateUtil::ageFromDate( avatar_data->born_on, LLDate::now());
+ std::string register_date = getString("AgeFormat", args);
+ getChild<LLUICtrl>("user_age")->setValue(register_date);
+ setDescriptionText(avatar_data->about_text);
+
+ if (avatar_data->image_id.notNull())
+ {
+ mSecondLifePic->setValue(avatar_data->image_id);
+ }
+ else
+ {
+ mSecondLifePic->setValue("Generic_Person_Large");
+ }
+
+ // 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);
+}
+
+void LLPanelProfileSecondLife::fillRightsData()
+{
+ 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);
+ }
+ else
+ {
+ mCanSeeOnlineIcon->setVisible(false);
+ mCantSeeOnlineIcon->setVisible(true);
+ mCanSeeOnMapIcon->setVisible(false);
+ mCantSeeOnMapIcon->setVisible(true);
+ mCanEditObjectsIcon->setVisible(false);
+ mCantEditObjectsIcon->setVisible(true);
+ }
+
+ childSetVisible("permissions_panel", NULL != relation);
+}
+
+void LLPanelProfileSecondLife::onImageLoaded(BOOL success, LLViewerFetchedTexture *imagep)
+{
+ LLRect imageRect = mSecondLifePicLayout->getRect();
+ if (!success || imagep->getFullWidth() == imagep->getFullHeight())
+ {
+ mSecondLifePicLayout->reshape(imageRect.getHeight(), imageRect.getHeight());
+ }
+ else
+ {
+ // assume 3:4, for sake of firestorm
+ mSecondLifePicLayout->reshape(imageRect.getHeight() * 4 / 3, imageRect.getHeight());
+ }
+}
+
+//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)
+{
+ if (mask & LLFriendObserver::ONLINE)
+ {
+ 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);
+ }
+ }
+}
+
+bool LLPanelProfileSecondLife::isGrantedToSeeOnlineStatus()
+{
+ // set text box visible to show online status for non-friends who has not set in Preferences
+ // "Only Friends & Groups can see when I am online"
+ if (!LLAvatarActions::isFriend(getAvatarId()))
+ {
+ return true;
+ }
+
+ // *NOTE: GRANT_ONLINE_STATUS is always set to false while changing any other status.
+ // When avatar disallow me to see her online status processOfflineNotification Message is received by the viewer
+ // see comments for ChangeUserRights template message. EXT-453.
+ // If GRANT_ONLINE_STATUS flag is changed it will be applied when viewer restarts. EXT-3880
+ const LLRelationship* relationship = LLAvatarTracker::instance().getBuddyInfo(getAvatarId());
+ return relationship->isRightGrantedFrom(LLRelationship::GRANT_ONLINE_STATUS);
+}
+
+// method was disabled according to EXT-2022. Re-enabled & improved according to EXT-3880
+void LLPanelProfileSecondLife::updateOnlineStatus()
+{
+ if (!LLAvatarActions::isFriend(getAvatarId())) return;
+ // For friend let check if he allowed me to see his status
+ const LLRelationship* relationship = LLAvatarTracker::instance().getBuddyInfo(getAvatarId());
+ bool online = relationship->isOnline();
+ processOnlineStatus(online);
+}
+
+void LLPanelProfileSecondLife::processOnlineStatus(bool 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;
+ }
+ 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))
+ {
+ //todo: image not supported notification
+ LL_WARNS("AvatarProperties") << "Failed to upload profile image of type " << (S32)PROFILE_IMAGE_SL << ", failed to open image" << LL_ENDL;
+ return;
+ }
+
+ std::string cap_url = gAgent.getRegionCapability(PROFILE_IMAGE_UPLOAD_CAP);
+ if (cap_url.empty())
+ {
+ LL_WARNS("AvatarProperties") << "Failed to upload profile image of type " << (S32)PROFILE_IMAGE_SL << ", no cap found" << LL_ENDL;
+ return;
+ }
+
+ LLPanelProfileSecondLife* panel = static_cast<LLPanelProfileSecondLife*>(mHandle->get());
+ panel->setProfileImageUploading(true);
+
+ 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 == "change_photo")
+ {
+ (new LLProfileImagePicker(PROFILE_IMAGE_SL, new LLHandle<LLPanel>(getHandle())))->getFile();
+ }
+ else if (item_name == "remove_photo")
+ {
+ LLSD params;
+ params["sl_image_id"] = LLUUID::null;
+
+ 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(), params));
+
+ mSecondLifePic->setValue("Generic_Person_Large");
+ }
+ else
+ {
+ LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL;
+ }
+ }
+}
+
+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 == "change_photo")
+ {
+ std::string cap_url = gAgent.getRegionCapability(PROFILE_IMAGE_UPLOAD_CAP);
+ return !cap_url.empty() && !mWaitingForImageUpload;
+ }
+ else if (item_name == "remove_photo")
+ {
+ std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP);
+ return !cap_url.empty() && !mWaitingForImageUpload;
+ }
+
+ 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);
+ mDescriptionText = text;
+ mDescriptionEdit->setValue(mDescriptionText);
+}
+
+void LLPanelProfileSecondLife::onSetDescriptionDirty()
+{
+ mSaveDescriptionChanges->setEnabled(TRUE);
+ mDiscardDescriptionChanges->setEnabled(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));
+ }
+ 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)));
+ }
+ else
+ {
+ LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL;
+ }
+
+ mSaveDescriptionChanges->setEnabled(FALSE);
+ mDiscardDescriptionChanges->setEnabled(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();
+
+ parent_floater->addDependentFloater(mFloaterPermissionsHandle);
+ }
+ }
+ else // already open
+ {
+ floater->closeFloater();
+ }
+}
+
+void LLPanelProfileSecondLife::onOpenNotes()
+{
+ LLFloater* parent_floater = gFloaterView->getParentFloater(this);
+ if (!parent_floater)
+ {
+ return;
+ }
+
+ LLTabContainer* tab_container = parent_floater->findChild<LLTabContainer>("panel_profile_tabs", TRUE);
+ if (!tab_container)
+ {
+ return;
+ }
+
+ tab_container->selectTabByName(PANEL_NOTES);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// 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()
+{
+}
+
+LLPanelProfileFirstLife::~LLPanelProfileFirstLife()
+{
+}
+
+BOOL LLPanelProfileFirstLife::postBuild()
+{
+ mDescriptionEdit = getChild<LLTextEditor>("fl_description_edit");
+ mPicture = getChild<LLIconCtrl>("real_world_pic");
+
+ mChangePhoto = getChild<LLButton>("fl_upload_image");
+ mRemovePhoto = getChild<LLButton>("fl_remove_image");
+ mSaveChanges = getChild<LLButton>("fl_save_changes");
+ mDiscardChanges = getChild<LLButton>("fl_discard_changes");
+
+ 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)
+{
+ mChangePhoto->setEnabled(!loading);
+ mRemovePhoto->setEnabled(!loading);
+
+ 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::onChangePhoto()
+{
+ (new LLProfileImagePicker(PROFILE_IMAGE_FL, new LLHandle<LLPanel>(getHandle())))->getFile();
+}
+
+void LLPanelProfileFirstLife::onRemovePhoto()
+{
+ LLSD params;
+ params["fl_image_id"] = LLUUID::null;
+
+ 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(), params));
+
+ mPicture->setValue("Generic_Person_Large");
+ }
+ 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);
+ mCurrentDescription = text;
+ mDescriptionEdit->setValue(mCurrentDescription);
+}
+
+void LLPanelProfileFirstLife::onSetDescriptionDirty()
+{
+ mSaveChanges->setEnabled(TRUE);
+ mDiscardChanges->setEnabled(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)));
+ }
+ else
+ {
+ LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL;
+ }
+
+ mSaveChanges->setEnabled(FALSE);
+ mDiscardChanges->setEnabled(FALSE);
+}
+
+void LLPanelProfileFirstLife::onDiscardDescriptionChanges()
+{
+ setDescriptionText(mCurrentDescription);
+}
+
+void LLPanelProfileFirstLife::processProperties(const LLAvatarData* avatar_data)
+{
+ setDescriptionText(avatar_data->fl_about_text);
+ if (avatar_data->fl_image_id.notNull())
+ {
+ mPicture->setValue(avatar_data->fl_image_id);
+ }
+ else
+ {
+ mPicture->setValue("Generic_Person_Large");
+ }
+ setLoaded();
+}
+
+void LLPanelProfileFirstLife::resetData()
+{
+ mDescriptionEdit->setValue(LLStringUtil::null);
+ mPicture->setValue("Generic_Person_Large");
+
+ 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);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+LLPanelProfileNotes::LLPanelProfileNotes()
+: LLPanelProfileTab()
+{
+
+}
+
+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));
+ }
+ }
+}
+
+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);
+ mCurrentNotes = text;
+ mNotesEditor->setValue(mCurrentNotes);
+}
+
+void LLPanelProfileNotes::onSetNotesDirty()
+{
+ mSaveChanges->setEnabled(TRUE);
+ mDiscardChanges->setEnabled(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)));
+
+
+ LLFloater* parent_floater = gFloaterView->getParentFloater(this);
+ if (!parent_floater)
+ {
+ return;
+ }
+
+ LLPanel* panel = parent_floater->findChild<LLPanel>(PANEL_SECONDLIFE, TRUE);
+ LLPanelProfileSecondLife *panel_sl = dynamic_cast<LLPanelProfileSecondLife*>(panel);
+ if (panel_sl)
+ {
+ panel_sl->setNotesSnippet(mCurrentNotes);
+ }
+ }
+ else
+ {
+ LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL;
+ }
+
+ mSaveChanges->setEnabled(FALSE);
+ mDiscardChanges->setEnabled(FALSE);
+}
+
+void LLPanelProfileNotes::onDiscardNotesChanges()
+{
+ setNotesText(mCurrentNotes);
+}
+
+void LLPanelProfileNotes::processProperties(LLAvatarNotes* avatar_notes)
+{
+ mNotesEditor->setValue(avatar_notes->notes);
+ mNotesEditor->setEnabled(TRUE);
+ setLoaded();
+}
+
+void LLPanelProfileNotes::resetData()
+{
+ resetLoading();
+ mNotesEditor->setValue(LLStringUtil::null);
+}
+
+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();
+ }
+ updateBtnsVisibility();
+}
+
+void LLPanelProfile::updateBtnsVisibility()
+{
+}
+
+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();
+
+ updateBtnsVisibility();
+
+ // 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();
+
+ 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::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);
+}
+
+void LLPanelProfile::showClassified(const LLUUID& classified_id, bool edit)
+{
+ if (classified_id.notNull())
+ {
+ mPanelClassifieds->selectClassified(classified_id, edit);
+ }
+ mTabContainer->selectTabPanel(mPanelClassifieds);
+}
+
+
+