summaryrefslogtreecommitdiff
path: root/indra/newview/llmarketplacefunctions.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llmarketplacefunctions.cpp')
-rwxr-xr-xindra/newview/llmarketplacefunctions.cpp1179
1 files changed, 1170 insertions, 9 deletions
diff --git a/indra/newview/llmarketplacefunctions.cpp b/indra/newview/llmarketplacefunctions.cpp
index 4a7a4e268d..8988b80a4f 100755
--- a/indra/newview/llmarketplacefunctions.cpp
+++ b/indra/newview/llmarketplacefunctions.cpp
@@ -29,14 +29,22 @@
#include "llmarketplacefunctions.h"
#include "llagent.h"
+#include "llbufferstream.h"
#include "llhttpclient.h"
+#include "llinventoryfunctions.h"
+#include "llinventoryobserver.h"
+#include "llnotificationsutil.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 +101,508 @@ 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;
+ // Prompt the user with the warning (so they know why things are failing)
+ LLSD subs;
+ subs["[ERROR_REASON]"] = reason;
+ subs["[ERROR_DESCRIPTION]"] = description;
+ LLNotificationsUtil::add("MerchantTransactionFailed", subs);
+
+}
+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() {}
+
+protected:
+ virtual void httpFailure()
+ {
+ if (sBypassMerchant)
+ {
+ // *TODO : Suppress that before shipping!
+ log_SLM_infos("Get /merchant", getStatus(), "SLM Connection error bypassed (debug only)");
+ LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_MERCHANT);
+ }
+ else if (HTTP_NOT_FOUND == getStatus())
+ {
+ log_SLM_infos("Get /merchant", getStatus(), "User is not a merchant");
+ LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_MERCHANT);
+ }
+ else if (HTTP_SERVICE_UNAVAILABLE == getStatus())
+ {
+ log_SLM_infos("Get /merchant", getStatus(), "Merchant is not migrated");
+ LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_MIGRATED_MERCHANT);
+ }
+ else
+ {
+ log_SLM_warning("Get /merchant", getStatus(), getReason(), getContent().get("error_code"), getContent().get("error_description"));
+ LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE);
+ }
+ }
+
+ virtual void httpSuccess()
+ {
+ log_SLM_infos("Get /merchant", getStatus(), "User is a merchant");
+ LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_MERCHANT);
+ }
+
+};
+
+class LLSLMGetListingsResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLSLMGetListingsResponder);
+public:
+
+ LLSLMGetListingsResponder(const LLUUID& folder_id)
+ {
+ mExpectedFolderId = folder_id;
+ }
+
+ virtual void completedRaw(const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+ {
+ LLMarketplaceData::instance().setUpdating(mExpectedFolderId,false);
+
+ LLBufferStream istr(channels, buffer.get());
+ std::stringstream strstrm;
+ strstrm << istr.rdbuf();
+ const std::string body = strstrm.str();
+
+ if (!isGoodStatus())
+ {
+ log_SLM_warning("Get /listings", getStatus(), getReason(), "", body);
+ update_marketplace_category(mExpectedFolderId, false);
+ gInventory.notifyObservers();
+ return;
+ }
+
+ Json::Value root;
+ Json::Reader reader;
+ if (!reader.parse(body,root))
+ {
+ log_SLM_warning("Get /listings", getStatus(), "Json parsing failed", reader.getFormatedErrorMessages(), body);
+ update_marketplace_category(mExpectedFolderId, false);
+ gInventory.notifyObservers();
+ return;
+ }
+
+ log_SLM_infos("Get /listings", getStatus(), 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++;
+ }
+
+ // Update all folders under the root
+ update_marketplace_category(mExpectedFolderId, false);
+ gInventory.notifyObservers();
+ }
+private:
+ LLUUID mExpectedFolderId;
+};
+
+class LLSLMCreateListingsResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLSLMCreateListingsResponder);
+public:
+
+ LLSLMCreateListingsResponder(const LLUUID& folder_id)
+ {
+ mExpectedFolderId = folder_id;
+ }
+
+ virtual void completedRaw(const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+ {
+ LLMarketplaceData::instance().setUpdating(mExpectedFolderId,false);
+
+ LLBufferStream istr(channels, buffer.get());
+ std::stringstream strstrm;
+ strstrm << istr.rdbuf();
+ const std::string body = strstrm.str();
+
+ if (!isGoodStatus())
+ {
+ log_SLM_warning("Post /listings", getStatus(), getReason(), "", body);
+ update_marketplace_category(mExpectedFolderId, false);
+ gInventory.notifyObservers();
+ return;
+ }
+
+ Json::Value root;
+ Json::Reader reader;
+ if (!reader.parse(body,root))
+ {
+ log_SLM_warning("Post /listings", getStatus(), "Json parsing failed", reader.getFormatedErrorMessages(), body);
+ update_marketplace_category(mExpectedFolderId, false);
+ gInventory.notifyObservers();
+ return;
+ }
+
+ log_SLM_infos("Post /listings", getStatus(), 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++;
+ }
+ }
+private:
+ LLUUID mExpectedFolderId;
+};
+
+class LLSLMGetListingResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLSLMGetListingResponder);
+public:
+
+ LLSLMGetListingResponder(const LLUUID& folder_id)
+ {
+ mExpectedFolderId = folder_id;
+ }
+
+ virtual void completedRaw(const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+ {
+ LLMarketplaceData::instance().setUpdating(mExpectedFolderId,false);
+
+ LLBufferStream istr(channels, buffer.get());
+ std::stringstream strstrm;
+ strstrm << istr.rdbuf();
+ const std::string body = strstrm.str();
+
+ if (!isGoodStatus())
+ {
+ log_SLM_warning("Get /listing", getStatus(), getReason(), "", body);
+ update_marketplace_category(mExpectedFolderId, false);
+ gInventory.notifyObservers();
+ return;
+ }
+
+ Json::Value root;
+ Json::Reader reader;
+ if (!reader.parse(body,root))
+ {
+ log_SLM_warning("Get /listing", getStatus(), "Json parsing failed", reader.getFormatedErrorMessages(), body);
+ update_marketplace_category(mExpectedFolderId, false);
+ gInventory.notifyObservers();
+ return;
+ }
+
+ log_SLM_infos("Get /listing", getStatus(), 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++;
+ }
+ }
+private:
+ LLUUID mExpectedFolderId;
+};
+
+class LLSLMUpdateListingsResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLSLMUpdateListingsResponder);
+public:
+
+ LLSLMUpdateListingsResponder(const LLUUID& folder_id, bool expected_listed_state, const LLUUID& expected_version_id)
+ {
+ mExpectedFolderId = folder_id;
+ mExpectedListedState = expected_listed_state;
+ mExpectedVersionUUID = expected_version_id;
+ }
+
+ virtual void completedRaw(const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+ {
+ LLMarketplaceData::instance().setUpdating(mExpectedFolderId,false);
+
+ LLBufferStream istr(channels, buffer.get());
+ std::stringstream strstrm;
+ strstrm << istr.rdbuf();
+ const std::string body = strstrm.str();
+
+ if (!isGoodStatus())
+ {
+ log_SLM_warning("Put /listing", getStatus(), getReason(), "", body);
+ update_marketplace_category(mExpectedFolderId, false);
+ gInventory.notifyObservers();
+ return;
+ }
+
+ Json::Value root;
+ Json::Reader reader;
+ if (!reader.parse(body,root))
+ {
+ log_SLM_warning("Put /listing", getStatus(), "Json parsing failed", reader.getFormatedErrorMessages(), body);
+ update_marketplace_category(mExpectedFolderId, false);
+ gInventory.notifyObservers();
+ return;
+ }
+
+ log_SLM_infos("Put /listing", getStatus(), 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);
+
+ // Show a notification alert if what we got is not what we expected
+ // (this actually doesn't result in an error status from the SLM API protocol)
+ if ((mExpectedListedState != is_listed) || (mExpectedVersionUUID != version_id))
+ {
+ LLSD subs;
+ subs["[URL]"] = edit_url;
+ LLNotificationsUtil::add("AlertMerchantListingNotUpdated", subs);
+ }
+
+ it++;
+ }
+ }
+private:
+ LLUUID mExpectedFolderId;
+ bool mExpectedListedState;
+ LLUUID mExpectedVersionUUID;
+};
+
+class LLSLMAssociateListingsResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLSLMAssociateListingsResponder);
+public:
+
+ LLSLMAssociateListingsResponder(const LLUUID& folder_id, const LLUUID& source_folder_id)
+ {
+ mExpectedFolderId = folder_id;
+ mSourceFolderId = source_folder_id;
+ }
+
+ virtual void completedRaw(const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+ {
+ LLMarketplaceData::instance().setUpdating(mExpectedFolderId,false);
+ LLMarketplaceData::instance().setUpdating(mSourceFolderId,false);
+
+ LLBufferStream istr(channels, buffer.get());
+ std::stringstream strstrm;
+ strstrm << istr.rdbuf();
+ const std::string body = strstrm.str();
+
+ if (!isGoodStatus())
+ {
+ log_SLM_warning("Put /associate_inventory", getStatus(), getReason(), "", body);
+ update_marketplace_category(mExpectedFolderId, false);
+ update_marketplace_category(mSourceFolderId, false);
+ gInventory.notifyObservers();
+ return;
+ }
+
+ Json::Value root;
+ Json::Reader reader;
+ if (!reader.parse(body,root))
+ {
+ log_SLM_warning("Put /associate_inventory", getStatus(), "Json parsing failed", reader.getFormatedErrorMessages(), body);
+ update_marketplace_category(mExpectedFolderId, false);
+ update_marketplace_category(mSourceFolderId, false);
+ gInventory.notifyObservers();
+ return;
+ }
+
+ log_SLM_infos("Put /associate_inventory", getStatus(), 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++;
+ }
+
+ // Always update the source folder so its widget updates
+ update_marketplace_category(mSourceFolderId, false);
+ }
+private:
+ LLUUID mExpectedFolderId; // This is the folder now associated with the id.
+ LLUUID mSourceFolderId; // This is the folder initially associated with the id. Can be LLUUI::null
+};
+
+class LLSLMDeleteListingsResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLSLMDeleteListingsResponder);
+public:
+
+ LLSLMDeleteListingsResponder(const LLUUID& folder_id)
+ {
+ mExpectedFolderId = folder_id;
+ }
+
+ virtual void completedRaw(const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+ {
+ LLMarketplaceData::instance().setUpdating(mExpectedFolderId,false);
+
+ LLBufferStream istr(channels, buffer.get());
+ std::stringstream strstrm;
+ strstrm << istr.rdbuf();
+ const std::string body = strstrm.str();
+
+ if (!isGoodStatus())
+ {
+ log_SLM_warning("Delete /listing", getStatus(), getReason(), "", body);
+ update_marketplace_category(mExpectedFolderId, false);
+ gInventory.notifyObservers();
+ return;
+ }
+
+ Json::Value root;
+ Json::Reader reader;
+ if (!reader.parse(body,root))
+ {
+ log_SLM_warning("Delete /listing", getStatus(), "Json parsing failed", reader.getFormatedErrorMessages(), body);
+ update_marketplace_category(mExpectedFolderId, false);
+ gInventory.notifyObservers();
+ return;
+ }
+
+ log_SLM_infos("Delete /listing", getStatus(), 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++;
+ }
+ }
+private:
+ LLUUID mExpectedFolderId;
+};
+
+// SLM Responders End
+///////////////////////////////////////////////////////////////////////////////
+
namespace LLMarketplaceImport
{
// Basic interface for this namespace
@@ -427,15 +937,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 +1040,654 @@ 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)
+{
+ if (mStatusUpdatedSignal == NULL)
+ {
+ mStatusUpdatedSignal = new status_updated_signal_t();
+ }
+ mStatusUpdatedSignal->connect(cb);
+
+ if (mMarketPlaceStatus != MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED)
+ {
+ // If already initialized, just confirm the status so the callback gets called
+ setSLMStatus(mMarketPlaceStatus);
+ }
+ else
+ {
+ // Initiate SLM connection and set responder
+ mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING;
+ 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()
+{
+ LLSD headers = LLSD::emptyMap();
+ headers["Accept"] = "application/json";
+ headers["Content-Type"] = "application/json";
+
+ // Send request
+ std::string url = getSLMConnectURL("/listings");
+ log_SLM_infos("LLHTTPClient::get", url, "");
+ const LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ setUpdating(marketplacelistings_id,true);
+ LLHTTPClient::get(url, new LLSLMGetListingsResponder(marketplacelistings_id), headers);
+}
+
+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, "");
+ LLUUID folder_id = LLMarketplaceData::instance().getListingFolder(listing_id);
+ setUpdating(folder_id,true);
+ LLHTTPClient::get(url, new LLSLMGetListingResponder(folder_id), 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);
+ setUpdating(folder_id,true);
+ LLHTTPClient::postRaw(url, data, size, new LLSLMCreateListingsResponder(folder_id), 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);
+ setUpdating(folder_id,true);
+ LLHTTPClient::putRaw(url, data, size, new LLSLMUpdateListingsResponder(folder_id, is_listed, version_id), headers);
+}
+
+void LLMarketplaceData::associateSLMListing(const LLUUID& folder_id, S32 listing_id, const LLUUID& source_folder_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"]["is_listed"] = false;
+ root["listing"]["inventory_info"]["listing_folder_id"] = folder_id.asString();
+ root["listing"]["inventory_info"]["version_folder_id"] = LLUUID::null.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);
+ setUpdating(folder_id,true);
+ setUpdating(source_folder_id,true);
+ LLHTTPClient::putRaw(url, data, size, new LLSLMAssociateListingsResponder(folder_id,source_folder_id), 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, "");
+ LLUUID folder_id = LLMarketplaceData::instance().getListingFolder(listing_id);
+ setUpdating(folder_id,true);
+ LLHTTPClient::del(url, new LLSLMDeleteListingsResponder(folder_id), 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();
+ }
+ else
+ {
+ llinfos << "Merov : DD cap = " << url << ", agent = " << gAgentID.asString() << llendl;
+ }
+ 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::getListing(S32 listing_id)
+{
+ if (listing_id == 0)
+ {
+ 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;
+ }
+
+ // Note: if the version_id is cleared, we need to unlist the listing, otherwise, state unchanged
+ bool is_listed = (version_id.isNull() ? false : 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, const LLUUID& source_folder_id, S32 listing_id)
+{
+ if (isListed(folder_id))
+ {
+ // Listing already exists -> exit with error
+ return false;
+ }
+
+ // Post the listing update request to SLM
+ associateSLMListing(folder_id, listing_id, source_folder_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, false);
+ 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, false);
+ 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::isListedAndActive(const LLUUID& folder_id)
+{
+ return (isListed(folder_id) && getActivationState(folder_id));
+}
+
+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);
+ bool active = getActivationState(listing_uuid);
+ LLUUID version_uuid = getVersionFolder(listing_uuid);
+ return (active && ((obj_id == version_uuid) || gInventory.isObjectDescendentOf(obj_id, version_uuid)));
+}
+
+LLUUID LLMarketplaceData::getActiveFolder(const LLUUID& obj_id)
+{
+ S32 depth = depth_nesting_in_marketplace(obj_id);
+ LLUUID listing_uuid = nested_parent_id(obj_id, depth);
+ return (getActivationState(listing_uuid) ? getVersionFolder(listing_uuid) : LLUUID::null);
+}
+
+bool LLMarketplaceData::isUpdating(const LLUUID& folder_id)
+{
+ S32 depth = depth_nesting_in_marketplace(folder_id);
+ if ((depth <= 0) || (depth > 2))
+ {
+ // Only listing and version folders though are concerned by that status
+ return false;
+ }
+ else
+ {
+ const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false);
+ std::set<LLUUID>::iterator it = mPendingUpdateSet.find(marketplace_listings_uuid);
+ if (it != mPendingUpdateSet.end())
+ {
+ // If we're waiting for data for the marketplace listings root, we are in the updating process for all
+ return true;
+ }
+ else
+ {
+ // Check if the listing folder is waiting or data
+ LLUUID listing_uuid = nested_parent_id(folder_id, depth);
+ it = mPendingUpdateSet.find(listing_uuid);
+ return (it != mPendingUpdateSet.end());
+ }
+ }
+}
+
+void LLMarketplaceData::setUpdating(const LLUUID& folder_id, bool isUpdating)
+{
+ std::set<LLUUID>::iterator it = mPendingUpdateSet.find(folder_id);
+ if (it != mPendingUpdateSet.end())
+ {
+ mPendingUpdateSet.erase(it);
+ }
+ if (isUpdating)
+ {
+ mPendingUpdateSet.insert(folder_id);
+ }
+}
+
+// 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, false);
+ 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, false);
+ update_marketplace_category(version_id, false);
+ 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, false);
+ 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;
+}
+
+
+