summaryrefslogtreecommitdiff
path: root/indra/newview/llmarketplacefunctions.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llmarketplacefunctions.cpp')
-rwxr-xr-xindra/newview/llmarketplacefunctions.cpp994
1 files changed, 985 insertions, 9 deletions
diff --git a/indra/newview/llmarketplacefunctions.cpp b/indra/newview/llmarketplacefunctions.cpp
index 4a7a4e268d..7925662663 100755
--- a/indra/newview/llmarketplacefunctions.cpp
+++ b/indra/newview/llmarketplacefunctions.cpp
@@ -29,14 +29,21 @@
#include "llmarketplacefunctions.h"
#include "llagent.h"
+#include "llbufferstream.h"
#include "llhttpclient.h"
+#include "llinventoryfunctions.h"
+#include "llinventoryobserver.h"
#include "llsdserialize.h"
#include "lltimer.h"
#include "lltrans.h"
#include "llviewercontrol.h"
+#include "llviewerinventory.h"
#include "llviewermedia.h"
#include "llviewernetwork.h"
+#include "llviewerregion.h"
+#include "reader.h" // JSON
+#include "writer.h" // JSON
//
// Helpers
@@ -93,6 +100,415 @@ LLSD getMarketplaceStringSubstitutions()
return marketplace_sub_map;
}
+///////////////////////////////////////////////////////////////////////////////
+// SLM Responders
+void log_SLM_warning(const std::string& request, U32 status, const std::string& reason, const std::string& code, const std::string& description)
+{
+ LL_WARNS("SLM") << "SLM API : Responder to " << request << ". status : " << status << ", reason : " << reason << ", code : " << code << ", description : " << description << LL_ENDL;
+}
+void log_SLM_infos(const std::string& request, U32 status, const std::string& body)
+{
+ if (gSavedSettings.getBOOL("MarketplaceListingsLogging"))
+ {
+ LL_INFOS("SLM") << "SLM API : Responder to " << request << ". status : " << status << ", body or description : " << body << LL_ENDL;
+ }
+}
+void log_SLM_infos(const std::string& request, const std::string& url, const std::string& body)
+{
+ if (gSavedSettings.getBOOL("MarketplaceListingsLogging"))
+ {
+ LL_INFOS("SLM") << "SLM API : Sending " << request << ". url : " << url << ", body : " << body << LL_ENDL;
+ }
+}
+
+// Merov: This is a temporary hack used by dev while secondlife-staging is down...
+// *TODO : Suppress that before shipping!
+static bool sBypassMerchant = false;
+
+class LLSLMGetMerchantResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLSLMGetMerchantResponder);
+public:
+
+ LLSLMGetMerchantResponder() {}
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content) { }
+
+ void completedHeader(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (((200 <= status ) && (status < 300)) || sBypassMerchant)
+ {
+ log_SLM_infos("Get /merchant", status, "User is a merchant");
+ LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_MERCHANT);
+ }
+ else if (status == SLMErrorCodes::SLM_NOT_FOUND)
+ {
+ log_SLM_infos("Get /merchant", status, "User is not a merchant");
+ LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_MERCHANT);
+ }
+ else
+ {
+ log_SLM_warning("Get /merchant", status, reason, content.get("error_code"), content.get("error_description"));
+ LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE);
+ }
+ }
+};
+
+class LLSLMGetListingsResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLSLMGetListingsResponder);
+public:
+
+ LLSLMGetListingsResponder() {}
+
+ virtual void completedRaw(U32 status,
+ const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+ {
+ if (!(200 <= status ) && (status < 300))
+ {
+ log_SLM_warning("Get /listings", status, reason, "", "");
+ return;
+ }
+
+ LLBufferStream istr(channels, buffer.get());
+ std::stringstream strstrm;
+ strstrm << istr.rdbuf();
+ const std::string body = strstrm.str();
+
+ Json::Value root;
+ Json::Reader reader;
+ if (!reader.parse(body,root))
+ {
+ log_SLM_warning("Get /listings", status, "Json parsing failed", reader.getFormatedErrorMessages(), body);
+ return;
+ }
+
+ log_SLM_infos("Get /listings", status, body);
+
+ // Extract the info from the Json string
+ Json::ValueIterator it = root["listings"].begin();
+
+ while (it != root["listings"].end())
+ {
+ Json::Value listing = *it;
+
+ int listing_id = listing["id"].asInt();
+ bool is_listed = listing["is_listed"].asBool();
+ std::string edit_url = listing["edit_url"].asString();
+ std::string folder_uuid_string = listing["inventory_info"]["listing_folder_id"].asString();
+ std::string version_uuid_string = listing["inventory_info"]["version_folder_id"].asString();
+
+ LLUUID folder_id(folder_uuid_string);
+ LLUUID version_id(version_uuid_string);
+ if (folder_id.notNull())
+ {
+ LLMarketplaceData::instance().deleteListing(folder_id,false);
+ LLMarketplaceData::instance().addListing(folder_id,listing_id,version_id,is_listed);
+ LLMarketplaceData::instance().setListingURL(folder_id, edit_url);
+ }
+ it++;
+ }
+ }
+};
+
+class LLSLMCreateListingsResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLSLMCreateListingsResponder);
+public:
+
+ LLSLMCreateListingsResponder() {}
+
+ virtual void completedRaw(U32 status,
+ const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+ {
+ if (!(200 <= status ) && (status < 300))
+ {
+ log_SLM_warning("Post /listings", status, reason, "", "");
+ return;
+ }
+
+ LLBufferStream istr(channels, buffer.get());
+ std::stringstream strstrm;
+ strstrm << istr.rdbuf();
+ const std::string body = strstrm.str();
+
+ Json::Value root;
+ Json::Reader reader;
+ if (!reader.parse(body,root))
+ {
+ log_SLM_warning("Post /listings", status, "Json parsing failed", reader.getFormatedErrorMessages(), body);
+ return;
+ }
+
+ log_SLM_infos("Post /listings", status, body);
+
+ // Extract the info from the Json string
+ Json::ValueIterator it = root["listings"].begin();
+
+ while (it != root["listings"].end())
+ {
+ Json::Value listing = *it;
+
+ int listing_id = listing["id"].asInt();
+ bool is_listed = listing["is_listed"].asBool();
+ std::string edit_url = listing["edit_url"].asString();
+ std::string folder_uuid_string = listing["inventory_info"]["listing_folder_id"].asString();
+ std::string version_uuid_string = listing["inventory_info"]["version_folder_id"].asString();
+
+ LLUUID folder_id(folder_uuid_string);
+ LLUUID version_id(version_uuid_string);
+ LLMarketplaceData::instance().addListing(folder_id,listing_id,version_id,is_listed);
+ LLMarketplaceData::instance().setListingURL(folder_id, edit_url);
+ it++;
+ }
+ }
+};
+
+class LLSLMGetListingResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLSLMGetListingResponder);
+public:
+
+ LLSLMGetListingResponder() {}
+
+ virtual void completedRaw(U32 status,
+ const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+ {
+ LLBufferStream istr(channels, buffer.get());
+ std::stringstream strstrm;
+ strstrm << istr.rdbuf();
+ const std::string body = strstrm.str();
+
+ if (!(200 <= status ) && (status < 300))
+ {
+ log_SLM_warning("Get /listing", status, reason, "", body);
+ return;
+ }
+
+ Json::Value root;
+ Json::Reader reader;
+ if (!reader.parse(body,root))
+ {
+ log_SLM_warning("Get /listing", status, "Json parsing failed", reader.getFormatedErrorMessages(), body);
+ return;
+ }
+
+ log_SLM_infos("Get /listing", status, body);
+
+ // Extract the info from the Json string
+ Json::ValueIterator it = root["listings"].begin();
+
+ while (it != root["listings"].end())
+ {
+ Json::Value listing = *it;
+
+ int listing_id = listing["id"].asInt();
+ bool is_listed = listing["is_listed"].asBool();
+ std::string edit_url = listing["edit_url"].asString();
+ std::string folder_uuid_string = listing["inventory_info"]["listing_folder_id"].asString();
+ std::string version_uuid_string = listing["inventory_info"]["version_folder_id"].asString();
+
+ LLUUID folder_id(folder_uuid_string);
+ LLUUID version_id(version_uuid_string);
+
+ // Update that listing
+ LLMarketplaceData::instance().setListingID(folder_id, listing_id);
+ LLMarketplaceData::instance().setVersionFolderID(folder_id, version_id);
+ LLMarketplaceData::instance().setActivationState(folder_id, is_listed);
+ LLMarketplaceData::instance().setListingURL(folder_id, edit_url);
+
+ it++;
+ }
+ }
+};
+
+class LLSLMUpdateListingsResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLSLMUpdateListingsResponder);
+public:
+
+ LLSLMUpdateListingsResponder() {}
+
+ virtual void completedRaw(U32 status,
+ const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+ {
+ LLBufferStream istr(channels, buffer.get());
+ std::stringstream strstrm;
+ strstrm << istr.rdbuf();
+ const std::string body = strstrm.str();
+
+ if (!(200 <= status ) && (status < 300))
+ {
+ log_SLM_warning("Put /listing", status, reason, "", body);
+ return;
+ }
+
+ Json::Value root;
+ Json::Reader reader;
+ if (!reader.parse(body,root))
+ {
+ log_SLM_warning("Put /listing", status, "Json parsing failed", reader.getFormatedErrorMessages(), body);
+ return;
+ }
+
+ log_SLM_infos("Put /listing", status, body);
+
+ // Extract the info from the Json string
+ Json::ValueIterator it = root["listings"].begin();
+
+ while (it != root["listings"].end())
+ {
+ Json::Value listing = *it;
+
+ int listing_id = listing["id"].asInt();
+ bool is_listed = listing["is_listed"].asBool();
+ std::string edit_url = listing["edit_url"].asString();
+ std::string folder_uuid_string = listing["inventory_info"]["listing_folder_id"].asString();
+ std::string version_uuid_string = listing["inventory_info"]["version_folder_id"].asString();
+
+ LLUUID folder_id(folder_uuid_string);
+ LLUUID version_id(version_uuid_string);
+
+ // Update that listing
+ LLMarketplaceData::instance().setListingID(folder_id, listing_id);
+ LLMarketplaceData::instance().setVersionFolderID(folder_id, version_id);
+ LLMarketplaceData::instance().setActivationState(folder_id, is_listed);
+ LLMarketplaceData::instance().setListingURL(folder_id, edit_url);
+
+ it++;
+ }
+ }
+};
+
+class LLSLMAssociateListingsResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLSLMAssociateListingsResponder);
+public:
+
+ LLSLMAssociateListingsResponder() {}
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content) { }
+
+ virtual void completedRaw(U32 status,
+ const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+ {
+ if (!(200 <= status ) && (status < 300))
+ {
+ log_SLM_warning("Put /associate_inventory", status, reason, "", "");
+ return;
+ }
+
+ LLBufferStream istr(channels, buffer.get());
+ std::stringstream strstrm;
+ strstrm << istr.rdbuf();
+ const std::string body = strstrm.str();
+
+ Json::Value root;
+ Json::Reader reader;
+ if (!reader.parse(body,root))
+ {
+ log_SLM_warning("Put /associate_inventory", status, "Json parsing failed", reader.getFormatedErrorMessages(), body);
+ return;
+ }
+
+ log_SLM_infos("Put /associate_inventory", status, body);
+
+ // Extract the info from the Json string
+ Json::ValueIterator it = root["listings"].begin();
+
+ while (it != root["listings"].end())
+ {
+ Json::Value listing = *it;
+
+ int listing_id = listing["id"].asInt();
+ bool is_listed = listing["is_listed"].asBool();
+ std::string edit_url = listing["edit_url"].asString();
+ std::string folder_uuid_string = listing["inventory_info"]["listing_folder_id"].asString();
+ std::string version_uuid_string = listing["inventory_info"]["version_folder_id"].asString();
+
+ LLUUID folder_id(folder_uuid_string);
+ LLUUID version_id(version_uuid_string);
+
+ // Check that the listing ID is not already associated to some other record
+ LLUUID old_listing = LLMarketplaceData::instance().getListingFolder(listing_id);
+ if (old_listing.notNull())
+ {
+ // If it is already used, unlist the old record (we can't have 2 listings with the same listing ID)
+ LLMarketplaceData::instance().deleteListing(old_listing);
+ }
+
+ // Add the new association
+ LLMarketplaceData::instance().addListing(folder_id,listing_id,version_id,is_listed);
+ LLMarketplaceData::instance().setListingURL(folder_id, edit_url);
+ it++;
+ }
+ }
+};
+
+class LLSLMDeleteListingsResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLSLMDeleteListingsResponder);
+public:
+
+ LLSLMDeleteListingsResponder() {}
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content) { }
+
+ virtual void completedRaw(U32 status,
+ const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+ {
+ LLBufferStream istr(channels, buffer.get());
+ std::stringstream strstrm;
+ strstrm << istr.rdbuf();
+ const std::string body = strstrm.str();
+
+ if (!(200 <= status ) && (status < 300))
+ {
+ log_SLM_warning("Delete /listing", status, reason, "", body);
+ return;
+ }
+
+ Json::Value root;
+ Json::Reader reader;
+ if (!reader.parse(body,root))
+ {
+ log_SLM_warning("Delete /listing", status, "Json parsing failed", reader.getFormatedErrorMessages(), body);
+ return;
+ }
+
+ log_SLM_infos("Delete /listing", status, body);
+
+ // Extract the info from the Json string
+ Json::ValueIterator it = root["listings"].begin();
+
+ while (it != root["listings"].end())
+ {
+ Json::Value listing = *it;
+
+ int listing_id = listing["id"].asInt();
+ LLUUID folder_id = LLMarketplaceData::instance().getListingFolder(listing_id);
+ LLMarketplaceData::instance().deleteListing(folder_id);
+
+ it++;
+ }
+ }
+};
+
+// SLM Responders End
+///////////////////////////////////////////////////////////////////////////////
+
namespace LLMarketplaceImport
{
// Basic interface for this namespace
@@ -427,15 +843,15 @@ void LLMarketplaceInventoryImporter::initialize()
return;
}
- if (!LLMarketplaceImport::hasSessionCookie())
- {
- mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING;
- LLMarketplaceImport::establishMarketplaceSessionCookie();
- }
- else
- {
- mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_MERCHANT;
- }
+ if (!LLMarketplaceImport::hasSessionCookie())
+ {
+ mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING;
+ LLMarketplaceImport::establishMarketplaceSessionCookie();
+ }
+ else
+ {
+ mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_MERCHANT;
+ }
}
void LLMarketplaceInventoryImporter::reinitializeAndTriggerImport()
@@ -530,3 +946,563 @@ void LLMarketplaceInventoryImporter::updateImport()
}
}
+//
+// Direct Delivery : Marketplace tuples and data
+//
+class LLMarketplaceInventoryObserver : public LLInventoryObserver
+{
+public:
+ LLMarketplaceInventoryObserver() {}
+ virtual ~LLMarketplaceInventoryObserver() {}
+ virtual void changed(U32 mask);
+};
+
+void LLMarketplaceInventoryObserver::changed(U32 mask)
+{
+ // When things are changed in the inventory, this can trigger a host of changes in the marketplace listings folder:
+ // * stock counts changing : no copy items coming in and out will change the stock count on folders
+ // * version and listing folders : moving those might invalidate the marketplace data itself
+ // Since we should cannot raise inventory change while the observer is called (the list will be cleared
+ // once observers are called) we need to raise a flag in the inventory to signal that things have been dirtied.
+
+ // That's the only changes that really do make sense for marketplace to worry about
+ if ((mask & (LLInventoryObserver::INTERNAL | LLInventoryObserver::STRUCTURE)) != 0)
+ {
+ const std::set<LLUUID>& changed_items = gInventory.getChangedIDs();
+
+ std::set<LLUUID>::const_iterator id_it = changed_items.begin();
+ std::set<LLUUID>::const_iterator id_end = changed_items.end();
+ for (;id_it != id_end; ++id_it)
+ {
+ LLInventoryObject* obj = gInventory.getObject(*id_it);
+ if (LLAssetType::AT_CATEGORY == obj->getType())
+ {
+ // If it's a folder known to the marketplace, let's check it's in proper shape
+ if (LLMarketplaceData::instance().isListed(*id_it) || LLMarketplaceData::instance().isVersionFolder(*id_it))
+ {
+ LLInventoryCategory* cat = (LLInventoryCategory*)(obj);
+ validate_marketplacelistings(cat);
+ }
+ }
+ else
+ {
+ // If it's not a category, it's an item...
+ LLInventoryItem* item = (LLInventoryItem*)(obj);
+ // If it's a no copy item, we may need to update the label count of marketplace listings
+ if (!item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID()))
+ {
+ LLMarketplaceData::instance().setDirtyCount();
+ }
+ }
+ }
+ }
+}
+
+// Tuple == Item
+LLMarketplaceTuple::LLMarketplaceTuple() :
+ mListingFolderId(),
+ mListingId(0),
+ mVersionFolderId(),
+ mIsActive(false),
+ mEditURL("")
+{
+}
+
+LLMarketplaceTuple::LLMarketplaceTuple(const LLUUID& folder_id) :
+ mListingFolderId(folder_id),
+ mListingId(0),
+ mVersionFolderId(),
+ mIsActive(false),
+ mEditURL("")
+{
+}
+
+LLMarketplaceTuple::LLMarketplaceTuple(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, bool is_listed) :
+ mListingFolderId(folder_id),
+ mListingId(listing_id),
+ mVersionFolderId(version_id),
+ mIsActive(is_listed),
+ mEditURL("")
+{
+}
+
+
+// Data map
+LLMarketplaceData::LLMarketplaceData() :
+ mMarketPlaceStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED),
+ mStatusUpdatedSignal(NULL),
+ mDirtyCount(false)
+{
+ mInventoryObserver = new LLMarketplaceInventoryObserver;
+ gInventory.addObserver(mInventoryObserver);
+}
+
+LLMarketplaceData::~LLMarketplaceData()
+{
+ gInventory.removeObserver(mInventoryObserver);
+}
+
+void LLMarketplaceData::initializeSLM(const status_updated_signal_t::slot_type& cb)
+{
+ mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING;
+ if (mStatusUpdatedSignal == NULL)
+ {
+ mStatusUpdatedSignal = new status_updated_signal_t();
+ }
+ mStatusUpdatedSignal->connect(cb);
+
+ std::string url = getSLMConnectURL("/merchant");
+ log_SLM_infos("LLHTTPClient::get", url, "");
+ LLHTTPClient::get(url, new LLSLMGetMerchantResponder(), LLSD());
+}
+
+// Get/Post/Put requests to the SLM Server using the SLM API
+void LLMarketplaceData::getSLMListings()
+{
+ std::string url = getSLMConnectURL("/listings");
+ log_SLM_infos("LLHTTPClient::get", url, "");
+ LLHTTPClient::get(url, new LLSLMGetListingsResponder(), LLSD());
+}
+
+void LLMarketplaceData::getSLMListing(S32 listing_id)
+{
+ LLSD headers = LLSD::emptyMap();
+ headers["Accept"] = "application/json";
+ headers["Content-Type"] = "application/json";
+
+ // Send request
+ std::string url = getSLMConnectURL("/listing/") + llformat("%d",listing_id);
+ log_SLM_infos("LLHTTPClient::get", url, "");
+ LLHTTPClient::get(url, new LLSLMGetListingResponder(), headers);
+}
+
+void LLMarketplaceData::createSLMListing(const LLUUID& folder_id)
+{
+ LLSD headers = LLSD::emptyMap();
+ headers["Accept"] = "application/json";
+ headers["Content-Type"] = "application/json";
+
+ LLViewerInventoryCategory* category = gInventory.getCategory(folder_id);
+
+ Json::Value root;
+ Json::FastWriter writer;
+
+ root["listing"]["name"] = category->getName();
+ root["listing"]["inventory_info"]["listing_folder_id"] = category->getUUID().asString();
+
+ std::string json_str = writer.write(root);
+
+ // postRaw() takes ownership of the buffer and releases it later.
+ size_t size = json_str.size();
+ U8 *data = new U8[size];
+ memcpy(data, (U8*)(json_str.c_str()), size);
+
+ // Send request
+ std::string url = getSLMConnectURL("/listings");
+ log_SLM_infos("LLHTTPClient::postRaw", url, json_str);
+ LLHTTPClient::postRaw(url, data, size, new LLSLMCreateListingsResponder(), headers);
+}
+
+void LLMarketplaceData::updateSLMListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, bool is_listed)
+{
+ LLSD headers = LLSD::emptyMap();
+ headers["Accept"] = "application/json";
+ headers["Content-Type"] = "application/json";
+
+ Json::Value root;
+ Json::FastWriter writer;
+
+ // Note : we're assuming that sending unchanged info won't break anything server side...
+ root["listing"]["id"] = listing_id;
+ root["listing"]["is_listed"] = is_listed;
+ root["listing"]["inventory_info"]["listing_folder_id"] = folder_id.asString();
+ root["listing"]["inventory_info"]["version_folder_id"] = version_id.asString();
+
+ std::string json_str = writer.write(root);
+
+ // postRaw() takes ownership of the buffer and releases it later.
+ size_t size = json_str.size();
+ U8 *data = new U8[size];
+ memcpy(data, (U8*)(json_str.c_str()), size);
+
+ // Send request
+ std::string url = getSLMConnectURL("/listing/") + llformat("%d",listing_id);
+ log_SLM_infos("LLHTTPClient::putRaw", url, json_str);
+ LLHTTPClient::putRaw(url, data, size, new LLSLMUpdateListingsResponder(), headers);
+}
+
+void LLMarketplaceData::associateSLMListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id)
+{
+ LLSD headers = LLSD::emptyMap();
+ headers["Accept"] = "application/json";
+ headers["Content-Type"] = "application/json";
+
+ Json::Value root;
+ Json::FastWriter writer;
+
+ // Note : we're assuming that sending unchanged info won't break anything server side...
+ root["listing"]["id"] = listing_id;
+ root["listing"]["inventory_info"]["listing_folder_id"] = folder_id.asString();
+ root["listing"]["inventory_info"]["version_folder_id"] = version_id.asString();
+
+ std::string json_str = writer.write(root);
+
+ // postRaw() takes ownership of the buffer and releases it later.
+ size_t size = json_str.size();
+ U8 *data = new U8[size];
+ memcpy(data, (U8*)(json_str.c_str()), size);
+
+ // Send request
+ std::string url = getSLMConnectURL("/associate_inventory/") + llformat("%d",listing_id);
+ log_SLM_infos("LLHTTPClient::putRaw", url, json_str);
+ LLHTTPClient::putRaw(url, data, size, new LLSLMAssociateListingsResponder(), headers);
+}
+
+void LLMarketplaceData::deleteSLMListing(S32 listing_id)
+{
+ LLSD headers = LLSD::emptyMap();
+ headers["Accept"] = "application/json";
+ headers["Content-Type"] = "application/json";
+
+ // Send request
+ std::string url = getSLMConnectURL("/listing/") + llformat("%d",listing_id);
+ log_SLM_infos("LLHTTPClient::del", url, "");
+ LLHTTPClient::del(url, new LLSLMDeleteListingsResponder(), headers);
+}
+
+std::string LLMarketplaceData::getSLMConnectURL(const std::string& route)
+{
+ std::string url("");
+ LLViewerRegion *regionp = gAgent.getRegion();
+ if (regionp)
+ {
+ // Get DirectDelivery cap
+ url = regionp->getCapability("DirectDelivery");
+ // *TODO : Take this DirectDelivery cap coping mechanism hack out
+ if (url == "")
+ {
+ url = "https://marketplace.secondlife-staging.com/api/1/viewer/" + gAgentID.asString();
+ }
+ url += route;
+ }
+ return url;
+}
+
+void LLMarketplaceData::setSLMStatus(U32 status)
+{
+ mMarketPlaceStatus = status;
+ if (mStatusUpdatedSignal)
+ {
+ (*mStatusUpdatedSignal)();
+ }
+}
+
+// Creation / Deletion / Update
+// Methods publicly called
+bool LLMarketplaceData::createListing(const LLUUID& folder_id)
+{
+ if (isListed(folder_id))
+ {
+ // Listing already exists -> exit with error
+ return false;
+ }
+
+ // Post the listing creation request to SLM
+ createSLMListing(folder_id);
+
+ return true;
+}
+
+bool LLMarketplaceData::clearListing(const LLUUID& folder_id)
+{
+ if (folder_id.isNull())
+ {
+ // Folder doesn't exists -> exit with error
+ return false;
+ }
+
+ // Folder id can be the root of the listing or not so we need to retrieve the root first
+ S32 depth = depth_nesting_in_marketplace(folder_id);
+ LLUUID listing_uuid = (isListed(folder_id) ? folder_id : nested_parent_id(folder_id, depth));
+ S32 listing_id = getListingID(listing_uuid);
+
+ if (listing_id == 0)
+ {
+ // Listing doesn't exists -> exit with error
+ return false;
+ }
+
+ // Update the SLM Server so that this listing is deleted (actually, archived...)
+ deleteSLMListing(listing_id);
+
+ return true;
+}
+
+bool LLMarketplaceData::getListing(const LLUUID& folder_id)
+{
+ if (folder_id.isNull())
+ {
+ // Folder doesn't exists -> exit with error
+ return false;
+ }
+
+ // Folder id can be the root of the listing or not so we need to retrieve the root first
+ S32 depth = depth_nesting_in_marketplace(folder_id);
+ LLUUID listing_uuid = (isListed(folder_id) ? folder_id : nested_parent_id(folder_id, depth));
+ S32 listing_id = getListingID(listing_uuid);
+
+ if (listing_id == 0)
+ {
+ // Listing doesn't exists -> exit with error
+ return false;
+ }
+
+ // Get listing data from SLM
+ getSLMListing(listing_id);
+
+ return true;
+}
+
+bool LLMarketplaceData::activateListing(const LLUUID& folder_id, bool activate)
+{
+ // Folder id can be the root of the listing or not so we need to retrieve the root first
+ S32 depth = depth_nesting_in_marketplace(folder_id);
+ LLUUID listing_uuid = nested_parent_id(folder_id, depth);
+ S32 listing_id = getListingID(listing_uuid);
+ if (listing_id == 0)
+ {
+ // Listing doesn't exists -> exit with error
+ return false;
+ }
+
+ LLUUID version_uuid = getVersionFolder(listing_uuid);
+
+ // Post the listing update request to SLM
+ updateSLMListing(listing_uuid, listing_id, version_uuid, activate);
+
+ return true;
+}
+
+bool LLMarketplaceData::setVersionFolder(const LLUUID& folder_id, const LLUUID& version_id)
+{
+ // Folder id can be the root of the listing or not so we need to retrieve the root first
+ S32 depth = depth_nesting_in_marketplace(folder_id);
+ LLUUID listing_uuid = nested_parent_id(folder_id, depth);
+ S32 listing_id = getListingID(listing_uuid);
+ if (listing_id == 0)
+ {
+ // Listing doesn't exists -> exit with error
+ return false;
+ }
+
+ bool is_listed = getActivationState(listing_uuid);
+
+ // Post the listing update request to SLM
+ updateSLMListing(listing_uuid, listing_id, version_id, is_listed);
+
+ return true;
+}
+
+bool LLMarketplaceData::associateListing(const LLUUID& folder_id, S32 listing_id)
+{
+ if (isListed(folder_id))
+ {
+ // Listing already exists -> exit with error
+ return false;
+ }
+
+ // Post the listing update request to SLM
+ LLUUID version_id;
+ associateSLMListing(folder_id, listing_id, version_id);
+
+ return true;
+}
+
+// Methods privately called or called by SLM responders to perform changes
+bool LLMarketplaceData::addListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& version_id, bool is_listed)
+{
+ if (isListed(folder_id))
+ {
+ // Listing already exists -> exit with error
+ return false;
+ }
+ mMarketplaceItems[folder_id] = LLMarketplaceTuple(folder_id, listing_id, version_id, is_listed);
+
+ update_marketplace_category(folder_id);
+ gInventory.notifyObservers();
+ return true;
+}
+
+bool LLMarketplaceData::deleteListing(const LLUUID& folder_id, bool update_slm)
+{
+ if (!isListed(folder_id))
+ {
+ // Listing doesn't exist -> exit with error
+ return false;
+ }
+ mMarketplaceItems.erase(folder_id);
+
+ if (update_slm)
+ {
+ update_marketplace_category(folder_id);
+ gInventory.notifyObservers();
+ }
+ return true;
+}
+
+// Accessors
+bool LLMarketplaceData::getActivationState(const LLUUID& folder_id)
+{
+ // Listing folder case
+ if (isListed(folder_id))
+ {
+ marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
+ return (it->second).mIsActive;
+ }
+ // We need to iterate through the list to check it's not a version folder
+ marketplace_items_list_t::iterator it = mMarketplaceItems.begin();
+ while (it != mMarketplaceItems.end())
+ {
+ if ((it->second).mVersionFolderId == folder_id)
+ {
+ return (it->second).mIsActive;
+ }
+ it++;
+ }
+ return false;
+}
+
+S32 LLMarketplaceData::getListingID(const LLUUID& folder_id)
+{
+ marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
+ return (it == mMarketplaceItems.end() ? 0 : (it->second).mListingId);
+}
+
+LLUUID LLMarketplaceData::getVersionFolder(const LLUUID& folder_id)
+{
+ marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
+ return (it == mMarketplaceItems.end() ? LLUUID::null : (it->second).mVersionFolderId);
+}
+
+// Reverse lookup : find the listing folder id from the listing id
+LLUUID LLMarketplaceData::getListingFolder(S32 listing_id)
+{
+ marketplace_items_list_t::iterator it = mMarketplaceItems.begin();
+ while (it != mMarketplaceItems.end())
+ {
+ if ((it->second).mListingId == listing_id)
+ {
+ return (it->second).mListingFolderId;
+ }
+ it++;
+ }
+ return LLUUID::null;
+}
+
+std::string LLMarketplaceData::getListingURL(const LLUUID& folder_id)
+{
+ S32 depth = depth_nesting_in_marketplace(folder_id);
+ LLUUID listing_uuid = nested_parent_id(folder_id, depth);
+
+ marketplace_items_list_t::iterator it = mMarketplaceItems.find(listing_uuid);
+ return (it == mMarketplaceItems.end() ? "" : (it->second).mEditURL);
+}
+
+bool LLMarketplaceData::isListed(const LLUUID& folder_id)
+{
+ marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
+ return (it != mMarketplaceItems.end());
+}
+
+bool LLMarketplaceData::isVersionFolder(const LLUUID& folder_id)
+{
+ marketplace_items_list_t::iterator it = mMarketplaceItems.begin();
+ while (it != mMarketplaceItems.end())
+ {
+ if ((it->second).mVersionFolderId == folder_id)
+ {
+ return true;
+ }
+ it++;
+ }
+ return false;
+}
+
+bool LLMarketplaceData::isInActiveFolder(const LLUUID& obj_id)
+{
+ S32 depth = depth_nesting_in_marketplace(obj_id);
+ LLUUID listing_uuid = nested_parent_id(obj_id, depth);
+ // *TODO: use true activation status once SLM is in decent shape again
+ //bool active = getActivationState(listing_uuid); Hack waiting for SLM to allow activation again...
+ bool active = true;
+ LLUUID version_uuid = getVersionFolder(listing_uuid);
+ return (active && gInventory.isObjectDescendentOf(obj_id, version_uuid));
+}
+
+// Private Modifiers
+bool LLMarketplaceData::setListingID(const LLUUID& folder_id, S32 listing_id)
+{
+ marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
+ if (it == mMarketplaceItems.end())
+ {
+ return false;
+ }
+
+ (it->second).mListingId = listing_id;
+
+ update_marketplace_category(folder_id);
+ gInventory.notifyObservers();
+ return true;
+}
+
+bool LLMarketplaceData::setVersionFolderID(const LLUUID& folder_id, const LLUUID& version_id)
+{
+ marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
+ if (it == mMarketplaceItems.end())
+ {
+ return false;
+ }
+
+ LLUUID old_version_id = (it->second).mVersionFolderId;
+ if (old_version_id == version_id)
+ {
+ return false;
+ }
+
+ (it->second).mVersionFolderId = version_id;
+
+ update_marketplace_category(old_version_id);
+ update_marketplace_category(version_id);
+ gInventory.notifyObservers();
+ return true;
+}
+
+bool LLMarketplaceData::setActivationState(const LLUUID& folder_id, bool activate)
+{
+ marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
+ if (it == mMarketplaceItems.end())
+ {
+ return false;
+ }
+
+ (it->second).mIsActive = activate;
+
+ update_marketplace_category((it->second).mListingFolderId);
+ gInventory.notifyObservers();
+ return true;
+}
+
+bool LLMarketplaceData::setListingURL(const LLUUID& folder_id, const std::string& edit_url)
+{
+ marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
+ if (it == mMarketplaceItems.end())
+ {
+ return false;
+ }
+
+ (it->second).mEditURL = edit_url;
+ return true;
+}
+
+
+