diff options
Diffstat (limited to 'indra/newview/llinventoryfunctions.cpp')
| -rwxr-xr-x | indra/newview/llinventoryfunctions.cpp | 1149 | 
1 files changed, 1139 insertions, 10 deletions
diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 1abc09bf3b..1ca658ebd2 100755 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -59,6 +59,7 @@  #include "llinventorypanel.h"  #include "lllineeditor.h"  #include "llmarketplacenotifications.h" +#include "llmarketplacefunctions.h"  #include "llmenugl.h"  #include "llnotificationsutil.h"  #include "llpanelmaininventory.h" @@ -89,6 +90,15 @@  BOOL LLInventoryState::sWearNewClothing = FALSE;  LLUUID LLInventoryState::sWearNewClothingTransactionID; +std::list<LLUUID> LLInventoryAction::sMarketplaceFolders; + +// Helper function : callback to update a folder after inventory action happened in the background +void update_folder_cb(const LLUUID& dest_folder) +{ +    LLViewerInventoryCategory* dest_cat = gInventory.getCategory(dest_folder); +    gInventory.updateCategory(dest_cat); +    gInventory.notifyObservers(); +}  // Generates a string containing the path to the item specified by  // item_id. @@ -111,6 +121,128 @@ void append_path(const LLUUID& id, std::string& path)  	path.append(temp);  } +void update_marketplace_folder_hierarchy(const LLUUID cat_id) +{ +    // When changing the marketplace status of a folder, the only thing that needs to happen is +    // for all observers of the folder to, possibly, change the display label of the folder +    // so that's the only thing we change on the update mask. +    gInventory.addChangedMask(LLInventoryObserver::LABEL, cat_id); + +    // Update all descendent folders down +	LLInventoryModel::cat_array_t* cat_array; +	LLInventoryModel::item_array_t* item_array; +	gInventory.getDirectDescendentsOf(cat_id,cat_array,item_array); +     +    LLInventoryModel::cat_array_t cat_array_copy = *cat_array; +    for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) +    { +        LLInventoryCategory* category = *iter; +        update_marketplace_folder_hierarchy(category->getUUID()); +    } +    return; +} + +void update_marketplace_category(const LLUUID& cur_uuid, bool perform_consistency_enforcement) +{ +    // When changing the marketplace status of an item, we usually have to change the status of all +    // folders in the same listing. This is because the display of each folder is affected by the +    // overall status of the whole listing. +    // Consequently, the only way to correctly update an item anywhere in the marketplace is to  +    // update the whole listing from its listing root. +    // This is not as bad as it seems as we only update folders, not items, and the folder nesting depth  +    // is limited to 4. +    // We also take care of degenerated cases so we don't update all folders in the inventory by mistake. + +    if (cur_uuid.isNull()) +    { +        return; +    } +     +    // Grab marketplace listing data for this item +    S32 depth = depth_nesting_in_marketplace(cur_uuid); +    if (depth > 0) +    { +        // Retrieve the listing uuid this object is in +        LLUUID listing_uuid = nested_parent_id(cur_uuid, depth); +     +        // Verify marketplace data consistency for this listing +        if (perform_consistency_enforcement && LLMarketplaceData::instance().isListed(listing_uuid)) +        { +            LLUUID version_folder_uuid = LLMarketplaceData::instance().getVersionFolder(listing_uuid); +            S32 version_depth = depth_nesting_in_marketplace(version_folder_uuid); +            if (version_folder_uuid.notNull() && (!gInventory.isObjectDescendentOf(version_folder_uuid, listing_uuid) || (version_depth != 2))) +            { +                LL_INFOS("SLM") << "Unlist and clear version folder as the version folder is not at the right place anymore!!" << LL_ENDL; +                LLMarketplaceData::instance().setVersionFolder(listing_uuid, LLUUID::null); +            } +        } +     +        // Update all descendents starting from the listing root +        update_marketplace_folder_hierarchy(listing_uuid); +    } +    else if (depth == 0) +    { +        // If this is the marketplace listings root itself, update all descendents +        if (gInventory.getCategory(cur_uuid)) +        { +            update_marketplace_folder_hierarchy(cur_uuid); +        } +    } +    else +    { +        // If the folder is outside the marletplace listings root, clear its SLM data if needs be +        if (perform_consistency_enforcement && LLMarketplaceData::instance().isListed(cur_uuid)) +        { +            LL_INFOS("SLM") << "Disassociate as the listing folder is not under the marketplace folder anymore!!" << LL_ENDL; +            LLMarketplaceData::instance().clearListing(cur_uuid); +        } +        // Update all descendents if this is a category +        if (gInventory.getCategory(cur_uuid)) +        { +            update_marketplace_folder_hierarchy(cur_uuid); +        } +    } + +    return; +} + +// Iterate through the marketplace and flag for label change all categories that countain a stock folder (i.e. stock folders and embedding folders up the hierarchy) +void update_all_marketplace_count(const LLUUID& cat_id) +{ +    // Get all descendent folders down +	LLInventoryModel::cat_array_t* cat_array; +	LLInventoryModel::item_array_t* item_array; +	gInventory.getDirectDescendentsOf(cat_id,cat_array,item_array); +     +    LLInventoryModel::cat_array_t cat_array_copy = *cat_array; +    for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) +    { +        LLInventoryCategory* category = *iter; +        if (category->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) +        { +            // Listing containing stock folders needs to be updated but not others +            // Note: we take advantage of the fact that stock folder *do not* contain sub folders to avoid a recursive call here +            update_marketplace_category(category->getUUID()); +        } +        else +        { +            // Explore the contained folders recursively +            update_all_marketplace_count(category->getUUID()); +        } +    } +} + +void update_all_marketplace_count() +{ +    // Get the marketplace root and launch the recursive exploration +    const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); +    if (!marketplace_listings_uuid.isNull()) +    { +        update_all_marketplace_count(marketplace_listings_uuid); +    } +    return; +} +  void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name)  {  	LLViewerInventoryCategory* cat; @@ -150,13 +282,14 @@ void copy_inventory_category(LLInventoryModel* model,  	for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++)  	{  		LLInventoryItem* item = *iter; +        LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb, new_cat_uuid));  		copy_inventory_item(  							gAgent.getID(),  							item->getPermissions().getOwner(),  							item->getUUID(),  							new_cat_uuid,  							std::string(), -							LLPointer<LLInventoryCallback>(NULL)); +							cb);  	}  	// Copy all the folders @@ -508,14 +641,17 @@ void open_outbox()  	LLFloaterReg::showInstance("outbox");  } -LLUUID create_folder_in_outbox_for_item(LLInventoryItem* item, const LLUUID& destFolderId, S32 operation_id) +// Create a new folder in destFolderId with the same name as the item name and return the uuid of the new folder +// Note: this is used locally in various situation where we need to wrap an item into a special folder +LLUUID create_folder_for_item(LLInventoryItem* item, const LLUUID& destFolderId)  {  	llassert(item);  	llassert(destFolderId.notNull());  	LLUUID created_folder_id = gInventory.createNewCategory(destFolderId, LLFolderType::FT_NONE, item->getName());  	gInventory.notifyObservers(); - +     +    // *TODO : Create different notifications for the various cases  	LLNotificationsUtil::add("OutboxFolderCreated");  	return created_folder_id; @@ -531,8 +667,7 @@ void move_to_outbox_cb_action(const LLSD& payload)  		// when moving item directly into outbox create folder with that name  		if (dest_folder_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false))  		{ -			S32 operation_id = payload["operation_id"].asInteger(); -			dest_folder_id = create_folder_in_outbox_for_item(viitem, dest_folder_id, operation_id); +			dest_folder_id = create_folder_for_item(viitem, dest_folder_id);  		}  		LLUUID parent = viitem->getParentUUID(); @@ -603,7 +738,7 @@ void copy_item_to_outbox(LLInventoryItem* inv_item, LLUUID dest_folder, const LL  			// when moving item directly into outbox create folder with that name  			if (dest_folder == gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false))  			{ -				dest_folder = create_folder_in_outbox_for_item(inv_item, dest_folder, operation_id); +				dest_folder = create_folder_for_item(inv_item, dest_folder);  			}  			copy_inventory_item(gAgent.getID(), @@ -633,7 +768,7 @@ void move_item_within_outbox(LLInventoryItem* inv_item, LLUUID dest_folder, S32  	// when moving item directly into outbox create folder with that name  	if (dest_folder == gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false))  	{ -		dest_folder = create_folder_in_outbox_for_item(inv_item, dest_folder, operation_id); +		dest_folder = create_folder_for_item(inv_item, dest_folder);  	}  	LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) inv_item; @@ -674,6 +809,894 @@ void copy_folder_to_outbox(LLInventoryCategory* inv_cat, const LLUUID& dest_fold  }  ///---------------------------------------------------------------------------- +// Marketplace functions +// +// Handles Copy and Move to or within the Marketplace listings folder. +// Handles creation of stock folders, nesting of listings and version folders, +// permission checking and listings validation. +///---------------------------------------------------------------------------- + +S32 depth_nesting_in_marketplace(LLUUID cur_uuid) +{ +    // Get the marketplace listings root, exit with -1 (i.e. not under the marketplace listings root) if none +    const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); +    if (marketplace_listings_uuid.isNull()) +    { +        return -1; +    } +    // If not a descendent of the marketplace listings root, then the nesting depth is -1 by definition +    if (!gInventory.isObjectDescendentOf(cur_uuid, marketplace_listings_uuid)) +    { +        return -1; +    } +     +    // Iterate through the parents till we hit the marketplace listings root +    // Note that the marketplace listings root itself will return 0 +    S32 depth = 0; +    LLInventoryObject* cur_object = gInventory.getObject(cur_uuid); +    while (cur_uuid != marketplace_listings_uuid) +    { +        depth++; +        cur_uuid = cur_object->getParentUUID(); +        cur_object = gInventory.getCategory(cur_uuid); +    } +    return depth; +} + +// Returns the UUID of the marketplace listing this object is in +LLUUID nested_parent_id(LLUUID cur_uuid, S32 depth) +{ +    if (depth < 1) +    { +        // For objects outside the marketplace listings root (or root itself), we return a NULL UUID +        return LLUUID::null; +    } +    else if (depth == 1) +    { +        // Just under the root, we return the passed UUID itself if it's a folder, NULL otherwise (not a listing) +        LLViewerInventoryCategory* cat = gInventory.getCategory(cur_uuid); +        return (cat ? cur_uuid : LLUUID::null); +    } + +    // depth > 1 +    LLInventoryObject* cur_object = gInventory.getObject(cur_uuid); +    while (depth > 1) +    { +        depth--; +        cur_uuid = cur_object->getParentUUID(); +        cur_object = gInventory.getCategory(cur_uuid); +    } +    return cur_uuid; +} + +S32 compute_stock_count(LLUUID cat_uuid) +{ +    // Handle the case of the folder being a stock folder immediately +    LLViewerInventoryCategory* cat = gInventory.getCategory(cat_uuid); +    if (!cat) +    { +        // Not a category so no stock count to speak of +        return -1; +    } +    if (cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) +    { +        // Note: stock folders are *not* supposed to have nested subfolders so we stop recursion here +        // Note: we *always* give a stock count for stock folders, it's useful even if the listing is unassociated +        return cat->getDescendentCount(); +    } + +    // Grab marketplace data for this folder +    S32 depth = depth_nesting_in_marketplace(cat_uuid); +    LLUUID listing_uuid = nested_parent_id(cat_uuid, depth); +    if (!LLMarketplaceData::instance().isListed(listing_uuid)) +    { +        // If not listed, the notion of stock is meaningless so it won't be computed for any level +        return -1; +    } + +    LLUUID version_folder_uuid = LLMarketplaceData::instance().getVersionFolder(listing_uuid); +    // Handle the case of the first 2 levels : listing and version folders +    if (depth == 1) +    { +        if (version_folder_uuid.notNull()) +        { +            // If there is a version folder, the stock value for the listing is the version folder stock +            return compute_stock_count(version_folder_uuid); +        } +        else +        { +            // If there's no version folder associated, the notion of stock count has no meaning +            return -1; +        } +    } +    else if (depth == 2) +    { +        if (version_folder_uuid.notNull() && (version_folder_uuid != cat_uuid)) +        { +            // If there is a version folder but we're not it, our stock count is meaningless +            return -1; +        } +    } +     +    // In all other cases, the stock count is the min of stock folders count found in the descendents +	LLInventoryModel::cat_array_t* cat_array; +	LLInventoryModel::item_array_t* item_array; +	gInventory.getDirectDescendentsOf(cat_uuid,cat_array,item_array); +     +    // "-1" denotes a folder that doesn't countain any stock folders in its descendents +    S32 curr_count = -1; + +    // Note: marketplace listings have a maximum depth nesting of 4 +    LLInventoryModel::cat_array_t cat_array_copy = *cat_array; +    for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) +    { +        LLInventoryCategory* category = *iter; +        S32 count = compute_stock_count(category->getUUID()); +        if ((curr_count == -1) || ((count != -1) && (count < curr_count))) +        { +            curr_count = count; +        } +    } +     +    return curr_count; +} + +// local helper +bool can_move_to_marketplace(LLInventoryItem* inv_item, std::string& tooltip_msg, bool resolve_links) +{ +	// Collapse links directly to items/folders +	LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) inv_item; +	LLViewerInventoryItem * linked_item = viewer_inv_item->getLinkedItem(); +    LLViewerInventoryCategory * linked_category = viewer_inv_item->getLinkedCategory(); + +    // Linked items and folders cannot be put for sale +    if (linked_category || linked_item) +    { +		tooltip_msg = LLTrans::getString("TooltipOutboxLinked"); +        return false; +    } +	 +    // A category is always considered as passing... +    if (linked_category != NULL) +	{ +        return true; +	} +     +    // Take the linked item if necessary +    if (linked_item != NULL) +	{ +		inv_item = linked_item; +	} +	 +    // Check that the agent has transfer permission on the item: this is required as a resident cannot +    // put on sale items she cannot transfer. Proceed with move if we have permission. +	bool allow_transfer = inv_item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()); +	if (!allow_transfer) +	{ +		tooltip_msg = LLTrans::getString("TooltipOutboxNoTransfer"); +		return false; +	} +     +    // Check worn/not worn status: worn items cannot be put on the marketplace +	bool worn = get_is_item_worn(inv_item->getUUID()); +	if (worn) +	{ +		tooltip_msg = LLTrans::getString("TooltipOutboxWorn"); +		return false; +	} + +    // Check library status: library items cannot be put on the marketplace +	if (!gInventory.isObjectDescendentOf(inv_item->getUUID(), gInventory.getRootFolderID())) +    { +		tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); +		return false; +    } + +    // Check type: for the moment, calling cards cannot be put on the marketplace +	bool calling_card = (LLAssetType::AT_CALLINGCARD == inv_item->getType()); +	if (calling_card) +	{ +		tooltip_msg = LLTrans::getString("TooltipOutboxCallingCard"); +		return false; +	} +	 +	return true; +} + +// local helper +// Returns the max tree length (in folder nodes) down from the argument folder +int get_folder_levels(LLInventoryCategory* inv_cat) +{ +	LLInventoryModel::cat_array_t* cats; +	LLInventoryModel::item_array_t* items; +	gInventory.getDirectDescendentsOf(inv_cat->getUUID(), cats, items); +     +	int max_child_levels = 0; +     +	for (S32 i=0; i < cats->size(); ++i) +	{ +		LLInventoryCategory* category = cats->at(i); +		max_child_levels = llmax(max_child_levels, get_folder_levels(category)); +	} +     +	return 1 + max_child_levels; +} + +// local helper +// Returns the distance (in folder nodes) between the ancestor and its descendant. Returns -1 if not related. +int get_folder_path_length(const LLUUID& ancestor_id, const LLUUID& descendant_id) +{ +	int depth = 0; +     +	if (ancestor_id == descendant_id) return depth; +     +	const LLInventoryCategory* category = gInventory.getCategory(descendant_id); +     +	while (category) +	{ +		LLUUID parent_id = category->getParentUUID(); +         +		if (parent_id.isNull()) break; +         +		depth++; +         +		if (parent_id == ancestor_id) return depth; +         +		category = gInventory.getCategory(parent_id); +	} +     +	LL_WARNS("SLM") << "get_folder_path_length() couldn't trace a path from the descendant to the ancestor" << LL_ENDL; +	return -1; +} + +// local helper +// Returns true if all items within the argument folder are fit for sale, false otherwise +bool has_correct_permissions_for_sale(LLInventoryCategory* cat, std::string& error_msg) +{ +	LLInventoryModel::cat_array_t* cat_array; +	LLInventoryModel::item_array_t* item_array; +	gInventory.getDirectDescendentsOf(cat->getUUID(),cat_array,item_array); +     +	LLInventoryModel::item_array_t item_array_copy = *item_array; +     +	for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++) +	{ +		LLInventoryItem* item = *iter; +        if (!can_move_to_marketplace(item, error_msg, false)) +        { +            return false; +        } +	} +     +	LLInventoryModel::cat_array_t cat_array_copy = *cat_array; +     +	for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) +	{ +		LLInventoryCategory* category = *iter; +		if (!has_correct_permissions_for_sale(category, error_msg)) +        { +            return false; +        } +	} +    return true; +} + +// Returns true if inv_item can be dropped in dest_folder, a folder nested in marketplace listings (or merchant inventory) under the root_folder root +// If returns is false, tooltip_msg contains an error message to display to the user (localized and all). +// bundle_size is the amount of sibling items that are getting moved to the marketplace at the same time. +bool can_move_item_to_marketplace(const LLInventoryCategory* root_folder, LLInventoryCategory* dest_folder, LLInventoryItem* inv_item, std::string& tooltip_msg, S32 bundle_size, bool from_paste) +{ +    // Check stock folder type matches item type in marketplace listings or merchant outbox (even if of no use there for the moment) +    LLViewerInventoryCategory* view_folder = dynamic_cast<LLViewerInventoryCategory*>(dest_folder); +    bool accept = (view_folder && view_folder->acceptItem(inv_item)); +    if (!accept) +    { +        tooltip_msg = LLTrans::getString("TooltipOutboxMixedStock"); +    } + +    // Check that the item has the right type and permissions to be sold on the marketplace +    if (accept) +    { +        accept = can_move_to_marketplace(inv_item, tooltip_msg, true); +    } +     +    // Check that the total amount of items won't violate the max limit on the marketplace +    if (accept) +    { +        int existing_item_count = bundle_size; +         +        // Get the version folder: that's where the counts start from +        const LLViewerInventoryCategory * version_folder = ((root_folder && (root_folder != dest_folder)) ? gInventory.getFirstDescendantOf(root_folder->getUUID(), dest_folder->getUUID()) : NULL); + +        if (version_folder) +        { +            if (!from_paste && gInventory.isObjectDescendentOf(inv_item->getUUID(), version_folder->getUUID())) +            { +                // Clear those counts or they will be counted twice because we're already inside the version category +                existing_item_count = 0; +            } + +            LLInventoryModel::cat_array_t existing_categories; +            LLInventoryModel::item_array_t existing_items; +             +            gInventory.collectDescendents(version_folder->getUUID(), existing_categories, existing_items, FALSE); +             +            existing_item_count += existing_items.size(); +        } +         +        if (existing_item_count > gSavedSettings.getU32("InventoryOutboxMaxItemCount")) +        { +            LLStringUtil::format_map_t args; +            U32 amount = gSavedSettings.getU32("InventoryOutboxMaxItemCount"); +            args["[AMOUNT]"] = llformat("%d",amount); +            tooltip_msg = LLTrans::getString("TooltipOutboxTooManyObjects", args); +            accept = false; +        } +    } + +    return accept; +} + +// Returns true if inv_cat can be dropped in dest_folder, a folder nested in marketplace listings (or merchant inventory) under the root_folder root +// If returns is false, tooltip_msg contains an error message to display to the user (localized and all). +// bundle_size is the amount of sibling items that are getting moved to the marketplace at the same time. +bool can_move_folder_to_marketplace(const LLInventoryCategory* root_folder, LLInventoryCategory* dest_folder, LLInventoryCategory* inv_cat, std::string& tooltip_msg, S32 bundle_size, bool check_items, bool from_paste) +{ +    bool accept = true; +     +    // Compute the nested folders level we'll add into with that incoming folder +    int incoming_folder_depth = get_folder_levels(inv_cat); +    // Compute the nested folders level we're inserting ourselves in +    // Note: add 1 when inserting under a listing folder as we need to take the root listing folder in the count +    int insertion_point_folder_depth = (root_folder ? get_folder_path_length(root_folder->getUUID(), dest_folder->getUUID()) + 1 : 0); + +    // Get the version folder: that's where the folders and items counts start from +    const LLViewerInventoryCategory * version_folder = (insertion_point_folder_depth >= 2 ? gInventory.getFirstDescendantOf(root_folder->getUUID(), dest_folder->getUUID()) : NULL); +     +    // Compare the whole with the nested folders depth limit +    // Note: substract 2 as we leave root and version folder out of the count threshold +    if ((incoming_folder_depth + insertion_point_folder_depth - 2) > (S32)(gSavedSettings.getU32("InventoryOutboxMaxFolderDepth"))) +    { +        LLStringUtil::format_map_t args; +        U32 amount = gSavedSettings.getU32("InventoryOutboxMaxFolderDepth"); +        args["[AMOUNT]"] = llformat("%d",amount); +        tooltip_msg = LLTrans::getString("TooltipOutboxFolderLevels", args); +        accept = false; +    } +     +    if (accept) +    { +        LLInventoryModel::cat_array_t descendent_categories; +        LLInventoryModel::item_array_t descendent_items; +        gInventory.collectDescendents(inv_cat->getUUID(), descendent_categories, descendent_items, FALSE); +     +        int dragged_folder_count = descendent_categories.size() + bundle_size;  // Note: We assume that we're moving a bunch of folders in. That might be wrong... +        int dragged_item_count = descendent_items.size(); +        int existing_item_count = 0; +        int existing_folder_count = 0; +     +        if (version_folder) +        { +            if (!from_paste && gInventory.isObjectDescendentOf(inv_cat->getUUID(), version_folder->getUUID())) +            { +                // Clear those counts or they will be counted twice because we're already inside the version category +                dragged_folder_count = 0; +                dragged_item_count = 0; +            } +         +            // Tally the total number of categories and items inside the root folder +            LLInventoryModel::cat_array_t existing_categories; +            LLInventoryModel::item_array_t existing_items; +            gInventory.collectDescendents(version_folder->getUUID(), existing_categories, existing_items, FALSE); +         +            existing_folder_count += existing_categories.size(); +            existing_item_count += existing_items.size(); +        } +     +        const int total_folder_count = existing_folder_count + dragged_folder_count; +        const int total_item_count = existing_item_count + dragged_item_count; +     +        if (total_folder_count > gSavedSettings.getU32("InventoryOutboxMaxFolderCount")) +        { +            LLStringUtil::format_map_t args; +            U32 amount = gSavedSettings.getU32("InventoryOutboxMaxFolderCount"); +            args["[AMOUNT]"] = llformat("%d",amount); +            tooltip_msg = LLTrans::getString("TooltipOutboxTooManyFolders", args); +            accept = false; +        } +        else if (total_item_count > gSavedSettings.getU32("InventoryOutboxMaxItemCount")) +        { +            LLStringUtil::format_map_t args; +            U32 amount = gSavedSettings.getU32("InventoryOutboxMaxItemCount"); +            args["[AMOUNT]"] = llformat("%d",amount); +            tooltip_msg = LLTrans::getString("TooltipOutboxTooManyObjects", args); +            accept = false; +        } +         +        // Now check that each item in the folder can be moved in the marketplace +        if (accept && check_items) +        { +            for (S32 i=0; i < descendent_items.size(); ++i) +            { +                LLInventoryItem* item = descendent_items[i]; +                if (!can_move_to_marketplace(item, tooltip_msg, false)) +                { +                    accept = false; +                    break; +                } +            } +        } +    } +     +    return accept; +} + +bool move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_folder, bool copy) +{ +    // Get the marketplace listings depth of the destination folder, exit with error if not under marketplace +    S32 depth = depth_nesting_in_marketplace(dest_folder); +    if (depth < 0) +    { +		LLSD subs; +		subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Merchant"); +		LLNotificationsUtil::add("MerchantPasteFailed", subs); +        return false; +    } + +    // We will collapse links into items/folders +	LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) inv_item; +	LLViewerInventoryCategory * linked_category = viewer_inv_item->getLinkedCategory(); +     +	if (linked_category != NULL) +	{ +        // Move the linked folder directly +		return move_folder_to_marketplacelistings(linked_category, dest_folder, copy); +	} +	else +	{ +        // Grab the linked item if any +		LLViewerInventoryItem * linked_item = viewer_inv_item->getLinkedItem(); +        viewer_inv_item = (linked_item != NULL ? linked_item : viewer_inv_item); +     +        // Check that the agent has transfer permission on the item: this is required as a resident cannot +        // put on sale items she cannot transfer. Proceed with move if we have permission. +        std::string error_msg; +        if (can_move_to_marketplace(inv_item, error_msg, true)) +        { +            // When moving an isolated item, we might need to create the folder structure to support it +            if (depth == 0) +            { +                // We need a listing folder +                dest_folder = gInventory.createNewCategory(dest_folder, LLFolderType::FT_NONE, viewer_inv_item->getName()); +                depth++; +            } +            if (depth == 1) +            { +                // We need a version folder +                dest_folder = gInventory.createNewCategory(dest_folder, LLFolderType::FT_NONE, viewer_inv_item->getName()); +                depth++; +            } +			LLViewerInventoryCategory* dest_cat = gInventory.getCategory(dest_folder); +            if (!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID()) && +                (dest_cat->getPreferredType() != LLFolderType::FT_MARKETPLACE_STOCK)) +            { +                // We need a stock folder +                dest_folder = gInventory.createNewCategory(dest_folder, LLFolderType::FT_MARKETPLACE_STOCK, viewer_inv_item->getName()); +                dest_cat = gInventory.getCategory(dest_folder); +                depth++; +            } +             +            // Verify we can have this item in that destination category +            if (!dest_cat->acceptItem(viewer_inv_item)) +            { +                LLSD subs; +                subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted"); +                LLNotificationsUtil::add("MerchantPasteFailed", subs); +                return false; +            } +             +            // Get the parent folder of the moved item : we may have to update it +            LLUUID src_folder = viewer_inv_item->getParentUUID(); + +            if (copy) +            { +                // Copy the item +                LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb, dest_folder)); +                copy_inventory_item( +                                    gAgent.getID(), +                                    viewer_inv_item->getPermissions().getOwner(), +                                    viewer_inv_item->getUUID(), +                                    dest_folder, +                                    std::string(), +                                    cb); +            } +            else +            { +                // Reparent the item +                gInventory.changeItemParent(viewer_inv_item, dest_folder, true); +            } +             +            // Update the modified folders +            update_marketplace_category(src_folder); +            update_marketplace_category(dest_folder); +            gInventory.notifyObservers(); +        } +        else +        { +            LLSD subs; +            subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + error_msg; +            LLNotificationsUtil::add("MerchantPasteFailed", subs); +            return false; +        } +    } +    return true; +} + +bool move_folder_to_marketplacelistings(LLInventoryCategory* inv_cat, const LLUUID& dest_folder, bool copy) +{ +    // Check that we have adequate permission on all items being moved. Proceed if we do. +    std::string error_msg; +    if (has_correct_permissions_for_sale(inv_cat, error_msg)) +    { +        // Get the destination folder +        LLViewerInventoryCategory* dest_cat = gInventory.getCategory(dest_folder); + +        // Check it's not a stock folder +        if (dest_cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) +        { +            LLSD subs; +            subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted"); +            LLNotificationsUtil::add("MerchantPasteFailed", subs); +            return false; +        } +         +        // Get the parent folder of the moved item : we may have to update it +        LLUUID src_folder = inv_cat->getParentUUID(); + +        LLViewerInventoryCategory * viewer_inv_cat = (LLViewerInventoryCategory *) inv_cat; +        if (copy) +        { +            // Copy the folder +            copy_inventory_category(&gInventory, viewer_inv_cat, dest_folder); +        } +        else +        { +            // Reparent the folder +            gInventory.changeCategoryParent(viewer_inv_cat, dest_folder, false); +        } + +        // Check the destination folder recursively for no copy items and promote the including folders if any +        validate_marketplacelistings(dest_cat); + +        // Update the modified folders +        update_marketplace_category(src_folder); +        update_marketplace_category(dest_folder); +        gInventory.notifyObservers(); +    } +    else +    { +        LLSD subs; +        subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + error_msg; +        LLNotificationsUtil::add("MerchantPasteFailed", subs); +        return false; +    } +    return true; +} + +// Make all relevant business logic checks on the marketplace listings starting with the folder as argument. +// This function does no deletion of listings but a mere audit and raises issues to the user (through the +// optional callback cb). It also returns a boolean, true if things validate, false if issues are raised. +// The only inventory changes that are done is to move and sort folders containing no-copy items to stock folders. +bool sort_alpha(const LLViewerInventoryCategory* cat1, const LLViewerInventoryCategory* cat2) +{ +	return cat1->getName().compare(cat2->getName()) < 0; +} + +bool validate_marketplacelistings(LLInventoryCategory* cat, validation_callback_t cb, bool fix_hierarchy) +{ +    // Folder is valid unless issue is raised +    bool result = true; +     +    // Get the type and the depth of the folder +    LLViewerInventoryCategory * viewer_cat = (LLViewerInventoryCategory *) (cat); +	const LLFolderType::EType folder_type = cat->getPreferredType(); +    S32 depth = depth_nesting_in_marketplace(cat->getUUID()); +    if (depth < 0) +    { +        // If the folder is not under the marketplace listings root, validation should not be applied +        return result; +    } +     +    // Set the indentation for print output (typically, audit button in marketplace folder floater) +    std::string indent; +    for (int i = 1; i < depth; i++) +    { +        indent += "    "; +    } + +    // Check out that version folders are marketplace ready +    if (depth == 2) +    { +        std::string message; +        // Note: if we fix the hierarchy, we want to check the items individually, hence the last argument here +        if (!can_move_folder_to_marketplace(cat, cat, cat, message, 0, fix_hierarchy)) +        { +            result = false; +            if (cb) +            { +                message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error") + " " + message; +                cb(message,LLError::LEVEL_ERROR); +            } +        } +    } +     +    // Check out that stock folders are at the right level +    if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth <= 2)) +    { +        if (fix_hierarchy) +        { +            if (cb) +            { +                std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Warning") + " " + LLTrans::getString("Marketplace Validation Warning Stock"); +                cb(message,LLError::LEVEL_WARN); +            } +            // Nest the stock folder one level deeper in a normal folder and restart from there +            LLUUID parent_uuid = cat->getParentUUID(); +            LLUUID folder_uuid = gInventory.createNewCategory(parent_uuid, LLFolderType::FT_NONE, cat->getName()); +            LLInventoryCategory* new_cat = gInventory.getCategory(folder_uuid); +            gInventory.changeCategoryParent(viewer_cat, folder_uuid, false); +            result &= validate_marketplacelistings(new_cat, cb, fix_hierarchy); +            return result; +        } +        else +        { +            result = false; +            if (cb) +            { +                std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error") + " " + LLTrans::getString("Marketplace Validation Warning Stock"); +                cb(message,LLError::LEVEL_ERROR); +            } +        } +    } +     +	LLInventoryModel::cat_array_t* cat_array; +	LLInventoryModel::item_array_t* item_array; +	gInventory.getDirectDescendentsOf(cat->getUUID(),cat_array,item_array); +     +    // Stock items : sorting and moving the various stock items is complicated as the set of constraints is high +    // For each folder, we need to: +    // * separate non stock items, stock items per types in different folders +    // * have stock items nested at depth 2 at least +    // * never ever move the non-stock items +     +    std::vector<std::vector<LLViewerInventoryItem*> > items_vector; +    items_vector.resize(LLInventoryType::IT_COUNT+1); + +    // Parse the items and create vectors of items to sort copyable items and stock items of various types +    bool has_bad_items = false; +	LLInventoryModel::item_array_t item_array_copy = *item_array; +	for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++) +	{ +		LLInventoryItem* item = *iter; +        LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) item; +         +        // Test but skip items that shouldn't be there to start with, raise an error message for those +        std::string error_msg; +        if (!can_move_to_marketplace(item, error_msg, false)) +        { +            has_bad_items = true; +            if (cb && fix_hierarchy) +            { +                std::string message = indent + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error") + " " + error_msg; +                cb(message,LLError::LEVEL_ERROR); +            } +            continue; +        } +        // Update the appropriate vector item for that type +        LLInventoryType::EType type = LLInventoryType::IT_COUNT;    // Default value for non stock items +        if (!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) +        { +            // Get the item type for stock items +            type = viewer_inv_item->getInventoryType(); +        } +        items_vector[type].push_back(viewer_inv_item); +	} +     +    // How many types of folders? Which type is it if only one? +    S32 count = 0; +    LLInventoryType::EType type = LLInventoryType::IT_COUNT; +    for (S32 i = 0; i <= LLInventoryType::IT_COUNT; i++) +    { +        if (!items_vector[i].empty()) +        { +            count++; +            type = (LLInventoryType::EType)(i); +        } +    } +     +    // If we have no items in there (only folders or empty), analyze a bit further  +    if ((count == 0) && !has_bad_items) +    { +        if (cat_array->size() == 0) +        { +            // If this is an empty version folder, raise an error +            if (depth == 2) +            { +                result = false; +                if (cb) +                { +                    std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Empty Version"); +                    cb(message,LLError::LEVEL_ERROR); +                } +            } +            // If this is a legit but empty stock folder, raise an error +            else if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth > 2)) +            { +                result = false; +                if (cb) +                { +                    std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Empty Stock"); +                    cb(message,LLError::LEVEL_ERROR); +                } +            } +            else if (cb) +            { +                // We warn if there's nothing in a regular folder (may be it's an under construction listing) +                std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Warning Empty"); +                cb(message,LLError::LEVEL_WARN); +            } +        } +        else +        { +            // Done with that folder : Print out the folder name unless we already found an error here +            if (cb && result && (depth >= 1)) +            { +                std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log"); +                cb(message,LLError::LEVEL_INFO); +            } +        } +    } +    // If we have a single type of items of the right type in the right place, we're done +    else if ((count == 1) && !has_bad_items && (((type == LLInventoryType::IT_COUNT) && (depth > 1)) || ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth > 2)))) +    { +        // Done with that folder : Print out the folder name unless we already found an error here +        if (cb && result && (depth >= 1)) +        { +            std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log"); +            cb(message,LLError::LEVEL_INFO); +        } +    } +    else +    { +        if (fix_hierarchy && !has_bad_items) +        { +            // Create one folder per vector at the right depth and of the right type +            for (S32 i = 0; i <= LLInventoryType::IT_COUNT; i++) +            { +                if (!items_vector[i].empty()) +                { +                    // Create a new folder +                    LLUUID parent_uuid = (depth > 2 ? viewer_cat->getParentUUID() : viewer_cat->getUUID()); +                    LLViewerInventoryItem* viewer_inv_item = items_vector[i].back(); +                    std::string folder_name = (depth > 1 ? viewer_cat->getName() : viewer_inv_item->getName()); +                    LLFolderType::EType new_folder_type = (i == LLInventoryType::IT_COUNT ? LLFolderType::FT_NONE : LLFolderType::FT_MARKETPLACE_STOCK); +                    if (cb) +                    { +                        std::string message = ""; +                        if (new_folder_type == LLFolderType::FT_MARKETPLACE_STOCK) +                        { +                            message = indent + folder_name + LLTrans::getString("Marketplace Validation Warning Create Stock"); +                        } +                        else +                        { +                            message = indent + folder_name + LLTrans::getString("Marketplace Validation Warning Create Version"); +                        } +                        cb(message,LLError::LEVEL_WARN); +                    } +                    LLUUID folder_uuid = gInventory.createNewCategory(parent_uuid, new_folder_type, folder_name); +                    // Move each item to the new folder +                    while (!items_vector[i].empty()) +                    { +                        LLViewerInventoryItem* viewer_inv_item = items_vector[i].back(); +                        if (cb) +                        { +                            std::string message = indent + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Warning Move"); +                            cb(message,LLError::LEVEL_WARN); +                        } +                        gInventory.changeItemParent(viewer_inv_item, folder_uuid, true); +                        items_vector[i].pop_back(); +                    } +                    update_marketplace_category(folder_uuid); +                    gInventory.notifyObservers(); +                } +            } +        } +        else if (cb) +        { +            // We are not fixing the hierarchy but reporting problems, report everything we can find +            // Print the folder name +            if (result && (depth >= 1)) +            { +                if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (count >= 2)) +                { +                    // Report if a stock folder contains a mix of items +                    result = false; +                    std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Mixed Stock"); +                    cb(message,LLError::LEVEL_ERROR); +                } +                else +                { +                    // Simply print the folder name +                    std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log"); +                    cb(message,LLError::LEVEL_INFO); +                } +            } +            // Scan each item and report if there's a problem +            LLInventoryModel::item_array_t item_array_copy = *item_array; +            for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++) +            { +                LLInventoryItem* item = *iter; +                LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) item; +                std::string error_msg; +                if (!can_move_to_marketplace(item, error_msg, false)) +                { +                    // Report items that shouldn't be there to start with +                    result = false; +                    std::string message = indent + "    " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error") + " " + error_msg; +                    cb(message,LLError::LEVEL_ERROR); +                } +                else if ((!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) && (folder_type != LLFolderType::FT_MARKETPLACE_STOCK)) +                { +                    // Report stock items that are misplaced +                    result = false; +                    std::string message = indent + "    " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error Stock Item"); +                    cb(message,LLError::LEVEL_ERROR); +                } +                else if (depth == 1) +                { +                    // Report items not wrapped in version folder +                    std::string message = indent + "    " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Warning Unwrapped Item"); +                    cb(message,LLError::LEVEL_WARN); +                } +            } +        } +         +        // Clean up +        if (viewer_cat->getDescendentCount() == 0) +        { +            // Remove the current folder if it ends up empty +            if (cb) +            { +                std::string message = indent + viewer_cat->getName() + LLTrans::getString("Marketplace Validation Warning Delete"); +                cb(message,LLError::LEVEL_WARN); +            } +            gInventory.removeCategory(cat->getUUID()); +            gInventory.notifyObservers(); +            return result && !has_bad_items; +        } +        else +        { +            // Update the current folder +            update_marketplace_category(cat->getUUID()); +            gInventory.notifyObservers(); +        } +    } + +    // Recursion : Perform the same validation on each nested folder +	LLInventoryModel::cat_array_t cat_array_copy = *cat_array; +    // Sort the folders in alphabetical order first +    std::sort(cat_array_copy.begin(), cat_array_copy.end(), sort_alpha); +    +	for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) +	{ +		LLInventoryCategory* category = *iter; +		result &= validate_marketplacelistings(category, cb, fix_hierarchy); +	} +     +    return result && !has_bad_items; +} + +///----------------------------------------------------------------------------  /// LLInventoryCollectFunctor implementations  ///---------------------------------------------------------------------------- @@ -1060,18 +2083,79 @@ void LLOpenFoldersWithSelection::doFolder(LLFolderViewFolder* folder)  	}  } -void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root, const std::string& action) +// Callback for doToSelected if DAMA required... +void LLInventoryAction::callback_doToSelected(const LLSD& notification, const LLSD& response, class LLInventoryModel* model, class LLFolderView* root, const std::string& action)  { +    S32 option = LLNotificationsUtil::getSelectedOption(notification, response); +    if (option == 0) // YES +    { +        doToSelected(model, root, action, FALSE); +    } +} + +void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root, const std::string& action, BOOL user_confirm) +{ +	std::set<LLFolderViewItem*> selected_items = root->getSelectionList(); +     +    // Prompt the user and check for authorization for some marketplace active listing edits +	if (user_confirm && (("delete" == action) || ("cut" == action) || ("rename" == action) || ("properties" == action) || ("task_properties" == action) || ("open" == action))) +    { +        std::set<LLFolderViewItem*>::iterator set_iter = selected_items.begin(); +        LLFolderViewModelItemInventory * viewModel = NULL; +        for (; set_iter != selected_items.end(); ++set_iter) +        { +            viewModel = dynamic_cast<LLFolderViewModelItemInventory *>((*set_iter)->getViewModelItem()); +            if (viewModel && (depth_nesting_in_marketplace(viewModel->getUUID()) >= 0)) +            { +                break; +            } +        } +        if (set_iter != selected_items.end()) +        { +            if ("open" == action) +            { +                if (get_can_item_be_worn(viewModel->getUUID())) +                { +                    // Wearing an object from any listing, active or not, is verbotten +                    LLNotificationsUtil::add("AlertMerchantListingCannotWear"); +                    return; +                } +                // Note: we do not prompt for change when opening items (e.g. textures or note cards) on the marketplace... +            } +            else if (LLMarketplaceData::instance().isInActiveFolder(viewModel->getUUID()) || +                     LLMarketplaceData::instance().isListedAndActive(viewModel->getUUID())) +            { +                // If item is in active listing, further confirmation is required +                if ((("cut" == action) || ("delete" == action)) && (LLMarketplaceData::instance().isListed(viewModel->getUUID()) || LLMarketplaceData::instance().isVersionFolder(viewModel->getUUID()))) +                { +                    // Cut or delete of the active version folder or listing folder itself will unlist the listing so ask that question specifically +                    LLNotificationsUtil::add("ConfirmMerchantUnlist", LLSD(), LLSD(), boost::bind(&LLInventoryAction::callback_doToSelected, _1, _2, model, root, action)); +                    return; +                } +                // Any other case will simply modify but not unlist a listing +                LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLInventoryAction::callback_doToSelected, _1, _2, model, root, action)); +                return; +            } +        } +    } +     +    // Keep track of the marketplace folders that will need update of their status/name after the operation is performed +    buildMarketplaceFolders(root); +      	if ("rename" == action)  	{  		root->startRenamingSelectedItem(); +        // Update the marketplace listings that have been affected by the operation +        updateMarketplaceFolders();  		return;  	} +      	if ("delete" == action)  	{  		LLSD args;  		args["QUESTION"] = LLTrans::getString(root->getSelectedCount() > 1 ? "DeleteItems" :  "DeleteItem");  		LLNotificationsUtil::add("DeleteItems", args, LLSD(), boost::bind(&LLInventoryAction::onItemsRemovalConfirmation, _1, _2, root)); +        // Note: marketplace listings will be updated in the callback if delete confirmed  		return;  	}  	if (("copy" == action) || ("cut" == action)) @@ -1089,12 +2173,12 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root  		LLViewerInventoryCategory *cat = model->getCategory(inventory_item->getUUID());  		if (!cat) return;  		cat->changeType(new_folder_type); +        // Update the marketplace listings that have been affected by the operation +        updateMarketplaceFolders();  		return;  	} -	std::set<LLFolderViewItem*> selected_items = root->getSelectionList(); -  	LLMultiPreview* multi_previewp = NULL;  	LLMultiProperties* multi_propertiesp = NULL; @@ -1125,6 +2209,9 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root  		bridge->performAction(model, action);  	} +    // Update the marketplace listings that have been affected by the operation +    updateMarketplaceFolders(); +      	LLFloater::setFloaterHost(NULL);  	if (multi_previewp)  	{ @@ -1168,5 +2255,47 @@ void LLInventoryAction::onItemsRemovalConfirmation( const LLSD& notification, co          //because once removed from root folder view the item is no longer a selected item          removeItemFromDND(root);  		root->removeSelectedItems(); +         +        // Update the marketplace listings that have been affected by the operation +        updateMarketplaceFolders();  	}  } + +void LLInventoryAction::buildMarketplaceFolders(LLFolderView* root) +{ +    // Make a list of all marketplace folders containing the elements in the selected list +    // as well as the elements themselves. +    // Once those elements are updated (cut, delete in particular but potentially any action), their +    // containing folder will need to be updated as well as their initially containing folder. For +    // instance, moving a stock folder from a listed folder to another will require an update of the +    // target listing *and* the original listing. So we need to keep track of both. +    sMarketplaceFolders.clear(); +    const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); +    std::set<LLFolderViewItem*> selected_items = root->getSelectionList(); +    std::set<LLFolderViewItem*>::iterator set_iter = selected_items.begin(); +    LLFolderViewModelItemInventory * viewModel = NULL; +    for (; set_iter != selected_items.end(); ++set_iter) +    { +        viewModel = dynamic_cast<LLFolderViewModelItemInventory *>((*set_iter)->getViewModelItem()); +        if (viewModel && gInventory.isObjectDescendentOf(viewModel->getInventoryObject()->getParentUUID(), marketplacelistings_id)) +        { +            sMarketplaceFolders.push_back(viewModel->getInventoryObject()->getParentUUID()); +            sMarketplaceFolders.push_back(viewModel->getInventoryObject()->getUUID()); +        } +    } +    // Suppress dupes in the list so we won't update listings twice +    sMarketplaceFolders.sort(); +    sMarketplaceFolders.unique(); +} + +void LLInventoryAction::updateMarketplaceFolders() +{ +    while (!sMarketplaceFolders.empty()) +    { +        update_marketplace_category(sMarketplaceFolders.back()); +        sMarketplaceFolders.pop_back(); +    } +} + + +  | 
