llmarketplacefunctions.cpp 108 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740
  1. /**
  2. * @file llmarketplacefunctions.cpp
  3. * @brief Implementation of assorted functions related to the marketplace
  4. *
  5. * $LicenseInfo:firstyear=2012&license=viewergpl$
  6. *
  7. * Copyright (c) 2012, Linden Research, Inc.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file ("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. #include "llviewerprecompiledheaders.h"
  33. #include "llmarketplacefunctions.h"
  34. #include "llcallbacklist.h"
  35. #include "llcorehttputil.h"
  36. #include "llnotifications.h"
  37. #include "llsdserialize.h"
  38. #include "lltrans.h"
  39. #include "llagent.h"
  40. #include "llgridmanager.h"
  41. #include "llinventorybridge.h"
  42. #include "llinventorymodel.h"
  43. #include "llviewerinventory.h"
  44. #include "llviewercontrol.h"
  45. #include "llviewermedia.h"
  46. #include "llweb.h"
  47. // static variable members
  48. std::string LLMarketplace::sMessage;
  49. LLUUID LLMarketplace::sMarketplaceListingId;
  50. // Helpers
  51. // Get the version folder: if there is only one subfolder, we will use it as a
  52. // version folder
  53. LLUUID getVersionFolderIfUnique(const LLUUID& folder_id)
  54. {
  55. LLUUID version_id;
  56. LLInventoryModel::cat_array_t* categories;
  57. LLInventoryModel::item_array_t* items;
  58. gInventory.getDirectDescendentsOf(folder_id, categories, items);
  59. if (categories && categories->size() == 1)
  60. {
  61. version_id = categories->begin()->get()->getUUID();
  62. }
  63. else
  64. {
  65. gNotifications.add("AlertMerchantListingActivateRequired");
  66. }
  67. return version_id;
  68. }
  69. void log_SLM_warning(const std::string& request, U32 status,
  70. const std::string& reason, const std::string& code,
  71. std::string message)
  72. {
  73. llwarns << "SLM API: Responder to: " << request << " - Status: " << status
  74. << " - Reason: " << reason << " - Code: " << code
  75. << " - Description: " << message << llendl;
  76. LLStringUtil::replaceString(message, std::string("["), std::string("- "));
  77. LLStringUtil::replaceString(message, std::string("\""), LLStringUtil::null);
  78. LLStringUtil::replaceString(message, std::string(","), "\n-");
  79. LLStringUtil::replaceString(message, std::string("]"), LLStringUtil::null);
  80. if (message.length() > 512)
  81. {
  82. // We do not show long messages in the alert (unlikely to be readable).
  83. // The full message string will be in the log though.
  84. message = message.substr(0, 504) + "\n.../...";
  85. }
  86. LLSD subs;
  87. subs["ERROR_REASON"] = reason;
  88. subs["ERROR_DESCRIPTION"] = message;
  89. gNotifications.add(status == 422 ? "MerchantUnprocessableEntity"
  90. : "MerchantTransactionFailed", subs);
  91. }
  92. ///////////////////////////////////////////////////////////////////////////////
  93. // New Marketplace Listings API tuples and data
  94. class LLMarketplaceInventoryObserver final : public LLInventoryObserver
  95. {
  96. protected:
  97. LOG_CLASS(LLMarketplaceInventoryObserver);
  98. public:
  99. LLMarketplaceInventoryObserver() {}
  100. ~LLMarketplaceInventoryObserver() override {}
  101. void changed(U32 mask) override;
  102. };
  103. void LLMarketplaceInventoryObserver::changed(U32 mask)
  104. {
  105. LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
  106. // When things are added to the marketplace, we might need to re-validate
  107. // and fix the containing listings
  108. if (mask & LLInventoryObserver::ADD)
  109. {
  110. const uuid_list_t& changed_items = gInventory.getChangedIDs();
  111. // First, count the number of items in this list...
  112. S32 count = 0;
  113. for (uuid_list_t::const_iterator it = changed_items.begin(),
  114. end = changed_items.end();
  115. it != end; ++it)
  116. {
  117. LLInventoryObject* obj = gInventory.getObject(*it);
  118. if (obj && obj->getType() != LLAssetType::AT_CATEGORY)
  119. {
  120. ++count;
  121. }
  122. }
  123. // Then, decrement the folders of that amount. Note that among all of
  124. // those, only one folder will be a listing folder (if at all), the
  125. // others will be ignored by the decrement method.
  126. for (uuid_list_t::const_iterator it = changed_items.begin(),
  127. end = changed_items.end();
  128. it != end; ++it)
  129. {
  130. LLInventoryObject* obj = gInventory.getObject(*it);
  131. if (obj && obj->getType() != LLAssetType::AT_CATEGORY)
  132. {
  133. marketdata->decrementValidationWaiting(obj->getUUID(), count);
  134. }
  135. }
  136. }
  137. // When things are changed in the inventory, this can trigger a host of
  138. // changes in the marketplace listings folder:
  139. // * stock counts changing: no copy items coming in and out will change
  140. // the stock count on folders;
  141. // * version and listing folders: moving those might invalidate the
  142. // marketplace data itself.
  143. // Since we cannot raise inventory change while the observer is called (the
  144. // list will be cleared once observers are called) we need to raise a flag
  145. // in the inventory to signal that things have been dirtied.
  146. if (mask & (LLInventoryObserver::INTERNAL | LLInventoryObserver::STRUCTURE))
  147. {
  148. const LLUUID& group_id = gAgent.getGroupID();
  149. const uuid_list_t& changed_items = gInventory.getChangedIDs();
  150. for (uuid_list_t::const_iterator it = changed_items.begin(),
  151. end = changed_items.end();
  152. it != end; ++it)
  153. {
  154. LLInventoryObject* objp = gInventory.getObject(*it);
  155. if (!objp) continue;
  156. if (objp->getType() == LLAssetType::AT_CATEGORY)
  157. {
  158. // If it is a folder known to the marketplace, let's check it
  159. // is in proper shape
  160. if (marketdata->isListed(*it) ||
  161. marketdata->isVersionFolder(*it))
  162. {
  163. marketdata->listForIdleValidation(*it);
  164. }
  165. }
  166. else
  167. {
  168. // If it is not a category, it is an item...
  169. LLViewerInventoryItem* itemp = gInventory.getItem(*it);
  170. // If it is a no copy item, we may need to update the label
  171. // count of marketplace listings
  172. if (itemp &&
  173. !itemp->getPermissions().allowCopyBy(gAgentID, group_id))
  174. {
  175. marketdata->setDirtyCount();
  176. }
  177. }
  178. }
  179. }
  180. }
  181. // Tuple == Item
  182. LLMarketplaceTuple::LLMarketplaceTuple()
  183. : mListingId(0),
  184. mIsActive(false),
  185. mCountOnHand(0)
  186. {
  187. }
  188. LLMarketplaceTuple::LLMarketplaceTuple(const LLUUID& folder_id)
  189. : mListingFolderId(folder_id),
  190. mListingId(0),
  191. mIsActive(false),
  192. mCountOnHand(0)
  193. {
  194. }
  195. LLMarketplaceTuple::LLMarketplaceTuple(const LLUUID& folder_id, S32 listing_id,
  196. const LLUUID& version_id, bool is_listed)
  197. : mListingFolderId(folder_id),
  198. mListingId(listing_id),
  199. mVersionFolderId(version_id),
  200. mIsActive(is_listed),
  201. mCountOnHand(0)
  202. {
  203. }
  204. LLMarketplaceTuple::LLMarketplaceTuple(const LLUUID& folder_id, S32 listing_id,
  205. const LLUUID& version_id, bool is_listed,
  206. const std::string& edit_url, S32 count)
  207. : mListingFolderId(folder_id),
  208. mListingId(listing_id),
  209. mVersionFolderId(version_id),
  210. mIsActive(is_listed),
  211. mEditURL(edit_url),
  212. mCountOnHand(count)
  213. {
  214. }
  215. // Data map
  216. LLMarketplaceData::LLMarketplaceData()
  217. : mMarketPlaceStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED),
  218. mMarketPlaceDataFetched(MarketplaceFetchCodes::MARKET_FETCH_NOT_DONE),
  219. mStatusUpdatedSignal(NULL),
  220. mDirtyCount(false),
  221. // NOTE: by using these instead of omitting the corresponding
  222. // xxxAndSuspend() parameters, we avoid seeing such classes constructed
  223. // and destroyed each time...
  224. mHttpOptions(new LLCore::HttpOptions),
  225. mHttpHeaders(new LLCore::HttpHeaders)
  226. {
  227. gIdleCallbacks.addFunction(idleCallback, this);
  228. mInventoryObserver = new LLMarketplaceInventoryObserver;
  229. gInventory.addObserver(mInventoryObserver);
  230. // NOTE: mHttpHeaders is used for Json requests only
  231. mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, "application/json");
  232. mHttpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, "application/json");
  233. }
  234. LLMarketplaceData::~LLMarketplaceData()
  235. {
  236. if (mStatusUpdatedSlot.connected())
  237. {
  238. mStatusUpdatedSlot.disconnect();
  239. }
  240. if (mStatusUpdatedSignal)
  241. {
  242. delete mStatusUpdatedSignal;
  243. }
  244. gIdleCallbacks.deleteFunction(idleCallback, this);
  245. if (mInventoryObserver)
  246. {
  247. gInventory.removeObserver(mInventoryObserver);
  248. mInventoryObserver = NULL;
  249. }
  250. mHttpOptions.reset();
  251. mHttpHeaders.reset();
  252. }
  253. void LLMarketplaceData::initializeSLM(const status_updated_signal_t::slot_type& cb)
  254. {
  255. if (gIsInSecondLifeBetaGrid)
  256. {
  257. // No Marketplace available in the SL beta grid... HB
  258. return;
  259. }
  260. if (!mStatusUpdatedSignal)
  261. {
  262. mStatusUpdatedSignal = new status_updated_signal_t();
  263. }
  264. if (mStatusUpdatedSlot.connected())
  265. {
  266. mStatusUpdatedSlot.disconnect();
  267. }
  268. mStatusUpdatedSlot = mStatusUpdatedSignal->connect(cb);
  269. if (mMarketPlaceStatus == MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED ||
  270. mMarketPlaceStatus == MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE)
  271. {
  272. // Initiate SLM connection and set responder
  273. std::string url = getSLMConnectURL("/merchant");
  274. if (url.empty())
  275. {
  276. // No capability... Init failed.
  277. LL_DEBUGS("Marketplace") << "Marketplace capability empty, cannot initialize"
  278. << LL_ENDL;
  279. setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE);
  280. }
  281. else
  282. {
  283. mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING;
  284. llinfos << "Initializing the Marketplace Listings" << llendl;
  285. LL_DEBUGS("Marketplace") << "Sending resquest: " << url << LL_ENDL;
  286. gCoros.launch("getMerchantStatus",
  287. boost::bind(&LLMarketplaceData::getMerchantStatusCoro,
  288. this, url));
  289. }
  290. }
  291. else
  292. {
  293. // If already initialized or initializing, just confirm the status so
  294. // that the callback gets called
  295. LL_DEBUGS("Marketplace") << "Marketplace already initialized or initializing"
  296. << LL_ENDL;
  297. setSLMStatus(mMarketPlaceStatus);
  298. }
  299. }
  300. void LLMarketplaceData::getMerchantStatusCoro(const std::string& url)
  301. {
  302. LLCore::HttpOptions::ptr_t options(new LLCore::HttpOptions);
  303. options->setFollowRedirects(true);
  304. LLCoreHttpUtil::HttpCoroutineAdapter adapter("getMerchantStatusCoro");
  305. LLSD result = adapter.getAndSuspend(url, options);
  306. if (!instanceExists()) return; // Viewer is being closed down !
  307. LLCore::HttpStatus status =
  308. LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
  309. S32 http_code = status.getType();
  310. if (status)
  311. {
  312. LL_DEBUGS("Marketplace") << "Status: " << http_code
  313. << " - User is a merchant" << LL_ENDL;
  314. setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_MERCHANT);
  315. }
  316. else if (http_code == HTTP_NOT_FOUND)
  317. {
  318. LL_DEBUGS("Marketplace") << "Status: " << http_code
  319. << " - User is not a merchant" << LL_ENDL;
  320. setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_MERCHANT);
  321. }
  322. else if (http_code == HTTP_SERVICE_UNAVAILABLE)
  323. {
  324. LL_DEBUGS("Marketplace") << "Status: " << http_code
  325. << " - Merchant is not migrated"
  326. << LL_ENDL;
  327. setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_MIGRATED_MERCHANT);
  328. }
  329. else if (http_code == HTTP_INTERNAL_ERROR)
  330. {
  331. // 499 includes timeout and ssl error - marketplace is down or having
  332. // issues, we do not show it in this request according to MAINT-5938
  333. llwarns << "Server internal error reported, reason: "
  334. << status.toString() << " - Code: "
  335. << result["error_code"].asString()
  336. << " - Description: " << result["error_description"].asString()
  337. << llendl;
  338. setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE);
  339. }
  340. else
  341. {
  342. log_SLM_warning("Get merchant", http_code, status.toString(),
  343. result["error_code"].asString(),
  344. result["error_description"].asString());
  345. setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE);
  346. }
  347. }
  348. // Get/Post/Put requests to the SLM Server using the SLM API
  349. void LLMarketplaceData::getSLMListings()
  350. {
  351. std::string url = getSLMConnectURL("/listings");
  352. if (url.empty()) return;
  353. // Send request
  354. const LLUUID& market_id = LLMarketplace::getMPL();
  355. if (market_id.notNull())
  356. {
  357. LL_DEBUGS("Marketplace") << "Sending resquest: " << url << LL_ENDL;
  358. setUpdating(market_id, true);
  359. gCoros.launch("getSLMListings",
  360. boost::bind(&LLMarketplaceData::getSLMListingsCoro, this,
  361. url, market_id));
  362. }
  363. }
  364. void LLMarketplaceData::getSLMListingsCoro(const std::string& url,
  365. LLUUID expected_folder_id)
  366. {
  367. LLCoreHttpUtil::HttpCoroutineAdapter adapter("getSLMListingsCoro");
  368. LLSD result = adapter.getJsonAndSuspend(url, mHttpOptions, mHttpHeaders);
  369. if (!instanceExists()) return; // Viewer is being closed down !
  370. setUpdating(expected_folder_id, false);
  371. LLCore::HttpStatus status =
  372. LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
  373. if (status)
  374. {
  375. result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS);
  376. LL_DEBUGS("Marketplace") << "Body: " << result << LL_ENDL;
  377. for (LLSD::array_iterator it = result["listings"].beginArray(),
  378. end = result["listings"].endArray();
  379. it != end; ++it)
  380. {
  381. const LLSD& listing = *it;
  382. S32 listing_id = listing["id"].asInteger();
  383. bool is_listed = listing["is_listed"].asBoolean();
  384. std::string edit_url = listing["edit_url"].asString();
  385. LLUUID folder_id = listing["inventory_info"]["listing_folder_id"].asUUID();
  386. LLUUID version_id = listing["inventory_info"]["version_folder_id"].asUUID();
  387. S32 count = listing["inventory_info"]["count_on_hand"].asInteger();
  388. if (folder_id.notNull())
  389. {
  390. addListing(folder_id, listing_id, version_id, is_listed,
  391. edit_url, count);
  392. }
  393. }
  394. setSLMDataFetched(MarketplaceFetchCodes::MARKET_FETCH_DONE);
  395. }
  396. else
  397. {
  398. log_SLM_warning("Get listings", status.getType(), status.toString(),
  399. "", result.asString());
  400. setSLMDataFetched(MarketplaceFetchCodes::MARKET_FETCH_FAILED);
  401. }
  402. // Update all folders under the root
  403. LLMarketplace::updateCategory(expected_folder_id, false);
  404. gInventory.notifyObservers();
  405. }
  406. void LLMarketplaceData::getSLMListing(S32 listing_id)
  407. {
  408. std::string url = getSLMConnectURL(llformat("/listing/%d", listing_id));
  409. if (url.empty()) return;
  410. // Send request
  411. LL_DEBUGS("Marketplace") << "Sending resquest: " << url << LL_ENDL;
  412. const LLUUID& folder_id = getListingFolder(listing_id);
  413. setUpdating(folder_id, true);
  414. gCoros.launch("getSLMListings",
  415. boost::bind(&LLMarketplaceData::getSLMListingCoro, this, url,
  416. folder_id));
  417. }
  418. void LLMarketplaceData::getSLMListingCoro(const std::string& url,
  419. LLUUID expected_folder_id)
  420. {
  421. LLCoreHttpUtil::HttpCoroutineAdapter adapter("getSLMListingCoro");
  422. LLSD result = adapter.getJsonAndSuspend(url, mHttpOptions, mHttpHeaders);
  423. if (!instanceExists()) return; // Viewer is being closed down !
  424. setUpdating(expected_folder_id, false);
  425. LLCore::HttpStatus status =
  426. LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
  427. if (status)
  428. {
  429. result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS);
  430. LL_DEBUGS("Marketplace") << "Body: " << result << LL_ENDL;
  431. for (LLSD::array_iterator it = result["listings"].beginArray(),
  432. end = result["listings"].endArray();
  433. it != end; ++it)
  434. {
  435. const LLSD& listing = *it;
  436. S32 listing_id = listing["id"].asInteger();
  437. bool is_listed = listing["is_listed"].asBoolean();
  438. std::string edit_url = listing["edit_url"].asString();
  439. LLUUID folder_id = listing["inventory_info"]["listing_folder_id"].asUUID();
  440. LLUUID version_id = listing["inventory_info"]["version_folder_id"].asUUID();
  441. S32 count = listing["inventory_info"]["count_on_hand"].asInteger();
  442. // Update that listing
  443. setListingID(folder_id, listing_id, false);
  444. setVersionFolderID(folder_id, version_id, false);
  445. setActivationState(folder_id, is_listed, false);
  446. setListingURL(folder_id, edit_url, false);
  447. setCountOnHand(folder_id, count, false);
  448. LLMarketplace::updateCategory(folder_id, false);
  449. gInventory.notifyObservers();
  450. }
  451. }
  452. else
  453. {
  454. S32 http_code = status.getType();
  455. if (http_code == HTTP_NOT_FOUND)
  456. {
  457. // That listing does not exist -> delete its record from the local
  458. // SLM data store
  459. deleteListing(expected_folder_id, false);
  460. }
  461. else
  462. {
  463. log_SLM_warning("Get listing", http_code, status.toString(), "",
  464. result.asString());
  465. }
  466. LLMarketplace::updateCategory(expected_folder_id, false);
  467. gInventory.notifyObservers();
  468. }
  469. }
  470. void LLMarketplaceData::createSLMListing(const LLUUID& folder_id,
  471. const LLUUID& version_id, S32 count)
  472. {
  473. std::string url = getSLMConnectURL("/listings");
  474. if (url.empty()) return;
  475. LLViewerInventoryCategory* category = gInventory.getCategory(folder_id);
  476. if (!category)
  477. {
  478. llwarns << "Cannot find category for folder Id: " << folder_id
  479. << llendl;
  480. return;
  481. }
  482. // Build the message
  483. LLSD inventory_info;
  484. inventory_info["listing_folder_id"] = folder_id;
  485. inventory_info["version_folder_id"] = version_id;
  486. inventory_info["count_on_hand"] = count;
  487. LLSD listing;
  488. listing["name"] = category->getName();
  489. listing["inventory_info"] = inventory_info;
  490. LLSD data;
  491. data["listing"] = listing;
  492. // Send request
  493. LL_DEBUGS("Marketplace") << "Sending resquest: " << url << " - Body:"
  494. << data << LL_ENDL;
  495. setUpdating(folder_id, true);
  496. gCoros.launch("createSLMListingCoro",
  497. boost::bind(&LLMarketplaceData::createSLMListingCoro, this,
  498. url, folder_id, data));
  499. }
  500. void LLMarketplaceData::createSLMListingCoro(const std::string& url,
  501. LLUUID expected_folder_id,
  502. const LLSD& data)
  503. {
  504. LLCoreHttpUtil::HttpCoroutineAdapter adapter("getSLMListingCoro");
  505. LLSD result = adapter.postJsonAndSuspend(url, data, mHttpOptions,
  506. mHttpHeaders);
  507. if (!instanceExists()) return; // Viewer is being closed down !
  508. setUpdating(expected_folder_id, false);
  509. LLCore::HttpStatus status =
  510. LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
  511. if (status)
  512. {
  513. result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS);
  514. LL_DEBUGS("Marketplace") << "Body: " << result << LL_ENDL;
  515. for (LLSD::array_iterator it = result["listings"].beginArray(),
  516. end = result["listings"].endArray();
  517. it != end; ++it)
  518. {
  519. const LLSD& listing = *it;
  520. S32 listing_id = listing["id"].asInteger();
  521. bool is_listed = listing["is_listed"].asBoolean();
  522. std::string edit_url = listing["edit_url"].asString();
  523. LLUUID folder_id = listing["inventory_info"]["listing_folder_id"].asUUID();
  524. LLUUID version_id = listing["inventory_info"]["version_folder_id"].asUUID();
  525. S32 count = listing["inventory_info"]["count_on_hand"].asInteger();
  526. addListing(folder_id, listing_id, version_id, is_listed, edit_url,
  527. count);
  528. LLMarketplace::updateCategory(folder_id, false);
  529. gInventory.notifyObservers();
  530. }
  531. }
  532. else
  533. {
  534. log_SLM_warning("Post listing", status.getType(), status.toString(),
  535. "", result.asString());
  536. LLMarketplace::updateCategory(expected_folder_id, false);
  537. gInventory.notifyObservers();
  538. }
  539. }
  540. void LLMarketplaceData::updateSLMListing(const LLUUID& folder_id,
  541. S32 listing_id,
  542. const LLUUID& version_id,
  543. bool is_listed, S32 count)
  544. {
  545. std::string url = getSLMConnectURL(llformat("/listing/%d", listing_id));
  546. if (url.empty()) return;
  547. // Auto unlist if the count is 0 (out of stock)
  548. if (is_listed && count == 0)
  549. {
  550. is_listed = false;
  551. gNotifications.add("AlertMerchantStockFolderEmpty");
  552. }
  553. // Note: we are assuming that sending unchanged info would not break
  554. // anything server side...
  555. // Build the message
  556. LLSD inventory_info;
  557. inventory_info["listing_folder_id"] = folder_id;
  558. inventory_info["version_folder_id"] = version_id;
  559. inventory_info["count_on_hand"] = count;
  560. LLSD listing;
  561. listing["id"] = listing_id;
  562. listing["is_listed"] = is_listed;
  563. listing["inventory_info"] = inventory_info;
  564. LLSD data;
  565. data["listing"] = listing;
  566. // Send request
  567. LL_DEBUGS("Marketplace") << "Sending resquest: " << url << " - Body:"
  568. << data << LL_ENDL;
  569. setUpdating(folder_id, true);
  570. gCoros.launch("updateSLMListingCoro",
  571. boost::bind(&LLMarketplaceData::updateSLMListingCoro, this,
  572. url, folder_id, version_id, is_listed, data));
  573. }
  574. // Notification callback for updateSLMListingCoro()
  575. bool edit_listing_callback(const LLSD& notification, const LLSD& response)
  576. {
  577. if (LLNotification::getSelectedOption(notification, response) == 0) // yes
  578. {
  579. std::string url = notification["payload"]["url"].asString();
  580. if (!url.empty())
  581. {
  582. LLWeb::loadURL(url);
  583. }
  584. }
  585. return false;
  586. }
  587. void LLMarketplaceData::updateSLMListingCoro(const std::string& url,
  588. LLUUID expected_folder_id,
  589. LLUUID expected_version_id,
  590. bool expected_listed,
  591. const LLSD& data)
  592. {
  593. LLCoreHttpUtil::HttpCoroutineAdapter adapter("getSLMListingCoro");
  594. LLSD result = adapter.putJsonAndSuspend(url, data, mHttpOptions,
  595. mHttpHeaders);
  596. if (!instanceExists()) return; // Viewer is being closed down !
  597. setUpdating(expected_folder_id, false);
  598. LLCore::HttpStatus status =
  599. LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
  600. if (status)
  601. {
  602. result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS);
  603. LL_DEBUGS("Marketplace") << "Body: " << result << LL_ENDL;
  604. for (LLSD::array_iterator it = result["listings"].beginArray(),
  605. end = result["listings"].endArray();
  606. it != end; ++it)
  607. {
  608. const LLSD& listing = *it;
  609. S32 listing_id = listing["id"].asInteger();
  610. bool is_listed = listing["is_listed"].asBoolean();
  611. std::string edit_url = listing["edit_url"].asString();
  612. LLUUID folder_id = listing["inventory_info"]["listing_folder_id"].asUUID();
  613. LLUUID version_id = listing["inventory_info"]["version_folder_id"].asUUID();
  614. S32 count = listing["inventory_info"]["count_on_hand"].asInteger();
  615. // Update that listing
  616. setListingID(folder_id, listing_id, false);
  617. setVersionFolderID(folder_id, version_id, false);
  618. setActivationState(folder_id, is_listed, false);
  619. setListingURL(folder_id, edit_url, false);
  620. setCountOnHand(folder_id, count, false);
  621. LLMarketplace::updateCategory(folder_id, false);
  622. gInventory.notifyObservers();
  623. // Show a notification alert if what we got is not what we expected
  624. // (this actually does not result in an error status from the SLM
  625. // API protocol)
  626. if (is_listed != expected_listed ||
  627. version_id != expected_version_id)
  628. {
  629. LLSD subs;
  630. LLViewerInventoryCategory* cat;
  631. cat = gInventory.getCategory(folder_id);
  632. if (cat)
  633. {
  634. subs["NAME"] = cat->getName();
  635. }
  636. else
  637. {
  638. subs["NAME"] = folder_id.asString();
  639. }
  640. LLSD payload;
  641. payload["url"] = edit_url;
  642. gNotifications.add("AlertMerchantListingNotUpdated", subs,
  643. payload, edit_listing_callback);
  644. }
  645. }
  646. }
  647. else
  648. {
  649. log_SLM_warning("Put listing", status.getType(), status.toString(), "",
  650. result.asString());
  651. LLMarketplace::updateCategory(expected_folder_id, false);
  652. gInventory.notifyObservers();
  653. }
  654. }
  655. void LLMarketplaceData::associateSLMListing(const LLUUID& folder_id,
  656. S32 listing_id,
  657. const LLUUID& version_id,
  658. const LLUUID& source_folder_id)
  659. {
  660. std::string url = getSLMConnectURL(llformat("/associate_inventory/%d",
  661. listing_id));
  662. if (url.empty()) return;
  663. // Note: we are assuming that sending unchanged info woould not break
  664. // anything server side...
  665. // Build the message
  666. LLSD inventory_info;
  667. inventory_info["listing_folder_id"] = folder_id;
  668. inventory_info["version_folder_id"] = version_id;
  669. LLSD listing;
  670. listing["id"] = listing_id;
  671. listing["inventory_info"] = inventory_info;
  672. LLSD data;
  673. data["listing"] = listing;
  674. // Send request
  675. LL_DEBUGS("Marketplace") << "Sending resquest: " << url << " - Body:"
  676. << data << LL_ENDL;
  677. // Send request
  678. setUpdating(folder_id, true);
  679. setUpdating(source_folder_id, true);
  680. gCoros.launch("updateSLMListingCoro",
  681. boost::bind(&LLMarketplaceData::associateSLMListingCoro,
  682. this, url, folder_id, source_folder_id, data));
  683. }
  684. void LLMarketplaceData::associateSLMListingCoro(const std::string& url,
  685. LLUUID expected_folder_id,
  686. LLUUID source_folder_id,
  687. const LLSD& data)
  688. {
  689. LLCoreHttpUtil::HttpCoroutineAdapter adapter("associateSLMListingCoro");
  690. LLSD result = adapter.putJsonAndSuspend(url, data, mHttpOptions,
  691. mHttpHeaders);
  692. if (!instanceExists()) return; // Viewer is being closed down !
  693. setUpdating(expected_folder_id, false);
  694. setUpdating(source_folder_id, false);
  695. LLCore::HttpStatus status =
  696. LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
  697. if (status)
  698. {
  699. result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS);
  700. LL_DEBUGS("Marketplace") << "Body: " << result << LL_ENDL;
  701. for (LLSD::array_iterator it = result["listings"].beginArray(),
  702. end = result["listings"].endArray();
  703. it != end; ++it)
  704. {
  705. const LLSD& listing = *it;
  706. S32 listing_id = listing["id"].asInteger();
  707. bool is_listed = listing["is_listed"].asBoolean();
  708. std::string edit_url = listing["edit_url"].asString();
  709. LLUUID folder_id = listing["inventory_info"]["listing_folder_id"].asUUID();
  710. LLUUID version_id = listing["inventory_info"]["version_folder_id"].asUUID();
  711. S32 count = listing["inventory_info"]["count_on_hand"].asInteger();
  712. // Check that the listing ID is not already associated to some
  713. // other record
  714. const LLUUID& old_listing = getListingFolder(listing_id);
  715. if (old_listing.notNull())
  716. {
  717. // If it is already used, unlist the old record (we cannot have
  718. // 2 listings with the same listing ID)
  719. deleteListing(old_listing);
  720. }
  721. // Add the new association
  722. addListing(folder_id, listing_id, version_id, is_listed, edit_url,
  723. count);
  724. LLMarketplace::updateCategory(folder_id, false);
  725. gInventory.notifyObservers();
  726. // The stock count needs to be updated with the new local count now
  727. updateCountOnHand(folder_id, 1);
  728. }
  729. }
  730. else
  731. {
  732. log_SLM_warning("Put associate_inventory", status.getType(),
  733. status.toString(), "", result.asString());
  734. LLMarketplace::updateCategory(expected_folder_id, false);
  735. gInventory.notifyObservers();
  736. }
  737. // Always update the source folder so its widget updates
  738. LLMarketplace::updateCategory(source_folder_id, false);
  739. gInventory.notifyObservers();
  740. }
  741. void LLMarketplaceData::deleteSLMListing(S32 listing_id)
  742. {
  743. std::string url = getSLMConnectURL(llformat("/listing/%d", listing_id));
  744. if (url.empty()) return;
  745. LLSD headers = LLSD::emptyMap();
  746. headers[HTTP_OUT_HEADER_ACCEPT] = "application/json";
  747. headers[HTTP_OUT_HEADER_CONTENT_TYPE] = "application/json";
  748. // Send request
  749. const LLUUID& folder_id = getListingFolder(listing_id);
  750. setUpdating(folder_id, true);
  751. LL_DEBUGS("Marketplace") << "Sending resquest: " << url << LL_ENDL;
  752. gCoros.launch("deleteSLMListingCoro",
  753. boost::bind(&LLMarketplaceData::deleteSLMListingCoro, this,
  754. url, folder_id));
  755. }
  756. void LLMarketplaceData::deleteSLMListingCoro(const std::string& url,
  757. LLUUID expected_folder_id)
  758. {
  759. LLCoreHttpUtil::HttpCoroutineAdapter adapter("deleteSLMListingCoro");
  760. LLSD result = adapter.deleteJsonAndSuspend(url, mHttpOptions,
  761. mHttpHeaders);
  762. if (!instanceExists()) return; // Viewer is being closed down !
  763. setUpdating(expected_folder_id, false);
  764. LLCore::HttpStatus status =
  765. LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
  766. if (status)
  767. {
  768. result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS);
  769. LL_DEBUGS("Marketplace") << "Body: " << result << LL_ENDL;
  770. for (LLSD::array_iterator it = result["listings"].beginArray(),
  771. end = result["listings"].endArray();
  772. it != end; ++it)
  773. {
  774. const LLSD& listing = *it;
  775. S32 listing_id = listing["id"].asInteger();
  776. const LLUUID& folder_id = getListingFolder(listing_id);
  777. deleteListing(folder_id);
  778. }
  779. }
  780. else
  781. {
  782. log_SLM_warning("Delete listing", status.getType(), status.toString(),
  783. "", result.asString());
  784. LLMarketplace::updateCategory(expected_folder_id, false);
  785. gInventory.notifyObservers();
  786. }
  787. }
  788. std::string LLMarketplaceData::getSLMConnectURL(const std::string& route)
  789. {
  790. std::string url = gAgent.getRegionCapability("DirectDelivery");
  791. if (!url.empty())
  792. {
  793. url += route;
  794. }
  795. return url;
  796. }
  797. void LLMarketplaceData::setSLMStatus(S32 status)
  798. {
  799. if (mMarketPlaceStatus != status)
  800. {
  801. mMarketPlaceStatus = status;
  802. if (mStatusUpdatedSignal)
  803. {
  804. (*mStatusUpdatedSignal)();
  805. }
  806. }
  807. }
  808. // Creation / Deletion / Update
  809. // Methods publicly called
  810. bool LLMarketplaceData::createListing(const LLUUID& folder_id)
  811. {
  812. if (isListed(folder_id))
  813. {
  814. // Listing already exists -> exit with error
  815. return false;
  816. }
  817. const LLUUID& version_id = getVersionFolderIfUnique(folder_id);
  818. S32 count = version_id.isNull() ? COMPUTE_STOCK_INFINITE
  819. : LLMarketplace::computeStockCount(version_id,
  820. true);
  821. // Validate the count on hand
  822. if (count == COMPUTE_STOCK_NOT_EVALUATED)
  823. {
  824. // If the count on hand cannot be evaluated, we will consider it empty
  825. // (out of stock) at creation time. It will get reevaluated and updated
  826. // once the items are fetched.
  827. count = 0;
  828. }
  829. // Post the listing creation request to SLM
  830. createSLMListing(folder_id, version_id, count);
  831. return true;
  832. }
  833. bool LLMarketplaceData::clearListing(const LLUUID& folder_id, S32 depth)
  834. {
  835. if (folder_id.isNull())
  836. {
  837. // Folder does not exist -> exit with error
  838. return false;
  839. }
  840. // Folder id can be the root of the listing or not so we need to retrieve
  841. // the root first
  842. if (depth < 0)
  843. {
  844. depth = LLMarketplace::depthNesting(folder_id);
  845. }
  846. const LLUUID& listing_uuid =
  847. isListed(folder_id) ? folder_id
  848. : LLMarketplace::nestedParentId(folder_id, depth);
  849. S32 listing_id = getListingID(listing_uuid);
  850. if (listing_id == 0)
  851. {
  852. // Listing does not exist -> exit with error
  853. return false;
  854. }
  855. // Update the SLM Server so that this listing is deleted (actually,
  856. // archived...)
  857. deleteSLMListing(listing_id);
  858. return true;
  859. }
  860. bool LLMarketplaceData::getListing(const LLUUID& folder_id, S32 depth)
  861. {
  862. if (folder_id.isNull())
  863. {
  864. // Folder does not exist -> exit with error
  865. return false;
  866. }
  867. // Folder id can be the root of the listing or not so we need to retrieve
  868. // the root first
  869. if (depth < 0)
  870. {
  871. depth = LLMarketplace::depthNesting(folder_id);
  872. }
  873. const LLUUID& listing_uuid =
  874. isListed(folder_id) ? folder_id
  875. : LLMarketplace::nestedParentId(folder_id, depth);
  876. S32 listing_id = getListingID(listing_uuid);
  877. if (listing_id == 0)
  878. {
  879. // Listing does not exist -> exit with error
  880. return false;
  881. }
  882. // Get listing data from SLM
  883. getSLMListing(listing_id);
  884. return true;
  885. }
  886. bool LLMarketplaceData::getListing(S32 listing_id)
  887. {
  888. if (listing_id == 0)
  889. {
  890. return false;
  891. }
  892. // Get listing data from SLM
  893. getSLMListing(listing_id);
  894. return true;
  895. }
  896. bool LLMarketplaceData::activateListing(const LLUUID& folder_id, bool activate,
  897. S32 depth)
  898. {
  899. // Folder id can be the root of the listing or not so we need to retrieve
  900. // the root first
  901. if (depth < 0)
  902. {
  903. depth = LLMarketplace::depthNesting(folder_id);
  904. }
  905. const LLUUID& listing_uuid = LLMarketplace::nestedParentId(folder_id,
  906. depth);
  907. S32 listing_id = getListingID(listing_uuid);
  908. if (listing_id == 0)
  909. {
  910. // Listing does not exist -> exit with error
  911. return false;
  912. }
  913. if (getActivationState(listing_uuid) == activate)
  914. {
  915. // If activation state is unchanged, no point spamming SLM with an
  916. // update
  917. return true;
  918. }
  919. const LLUUID& version_uuid = getVersionFolder(listing_uuid);
  920. // Also update the count on hand
  921. S32 count = LLMarketplace::computeStockCount(folder_id);
  922. if (count == COMPUTE_STOCK_NOT_EVALUATED)
  923. {
  924. // If the count on hand cannot be evaluated locally, we should not
  925. // change that SLM value. We are assuming that this issue is local and
  926. // should not modify server side values.
  927. count = getCountOnHand(listing_uuid);
  928. }
  929. // Post the listing update request to SLM
  930. updateSLMListing(listing_uuid, listing_id, version_uuid, activate, count);
  931. return true;
  932. }
  933. bool LLMarketplaceData::setVersionFolder(const LLUUID& folder_id,
  934. const LLUUID& version_id, S32 depth)
  935. {
  936. // Folder id can be the root of the listing or not so we need to retrieve
  937. // the root first
  938. if (depth < 0)
  939. {
  940. depth = LLMarketplace::depthNesting(folder_id);
  941. }
  942. const LLUUID& listing_uuid = LLMarketplace::nestedParentId(folder_id,
  943. depth);
  944. S32 listing_id = getListingID(listing_uuid);
  945. if (listing_id == 0)
  946. {
  947. // Listing does not exist -> exit with error
  948. return false;
  949. }
  950. if (getVersionFolder(listing_uuid) == version_id)
  951. {
  952. // If version folder is unchanged, no point spamming SLM with an update
  953. return true;
  954. }
  955. // Note: if the version_id is cleared, we need to unlist the listing,
  956. // otherwise, state unchanged
  957. bool is_listed = version_id.isNull() ? false
  958. : getActivationState(listing_uuid);
  959. // Also update the count on hand
  960. S32 count = LLMarketplace::computeStockCount(version_id);
  961. if (count == COMPUTE_STOCK_NOT_EVALUATED)
  962. {
  963. // If the count on hand cannot be evaluated, we will consider it empty
  964. // (out of stock) at creation time. It will get reevaluated and updated
  965. // once the items are fetched.
  966. count = 0;
  967. }
  968. // Post the listing update request to SLM
  969. updateSLMListing(listing_uuid, listing_id, version_id, is_listed, count);
  970. return true;
  971. }
  972. bool LLMarketplaceData::updateCountOnHand(const LLUUID& folder_id, S32 depth)
  973. {
  974. // Folder id can be the root of the listing or not so we need to retrieve
  975. // the root first
  976. if (depth < 0)
  977. {
  978. depth = LLMarketplace::depthNesting(folder_id);
  979. }
  980. const LLUUID& listing_uuid = LLMarketplace::nestedParentId(folder_id,
  981. depth);
  982. S32 listing_id = getListingID(listing_uuid);
  983. if (listing_id == 0)
  984. {
  985. // Listing does not exist -> exit with error
  986. return false;
  987. }
  988. // Compute the new count on hand
  989. S32 count = LLMarketplace::computeStockCount(folder_id);
  990. if (count == getCountOnHand(listing_uuid))
  991. {
  992. // If count on hand is unchanged, no point spamming SLM with an update
  993. return true;
  994. }
  995. if (count == COMPUTE_STOCK_NOT_EVALUATED)
  996. {
  997. // If local count on hand is not known at that point, do *not* force an
  998. // update to SLM
  999. return false;
  1000. }
  1001. // Get the unchanged values
  1002. bool is_listed = getActivationState(listing_uuid);
  1003. const LLUUID& version_uuid = getVersionFolder(listing_uuid);
  1004. // Post the listing update request to SLM
  1005. updateSLMListing(listing_uuid, listing_id, version_uuid, is_listed, count);
  1006. // Force the local value as it prevents spamming (count update may occur in
  1007. // burst when restocking). Note that if SLM has a good reason to return a
  1008. // different value, it'll be updated by the responder
  1009. setCountOnHand(listing_uuid, count, false);
  1010. return true;
  1011. }
  1012. bool LLMarketplaceData::associateListing(const LLUUID& folder_id,
  1013. const LLUUID& source_folder_id,
  1014. S32 listing_id)
  1015. {
  1016. if (isListed(folder_id))
  1017. {
  1018. // Listing already exists -> exit with error
  1019. return false;
  1020. }
  1021. // Get the version folder: if there is only one subfolder, we will set it
  1022. // as a version folder immediately
  1023. const LLUUID& version_id = getVersionFolderIfUnique(folder_id);
  1024. // Post the listing update request to SLM
  1025. associateSLMListing(folder_id, listing_id, version_id, source_folder_id);
  1026. return true;
  1027. }
  1028. // Methods privately called or called by SLM responders to perform changes
  1029. bool LLMarketplaceData::addListing(const LLUUID& folder_id, S32 listing_id,
  1030. const LLUUID& version_id, bool is_listed,
  1031. const std::string& edit_url, S32 count)
  1032. {
  1033. mMarketplaceItems[folder_id] = LLMarketplaceTuple(folder_id, listing_id,
  1034. version_id, is_listed,
  1035. edit_url, count);
  1036. if (version_id.notNull())
  1037. {
  1038. mVersionFolders[version_id] = folder_id;
  1039. }
  1040. return true;
  1041. }
  1042. bool LLMarketplaceData::deleteListing(const LLUUID& folder_id, bool update)
  1043. {
  1044. if (mMarketplaceItems.erase(folder_id) != 1)
  1045. {
  1046. return false;
  1047. }
  1048. const LLUUID& vf_uuid = getVersionFolder(folder_id);
  1049. if (vf_uuid.notNull())
  1050. {
  1051. mVersionFolders.erase(vf_uuid);
  1052. }
  1053. if (update)
  1054. {
  1055. LLMarketplace::updateCategory(folder_id, false);
  1056. gInventory.notifyObservers();
  1057. }
  1058. return true;
  1059. }
  1060. bool LLMarketplaceData::deleteListing(S32 listing_id, bool update)
  1061. {
  1062. if (listing_id == 0)
  1063. {
  1064. return false;
  1065. }
  1066. LLUUID folder_id = getListingFolder(listing_id);
  1067. return deleteListing(folder_id, update);
  1068. }
  1069. // Accessors
  1070. bool LLMarketplaceData::getActivationState(const LLUUID& folder_id)
  1071. {
  1072. // Listing folder case
  1073. marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
  1074. if (it != mMarketplaceItems.end())
  1075. {
  1076. return (it->second).mIsActive;
  1077. }
  1078. // Version folder case
  1079. version_folders_list_t::iterator vit = mVersionFolders.find(folder_id);
  1080. if (vit != mVersionFolders.end())
  1081. {
  1082. it = mMarketplaceItems.find(vit->second);
  1083. if (it != mMarketplaceItems.end())
  1084. {
  1085. return (it->second).mIsActive;
  1086. }
  1087. }
  1088. return false;
  1089. }
  1090. S32 LLMarketplaceData::getListingID(const LLUUID& folder_id)
  1091. {
  1092. marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
  1093. return it == mMarketplaceItems.end() ? 0 : (it->second).mListingId;
  1094. }
  1095. S32 LLMarketplaceData::getCountOnHand(const LLUUID& folder_id)
  1096. {
  1097. marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
  1098. return it == mMarketplaceItems.end() ? -1 : (it->second).mCountOnHand;
  1099. }
  1100. LLUUID LLMarketplaceData::getVersionFolder(const LLUUID& folder_id)
  1101. {
  1102. marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
  1103. return it == mMarketplaceItems.end() ? LLUUID::null
  1104. : (it->second).mVersionFolderId;
  1105. }
  1106. // Reverse lookup : find the listing folder id from the listing id
  1107. LLUUID LLMarketplaceData::getListingFolder(S32 listing_id)
  1108. {
  1109. for (marketplace_items_list_t::iterator it = mMarketplaceItems.begin(),
  1110. end = mMarketplaceItems.end();
  1111. it != end; ++it)
  1112. {
  1113. if ((it->second).mListingId == listing_id)
  1114. {
  1115. return (it->second).mListingFolderId;
  1116. }
  1117. }
  1118. return LLUUID::null;
  1119. }
  1120. std::string LLMarketplaceData::getListingURL(const LLUUID& folder_id,
  1121. S32 depth)
  1122. {
  1123. if (depth < 0)
  1124. {
  1125. depth = LLMarketplace::depthNesting(folder_id);
  1126. }
  1127. const LLUUID& listing_uuid = LLMarketplace::nestedParentId(folder_id,
  1128. depth);
  1129. marketplace_items_list_t::iterator it =
  1130. mMarketplaceItems.find(listing_uuid);
  1131. return it == mMarketplaceItems.end() ? "" : (it->second).mEditURL;
  1132. }
  1133. bool LLMarketplaceData::isListed(const LLUUID& folder_id)
  1134. {
  1135. return mMarketplaceItems.count(folder_id) != 0;
  1136. }
  1137. bool LLMarketplaceData::isListedAndActive(const LLUUID& folder_id)
  1138. {
  1139. return isListed(folder_id) && getActivationState(folder_id);
  1140. }
  1141. bool LLMarketplaceData::isVersionFolder(const LLUUID& folder_id)
  1142. {
  1143. return mVersionFolders.count(folder_id) != 0;
  1144. }
  1145. bool LLMarketplaceData::isInActiveFolder(const LLUUID& obj_id, S32 depth)
  1146. {
  1147. if (depth < 0)
  1148. {
  1149. depth = LLMarketplace::depthNesting(obj_id);
  1150. }
  1151. const LLUUID& listing_uuid = LLMarketplace::nestedParentId(obj_id, depth);
  1152. bool active = getActivationState(listing_uuid);
  1153. if (!active)
  1154. {
  1155. return false;
  1156. }
  1157. const LLUUID& version_uuid = getVersionFolder(listing_uuid);
  1158. return obj_id == version_uuid ||
  1159. gInventory.isObjectDescendentOf(obj_id, version_uuid);
  1160. }
  1161. LLUUID LLMarketplaceData::getActiveFolder(const LLUUID& obj_id, S32 depth)
  1162. {
  1163. if (depth < 0)
  1164. {
  1165. depth = LLMarketplace::depthNesting(obj_id);
  1166. }
  1167. const LLUUID& listing_uuid = LLMarketplace::nestedParentId(obj_id, depth);
  1168. return getActivationState(listing_uuid) ? getVersionFolder(listing_uuid)
  1169. : LLUUID::null;
  1170. }
  1171. bool LLMarketplaceData::isUpdating(const LLUUID& folder_id, S32 depth)
  1172. {
  1173. if (depth < 0)
  1174. {
  1175. depth = LLMarketplace::depthNesting(folder_id);
  1176. }
  1177. if (depth < 0)
  1178. {
  1179. // Not a Marketplace folder
  1180. return false;
  1181. }
  1182. if (depth == 0 &&
  1183. getSLMStatus() <= MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING)
  1184. {
  1185. // If the Marketplace is not yet initialized, then yes, we are
  1186. // definitely updating...
  1187. return true;
  1188. }
  1189. const LLUUID& market_id = LLMarketplace::getMPL();
  1190. if (mPendingUpdateSet.find(market_id) != mPendingUpdateSet.end())
  1191. {
  1192. // If we are waiting for data for the marketplace listings root, we are
  1193. // in the updating process for all
  1194. return true;
  1195. }
  1196. #if 0 // Stock folders too...
  1197. if (depth > 2)
  1198. {
  1199. // Only listing and version folders though are concerned by that status
  1200. return false;
  1201. }
  1202. #endif
  1203. // Check if the listing folder is waiting or data
  1204. const LLUUID& listing_uuid = LLMarketplace::nestedParentId(folder_id,
  1205. depth);
  1206. return mPendingUpdateSet.find(listing_uuid) != mPendingUpdateSet.end();
  1207. }
  1208. void LLMarketplaceData::setUpdating(const LLUUID& folder_id, bool is_updating)
  1209. {
  1210. uuid_list_t::iterator it = mPendingUpdateSet.find(folder_id);
  1211. if (it != mPendingUpdateSet.end())
  1212. {
  1213. mPendingUpdateSet.erase(it);
  1214. }
  1215. if (is_updating)
  1216. {
  1217. mPendingUpdateSet.emplace(folder_id);
  1218. }
  1219. }
  1220. void LLMarketplaceData::listForIdleValidation(const LLUUID& folder_id)
  1221. {
  1222. mPendingValidations.emplace(folder_id);
  1223. }
  1224. void LLMarketplaceData::setValidationWaiting(const LLUUID& folder_id,
  1225. S32 count)
  1226. {
  1227. mValidationWaitingList[folder_id] = count;
  1228. }
  1229. void LLMarketplaceData::decrementValidationWaiting(const LLUUID& folder_id,
  1230. S32 count)
  1231. {
  1232. waiting_list_t::iterator it = mValidationWaitingList.find(folder_id);
  1233. if (it != mValidationWaitingList.end())
  1234. {
  1235. it->second -= count;
  1236. if (it->second <= 0)
  1237. {
  1238. mValidationWaitingList.hmap_erase(it);
  1239. mPendingValidations.emplace(folder_id);
  1240. }
  1241. }
  1242. }
  1243. //static
  1244. void LLMarketplaceData::idleCallback(void* userdata)
  1245. {
  1246. LLMarketplaceData* self = (LLMarketplaceData*)userdata;
  1247. if (!self || self->mPendingValidations.empty()) return;
  1248. for (uuid_list_t::const_iterator it = self->mPendingValidations.begin(),
  1249. end = self->mPendingValidations.end();
  1250. it != end; ++it)
  1251. {
  1252. LLViewerInventoryCategory* cat = gInventory.getCategory(*it);
  1253. if (cat)
  1254. {
  1255. LLMarketplace::validateListings(cat);
  1256. }
  1257. }
  1258. self->mPendingValidations.clear();
  1259. }
  1260. // Private Modifiers
  1261. bool LLMarketplaceData::setListingID(const LLUUID& folder_id, S32 listing_id,
  1262. bool update)
  1263. {
  1264. marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
  1265. if (it == mMarketplaceItems.end())
  1266. {
  1267. return false;
  1268. }
  1269. it->second.mListingId = listing_id;
  1270. if (update)
  1271. {
  1272. LLMarketplace::updateCategory(folder_id, false);
  1273. gInventory.notifyObservers();
  1274. }
  1275. return true;
  1276. }
  1277. bool LLMarketplaceData::setCountOnHand(const LLUUID& folder_id, S32 count,
  1278. bool update)
  1279. {
  1280. marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
  1281. if (it == mMarketplaceItems.end())
  1282. {
  1283. return false;
  1284. }
  1285. it->second.mCountOnHand = count;
  1286. return true;
  1287. }
  1288. bool LLMarketplaceData::setVersionFolderID(const LLUUID& folder_id,
  1289. const LLUUID& version_id,
  1290. bool update)
  1291. {
  1292. marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
  1293. if (it == mMarketplaceItems.end())
  1294. {
  1295. return false;
  1296. }
  1297. // Note: do not use LLUUID& here since we need an actual copy of the old
  1298. // UUID, not a pointer on (it->second).mVersionFolderId.
  1299. LLUUID old_version_id = (it->second).mVersionFolderId;
  1300. if (version_id == old_version_id)
  1301. {
  1302. return false;
  1303. }
  1304. it->second.mVersionFolderId = version_id;
  1305. bool update_old = false;
  1306. if (old_version_id.notNull())
  1307. {
  1308. mVersionFolders.erase(old_version_id);
  1309. update_old = update;
  1310. }
  1311. bool update_new = false;
  1312. if (version_id.notNull())
  1313. {
  1314. mVersionFolders[version_id] = folder_id;
  1315. update_new = update;
  1316. }
  1317. // Now that the version folder has been changed, we can update the folders
  1318. // hierarchy if needed.
  1319. if (update_old)
  1320. {
  1321. LLMarketplace::updateCategory(old_version_id, false);
  1322. }
  1323. if (update_new)
  1324. {
  1325. LLMarketplace::updateCategory(version_id, false);
  1326. }
  1327. if (update_old || update_new)
  1328. {
  1329. gInventory.notifyObservers();
  1330. }
  1331. return true;
  1332. }
  1333. bool LLMarketplaceData::setActivationState(const LLUUID& folder_id,
  1334. bool activate, bool update)
  1335. {
  1336. marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
  1337. if (it == mMarketplaceItems.end())
  1338. {
  1339. return false;
  1340. }
  1341. it->second.mIsActive = activate;
  1342. if (update)
  1343. {
  1344. LLMarketplace::updateCategory((it->second).mListingFolderId, false);
  1345. gInventory.notifyObservers();
  1346. }
  1347. return true;
  1348. }
  1349. bool LLMarketplaceData::setListingURL(const LLUUID& folder_id,
  1350. const std::string& edit_url, bool update)
  1351. {
  1352. marketplace_items_list_t::iterator it = mMarketplaceItems.find(folder_id);
  1353. if (it == mMarketplaceItems.end())
  1354. {
  1355. return false;
  1356. }
  1357. it->second.mEditURL = edit_url;
  1358. return true;
  1359. }
  1360. ///////////////////////////////////////////////////////////////////////////////
  1361. // New Marketplace Listings API related functions
  1362. // Local helper
  1363. bool can_move_to_marketplace(LLViewerInventoryItem* inv_item,
  1364. std::string& tooltip_msg,
  1365. bool resolve_links = false)
  1366. {
  1367. if (!inv_item)
  1368. {
  1369. tooltip_msg = "NULL inventory item";
  1370. return false;
  1371. }
  1372. LLViewerInventoryItem* vitem = inv_item;
  1373. LLViewerInventoryItem* linked_item = vitem->getLinkedItem();
  1374. LLViewerInventoryCategory* linked_category = vitem->getLinkedCategory();
  1375. if (resolve_links)
  1376. {
  1377. if (linked_item)
  1378. {
  1379. vitem = linked_item;
  1380. linked_item = NULL; // Link resolved, so allow to pass next test
  1381. }
  1382. else if (linked_category)
  1383. {
  1384. vitem = (LLViewerInventoryItem*)linked_category;
  1385. // Link resolved, so allow to pass next test
  1386. linked_category = NULL;
  1387. }
  1388. }
  1389. // Linked items and folders cannot be put for sale
  1390. if (linked_category || linked_item)
  1391. {
  1392. tooltip_msg = LLTrans::getString("TooltipOutboxLinked");
  1393. return false;
  1394. }
  1395. const LLUUID& item_uuid = vitem->getUUID();
  1396. // Check library status: library items cannot be put on the marketplace
  1397. if (!gInventory.isObjectDescendentOf(item_uuid,
  1398. gInventory.getRootFolderID()))
  1399. {
  1400. tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory");
  1401. return false;
  1402. }
  1403. // Check type
  1404. S32 type = vitem->getType();
  1405. // A category is always considered as passing...
  1406. if (type == LLAssetType::AT_CATEGORY)
  1407. {
  1408. return true;
  1409. }
  1410. // For the moment, calling cards cannot be put on the marketplace
  1411. if (type == LLAssetType::AT_CALLINGCARD)
  1412. {
  1413. tooltip_msg = LLTrans::getString("TooltipOutboxCallingCard");
  1414. return false;
  1415. }
  1416. // Check that the agent has transfer permission on the item: this is
  1417. // required as a resident cannot put on sale items they cannot transfer.
  1418. // Proceed with move if we have permission.
  1419. if (!vitem->getPermissions().allowTransferBy(gAgentID))
  1420. {
  1421. tooltip_msg = LLTrans::getString("TooltipOutboxNoTransfer");
  1422. return false;
  1423. }
  1424. // Check worn/not worn status: worn items cannot be put on the marketplace
  1425. if (get_is_item_worn(item_uuid))
  1426. {
  1427. tooltip_msg = LLTrans::getString("TooltipOutboxWorn");
  1428. return false;
  1429. }
  1430. return true;
  1431. }
  1432. // Local helper
  1433. // Counts only the copyable items, i.e. skip the stock items (which are no
  1434. // copy)
  1435. S32 count_copyable_items(const LLInventoryModel::item_array_t& items)
  1436. {
  1437. S32 count = 0;
  1438. const LLUUID& group_id = gAgent.getGroupID();
  1439. for (LLInventoryModel::item_array_t::const_iterator it = items.begin(),
  1440. end = items.end();
  1441. it != end; ++it)
  1442. {
  1443. LLViewerInventoryItem* itemp = *it;
  1444. if (itemp && itemp->getPermissions().allowCopyBy(gAgentID, group_id))
  1445. {
  1446. ++count;
  1447. }
  1448. }
  1449. return count;
  1450. }
  1451. // Local helper
  1452. // Count only the non-copyable items, i.e. the stock items, skip the others
  1453. S32 count_stock_items(const LLInventoryModel::item_array_t& items)
  1454. {
  1455. S32 count = 0;
  1456. const LLUUID& group_id = gAgent.getGroupID();
  1457. for (LLInventoryModel::item_array_t::const_iterator it = items.begin(),
  1458. end = items.end();
  1459. it != end; ++it)
  1460. {
  1461. LLViewerInventoryItem* itemp = *it;
  1462. if (itemp && !itemp->getPermissions().allowCopyBy(gAgentID, group_id))
  1463. {
  1464. ++count;
  1465. }
  1466. }
  1467. return count;
  1468. }
  1469. // Local helper
  1470. // Counts the number of stock folders
  1471. S32 count_stock_folders(const LLInventoryModel::cat_array_t& cats)
  1472. {
  1473. S32 count = 0;
  1474. for (LLInventoryModel::cat_array_t::const_iterator it = cats.begin(),
  1475. end = cats.end();
  1476. it != end; ++it)
  1477. {
  1478. LLViewerInventoryCategory* cat = *it;
  1479. if (cat &&
  1480. cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)
  1481. {
  1482. ++count;
  1483. }
  1484. }
  1485. return count;
  1486. }
  1487. //static
  1488. bool LLMarketplace::contains(const LLUUID& item_id)
  1489. {
  1490. return sMarketplaceListingId.notNull() &&
  1491. gInventory.isObjectDescendentOf(item_id, sMarketplaceListingId);
  1492. }
  1493. // Get the marketplace listings root, exit with -1 (i.e. not under the
  1494. // marketplace listings root) if none
  1495. //static
  1496. S32 LLMarketplace::depthNesting(const LLUUID& item_id)
  1497. {
  1498. if (sMarketplaceListingId.isNull() ||
  1499. !gInventory.isObjectDescendentOf(item_id, sMarketplaceListingId))
  1500. {
  1501. return -1;
  1502. }
  1503. // Iterate through the parents till we hit the marketplace listings root
  1504. // Note that the marketplace listings root itself will return 0
  1505. S32 depth = 0;
  1506. LLInventoryObject* cur_object = gInventory.getObject(item_id);
  1507. if (cur_object)
  1508. {
  1509. LLUUID cur_uuid(item_id);
  1510. while (cur_uuid != sMarketplaceListingId)
  1511. {
  1512. ++depth;
  1513. cur_uuid = cur_object->getParentUUID();
  1514. cur_object = gInventory.getCategory(cur_uuid);
  1515. if (!cur_object)
  1516. {
  1517. return -1;
  1518. }
  1519. }
  1520. }
  1521. return depth;
  1522. }
  1523. // Returns the UUID of the marketplace listing this object is in
  1524. //static
  1525. LLUUID LLMarketplace::nestedParentId(const LLUUID& item_id, S32 depth)
  1526. {
  1527. if (depth < 1)
  1528. {
  1529. // For objects outside the marketplace listings root (or root itself),
  1530. // we return a NULL UUID
  1531. return LLUUID::null;
  1532. }
  1533. else if (depth == 1)
  1534. {
  1535. // Just under the root, we return the passed UUID itself if it's a
  1536. // folder, NULL otherwise (not a listing)
  1537. LLViewerInventoryCategory* cat = gInventory.getCategory(item_id);
  1538. return cat ? item_id : LLUUID::null;
  1539. }
  1540. // depth > 1
  1541. LLInventoryObject* cur_object = gInventory.getObject(item_id);
  1542. LLUUID cur_uuid(item_id);
  1543. while (cur_object && depth-- > 1)
  1544. {
  1545. cur_uuid = cur_object->getParentUUID();
  1546. cur_object = gInventory.getCategory(cur_uuid);
  1547. }
  1548. return cur_uuid;
  1549. }
  1550. //static
  1551. S32 LLMarketplace::computeStockCount(const LLUUID& cat_id, bool force_count)
  1552. {
  1553. // Handle the case of the folder being a stock folder immediately
  1554. LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
  1555. if (!cat)
  1556. {
  1557. // Not a category so no stock count to speak of
  1558. return COMPUTE_STOCK_INFINITE;
  1559. }
  1560. if (cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)
  1561. {
  1562. if (cat->isVersionUnknown())
  1563. {
  1564. // COMPUTE_STOCK_NOT_EVALUATED denotes that a stock folder has a
  1565. // count that cannot be evaluated at this time (folder not up to
  1566. // date)
  1567. return COMPUTE_STOCK_NOT_EVALUATED;
  1568. }
  1569. // Note: stock folders are *not* supposed to have nested subfolders so
  1570. // we stop recursion here but we count only items (subfolders will be
  1571. // ignored)
  1572. // Note: we *always* give a stock count for stock folders, it's useful
  1573. // even if the listing is unassociated
  1574. LLInventoryModel::cat_array_t* cat_array;
  1575. LLInventoryModel::item_array_t* item_array;
  1576. gInventory.getDirectDescendentsOf(cat_id, cat_array, item_array);
  1577. return item_array ? item_array->size() : COMPUTE_STOCK_NOT_EVALUATED;
  1578. }
  1579. LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
  1580. // When force_count is true, we do not do any verification of the
  1581. // marketplace status and simply compute the stock amount based on the
  1582. // descendent hierarchy. This is used specifically when creating a listing.
  1583. if (!force_count)
  1584. {
  1585. // Grab marketplace data for this folder
  1586. S32 depth = depthNesting(cat_id);
  1587. LLUUID listing_uuid = nestedParentId(cat_id, depth);
  1588. if (!marketdata->isListed(listing_uuid))
  1589. {
  1590. // If not listed, the notion of stock is meaningless so it would
  1591. // not be computed for any level
  1592. return COMPUTE_STOCK_INFINITE;
  1593. }
  1594. const LLUUID& vf_uuid = marketdata->getVersionFolder(listing_uuid);
  1595. // Handle the case of the first 2 levels : listing and version folders
  1596. if (depth == 1)
  1597. {
  1598. if (vf_uuid.notNull())
  1599. {
  1600. // If there is a version folder, the stock value for the
  1601. // listing is the version folder stock
  1602. return computeStockCount(vf_uuid, true);
  1603. }
  1604. else
  1605. {
  1606. // If there's no version folder associated, the notion of stock
  1607. // count has no meaning
  1608. return COMPUTE_STOCK_INFINITE;
  1609. }
  1610. }
  1611. else if (depth == 2)
  1612. {
  1613. if (vf_uuid.notNull() && vf_uuid != cat_id)
  1614. {
  1615. // If there is a version folder but we're not it, our stock
  1616. // count is meaningless
  1617. return COMPUTE_STOCK_INFINITE;
  1618. }
  1619. }
  1620. }
  1621. // In all other cases, the stock count is the min of stock folders count
  1622. // found in the descendents
  1623. LLInventoryModel::cat_array_t* cat_array;
  1624. LLInventoryModel::item_array_t* item_array;
  1625. gInventory.getDirectDescendentsOf(cat_id, cat_array, item_array);
  1626. if (!cat_array || !item_array)
  1627. {
  1628. llwarns << "Failed to get descendents of: " << cat_id << llendl;
  1629. return COMPUTE_STOCK_INFINITE;
  1630. }
  1631. // COMPUTE_STOCK_INFINITE denotes a folder that does not contain any stock
  1632. // folder in its descendents
  1633. S32 curr_count = COMPUTE_STOCK_INFINITE;
  1634. // Note: marketplace listings have a maximum depth nesting of 4
  1635. LLInventoryModel::cat_array_t cat_array_copy = *cat_array;
  1636. for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(),
  1637. end = cat_array_copy.end();
  1638. iter != end; ++iter)
  1639. {
  1640. LLViewerInventoryCategory* category = *iter;
  1641. if (!category) continue; // Paranoia
  1642. S32 count = computeStockCount(category->getUUID(), true);
  1643. if (curr_count == COMPUTE_STOCK_INFINITE ||
  1644. (count != COMPUTE_STOCK_INFINITE && count < curr_count))
  1645. {
  1646. curr_count = count;
  1647. }
  1648. }
  1649. return curr_count;
  1650. }
  1651. //static
  1652. bool LLMarketplace::processUpdateNotification(const LLSD& data)
  1653. {
  1654. LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
  1655. S32 listing_id = data["listing_id"].asInteger();
  1656. std::string state = data["state"].asString();
  1657. if (state == "deleted")
  1658. {
  1659. // Perform the deletion viewer side, no alert shown in this case
  1660. marketdata->deleteListing(listing_id);
  1661. return true;
  1662. }
  1663. else
  1664. {
  1665. // In general, no message will be displayed, all we want is to get the
  1666. // listing updated in the inventory. If getListing() fails though, the
  1667. // message of the alert will be shown by the caller
  1668. return marketdata->getListing(listing_id);
  1669. }
  1670. }
  1671. //static
  1672. bool LLMarketplace::updateIfListed(const LLUUID& folder_id,
  1673. const LLUUID& parent_id)
  1674. {
  1675. S32 depth = LLMarketplace::depthNesting(folder_id);
  1676. if (depth == 1 || depth == 2)
  1677. {
  1678. // Trigger an SLM listing update
  1679. LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
  1680. S32 listing_id = depth == 1 ? marketdata->getListingID(folder_id)
  1681. : marketdata->getListingID(parent_id);
  1682. marketdata->getListing(listing_id);
  1683. return true;
  1684. }
  1685. return false;
  1686. }
  1687. //static
  1688. void LLMarketplace::inventoryContextMenu(LLFolderBridge* folder,
  1689. const LLUUID& id, U32 flags,
  1690. std::vector<std::string>& items,
  1691. std::vector<std::string>& disabled_items)
  1692. {
  1693. if (!folder)
  1694. {
  1695. llwarns << "NULL folder bridge !" << llendl;
  1696. llassert(false);
  1697. return;
  1698. }
  1699. LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
  1700. U32 status = marketdata->getSLMStatus();
  1701. if (status != MarketplaceStatusCodes::MARKET_PLACE_MERCHANT &&
  1702. status != MarketplaceStatusCodes::MARKET_PLACE_MIGRATED_MERCHANT)
  1703. {
  1704. // Disable everything that could harm the Marketplace listings while
  1705. // we are not connected.
  1706. disabled_items.emplace_back("Rename");
  1707. disabled_items.emplace_back("Cut");
  1708. disabled_items.emplace_back("Paste");
  1709. disabled_items.emplace_back("Delete");
  1710. if ((status == MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE ||
  1711. status == MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED))
  1712. {
  1713. items.emplace_back("Marketplace Connect");
  1714. }
  1715. return;
  1716. }
  1717. S32 depth = depthNesting(id);
  1718. bool is_updating = marketdata->isUpdating(id, depth);
  1719. // Non Marketplace-specific entries
  1720. if (depth > 0 &&
  1721. folder->getPreferredType() != LLFolderType::FT_MARKETPLACE_STOCK)
  1722. {
  1723. items.emplace_back("New Folder");
  1724. if (is_updating)
  1725. {
  1726. disabled_items.emplace_back("New Folder");
  1727. }
  1728. else if (depth >= 2)
  1729. {
  1730. // Prevent creation of new folders if the max count has been
  1731. // reached on this version folder (active or not)
  1732. const LLUUID& local_listing_id = nestedParentId(id, depth - 1);
  1733. LLInventoryModel::cat_array_t categories;
  1734. LLInventoryModel::item_array_t items;
  1735. gInventory.collectDescendents(local_listing_id, categories, items,
  1736. false);
  1737. U32 max_count = gSavedSettings.getU32("InventoryOutboxMaxFolderCount");
  1738. if (categories.size() >= max_count)
  1739. {
  1740. disabled_items.emplace_back("New Folder");
  1741. }
  1742. }
  1743. }
  1744. if (is_updating)
  1745. {
  1746. disabled_items.emplace_back("Rename");
  1747. disabled_items.emplace_back("Cut");
  1748. disabled_items.emplace_back("Copy");
  1749. disabled_items.emplace_back("Paste");
  1750. disabled_items.emplace_back("Delete");
  1751. }
  1752. // Marketplace-specific entries
  1753. items.emplace_back("Marketplace Separator");
  1754. if (depth == 0)
  1755. {
  1756. items.emplace_back("Marketplace Check Listing");
  1757. }
  1758. else if (depth == 1)
  1759. {
  1760. // Options available at the Listing Folder level
  1761. items.emplace_back("Marketplace Create Listing");
  1762. items.emplace_back("Marketplace Associate Listing");
  1763. items.emplace_back("Marketplace Check Listing");
  1764. items.emplace_back("Marketplace List");
  1765. items.emplace_back("Marketplace Unlist");
  1766. if (is_updating || (flags & FIRST_SELECTED_ITEM) == 0)
  1767. {
  1768. // During SLM update, disable all marketplace related options
  1769. // Also disable all if multiple selected items
  1770. disabled_items.emplace_back("Marketplace Create Listing");
  1771. disabled_items.emplace_back("Marketplace Associate Listing");
  1772. disabled_items.emplace_back("Marketplace Check Listing");
  1773. disabled_items.emplace_back("Marketplace List");
  1774. disabled_items.emplace_back("Marketplace Unlist");
  1775. }
  1776. else
  1777. {
  1778. bool listing_logging = false;
  1779. LL_DEBUGS("Marketplace") << "Adding 'Get/refresh listing' for debug purpose";
  1780. listing_logging = true;
  1781. LL_CONT << LL_ENDL;
  1782. if (listing_logging)
  1783. {
  1784. items.emplace_back("Marketplace Get Listing");
  1785. }
  1786. if (marketdata->isListed(id))
  1787. {
  1788. disabled_items.emplace_back("Marketplace Create Listing");
  1789. disabled_items.emplace_back("Marketplace Associate Listing");
  1790. if (marketdata->getVersionFolder(id).isNull())
  1791. {
  1792. disabled_items.emplace_back("Marketplace List");
  1793. disabled_items.emplace_back("Marketplace Unlist");
  1794. }
  1795. else
  1796. {
  1797. if (marketdata->getActivationState(id))
  1798. {
  1799. disabled_items.emplace_back("Marketplace List");
  1800. }
  1801. else
  1802. {
  1803. disabled_items.emplace_back("Marketplace Unlist");
  1804. }
  1805. }
  1806. }
  1807. else
  1808. {
  1809. disabled_items.emplace_back("Marketplace List");
  1810. disabled_items.emplace_back("Marketplace Unlist");
  1811. if (listing_logging)
  1812. {
  1813. disabled_items.emplace_back("Marketplace Get Listing");
  1814. }
  1815. }
  1816. }
  1817. }
  1818. else if (depth == 2)
  1819. {
  1820. // Options available at the Version Folder levels and only for folders
  1821. LLViewerInventoryCategory* cat = gInventory.getCategory(id);
  1822. if (cat && marketdata->isListed(cat->getParentUUID()))
  1823. {
  1824. items.emplace_back("Marketplace Activate");
  1825. items.emplace_back("Marketplace Deactivate");
  1826. if (is_updating || (flags & FIRST_SELECTED_ITEM) == 0)
  1827. {
  1828. // During SLM update, disable all marketplace related options
  1829. // Also disable all if multiple selected items
  1830. disabled_items.emplace_back("Marketplace Activate");
  1831. disabled_items.emplace_back("Marketplace Deactivate");
  1832. }
  1833. else
  1834. {
  1835. if (marketdata->isVersionFolder(id))
  1836. {
  1837. disabled_items.emplace_back("Marketplace Activate");
  1838. if (marketdata->getActivationState(id))
  1839. {
  1840. disabled_items.emplace_back("Marketplace Deactivate");
  1841. }
  1842. }
  1843. else
  1844. {
  1845. disabled_items.emplace_back("Marketplace Deactivate");
  1846. }
  1847. }
  1848. }
  1849. }
  1850. if (depth > 0)
  1851. {
  1852. // Options available at all sub-levels on items and categories
  1853. items.emplace_back("Marketplace Edit Listing");
  1854. const LLUUID& listing_id = nestedParentId(id, depth);
  1855. const LLUUID& version_id = marketdata->getVersionFolder(listing_id);
  1856. if (version_id.isNull() || !marketdata->isListed(listing_id))
  1857. {
  1858. disabled_items.emplace_back("Marketplace Edit Listing");
  1859. }
  1860. }
  1861. }
  1862. //static
  1863. std::string LLMarketplace::rootFolderLabelSuffix()
  1864. {
  1865. std::string suffix;
  1866. LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
  1867. switch (marketdata->getSLMStatus())
  1868. {
  1869. case MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING:
  1870. suffix = LLTrans::getString("MarketplaceInitializing");
  1871. break;
  1872. case MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE:
  1873. suffix = LLTrans::getString("MarketplaceFailure");
  1874. break;
  1875. case MarketplaceStatusCodes::MARKET_PLACE_MERCHANT:
  1876. case MarketplaceStatusCodes::MARKET_PLACE_MIGRATED_MERCHANT:
  1877. {
  1878. switch (marketdata->getSLMDataFetched())
  1879. {
  1880. case MarketplaceFetchCodes::MARKET_FETCH_NOT_DONE:
  1881. case MarketplaceFetchCodes::MARKET_FETCH_LOADING:
  1882. suffix = LLTrans::getString("MarketplaceFetching");
  1883. break;
  1884. case MarketplaceFetchCodes::MARKET_FETCH_FAILED:
  1885. suffix = LLTrans::getString("MarketplaceFetchFailed");
  1886. break;
  1887. case MarketplaceFetchCodes::MARKET_FETCH_DONE:
  1888. default:
  1889. suffix = LLTrans::getString("MarketplaceMerchant");
  1890. }
  1891. break;
  1892. }
  1893. case MarketplaceStatusCodes::MARKET_PLACE_NOT_MERCHANT:
  1894. suffix = LLTrans::getString("MarketplaceNotMerchant");
  1895. break;
  1896. case MarketplaceStatusCodes::MARKET_PLACE_NOT_MIGRATED_MERCHANT:
  1897. suffix = LLTrans::getString("MarketplaceNotMigrated");
  1898. default:
  1899. break;
  1900. }
  1901. if (!suffix.empty())
  1902. {
  1903. suffix = " (" + suffix + ")";
  1904. }
  1905. return suffix;
  1906. }
  1907. //static
  1908. std::string LLMarketplace::folderLabelSuffix(const LLUUID& cat_id)
  1909. {
  1910. std::string suffix;
  1911. LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
  1912. U32 status = marketdata->getSLMStatus();
  1913. if (status != MarketplaceStatusCodes::MARKET_PLACE_MERCHANT &&
  1914. status != MarketplaceStatusCodes::MARKET_PLACE_MIGRATED_MERCHANT)
  1915. {
  1916. return suffix;
  1917. }
  1918. if (marketdata->isUpdating(cat_id))
  1919. {
  1920. // Skip expensive computations if we are waiting for an update
  1921. suffix = LLTrans::getString("MarketplaceUpdating");
  1922. }
  1923. else
  1924. {
  1925. if (marketdata->isListed(cat_id)) // Listing folder case
  1926. {
  1927. S32 id = marketdata->getListingID(cat_id);
  1928. if (id)
  1929. {
  1930. suffix = llformat("%d", id);
  1931. }
  1932. else
  1933. {
  1934. suffix = LLTrans::getString("MarketplaceNoID");
  1935. }
  1936. if (marketdata->getActivationState(cat_id))
  1937. {
  1938. suffix += " - " + LLTrans::getString("MarketplaceLive");
  1939. }
  1940. }
  1941. else if (marketdata->isVersionFolder(cat_id)) // Version folder case
  1942. {
  1943. suffix = LLTrans::getString("MarketplaceActive");
  1944. }
  1945. // Add stock amount
  1946. S32 stock_count = computeStockCount(cat_id);
  1947. if (stock_count == COMPUTE_STOCK_NOT_EVALUATED)
  1948. {
  1949. // Add updating suffix
  1950. if (!suffix.empty())
  1951. {
  1952. suffix += " - ";
  1953. }
  1954. suffix += LLTrans::getString("MarketplaceUpdating");
  1955. }
  1956. else if (stock_count == 0)
  1957. {
  1958. if (!suffix.empty())
  1959. {
  1960. suffix += " - ";
  1961. }
  1962. suffix += LLTrans::getString("MarketplaceNoStock");
  1963. }
  1964. else if (stock_count > 0)
  1965. {
  1966. if (!suffix.empty())
  1967. {
  1968. suffix += " - ";
  1969. }
  1970. LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
  1971. if (cat &&
  1972. cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)
  1973. {
  1974. suffix += LLTrans::getString("MarketplaceStock") + "=" +
  1975. llformat("%d", stock_count);
  1976. }
  1977. else
  1978. {
  1979. suffix += LLTrans::getString("MarketplaceMax") + "=" +
  1980. llformat("%d", stock_count);
  1981. }
  1982. }
  1983. }
  1984. if (!suffix.empty())
  1985. {
  1986. suffix = " (" + suffix + ")";
  1987. }
  1988. return suffix;
  1989. }
  1990. //static
  1991. bool LLMarketplace::isFolderActive(const LLUUID& cat_id)
  1992. {
  1993. LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
  1994. U32 status = marketdata->getSLMStatus();
  1995. return (status == MarketplaceStatusCodes::MARKET_PLACE_MIGRATED_MERCHANT ||
  1996. status == MarketplaceStatusCodes::MARKET_PLACE_MERCHANT) &&
  1997. marketdata->getActivationState(cat_id);
  1998. }
  1999. //static
  2000. void LLMarketplace::getListing(const LLUUID& folder_id)
  2001. {
  2002. LLMarketplaceData::getInstance()->getListing(folder_id);
  2003. }
  2004. //static
  2005. void LLMarketplace::createListing(const LLUUID& folder_id)
  2006. {
  2007. LLViewerInventoryCategory* cat = gInventory.getCategory(folder_id);
  2008. sMessage.clear();
  2009. bool valid = validateListings(cat,
  2010. boost::bind(&LLMarketplace::gatherMessage,
  2011. _1, _2, _3),
  2012. false);
  2013. if (!valid)
  2014. {
  2015. sMessage.clear();
  2016. valid = validateListings(cat,
  2017. boost::bind(&LLMarketplace::gatherMessage,
  2018. _1, _2, _3));
  2019. if (valid)
  2020. {
  2021. gNotifications.add("MerchantForceValidateListing");
  2022. }
  2023. }
  2024. if (valid)
  2025. {
  2026. LLMarketplaceData::getInstance()->createListing(folder_id);
  2027. }
  2028. else
  2029. {
  2030. LLSD subs;
  2031. subs["ERROR_CODE"] = sMessage;
  2032. gNotifications.add("MerchantListingFailed", subs);
  2033. }
  2034. }
  2035. //static
  2036. void LLMarketplace::clearListing(const LLUUID& folder_id)
  2037. {
  2038. LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
  2039. if (marketdata->isListed(folder_id))
  2040. {
  2041. marketdata->clearListing(folder_id);
  2042. }
  2043. }
  2044. //static
  2045. void LLMarketplace::editListing(const LLUUID& folder_id)
  2046. {
  2047. std::string url =
  2048. LLMarketplaceData::getInstance()->getListingURL(folder_id);
  2049. if (!url.empty())
  2050. {
  2051. LLWeb::loadURL(url);
  2052. }
  2053. }
  2054. //static
  2055. void LLMarketplace::gatherMessage(std::string& message, S32 depth,
  2056. LLError::ELevel log_level)
  2057. {
  2058. if (log_level > LLError::LEVEL_WARN && !sMessage.empty())
  2059. {
  2060. // Currently, we do not gather all messages as it creates very long
  2061. // alerts. Users can get to the whole list of errors on a listing using
  2062. // the "Check listing" right click menu
  2063. return;
  2064. }
  2065. // Take the leading spaces out...
  2066. std::string::size_type start = message.find_first_not_of(' ');
  2067. // Append the message
  2068. sMessage += message.substr(start, message.length() - start);
  2069. }
  2070. //static
  2071. void LLMarketplace::listFolder(const LLUUID& folder_id, bool list)
  2072. {
  2073. if (depthNesting(folder_id) == 1)
  2074. {
  2075. LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
  2076. if (list)
  2077. {
  2078. const LLUUID& version_id = marketdata->getVersionFolder(folder_id);
  2079. LLViewerInventoryCategory* cat = gInventory.getCategory(version_id);
  2080. sMessage.clear();
  2081. if (!validateListings(cat,
  2082. boost::bind(&LLMarketplace::gatherMessage,
  2083. _1, _2, _3)))
  2084. {
  2085. LLSD subs;
  2086. subs["ERROR_CODE"] = sMessage;
  2087. gNotifications.add("MerchantListingFailed", subs);
  2088. }
  2089. else
  2090. {
  2091. marketdata->activateListing(folder_id, true, 1);
  2092. }
  2093. }
  2094. else
  2095. {
  2096. marketdata->activateListing(folder_id, false, 1);
  2097. }
  2098. }
  2099. }
  2100. //static
  2101. void LLMarketplace::activateFolder(const LLUUID& folder_id, bool activate)
  2102. {
  2103. if (depthNesting(folder_id) == 2)
  2104. {
  2105. LLViewerInventoryCategory* cat = gInventory.getCategory(folder_id);
  2106. if (cat)
  2107. {
  2108. sMessage.clear();
  2109. if (activate &&
  2110. !validateListings(cat,
  2111. boost::bind(&LLMarketplace::gatherMessage,
  2112. _1, _2, _3),
  2113. false, 2))
  2114. {
  2115. LLSD subs;
  2116. subs["ERROR_CODE"] = sMessage;
  2117. gNotifications.add("MerchantFolderActivationFailed", subs);
  2118. return;
  2119. }
  2120. LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
  2121. const LLUUID& link_id = activate ? folder_id : LLUUID::null;
  2122. marketdata->setVersionFolder(cat->getParentUUID(), link_id);
  2123. }
  2124. }
  2125. }
  2126. //static
  2127. void LLMarketplace::updateFolderHierarchy(const LLUUID& cat_id)
  2128. {
  2129. // When changing the marketplace status of a folder, the only thing that
  2130. // needs to happen is for all observers of the folder to, possibly, change
  2131. // the display label of the folder so that's the only thing we change on
  2132. // the update mask.
  2133. gInventory.addChangedMask(LLInventoryObserver::LABEL, cat_id);
  2134. // Update all descendent folders down
  2135. LLInventoryModel::cat_array_t* cat_array;
  2136. LLInventoryModel::item_array_t* item_array;
  2137. gInventory.getDirectDescendentsOf(cat_id, cat_array, item_array);
  2138. if (!cat_array || !item_array)
  2139. {
  2140. llwarns << "Failed to get descendents of: " << cat_id << llendl;
  2141. return;
  2142. }
  2143. LLInventoryModel::cat_array_t cat_array_copy = *cat_array;
  2144. for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(),
  2145. end = cat_array_copy.end();
  2146. iter != end; ++iter)
  2147. {
  2148. LLViewerInventoryCategory* category = *iter;
  2149. if (category)
  2150. {
  2151. updateFolderHierarchy(category->getUUID());
  2152. }
  2153. }
  2154. }
  2155. //static
  2156. void LLMarketplace::updateCategory(const LLUUID& cur_uuid,
  2157. bool perform_consistency_enforcement)
  2158. {
  2159. // When changing the marketplace status of an item, we usually have to
  2160. // change the status of all folders in the same listing. This is because
  2161. // the display of each folder is affected by the overall status of the
  2162. // whole listing. Consequently, the only way to correctly update an item
  2163. // anywhere in the marketplace is to update the whole listing from its
  2164. // listing root. This is not as bad as it seems as we only update folders,
  2165. // not items, and the folder nesting depth is limited to 4.
  2166. // We also take care of degenerated cases so we do not update all folders
  2167. // in the inventory by mistake.
  2168. if (cur_uuid.isNull())
  2169. {
  2170. return;
  2171. }
  2172. LLViewerInventoryCategory* cat = gInventory.getCategory(cur_uuid);
  2173. if (!cat || cat->isVersionUnknown())
  2174. {
  2175. return;
  2176. }
  2177. LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
  2178. // Grab marketplace listing data for this item
  2179. S32 depth = depthNesting(cur_uuid);
  2180. if (depth > 0)
  2181. {
  2182. // Retrieve the listing uuid this object is in
  2183. const LLUUID& listing_uuid = nestedParentId(cur_uuid, depth);
  2184. if (perform_consistency_enforcement)
  2185. {
  2186. cat = gInventory.getCategory(listing_uuid);
  2187. if (!cat || cat->isVersionUnknown())
  2188. {
  2189. perform_consistency_enforcement = false;
  2190. }
  2191. }
  2192. // Verify marketplace data consistency for this listing
  2193. if (perform_consistency_enforcement &&
  2194. marketdata->isListed(listing_uuid))
  2195. {
  2196. const LLUUID& vf_uuid = marketdata->getVersionFolder(listing_uuid);
  2197. if (vf_uuid.notNull())
  2198. {
  2199. S32 version_depth = depthNesting(vf_uuid);
  2200. if (version_depth != 2 ||
  2201. !gInventory.isObjectDescendentOf(vf_uuid, listing_uuid))
  2202. {
  2203. llinfos << "Unlisting and clearing the listing folder "
  2204. << listing_uuid
  2205. << " because the version folder " << vf_uuid
  2206. << " is not at the right place anymore" << llendl;
  2207. marketdata->setVersionFolder(listing_uuid, LLUUID::null);
  2208. }
  2209. else if (gInventory.isCategoryComplete(vf_uuid) &&
  2210. marketdata->getActivationState(vf_uuid) &&
  2211. count_descendants_items(vf_uuid) == 0 &&
  2212. !marketdata->isUpdating(vf_uuid, depth))
  2213. {
  2214. llinfos << "Unlisting the listing folder " << listing_uuid
  2215. << " because the version folder " << vf_uuid
  2216. << " is empty" << llendl;
  2217. marketdata->activateListing(listing_uuid, false);
  2218. }
  2219. }
  2220. }
  2221. // Check if the count on hand needs to be updated on SLM
  2222. if (perform_consistency_enforcement &&
  2223. computeStockCount(listing_uuid,
  2224. true) != marketdata->getCountOnHand(listing_uuid))
  2225. {
  2226. marketdata->updateCountOnHand(listing_uuid);
  2227. }
  2228. // Update all descendents starting from the listing root
  2229. updateFolderHierarchy(listing_uuid);
  2230. }
  2231. else if (depth == 0)
  2232. {
  2233. // If this is the marketplace listings root itself, update all descendents
  2234. if (gInventory.getCategory(cur_uuid))
  2235. {
  2236. updateFolderHierarchy(cur_uuid);
  2237. }
  2238. }
  2239. else
  2240. {
  2241. // If the folder is outside the marketplace listings root, clear its
  2242. // SLM data if needs be
  2243. if (perform_consistency_enforcement && marketdata->isListed(cur_uuid))
  2244. {
  2245. llinfos << "Disassociating since the listing folder is not under the marketplace folder anymore"
  2246. << llendl;
  2247. marketdata->clearListing(cur_uuid);
  2248. }
  2249. // Update all descendents if this is a category
  2250. if (gInventory.getCategory(cur_uuid))
  2251. {
  2252. updateFolderHierarchy(cur_uuid);
  2253. }
  2254. }
  2255. }
  2256. // Iterate through the marketplace and flag for label change all categories
  2257. // that countain a stock folder (i.e. stock folders and embedding folders up
  2258. // the hierarchy)
  2259. //static
  2260. void LLMarketplace::updateAllCounts(const LLUUID& cat_id)
  2261. {
  2262. // Get all descendent folders down
  2263. LLInventoryModel::cat_array_t* cat_array;
  2264. LLInventoryModel::item_array_t* item_array;
  2265. gInventory.getDirectDescendentsOf(cat_id, cat_array, item_array);
  2266. if (!cat_array || !item_array)
  2267. {
  2268. llwarns << "Failed to get descendents of: " << cat_id << llendl;
  2269. return;
  2270. }
  2271. LLInventoryModel::cat_array_t cat_array_copy = *cat_array;
  2272. for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(),
  2273. end = cat_array_copy.end();
  2274. iter != end; ++iter)
  2275. {
  2276. LLViewerInventoryCategory* category = *iter;
  2277. if (!category) continue; // Paranoia
  2278. if (category->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)
  2279. {
  2280. // Listing containing stock folders needs to be updated but not
  2281. // others. Note: we take advantage of the fact that stock folder
  2282. // *do not* contain sub folders to avoid a recursive call here.
  2283. updateCategory(category->getUUID());
  2284. gInventory.notifyObservers();
  2285. }
  2286. else
  2287. {
  2288. // Explore the contained folders recursively
  2289. updateAllCounts(category->getUUID());
  2290. }
  2291. }
  2292. }
  2293. //static
  2294. void LLMarketplace::updateAllCounts()
  2295. {
  2296. if (LLMarketplaceData::getInstance()->checkDirtyCount())
  2297. {
  2298. // Get the marketplace root and launch the recursive exploration
  2299. if (sMarketplaceListingId.notNull())
  2300. {
  2301. updateAllCounts(sMarketplaceListingId);
  2302. }
  2303. }
  2304. }
  2305. //static
  2306. void LLMarketplace::initializeCallback()
  2307. {
  2308. LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
  2309. U32 status = marketdata->getSLMStatus();
  2310. if (status == MarketplaceStatusCodes::MARKET_PLACE_MERCHANT ||
  2311. status == MarketplaceStatusCodes::MARKET_PLACE_MIGRATED_MERCHANT)
  2312. {
  2313. // Create the Marketplace Listings folder if missing
  2314. sMarketplaceListingId =
  2315. gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS,
  2316. true);
  2317. if (sMarketplaceListingId.isNull())
  2318. {
  2319. llwarns << "Failed to create the Marketplace Listings folder"
  2320. << llendl;
  2321. marketdata->setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED);
  2322. }
  2323. else
  2324. {
  2325. marketdata->setSLMDataFetched(MarketplaceFetchCodes::MARKET_FETCH_LOADING);
  2326. marketdata->getSLMListings();
  2327. }
  2328. }
  2329. else
  2330. {
  2331. sMarketplaceListingId =
  2332. gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS,
  2333. false);
  2334. }
  2335. if (sMarketplaceListingId.isNull())
  2336. {
  2337. return;
  2338. }
  2339. // We should not have to do that but with a client/server system relying on
  2340. // a "well known folder" convention, things get messy and conventions get
  2341. // broken down eventually
  2342. gInventory.consolidateForType(sMarketplaceListingId,
  2343. LLFolderType::FT_MARKETPLACE_LISTINGS);
  2344. // Force an update of the market place items labels
  2345. LL_DEBUGS("Marketplace") << "Updating Marketplace Listings folder items labels"
  2346. << LL_ENDL;
  2347. gInventory.addChangedMask(LLInventoryObserver::LABEL,
  2348. sMarketplaceListingId);
  2349. #if 0 // We needed this during the SLM transition, because when crossing
  2350. // borders with a non-SLM-aware region, the labels of the folders were
  2351. // changed based on the 'not MARKET_PLACE_MERCHANT' status, instead of
  2352. // being updated by the responders (which obviously could not respond
  2353. // any more to anything...)
  2354. LLInventoryModel::cat_array_t descendent_categories;
  2355. LLInventoryModel::item_array_t descendent_items;
  2356. gInventory.collectDescendents(sMarketplaceListingId,
  2357. descendent_categories, descendent_items,
  2358. false);
  2359. for (S32 i = 0, count = descendent_categories.size(); i < count; ++i)
  2360. {
  2361. LLViewerInventoryCategory* cat = descendent_categories[i];
  2362. if (cat)
  2363. {
  2364. gInventory.addChangedMask(LLInventoryObserver::LABEL,
  2365. cat->getUUID());
  2366. }
  2367. }
  2368. #endif
  2369. gInventory.notifyObservers();
  2370. }
  2371. //static
  2372. void LLMarketplace::setup(bool warn)
  2373. {
  2374. sMarketplaceListingId =
  2375. gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS,
  2376. false);
  2377. if (sMarketplaceListingId.notNull())
  2378. {
  2379. LLInventoryModel::cat_array_t categories;
  2380. LLInventoryModel::item_array_t items;
  2381. gInventory.collectDescendents(sMarketplaceListingId, categories, items,
  2382. false);
  2383. U32 max_count = gSavedSettings.getU32("MarketplaceLargeInventory");
  2384. if (categories.size() >= max_count)
  2385. {
  2386. if (warn)
  2387. {
  2388. gNotifications.add("AlertLargeMarketplace");
  2389. }
  2390. return;
  2391. }
  2392. }
  2393. LLMarketplaceData::getInstance()->initializeSLM(boost::bind(&LLMarketplace::initializeCallback));
  2394. }
  2395. //static
  2396. void LLMarketplace::checkMerchantStatus()
  2397. {
  2398. LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
  2399. marketdata->setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED);
  2400. marketdata->initializeSLM(boost::bind(&LLMarketplace::initializeCallback));
  2401. }
  2402. //static
  2403. bool LLMarketplace::connected()
  2404. {
  2405. U32 status = LLMarketplaceData::getInstance()->getSLMStatus();
  2406. return status == MarketplaceStatusCodes::MARKET_PLACE_MERCHANT ||
  2407. status == MarketplaceStatusCodes::MARKET_PLACE_MIGRATED_MERCHANT;
  2408. }
  2409. bool sort_alpha(const LLViewerInventoryCategory* cat1,
  2410. const LLViewerInventoryCategory* cat2)
  2411. {
  2412. return cat1->getName().compare(cat2->getName()) < 0;
  2413. }
  2414. // Make all relevant business logic checks on the marketplace listings starting
  2415. // with the folder as argument. This function does no deletion of listings but
  2416. // a mere audit and raises issues to the user (through the optional callback).
  2417. // It also returns a boolean, true if things validate, false if issues are
  2418. // raised. The only inventory changes that are done is to move and sort folders
  2419. // containing no-copy items to stock folders.
  2420. //static
  2421. bool LLMarketplace::validateListings(LLViewerInventoryCategory* cat,
  2422. LLMarketplace::validation_callback_t cb,
  2423. bool fix_hierarchy, S32 depth)
  2424. {
  2425. if (!cat) return false;
  2426. // Folder is valid unless issue is raised
  2427. bool result = true;
  2428. // Get the type and the depth of the folder
  2429. LLViewerInventoryCategory* viewer_cat = cat;
  2430. const LLFolderType::EType folder_type = cat->getPreferredType();
  2431. if (depth < 0)
  2432. {
  2433. // If the depth argument was not provided, evaluate the depth directly
  2434. depth = depthNesting(cat->getUUID());
  2435. }
  2436. if (depth < 0)
  2437. {
  2438. // If the folder is not under the marketplace listings root, we run
  2439. // validation as if it was a listing folder and prevent any hierarchy
  2440. // fix. This allows the function to be used to pre-validate a folder
  2441. // anywhere in the inventory.
  2442. depth = 1;
  2443. fix_hierarchy = false;
  2444. }
  2445. // Set the indentation for print output
  2446. std::string indent;
  2447. for (S32 i = 1; i < depth; ++i)
  2448. {
  2449. indent += " ";
  2450. }
  2451. std::string message;
  2452. // Check out that version folders are marketplace ready
  2453. if (depth == 2)
  2454. {
  2455. if (!canMoveFolderInto(cat, cat, cat, message, 0))
  2456. {
  2457. result = false;
  2458. if (cb)
  2459. {
  2460. message = indent + cat->getName() +
  2461. LLTrans::getString("Marketplace Validation Error") +
  2462. " " + message;
  2463. cb(message, depth, LLError::LEVEL_ERROR);
  2464. }
  2465. }
  2466. }
  2467. // Check out that stock folders are at the right level
  2468. if (folder_type == LLFolderType::FT_MARKETPLACE_STOCK && depth <= 2)
  2469. {
  2470. if (cb)
  2471. {
  2472. message = indent + cat->getName();
  2473. }
  2474. if (fix_hierarchy)
  2475. {
  2476. if (cb)
  2477. {
  2478. message +=
  2479. LLTrans::getString("Marketplace Validation Warning") +
  2480. " " +
  2481. LLTrans::getString("Marketplace Validation Warning Stock");
  2482. cb(message, depth, LLError::LEVEL_WARN);
  2483. }
  2484. // Nest the stock folder one level deeper in a normal folder and
  2485. // restart from there
  2486. const LLUUID& parent_id = cat->getParentUUID();
  2487. const LLUUID& folder_id =
  2488. gInventory.createCategoryUDP(parent_id,
  2489. LLFolderType::FT_NONE,
  2490. cat->getName());
  2491. gInventory.notifyObservers();
  2492. LLViewerInventoryCategory* new_cat =
  2493. gInventory.getCategory(folder_id);
  2494. gInventory.changeCategoryParent(viewer_cat, folder_id, false);
  2495. gInventory.notifyObservers();
  2496. result &= validateListings(new_cat, cb, fix_hierarchy, ++depth);
  2497. return result;
  2498. }
  2499. result = false;
  2500. if (cb)
  2501. {
  2502. message += LLTrans::getString("Marketplace Validation Error") +
  2503. " " +
  2504. LLTrans::getString("Marketplace Validation Warning Stock");
  2505. cb(message, depth, LLError::LEVEL_ERROR);
  2506. }
  2507. }
  2508. // Item sorting and validation: sorting and moving the various stock items
  2509. // is complicated as the set of constraints is high. We need to:
  2510. // * separate non stock items, stock items per types in different folders
  2511. // * have stock items nested at depth 2 at least
  2512. // * never ever move the non-stock items
  2513. LLInventoryModel::cat_array_t* cat_array;
  2514. LLInventoryModel::item_array_t* item_array;
  2515. gInventory.getDirectDescendentsOf(cat->getUUID(), cat_array, item_array);
  2516. if (!cat_array || !item_array)
  2517. {
  2518. if (cb)
  2519. {
  2520. message = indent + cat->getName() +
  2521. LLTrans::getString("Marketplace Failed Descendents");
  2522. cb(message, depth, LLError::LEVEL_ERROR);
  2523. }
  2524. return false;
  2525. }
  2526. // We use a composite (type, permissions) key on that map to store UUIDs of
  2527. // items of same (type, permissions)
  2528. std::map<U32, std::vector<LLUUID> > items_vector;
  2529. // Parse the items and create vectors of item UUIDs sorting copyable items
  2530. // and stock items of various types
  2531. const LLUUID& group_id = gAgent.getGroupID();
  2532. bool has_bad_items = false;
  2533. LLInventoryModel::item_array_t item_array_copy = *item_array;
  2534. for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin();
  2535. iter != item_array_copy.end(); ++iter)
  2536. {
  2537. LLViewerInventoryItem* itemp = *iter;
  2538. if (!itemp) continue; // Paranoia
  2539. // Test but skip items that should not be there to start with, raise
  2540. // an error message for those
  2541. std::string error_msg;
  2542. if (!can_move_to_marketplace(itemp, error_msg))
  2543. {
  2544. has_bad_items = true;
  2545. if (cb && fix_hierarchy)
  2546. {
  2547. message = indent + itemp->getName() +
  2548. LLTrans::getString("Marketplace Validation Error") +
  2549. " " + error_msg;
  2550. cb(message, depth, LLError::LEVEL_ERROR);
  2551. }
  2552. continue;
  2553. }
  2554. // Update the appropriate vector item for that type
  2555. // Default value for non stock items:
  2556. LLInventoryType::EType type = LLInventoryType::IT_COUNT;
  2557. U32 perms = 0;
  2558. if (!itemp->getPermissions().allowCopyBy(gAgentID, group_id))
  2559. {
  2560. // Get the item type for stock items
  2561. type = itemp->getInventoryType();
  2562. perms = itemp->getPermissions().getMaskNextOwner();
  2563. }
  2564. U32 key = (((U32)(type) & 0xFF) << 24) | (perms & 0xFFFFFF);
  2565. items_vector[key].emplace_back(itemp->getUUID());
  2566. }
  2567. // How many types of items ? Which type is it if only one ?
  2568. S32 count = items_vector.size();
  2569. // This is the key for any normal copyable item:
  2570. U32 default_key = (U32)(LLInventoryType::IT_COUNT) << 24;
  2571. // The key in the case of one item type only:
  2572. U32 unique_key = count == 1 ? items_vector.begin()->first : default_key;
  2573. // If we have no items in there (only folders or empty), analyze a bit
  2574. // further
  2575. if (count == 0 && !has_bad_items)
  2576. {
  2577. if (cb)
  2578. {
  2579. message = indent + cat->getName();
  2580. if (cat_array->size() == 0)
  2581. {
  2582. // So we have no item and no folder. That is a warning.
  2583. if (depth == 2)
  2584. {
  2585. // If this is an empty version folder, warn only (listing
  2586. // would not be delivered by AIS, but only AIS should
  2587. // unlist)
  2588. message +=
  2589. LLTrans::getString("Marketplace Validation Error Empty Version");
  2590. cb(message, depth, LLError::LEVEL_WARN);
  2591. }
  2592. else if (depth > 2 &&
  2593. folder_type == LLFolderType::FT_MARKETPLACE_STOCK)
  2594. {
  2595. // If this is a legit but empty stock folder, warn only
  2596. // (listing must stay searchable when out of stock)
  2597. message +=
  2598. LLTrans::getString("Marketplace Validation Error Empty Stock");
  2599. cb(message, depth, LLError::LEVEL_WARN);
  2600. }
  2601. else
  2602. {
  2603. // We warn if there's nothing in a regular folder (may be it's
  2604. // an under construction listing)
  2605. message +=
  2606. LLTrans::getString("Marketplace Validation Warning Empty");
  2607. cb(message, depth, LLError::LEVEL_WARN);
  2608. }
  2609. }
  2610. else if (result && depth >= 1)
  2611. {
  2612. // Done with that folder: print out the folder name unless we
  2613. // already found an error here
  2614. message += LLTrans::getString("Marketplace Validation Log");
  2615. cb(message, depth, LLError::LEVEL_INFO);
  2616. }
  2617. }
  2618. }
  2619. // If we have a single type of items of the right type in the right place,
  2620. // we are done
  2621. else if (count == 1 && !has_bad_items &&
  2622. ((unique_key == default_key && depth > 1) ||
  2623. (folder_type == LLFolderType::FT_MARKETPLACE_STOCK &&
  2624. depth > 2 && cat_array->size() == 0)))
  2625. {
  2626. // Done with that folder: print out the folder name unless we already
  2627. // found an error here
  2628. if (cb && result && depth >= 1)
  2629. {
  2630. message = indent + cat->getName() +
  2631. LLTrans::getString("Marketplace Validation Log");
  2632. cb(message, depth, LLError::LEVEL_INFO);
  2633. }
  2634. }
  2635. else
  2636. {
  2637. if (fix_hierarchy && !has_bad_items)
  2638. {
  2639. // Alert the user when an existing stock folder has to be split
  2640. if (folder_type == LLFolderType::FT_MARKETPLACE_STOCK &&
  2641. (count >= 2 || cat_array->size() > 0))
  2642. {
  2643. gNotifications.add("AlertMerchantStockFolderSplit");
  2644. }
  2645. // If we have more than 1 type of items or we are at the listing
  2646. // level or we have stock/no stock type mismatch, wrap the items
  2647. // in subfolders
  2648. if (count > 1 || depth == 1 ||
  2649. (folder_type == LLFolderType::FT_MARKETPLACE_STOCK &&
  2650. unique_key == default_key) ||
  2651. (folder_type != LLFolderType::FT_MARKETPLACE_STOCK &&
  2652. unique_key != default_key))
  2653. {
  2654. // Create one folder per vector at the right depth and of the
  2655. // right type
  2656. for (std::map<U32, std::vector<LLUUID> >::iterator
  2657. it = items_vector.begin(), end = items_vector.end();
  2658. it != end; ++it)
  2659. {
  2660. // Create a new folder
  2661. const LLUUID& parent_uuid = depth > 2 ? viewer_cat->getParentUUID()
  2662. : viewer_cat->getUUID();
  2663. LLViewerInventoryItem* item = gInventory.getItem(it->second.back());
  2664. std::string folder_name = depth >= 1 ? viewer_cat->getName()
  2665. : item->getName();
  2666. LLFolderType::EType new_folder_type =
  2667. it->first == default_key ? LLFolderType::FT_NONE
  2668. : LLFolderType::FT_MARKETPLACE_STOCK;
  2669. if (cb)
  2670. {
  2671. message = indent + folder_name;
  2672. if (new_folder_type == LLFolderType::FT_MARKETPLACE_STOCK)
  2673. {
  2674. message +=
  2675. LLTrans::getString("Marketplace Validation Warning Create Stock");
  2676. }
  2677. else
  2678. {
  2679. message +=
  2680. LLTrans::getString("Marketplace Validation Warning Create Version");
  2681. }
  2682. cb(message, depth, LLError::LEVEL_WARN);
  2683. }
  2684. LLUUID folder_uuid = gInventory.createCategoryUDP(parent_uuid,
  2685. new_folder_type,
  2686. folder_name);
  2687. gInventory.notifyObservers();
  2688. // Move each item to the new folder
  2689. while (!it->second.empty())
  2690. {
  2691. item = gInventory.getItem(it->second.back());
  2692. if (cb)
  2693. {
  2694. message = indent + item->getName() +
  2695. LLTrans::getString("Marketplace Validation Warning Move");
  2696. cb(message, depth, LLError::LEVEL_WARN);
  2697. }
  2698. gInventory.changeItemParent(item, folder_uuid, true);
  2699. gInventory.notifyObservers();
  2700. it->second.pop_back();
  2701. }
  2702. updateCategory(parent_uuid);
  2703. gInventory.notifyObservers();
  2704. updateCategory(folder_uuid);
  2705. gInventory.notifyObservers();
  2706. }
  2707. }
  2708. // Stock folder should have no sub folder so reparent those up
  2709. if (folder_type == LLFolderType::FT_MARKETPLACE_STOCK)
  2710. {
  2711. const LLUUID& parent_uuid = cat->getParentUUID();
  2712. gInventory.getDirectDescendentsOf(cat->getUUID(), cat_array,
  2713. item_array);
  2714. if (!cat_array || !item_array)
  2715. {
  2716. if (cb)
  2717. {
  2718. message = indent + cat->getName() +
  2719. LLTrans::getString("Marketplace Failed Descendents");
  2720. cb(message, depth, LLError::LEVEL_ERROR);
  2721. }
  2722. result = false;
  2723. }
  2724. else
  2725. {
  2726. LLInventoryModel::cat_array_t cat_array_copy = *cat_array;
  2727. for (LLInventoryModel::cat_array_t::iterator
  2728. iter = cat_array_copy.begin();
  2729. iter != cat_array_copy.end(); ++iter)
  2730. {
  2731. LLViewerInventoryCategory* viewer_cat = *iter;
  2732. if (!viewer_cat) continue; // Paranoia
  2733. gInventory.changeCategoryParent(viewer_cat, parent_uuid,
  2734. false);
  2735. gInventory.notifyObservers();
  2736. result &= validateListings(viewer_cat, cb, fix_hierarchy,
  2737. depth);
  2738. }
  2739. }
  2740. }
  2741. }
  2742. else if (cb)
  2743. {
  2744. // We are not fixing the hierarchy but reporting problems, report
  2745. // everything we can find.
  2746. // Print the folder name
  2747. if (result && depth >= 1)
  2748. {
  2749. message = indent + cat->getName();
  2750. if (folder_type == LLFolderType::FT_MARKETPLACE_STOCK)
  2751. {
  2752. if (count >= 2)
  2753. {
  2754. // Report if a stock folder contains a mix of items
  2755. result = false;
  2756. message +=
  2757. LLTrans::getString("Marketplace Validation Error Mixed Stock");
  2758. cb(message, depth, LLError::LEVEL_ERROR);
  2759. }
  2760. else if (cat_array->size())
  2761. {
  2762. // Report if a stock folder contains subfolders
  2763. result = false;
  2764. message +=
  2765. LLTrans::getString("Marketplace Validation Error Subfolder In Stock");
  2766. cb(message, depth, LLError::LEVEL_ERROR);
  2767. }
  2768. }
  2769. if (result)
  2770. {
  2771. // Simply print the folder name
  2772. message += LLTrans::getString("Marketplace Validation Log");
  2773. cb(message, depth, LLError::LEVEL_INFO);
  2774. }
  2775. }
  2776. // Scan each item and report if there's a problem
  2777. LLInventoryModel::item_array_t item_array_copy = *item_array;
  2778. for (LLInventoryModel::item_array_t::iterator
  2779. iter = item_array_copy.begin();
  2780. iter != item_array_copy.end(); ++iter)
  2781. {
  2782. LLViewerInventoryItem* item = *iter;
  2783. if (!item) continue; // Paranoia
  2784. message = indent + " " + item->getName();
  2785. std::string error_msg;
  2786. if (!can_move_to_marketplace(item, error_msg))
  2787. {
  2788. // Report items that should not be there to start with
  2789. result = false;
  2790. message += LLTrans::getString("Marketplace Validation Error") +
  2791. " " + error_msg;
  2792. cb(message, depth, LLError::LEVEL_ERROR);
  2793. }
  2794. else if (folder_type != LLFolderType::FT_MARKETPLACE_STOCK &&
  2795. !item->getPermissions().allowCopyBy(gAgentID,
  2796. group_id))
  2797. {
  2798. // Report stock items that are misplaced
  2799. result = false;
  2800. message +=
  2801. LLTrans::getString("Marketplace Validation Error Stock Item");
  2802. cb(message, depth, LLError::LEVEL_ERROR);
  2803. }
  2804. else if (depth == 1)
  2805. {
  2806. // Report items not wrapped in version folder
  2807. result = false;
  2808. message +=
  2809. LLTrans::getString("Marketplace Validation Warning Unwrapped Item");
  2810. cb(message, depth, LLError::LEVEL_ERROR);
  2811. }
  2812. }
  2813. }
  2814. // Clean up
  2815. if (viewer_cat->getDescendentCount() == 0)
  2816. {
  2817. // Remove the current folder if it ends up empty
  2818. if (cb)
  2819. {
  2820. message = indent + viewer_cat->getName() +
  2821. LLTrans::getString("Marketplace Validation Warning Delete");
  2822. cb(message, depth, LLError::LEVEL_WARN);
  2823. }
  2824. gInventory.removeCategory(cat->getUUID());
  2825. gInventory.notifyObservers();
  2826. return result && !has_bad_items;
  2827. }
  2828. }
  2829. // Recursion : Perform the same validation on each nested folder
  2830. gInventory.getDirectDescendentsOf(cat->getUUID(), cat_array, item_array);
  2831. if (!cat_array || !item_array)
  2832. {
  2833. if (cb)
  2834. {
  2835. message = indent + cat->getName() +
  2836. LLTrans::getString("Marketplace Failed Descendents");
  2837. cb(message, depth, LLError::LEVEL_ERROR);
  2838. }
  2839. return false;
  2840. }
  2841. LLInventoryModel::cat_array_t cat_array_copy = *cat_array;
  2842. // Sort the folders in alphabetical order first
  2843. std::sort(cat_array_copy.begin(), cat_array_copy.end(), sort_alpha);
  2844. for (LLInventoryModel::cat_array_t::iterator
  2845. iter = cat_array_copy.begin();
  2846. iter != cat_array_copy.end(); ++iter)
  2847. {
  2848. LLViewerInventoryCategory* category = *iter;
  2849. result &= validateListings(category, cb, fix_hierarchy, depth + 1);
  2850. }
  2851. // Update the current folder
  2852. updateCategory(cat->getUUID(), fix_hierarchy);
  2853. gInventory.notifyObservers();
  2854. return result && !has_bad_items;
  2855. }
  2856. //static
  2857. bool LLMarketplace::hasPermissionsForSale(LLViewerInventoryCategory* cat,
  2858. std::string& error_msg)
  2859. {
  2860. if (!cat)
  2861. {
  2862. error_msg = "NULL category !";
  2863. return false;
  2864. }
  2865. LLInventoryModel::cat_array_t* cat_array;
  2866. LLInventoryModel::item_array_t* item_array;
  2867. gInventory.getDirectDescendentsOf(cat->getUUID(), cat_array, item_array);
  2868. if (!cat_array || !item_array)
  2869. {
  2870. llwarns << "Failed to get descendents of: " << cat->getUUID()
  2871. << llendl;
  2872. return false;
  2873. }
  2874. LLInventoryModel::item_array_t item_array_copy = *item_array;
  2875. for (LLInventoryModel::item_array_t::iterator
  2876. iter = item_array_copy.begin(), end = item_array_copy.end();
  2877. iter != end; ++iter)
  2878. {
  2879. LLViewerInventoryItem* item = *iter;
  2880. if (!item || !can_move_to_marketplace(item, error_msg, false))
  2881. {
  2882. return false;
  2883. }
  2884. }
  2885. LLInventoryModel::cat_array_t cat_array_copy = *cat_array;
  2886. for (LLInventoryModel::cat_array_t::iterator
  2887. iter = cat_array_copy.begin(), end = cat_array_copy.end();
  2888. iter != end; ++iter)
  2889. {
  2890. LLViewerInventoryCategory* category = *iter;
  2891. if (!category || !hasPermissionsForSale(category, error_msg))
  2892. {
  2893. return false;
  2894. }
  2895. }
  2896. return true;
  2897. }
  2898. // Returns true if inv_item can be dropped in dest_folder, a folder nested in
  2899. // Marketplace listings (or merchant inventory) under the root_folder root. If
  2900. // false is returned, tooltip_msg contains an error message to display to the
  2901. // user (localized and all). bundle_size is the amount of sibling items that
  2902. // are getting moved to the marketplace at the same time.
  2903. //static
  2904. bool LLMarketplace::canMoveItemInto(const LLViewerInventoryCategory* root_folder,
  2905. LLViewerInventoryCategory* dest_folder,
  2906. LLViewerInventoryItem* inv_item,
  2907. std::string& tooltip_msg,
  2908. S32 bundle_size, bool from_paste)
  2909. {
  2910. // Check stock folder type matches item type in marketplace listings or
  2911. // merchant outbox (even if of no use there for the moment)
  2912. bool move_in_stock =
  2913. dest_folder &&
  2914. dest_folder->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK;
  2915. bool accept = dest_folder && dest_folder->acceptItem(inv_item);
  2916. if (!accept)
  2917. {
  2918. tooltip_msg = LLTrans::getString("TooltipOutboxMixedStock");
  2919. }
  2920. // Check that the item has the right type and permissions to be sold on the
  2921. // marketplace
  2922. if (accept)
  2923. {
  2924. accept = can_move_to_marketplace(inv_item, tooltip_msg, true);
  2925. }
  2926. // Check that the total amount of items woould not violate the max limit on
  2927. // the marketplace
  2928. if (accept)
  2929. {
  2930. // If the dest folder is a stock folder, we do not count the incoming
  2931. // items toward the total (stock items are seen as one)
  2932. S32 existing_item_count = move_in_stock ? 0 : bundle_size;
  2933. // If the dest folder is a stock folder, we do assume that the incoming
  2934. // items are also stock items (they should anyway)
  2935. S32 existing_stock_count = move_in_stock ? bundle_size : 0;
  2936. S32 existing_folder_count = 0;
  2937. // Get the version folder: that's where the counts start from
  2938. const LLViewerInventoryCategory* version_folder = NULL;
  2939. if (root_folder && root_folder != dest_folder)
  2940. {
  2941. version_folder =
  2942. gInventory.getFirstDescendantOf(root_folder->getUUID(),
  2943. dest_folder->getUUID());
  2944. }
  2945. if (version_folder)
  2946. {
  2947. if (!from_paste &&
  2948. gInventory.isObjectDescendentOf(inv_item->getUUID(),
  2949. version_folder->getUUID()))
  2950. {
  2951. // Clear those counts or they will be counted twice because
  2952. // we are already inside the version category
  2953. existing_item_count = 0;
  2954. }
  2955. LLInventoryModel::cat_array_t existing_categories;
  2956. LLInventoryModel::item_array_t existing_items;
  2957. gInventory.collectDescendents(version_folder->getUUID(),
  2958. existing_categories, existing_items,
  2959. false);
  2960. existing_item_count += count_copyable_items(existing_items) +
  2961. count_stock_folders(existing_categories);
  2962. existing_stock_count += count_stock_items(existing_items);
  2963. existing_folder_count += existing_categories.size();
  2964. // If the incoming item is a nocopy (stock) item, we need to
  2965. // consider that it will create a stock folder
  2966. if (!move_in_stock &&
  2967. !inv_item->getPermissions().allowCopyBy(gAgentID,
  2968. gAgent.getGroupID()))
  2969. {
  2970. // Note: we do not assume that all incoming items are no-copy
  2971. // of different kinds...
  2972. ++existing_folder_count;
  2973. }
  2974. }
  2975. static LLCachedControl<U32> max_items(gSavedSettings,
  2976. "InventoryOutboxMaxItemCount");
  2977. static LLCachedControl<U32> max_stock(gSavedSettings,
  2978. "InventoryOutboxMaxStockItemCount");
  2979. static LLCachedControl<U32> max_folders(gSavedSettings,
  2980. "InventoryOutboxMaxFolderCount");
  2981. if (existing_item_count > (S32)max_items)
  2982. {
  2983. LLStringUtil::format_map_t args;
  2984. args["[AMOUNT]"] = llformat("%d", (S32)max_items);
  2985. tooltip_msg = LLTrans::getString("TooltipOutboxTooManyObjects",
  2986. args);
  2987. accept = false;
  2988. }
  2989. else if (existing_stock_count > (S32)max_stock)
  2990. {
  2991. LLStringUtil::format_map_t args;
  2992. args["[AMOUNT]"] = llformat("%d", (S32)max_stock);
  2993. tooltip_msg = LLTrans::getString("TooltipOutboxTooManyStockItems",
  2994. args);
  2995. accept = false;
  2996. }
  2997. else if (existing_folder_count > (S32)max_folders)
  2998. {
  2999. LLStringUtil::format_map_t args;
  3000. args["[AMOUNT]"] = llformat("%d", (S32)max_folders);
  3001. tooltip_msg = LLTrans::getString("TooltipOutboxTooManyFolders",
  3002. args);
  3003. accept = false;
  3004. }
  3005. }
  3006. return accept;
  3007. }
  3008. // Returns true if inv_cat can be dropped in dest_folder, a folder nested in
  3009. // marketplace listings (or merchant inventory) under the root_folder root.
  3010. // If returns is false, tooltip_msg contains an error message to display to the
  3011. // user (localized and all). bundle_size is the amount of sibling items that
  3012. // are getting moved to the marketplace at the same time.
  3013. //static
  3014. bool LLMarketplace::canMoveFolderInto(const LLViewerInventoryCategory* root_folder,
  3015. LLViewerInventoryCategory* dest_folder,
  3016. LLViewerInventoryCategory* inv_cat,
  3017. std::string& tooltip_msg,
  3018. S32 bundle_size, bool from_paste)
  3019. {
  3020. bool accept = true;
  3021. // Compute the nested folders level we will add into with that incoming
  3022. // folder
  3023. S32 incoming_folder_depth = get_folder_levels(inv_cat);
  3024. // Compute the nested folders level we are inserting ourselves in.
  3025. // Note: add 1 when inserting under a listing folder as we need to take the
  3026. // root listing folder in the count
  3027. S32 insertion_point = 1;
  3028. if (root_folder)
  3029. {
  3030. insertion_point = get_folder_path_length(root_folder->getUUID(),
  3031. dest_folder->getUUID()) + 1;
  3032. }
  3033. // Get the version folder: that's where the folders and items counts start
  3034. // from
  3035. const LLViewerInventoryCategory* version_folder = NULL;
  3036. if (insertion_point >= 2)
  3037. {
  3038. version_folder = gInventory.getFirstDescendantOf(root_folder->getUUID(),
  3039. dest_folder->getUUID());
  3040. }
  3041. // Compare the whole with the nested folders depth limit. Note: substract 2
  3042. // as we leave root and version folder out of the count threshold
  3043. U32 max_depth = gSavedSettings.getU32("InventoryOutboxMaxFolderDepth");
  3044. if (incoming_folder_depth + insertion_point - 2 > (S32)max_depth)
  3045. {
  3046. LLStringUtil::format_map_t args;
  3047. args["[AMOUNT]"] = llformat("%d", (S32)max_depth);
  3048. tooltip_msg = LLTrans::getString("TooltipOutboxFolderLevels", args);
  3049. accept = false;
  3050. }
  3051. if (accept)
  3052. {
  3053. LLInventoryModel::cat_array_t descendent_categories;
  3054. LLInventoryModel::item_array_t descendent_items;
  3055. gInventory.collectDescendents(inv_cat->getUUID(),
  3056. descendent_categories, descendent_items,
  3057. false);
  3058. // Note: we assume that we're moving a bunch of folders in. That might
  3059. // be wrong...
  3060. S32 dragged_folder_count = descendent_categories.size() + bundle_size;
  3061. S32 dragged_item_count = count_copyable_items(descendent_items) +
  3062. count_stock_folders(descendent_categories);
  3063. S32 dragged_stock_count = count_stock_items(descendent_items);
  3064. S32 existing_item_count = 0;
  3065. S32 existing_stock_count = 0;
  3066. S32 existing_folder_count = 0;
  3067. if (version_folder)
  3068. {
  3069. if (!from_paste &&
  3070. gInventory.isObjectDescendentOf(inv_cat->getUUID(),
  3071. version_folder->getUUID()))
  3072. {
  3073. // Clear those counts or they will be counted twice because
  3074. // we are already inside the version category
  3075. dragged_folder_count = 0;
  3076. dragged_item_count = 0;
  3077. dragged_stock_count = 0;
  3078. }
  3079. // Tally the total number of categories and items inside the root
  3080. // folder
  3081. LLInventoryModel::cat_array_t existing_categories;
  3082. LLInventoryModel::item_array_t existing_items;
  3083. gInventory.collectDescendents(version_folder->getUUID(),
  3084. existing_categories, existing_items,
  3085. false);
  3086. existing_folder_count += existing_categories.size();
  3087. existing_item_count += count_copyable_items(existing_items) +
  3088. count_stock_folders(existing_categories);
  3089. existing_stock_count += count_stock_items(existing_items);
  3090. }
  3091. const S32 total_folder_count = existing_folder_count + dragged_folder_count;
  3092. const S32 total_item_count = existing_item_count + dragged_item_count;
  3093. const S32 total_stock_count = existing_stock_count + dragged_stock_count;
  3094. static LLCachedControl<U32> max_items(gSavedSettings,
  3095. "InventoryOutboxMaxItemCount");
  3096. static LLCachedControl<U32> max_stock(gSavedSettings,
  3097. "InventoryOutboxMaxStockItemCount");
  3098. static LLCachedControl<U32> max_folders(gSavedSettings,
  3099. "InventoryOutboxMaxFolderCount");
  3100. if (total_folder_count > (S32)max_folders)
  3101. {
  3102. LLStringUtil::format_map_t args;
  3103. args["[AMOUNT]"] = llformat("%d", (S32)max_folders);
  3104. tooltip_msg = LLTrans::getString("TooltipOutboxTooManyFolders",
  3105. args);
  3106. accept = false;
  3107. }
  3108. else if (total_item_count > (S32)max_items)
  3109. {
  3110. LLStringUtil::format_map_t args;
  3111. args["[AMOUNT]"] = llformat("%d", (S32)max_items);
  3112. tooltip_msg = LLTrans::getString("TooltipOutboxTooManyObjects",
  3113. args);
  3114. accept = false;
  3115. }
  3116. else if (total_stock_count > (S32)max_stock)
  3117. {
  3118. LLStringUtil::format_map_t args;
  3119. args["[AMOUNT]"] = llformat("%d", (S32)max_stock);
  3120. tooltip_msg = LLTrans::getString("TooltipOutboxTooManyStockItems",
  3121. args);
  3122. accept = false;
  3123. }
  3124. // Now check that each item in the folder can be moved into the
  3125. // marketplace
  3126. if (accept)
  3127. {
  3128. for (S32 i = 0, count = descendent_items.size(); i < count; ++i)
  3129. {
  3130. LLViewerInventoryItem* item = descendent_items[i];
  3131. if (!can_move_to_marketplace(item, tooltip_msg))
  3132. {
  3133. accept = false;
  3134. break;
  3135. }
  3136. }
  3137. }
  3138. }
  3139. return accept;
  3140. }
  3141. //static
  3142. bool LLMarketplace::moveItemInto(LLViewerInventoryItem* inv_item,
  3143. const LLUUID& dest_folder, bool copy)
  3144. {
  3145. // Get the marketplace listings depth of the destination folder, exit with
  3146. // error if not under marketplace
  3147. S32 depth = depthNesting(dest_folder);
  3148. if (depth < 0)
  3149. {
  3150. LLSD subs;
  3151. subs["ERROR_CODE"] = LLTrans::getString("Marketplace Error Prefix") +
  3152. LLTrans::getString("Marketplace Error Not Merchant");
  3153. gNotifications.add("MerchantPasteFailed", subs);
  3154. return false;
  3155. }
  3156. // We will collapse links into items/folders
  3157. LLViewerInventoryItem* vitem = inv_item;
  3158. LLViewerInventoryCategory* linked_category = vitem->getLinkedCategory();
  3159. if (linked_category)
  3160. {
  3161. // Move the linked folder directly
  3162. return moveFolderInto(linked_category, dest_folder, copy);
  3163. }
  3164. // Grab the linked item if any
  3165. LLViewerInventoryItem* linked_item = vitem->getLinkedItem();
  3166. if (linked_item)
  3167. {
  3168. vitem = linked_item;
  3169. }
  3170. // If we want to copy but the item is no copy, fail silently (this is a
  3171. // common case that does not warrant notification)
  3172. if (copy &&
  3173. !vitem->getPermissions().allowCopyBy(gAgentID, gAgent.getGroupID()))
  3174. {
  3175. return false;
  3176. }
  3177. // Check that the agent has transfer permission on the item: this is
  3178. // required as a resident cannot put on sale items they cannot transfer.
  3179. // Proceed with move if we have permission.
  3180. std::string error_msg;
  3181. if (!can_move_to_marketplace(inv_item, error_msg, true))
  3182. {
  3183. LLSD subs;
  3184. subs["ERROR_CODE"] = LLTrans::getString("Marketplace Error Prefix") +
  3185. error_msg;
  3186. gNotifications.add("MerchantPasteFailed", subs);
  3187. return false;
  3188. }
  3189. LLUUID dest_id(dest_folder); // Destination id may change
  3190. // When moving an isolated item, we might need to create the folder
  3191. // structure to support it
  3192. if (depth == 0)
  3193. {
  3194. // We need a listing folder
  3195. dest_id = gInventory.createCategoryUDP(dest_id, LLFolderType::FT_NONE,
  3196. vitem->getName());
  3197. gInventory.notifyObservers();
  3198. ++depth;
  3199. }
  3200. if (depth == 1)
  3201. {
  3202. // We need a version folder
  3203. dest_id = gInventory.createCategoryUDP(dest_id, LLFolderType::FT_NONE,
  3204. vitem->getName());
  3205. gInventory.notifyObservers();
  3206. ++depth;
  3207. }
  3208. LLViewerInventoryCategory* dest_cat = gInventory.getCategory(dest_id);
  3209. if (!dest_cat)
  3210. {
  3211. llwarns << "Cannot find category for destination folder Id: "
  3212. << dest_id << llendl;
  3213. return false;
  3214. }
  3215. if (dest_cat->getPreferredType() != LLFolderType::FT_MARKETPLACE_STOCK &&
  3216. !vitem->getPermissions().allowCopyBy(gAgentID, gAgent.getGroupID()))
  3217. {
  3218. // We need to create a stock folder to move a no copy item
  3219. dest_id = gInventory.createCategoryUDP(dest_id,
  3220. LLFolderType::FT_MARKETPLACE_STOCK,
  3221. vitem->getName());
  3222. gInventory.notifyObservers();
  3223. dest_cat = gInventory.getCategory(dest_id);
  3224. ++depth;
  3225. }
  3226. // Verify we can have this item in that destination category
  3227. if (!dest_cat->acceptItem(vitem))
  3228. {
  3229. LLSD subs;
  3230. subs["ERROR_CODE"] = LLTrans::getString("Marketplace Error Prefix") +
  3231. LLTrans::getString("Marketplace Error Not Accepted");
  3232. gNotifications.add("MerchantPasteFailed", subs);
  3233. return false;
  3234. }
  3235. if (copy)
  3236. {
  3237. // Copy the item
  3238. LLPointer<LLInventoryCallback> cb =
  3239. new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb,
  3240. dest_id));
  3241. copy_inventory_item(vitem->getPermissions().getOwner(),
  3242. vitem->getUUID(), dest_id, LLStringUtil::null, cb);
  3243. }
  3244. else
  3245. {
  3246. // Reparent the item
  3247. gInventory.changeItemParent(vitem, dest_id, true);
  3248. gInventory.notifyObservers();
  3249. }
  3250. return true;
  3251. }
  3252. //static
  3253. bool LLMarketplace::moveFolderInto(LLViewerInventoryCategory* inv_cat,
  3254. const LLUUID& dest_folder, bool copy,
  3255. bool move_no_copy_items)
  3256. {
  3257. S32 depth = depthNesting(dest_folder);
  3258. if (depth < 0)
  3259. {
  3260. LLSD subs;
  3261. subs["ERROR_CODE"] = LLTrans::getString("Marketplace Error Prefix") +
  3262. LLTrans::getString("Marketplace Error Not Merchant");
  3263. gNotifications.add("MerchantPasteFailed", subs);
  3264. return false;
  3265. }
  3266. // Check that we have adequate permission on all items being moved. Proceed
  3267. // if we do.
  3268. std::string error_msg;
  3269. if (!hasPermissionsForSale(inv_cat, error_msg))
  3270. {
  3271. LLSD subs;
  3272. subs["ERROR_CODE"] = LLTrans::getString("Marketplace Error Prefix") +
  3273. error_msg;
  3274. gNotifications.add("MerchantPasteFailed", subs);
  3275. return false;
  3276. }
  3277. // Get the destination folder
  3278. LLViewerInventoryCategory* dest_cat = gInventory.getCategory(dest_folder);
  3279. if (!dest_cat)
  3280. {
  3281. llwarns << "Cannot find category for destination folder Id: "
  3282. << dest_folder << llendl;
  3283. return false;
  3284. }
  3285. // Check it's not a stock folder
  3286. if (dest_cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)
  3287. {
  3288. LLSD subs;
  3289. subs["ERROR_CODE"] = LLTrans::getString("Marketplace Error Prefix") +
  3290. LLTrans::getString("Marketplace Error Not Accepted");
  3291. gNotifications.add("MerchantPasteFailed", subs);
  3292. return false;
  3293. }
  3294. // Get the parent folder of the moved item: we may have to update it
  3295. const LLUUID& src_folder = inv_cat->getParentUUID();
  3296. LLUUID dest_id(dest_folder); // destination id may change
  3297. if (copy)
  3298. {
  3299. if (depth == 0)
  3300. {
  3301. // We need a listing folder
  3302. dest_id = gInventory.createCategoryUDP(dest_id,
  3303. LLFolderType::FT_NONE,
  3304. inv_cat->getName());
  3305. gInventory.notifyObservers();
  3306. ++depth;
  3307. }
  3308. // Copy the folder
  3309. copy_inventory_category(&gInventory, inv_cat, dest_id, LLUUID::null,
  3310. move_no_copy_items);
  3311. }
  3312. else
  3313. {
  3314. // Reparent the folder
  3315. gInventory.changeCategoryParent(inv_cat, dest_id, false);
  3316. gInventory.notifyObservers();
  3317. // Check the destination folder recursively for no copy items and
  3318. // promote the including folders if any
  3319. validateListings(dest_cat);
  3320. }
  3321. // Update the modified folders
  3322. updateCategory(src_folder);
  3323. gInventory.notifyObservers();
  3324. updateCategory(dest_id);
  3325. gInventory.notifyObservers();
  3326. return true;
  3327. }
  3328. //static
  3329. void LLMarketplace::updateMovedFrom(const LLUUID& from_folder_uuid,
  3330. const LLUUID& cat_id)
  3331. {
  3332. LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
  3333. if (from_folder_uuid == sMarketplaceListingId && cat_id.notNull())
  3334. {
  3335. // If we moved a folder at the listing folder level (i.e. its parent
  3336. // is the marketplace listings folder). Unlist it.
  3337. if (marketdata->isListed(cat_id))
  3338. {
  3339. marketdata->clearListing(cat_id);
  3340. }
  3341. }
  3342. else
  3343. {
  3344. #if 0 // Nope, does not work for forcing an inventory folder label update
  3345. // when moving a stock folder out of a version folder... HB
  3346. LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
  3347. if (cat && cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)
  3348. {
  3349. // If we moved a stock folder, flag all stock counts as dirty
  3350. marketdata->setDirtyCount();
  3351. }
  3352. #endif
  3353. LLUUID version_id = marketdata->getActiveFolder(from_folder_uuid);
  3354. if (version_id.notNull())
  3355. {
  3356. LLViewerInventoryCategory* cat = gInventory.getCategory(version_id);
  3357. if (cat && !validateListings(cat))
  3358. {
  3359. // If we move from an active (listed) listing, check that it is
  3360. // still valid, if not, unlist
  3361. marketdata->activateListing(version_id, false);
  3362. }
  3363. }
  3364. #if 0 // Nope, does not work for forcing an inventory folder label update
  3365. // when moving a stock folder out of a version folder... HB
  3366. version_id = marketdata->getVersionFolder(from_folder_uuid);
  3367. if (version_id.notNull())
  3368. {
  3369. LLViewerInventoryCategory* cat = gInventory.getCategory(version_id);
  3370. if (cat)
  3371. {
  3372. // Update the listing folder we moved from
  3373. updateCategory(cat->getParentUUID());
  3374. gInventory.notifyObservers();
  3375. return;
  3376. }
  3377. }
  3378. #endif
  3379. // Update the folder we moved from anyway
  3380. updateCategory(from_folder_uuid);
  3381. gInventory.notifyObservers();
  3382. }
  3383. }