summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
authorAndrey Kleshchev <andreykproductengine@lindenlab.com>2022-03-25 21:54:07 +0200
committerAndrey Kleshchev <andreykproductengine@lindenlab.com>2022-03-25 22:29:17 +0200
commitb7bd7029eb7c38a70c20278298ee650e0dbbdfa1 (patch)
treed5910a5367289416d4161cdd2dc3c1d596678a0f /indra/newview
parent7e040000c6c3471f53011994d95bc3b3d721782d (diff)
SL-16937 New Profile capability, image uploader cap #5
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/llpanelprofile.cpp190
-rw-r--r--indra/newview/llviewerregion.cpp1
2 files changed, 174 insertions, 17 deletions
diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp
index 5e85fa227e..5986ef7171 100644
--- a/indra/newview/llpanelprofile.cpp
+++ b/indra/newview/llpanelprofile.cpp
@@ -47,6 +47,9 @@
#include "lltoggleablemenu.h"
#include "llgrouplist.h"
+// Image
+#include "llimagej2c.h"
+
// Newview
#include "llagent.h" //gAgent
#include "llagentpicksinfo.h"
@@ -65,6 +68,7 @@
#include "lltrans.h"
#include "llviewercontrol.h"
#include "llviewermenu.h" //is_agent_mappable
+#include "llviewertexturelist.h"
#include "llvoiceclient.h"
#include "llweb.h"
@@ -87,6 +91,7 @@ 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";
//////////////////////////////////////////////////////////////////////////
@@ -264,7 +269,7 @@ void put_avatar_properties_coro(std::string cap_url, LLUUID agent_id, LLSD data)
{
LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
- httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("request_avatar_properties_coro", httpPolicy));
+ httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("put_avatar_properties_coro", httpPolicy));
LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
LLCore::HttpHeaders::ptr_t httpHeaders;
@@ -285,6 +290,136 @@ void put_avatar_properties_coro(std::string cap_url, LLUUID agent_id, LLSD data)
}
}
+enum EProfileImageType
+{
+ PROFILE_IMAGE_SL,
+ PROFILE_IMAGE_FL,
+};
+
+void post_profile_image_coro(std::string cap_url, EProfileImageType type, std::string &path_to_image)
+{
+ 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 first_data;
+ switch (type)
+ {
+ case PROFILE_IMAGE_SL:
+ first_data["profile-image-asset"] = "sl_image_id";
+ break;
+ case PROFILE_IMAGE_FL:
+ first_data["profile-image-asset"] = "fl_image_id";
+ break;
+ }
+
+ 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)
+ {
+ LL_WARNS("AvatarProperties") << "Failed to get uploader cap " << status.toString() << LL_ENDL;
+ return;
+ }
+ if (!result.has("uploader"))
+ {
+ LL_WARNS("AvatarProperties") << "Failed to get uploader cap, response contains no data." << LL_ENDL;
+ return;
+ }
+ std::string uploader_cap = result["uploader"].asString();
+ if (uploader_cap.empty())
+ {
+ LL_WARNS("AvatarProperties") << "Failed to get uploader cap, cap invalid." << LL_ENDL;
+ return;
+ }
+
+ // Load the image
+
+
+ // Convert and validate: the upload procedure is the same for all images,
+ U32 codec = LLImageBase::getCodecFromExtension(gDirUtilp->getExtension(path_to_image));
+
+ LLPointer<LLImageFormatted> image = LLImageFormatted::createFromType(codec);
+ if (image.isNull())
+ {
+ LL_WARNS("AvatarProperties") << "Couldn't open the image to be uploaded." << LL_ENDL;
+ return;
+ }
+ if (!image->load(path_to_image))
+ {
+ LL_WARNS("AvatarProperties") << "Couldn't load the image to be uploaded." << LL_ENDL;
+ return;
+ }
+ // Decompress or expand it in a raw image structure
+ LLPointer<LLImageRaw> raw_image = new LLImageRaw;
+ if (!image->decode(raw_image, 0.0f))
+ {
+ LL_WARNS("AvatarProperties") << "Couldn't decode the image to be uploaded." << LL_ENDL;
+ return;
+ }
+
+ // Check the image constraints
+ if ((image->getComponents() != 3) && (image->getComponents() != 4))
+ {
+ LL_WARNS("AvatarProperties") << "Image files with less than 3 or more than 4 components are not supported." << LL_ENDL;
+ return;
+ }
+
+ const S32 MAX_DIM = 256;
+ raw_image->biasedScaleToPowerOfTwo(MAX_DIM); // should it actually be power of two?
+
+ // Convert to j2c (JPEG2000)
+ LLPointer<LLImageJ2C> j2cImage = LLViewerTextureList::convertToUploadFile(raw_image);
+ if (j2cImage.isNull())
+ {
+ LL_WARNS("AvatarProperties") << "Couldn't convert the image to jpeg2000." << LL_ENDL;
+ return;
+ }
+ S32 data_size = j2cImage->getDataSize();
+ /*U8 *data_start = compressedImage->getData();
+ LLSD::Binary bin_data(data_start, data_start + data_size);*/
+ LLCore::BufferArray::ptr_t bin_data(new LLCore::BufferArray);
+ LLCore::BufferArrayStream bas(bin_data.get());
+
+ U8* image_data = j2cImage->getData();
+ for (S32 i = 0; i < j2cImage->getDataSize(); ++i)
+ {
+ bas << image_data[i];
+ }
+
+ LLCore::HttpRequest::ptr_t uploaderhttpRequest(new LLCore::HttpRequest);
+ LLCore::HttpHeaders::ptr_t uploaderhttpHeaders(new LLCore::HttpHeaders);
+ LLCore::HttpOptions::ptr_t uploaderhttpOpts(new LLCore::HttpOptions);
+ uploaderhttpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, "application/jp2");
+ uploaderhttpHeaders->append(HTTP_OUT_HEADER_CONTENT_LENGTH, llformat("%d", data_size));
+ uploaderhttpOpts->setFollowRedirects(true);
+
+
+ result = httpAdapter->postAndSuspend(uploaderhttpRequest, uploader_cap, bin_data, uploaderhttpOpts, uploaderhttpHeaders);
+
+ httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+ status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+
+ if (!status)
+ {
+ LL_WARNS("AvatarProperties") << "Failed to upload image " << status.toString() << LL_ENDL;
+ return;
+ }
+
+ // Todo:
+ // handle 'message':'No Error','state':'failure'
+ // handle 'state':'complete'
+
+ LL_WARNS() << result << LL_ENDL;
+}
+
//////////////////////////////////////////////////////////////////////////
// LLProfileHandler
@@ -565,32 +700,53 @@ void LLPanelProfileSecondLife::apply(LLAvatarData* data)
{
// Might be a better idea to accumulate changes in floater
// instead of sending a request per tab
- std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP);
- if (!cap_url.empty())
+
+ LLSD params = LLSDMap();
+ // we have an image, check if it is local. Server won't recognize local ids.
+ if (data->image_id != mSecondLifePic->getImageAssetID()
+ && !LLLocalBitmapMgr::getInstance()->isLocal(mSecondLifePic->getImageAssetID()))
+ {
+ params["sl_image_id"] = mSecondLifePic->getImageAssetID();
+ }
+ if (data->about_text != mDescriptionEdit->getValue().asString())
+ {
+ params["sl_about_text"] = mDescriptionEdit->getValue().asString();
+ }
+ if ((bool)data->allow_publish != mShowInSearchCheckbox->getValue().asBoolean())
+ {
+ params["allow_publish"] = mShowInSearchCheckbox->getValue().asBoolean();
+ }
+ if (!params.emptyMap())
{
- LLSD params = LLSDMap();
- if (data->image_id != mSecondLifePic->getImageAssetID())
+ std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP);
+ if (!cap_url.empty())
{
- params["sl_image_id"] = mSecondLifePic->getImageAssetID();
+ LLCoros::instance().launch("putAgentUserInfoCoro",
+ boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), params));
}
- if (data->about_text != mDescriptionEdit->getValue().asString())
+ else
{
- params["sl_about_text"] = mDescriptionEdit->getValue().asString();
+ LL_WARNS() << "Failed to update profile data, no cap found" << LL_ENDL;
}
- if ((bool)data->allow_publish != mShowInSearchCheckbox->getValue().asBoolean())
+ }
+
+ // Only if image is local
+ if (data->image_id != mSecondLifePic->getImageAssetID()
+ && LLLocalBitmapMgr::getInstance()->isLocal(mSecondLifePic->getImageAssetID()))
+ {
+ std::string cap_url = gAgent.getRegionCapability(PROFILE_IMAGE_UPLOAD_CAP);
+ if (!cap_url.empty())
{
- params["allow_publish"] = mShowInSearchCheckbox->getValue().asBoolean();
+ // Temp path, will add a proper one once UI updates to support this
+ std::string full_path = gDirUtilp->findSkinnedFilename("textures", "icons/Default_Outfit_Photo.png");
+ LLCoros::instance().launch("postAgentUserImageCoro",
+ boost::bind(post_profile_image_coro, cap_url, PROFILE_IMAGE_SL, full_path));
}
- if (!params.emptyMap())
+ else
{
- LLCoros::instance().launch("putAgentUserInfoCoro",
- boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), params));
+ LL_WARNS() << "Failed to upload sl profile image, no cap found" << LL_ENDL;
}
}
- else
- {
- LL_WARNS() << "Failed to update profile data, no cap found" << LL_ENDL;
- }
}
}
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index 747cd6e626..779a8d8594 100644
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -3044,6 +3044,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
capabilityNames.append("UpdateScriptTask");
capabilityNames.append("UpdateSettingsAgentInventory");
capabilityNames.append("UpdateSettingsTaskInventory");
+ capabilityNames.append("UploadAgentProfileImage");
capabilityNames.append("UploadBakedTexture");
capabilityNames.append("UserInfo");
capabilityNames.append("ViewerAsset");