12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740 |
- /**
- * @file llmarketplacefunctions.cpp
- * @brief Implementation of assorted functions related to the marketplace
- *
- * $LicenseInfo:firstyear=2012&license=viewergpl$
- *
- * Copyright (c) 2012, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "llviewerprecompiledheaders.h"
- #include "llmarketplacefunctions.h"
- #include "llcallbacklist.h"
- #include "llcorehttputil.h"
- #include "llnotifications.h"
- #include "llsdserialize.h"
- #include "lltrans.h"
- #include "llagent.h"
- #include "llgridmanager.h"
- #include "llinventorybridge.h"
- #include "llinventorymodel.h"
- #include "llviewerinventory.h"
- #include "llviewercontrol.h"
- #include "llviewermedia.h"
- #include "llweb.h"
- // static variable members
- std::string LLMarketplace::sMessage;
- LLUUID LLMarketplace::sMarketplaceListingId;
- // Helpers
- // Get the version folder: if there is only one subfolder, we will use it as a
- // version folder
- LLUUID getVersionFolderIfUnique(const LLUUID& folder_id)
- {
- LLUUID version_id;
- LLInventoryModel::cat_array_t* categories;
- LLInventoryModel::item_array_t* items;
- gInventory.getDirectDescendentsOf(folder_id, categories, items);
- if (categories && categories->size() == 1)
- {
- version_id = categories->begin()->get()->getUUID();
- }
- else
- {
- gNotifications.add("AlertMerchantListingActivateRequired");
- }
- return version_id;
- }
- void log_SLM_warning(const std::string& request, U32 status,
- const std::string& reason, const std::string& code,
- std::string message)
- {
- llwarns << "SLM API: Responder to: " << request << " - Status: " << status
- << " - Reason: " << reason << " - Code: " << code
- << " - Description: " << message << llendl;
- LLStringUtil::replaceString(message, std::string("["), std::string("- "));
- LLStringUtil::replaceString(message, std::string("\""), LLStringUtil::null);
- LLStringUtil::replaceString(message, std::string(","), "\n-");
- LLStringUtil::replaceString(message, std::string("]"), LLStringUtil::null);
- if (message.length() > 512)
- {
- // We do not show long messages in the alert (unlikely to be readable).
- // The full message string will be in the log though.
- message = message.substr(0, 504) + "\n.../...";
- }
- LLSD subs;
- subs["ERROR_REASON"] = reason;
- subs["ERROR_DESCRIPTION"] = message;
- gNotifications.add(status == 422 ? "MerchantUnprocessableEntity"
- : "MerchantTransactionFailed", subs);
- }
- ///////////////////////////////////////////////////////////////////////////////
- // New Marketplace Listings API tuples and data
- class LLMarketplaceInventoryObserver final : public LLInventoryObserver
- {
- protected:
- LOG_CLASS(LLMarketplaceInventoryObserver);
- public:
- LLMarketplaceInventoryObserver() {}
- ~LLMarketplaceInventoryObserver() override {}
- void changed(U32 mask) override;
- };
- void LLMarketplaceInventoryObserver::changed(U32 mask)
- {
- LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
- // When things are added to the marketplace, we might need to re-validate
- // and fix the containing listings
- if (mask & LLInventoryObserver::ADD)
- {
- const uuid_list_t& changed_items = gInventory.getChangedIDs();
- // First, count the number of items in this list...
- S32 count = 0;
- for (uuid_list_t::const_iterator it = changed_items.begin(),
- end = changed_items.end();
- it != end; ++it)
- {
- LLInventoryObject* obj = gInventory.getObject(*it);
- if (obj && obj->getType() != LLAssetType::AT_CATEGORY)
- {
- ++count;
- }
- }
- // Then, decrement the folders of that amount. Note that among all of
- // those, only one folder will be a listing folder (if at all), the
- // others will be ignored by the decrement method.
- for (uuid_list_t::const_iterator it = changed_items.begin(),
- end = changed_items.end();
- it != end; ++it)
- {
- LLInventoryObject* obj = gInventory.getObject(*it);
- if (obj && obj->getType() != LLAssetType::AT_CATEGORY)
- {
- marketdata->decrementValidationWaiting(obj->getUUID(), count);
- }
- }
- }
- // 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 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.
- if (mask & (LLInventoryObserver::INTERNAL | LLInventoryObserver::STRUCTURE))
- {
- const LLUUID& group_id = gAgent.getGroupID();
- const uuid_list_t& changed_items = gInventory.getChangedIDs();
- for (uuid_list_t::const_iterator it = changed_items.begin(),
- end = changed_items.end();
- it != end; ++it)
- {
- LLInventoryObject* objp = gInventory.getObject(*it);
- if (!objp) continue;
- if (objp->getType() == LLAssetType::AT_CATEGORY)
- {
- // If it is a folder known to the marketplace, let's check it
- // is in proper shape
- if (marketdata->isListed(*it) ||
- marketdata->isVersionFolder(*it))
- {
- marketdata->listForIdleValidation(*it);
- }
- }
- else
- {
- // If it is not a category, it is an item...
- LLViewerInventoryItem* itemp = gInventory.getItem(*it);
- // If it is a no copy item, we may need to update the label
- // count of marketplace listings
- if (itemp &&
- !itemp->getPermissions().allowCopyBy(gAgentID, group_id))
- {
- marketdata->setDirtyCount();
- }
- }
- }
- }
- }
- // Tuple == Item
- LLMarketplaceTuple::LLMarketplaceTuple()
- : mListingId(0),
- mIsActive(false),
- mCountOnHand(0)
- {
- }
- LLMarketplaceTuple::LLMarketplaceTuple(const LLUUID& folder_id)
- : mListingFolderId(folder_id),
- mListingId(0),
- mIsActive(false),
- mCountOnHand(0)
- {
- }
- 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),
- mCountOnHand(0)
- {
- }
- LLMarketplaceTuple::LLMarketplaceTuple(const LLUUID& folder_id, S32 listing_id,
- const LLUUID& version_id, bool is_listed,
- const std::string& edit_url, S32 count)
- : mListingFolderId(folder_id),
- mListingId(listing_id),
- mVersionFolderId(version_id),
- mIsActive(is_listed),
- mEditURL(edit_url),
- mCountOnHand(count)
- {
- }
- // Data map
- LLMarketplaceData::LLMarketplaceData()
- : mMarketPlaceStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED),
- mMarketPlaceDataFetched(MarketplaceFetchCodes::MARKET_FETCH_NOT_DONE),
- mStatusUpdatedSignal(NULL),
- mDirtyCount(false),
- // NOTE: by using these instead of omitting the corresponding
- // xxxAndSuspend() parameters, we avoid seeing such classes constructed
- // and destroyed each time...
- mHttpOptions(new LLCore::HttpOptions),
- mHttpHeaders(new LLCore::HttpHeaders)
- {
- gIdleCallbacks.addFunction(idleCallback, this);
- mInventoryObserver = new LLMarketplaceInventoryObserver;
- gInventory.addObserver(mInventoryObserver);
- // NOTE: mHttpHeaders is used for Json requests only
- mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, "application/json");
- mHttpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, "application/json");
- }
- LLMarketplaceData::~LLMarketplaceData()
- {
- if (mStatusUpdatedSlot.connected())
- {
- mStatusUpdatedSlot.disconnect();
- }
- if (mStatusUpdatedSignal)
- {
- delete mStatusUpdatedSignal;
- }
- gIdleCallbacks.deleteFunction(idleCallback, this);
- if (mInventoryObserver)
- {
- gInventory.removeObserver(mInventoryObserver);
- mInventoryObserver = NULL;
- }
- mHttpOptions.reset();
- mHttpHeaders.reset();
- }
- void LLMarketplaceData::initializeSLM(const status_updated_signal_t::slot_type& cb)
- {
- if (gIsInSecondLifeBetaGrid)
- {
- // No Marketplace available in the SL beta grid... HB
- return;
- }
- if (!mStatusUpdatedSignal)
- {
- mStatusUpdatedSignal = new status_updated_signal_t();
- }
- if (mStatusUpdatedSlot.connected())
- {
- mStatusUpdatedSlot.disconnect();
- }
- mStatusUpdatedSlot = mStatusUpdatedSignal->connect(cb);
- if (mMarketPlaceStatus == MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED ||
- mMarketPlaceStatus == MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE)
- {
- // Initiate SLM connection and set responder
- std::string url = getSLMConnectURL("/merchant");
- if (url.empty())
- {
- // No capability... Init failed.
- LL_DEBUGS("Marketplace") << "Marketplace capability empty, cannot initialize"
- << LL_ENDL;
- setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE);
- }
- else
- {
- mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING;
- llinfos << "Initializing the Marketplace Listings" << llendl;
- LL_DEBUGS("Marketplace") << "Sending resquest: " << url << LL_ENDL;
- gCoros.launch("getMerchantStatus",
- boost::bind(&LLMarketplaceData::getMerchantStatusCoro,
- this, url));
- }
- }
- else
- {
- // If already initialized or initializing, just confirm the status so
- // that the callback gets called
- LL_DEBUGS("Marketplace") << "Marketplace already initialized or initializing"
- << LL_ENDL;
- setSLMStatus(mMarketPlaceStatus);
- }
- }
- void LLMarketplaceData::getMerchantStatusCoro(const std::string& url)
- {
- LLCore::HttpOptions::ptr_t options(new LLCore::HttpOptions);
- options->setFollowRedirects(true);
- LLCoreHttpUtil::HttpCoroutineAdapter adapter("getMerchantStatusCoro");
- LLSD result = adapter.getAndSuspend(url, options);
- if (!instanceExists()) return; // Viewer is being closed down !
- LLCore::HttpStatus status =
- LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
- S32 http_code = status.getType();
- if (status)
- {
- LL_DEBUGS("Marketplace") << "Status: " << http_code
- << " - User is a merchant" << LL_ENDL;
- setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_MERCHANT);
- }
- else if (http_code == HTTP_NOT_FOUND)
- {
- LL_DEBUGS("Marketplace") << "Status: " << http_code
- << " - User is not a merchant" << LL_ENDL;
- setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_MERCHANT);
- }
- else if (http_code == HTTP_SERVICE_UNAVAILABLE)
- {
- LL_DEBUGS("Marketplace") << "Status: " << http_code
- << " - Merchant is not migrated"
- << LL_ENDL;
- setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_MIGRATED_MERCHANT);
- }
- else if (http_code == HTTP_INTERNAL_ERROR)
- {
- // 499 includes timeout and ssl error - marketplace is down or having
- // issues, we do not show it in this request according to MAINT-5938
- llwarns << "Server internal error reported, reason: "
- << status.toString() << " - Code: "
- << result["error_code"].asString()
- << " - Description: " << result["error_description"].asString()
- << llendl;
- setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE);
- }
- else
- {
- log_SLM_warning("Get merchant", http_code, status.toString(),
- result["error_code"].asString(),
- result["error_description"].asString());
- setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE);
- }
- }
- // Get/Post/Put requests to the SLM Server using the SLM API
- void LLMarketplaceData::getSLMListings()
- {
- std::string url = getSLMConnectURL("/listings");
- if (url.empty()) return;
- // Send request
- const LLUUID& market_id = LLMarketplace::getMPL();
- if (market_id.notNull())
- {
- LL_DEBUGS("Marketplace") << "Sending resquest: " << url << LL_ENDL;
- setUpdating(market_id, true);
- gCoros.launch("getSLMListings",
- boost::bind(&LLMarketplaceData::getSLMListingsCoro, this,
- url, market_id));
- }
- }
- void LLMarketplaceData::getSLMListingsCoro(const std::string& url,
- LLUUID expected_folder_id)
- {
- LLCoreHttpUtil::HttpCoroutineAdapter adapter("getSLMListingsCoro");
- LLSD result = adapter.getJsonAndSuspend(url, mHttpOptions, mHttpHeaders);
- if (!instanceExists()) return; // Viewer is being closed down !
- setUpdating(expected_folder_id, false);
- LLCore::HttpStatus status =
- LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
- if (status)
- {
- result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS);
- LL_DEBUGS("Marketplace") << "Body: " << result << LL_ENDL;
- for (LLSD::array_iterator it = result["listings"].beginArray(),
- end = result["listings"].endArray();
- it != end; ++it)
- {
- const LLSD& listing = *it;
- S32 listing_id = listing["id"].asInteger();
- bool is_listed = listing["is_listed"].asBoolean();
- std::string edit_url = listing["edit_url"].asString();
- LLUUID folder_id = listing["inventory_info"]["listing_folder_id"].asUUID();
- LLUUID version_id = listing["inventory_info"]["version_folder_id"].asUUID();
- S32 count = listing["inventory_info"]["count_on_hand"].asInteger();
- if (folder_id.notNull())
- {
- addListing(folder_id, listing_id, version_id, is_listed,
- edit_url, count);
- }
- }
- setSLMDataFetched(MarketplaceFetchCodes::MARKET_FETCH_DONE);
- }
- else
- {
- log_SLM_warning("Get listings", status.getType(), status.toString(),
- "", result.asString());
- setSLMDataFetched(MarketplaceFetchCodes::MARKET_FETCH_FAILED);
- }
- // Update all folders under the root
- LLMarketplace::updateCategory(expected_folder_id, false);
- gInventory.notifyObservers();
- }
- void LLMarketplaceData::getSLMListing(S32 listing_id)
- {
- std::string url = getSLMConnectURL(llformat("/listing/%d", listing_id));
- if (url.empty()) return;
- // Send request
- LL_DEBUGS("Marketplace") << "Sending resquest: " << url << LL_ENDL;
- const LLUUID& folder_id = getListingFolder(listing_id);
- setUpdating(folder_id, true);
- gCoros.launch("getSLMListings",
- boost::bind(&LLMarketplaceData::getSLMListingCoro, this, url,
- folder_id));
- }
- void LLMarketplaceData::getSLMListingCoro(const std::string& url,
- LLUUID expected_folder_id)
- {
- LLCoreHttpUtil::HttpCoroutineAdapter adapter("getSLMListingCoro");
- LLSD result = adapter.getJsonAndSuspend(url, mHttpOptions, mHttpHeaders);
- if (!instanceExists()) return; // Viewer is being closed down !
- setUpdating(expected_folder_id, false);
- LLCore::HttpStatus status =
- LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
- if (status)
- {
- result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS);
- LL_DEBUGS("Marketplace") << "Body: " << result << LL_ENDL;
- for (LLSD::array_iterator it = result["listings"].beginArray(),
- end = result["listings"].endArray();
- it != end; ++it)
- {
- const LLSD& listing = *it;
- S32 listing_id = listing["id"].asInteger();
- bool is_listed = listing["is_listed"].asBoolean();
- std::string edit_url = listing["edit_url"].asString();
- LLUUID folder_id = listing["inventory_info"]["listing_folder_id"].asUUID();
- LLUUID version_id = listing["inventory_info"]["version_folder_id"].asUUID();
- S32 count = listing["inventory_info"]["count_on_hand"].asInteger();
- // Update that listing
- setListingID(folder_id, listing_id, false);
- setVersionFolderID(folder_id, version_id, false);
- setActivationState(folder_id, is_listed, false);
- setListingURL(folder_id, edit_url, false);
- setCountOnHand(folder_id, count, false);
- LLMarketplace::updateCategory(folder_id, false);
- gInventory.notifyObservers();
- }
- }
- else
- {
- S32 http_code = status.getType();
- if (http_code == HTTP_NOT_FOUND)
- {
- // That listing does not exist -> delete its record from the local
- // SLM data store
- deleteListing(expected_folder_id, false);
- }
- else
- {
- log_SLM_warning("Get listing", http_code, status.toString(), "",
- result.asString());
- }
- LLMarketplace::updateCategory(expected_folder_id, false);
- gInventory.notifyObservers();
- }
- }
- void LLMarketplaceData::createSLMListing(const LLUUID& folder_id,
- const LLUUID& version_id, S32 count)
- {
- std::string url = getSLMConnectURL("/listings");
- if (url.empty()) return;
- LLViewerInventoryCategory* category = gInventory.getCategory(folder_id);
- if (!category)
- {
- llwarns << "Cannot find category for folder Id: " << folder_id
- << llendl;
- return;
- }
- // Build the message
- LLSD inventory_info;
- inventory_info["listing_folder_id"] = folder_id;
- inventory_info["version_folder_id"] = version_id;
- inventory_info["count_on_hand"] = count;
- LLSD listing;
- listing["name"] = category->getName();
- listing["inventory_info"] = inventory_info;
- LLSD data;
- data["listing"] = listing;
- // Send request
- LL_DEBUGS("Marketplace") << "Sending resquest: " << url << " - Body:"
- << data << LL_ENDL;
- setUpdating(folder_id, true);
- gCoros.launch("createSLMListingCoro",
- boost::bind(&LLMarketplaceData::createSLMListingCoro, this,
- url, folder_id, data));
- }
- void LLMarketplaceData::createSLMListingCoro(const std::string& url,
- LLUUID expected_folder_id,
- const LLSD& data)
- {
- LLCoreHttpUtil::HttpCoroutineAdapter adapter("getSLMListingCoro");
- LLSD result = adapter.postJsonAndSuspend(url, data, mHttpOptions,
- mHttpHeaders);
- if (!instanceExists()) return; // Viewer is being closed down !
- setUpdating(expected_folder_id, false);
- LLCore::HttpStatus status =
- LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
- if (status)
- {
- result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS);
- LL_DEBUGS("Marketplace") << "Body: " << result << LL_ENDL;
- for (LLSD::array_iterator it = result["listings"].beginArray(),
- end = result["listings"].endArray();
- it != end; ++it)
- {
- const LLSD& listing = *it;
- S32 listing_id = listing["id"].asInteger();
- bool is_listed = listing["is_listed"].asBoolean();
- std::string edit_url = listing["edit_url"].asString();
- LLUUID folder_id = listing["inventory_info"]["listing_folder_id"].asUUID();
- LLUUID version_id = listing["inventory_info"]["version_folder_id"].asUUID();
- S32 count = listing["inventory_info"]["count_on_hand"].asInteger();
- addListing(folder_id, listing_id, version_id, is_listed, edit_url,
- count);
- LLMarketplace::updateCategory(folder_id, false);
- gInventory.notifyObservers();
- }
- }
- else
- {
- log_SLM_warning("Post listing", status.getType(), status.toString(),
- "", result.asString());
- LLMarketplace::updateCategory(expected_folder_id, false);
- gInventory.notifyObservers();
- }
- }
- void LLMarketplaceData::updateSLMListing(const LLUUID& folder_id,
- S32 listing_id,
- const LLUUID& version_id,
- bool is_listed, S32 count)
- {
- std::string url = getSLMConnectURL(llformat("/listing/%d", listing_id));
- if (url.empty()) return;
- // Auto unlist if the count is 0 (out of stock)
- if (is_listed && count == 0)
- {
- is_listed = false;
- gNotifications.add("AlertMerchantStockFolderEmpty");
- }
- // Note: we are assuming that sending unchanged info would not break
- // anything server side...
- // Build the message
- LLSD inventory_info;
- inventory_info["listing_folder_id"] = folder_id;
- inventory_info["version_folder_id"] = version_id;
- inventory_info["count_on_hand"] = count;
- LLSD listing;
- listing["id"] = listing_id;
- listing["is_listed"] = is_listed;
- listing["inventory_info"] = inventory_info;
- LLSD data;
- data["listing"] = listing;
- // Send request
- LL_DEBUGS("Marketplace") << "Sending resquest: " << url << " - Body:"
- << data << LL_ENDL;
- setUpdating(folder_id, true);
- gCoros.launch("updateSLMListingCoro",
- boost::bind(&LLMarketplaceData::updateSLMListingCoro, this,
- url, folder_id, version_id, is_listed, data));
- }
- // Notification callback for updateSLMListingCoro()
- bool edit_listing_callback(const LLSD& notification, const LLSD& response)
- {
- if (LLNotification::getSelectedOption(notification, response) == 0) // yes
- {
- std::string url = notification["payload"]["url"].asString();
- if (!url.empty())
- {
- LLWeb::loadURL(url);
- }
- }
- return false;
- }
- void LLMarketplaceData::updateSLMListingCoro(const std::string& url,
- LLUUID expected_folder_id,
- LLUUID expected_version_id,
- bool expected_listed,
- const LLSD& data)
- {
- LLCoreHttpUtil::HttpCoroutineAdapter adapter("getSLMListingCoro");
- LLSD result = adapter.putJsonAndSuspend(url, data, mHttpOptions,
- mHttpHeaders);
- if (!instanceExists()) return; // Viewer is being closed down !
- setUpdating(expected_folder_id, false);
- LLCore::HttpStatus status =
- LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
- if (status)
- {
- result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS);
- LL_DEBUGS("Marketplace") << "Body: " << result << LL_ENDL;
- for (LLSD::array_iterator it = result["listings"].beginArray(),
- end = result["listings"].endArray();
- it != end; ++it)
- {
- const LLSD& listing = *it;
- S32 listing_id = listing["id"].asInteger();
- bool is_listed = listing["is_listed"].asBoolean();
- std::string edit_url = listing["edit_url"].asString();
- LLUUID folder_id = listing["inventory_info"]["listing_folder_id"].asUUID();
- LLUUID version_id = listing["inventory_info"]["version_folder_id"].asUUID();
- S32 count = listing["inventory_info"]["count_on_hand"].asInteger();
- // Update that listing
- setListingID(folder_id, listing_id, false);
- setVersionFolderID(folder_id, version_id, false);
- setActivationState(folder_id, is_listed, false);
- setListingURL(folder_id, edit_url, false);
- setCountOnHand(folder_id, count, false);
- LLMarketplace::updateCategory(folder_id, false);
- gInventory.notifyObservers();
- // Show a notification alert if what we got is not what we expected
- // (this actually does not result in an error status from the SLM
- // API protocol)
- if (is_listed != expected_listed ||
- version_id != expected_version_id)
- {
- LLSD subs;
- LLViewerInventoryCategory* cat;
- cat = gInventory.getCategory(folder_id);
- if (cat)
- {
- subs["NAME"] = cat->getName();
- }
- else
- {
- subs["NAME"] = folder_id.asString();
- }
- LLSD payload;
- payload["url"] = edit_url;
- gNotifications.add("AlertMerchantListingNotUpdated", subs,
- payload, edit_listing_callback);
- }
- }
- }
- else
- {
- log_SLM_warning("Put listing", status.getType(), status.toString(), "",
- result.asString());
- LLMarketplace::updateCategory(expected_folder_id, false);
- gInventory.notifyObservers();
- }
- }
- void LLMarketplaceData::associateSLMListing(const LLUUID& folder_id,
- S32 listing_id,
- const LLUUID& version_id,
- const LLUUID& source_folder_id)
- {
- std::string url = getSLMConnectURL(llformat("/associate_inventory/%d",
- listing_id));
- if (url.empty()) return;
- // Note: we are assuming that sending unchanged info woould not break
- // anything server side...
- // Build the message
- LLSD inventory_info;
- inventory_info["listing_folder_id"] = folder_id;
- inventory_info["version_folder_id"] = version_id;
- LLSD listing;
- listing["id"] = listing_id;
- listing["inventory_info"] = inventory_info;
- LLSD data;
- data["listing"] = listing;
- // Send request
- LL_DEBUGS("Marketplace") << "Sending resquest: " << url << " - Body:"
- << data << LL_ENDL;
- // Send request
- setUpdating(folder_id, true);
- setUpdating(source_folder_id, true);
- gCoros.launch("updateSLMListingCoro",
- boost::bind(&LLMarketplaceData::associateSLMListingCoro,
- this, url, folder_id, source_folder_id, data));
- }
- void LLMarketplaceData::associateSLMListingCoro(const std::string& url,
- LLUUID expected_folder_id,
- LLUUID source_folder_id,
- const LLSD& data)
- {
- LLCoreHttpUtil::HttpCoroutineAdapter adapter("associateSLMListingCoro");
- LLSD result = adapter.putJsonAndSuspend(url, data, mHttpOptions,
- mHttpHeaders);
- if (!instanceExists()) return; // Viewer is being closed down !
- setUpdating(expected_folder_id, false);
- setUpdating(source_folder_id, false);
- LLCore::HttpStatus status =
- LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
- if (status)
- {
- result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS);
- LL_DEBUGS("Marketplace") << "Body: " << result << LL_ENDL;
- for (LLSD::array_iterator it = result["listings"].beginArray(),
- end = result["listings"].endArray();
- it != end; ++it)
- {
- const LLSD& listing = *it;
- S32 listing_id = listing["id"].asInteger();
- bool is_listed = listing["is_listed"].asBoolean();
- std::string edit_url = listing["edit_url"].asString();
- LLUUID folder_id = listing["inventory_info"]["listing_folder_id"].asUUID();
- LLUUID version_id = listing["inventory_info"]["version_folder_id"].asUUID();
- S32 count = listing["inventory_info"]["count_on_hand"].asInteger();
- // Check that the listing ID is not already associated to some
- // other record
- const LLUUID& old_listing = getListingFolder(listing_id);
- if (old_listing.notNull())
- {
- // If it is already used, unlist the old record (we cannot have
- // 2 listings with the same listing ID)
- deleteListing(old_listing);
- }
- // Add the new association
- addListing(folder_id, listing_id, version_id, is_listed, edit_url,
- count);
- LLMarketplace::updateCategory(folder_id, false);
- gInventory.notifyObservers();
- // The stock count needs to be updated with the new local count now
- updateCountOnHand(folder_id, 1);
- }
- }
- else
- {
- log_SLM_warning("Put associate_inventory", status.getType(),
- status.toString(), "", result.asString());
- LLMarketplace::updateCategory(expected_folder_id, false);
- gInventory.notifyObservers();
- }
- // Always update the source folder so its widget updates
- LLMarketplace::updateCategory(source_folder_id, false);
- gInventory.notifyObservers();
- }
- void LLMarketplaceData::deleteSLMListing(S32 listing_id)
- {
- std::string url = getSLMConnectURL(llformat("/listing/%d", listing_id));
- if (url.empty()) return;
- LLSD headers = LLSD::emptyMap();
- headers[HTTP_OUT_HEADER_ACCEPT] = "application/json";
- headers[HTTP_OUT_HEADER_CONTENT_TYPE] = "application/json";
- // Send request
- const LLUUID& folder_id = getListingFolder(listing_id);
- setUpdating(folder_id, true);
- LL_DEBUGS("Marketplace") << "Sending resquest: " << url << LL_ENDL;
- gCoros.launch("deleteSLMListingCoro",
- boost::bind(&LLMarketplaceData::deleteSLMListingCoro, this,
- url, folder_id));
- }
- void LLMarketplaceData::deleteSLMListingCoro(const std::string& url,
- LLUUID expected_folder_id)
- {
- LLCoreHttpUtil::HttpCoroutineAdapter adapter("deleteSLMListingCoro");
- LLSD result = adapter.deleteJsonAndSuspend(url, mHttpOptions,
- mHttpHeaders);
- if (!instanceExists()) return; // Viewer is being closed down !
- setUpdating(expected_folder_id, false);
- LLCore::HttpStatus status =
- LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
- if (status)
- {
- result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS);
- LL_DEBUGS("Marketplace") << "Body: " << result << LL_ENDL;
- for (LLSD::array_iterator it = result["listings"].beginArray(),
- end = result["listings"].endArray();
- it != end; ++it)
- {
- const LLSD& listing = *it;
- S32 listing_id = listing["id"].asInteger();
- const LLUUID& folder_id = getListingFolder(listing_id);
- deleteListing(folder_id);
- }
- }
- else
- {
- log_SLM_warning("Delete listing", status.getType(), status.toString(),
- "", result.asString());
- LLMarketplace::updateCategory(expected_folder_id, false);
- gInventory.notifyObservers();
- }
- }
- std::string LLMarketplaceData::getSLMConnectURL(const std::string& route)
- {
- std::string url = gAgent.getRegionCapability("DirectDelivery");
- if (!url.empty())
- {
- url += route;
- }
- return url;
- }
- void LLMarketplaceData::setSLMStatus(S32 status)
- {
- if (mMarketPlaceStatus != 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;
- }
- const LLUUID& version_id = getVersionFolderIfUnique(folder_id);
- S32 count = version_id.isNull() ? COMPUTE_STOCK_INFINITE
- : LLMarketplace::computeStockCount(version_id,
- true);
- // Validate the count on hand
- if (count == COMPUTE_STOCK_NOT_EVALUATED)
- {
- // If the count on hand cannot be evaluated, we will consider it empty
- // (out of stock) at creation time. It will get reevaluated and updated
- // once the items are fetched.
- count = 0;
- }
- // Post the listing creation request to SLM
- createSLMListing(folder_id, version_id, count);
- return true;
- }
- bool LLMarketplaceData::clearListing(const LLUUID& folder_id, S32 depth)
- {
- if (folder_id.isNull())
- {
- // Folder does not exist -> exit with error
- return false;
- }
- // Folder id can be the root of the listing or not so we need to retrieve
- // the root first
- if (depth < 0)
- {
- depth = LLMarketplace::depthNesting(folder_id);
- }
- const LLUUID& listing_uuid =
- isListed(folder_id) ? folder_id
- : LLMarketplace::nestedParentId(folder_id, depth);
- S32 listing_id = getListingID(listing_uuid);
- if (listing_id == 0)
- {
- // Listing does not exist -> 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, S32 depth)
- {
- if (folder_id.isNull())
- {
- // Folder does not exist -> exit with error
- return false;
- }
- // Folder id can be the root of the listing or not so we need to retrieve
- // the root first
- if (depth < 0)
- {
- depth = LLMarketplace::depthNesting(folder_id);
- }
- const LLUUID& listing_uuid =
- isListed(folder_id) ? folder_id
- : LLMarketplace::nestedParentId(folder_id, depth);
- S32 listing_id = getListingID(listing_uuid);
- if (listing_id == 0)
- {
- // Listing does not exist -> 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,
- S32 depth)
- {
- // Folder id can be the root of the listing or not so we need to retrieve
- // the root first
- if (depth < 0)
- {
- depth = LLMarketplace::depthNesting(folder_id);
- }
- const LLUUID& listing_uuid = LLMarketplace::nestedParentId(folder_id,
- depth);
- S32 listing_id = getListingID(listing_uuid);
- if (listing_id == 0)
- {
- // Listing does not exist -> exit with error
- return false;
- }
- if (getActivationState(listing_uuid) == activate)
- {
- // If activation state is unchanged, no point spamming SLM with an
- // update
- return true;
- }
- const LLUUID& version_uuid = getVersionFolder(listing_uuid);
- // Also update the count on hand
- S32 count = LLMarketplace::computeStockCount(folder_id);
- if (count == COMPUTE_STOCK_NOT_EVALUATED)
- {
- // If the count on hand cannot be evaluated locally, we should not
- // change that SLM value. We are assuming that this issue is local and
- // should not modify server side values.
- count = getCountOnHand(listing_uuid);
- }
- // Post the listing update request to SLM
- updateSLMListing(listing_uuid, listing_id, version_uuid, activate, count);
- return true;
- }
- bool LLMarketplaceData::setVersionFolder(const LLUUID& folder_id,
- const LLUUID& version_id, S32 depth)
- {
- // Folder id can be the root of the listing or not so we need to retrieve
- // the root first
- if (depth < 0)
- {
- depth = LLMarketplace::depthNesting(folder_id);
- }
- const LLUUID& listing_uuid = LLMarketplace::nestedParentId(folder_id,
- depth);
- S32 listing_id = getListingID(listing_uuid);
- if (listing_id == 0)
- {
- // Listing does not exist -> exit with error
- return false;
- }
- if (getVersionFolder(listing_uuid) == version_id)
- {
- // If version folder is unchanged, no point spamming SLM with an update
- return true;
- }
- // 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);
- // Also update the count on hand
- S32 count = LLMarketplace::computeStockCount(version_id);
- if (count == COMPUTE_STOCK_NOT_EVALUATED)
- {
- // If the count on hand cannot be evaluated, we will consider it empty
- // (out of stock) at creation time. It will get reevaluated and updated
- // once the items are fetched.
- count = 0;
- }
- // Post the listing update request to SLM
- updateSLMListing(listing_uuid, listing_id, version_id, is_listed, count);
- return true;
- }
- bool LLMarketplaceData::updateCountOnHand(const LLUUID& folder_id, S32 depth)
- {
- // Folder id can be the root of the listing or not so we need to retrieve
- // the root first
- if (depth < 0)
- {
- depth = LLMarketplace::depthNesting(folder_id);
- }
- const LLUUID& listing_uuid = LLMarketplace::nestedParentId(folder_id,
- depth);
- S32 listing_id = getListingID(listing_uuid);
- if (listing_id == 0)
- {
- // Listing does not exist -> exit with error
- return false;
- }
- // Compute the new count on hand
- S32 count = LLMarketplace::computeStockCount(folder_id);
- if (count == getCountOnHand(listing_uuid))
- {
- // If count on hand is unchanged, no point spamming SLM with an update
- return true;
- }
- if (count == COMPUTE_STOCK_NOT_EVALUATED)
- {
- // If local count on hand is not known at that point, do *not* force an
- // update to SLM
- return false;
- }
- // Get the unchanged values
- bool is_listed = getActivationState(listing_uuid);
- const LLUUID& version_uuid = getVersionFolder(listing_uuid);
- // Post the listing update request to SLM
- updateSLMListing(listing_uuid, listing_id, version_uuid, is_listed, count);
- // Force the local value as it prevents spamming (count update may occur in
- // burst when restocking). Note that if SLM has a good reason to return a
- // different value, it'll be updated by the responder
- setCountOnHand(listing_uuid, count, false);
- 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;
- }
- // Get the version folder: if there is only one subfolder, we will set it
- // as a version folder immediately
- const LLUUID& version_id = getVersionFolderIfUnique(folder_id);
- // Post the listing update request to SLM
- associateSLMListing(folder_id, listing_id, version_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,
- const std::string& edit_url, S32 count)
- {
- mMarketplaceItems[folder_id] = LLMarketplaceTuple(folder_id, listing_id,
- version_id, is_listed,
- edit_url, count);
- if (version_id.notNull())
- {
- mVersionFolders[version_id] = folder_id;
- }
- return true;
- }
- bool LLMarketplaceData::deleteListing(const LLUUID& folder_id, bool update)
- {
- if (mMarketplaceItems.erase(folder_id) != 1)
- {
- return false;
- }
- const LLUUID& vf_uuid = getVersionFolder(folder_id);
- if (vf_uuid.notNull())
- {
- mVersionFolders.erase(vf_uuid);
- }
- if (update)
- {
- LLMarketplace::updateCategory(folder_id, false);
- gInventory.notifyObservers();
- }
- return true;
- }
- bool LLMarketplaceData::deleteListing(S32 listing_id, bool update)
- {
- if (listing_id == 0)
- {
- return false;
- }
- LLUUID folder_id = getListingFolder(listing_id);
- return deleteListing(folder_id, update);
- }
- // Accessors
- bool LLMarketplaceData::getActivationState(const LLUUID& folder_id)
- {
- // Listing folder case
- marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
- if (it != mMarketplaceItems.end())
- {
- return (it->second).mIsActive;
- }
- // Version folder case
- version_folders_list_t::iterator vit = mVersionFolders.find(folder_id);
- if (vit != mVersionFolders.end())
- {
- it = mMarketplaceItems.find(vit->second);
- if (it != mMarketplaceItems.end())
- {
- return (it->second).mIsActive;
- }
- }
- 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;
- }
- S32 LLMarketplaceData::getCountOnHand(const LLUUID& folder_id)
- {
- marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
- return it == mMarketplaceItems.end() ? -1 : (it->second).mCountOnHand;
- }
- 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)
- {
- for (marketplace_items_list_t::iterator it = mMarketplaceItems.begin(),
- end = mMarketplaceItems.end();
- it != end; ++it)
- {
- if ((it->second).mListingId == listing_id)
- {
- return (it->second).mListingFolderId;
- }
- }
- return LLUUID::null;
- }
- std::string LLMarketplaceData::getListingURL(const LLUUID& folder_id,
- S32 depth)
- {
- if (depth < 0)
- {
- depth = LLMarketplace::depthNesting(folder_id);
- }
- const LLUUID& listing_uuid = LLMarketplace::nestedParentId(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)
- {
- return mMarketplaceItems.count(folder_id) != 0;
- }
- bool LLMarketplaceData::isListedAndActive(const LLUUID& folder_id)
- {
- return isListed(folder_id) && getActivationState(folder_id);
- }
- bool LLMarketplaceData::isVersionFolder(const LLUUID& folder_id)
- {
- return mVersionFolders.count(folder_id) != 0;
- }
- bool LLMarketplaceData::isInActiveFolder(const LLUUID& obj_id, S32 depth)
- {
- if (depth < 0)
- {
- depth = LLMarketplace::depthNesting(obj_id);
- }
- const LLUUID& listing_uuid = LLMarketplace::nestedParentId(obj_id, depth);
- bool active = getActivationState(listing_uuid);
- if (!active)
- {
- return false;
- }
- const LLUUID& version_uuid = getVersionFolder(listing_uuid);
- return obj_id == version_uuid ||
- gInventory.isObjectDescendentOf(obj_id, version_uuid);
- }
- LLUUID LLMarketplaceData::getActiveFolder(const LLUUID& obj_id, S32 depth)
- {
- if (depth < 0)
- {
- depth = LLMarketplace::depthNesting(obj_id);
- }
- const LLUUID& listing_uuid = LLMarketplace::nestedParentId(obj_id, depth);
- return getActivationState(listing_uuid) ? getVersionFolder(listing_uuid)
- : LLUUID::null;
- }
- bool LLMarketplaceData::isUpdating(const LLUUID& folder_id, S32 depth)
- {
- if (depth < 0)
- {
- depth = LLMarketplace::depthNesting(folder_id);
- }
- if (depth < 0)
- {
- // Not a Marketplace folder
- return false;
- }
- if (depth == 0 &&
- getSLMStatus() <= MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING)
- {
- // If the Marketplace is not yet initialized, then yes, we are
- // definitely updating...
- return true;
- }
- const LLUUID& market_id = LLMarketplace::getMPL();
- if (mPendingUpdateSet.find(market_id) != mPendingUpdateSet.end())
- {
- // If we are waiting for data for the marketplace listings root, we are
- // in the updating process for all
- return true;
- }
- #if 0 // Stock folders too...
- if (depth > 2)
- {
- // Only listing and version folders though are concerned by that status
- return false;
- }
- #endif
- // Check if the listing folder is waiting or data
- const LLUUID& listing_uuid = LLMarketplace::nestedParentId(folder_id,
- depth);
- return mPendingUpdateSet.find(listing_uuid) != mPendingUpdateSet.end();
- }
- void LLMarketplaceData::setUpdating(const LLUUID& folder_id, bool is_updating)
- {
- uuid_list_t::iterator it = mPendingUpdateSet.find(folder_id);
- if (it != mPendingUpdateSet.end())
- {
- mPendingUpdateSet.erase(it);
- }
- if (is_updating)
- {
- mPendingUpdateSet.emplace(folder_id);
- }
- }
- void LLMarketplaceData::listForIdleValidation(const LLUUID& folder_id)
- {
- mPendingValidations.emplace(folder_id);
- }
- void LLMarketplaceData::setValidationWaiting(const LLUUID& folder_id,
- S32 count)
- {
- mValidationWaitingList[folder_id] = count;
- }
- void LLMarketplaceData::decrementValidationWaiting(const LLUUID& folder_id,
- S32 count)
- {
- waiting_list_t::iterator it = mValidationWaitingList.find(folder_id);
- if (it != mValidationWaitingList.end())
- {
- it->second -= count;
- if (it->second <= 0)
- {
- mValidationWaitingList.hmap_erase(it);
- mPendingValidations.emplace(folder_id);
- }
- }
- }
- //static
- void LLMarketplaceData::idleCallback(void* userdata)
- {
- LLMarketplaceData* self = (LLMarketplaceData*)userdata;
- if (!self || self->mPendingValidations.empty()) return;
- for (uuid_list_t::const_iterator it = self->mPendingValidations.begin(),
- end = self->mPendingValidations.end();
- it != end; ++it)
- {
- LLViewerInventoryCategory* cat = gInventory.getCategory(*it);
- if (cat)
- {
- LLMarketplace::validateListings(cat);
- }
- }
- self->mPendingValidations.clear();
- }
- // Private Modifiers
- bool LLMarketplaceData::setListingID(const LLUUID& folder_id, S32 listing_id,
- bool update)
- {
- marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
- if (it == mMarketplaceItems.end())
- {
- return false;
- }
- it->second.mListingId = listing_id;
- if (update)
- {
- LLMarketplace::updateCategory(folder_id, false);
- gInventory.notifyObservers();
- }
- return true;
- }
- bool LLMarketplaceData::setCountOnHand(const LLUUID& folder_id, S32 count,
- bool update)
- {
- marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
- if (it == mMarketplaceItems.end())
- {
- return false;
- }
- it->second.mCountOnHand = count;
- return true;
- }
- bool LLMarketplaceData::setVersionFolderID(const LLUUID& folder_id,
- const LLUUID& version_id,
- bool update)
- {
- marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
- if (it == mMarketplaceItems.end())
- {
- return false;
- }
- // Note: do not use LLUUID& here since we need an actual copy of the old
- // UUID, not a pointer on (it->second).mVersionFolderId.
- LLUUID old_version_id = (it->second).mVersionFolderId;
- if (version_id == old_version_id)
- {
- return false;
- }
- it->second.mVersionFolderId = version_id;
- bool update_old = false;
- if (old_version_id.notNull())
- {
- mVersionFolders.erase(old_version_id);
- update_old = update;
- }
- bool update_new = false;
- if (version_id.notNull())
- {
- mVersionFolders[version_id] = folder_id;
- update_new = update;
- }
- // Now that the version folder has been changed, we can update the folders
- // hierarchy if needed.
- if (update_old)
- {
- LLMarketplace::updateCategory(old_version_id, false);
- }
- if (update_new)
- {
- LLMarketplace::updateCategory(version_id, false);
- }
- if (update_old || update_new)
- {
- gInventory.notifyObservers();
- }
- return true;
- }
- bool LLMarketplaceData::setActivationState(const LLUUID& folder_id,
- bool activate, bool update)
- {
- marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
- if (it == mMarketplaceItems.end())
- {
- return false;
- }
- it->second.mIsActive = activate;
- if (update)
- {
- LLMarketplace::updateCategory((it->second).mListingFolderId, false);
- gInventory.notifyObservers();
- }
- return true;
- }
- bool LLMarketplaceData::setListingURL(const LLUUID& folder_id,
- const std::string& edit_url, bool update)
- {
- marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
- if (it == mMarketplaceItems.end())
- {
- return false;
- }
- it->second.mEditURL = edit_url;
- return true;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // New Marketplace Listings API related functions
- // Local helper
- bool can_move_to_marketplace(LLViewerInventoryItem* inv_item,
- std::string& tooltip_msg,
- bool resolve_links = false)
- {
- if (!inv_item)
- {
- tooltip_msg = "NULL inventory item";
- return false;
- }
- LLViewerInventoryItem* vitem = inv_item;
- LLViewerInventoryItem* linked_item = vitem->getLinkedItem();
- LLViewerInventoryCategory* linked_category = vitem->getLinkedCategory();
- if (resolve_links)
- {
- if (linked_item)
- {
- vitem = linked_item;
- linked_item = NULL; // Link resolved, so allow to pass next test
- }
- else if (linked_category)
- {
- vitem = (LLViewerInventoryItem*)linked_category;
- // Link resolved, so allow to pass next test
- linked_category = NULL;
- }
- }
- // Linked items and folders cannot be put for sale
- if (linked_category || linked_item)
- {
- tooltip_msg = LLTrans::getString("TooltipOutboxLinked");
- return false;
- }
- const LLUUID& item_uuid = vitem->getUUID();
- // Check library status: library items cannot be put on the marketplace
- if (!gInventory.isObjectDescendentOf(item_uuid,
- gInventory.getRootFolderID()))
- {
- tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory");
- return false;
- }
- // Check type
- S32 type = vitem->getType();
- // A category is always considered as passing...
- if (type == LLAssetType::AT_CATEGORY)
- {
- return true;
- }
- // For the moment, calling cards cannot be put on the marketplace
- if (type == LLAssetType::AT_CALLINGCARD)
- {
- tooltip_msg = LLTrans::getString("TooltipOutboxCallingCard");
- return false;
- }
- // Check that the agent has transfer permission on the item: this is
- // required as a resident cannot put on sale items they cannot transfer.
- // Proceed with move if we have permission.
- if (!vitem->getPermissions().allowTransferBy(gAgentID))
- {
- tooltip_msg = LLTrans::getString("TooltipOutboxNoTransfer");
- return false;
- }
- // Check worn/not worn status: worn items cannot be put on the marketplace
- if (get_is_item_worn(item_uuid))
- {
- tooltip_msg = LLTrans::getString("TooltipOutboxWorn");
- return false;
- }
- return true;
- }
- // Local helper
- // Counts only the copyable items, i.e. skip the stock items (which are no
- // copy)
- S32 count_copyable_items(const LLInventoryModel::item_array_t& items)
- {
- S32 count = 0;
- const LLUUID& group_id = gAgent.getGroupID();
- for (LLInventoryModel::item_array_t::const_iterator it = items.begin(),
- end = items.end();
- it != end; ++it)
- {
- LLViewerInventoryItem* itemp = *it;
- if (itemp && itemp->getPermissions().allowCopyBy(gAgentID, group_id))
- {
- ++count;
- }
- }
- return count;
- }
- // Local helper
- // Count only the non-copyable items, i.e. the stock items, skip the others
- S32 count_stock_items(const LLInventoryModel::item_array_t& items)
- {
- S32 count = 0;
- const LLUUID& group_id = gAgent.getGroupID();
- for (LLInventoryModel::item_array_t::const_iterator it = items.begin(),
- end = items.end();
- it != end; ++it)
- {
- LLViewerInventoryItem* itemp = *it;
- if (itemp && !itemp->getPermissions().allowCopyBy(gAgentID, group_id))
- {
- ++count;
- }
- }
- return count;
- }
- // Local helper
- // Counts the number of stock folders
- S32 count_stock_folders(const LLInventoryModel::cat_array_t& cats)
- {
- S32 count = 0;
- for (LLInventoryModel::cat_array_t::const_iterator it = cats.begin(),
- end = cats.end();
- it != end; ++it)
- {
- LLViewerInventoryCategory* cat = *it;
- if (cat &&
- cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)
- {
- ++count;
- }
- }
- return count;
- }
- //static
- bool LLMarketplace::contains(const LLUUID& item_id)
- {
- return sMarketplaceListingId.notNull() &&
- gInventory.isObjectDescendentOf(item_id, sMarketplaceListingId);
- }
- // Get the marketplace listings root, exit with -1 (i.e. not under the
- // marketplace listings root) if none
- //static
- S32 LLMarketplace::depthNesting(const LLUUID& item_id)
- {
- if (sMarketplaceListingId.isNull() ||
- !gInventory.isObjectDescendentOf(item_id, sMarketplaceListingId))
- {
- return -1;
- }
- // Iterate through the parents till we hit the marketplace listings root
- // Note that the marketplace listings root itself will return 0
- S32 depth = 0;
- LLInventoryObject* cur_object = gInventory.getObject(item_id);
- if (cur_object)
- {
- LLUUID cur_uuid(item_id);
- while (cur_uuid != sMarketplaceListingId)
- {
- ++depth;
- cur_uuid = cur_object->getParentUUID();
- cur_object = gInventory.getCategory(cur_uuid);
- if (!cur_object)
- {
- return -1;
- }
- }
- }
- return depth;
- }
- // Returns the UUID of the marketplace listing this object is in
- //static
- LLUUID LLMarketplace::nestedParentId(const LLUUID& item_id, S32 depth)
- {
- if (depth < 1)
- {
- // For objects outside the marketplace listings root (or root itself),
- // we return a NULL UUID
- return LLUUID::null;
- }
- else if (depth == 1)
- {
- // Just under the root, we return the passed UUID itself if it's a
- // folder, NULL otherwise (not a listing)
- LLViewerInventoryCategory* cat = gInventory.getCategory(item_id);
- return cat ? item_id : LLUUID::null;
- }
- // depth > 1
- LLInventoryObject* cur_object = gInventory.getObject(item_id);
- LLUUID cur_uuid(item_id);
- while (cur_object && depth-- > 1)
- {
- cur_uuid = cur_object->getParentUUID();
- cur_object = gInventory.getCategory(cur_uuid);
- }
- return cur_uuid;
- }
- //static
- S32 LLMarketplace::computeStockCount(const LLUUID& cat_id, bool force_count)
- {
- // Handle the case of the folder being a stock folder immediately
- LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
- if (!cat)
- {
- // Not a category so no stock count to speak of
- return COMPUTE_STOCK_INFINITE;
- }
- if (cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)
- {
- if (cat->isVersionUnknown())
- {
- // COMPUTE_STOCK_NOT_EVALUATED denotes that a stock folder has a
- // count that cannot be evaluated at this time (folder not up to
- // date)
- return COMPUTE_STOCK_NOT_EVALUATED;
- }
- // Note: stock folders are *not* supposed to have nested subfolders so
- // we stop recursion here but we count only items (subfolders will be
- // ignored)
- // Note: we *always* give a stock count for stock folders, it's useful
- // even if the listing is unassociated
- LLInventoryModel::cat_array_t* cat_array;
- LLInventoryModel::item_array_t* item_array;
- gInventory.getDirectDescendentsOf(cat_id, cat_array, item_array);
- return item_array ? item_array->size() : COMPUTE_STOCK_NOT_EVALUATED;
- }
- LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
- // When force_count is true, we do not do any verification of the
- // marketplace status and simply compute the stock amount based on the
- // descendent hierarchy. This is used specifically when creating a listing.
- if (!force_count)
- {
- // Grab marketplace data for this folder
- S32 depth = depthNesting(cat_id);
- LLUUID listing_uuid = nestedParentId(cat_id, depth);
- if (!marketdata->isListed(listing_uuid))
- {
- // If not listed, the notion of stock is meaningless so it would
- // not be computed for any level
- return COMPUTE_STOCK_INFINITE;
- }
- const LLUUID& vf_uuid = marketdata->getVersionFolder(listing_uuid);
- // Handle the case of the first 2 levels : listing and version folders
- if (depth == 1)
- {
- if (vf_uuid.notNull())
- {
- // If there is a version folder, the stock value for the
- // listing is the version folder stock
- return computeStockCount(vf_uuid, true);
- }
- else
- {
- // If there's no version folder associated, the notion of stock
- // count has no meaning
- return COMPUTE_STOCK_INFINITE;
- }
- }
- else if (depth == 2)
- {
- if (vf_uuid.notNull() && vf_uuid != cat_id)
- {
- // If there is a version folder but we're not it, our stock
- // count is meaningless
- return COMPUTE_STOCK_INFINITE;
- }
- }
- }
- // In all other cases, the stock count is the min of stock folders count
- // found in the descendents
- LLInventoryModel::cat_array_t* cat_array;
- LLInventoryModel::item_array_t* item_array;
- gInventory.getDirectDescendentsOf(cat_id, cat_array, item_array);
- if (!cat_array || !item_array)
- {
- llwarns << "Failed to get descendents of: " << cat_id << llendl;
- return COMPUTE_STOCK_INFINITE;
- }
- // COMPUTE_STOCK_INFINITE denotes a folder that does not contain any stock
- // folder in its descendents
- S32 curr_count = COMPUTE_STOCK_INFINITE;
- // Note: marketplace listings have a maximum depth nesting of 4
- LLInventoryModel::cat_array_t cat_array_copy = *cat_array;
- for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(),
- end = cat_array_copy.end();
- iter != end; ++iter)
- {
- LLViewerInventoryCategory* category = *iter;
- if (!category) continue; // Paranoia
- S32 count = computeStockCount(category->getUUID(), true);
- if (curr_count == COMPUTE_STOCK_INFINITE ||
- (count != COMPUTE_STOCK_INFINITE && count < curr_count))
- {
- curr_count = count;
- }
- }
- return curr_count;
- }
- //static
- bool LLMarketplace::processUpdateNotification(const LLSD& data)
- {
- LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
- S32 listing_id = data["listing_id"].asInteger();
- std::string state = data["state"].asString();
- if (state == "deleted")
- {
- // Perform the deletion viewer side, no alert shown in this case
- marketdata->deleteListing(listing_id);
- return true;
- }
- else
- {
- // In general, no message will be displayed, all we want is to get the
- // listing updated in the inventory. If getListing() fails though, the
- // message of the alert will be shown by the caller
- return marketdata->getListing(listing_id);
- }
- }
- //static
- bool LLMarketplace::updateIfListed(const LLUUID& folder_id,
- const LLUUID& parent_id)
- {
- S32 depth = LLMarketplace::depthNesting(folder_id);
- if (depth == 1 || depth == 2)
- {
- // Trigger an SLM listing update
- LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
- S32 listing_id = depth == 1 ? marketdata->getListingID(folder_id)
- : marketdata->getListingID(parent_id);
- marketdata->getListing(listing_id);
- return true;
- }
- return false;
- }
- //static
- void LLMarketplace::inventoryContextMenu(LLFolderBridge* folder,
- const LLUUID& id, U32 flags,
- std::vector<std::string>& items,
- std::vector<std::string>& disabled_items)
- {
- if (!folder)
- {
- llwarns << "NULL folder bridge !" << llendl;
- llassert(false);
- return;
- }
- LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
- U32 status = marketdata->getSLMStatus();
- if (status != MarketplaceStatusCodes::MARKET_PLACE_MERCHANT &&
- status != MarketplaceStatusCodes::MARKET_PLACE_MIGRATED_MERCHANT)
- {
- // Disable everything that could harm the Marketplace listings while
- // we are not connected.
- disabled_items.emplace_back("Rename");
- disabled_items.emplace_back("Cut");
- disabled_items.emplace_back("Paste");
- disabled_items.emplace_back("Delete");
- if ((status == MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE ||
- status == MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED))
- {
- items.emplace_back("Marketplace Connect");
- }
- return;
- }
- S32 depth = depthNesting(id);
- bool is_updating = marketdata->isUpdating(id, depth);
- // Non Marketplace-specific entries
- if (depth > 0 &&
- folder->getPreferredType() != LLFolderType::FT_MARKETPLACE_STOCK)
- {
- items.emplace_back("New Folder");
- if (is_updating)
- {
- disabled_items.emplace_back("New Folder");
- }
- else if (depth >= 2)
- {
- // Prevent creation of new folders if the max count has been
- // reached on this version folder (active or not)
- const LLUUID& local_listing_id = nestedParentId(id, depth - 1);
- LLInventoryModel::cat_array_t categories;
- LLInventoryModel::item_array_t items;
- gInventory.collectDescendents(local_listing_id, categories, items,
- false);
- U32 max_count = gSavedSettings.getU32("InventoryOutboxMaxFolderCount");
- if (categories.size() >= max_count)
- {
- disabled_items.emplace_back("New Folder");
- }
- }
- }
- if (is_updating)
- {
- disabled_items.emplace_back("Rename");
- disabled_items.emplace_back("Cut");
- disabled_items.emplace_back("Copy");
- disabled_items.emplace_back("Paste");
- disabled_items.emplace_back("Delete");
- }
- // Marketplace-specific entries
- items.emplace_back("Marketplace Separator");
- if (depth == 0)
- {
- items.emplace_back("Marketplace Check Listing");
- }
- else if (depth == 1)
- {
- // Options available at the Listing Folder level
- items.emplace_back("Marketplace Create Listing");
- items.emplace_back("Marketplace Associate Listing");
- items.emplace_back("Marketplace Check Listing");
- items.emplace_back("Marketplace List");
- items.emplace_back("Marketplace Unlist");
- if (is_updating || (flags & FIRST_SELECTED_ITEM) == 0)
- {
- // During SLM update, disable all marketplace related options
- // Also disable all if multiple selected items
- disabled_items.emplace_back("Marketplace Create Listing");
- disabled_items.emplace_back("Marketplace Associate Listing");
- disabled_items.emplace_back("Marketplace Check Listing");
- disabled_items.emplace_back("Marketplace List");
- disabled_items.emplace_back("Marketplace Unlist");
- }
- else
- {
- bool listing_logging = false;
- LL_DEBUGS("Marketplace") << "Adding 'Get/refresh listing' for debug purpose";
- listing_logging = true;
- LL_CONT << LL_ENDL;
- if (listing_logging)
- {
- items.emplace_back("Marketplace Get Listing");
- }
- if (marketdata->isListed(id))
- {
- disabled_items.emplace_back("Marketplace Create Listing");
- disabled_items.emplace_back("Marketplace Associate Listing");
- if (marketdata->getVersionFolder(id).isNull())
- {
- disabled_items.emplace_back("Marketplace List");
- disabled_items.emplace_back("Marketplace Unlist");
- }
- else
- {
- if (marketdata->getActivationState(id))
- {
- disabled_items.emplace_back("Marketplace List");
- }
- else
- {
- disabled_items.emplace_back("Marketplace Unlist");
- }
- }
- }
- else
- {
- disabled_items.emplace_back("Marketplace List");
- disabled_items.emplace_back("Marketplace Unlist");
- if (listing_logging)
- {
- disabled_items.emplace_back("Marketplace Get Listing");
- }
- }
- }
- }
- else if (depth == 2)
- {
- // Options available at the Version Folder levels and only for folders
- LLViewerInventoryCategory* cat = gInventory.getCategory(id);
- if (cat && marketdata->isListed(cat->getParentUUID()))
- {
- items.emplace_back("Marketplace Activate");
- items.emplace_back("Marketplace Deactivate");
- if (is_updating || (flags & FIRST_SELECTED_ITEM) == 0)
- {
- // During SLM update, disable all marketplace related options
- // Also disable all if multiple selected items
- disabled_items.emplace_back("Marketplace Activate");
- disabled_items.emplace_back("Marketplace Deactivate");
- }
- else
- {
- if (marketdata->isVersionFolder(id))
- {
- disabled_items.emplace_back("Marketplace Activate");
- if (marketdata->getActivationState(id))
- {
- disabled_items.emplace_back("Marketplace Deactivate");
- }
- }
- else
- {
- disabled_items.emplace_back("Marketplace Deactivate");
- }
- }
- }
- }
- if (depth > 0)
- {
- // Options available at all sub-levels on items and categories
- items.emplace_back("Marketplace Edit Listing");
- const LLUUID& listing_id = nestedParentId(id, depth);
- const LLUUID& version_id = marketdata->getVersionFolder(listing_id);
- if (version_id.isNull() || !marketdata->isListed(listing_id))
- {
- disabled_items.emplace_back("Marketplace Edit Listing");
- }
- }
- }
- //static
- std::string LLMarketplace::rootFolderLabelSuffix()
- {
- std::string suffix;
- LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
- switch (marketdata->getSLMStatus())
- {
- case MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING:
- suffix = LLTrans::getString("MarketplaceInitializing");
- break;
- case MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE:
- suffix = LLTrans::getString("MarketplaceFailure");
- break;
- case MarketplaceStatusCodes::MARKET_PLACE_MERCHANT:
- case MarketplaceStatusCodes::MARKET_PLACE_MIGRATED_MERCHANT:
- {
- switch (marketdata->getSLMDataFetched())
- {
- case MarketplaceFetchCodes::MARKET_FETCH_NOT_DONE:
- case MarketplaceFetchCodes::MARKET_FETCH_LOADING:
- suffix = LLTrans::getString("MarketplaceFetching");
- break;
- case MarketplaceFetchCodes::MARKET_FETCH_FAILED:
- suffix = LLTrans::getString("MarketplaceFetchFailed");
- break;
- case MarketplaceFetchCodes::MARKET_FETCH_DONE:
- default:
- suffix = LLTrans::getString("MarketplaceMerchant");
- }
- break;
- }
- case MarketplaceStatusCodes::MARKET_PLACE_NOT_MERCHANT:
- suffix = LLTrans::getString("MarketplaceNotMerchant");
- break;
- case MarketplaceStatusCodes::MARKET_PLACE_NOT_MIGRATED_MERCHANT:
- suffix = LLTrans::getString("MarketplaceNotMigrated");
- default:
- break;
- }
- if (!suffix.empty())
- {
- suffix = " (" + suffix + ")";
- }
- return suffix;
- }
- //static
- std::string LLMarketplace::folderLabelSuffix(const LLUUID& cat_id)
- {
- std::string suffix;
- LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
- U32 status = marketdata->getSLMStatus();
- if (status != MarketplaceStatusCodes::MARKET_PLACE_MERCHANT &&
- status != MarketplaceStatusCodes::MARKET_PLACE_MIGRATED_MERCHANT)
- {
- return suffix;
- }
- if (marketdata->isUpdating(cat_id))
- {
- // Skip expensive computations if we are waiting for an update
- suffix = LLTrans::getString("MarketplaceUpdating");
- }
- else
- {
- if (marketdata->isListed(cat_id)) // Listing folder case
- {
- S32 id = marketdata->getListingID(cat_id);
- if (id)
- {
- suffix = llformat("%d", id);
- }
- else
- {
- suffix = LLTrans::getString("MarketplaceNoID");
- }
- if (marketdata->getActivationState(cat_id))
- {
- suffix += " - " + LLTrans::getString("MarketplaceLive");
- }
- }
- else if (marketdata->isVersionFolder(cat_id)) // Version folder case
- {
- suffix = LLTrans::getString("MarketplaceActive");
- }
- // Add stock amount
- S32 stock_count = computeStockCount(cat_id);
- if (stock_count == COMPUTE_STOCK_NOT_EVALUATED)
- {
- // Add updating suffix
- if (!suffix.empty())
- {
- suffix += " - ";
- }
- suffix += LLTrans::getString("MarketplaceUpdating");
- }
- else if (stock_count == 0)
- {
- if (!suffix.empty())
- {
- suffix += " - ";
- }
- suffix += LLTrans::getString("MarketplaceNoStock");
- }
- else if (stock_count > 0)
- {
- if (!suffix.empty())
- {
- suffix += " - ";
- }
- LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
- if (cat &&
- cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)
- {
- suffix += LLTrans::getString("MarketplaceStock") + "=" +
- llformat("%d", stock_count);
- }
- else
- {
- suffix += LLTrans::getString("MarketplaceMax") + "=" +
- llformat("%d", stock_count);
- }
- }
- }
- if (!suffix.empty())
- {
- suffix = " (" + suffix + ")";
- }
- return suffix;
- }
- //static
- bool LLMarketplace::isFolderActive(const LLUUID& cat_id)
- {
- LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
- U32 status = marketdata->getSLMStatus();
- return (status == MarketplaceStatusCodes::MARKET_PLACE_MIGRATED_MERCHANT ||
- status == MarketplaceStatusCodes::MARKET_PLACE_MERCHANT) &&
- marketdata->getActivationState(cat_id);
- }
- //static
- void LLMarketplace::getListing(const LLUUID& folder_id)
- {
- LLMarketplaceData::getInstance()->getListing(folder_id);
- }
- //static
- void LLMarketplace::createListing(const LLUUID& folder_id)
- {
- LLViewerInventoryCategory* cat = gInventory.getCategory(folder_id);
- sMessage.clear();
- bool valid = validateListings(cat,
- boost::bind(&LLMarketplace::gatherMessage,
- _1, _2, _3),
- false);
- if (!valid)
- {
- sMessage.clear();
- valid = validateListings(cat,
- boost::bind(&LLMarketplace::gatherMessage,
- _1, _2, _3));
- if (valid)
- {
- gNotifications.add("MerchantForceValidateListing");
- }
- }
- if (valid)
- {
- LLMarketplaceData::getInstance()->createListing(folder_id);
- }
- else
- {
- LLSD subs;
- subs["ERROR_CODE"] = sMessage;
- gNotifications.add("MerchantListingFailed", subs);
- }
- }
- //static
- void LLMarketplace::clearListing(const LLUUID& folder_id)
- {
- LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
- if (marketdata->isListed(folder_id))
- {
- marketdata->clearListing(folder_id);
- }
- }
- //static
- void LLMarketplace::editListing(const LLUUID& folder_id)
- {
- std::string url =
- LLMarketplaceData::getInstance()->getListingURL(folder_id);
- if (!url.empty())
- {
- LLWeb::loadURL(url);
- }
- }
- //static
- void LLMarketplace::gatherMessage(std::string& message, S32 depth,
- LLError::ELevel log_level)
- {
- if (log_level > LLError::LEVEL_WARN && !sMessage.empty())
- {
- // Currently, we do not gather all messages as it creates very long
- // alerts. Users can get to the whole list of errors on a listing using
- // the "Check listing" right click menu
- return;
- }
- // Take the leading spaces out...
- std::string::size_type start = message.find_first_not_of(' ');
- // Append the message
- sMessage += message.substr(start, message.length() - start);
- }
- //static
- void LLMarketplace::listFolder(const LLUUID& folder_id, bool list)
- {
- if (depthNesting(folder_id) == 1)
- {
- LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
- if (list)
- {
- const LLUUID& version_id = marketdata->getVersionFolder(folder_id);
- LLViewerInventoryCategory* cat = gInventory.getCategory(version_id);
- sMessage.clear();
- if (!validateListings(cat,
- boost::bind(&LLMarketplace::gatherMessage,
- _1, _2, _3)))
- {
- LLSD subs;
- subs["ERROR_CODE"] = sMessage;
- gNotifications.add("MerchantListingFailed", subs);
- }
- else
- {
- marketdata->activateListing(folder_id, true, 1);
- }
- }
- else
- {
- marketdata->activateListing(folder_id, false, 1);
- }
- }
- }
- //static
- void LLMarketplace::activateFolder(const LLUUID& folder_id, bool activate)
- {
- if (depthNesting(folder_id) == 2)
- {
- LLViewerInventoryCategory* cat = gInventory.getCategory(folder_id);
- if (cat)
- {
- sMessage.clear();
- if (activate &&
- !validateListings(cat,
- boost::bind(&LLMarketplace::gatherMessage,
- _1, _2, _3),
- false, 2))
- {
- LLSD subs;
- subs["ERROR_CODE"] = sMessage;
- gNotifications.add("MerchantFolderActivationFailed", subs);
- return;
- }
- LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
- const LLUUID& link_id = activate ? folder_id : LLUUID::null;
- marketdata->setVersionFolder(cat->getParentUUID(), link_id);
- }
- }
- }
- //static
- void LLMarketplace::updateFolderHierarchy(const LLUUID& cat_id)
- {
- // When changing the marketplace status of a folder, the only thing that
- // needs to happen is for all observers of the folder to, possibly, change
- // the display label of the folder so that's the only thing we change on
- // the update mask.
- gInventory.addChangedMask(LLInventoryObserver::LABEL, cat_id);
- // Update all descendent folders down
- LLInventoryModel::cat_array_t* cat_array;
- LLInventoryModel::item_array_t* item_array;
- gInventory.getDirectDescendentsOf(cat_id, cat_array, item_array);
- if (!cat_array || !item_array)
- {
- llwarns << "Failed to get descendents of: " << cat_id << llendl;
- return;
- }
- LLInventoryModel::cat_array_t cat_array_copy = *cat_array;
- for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(),
- end = cat_array_copy.end();
- iter != end; ++iter)
- {
- LLViewerInventoryCategory* category = *iter;
- if (category)
- {
- updateFolderHierarchy(category->getUUID());
- }
- }
- }
- //static
- void LLMarketplace::updateCategory(const LLUUID& cur_uuid,
- bool perform_consistency_enforcement)
- {
- // When changing the marketplace status of an item, we usually have to
- // change the status of all folders in the same listing. This is because
- // the display of each folder is affected by the overall status of the
- // whole listing. Consequently, the only way to correctly update an item
- // anywhere in the marketplace is to update the whole listing from its
- // listing root. This is not as bad as it seems as we only update folders,
- // not items, and the folder nesting depth is limited to 4.
- // We also take care of degenerated cases so we do not update all folders
- // in the inventory by mistake.
- if (cur_uuid.isNull())
- {
- return;
- }
- LLViewerInventoryCategory* cat = gInventory.getCategory(cur_uuid);
- if (!cat || cat->isVersionUnknown())
- {
- return;
- }
- LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
- // Grab marketplace listing data for this item
- S32 depth = depthNesting(cur_uuid);
- if (depth > 0)
- {
- // Retrieve the listing uuid this object is in
- const LLUUID& listing_uuid = nestedParentId(cur_uuid, depth);
- if (perform_consistency_enforcement)
- {
- cat = gInventory.getCategory(listing_uuid);
- if (!cat || cat->isVersionUnknown())
- {
- perform_consistency_enforcement = false;
- }
- }
- // Verify marketplace data consistency for this listing
- if (perform_consistency_enforcement &&
- marketdata->isListed(listing_uuid))
- {
- const LLUUID& vf_uuid = marketdata->getVersionFolder(listing_uuid);
- if (vf_uuid.notNull())
- {
- S32 version_depth = depthNesting(vf_uuid);
- if (version_depth != 2 ||
- !gInventory.isObjectDescendentOf(vf_uuid, listing_uuid))
- {
- llinfos << "Unlisting and clearing the listing folder "
- << listing_uuid
- << " because the version folder " << vf_uuid
- << " is not at the right place anymore" << llendl;
- marketdata->setVersionFolder(listing_uuid, LLUUID::null);
- }
- else if (gInventory.isCategoryComplete(vf_uuid) &&
- marketdata->getActivationState(vf_uuid) &&
- count_descendants_items(vf_uuid) == 0 &&
- !marketdata->isUpdating(vf_uuid, depth))
- {
- llinfos << "Unlisting the listing folder " << listing_uuid
- << " because the version folder " << vf_uuid
- << " is empty" << llendl;
- marketdata->activateListing(listing_uuid, false);
- }
- }
- }
- // Check if the count on hand needs to be updated on SLM
- if (perform_consistency_enforcement &&
- computeStockCount(listing_uuid,
- true) != marketdata->getCountOnHand(listing_uuid))
- {
- marketdata->updateCountOnHand(listing_uuid);
- }
- // Update all descendents starting from the listing root
- updateFolderHierarchy(listing_uuid);
- }
- else if (depth == 0)
- {
- // If this is the marketplace listings root itself, update all descendents
- if (gInventory.getCategory(cur_uuid))
- {
- updateFolderHierarchy(cur_uuid);
- }
- }
- else
- {
- // If the folder is outside the marketplace listings root, clear its
- // SLM data if needs be
- if (perform_consistency_enforcement && marketdata->isListed(cur_uuid))
- {
- llinfos << "Disassociating since the listing folder is not under the marketplace folder anymore"
- << llendl;
- marketdata->clearListing(cur_uuid);
- }
- // Update all descendents if this is a category
- if (gInventory.getCategory(cur_uuid))
- {
- updateFolderHierarchy(cur_uuid);
- }
- }
- }
- // Iterate through the marketplace and flag for label change all categories
- // that countain a stock folder (i.e. stock folders and embedding folders up
- // the hierarchy)
- //static
- void LLMarketplace::updateAllCounts(const LLUUID& cat_id)
- {
- // Get all descendent folders down
- LLInventoryModel::cat_array_t* cat_array;
- LLInventoryModel::item_array_t* item_array;
- gInventory.getDirectDescendentsOf(cat_id, cat_array, item_array);
- if (!cat_array || !item_array)
- {
- llwarns << "Failed to get descendents of: " << cat_id << llendl;
- return;
- }
- LLInventoryModel::cat_array_t cat_array_copy = *cat_array;
- for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(),
- end = cat_array_copy.end();
- iter != end; ++iter)
- {
- LLViewerInventoryCategory* category = *iter;
- if (!category) continue; // Paranoia
- if (category->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)
- {
- // Listing containing stock folders needs to be updated but not
- // others. Note: we take advantage of the fact that stock folder
- // *do not* contain sub folders to avoid a recursive call here.
- updateCategory(category->getUUID());
- gInventory.notifyObservers();
- }
- else
- {
- // Explore the contained folders recursively
- updateAllCounts(category->getUUID());
- }
- }
- }
- //static
- void LLMarketplace::updateAllCounts()
- {
- if (LLMarketplaceData::getInstance()->checkDirtyCount())
- {
- // Get the marketplace root and launch the recursive exploration
- if (sMarketplaceListingId.notNull())
- {
- updateAllCounts(sMarketplaceListingId);
- }
- }
- }
- //static
- void LLMarketplace::initializeCallback()
- {
- LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
- U32 status = marketdata->getSLMStatus();
- if (status == MarketplaceStatusCodes::MARKET_PLACE_MERCHANT ||
- status == MarketplaceStatusCodes::MARKET_PLACE_MIGRATED_MERCHANT)
- {
- // Create the Marketplace Listings folder if missing
- sMarketplaceListingId =
- gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS,
- true);
- if (sMarketplaceListingId.isNull())
- {
- llwarns << "Failed to create the Marketplace Listings folder"
- << llendl;
- marketdata->setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED);
- }
- else
- {
- marketdata->setSLMDataFetched(MarketplaceFetchCodes::MARKET_FETCH_LOADING);
- marketdata->getSLMListings();
- }
- }
- else
- {
- sMarketplaceListingId =
- gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS,
- false);
- }
- if (sMarketplaceListingId.isNull())
- {
- return;
- }
- // We should not have to do that but with a client/server system relying on
- // a "well known folder" convention, things get messy and conventions get
- // broken down eventually
- gInventory.consolidateForType(sMarketplaceListingId,
- LLFolderType::FT_MARKETPLACE_LISTINGS);
- // Force an update of the market place items labels
- LL_DEBUGS("Marketplace") << "Updating Marketplace Listings folder items labels"
- << LL_ENDL;
- gInventory.addChangedMask(LLInventoryObserver::LABEL,
- sMarketplaceListingId);
- #if 0 // We needed this during the SLM transition, because when crossing
- // borders with a non-SLM-aware region, the labels of the folders were
- // changed based on the 'not MARKET_PLACE_MERCHANT' status, instead of
- // being updated by the responders (which obviously could not respond
- // any more to anything...)
- LLInventoryModel::cat_array_t descendent_categories;
- LLInventoryModel::item_array_t descendent_items;
- gInventory.collectDescendents(sMarketplaceListingId,
- descendent_categories, descendent_items,
- false);
- for (S32 i = 0, count = descendent_categories.size(); i < count; ++i)
- {
- LLViewerInventoryCategory* cat = descendent_categories[i];
- if (cat)
- {
- gInventory.addChangedMask(LLInventoryObserver::LABEL,
- cat->getUUID());
- }
- }
- #endif
- gInventory.notifyObservers();
- }
- //static
- void LLMarketplace::setup(bool warn)
- {
- sMarketplaceListingId =
- gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS,
- false);
- if (sMarketplaceListingId.notNull())
- {
- LLInventoryModel::cat_array_t categories;
- LLInventoryModel::item_array_t items;
- gInventory.collectDescendents(sMarketplaceListingId, categories, items,
- false);
- U32 max_count = gSavedSettings.getU32("MarketplaceLargeInventory");
- if (categories.size() >= max_count)
- {
- if (warn)
- {
- gNotifications.add("AlertLargeMarketplace");
- }
- return;
- }
- }
- LLMarketplaceData::getInstance()->initializeSLM(boost::bind(&LLMarketplace::initializeCallback));
- }
- //static
- void LLMarketplace::checkMerchantStatus()
- {
- LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
- marketdata->setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED);
- marketdata->initializeSLM(boost::bind(&LLMarketplace::initializeCallback));
- }
- //static
- bool LLMarketplace::connected()
- {
- U32 status = LLMarketplaceData::getInstance()->getSLMStatus();
- return status == MarketplaceStatusCodes::MARKET_PLACE_MERCHANT ||
- status == MarketplaceStatusCodes::MARKET_PLACE_MIGRATED_MERCHANT;
- }
- bool sort_alpha(const LLViewerInventoryCategory* cat1,
- const LLViewerInventoryCategory* cat2)
- {
- return cat1->getName().compare(cat2->getName()) < 0;
- }
- // Make all relevant business logic checks on the marketplace listings starting
- // with the folder as argument. This function does no deletion of listings but
- // a mere audit and raises issues to the user (through the optional callback).
- // It also returns a boolean, true if things validate, false if issues are
- // raised. The only inventory changes that are done is to move and sort folders
- // containing no-copy items to stock folders.
- //static
- bool LLMarketplace::validateListings(LLViewerInventoryCategory* cat,
- LLMarketplace::validation_callback_t cb,
- bool fix_hierarchy, S32 depth)
- {
- if (!cat) return false;
- // Folder is valid unless issue is raised
- bool result = true;
- // Get the type and the depth of the folder
- LLViewerInventoryCategory* viewer_cat = cat;
- const LLFolderType::EType folder_type = cat->getPreferredType();
- if (depth < 0)
- {
- // If the depth argument was not provided, evaluate the depth directly
- depth = depthNesting(cat->getUUID());
- }
- if (depth < 0)
- {
- // If the folder is not under the marketplace listings root, we run
- // validation as if it was a listing folder and prevent any hierarchy
- // fix. This allows the function to be used to pre-validate a folder
- // anywhere in the inventory.
- depth = 1;
- fix_hierarchy = false;
- }
- // Set the indentation for print output
- std::string indent;
- for (S32 i = 1; i < depth; ++i)
- {
- indent += " ";
- }
- std::string message;
- // Check out that version folders are marketplace ready
- if (depth == 2)
- {
- if (!canMoveFolderInto(cat, cat, cat, message, 0))
- {
- result = false;
- if (cb)
- {
- message = indent + cat->getName() +
- LLTrans::getString("Marketplace Validation Error") +
- " " + message;
- cb(message, depth, LLError::LEVEL_ERROR);
- }
- }
- }
- // Check out that stock folders are at the right level
- if (folder_type == LLFolderType::FT_MARKETPLACE_STOCK && depth <= 2)
- {
- if (cb)
- {
- message = indent + cat->getName();
- }
- if (fix_hierarchy)
- {
- if (cb)
- {
- message +=
- LLTrans::getString("Marketplace Validation Warning") +
- " " +
- LLTrans::getString("Marketplace Validation Warning Stock");
- cb(message, depth, LLError::LEVEL_WARN);
- }
- // Nest the stock folder one level deeper in a normal folder and
- // restart from there
- const LLUUID& parent_id = cat->getParentUUID();
- const LLUUID& folder_id =
- gInventory.createCategoryUDP(parent_id,
- LLFolderType::FT_NONE,
- cat->getName());
- gInventory.notifyObservers();
- LLViewerInventoryCategory* new_cat =
- gInventory.getCategory(folder_id);
- gInventory.changeCategoryParent(viewer_cat, folder_id, false);
- gInventory.notifyObservers();
- result &= validateListings(new_cat, cb, fix_hierarchy, ++depth);
- return result;
- }
- result = false;
- if (cb)
- {
- message += LLTrans::getString("Marketplace Validation Error") +
- " " +
- LLTrans::getString("Marketplace Validation Warning Stock");
- cb(message, depth, LLError::LEVEL_ERROR);
- }
- }
- // Item sorting and validation: sorting and moving the various stock items
- // is complicated as the set of constraints is high. We need to:
- // * separate non stock items, stock items per types in different folders
- // * have stock items nested at depth 2 at least
- // * never ever move the non-stock items
- LLInventoryModel::cat_array_t* cat_array;
- LLInventoryModel::item_array_t* item_array;
- gInventory.getDirectDescendentsOf(cat->getUUID(), cat_array, item_array);
- if (!cat_array || !item_array)
- {
- if (cb)
- {
- message = indent + cat->getName() +
- LLTrans::getString("Marketplace Failed Descendents");
- cb(message, depth, LLError::LEVEL_ERROR);
- }
- return false;
- }
- // We use a composite (type, permissions) key on that map to store UUIDs of
- // items of same (type, permissions)
- std::map<U32, std::vector<LLUUID> > items_vector;
- // Parse the items and create vectors of item UUIDs sorting copyable items
- // and stock items of various types
- const LLUUID& group_id = gAgent.getGroupID();
- bool has_bad_items = false;
- LLInventoryModel::item_array_t item_array_copy = *item_array;
- for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin();
- iter != item_array_copy.end(); ++iter)
- {
- LLViewerInventoryItem* itemp = *iter;
- if (!itemp) continue; // Paranoia
- // Test but skip items that should not be there to start with, raise
- // an error message for those
- std::string error_msg;
- if (!can_move_to_marketplace(itemp, error_msg))
- {
- has_bad_items = true;
- if (cb && fix_hierarchy)
- {
- message = indent + itemp->getName() +
- LLTrans::getString("Marketplace Validation Error") +
- " " + error_msg;
- cb(message, depth, LLError::LEVEL_ERROR);
- }
- continue;
- }
- // Update the appropriate vector item for that type
- // Default value for non stock items:
- LLInventoryType::EType type = LLInventoryType::IT_COUNT;
- U32 perms = 0;
- if (!itemp->getPermissions().allowCopyBy(gAgentID, group_id))
- {
- // Get the item type for stock items
- type = itemp->getInventoryType();
- perms = itemp->getPermissions().getMaskNextOwner();
- }
- U32 key = (((U32)(type) & 0xFF) << 24) | (perms & 0xFFFFFF);
- items_vector[key].emplace_back(itemp->getUUID());
- }
- // How many types of items ? Which type is it if only one ?
- S32 count = items_vector.size();
- // This is the key for any normal copyable item:
- U32 default_key = (U32)(LLInventoryType::IT_COUNT) << 24;
- // The key in the case of one item type only:
- U32 unique_key = count == 1 ? items_vector.begin()->first : default_key;
- // If we have no items in there (only folders or empty), analyze a bit
- // further
- if (count == 0 && !has_bad_items)
- {
- if (cb)
- {
- message = indent + cat->getName();
- if (cat_array->size() == 0)
- {
- // So we have no item and no folder. That is a warning.
- if (depth == 2)
- {
- // If this is an empty version folder, warn only (listing
- // would not be delivered by AIS, but only AIS should
- // unlist)
- message +=
- LLTrans::getString("Marketplace Validation Error Empty Version");
- cb(message, depth, LLError::LEVEL_WARN);
- }
- else if (depth > 2 &&
- folder_type == LLFolderType::FT_MARKETPLACE_STOCK)
- {
- // If this is a legit but empty stock folder, warn only
- // (listing must stay searchable when out of stock)
- message +=
- LLTrans::getString("Marketplace Validation Error Empty Stock");
- cb(message, depth, LLError::LEVEL_WARN);
- }
- else
- {
- // We warn if there's nothing in a regular folder (may be it's
- // an under construction listing)
- message +=
- LLTrans::getString("Marketplace Validation Warning Empty");
- cb(message, depth, LLError::LEVEL_WARN);
- }
- }
- else if (result && depth >= 1)
- {
- // Done with that folder: print out the folder name unless we
- // already found an error here
- message += LLTrans::getString("Marketplace Validation Log");
- cb(message, depth, LLError::LEVEL_INFO);
- }
- }
- }
- // If we have a single type of items of the right type in the right place,
- // we are done
- else if (count == 1 && !has_bad_items &&
- ((unique_key == default_key && depth > 1) ||
- (folder_type == LLFolderType::FT_MARKETPLACE_STOCK &&
- depth > 2 && cat_array->size() == 0)))
- {
- // Done with that folder: print out the folder name unless we already
- // found an error here
- if (cb && result && depth >= 1)
- {
- message = indent + cat->getName() +
- LLTrans::getString("Marketplace Validation Log");
- cb(message, depth, LLError::LEVEL_INFO);
- }
- }
- else
- {
- if (fix_hierarchy && !has_bad_items)
- {
- // Alert the user when an existing stock folder has to be split
- if (folder_type == LLFolderType::FT_MARKETPLACE_STOCK &&
- (count >= 2 || cat_array->size() > 0))
- {
- gNotifications.add("AlertMerchantStockFolderSplit");
- }
- // If we have more than 1 type of items or we are at the listing
- // level or we have stock/no stock type mismatch, wrap the items
- // in subfolders
- if (count > 1 || depth == 1 ||
- (folder_type == LLFolderType::FT_MARKETPLACE_STOCK &&
- unique_key == default_key) ||
- (folder_type != LLFolderType::FT_MARKETPLACE_STOCK &&
- unique_key != default_key))
- {
- // Create one folder per vector at the right depth and of the
- // right type
- for (std::map<U32, std::vector<LLUUID> >::iterator
- it = items_vector.begin(), end = items_vector.end();
- it != end; ++it)
- {
- // Create a new folder
- const LLUUID& parent_uuid = depth > 2 ? viewer_cat->getParentUUID()
- : viewer_cat->getUUID();
- LLViewerInventoryItem* item = gInventory.getItem(it->second.back());
- std::string folder_name = depth >= 1 ? viewer_cat->getName()
- : item->getName();
- LLFolderType::EType new_folder_type =
- it->first == default_key ? LLFolderType::FT_NONE
- : LLFolderType::FT_MARKETPLACE_STOCK;
- if (cb)
- {
- message = indent + folder_name;
- if (new_folder_type == LLFolderType::FT_MARKETPLACE_STOCK)
- {
- message +=
- LLTrans::getString("Marketplace Validation Warning Create Stock");
- }
- else
- {
- message +=
- LLTrans::getString("Marketplace Validation Warning Create Version");
- }
- cb(message, depth, LLError::LEVEL_WARN);
- }
- LLUUID folder_uuid = gInventory.createCategoryUDP(parent_uuid,
- new_folder_type,
- folder_name);
- gInventory.notifyObservers();
- // Move each item to the new folder
- while (!it->second.empty())
- {
- item = gInventory.getItem(it->second.back());
- if (cb)
- {
- message = indent + item->getName() +
- LLTrans::getString("Marketplace Validation Warning Move");
- cb(message, depth, LLError::LEVEL_WARN);
- }
- gInventory.changeItemParent(item, folder_uuid, true);
- gInventory.notifyObservers();
- it->second.pop_back();
- }
- updateCategory(parent_uuid);
- gInventory.notifyObservers();
- updateCategory(folder_uuid);
- gInventory.notifyObservers();
- }
- }
- // Stock folder should have no sub folder so reparent those up
- if (folder_type == LLFolderType::FT_MARKETPLACE_STOCK)
- {
- const LLUUID& parent_uuid = cat->getParentUUID();
- gInventory.getDirectDescendentsOf(cat->getUUID(), cat_array,
- item_array);
- if (!cat_array || !item_array)
- {
- if (cb)
- {
- message = indent + cat->getName() +
- LLTrans::getString("Marketplace Failed Descendents");
- cb(message, depth, LLError::LEVEL_ERROR);
- }
- result = false;
- }
- else
- {
- LLInventoryModel::cat_array_t cat_array_copy = *cat_array;
- for (LLInventoryModel::cat_array_t::iterator
- iter = cat_array_copy.begin();
- iter != cat_array_copy.end(); ++iter)
- {
- LLViewerInventoryCategory* viewer_cat = *iter;
- if (!viewer_cat) continue; // Paranoia
- gInventory.changeCategoryParent(viewer_cat, parent_uuid,
- false);
- gInventory.notifyObservers();
- result &= validateListings(viewer_cat, cb, fix_hierarchy,
- depth);
- }
- }
- }
- }
- else if (cb)
- {
- // We are not fixing the hierarchy but reporting problems, report
- // everything we can find.
- // Print the folder name
- if (result && depth >= 1)
- {
- message = indent + cat->getName();
- if (folder_type == LLFolderType::FT_MARKETPLACE_STOCK)
- {
- if (count >= 2)
- {
- // Report if a stock folder contains a mix of items
- result = false;
- message +=
- LLTrans::getString("Marketplace Validation Error Mixed Stock");
- cb(message, depth, LLError::LEVEL_ERROR);
- }
- else if (cat_array->size())
- {
- // Report if a stock folder contains subfolders
- result = false;
- message +=
- LLTrans::getString("Marketplace Validation Error Subfolder In Stock");
- cb(message, depth, LLError::LEVEL_ERROR);
- }
- }
- if (result)
- {
- // Simply print the folder name
- message += LLTrans::getString("Marketplace Validation Log");
- cb(message, depth, LLError::LEVEL_INFO);
- }
- }
- // Scan each item and report if there's a problem
- LLInventoryModel::item_array_t item_array_copy = *item_array;
- for (LLInventoryModel::item_array_t::iterator
- iter = item_array_copy.begin();
- iter != item_array_copy.end(); ++iter)
- {
- LLViewerInventoryItem* item = *iter;
- if (!item) continue; // Paranoia
- message = indent + " " + item->getName();
- std::string error_msg;
- if (!can_move_to_marketplace(item, error_msg))
- {
- // Report items that should not be there to start with
- result = false;
- message += LLTrans::getString("Marketplace Validation Error") +
- " " + error_msg;
- cb(message, depth, LLError::LEVEL_ERROR);
- }
- else if (folder_type != LLFolderType::FT_MARKETPLACE_STOCK &&
- !item->getPermissions().allowCopyBy(gAgentID,
- group_id))
- {
- // Report stock items that are misplaced
- result = false;
- message +=
- LLTrans::getString("Marketplace Validation Error Stock Item");
- cb(message, depth, LLError::LEVEL_ERROR);
- }
- else if (depth == 1)
- {
- // Report items not wrapped in version folder
- result = false;
- message +=
- LLTrans::getString("Marketplace Validation Warning Unwrapped Item");
- cb(message, depth, LLError::LEVEL_ERROR);
- }
- }
- }
- // Clean up
- if (viewer_cat->getDescendentCount() == 0)
- {
- // Remove the current folder if it ends up empty
- if (cb)
- {
- message = indent + viewer_cat->getName() +
- LLTrans::getString("Marketplace Validation Warning Delete");
- cb(message, depth, LLError::LEVEL_WARN);
- }
- gInventory.removeCategory(cat->getUUID());
- gInventory.notifyObservers();
- return result && !has_bad_items;
- }
- }
- // Recursion : Perform the same validation on each nested folder
- gInventory.getDirectDescendentsOf(cat->getUUID(), cat_array, item_array);
- if (!cat_array || !item_array)
- {
- if (cb)
- {
- message = indent + cat->getName() +
- LLTrans::getString("Marketplace Failed Descendents");
- cb(message, depth, LLError::LEVEL_ERROR);
- }
- return false;
- }
- LLInventoryModel::cat_array_t cat_array_copy = *cat_array;
- // Sort the folders in alphabetical order first
- std::sort(cat_array_copy.begin(), cat_array_copy.end(), sort_alpha);
- for (LLInventoryModel::cat_array_t::iterator
- iter = cat_array_copy.begin();
- iter != cat_array_copy.end(); ++iter)
- {
- LLViewerInventoryCategory* category = *iter;
- result &= validateListings(category, cb, fix_hierarchy, depth + 1);
- }
- // Update the current folder
- updateCategory(cat->getUUID(), fix_hierarchy);
- gInventory.notifyObservers();
- return result && !has_bad_items;
- }
- //static
- bool LLMarketplace::hasPermissionsForSale(LLViewerInventoryCategory* cat,
- std::string& error_msg)
- {
- if (!cat)
- {
- error_msg = "NULL category !";
- return false;
- }
- LLInventoryModel::cat_array_t* cat_array;
- LLInventoryModel::item_array_t* item_array;
- gInventory.getDirectDescendentsOf(cat->getUUID(), cat_array, item_array);
- if (!cat_array || !item_array)
- {
- llwarns << "Failed to get descendents of: " << cat->getUUID()
- << llendl;
- return false;
- }
- LLInventoryModel::item_array_t item_array_copy = *item_array;
- for (LLInventoryModel::item_array_t::iterator
- iter = item_array_copy.begin(), end = item_array_copy.end();
- iter != end; ++iter)
- {
- LLViewerInventoryItem* item = *iter;
- if (!item || !can_move_to_marketplace(item, error_msg, false))
- {
- return false;
- }
- }
- LLInventoryModel::cat_array_t cat_array_copy = *cat_array;
- for (LLInventoryModel::cat_array_t::iterator
- iter = cat_array_copy.begin(), end = cat_array_copy.end();
- iter != end; ++iter)
- {
- LLViewerInventoryCategory* category = *iter;
- if (!category || !hasPermissionsForSale(category, error_msg))
- {
- return false;
- }
- }
- return true;
- }
- // Returns true if inv_item can be dropped in dest_folder, a folder nested in
- // Marketplace listings (or merchant inventory) under the root_folder root. If
- // false is returned, tooltip_msg contains an error message to display to the
- // user (localized and all). bundle_size is the amount of sibling items that
- // are getting moved to the marketplace at the same time.
- //static
- bool LLMarketplace::canMoveItemInto(const LLViewerInventoryCategory* root_folder,
- LLViewerInventoryCategory* dest_folder,
- LLViewerInventoryItem* inv_item,
- std::string& tooltip_msg,
- S32 bundle_size, bool from_paste)
- {
- // Check stock folder type matches item type in marketplace listings or
- // merchant outbox (even if of no use there for the moment)
- bool move_in_stock =
- dest_folder &&
- dest_folder->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK;
- bool accept = dest_folder && dest_folder->acceptItem(inv_item);
- if (!accept)
- {
- tooltip_msg = LLTrans::getString("TooltipOutboxMixedStock");
- }
- // Check that the item has the right type and permissions to be sold on the
- // marketplace
- if (accept)
- {
- accept = can_move_to_marketplace(inv_item, tooltip_msg, true);
- }
- // Check that the total amount of items woould not violate the max limit on
- // the marketplace
- if (accept)
- {
- // If the dest folder is a stock folder, we do not count the incoming
- // items toward the total (stock items are seen as one)
- S32 existing_item_count = move_in_stock ? 0 : bundle_size;
- // If the dest folder is a stock folder, we do assume that the incoming
- // items are also stock items (they should anyway)
- S32 existing_stock_count = move_in_stock ? bundle_size : 0;
- S32 existing_folder_count = 0;
- // Get the version folder: that's where the counts start from
- const LLViewerInventoryCategory* version_folder = NULL;
- if (root_folder && root_folder != dest_folder)
- {
- version_folder =
- gInventory.getFirstDescendantOf(root_folder->getUUID(),
- dest_folder->getUUID());
- }
- if (version_folder)
- {
- if (!from_paste &&
- gInventory.isObjectDescendentOf(inv_item->getUUID(),
- version_folder->getUUID()))
- {
- // Clear those counts or they will be counted twice because
- // we are already inside the version category
- existing_item_count = 0;
- }
- LLInventoryModel::cat_array_t existing_categories;
- LLInventoryModel::item_array_t existing_items;
- gInventory.collectDescendents(version_folder->getUUID(),
- existing_categories, existing_items,
- false);
- existing_item_count += count_copyable_items(existing_items) +
- count_stock_folders(existing_categories);
- existing_stock_count += count_stock_items(existing_items);
- existing_folder_count += existing_categories.size();
- // If the incoming item is a nocopy (stock) item, we need to
- // consider that it will create a stock folder
- if (!move_in_stock &&
- !inv_item->getPermissions().allowCopyBy(gAgentID,
- gAgent.getGroupID()))
- {
- // Note: we do not assume that all incoming items are no-copy
- // of different kinds...
- ++existing_folder_count;
- }
- }
- static LLCachedControl<U32> max_items(gSavedSettings,
- "InventoryOutboxMaxItemCount");
- static LLCachedControl<U32> max_stock(gSavedSettings,
- "InventoryOutboxMaxStockItemCount");
- static LLCachedControl<U32> max_folders(gSavedSettings,
- "InventoryOutboxMaxFolderCount");
- if (existing_item_count > (S32)max_items)
- {
- LLStringUtil::format_map_t args;
- args["[AMOUNT]"] = llformat("%d", (S32)max_items);
- tooltip_msg = LLTrans::getString("TooltipOutboxTooManyObjects",
- args);
- accept = false;
- }
- else if (existing_stock_count > (S32)max_stock)
- {
- LLStringUtil::format_map_t args;
- args["[AMOUNT]"] = llformat("%d", (S32)max_stock);
- tooltip_msg = LLTrans::getString("TooltipOutboxTooManyStockItems",
- args);
- accept = false;
- }
- else if (existing_folder_count > (S32)max_folders)
- {
- LLStringUtil::format_map_t args;
- args["[AMOUNT]"] = llformat("%d", (S32)max_folders);
- tooltip_msg = LLTrans::getString("TooltipOutboxTooManyFolders",
- args);
- accept = false;
- }
- }
- return accept;
- }
- // Returns true if inv_cat can be dropped in dest_folder, a folder nested in
- // marketplace listings (or merchant inventory) under the root_folder root.
- // If returns is false, tooltip_msg contains an error message to display to the
- // user (localized and all). bundle_size is the amount of sibling items that
- // are getting moved to the marketplace at the same time.
- //static
- bool LLMarketplace::canMoveFolderInto(const LLViewerInventoryCategory* root_folder,
- LLViewerInventoryCategory* dest_folder,
- LLViewerInventoryCategory* inv_cat,
- std::string& tooltip_msg,
- S32 bundle_size, bool from_paste)
- {
- bool accept = true;
- // Compute the nested folders level we will add into with that incoming
- // folder
- S32 incoming_folder_depth = get_folder_levels(inv_cat);
- // Compute the nested folders level we are inserting ourselves in.
- // Note: add 1 when inserting under a listing folder as we need to take the
- // root listing folder in the count
- S32 insertion_point = 1;
- if (root_folder)
- {
- insertion_point = get_folder_path_length(root_folder->getUUID(),
- dest_folder->getUUID()) + 1;
- }
- // Get the version folder: that's where the folders and items counts start
- // from
- const LLViewerInventoryCategory* version_folder = NULL;
- if (insertion_point >= 2)
- {
- version_folder = gInventory.getFirstDescendantOf(root_folder->getUUID(),
- dest_folder->getUUID());
- }
- // Compare the whole with the nested folders depth limit. Note: substract 2
- // as we leave root and version folder out of the count threshold
- U32 max_depth = gSavedSettings.getU32("InventoryOutboxMaxFolderDepth");
- if (incoming_folder_depth + insertion_point - 2 > (S32)max_depth)
- {
- LLStringUtil::format_map_t args;
- args["[AMOUNT]"] = llformat("%d", (S32)max_depth);
- tooltip_msg = LLTrans::getString("TooltipOutboxFolderLevels", args);
- accept = false;
- }
- if (accept)
- {
- LLInventoryModel::cat_array_t descendent_categories;
- LLInventoryModel::item_array_t descendent_items;
- gInventory.collectDescendents(inv_cat->getUUID(),
- descendent_categories, descendent_items,
- false);
- // Note: we assume that we're moving a bunch of folders in. That might
- // be wrong...
- S32 dragged_folder_count = descendent_categories.size() + bundle_size;
- S32 dragged_item_count = count_copyable_items(descendent_items) +
- count_stock_folders(descendent_categories);
- S32 dragged_stock_count = count_stock_items(descendent_items);
- S32 existing_item_count = 0;
- S32 existing_stock_count = 0;
- S32 existing_folder_count = 0;
- if (version_folder)
- {
- if (!from_paste &&
- gInventory.isObjectDescendentOf(inv_cat->getUUID(),
- version_folder->getUUID()))
- {
- // Clear those counts or they will be counted twice because
- // we are already inside the version category
- dragged_folder_count = 0;
- dragged_item_count = 0;
- dragged_stock_count = 0;
- }
- // Tally the total number of categories and items inside the root
- // folder
- LLInventoryModel::cat_array_t existing_categories;
- LLInventoryModel::item_array_t existing_items;
- gInventory.collectDescendents(version_folder->getUUID(),
- existing_categories, existing_items,
- false);
- existing_folder_count += existing_categories.size();
- existing_item_count += count_copyable_items(existing_items) +
- count_stock_folders(existing_categories);
- existing_stock_count += count_stock_items(existing_items);
- }
- const S32 total_folder_count = existing_folder_count + dragged_folder_count;
- const S32 total_item_count = existing_item_count + dragged_item_count;
- const S32 total_stock_count = existing_stock_count + dragged_stock_count;
- static LLCachedControl<U32> max_items(gSavedSettings,
- "InventoryOutboxMaxItemCount");
- static LLCachedControl<U32> max_stock(gSavedSettings,
- "InventoryOutboxMaxStockItemCount");
- static LLCachedControl<U32> max_folders(gSavedSettings,
- "InventoryOutboxMaxFolderCount");
- if (total_folder_count > (S32)max_folders)
- {
- LLStringUtil::format_map_t args;
- args["[AMOUNT]"] = llformat("%d", (S32)max_folders);
- tooltip_msg = LLTrans::getString("TooltipOutboxTooManyFolders",
- args);
- accept = false;
- }
- else if (total_item_count > (S32)max_items)
- {
- LLStringUtil::format_map_t args;
- args["[AMOUNT]"] = llformat("%d", (S32)max_items);
- tooltip_msg = LLTrans::getString("TooltipOutboxTooManyObjects",
- args);
- accept = false;
- }
- else if (total_stock_count > (S32)max_stock)
- {
- LLStringUtil::format_map_t args;
- args["[AMOUNT]"] = llformat("%d", (S32)max_stock);
- tooltip_msg = LLTrans::getString("TooltipOutboxTooManyStockItems",
- args);
- accept = false;
- }
- // Now check that each item in the folder can be moved into the
- // marketplace
- if (accept)
- {
- for (S32 i = 0, count = descendent_items.size(); i < count; ++i)
- {
- LLViewerInventoryItem* item = descendent_items[i];
- if (!can_move_to_marketplace(item, tooltip_msg))
- {
- accept = false;
- break;
- }
- }
- }
- }
- return accept;
- }
- //static
- bool LLMarketplace::moveItemInto(LLViewerInventoryItem* inv_item,
- const LLUUID& dest_folder, bool copy)
- {
- // Get the marketplace listings depth of the destination folder, exit with
- // error if not under marketplace
- S32 depth = depthNesting(dest_folder);
- if (depth < 0)
- {
- LLSD subs;
- subs["ERROR_CODE"] = LLTrans::getString("Marketplace Error Prefix") +
- LLTrans::getString("Marketplace Error Not Merchant");
- gNotifications.add("MerchantPasteFailed", subs);
- return false;
- }
- // We will collapse links into items/folders
- LLViewerInventoryItem* vitem = inv_item;
- LLViewerInventoryCategory* linked_category = vitem->getLinkedCategory();
- if (linked_category)
- {
- // Move the linked folder directly
- return moveFolderInto(linked_category, dest_folder, copy);
- }
- // Grab the linked item if any
- LLViewerInventoryItem* linked_item = vitem->getLinkedItem();
- if (linked_item)
- {
- vitem = linked_item;
- }
- // If we want to copy but the item is no copy, fail silently (this is a
- // common case that does not warrant notification)
- if (copy &&
- !vitem->getPermissions().allowCopyBy(gAgentID, gAgent.getGroupID()))
- {
- return false;
- }
- // Check that the agent has transfer permission on the item: this is
- // required as a resident cannot put on sale items they cannot transfer.
- // Proceed with move if we have permission.
- std::string error_msg;
- if (!can_move_to_marketplace(inv_item, error_msg, true))
- {
- LLSD subs;
- subs["ERROR_CODE"] = LLTrans::getString("Marketplace Error Prefix") +
- error_msg;
- gNotifications.add("MerchantPasteFailed", subs);
- return false;
- }
- LLUUID dest_id(dest_folder); // Destination id may change
- // When moving an isolated item, we might need to create the folder
- // structure to support it
- if (depth == 0)
- {
- // We need a listing folder
- dest_id = gInventory.createCategoryUDP(dest_id, LLFolderType::FT_NONE,
- vitem->getName());
- gInventory.notifyObservers();
- ++depth;
- }
- if (depth == 1)
- {
- // We need a version folder
- dest_id = gInventory.createCategoryUDP(dest_id, LLFolderType::FT_NONE,
- vitem->getName());
- gInventory.notifyObservers();
- ++depth;
- }
- LLViewerInventoryCategory* dest_cat = gInventory.getCategory(dest_id);
- if (!dest_cat)
- {
- llwarns << "Cannot find category for destination folder Id: "
- << dest_id << llendl;
- return false;
- }
- if (dest_cat->getPreferredType() != LLFolderType::FT_MARKETPLACE_STOCK &&
- !vitem->getPermissions().allowCopyBy(gAgentID, gAgent.getGroupID()))
- {
- // We need to create a stock folder to move a no copy item
- dest_id = gInventory.createCategoryUDP(dest_id,
- LLFolderType::FT_MARKETPLACE_STOCK,
- vitem->getName());
- gInventory.notifyObservers();
- dest_cat = gInventory.getCategory(dest_id);
- ++depth;
- }
- // Verify we can have this item in that destination category
- if (!dest_cat->acceptItem(vitem))
- {
- LLSD subs;
- subs["ERROR_CODE"] = LLTrans::getString("Marketplace Error Prefix") +
- LLTrans::getString("Marketplace Error Not Accepted");
- gNotifications.add("MerchantPasteFailed", subs);
- return false;
- }
- if (copy)
- {
- // Copy the item
- LLPointer<LLInventoryCallback> cb =
- new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb,
- dest_id));
- copy_inventory_item(vitem->getPermissions().getOwner(),
- vitem->getUUID(), dest_id, LLStringUtil::null, cb);
- }
- else
- {
- // Reparent the item
- gInventory.changeItemParent(vitem, dest_id, true);
- gInventory.notifyObservers();
- }
- return true;
- }
- //static
- bool LLMarketplace::moveFolderInto(LLViewerInventoryCategory* inv_cat,
- const LLUUID& dest_folder, bool copy,
- bool move_no_copy_items)
- {
- S32 depth = depthNesting(dest_folder);
- if (depth < 0)
- {
- LLSD subs;
- subs["ERROR_CODE"] = LLTrans::getString("Marketplace Error Prefix") +
- LLTrans::getString("Marketplace Error Not Merchant");
- gNotifications.add("MerchantPasteFailed", subs);
- return false;
- }
- // Check that we have adequate permission on all items being moved. Proceed
- // if we do.
- std::string error_msg;
- if (!hasPermissionsForSale(inv_cat, error_msg))
- {
- LLSD subs;
- subs["ERROR_CODE"] = LLTrans::getString("Marketplace Error Prefix") +
- error_msg;
- gNotifications.add("MerchantPasteFailed", subs);
- return false;
- }
- // Get the destination folder
- LLViewerInventoryCategory* dest_cat = gInventory.getCategory(dest_folder);
- if (!dest_cat)
- {
- llwarns << "Cannot find category for destination folder Id: "
- << dest_folder << llendl;
- return false;
- }
- // Check it's not a stock folder
- if (dest_cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)
- {
- LLSD subs;
- subs["ERROR_CODE"] = LLTrans::getString("Marketplace Error Prefix") +
- LLTrans::getString("Marketplace Error Not Accepted");
- gNotifications.add("MerchantPasteFailed", subs);
- return false;
- }
- // Get the parent folder of the moved item: we may have to update it
- const LLUUID& src_folder = inv_cat->getParentUUID();
- LLUUID dest_id(dest_folder); // destination id may change
- if (copy)
- {
- if (depth == 0)
- {
- // We need a listing folder
- dest_id = gInventory.createCategoryUDP(dest_id,
- LLFolderType::FT_NONE,
- inv_cat->getName());
- gInventory.notifyObservers();
- ++depth;
- }
- // Copy the folder
- copy_inventory_category(&gInventory, inv_cat, dest_id, LLUUID::null,
- move_no_copy_items);
- }
- else
- {
- // Reparent the folder
- gInventory.changeCategoryParent(inv_cat, dest_id, false);
- gInventory.notifyObservers();
- // Check the destination folder recursively for no copy items and
- // promote the including folders if any
- validateListings(dest_cat);
- }
- // Update the modified folders
- updateCategory(src_folder);
- gInventory.notifyObservers();
- updateCategory(dest_id);
- gInventory.notifyObservers();
- return true;
- }
- //static
- void LLMarketplace::updateMovedFrom(const LLUUID& from_folder_uuid,
- const LLUUID& cat_id)
- {
- LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
- if (from_folder_uuid == sMarketplaceListingId && cat_id.notNull())
- {
- // If we moved a folder at the listing folder level (i.e. its parent
- // is the marketplace listings folder). Unlist it.
- if (marketdata->isListed(cat_id))
- {
- marketdata->clearListing(cat_id);
- }
- }
- else
- {
- #if 0 // Nope, does not work for forcing an inventory folder label update
- // when moving a stock folder out of a version folder... HB
- LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
- if (cat && cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)
- {
- // If we moved a stock folder, flag all stock counts as dirty
- marketdata->setDirtyCount();
- }
- #endif
- LLUUID version_id = marketdata->getActiveFolder(from_folder_uuid);
- if (version_id.notNull())
- {
- LLViewerInventoryCategory* cat = gInventory.getCategory(version_id);
- if (cat && !validateListings(cat))
- {
- // If we move from an active (listed) listing, check that it is
- // still valid, if not, unlist
- marketdata->activateListing(version_id, false);
- }
- }
- #if 0 // Nope, does not work for forcing an inventory folder label update
- // when moving a stock folder out of a version folder... HB
- version_id = marketdata->getVersionFolder(from_folder_uuid);
- if (version_id.notNull())
- {
- LLViewerInventoryCategory* cat = gInventory.getCategory(version_id);
- if (cat)
- {
- // Update the listing folder we moved from
- updateCategory(cat->getParentUUID());
- gInventory.notifyObservers();
- return;
- }
- }
- #endif
- // Update the folder we moved from anyway
- updateCategory(from_folder_uuid);
- gInventory.notifyObservers();
- }
- }
|