SH-4137 WIP - added callback-based support for purge descendents, remove category

master
Brad Payne (Vir Linden) 2013-04-25 17:09:05 -04:00
parent e60cb90b63
commit d843a0d8be
8 changed files with 321 additions and 229 deletions

View File

@ -249,13 +249,6 @@ BOOL LLInventoryObject::exportLegacyStream(std::ostream& output_stream, BOOL) co
return TRUE;
}
void LLInventoryObject::removeFromServer()
{
// don't do nothin'
llwarns << "LLInventoryObject::removeFromServer() called. Doesn't do anything." << llendl;
}
void LLInventoryObject::updateParentOnServer(BOOL) const
{
// don't do nothin'

View File

@ -101,7 +101,6 @@ public:
virtual BOOL importLegacyStream(std::istream& input_stream);
virtual BOOL exportLegacyStream(std::ostream& output_stream, BOOL include_asset_key = TRUE) const;
virtual void removeFromServer();
virtual void updateParentOnServer(BOOL) const;
virtual void updateServer(BOOL) const;

View File

@ -1159,17 +1159,10 @@ LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type,
void LLInvFVBridge::purgeItem(LLInventoryModel *model, const LLUUID &uuid)
{
LLInventoryCategory* cat = model->getCategory(uuid);
if (cat)
{
model->purgeDescendentsOf(uuid);
model->notifyObservers();
}
LLInventoryObject* obj = model->getObject(uuid);
if (obj)
{
model->purgeObject(uuid);
model->notifyObservers();
remove_inventory_object(uuid, NULL);
}
}

View File

@ -1136,6 +1136,79 @@ void LLInventoryModel::changeCategoryParent(LLViewerInventoryCategory* cat,
notifyObservers();
}
// Update model after descendents have been purged.
void LLInventoryModel::onDescendentsPurgedFromServer(const LLUUID& object_id)
{
LLPointer<LLViewerInventoryCategory> cat = getCategory(object_id);
if (cat.notNull())
{
// do the cache accounting
S32 descendents = cat->getDescendentCount();
if(descendents > 0)
{
LLInventoryModel::LLCategoryUpdate up(object_id, -descendents);
accountForUpdate(up);
}
// we know that descendent count is 0, however since the
// accounting may actually not do an update, we should force
// it here.
cat->setDescendentCount(0);
// unceremoniously remove anything we have locally stored.
LLInventoryModel::cat_array_t categories;
LLInventoryModel::item_array_t items;
collectDescendents(object_id,
categories,
items,
LLInventoryModel::INCLUDE_TRASH);
S32 count = items.count();
LLUUID uu_id;
for(S32 i = 0; i < count; ++i)
{
uu_id = items.get(i)->getUUID();
// This check prevents the deletion of a previously deleted item.
// This is necessary because deletion is not done in a hierarchical
// order. The current item may have been already deleted as a child
// of its deleted parent.
if (getItem(uu_id))
{
deleteObject(uu_id);
}
}
count = categories.count();
for(S32 i = 0; i < count; ++i)
{
uu_id = categories.get(i)->getUUID();
if (getCategory(uu_id))
{
deleteObject(uu_id);
}
}
}
}
// Update model after an item is confirmed as removed from
// server. Works for categories or items.
void LLInventoryModel::onObjectDeletedFromServer(const LLUUID& object_id)
{
LLPointer<LLInventoryObject> obj = getObject(object_id);
if(obj)
{
// From item/cat removeFromServer()
LLInventoryModel::LLCategoryUpdate up(obj->getParentUUID(), -1);
accountForUpdate(up);
// From purgeObject()
LLPreview::hide(object_id);
deleteObject(object_id);
}
}
// Delete a particular inventory object by ID.
void LLInventoryModel::deleteObject(const LLUUID& id)
{
@ -1187,20 +1260,7 @@ void LLInventoryModel::deleteObject(const LLUUID& id)
{
updateLinkedObjectsFromPurge(id);
}
gInventory.notifyObservers();
}
// Delete a particular inventory item by ID, and remove it from the server.
void LLInventoryModel::purgeObject(const LLUUID &id)
{
lldebugs << "LLInventoryModel::purgeObject() [ id: " << id << " ] " << llendl;
LLPointer<LLInventoryObject> obj = getObject(id);
if(obj)
{
obj->removeFromServer();
LLPreview::hide(id);
deleteObject(id);
}
notifyObservers();
}
void LLInventoryModel::updateLinkedObjectsFromPurge(const LLUUID &baseobj_id)
@ -1225,119 +1285,6 @@ void LLInventoryModel::updateLinkedObjectsFromPurge(const LLUUID &baseobj_id)
}
}
// This is a method which collects the descendents of the id
// provided. If the category is not found, no action is
// taken. This method goes through the long winded process of
// cancelling any calling cards, removing server representation of
// folders, items, etc in a fairly efficient manner.
void LLInventoryModel::purgeDescendentsOf(const LLUUID& id)
{
EHasChildren children = categoryHasChildren(id);
if(children == CHILDREN_NO)
{
llinfos << "Not purging descendents of " << id << llendl;
return;
}
LLPointer<LLViewerInventoryCategory> cat = getCategory(id);
if (cat.notNull())
{
if (LLClipboard::instance().hasContents() && LLClipboard::instance().isCutMode())
{
// Something on the clipboard is in "cut mode" and needs to be preserved
llinfos << "LLInventoryModel::purgeDescendentsOf " << cat->getName()
<< " iterate and purge non hidden items" << llendl;
cat_array_t* categories;
item_array_t* items;
// Get the list of direct descendants in tha categoy passed as argument
getDirectDescendentsOf(id, categories, items);
std::vector<LLUUID> list_uuids;
// Make a unique list with all the UUIDs of the direct descendants (items and categories are not treated differently)
// Note: we need to do that shallow copy as purging things will invalidate the categories or items lists
for (cat_array_t::const_iterator it = categories->begin(); it != categories->end(); ++it)
{
list_uuids.push_back((*it)->getUUID());
}
for (item_array_t::const_iterator it = items->begin(); it != items->end(); ++it)
{
list_uuids.push_back((*it)->getUUID());
}
// Iterate through the list and only purge the UUIDs that are not on the clipboard
for (std::vector<LLUUID>::const_iterator it = list_uuids.begin(); it != list_uuids.end(); ++it)
{
if (!LLClipboard::instance().isOnClipboard(*it))
{
purgeObject(*it);
}
}
}
else
{
// Fast purge
// do the cache accounting
llinfos << "LLInventoryModel::purgeDescendentsOf " << cat->getName()
<< llendl;
S32 descendents = cat->getDescendentCount();
if(descendents > 0)
{
LLCategoryUpdate up(id, -descendents);
accountForUpdate(up);
}
// we know that descendent count is 0, however since the
// accounting may actually not do an update, we should force
// it here.
cat->setDescendentCount(0);
// send it upstream
LLMessageSystem* msg = gMessageSystem;
msg->newMessage("PurgeInventoryDescendents");
msg->nextBlock("AgentData");
msg->addUUID("AgentID", gAgent.getID());
msg->addUUID("SessionID", gAgent.getSessionID());
msg->nextBlock("InventoryData");
msg->addUUID("FolderID", id);
gAgent.sendReliableMessage();
// unceremoniously remove anything we have locally stored.
cat_array_t categories;
item_array_t items;
collectDescendents(id,
categories,
items,
INCLUDE_TRASH);
S32 count = items.count();
item_map_t::iterator item_map_end = mItemMap.end();
cat_map_t::iterator cat_map_end = mCategoryMap.end();
LLUUID uu_id;
for(S32 i = 0; i < count; ++i)
{
uu_id = items.get(i)->getUUID();
// This check prevents the deletion of a previously deleted item.
// This is necessary because deletion is not done in a hierarchical
// order. The current item may have been already deleted as a child
// of its deleted parent.
if (mItemMap.find(uu_id) != item_map_end)
{
deleteObject(uu_id);
}
}
count = categories.count();
for(S32 i = 0; i < count; ++i)
{
uu_id = categories.get(i)->getUUID();
if (mCategoryMap.find(uu_id) != cat_map_end)
{
deleteObject(uu_id);
}
}
}
}
}
// Add/remove an observer. If the observer is destroyed, be sure to
// remove it.
void LLInventoryModel::addObserver(LLInventoryObserver* observer)
@ -3114,8 +3061,7 @@ bool LLInventoryModel::callbackEmptyFolderType(const LLSD& notification, const L
if (option == 0) // YES
{
const LLUUID folder_id = findCategoryUUIDForType(preferred_type);
purgeDescendentsOf(folder_id);
notifyObservers();
purge_descendents_of(folder_id, NULL);
}
return false;
}
@ -3130,8 +3076,7 @@ void LLInventoryModel::emptyFolderType(const std::string notification, LLFolderT
else
{
const LLUUID folder_id = findCategoryUUIDForType(preferred_type);
purgeDescendentsOf(folder_id);
notifyObservers();
purge_descendents_of(folder_id, NULL);
}
}

View File

@ -325,6 +325,14 @@ public:
// Delete
//--------------------------------------------------------------------
public:
// Update model after an item is confirmed as removed from
// server. Works for categories or items.
void onObjectDeletedFromServer(const LLUUID& item_id);
// Update model after all descendents removed from server.
void onDescendentsPurgedFromServer(const LLUUID& object_id);
// Delete a particular inventory object by ID. Will purge one
// object from the internal data structures, maintaining a
// consistent internal state. No cache accounting, observer
@ -337,17 +345,6 @@ public:
/// removeItem() or removeCategory(), whichever is appropriate
void removeObject(const LLUUID& object_id);
// Delete a particular inventory object by ID, and delete it from
// the server. Also updates linked items.
void purgeObject(const LLUUID& id);
// Collects and purges the descendants of the id
// provided. If the category is not found, no action is
// taken. This method goes through the long winded process of
// removing server representation of folders and items while doing
// cache accounting in a fairly efficient manner. This method does
// not notify observers (though maybe it should...)
void purgeDescendentsOf(const LLUUID& id);
protected:
void updateLinkedObjectsFromPurge(const LLUUID& baseobj_id);

View File

@ -401,13 +401,6 @@ void LLPreview::onDiscardBtn(void* data)
self->mForceClose = TRUE;
self->closeFloater();
// Delete the item entirely
/*
item->removeFromServer();
gInventory.deleteObject(item->getUUID());
gInventory.notifyObservers();
*/
// Move the item to the trash
const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
if (item->getParentUUID() != trash_id)

View File

@ -65,6 +65,7 @@
#include "llavataractions.h"
#include "lllogininstance.h"
#include "llfavoritesbar.h"
#include "llclipboard.h"
// Two do-nothing ops for use in callbacks.
void no_op_inventory_func(const LLUUID&) {}
@ -345,24 +346,6 @@ void LLViewerInventoryItem::cloneViewerItem(LLPointer<LLViewerInventoryItem>& ne
}
}
void LLViewerInventoryItem::removeFromServer()
{
lldebugs << "Removing inventory item " << mUUID << " from server."
<< llendl;
LLInventoryModel::LLCategoryUpdate up(mParentUUID, -1);
gInventory.accountForUpdate(up);
LLMessageSystem* msg = gMessageSystem;
msg->newMessageFast(_PREHASH_RemoveInventoryItem);
msg->nextBlockFast(_PREHASH_AgentData);
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
msg->nextBlockFast(_PREHASH_InventoryData);
msg->addUUIDFast(_PREHASH_ItemID, mUUID);
gAgent.sendReliableMessage();
}
void LLViewerInventoryItem::updateServer(BOOL is_new) const
{
if(!mIsComplete)
@ -637,30 +620,6 @@ void LLViewerInventoryCategory::updateServer(BOOL is_new) const
gAgent.sendReliableMessage();
}
void LLViewerInventoryCategory::removeFromServer( void )
{
llinfos << "Removing inventory category " << mUUID << " from server."
<< llendl;
// communicate that change with the server.
if(LLFolderType::lookupIsProtectedType(mPreferredType))
{
LLNotificationsUtil::add("CannotRemoveProtectedCategories");
return;
}
LLInventoryModel::LLCategoryUpdate up(mParentUUID, -1);
gInventory.accountForUpdate(up);
LLMessageSystem* msg = gMessageSystem;
msg->newMessageFast(_PREHASH_RemoveInventoryFolder);
msg->nextBlockFast(_PREHASH_AgentData);
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
msg->nextBlockFast(_PREHASH_FolderData);
msg->addUUIDFast(_PREHASH_FolderID, mUUID);
gAgent.sendReliableMessage();
}
S32 LLViewerInventoryCategory::getVersion() const
{
return mVersion;
@ -1179,25 +1138,10 @@ void move_inventory_item(
gAgent.sendReliableMessage();
}
void handle_item_deletion(const LLUUID& item_id)
{
LLPointer<LLViewerInventoryItem> obj = gInventory.getItem(item_id);
if(obj)
{
// From item removeFromServer()
LLInventoryModel::LLCategoryUpdate up(obj->getParentUUID(), -1);
gInventory.accountForUpdate(up);
// From purgeObject()
LLPreview::hide(item_id);
gInventory.deleteObject(item_id);
}
}
class RemoveItemResponder: public LLHTTPClient::Responder
class RemoveObjectResponder: public LLHTTPClient::Responder
{
public:
RemoveItemResponder(const LLUUID& item_id, LLPointer<LLInventoryCallback> callback):
RemoveObjectResponder(const LLUUID& item_id, LLPointer<LLInventoryCallback> callback):
mItemUUID(item_id),
mCallback(callback)
{
@ -1212,7 +1156,7 @@ public:
}
llinfos << "succeeded: " << ll_pretty_print_sd(content) << llendl;
handle_item_deletion(mItemUUID);
gInventory.onObjectDeletedFromServer(mItemUUID);
if (mCallback)
{
@ -1251,7 +1195,7 @@ void remove_inventory_item(
{
std::string url = cap + std::string("/item/") + item_id.asString();
llinfos << "url: " << url << llendl;
LLCurl::ResponderPtr responder_ptr = new RemoveItemResponder(item_id,cb);
LLCurl::ResponderPtr responder_ptr = new RemoveObjectResponder(item_id,cb);
LLHTTPClient::del(url,responder_ptr);
}
else // no cap
@ -1267,7 +1211,7 @@ void remove_inventory_item(
// Update inventory and call callback immediately since
// message-based system has no callback mechanism (!)
handle_item_deletion(item_id);
gInventory.onObjectDeletedFromServer(item_id);
if (cb)
{
cb->fire(item_id);
@ -1280,6 +1224,224 @@ void remove_inventory_item(
}
}
class LLRemoveObjectOnDestroy: public LLInventoryCallback
{
public:
LLRemoveObjectOnDestroy(const LLUUID& item_id, LLPointer<LLInventoryCallback> cb):
mID(item_id),
mCB(cb)
{
}
/* virtual */ void fire(const LLUUID& item_id) {}
~LLRemoveObjectOnDestroy()
{
remove_inventory_object(mID, mCB);
}
private:
LLUUID mID;
LLPointer<LLInventoryCallback> mCB;
};
void remove_inventory_category(
const LLUUID& cat_id,
LLPointer<LLInventoryCallback> cb)
{
llinfos << "cat_id: [" << cat_id << "] " << llendl;
LLPointer<LLViewerInventoryCategory> obj = gInventory.getCategory(cat_id);
if(obj)
{
if(LLFolderType::lookupIsProtectedType(obj->getPreferredType()))
{
LLNotificationsUtil::add("CannotRemoveProtectedCategories");
return;
}
LLInventoryModel::EHasChildren children = gInventory.categoryHasChildren(cat_id);
if(children != LLInventoryModel::CHILDREN_NO)
{
llinfos << "Will purge descendents first before deleting category " << cat_id << llendl;
LLPointer<LLInventoryCallback> wrap_cb = new LLRemoveObjectOnDestroy(cat_id,cb);
purge_descendents_of(cat_id, wrap_cb);
return;
}
std::string cap;
if (gAgent.getRegion())
{
cap = gAgent.getRegion()->getCapability("InventoryAPIv3");
}
if (!cap.empty())
{
std::string url = cap + std::string("/category/") + cat_id.asString();
llinfos << "url: " << url << llendl;
LLCurl::ResponderPtr responder_ptr = new RemoveObjectResponder(cat_id,cb);
LLHTTPClient::del(url,responder_ptr);
}
else // no cap
{
LLMessageSystem* msg = gMessageSystem;
msg->newMessageFast(_PREHASH_RemoveInventoryFolder);
msg->nextBlockFast(_PREHASH_AgentData);
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
msg->nextBlockFast(_PREHASH_FolderData);
msg->addUUIDFast(_PREHASH_FolderID, cat_id);
gAgent.sendReliableMessage();
// Update inventory and call callback immediately since
// message-based system has no callback mechanism (!)
gInventory.onObjectDeletedFromServer(cat_id);
if (cb)
{
cb->fire(cat_id);
}
}
}
else
{
llwarns << "remove_inventory_category called for invalid or nonexistent item " << cat_id << llendl;
}
}
void remove_inventory_object(
const LLUUID& object_id,
LLPointer<LLInventoryCallback> cb)
{
if (gInventory.getCategory(object_id))
{
remove_inventory_category(object_id, cb);
}
else
{
remove_inventory_item(object_id, cb);
}
}
class PurgeDescendentsResponder: public LLHTTPClient::Responder
{
public:
PurgeDescendentsResponder(const LLUUID& item_id, LLPointer<LLInventoryCallback> callback):
mItemUUID(item_id),
mCallback(callback)
{
}
/* virtual */ void httpSuccess()
{
const LLSD& content = getContent();
if (!content.isMap())
{
failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content);
return;
}
llinfos << "succeeded: " << ll_pretty_print_sd(content) << llendl;
gInventory.onDescendentsPurgedFromServer(mItemUUID);
if (mCallback)
{
mCallback->fire(mItemUUID);
}
}
/*virtual*/ void httpFailure()
{
const LLSD& content = getContent();
if (!content.isMap())
{
failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content);
return;
}
llwarns << "failed for " << mItemUUID << " content: " << ll_pretty_print_sd(content) << llendl;
}
private:
LLPointer<LLInventoryCallback> mCallback;
const LLUUID mItemUUID;
};
// This is a method which collects the descendents of the id
// provided. If the category is not found, no action is
// taken. This method goes through the long winded process of
// cancelling any calling cards, removing server representation of
// folders, items, etc in a fairly efficient manner.
void purge_descendents_of(const LLUUID& id, LLPointer<LLInventoryCallback> cb)
{
LLInventoryModel::EHasChildren children = gInventory.categoryHasChildren(id);
if(children == LLInventoryModel::CHILDREN_NO)
{
llinfos << "No descendents to purge for " << id << llendl;
return;
}
LLPointer<LLViewerInventoryCategory> cat = gInventory.getCategory(id);
if (cat.notNull())
{
if (LLClipboard::instance().hasContents() && LLClipboard::instance().isCutMode())
{
// Something on the clipboard is in "cut mode" and needs to be preserved
llinfos << "purge_descendents_of clipboard case " << cat->getName()
<< " iterate and purge non hidden items" << llendl;
LLInventoryModel::cat_array_t* categories;
LLInventoryModel::item_array_t* items;
// Get the list of direct descendants in tha categoy passed as argument
gInventory.getDirectDescendentsOf(id, categories, items);
std::vector<LLUUID> list_uuids;
// Make a unique list with all the UUIDs of the direct descendants (items and categories are not treated differently)
// Note: we need to do that shallow copy as purging things will invalidate the categories or items lists
for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin(); it != categories->end(); ++it)
{
list_uuids.push_back((*it)->getUUID());
}
for (LLInventoryModel::item_array_t::const_iterator it = items->begin(); it != items->end(); ++it)
{
list_uuids.push_back((*it)->getUUID());
}
// Iterate through the list and only purge the UUIDs that are not on the clipboard
for (std::vector<LLUUID>::const_iterator it = list_uuids.begin(); it != list_uuids.end(); ++it)
{
if (!LLClipboard::instance().isOnClipboard(*it))
{
remove_inventory_object(*it, NULL);
}
}
}
else
{
std::string cap;
if (gAgent.getRegion())
{
cap = gAgent.getRegion()->getCapability("InventoryAPIv3");
}
if (!cap.empty())
{
std::string url = cap + std::string("/category/") + id.asString() + "/children";
llinfos << "url: " << url << llendl;
LLCurl::ResponderPtr responder_ptr = new PurgeDescendentsResponder(id,cb);
LLHTTPClient::del(url,responder_ptr);
}
else // no cap
{
// Fast purge
llinfos << "purge_descendents_of fast case " << cat->getName() << llendl;
// send it upstream
LLMessageSystem* msg = gMessageSystem;
msg->newMessage("PurgeInventoryDescendents");
msg->nextBlock("AgentData");
msg->addUUID("AgentID", gAgent.getID());
msg->addUUID("SessionID", gAgent.getSessionID());
msg->nextBlock("InventoryData");
msg->addUUID("FolderID", id);
gAgent.sendReliableMessage();
// Update model immediately because there is no callback mechanism.
gInventory.onDescendentsPurgedFromServer(id);
if (cb)
{
cb->fire(id);
}
}
}
}
}
const LLUUID get_folder_by_itemtype(const LLInventoryItem *src)
{
LLUUID retval = LLUUID::null;

View File

@ -118,7 +118,6 @@ public:
void cloneViewerItem(LLPointer<LLViewerInventoryItem>& newitem) const;
// virtual methods
virtual void removeFromServer( void );
virtual void updateParentOnServer(BOOL restamp) const;
virtual void updateServer(BOOL is_new) const;
void fetchFromServer(void) const;
@ -198,7 +197,6 @@ public:
LLViewerInventoryCategory(const LLViewerInventoryCategory* other);
void copyViewerCategory(const LLViewerInventoryCategory* other);
virtual void removeFromServer();
virtual void updateParentOnServer(BOOL restamp_children) const;
virtual void updateServer(BOOL is_new) const;
@ -369,6 +367,18 @@ void remove_inventory_item(
const LLUUID& item_id,
LLPointer<LLInventoryCallback> cb);
void remove_inventory_category(
const LLUUID& cat_id,
LLPointer<LLInventoryCallback> cb);
void remove_inventory_object(
const LLUUID& object_id,
LLPointer<LLInventoryCallback> cb);
void purge_descendents_of(
const LLUUID& cat_id,
LLPointer<LLInventoryCallback> cb);
const LLUUID get_folder_by_itemtype(const LLInventoryItem *src);
void copy_inventory_from_notecard(const LLUUID& destination_id,