From 4df30e26c68fa1b3a3b3122fbd529aea6b779a24 Mon Sep 17 00:00:00 2001
From: cinder <cinder@cinderblocks.biz>
Date: Fri, 2 Dec 2022 13:05:40 -0600
Subject: [PATCH] Plumbing for Local Inventory

---
 indra/llinventory/llfoldertype.cpp            |  1 +
 indra/llinventory/llfoldertype.h              |  2 +
 .../newview/app_settings/settings_alchemy.xml | 11 +++
 indra/newview/llinventorybridge.cpp           | 60 +++++++++++-
 indra/newview/llinventorymodel.cpp            | 91 +++++++++++++++++--
 indra/newview/llinventorymodel.h              |  2 +-
 indra/newview/llinventorypanel.cpp            |  1 +
 indra/newview/llstartup.cpp                   |  8 ++
 indra/newview/lltooldraganddrop.cpp           |  7 +-
 indra/newview/llviewerfoldertype.cpp          |  2 +
 indra/newview/llviewerinventory.cpp           | 16 ++++
 indra/newview/llviewerinventory.h             |  3 +
 .../newview/skins/default/xui/en/strings.xml  |  1 +
 13 files changed, 189 insertions(+), 16 deletions(-)

diff --git a/indra/llinventory/llfoldertype.cpp b/indra/llinventory/llfoldertype.cpp
index e47ed958e1d..3c20892f53a 100644
--- a/indra/llinventory/llfoldertype.cpp
+++ b/indra/llinventory/llfoldertype.cpp
@@ -127,6 +127,7 @@ LLFolderDictionary::LLFolderDictionary()
     addEntry(LLFolderType::FT_ANIM_OVERRIDES, 		new FolderEntry("animover", TRUE, FALSE, FALSE));
     
     addEntry(LLFolderType::FT_RLV,					new FolderEntry("rlv", 		TRUE, FALSE, FALSE));
+    addEntry(LLFolderType::FT_LOCAL,				new FolderEntry("local",	TRUE, FALSE, TRUE));
 
 	addEntry(LLFolderType::FT_NONE, 				new FolderEntry("-1",		FALSE, FALSE, FALSE));
 };
diff --git a/indra/llinventory/llfoldertype.h b/indra/llinventory/llfoldertype.h
index 600365c9ce3..fd4a7eced52 100644
--- a/indra/llinventory/llfoldertype.h
+++ b/indra/llinventory/llfoldertype.h
@@ -97,6 +97,8 @@ class LLFolderType
 		FT_TOXIC = 58,
 		FT_RLV = 59,
 
+		FT_LOCAL = 69,
+
 		FT_SUITCASE = 100,
 
 
diff --git a/indra/newview/app_settings/settings_alchemy.xml b/indra/newview/app_settings/settings_alchemy.xml
index 450f6c99615..21e728cac6c 100644
--- a/indra/newview/app_settings/settings_alchemy.xml
+++ b/indra/newview/app_settings/settings_alchemy.xml
@@ -749,6 +749,17 @@
       <string>String</string>
       <key>Value</key>
       <string />
+    </map>
+    <key>LocalInventoryEnabled</key>
+    <map>
+      <key>Comment</key>
+      <string>This enables the local inventory system. (Requires restart.)</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <integer>1</integer>
     </map>
 	<key>OpenSimSearchURL</key>
 	<map>
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index a408ab965ae..1137d0da82b 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -517,6 +517,8 @@ void  LLInvFVBridge::removeBatchNoCheck(std::vector<LLFolderViewModelItem*>&  ba
 			move_ids.push_back(item->getUUID());
 			--update[item->getParentUUID()];
 			++update[trash_id];
+			if(!gInventory.isObjectDescendentOf(item->getUUID(), gLocalInventory))
+			{
 			if(start_new_message)
 			{
 				start_new_message = false;
@@ -537,6 +539,7 @@ void  LLInvFVBridge::removeBatchNoCheck(std::vector<LLFolderViewModelItem*>&  ba
 				gInventory.accountForUpdate(update);
 				update.clear();
 			}
+			}
 		}
 	}
 	if(!start_new_message)
@@ -558,6 +561,8 @@ void  LLInvFVBridge::removeBatchNoCheck(std::vector<LLFolderViewModelItem*>&  ba
 			move_ids.push_back(cat->getUUID());
 			--update[cat->getParentUUID()];
 			++update[trash_id];
+			if(!gInventory.isObjectDescendentOf(cat->getUUID(), gLocalInventory))
+			{
 			if(start_new_message)
 			{
 				start_new_message = false;
@@ -577,6 +582,7 @@ void  LLInvFVBridge::removeBatchNoCheck(std::vector<LLFolderViewModelItem*>&  ba
 				gInventory.accountForUpdate(update);
 				update.clear();
 			}
+			}
 		}
 	}
 	if(!start_new_message)
@@ -590,12 +596,43 @@ void  LLInvFVBridge::removeBatchNoCheck(std::vector<LLFolderViewModelItem*>&  ba
 	uuid_vec_t::iterator end = move_ids.end();
 	for(; it != end; ++it)
 	{
+		if(gInventory.isObjectDescendentOf(*it, gLocalInventory))
+		{
+			// if it's a category, delete descendents
+			if(gInventory.getCategory(*it))
+			{
+				LLViewerInventoryCategory* cat = gInventory.getCategory(*it);
+				cat->setDescendentCount(0);
+				LLInventoryModel::cat_array_t categories;
+				LLInventoryModel::item_array_t items;
+				gInventory.collectDescendents(cat->getUUID(),
+								   categories,
+								   items,
+								   false); // include trash?
+				S32 count = items.size();
+				S32 i;
+				for(i = 0; i < count; ++i)
+				{
+					gInventory.deleteObject(items.at(i)->getUUID());
+				}
+				count = categories.size();
+				for(i = 0; i < count; ++i)
+				{
+					gInventory.deleteObject(categories.at(i)->getUUID());
+				}
+			}
+			// delete it
+			gInventory.deleteObject(*it);
+		}
+		else
+		{
 		gInventory.moveObject((*it), trash_id);
 		LLViewerInventoryItem* item = gInventory.getItem(*it);
 		if (item)
 		{
 			model->updateItem(item);
 		}
+		}
 	}
 
 	// notify inventory observers.
@@ -1840,7 +1877,7 @@ void copy_slurl_to_clipboard_callback_inv(const std::string& slurl)
 void LLItemBridge::selectItem()
 {
 	LLViewerInventoryItem* item = static_cast<LLViewerInventoryItem*>(getItem());
-	if(item && !item->isFinished())
+	if(item && !item->isFinished() && !(gInventory.isObjectDescendentOf(mUUID, gLocalInventory)))
 	{
 		//item->fetchFromServer();
 		LLInventoryModelBackgroundFetch::instance().start(item->getUUID(), false);
@@ -2110,6 +2147,17 @@ BOOL LLItemBridge::removeItem()
 	}
 	// Already in trash
 	if (model->isObjectDescendentOf(mUUID, trash_id)) return FALSE;
+	else
+	{
+		if(gInventory.isObjectDescendentOf(mUUID, gLocalInventory))
+		{
+			LLInventoryModel::LLCategoryUpdate up(item->getParentUUID(), -1);
+			gInventory.deleteObject(mUUID);
+			gInventory.accountForUpdate(up);
+			gInventory.notifyObservers();
+			return TRUE;
+		}
+	}
 
 	LLNotification::Params params("ConfirmItemDeleteHasLinks");
 	params.functor.function(boost::bind(&LLItemBridge::confirmRemoveItem, this, _1, _2));
@@ -2402,7 +2450,7 @@ BOOL LLFolderBridge::isUpToDate() const
 		return FALSE;
 	}
 
-	return category->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN;
+	return (category->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN) || (mUUID == gLocalInventory) || (gInventory.isObjectDescendentOf(mUUID, gLocalInventory));
 }
 
 BOOL LLFolderBridge::isItemCopyable() const
@@ -4362,8 +4410,12 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t&   items
 		{
 			// it's all on its way - add an observer, and the inventory will call done for us when everything is here.
 			gInventory.addObserver(fetch);
-        }
-    }
+	}
+	if (mUUID == gLocalInventory)
+	{
+		items.clear();
+	}
+}
 }
 
 void LLFolderBridge::buildContextMenuFolderOptions(U32 flags,   menuentry_vec_t& items, menuentry_vec_t& disabled_items)
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index c9978a402c7..d2039136c6e 100644
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -266,14 +266,33 @@ void LLInventoryModel::cleanupInventory()
 // This is a convenience function to check if one object has a parent
 // chain up to the category specified by UUID.
 BOOL LLInventoryModel::isObjectDescendentOf(const LLUUID& obj_id,
-											const LLUUID& cat_id) const
+											const LLUUID& cat_id,
+											const bool break_on_recursion) const
 {
 	if (obj_id == cat_id) return TRUE;
 
+	if (cat_id.isNull()) return FALSE;
+	U32 depthCounter = 0;
+
 	const LLInventoryObject* obj = getObject(obj_id);
 	while(obj)
 	{
 		const LLUUID& parent_id = obj->getParentUUID();
+		if(break_on_recursion)
+		{
+			if (depthCounter++ > 100)
+			{
+				LL_WARNS(LOG_INV) << "Possibly recursive parenting!" << LL_ENDL;
+				LL_INFOS(LOG_INV) << obj->getName() << " : " << obj->getUUID() << " : " << parent_id << LL_ENDL;
+				return FALSE;
+			}
+			if (parent_id == obj->getUUID())
+			{
+				// infinite loop... same thing as having no parent.
+				LL_WARNS(LOG_INV) << "Object has itself as parent! " << parent_id.asString() << ", " << obj->getName() << LL_ENDL;
+				return FALSE;
+			}
+		}
 		if( parent_id.isNull() )
 		{
 			return FALSE;
@@ -727,7 +746,7 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,
 		return id;
 	}
 
-	if (preferred_type != LLFolderType::FT_NONE)
+	if (preferred_type != LLFolderType::FT_NONE && preferred_type != LLFolderType::FT_LOCAL)
 	{
 		// Ultimately this should only be done for non-singleton
 		// types. Requires back-end changes to guarantee that others
@@ -745,7 +764,21 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,
 	{
 		name.assign(LLViewerFolderType::lookupNewCategoryName(preferred_type));
 	}
-	
+
+	// Local Inventory is local as such, finish up before server comms.
+    if (preferred_type == LLFolderType::FT_LOCAL || parent_id.isNull() || parent_id == gLocalInventory)
+    {
+        LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory(id, parent_id, preferred_type, name, gAgent.getID());
+        cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL - 1);  // accountForUpdate() will increase version by 1
+        cat->setDescendentCount(0);
+        LLCategoryUpdate update(cat->getParentUUID(), 1);
+        accountForUpdate(update);
+        updateCategory(cat);
+
+		return id;
+	}
+
+	// back to our regularly scheduled programming
 	LLViewerRegion* viewer_region = gAgent.getRegion();
 	std::string url;
 	if ( viewer_region )
@@ -1468,6 +1501,8 @@ void LLInventoryModel::changeItemParent(LLViewerInventoryItem* item,
 										const LLUUID& new_parent_id,
 										BOOL restamp)
 {
+	bool send_parent_update = gInventory.isObjectDescendentOf(item->getUUID(), gInventory.getRootFolderID());
+
 	if (item->getParentUUID() == new_parent_id)
 	{
 		LL_DEBUGS(LOG_INV) << "'" << item->getName() << "' (" << item->getUUID()
@@ -1487,7 +1522,10 @@ void LLInventoryModel::changeItemParent(LLViewerInventoryItem* item,
 
 		LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
 		new_item->setParent(new_parent_id);
-		new_item->updateParentOnServer(restamp);
+
+		if(send_parent_update) {
+			new_item->updateParentOnServer(restamp);
+		}
 		updateItem(new_item);
 		notifyObservers();
 	}
@@ -2280,6 +2318,8 @@ LLInventoryModel::EHasChildren LLInventoryModel::categoryHasChildren(
 
 bool LLInventoryModel::isCategoryComplete(const LLUUID& cat_id) const
 {
+	if((cat_id == gLocalInventory) || gInventory.isObjectDescendentOf(cat_id, gLocalInventory)) return true;
+
 	LLViewerInventoryCategory* cat = getCategory(cat_id);
 	if(cat && (cat->getVersion()!=LLViewerInventoryCategory::VERSION_UNKNOWN))
 	{
@@ -2525,7 +2565,7 @@ bool LLInventoryModel::loadSkeleton(
 		// reason (e.g. one of the descendents was a broken link).
 		for (cat_set_t::iterator invalid_cat_it = invalid_categories.begin();
 			 invalid_cat_it != invalid_categories.end();
-			 invalid_cat_it++)
+			 ++invalid_cat_it)
 		{
 			LLViewerInventoryCategory* cat = (*invalid_cat_it).get();
 			cat->setVersion(NO_VERSION);
@@ -3860,10 +3900,45 @@ void LLInventoryModel::removeCategory(const LLUUID& category_id)
 	LLViewerInventoryCategory* cat = getCategory(category_id);
 	if (cat)
 	{
-		const LLUUID trash_id = findCategoryUUIDForType(LLFolderType::FT_TRASH);
-		if (trash_id.notNull())
+		if(gInventory.isObjectDescendentOf(cat->getUUID(), gLocalInventory))
 		{
-			changeCategoryParent(cat, trash_id, TRUE);
+			S32 descendents = cat->getDescendentCount();
+			if(descendents > 0)
+			{
+				LLInventoryModel::LLCategoryUpdate up(cat->getUUID(), -descendents);
+				gInventory.accountForUpdate(up);
+			}
+			cat->setDescendentCount(0);
+			LLInventoryModel::cat_array_t categories;
+			LLInventoryModel::item_array_t items;
+			gInventory.collectDescendents(cat->getUUID(),
+			                              categories,
+			                              items,
+			                              false); // include trash?
+			S32 count = items.size();
+			S32 i;
+			for(i = 0; i < count; ++i)
+			{
+				gInventory.deleteObject(items.at(i)->getUUID());
+			}
+			count = categories.size();
+			for(i = 0; i < count; ++i)
+			{
+				gInventory.deleteObject(categories.at(i)->getUUID());
+			}
+
+			LLInventoryModel::LLCategoryUpdate up(cat->getParentUUID(), -descendents);
+			gInventory.deleteObject(cat->getUUID());
+			gInventory.accountForUpdate(up);
+			gInventory.notifyObservers();
+		}
+		else
+		{
+			const LLUUID& trash_id = findCategoryUUIDForType(LLFolderType::FT_TRASH);
+			if (trash_id.notNull())
+			{
+				changeCategoryParent(cat, trash_id, TRUE);
+			}
 		}
 	}
 
diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h
index c48e4a81d03..f8c9905ca61 100644
--- a/indra/newview/llinventorymodel.h
+++ b/indra/newview/llinventorymodel.h
@@ -295,7 +295,7 @@ class LLInventoryModel
 	item_array_t collectLinksTo(const LLUUID& item_id);
 
 	// Check if one object has a parent chain up to the category specified by UUID.
-	BOOL isObjectDescendentOf(const LLUUID& obj_id, const LLUUID& cat_id) const;
+	BOOL isObjectDescendentOf(const LLUUID& obj_id, const LLUUID& cat_id, const bool break_on_recursion = false) const;
     
     enum EAnscestorResult{
         ANSCESTOR_OK = 0,
diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp
index 761f2bf69b3..b2c33a0daa6 100644
--- a/indra/newview/llinventorypanel.cpp
+++ b/indra/newview/llinventorypanel.cpp
@@ -2157,5 +2157,6 @@ namespace LLInitParam
 		declare(LLFolderType::lookup(LLFolderType::FT_MARKETPLACE_STOCK), LLFolderType::FT_MARKETPLACE_STOCK);
 		declare(LLFolderType::lookup(LLFolderType::FT_MARKETPLACE_VERSION), LLFolderType::FT_MARKETPLACE_VERSION);
 		declare(LLFolderType::lookup(LLFolderType::FT_SUITCASE), LLFolderType::FT_SUITCASE);
+        declare(LLFolderType::lookup(LLFolderType::FT_LOCAL), LLFolderType::FT_LOCAL);
 	}
 }
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 89bddd44c6a..edad0e3e8e8 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -228,6 +228,8 @@ S32  gMaxAgentGroups;
 const std::string SCREEN_HOME_FILENAME = "screen_home%s.png";
 const std::string SCREEN_LAST_FILENAME = "screen_last%s.png";
 
+extern const char* const LOCAL_INVENTORY_FOLDER_NAME;
+
 LLPointer<LLViewerTexture> gStartTexture;
 
 //
@@ -1930,6 +1932,12 @@ bool idle_startup()
 		LL_INFOS() << "Requesting Agent Data" << LL_ENDL;
 		gAgent.sendAgentDataUpdateRequest();
 		display_startup();
+		if (gSavedSettings.getBOOL("LocalInventoryEnabled"))
+ 		{
+			gLocalInventory = gInventory.createNewCategory(LLUUID::null,
+														   LLFolderType::FT_LOCAL, 
+				                                           LOCAL_INVENTORY_FOLDER_NAME);
+ 		}
 		// Create the inventory views
 		LL_INFOS() << "Creating Inventory Views" << LL_ENDL;
 		LLFloaterReg::getInstance("inventory");
diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp
index 83d54fe0971..20b4fc2b497 100644
--- a/indra/newview/lltooldraganddrop.cpp
+++ b/indra/newview/lltooldraganddrop.cpp
@@ -1481,7 +1481,8 @@ EAcceptance LLToolDragAndDrop::willObjectAcceptInventory(LLViewerObject* obj, LL
 	if (!item || !obj) return ACCEPT_NO;
 	// HACK: downcast
 	LLViewerInventoryItem* vitem = (LLViewerInventoryItem*)item;
-	if (!vitem->isFinished() && (type != DAD_CATEGORY))
+	if (!vitem->isFinished() && (type != DAD_CATEGORY)
+	&& !(gInventory.isObjectDescendentOf(vitem->getUUID(), gLocalInventory)))
 	{
 		// Note: for DAD_CATEGORY we assume that folder version check passed and folder 
 		// is complete, meaning that items inside are up to date. 
@@ -1996,7 +1997,7 @@ EAcceptance LLToolDragAndDrop::dad3dRezScript(
 	LLViewerInventoryItem* item;
 	LLViewerInventoryCategory* cat;
 	locateInventory(item, cat);
-	if (!item || !item->isFinished()) return ACCEPT_NO;
+	if (!item || (!item->isFinished() && !(gInventory.isObjectDescendentOf(item->getUUID(), gLocalInventory)))) return ACCEPT_NO;
 	EAcceptance rv = willObjectAcceptInventory(obj, item);
 	if(drop && (ACCEPT_YES_SINGLE <= rv))
 	{
@@ -2034,7 +2035,7 @@ EAcceptance LLToolDragAndDrop::dad3dApplyToObject(
 	LLViewerInventoryItem* item;
 	LLViewerInventoryCategory* cat;
 	locateInventory(item, cat);
-	if (!item || !item->isFinished()) return ACCEPT_NO;
+	if( !item || (!item->isFinished() && !(gInventory.isObjectDescendentOf(item->getUUID(), gLocalInventory))) ) return ACCEPT_NO;
 	EAcceptance rv = willObjectAcceptInventory(obj, item);
 	if((mask & MASK_CONTROL))
 	{
diff --git a/indra/newview/llviewerfoldertype.cpp b/indra/newview/llviewerfoldertype.cpp
index 44ac1a99ce3..908efd51f11 100644
--- a/indra/newview/llviewerfoldertype.cpp
+++ b/indra/newview/llviewerfoldertype.cpp
@@ -150,6 +150,8 @@ LLViewerFolderDictionary::LLViewerFolderDictionary()
 	addEntry(LLFolderType::FT_NONE, 				new ViewerFolderEntry("New Folder",				"Inv_FolderOpen",		"Inv_FolderClosed",		FALSE,     false, "default"));
 	addEntry(LLFolderType::FT_TOXIC, 			new ViewerFolderEntry("Firstorm",				"Inv_FolderOpenToxic",		"Inv_FolderClosedToxic",		FALSE,     false));
 	addEntry(LLFolderType::FT_RLV, 					new ViewerFolderEntry("RlvRoot",				"Inv_SysOpen",			"Inv_SysClosed",		FALSE,     false));
+    addEntry(LLFolderType::FT_LOCAL,				new ViewerFolderEntry("Local Inventory",		"Inv_SysOpen",		"Inv_SysClosed",		FALSE, true));
+
 
 	for (U32 type = (U32)LLFolderType::FT_ENSEMBLE_START; type <= (U32)LLFolderType::FT_ENSEMBLE_END; ++type)
 	{
diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp
index 565b9620765..f174835eccd 100644
--- a/indra/newview/llviewerinventory.cpp
+++ b/indra/newview/llviewerinventory.cpp
@@ -86,6 +86,9 @@ static const char * const LOG_NOTECARD("copy_inventory_from_notecard");
 static const std::string INV_OWNER_ID("owner_id");
 static const std::string INV_VERSION("version");
 
+LLUUID gLocalInventory;
+const char* const LOCAL_INVENTORY_FOLDER_NAME("Local Inventory");
+
 #if 1
 // *TODO$: LLInventoryCallback should be deprecated to conform to the new boost::bind/coroutine model.
 // temp code in transition
@@ -184,6 +187,8 @@ LLLocalizedInventoryItemsDictionary::LLLocalizedInventoryItemsDictionary()
 	mInventoryItemsDict["Speech Gestures"]	= LLTrans::getString("Speech Gestures");
 	mInventoryItemsDict["Common Gestures"]	= LLTrans::getString("Common Gestures");
 
+	mInventoryItemsDict[LOCAL_INVENTORY_FOLDER_NAME] = LLTrans::getString(LOCAL_INVENTORY_FOLDER_NAME);
+
 	//predefined gestures
 
 	//male
@@ -418,6 +423,11 @@ void LLViewerInventoryItem::updateServer(BOOL is_new) const
 						 << LL_ENDL;
 		return;
 	}
+
+	if((mParentUUID == gLocalInventory) || (gInventory.isObjectDescendentOf(mUUID, gLocalInventory)))
+	{
+		return;
+	}
 	if(gAgent.getID() != mPermissions.getOwner())
 	{
 		// *FIX: deal with this better.
@@ -572,6 +582,8 @@ BOOL LLViewerInventoryItem::importLegacyStream(std::istream& input_stream)
 
 void LLViewerInventoryItem::updateParentOnServer(BOOL restamp) const
 {
+	if (gInventory.isObjectDescendentOf(mUUID, gLocalInventory)) return;
+
 	LLMessageSystem* msg = gMessageSystem;
 	msg->newMessageFast(_PREHASH_MoveInventoryItem);
 	msg->nextBlockFast(_PREHASH_AgentData);
@@ -650,6 +662,8 @@ void LLViewerInventoryCategory::packMessage(LLMessageSystem* msg) const
 
 void LLViewerInventoryCategory::updateParentOnServer(BOOL restamp) const
 {
+	if(gInventory.isObjectDescendentOf(mUUID, gLocalInventory)) return;
+
 	LLMessageSystem* msg = gMessageSystem;
 	msg->newMessageFast(_PREHASH_MoveInventoryFolder);
 	msg->nextBlockFast(_PREHASH_AgentData);
@@ -707,6 +721,8 @@ void LLViewerInventoryCategory::setVersion(S32 version)
 
 bool LLViewerInventoryCategory::fetch()
 {
+	if((mUUID == gLocalInventory) || (gInventory.isObjectDescendentOf(mUUID, gLocalInventory))) return false;
+
 	if((VERSION_UNKNOWN == getVersion())
 	   && mDescendentsRequested.hasExpired())	//Expired check prevents multiple downloads.
 	{
diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h
index 322df060250..01081977f39 100644
--- a/indra/newview/llviewerinventory.h
+++ b/indra/newview/llviewerinventory.h
@@ -42,6 +42,9 @@ class LLViewerInventoryCategory;
 class LLInventoryCallback;
 class LLAvatarName;
 
+extern LLUUID gLocalInventory;
+extern const char* const LOCAL_INVENTORY_FOLDER_NAME;
+
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Class LLViewerInventoryItem
 //
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index 46eb2ca80b1..d261ce670a3 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -3872,6 +3872,7 @@ Abuse Report</string>
   <string name="Common Gestures">Common Gestures</string>
   <!-- Additional inventory entries -->
   <string name="RlvRoot">#RLV</string>
+  <string name="Local Inventory">Local Inventory</string>
   <!-- gestures -->
   <string name="Male - Excuse me">Male - Excuse me</string>
   <string name="Male - Get lost">Male - Get lost</string>
-- 
GitLab