/** * @file llinventorylistener.cpp * * $LicenseInfo:firstyear=2024&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2024, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" #include "llinventorylistener.h" #include "llappearancemgr.h" #include "llinventoryfunctions.h" #include "lltransutil.h" #include "llwearableitemslist.h" #include "stringize.h" static const F32 MAX_ITEM_LIMIT = 100; LLInventoryListener::LLInventoryListener() : LLEventAPI("LLInventory", "API for interactions with viewer Inventory items") { add("getItemsInfo", "Return information about items or folders defined in [\"item_ids\"]:\n" "reply will contain [\"items\"] and [\"categories\"] tables accordingly", &LLInventoryListener::getItemsInfo, llsd::map("item_ids", LLSD(), "reply", LLSD())); add("getFolderTypeNames", "Return the table of folder type names, contained in [\"names\"]\n", &LLInventoryListener::getFolderTypeNames, llsd::map("reply", LLSD())); add("getAssetTypeNames", "Return the table of asset type names, contained in [\"names\"]\n", &LLInventoryListener::getAssetTypeNames, llsd::map("reply", LLSD())); add("getBasicFolderID", "Return the UUID of the folder by specified folder type name, for example:\n" "\"Textures\", \"My outfits\", \"Sounds\" and other basic folders which have associated type", &LLInventoryListener::getBasicFolderID, llsd::map("ft_name", LLSD(), "reply", LLSD())); add("getDirectDescendents", "Return the direct descendents(both items and folders) of the [\"folder_id\"]", &LLInventoryListener::getDirectDescendents, llsd::map("folder_id", LLSD(), "reply", LLSD())); add("collectDescendentsIf", "Return the descendents(both items and folders) of the [\"folder_id\"], if it passes specified filters:\n" "[\"name\"] is a substring of object's name,\n" "[\"desc\"] is a substring of object's description,\n" "asset [\"type\"] corresponds to the string name of the object's asset type\n" "[\"limit\"] sets item count limit in reply, maximum and default is 100\n" "[\"filter_links\"]: EXCLUDE_LINKS - don't show links, ONLY_LINKS - only show links, INCLUDE_LINKS - show links too (default)", &LLInventoryListener::collectDescendentsIf, llsd::map("folder_id", LLSD(), "reply", LLSD())); } void add_item_info(LLEventAPI::Response& response, LLViewerInventoryItem* item) { response["items"].insert(item->getUUID().asString(), llsd::map("name", item->getName(), "parent_id", item->getParentUUID(), "desc", item->getDescription(), "inv_type", LLInventoryType::lookup(item->getInventoryType()), "asset_type", LLAssetType::lookup(item->getType()), "creation_date", (S32) item->getCreationDate(), "asset_id", item->getAssetUUID(), "is_link", item->getIsLinkType(), "linked_id", item->getLinkedUUID())); } void add_cat_info(LLEventAPI::Response &response, LLViewerInventoryCategory *cat) { response["categories"].insert(cat->getUUID().asString(), llsd::map("name", cat->getName(), "parent_id", cat->getParentUUID(), "type", LLFolderType::lookup(cat->getPreferredType()))); } void add_objects_info(LLEventAPI::Response& response, LLInventoryModel::cat_array_t cat_array, LLInventoryModel::item_array_t item_array) { for (auto &p : item_array) { add_item_info(response, p); } for (auto &p : cat_array) { add_cat_info(response, p); } } void LLInventoryListener::getItemsInfo(LLSD const &data) { Response response(LLSD(), data); uuid_vec_t ids = LLSDParam(data["item_ids"]); for (auto &it : ids) { LLViewerInventoryItem* item = gInventory.getItem(it); if (item) { add_item_info(response, item); } else { LLViewerInventoryCategory *cat = gInventory.getCategory(it); if (cat) { add_cat_info(response, cat); } } } } void LLInventoryListener::getFolderTypeNames(LLSD const &data) { Response response(llsd::map("names", LLFolderType::getTypeNames()), data); } void LLInventoryListener::getAssetTypeNames(LLSD const &data) { Response response(llsd::map("names", LLAssetType::getTypeNames()), data); } void LLInventoryListener::getBasicFolderID(LLSD const &data) { Response response(llsd::map("id", gInventory.findCategoryUUIDForType(LLFolderType::lookup(data["ft_name"].asString()))), data); } void LLInventoryListener::getDirectDescendents(LLSD const &data) { Response response(LLSD(), data); LLInventoryModel::cat_array_t* cats; LLInventoryModel::item_array_t* items; gInventory.getDirectDescendentsOf(data["folder_id"], cats, items); add_objects_info(response, *cats, *items); } struct LLFilteredCollector : public LLInventoryCollectFunctor { enum EFilterLink { INCLUDE_LINKS, // show links too EXCLUDE_LINKS, // don't show links ONLY_LINKS // only show links }; LLFilteredCollector(LLSD const &data); virtual ~LLFilteredCollector() {} virtual bool operator()(LLInventoryCategory *cat, LLInventoryItem *item) override; virtual bool exceedsLimit() override { return (mItemLimit <= mItemCount); }; protected: bool checkagainstType(LLInventoryCategory *cat, LLInventoryItem *item); bool checkagainstNameDesc(LLInventoryCategory *cat, LLInventoryItem *item); bool checkagainstLinks(LLInventoryCategory *cat, LLInventoryItem *item); LLAssetType::EType mType; std::string mName; std::string mDesc; EFilterLink mLinkFilter; S32 mItemLimit; S32 mItemCount; }; void LLInventoryListener::collectDescendentsIf(LLSD const &data) { Response response(LLSD(), data); LLUUID folder_id(data["folder_id"].asUUID()); LLViewerInventoryCategory *cat = gInventory.getCategory(folder_id); if (!cat) { return response.error(stringize("Folder ", std::quoted(data["folder_id"].asString()), " was not found")); } LLInventoryModel::cat_array_t cat_array; LLInventoryModel::item_array_t item_array; LLFilteredCollector collector = LLFilteredCollector(data); gInventory.collectDescendentsIf(folder_id, cat_array, item_array, LLInventoryModel::EXCLUDE_TRASH, collector); add_objects_info(response, cat_array, item_array); } LLFilteredCollector::LLFilteredCollector(LLSD const &data) : mType(LLAssetType::EType::AT_UNKNOWN), mLinkFilter(INCLUDE_LINKS), mItemLimit(MAX_ITEM_LIMIT), mItemCount(0) { mName = data["name"].asString(); mDesc = data["desc"].asString(); if (data.has("type")) { mType = LLAssetType::lookup(data["type"]); } if (data.has("filter_links")) { if (data["filter_links"] == "EXCLUDE_LINKS") { mLinkFilter = EXCLUDE_LINKS; } else if (data["filter_links"] == "ONLY_LINKS") { mLinkFilter = ONLY_LINKS; } } if (data["limit"].isInteger()) { mItemLimit = llclamp(data["limit"].asInteger(), 1, MAX_ITEM_LIMIT); } } bool LLFilteredCollector::operator()(LLInventoryCategory *cat, LLInventoryItem *item) { bool passed = checkagainstType(cat, item); passed = passed && checkagainstNameDesc(cat, item); passed = passed && checkagainstLinks(cat, item); if (passed) { ++mItemCount; } return passed; } bool LLFilteredCollector::checkagainstNameDesc(LLInventoryCategory *cat, LLInventoryItem *item) { std::string name, desc; bool passed(true); if (cat) { if (!mDesc.empty()) return false; name = cat->getName(); } if (item) { name = item->getName(); passed = (mDesc.empty() || (item->getDescription().find(mDesc) != std::string::npos)); } return passed && (mName.empty() || name.find(mName) != std::string::npos); } bool LLFilteredCollector::checkagainstType(LLInventoryCategory *cat, LLInventoryItem *item) { if (mType == LLAssetType::AT_UNKNOWN) { return true; } if (cat && (mType == LLAssetType::AT_CATEGORY)) { return true; } if (item && item->getType() == mType) { return true; } return false; } bool LLFilteredCollector::checkagainstLinks(LLInventoryCategory *cat, LLInventoryItem *item) { bool is_link = cat ? cat->getIsLinkType() : item->getIsLinkType(); if (is_link && (mLinkFilter == EXCLUDE_LINKS)) return false; if (!is_link && (mLinkFilter == ONLY_LINKS)) return false; return true; }