diff options
Diffstat (limited to 'indra/newview/llmarketplacefunctions.cpp')
-rwxr-xr-x | indra/newview/llmarketplacefunctions.cpp | 1179 |
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; +} + + + |