Inventory Offer changes and clickable inventory slurls

master
Eric M. Tulla (BigPapi) 2009-11-20 19:27:33 -05:00
parent 521820ad8d
commit a3930c7ff7
9 changed files with 239 additions and 103 deletions

View File

@ -34,6 +34,7 @@
#include "linden_common.h"
#include "llurlentry.h"
#include "lluri.h"
#include "llcachename.h"
#include "lltrans.h"
#include "lluicolortable.h"
@ -383,6 +384,38 @@ std::string LLUrlEntryGroup::getLabel(const std::string &url, const LLUrlLabelCa
}
}
//
// LLUrlEntryInventory Describes a Second Life inventory Url, e.g.,
// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select
//
LLUrlEntryInventory::LLUrlEntryInventory()
{
mPattern = boost::regex("secondlife:///app/inventory/[\\da-f-]+/\\w+",
boost::regex::perl|boost::regex::icase);
mMenuName = "menu_url_inventory.xml";
}
std::string LLUrlEntryInventory::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
{
return unescapeUrl(url);
// TODO: Figure out if we can somehow access the inventory from here to get the actual item name
/*
std::string inventory_id_string = getIDStringFromUrl(url);
if (inventory_id_string.empty())
{
// something went wrong, give raw url
return unescapeUrl(url);
}
LLUUID inventory_id(inventory_id_string);
LLInventoryItem* item = gInventory.getItem(inventory_id);
if(!item)
{
return unescapeUrl(url);
}
return item->getName(); */
}
///
/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g.,
/// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about

View File

@ -173,6 +173,19 @@ private:
const std::string& last, BOOL is_group);
};
///
/// LLUrlEntryInventory Describes a Second Life inventory Url, e.g.,
/// secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select
///
class LLUrlEntryInventory : public LLUrlEntryBase
{
public:
LLUrlEntryInventory();
/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
private:
};
///
/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g.,
/// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about

View File

@ -55,6 +55,7 @@ LLUrlRegistry::LLUrlRegistry()
registerUrl(new LLUrlEntryPlace());
registerUrl(new LLUrlEntrySL());
registerUrl(new LLUrlEntrySLLabel());
registerUrl(new LLUrlEntryInventory());
}
LLUrlRegistry::~LLUrlRegistry()

View File

@ -113,7 +113,7 @@ public:
bool isEverythingComplete() const;
void fetchItems(const item_ref_t& ids);
virtual void done() = 0;
virtual void done() {};
protected:
item_ref_t mComplete;

View File

@ -56,11 +56,43 @@
#include "lltrans.h"
#include "llappearancemgr.h"
#include "llfloatercustomize.h"
#include "llcommandhandler.h"
#include "llviewermessage.h"
///----------------------------------------------------------------------------
/// Local function declarations, constants, enums, and typedefs
///----------------------------------------------------------------------------
class LLInventoryHandler : public LLCommandHandler
{
public:
// requires trusted browser to trigger
LLInventoryHandler() : LLCommandHandler("inventory", UNTRUSTED_THROTTLE) { }
bool handle(const LLSD& params, const LLSD& query_map,
LLMediaCtrl* web)
{
if (params.size() < 2) return false;
LLUUID inventory_id;
if (!inventory_id.set(params[0], FALSE))
{
return false;
}
const std::string verb = params[1].asString();
if (verb == "select")
{
std::vector<LLUUID> items_to_open;
items_to_open.push_back(inventory_id);
open_inventory_offer(items_to_open, "");
return true;
}
return false;
}
};
LLInventoryHandler gInventoryHandler;
///----------------------------------------------------------------------------
/// Class LLViewerInventoryItem
///----------------------------------------------------------------------------

View File

@ -726,7 +726,7 @@ public:
LLOpenAgentOffer(const std::string& from_name) : mFromName(from_name) {}
/*virtual*/ void done()
{
open_offer(mComplete, mFromName);
open_inventory_offer(mComplete, mFromName);
gInventory.removeObserver(this);
delete this;
}
@ -875,7 +875,7 @@ bool check_offer_throttle(const std::string& from_name, bool check_only)
}
}
void open_offer(const std::vector<LLUUID>& items, const std::string& from_name)
void open_inventory_offer(const std::vector<LLUUID>& items, const std::string& from_name)
{
std::vector<LLUUID>::const_iterator it = items.begin();
std::vector<LLUUID>::const_iterator end = items.end();
@ -1060,22 +1060,8 @@ LLSD LLOfferInfo::asLLSD()
return sd;
}
bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& response)
{
LLChat chat;
std::string log_message;
S32 button = LLNotification::getSelectedOption(notification, response);
// For muting, we need to add the mute, then decline the offer.
// This must be done here because:
// * callback may be called immediately,
// * adding the mute sends a message,
// * we can't build two messages at once.
if (2 == button)
{
gCacheName->get(mFromID, mFromGroup, &inventory_offer_mute_callback);
}
void LLOfferInfo::send_auto_receive_response(void)
{
LLMessageSystem* msg = gMessageSystem;
msg->newMessageFast(_PREHASH_ImprovedInstantMessage);
msg->nextBlockFast(_PREHASH_AgentData);
@ -1094,6 +1080,31 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD&
msg->addU32Fast(_PREHASH_ParentEstateID, 0);
msg->addUUIDFast(_PREHASH_RegionID, LLUUID::null);
msg->addVector3Fast(_PREHASH_Position, gAgent.getPositionAgent());
// Auto Receive Message. The math for the dialog works, because the accept
// for inventory_offered, task_inventory_offer or
// group_notice_inventory is 1 greater than the offer integer value.
// Generates IM_INVENTORY_ACCEPTED, IM_TASK_INVENTORY_ACCEPTED,
// or IM_GROUP_NOTICE_INVENTORY_ACCEPTED
msg->addU8Fast(_PREHASH_Dialog, (U8)(mIM + 1));
msg->addBinaryDataFast(_PREHASH_BinaryBucket, &(mFolderID.mData),
sizeof(mFolderID.mData));
// send the message
msg->sendReliable(mHost);
if(IM_INVENTORY_OFFERED == mIM)
{
// add buddy to recent people list
LLRecentPeople::instance().add(mFromID);
}
}
bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& response)
{
LLChat chat;
std::string log_message;
S32 button = LLNotification::getSelectedOption(notification, response);
LLInventoryObserver* opener = NULL;
LLViewerInventoryCategory* catp = NULL;
catp = (LLViewerInventoryCategory*)gInventory.getCategory(mObjectID);
@ -1102,6 +1113,16 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD&
{
itemp = (LLViewerInventoryItem*)gInventory.getItem(mObjectID);
}
// For muting, we need to add the mute, then decline the offer.
// This must be done here because:
// * callback may be called immediately,
// * adding the mute sends a message,
// * we can't build two messages at once.
if (2 == button)
{
gCacheName->get(mFromID, mFromGroup, &inventory_offer_mute_callback);
}
std::string from_string; // Used in the pop-up.
std::string chatHistory_string; // Used in chat history.
@ -1152,64 +1173,54 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD&
switch(button)
{
case IOR_ACCEPT:
// ACCEPT. The math for the dialog works, because the accept
// for inventory_offered, task_inventory_offer or
// group_notice_inventory is 1 greater than the offer integer value.
// Generates IM_INVENTORY_ACCEPTED, IM_TASK_INVENTORY_ACCEPTED,
// or IM_GROUP_NOTICE_INVENTORY_ACCEPTED
msg->addU8Fast(_PREHASH_Dialog, (U8)(mIM + 1));
msg->addBinaryDataFast(_PREHASH_BinaryBucket, &(mFolderID.mData),
sizeof(mFolderID.mData));
// send the message
msg->sendReliable(mHost);
//don't spam them if they are getting flooded
if (check_offer_throttle(mFromName, true))
{
log_message = chatHistory_string + " " + LLTrans::getString("InvOfferGaveYou") + " " + mDesc + LLTrans::getString(".");
chat.mText = log_message;
LLFloaterChat::addChatHistory(chat);
}
case IOR_SHOW:
// we will want to open this item when it comes back.
LL_DEBUGS("Messaging") << "Initializing an opener for tid: " << mTransactionID
<< LL_ENDL;
switch (mIM)
{
case IM_INVENTORY_OFFERED:
{
// This is an offer from an agent. In this case, the back
// end has already copied the items into your inventory,
// so we can fetch it out of our inventory.
LLInventoryFetchObserver::item_ref_t items;
items.push_back(mObjectID);
LLOpenAgentOffer* open_agent_offer = new LLOpenAgentOffer(from_string);
open_agent_offer->fetchItems(items);
if(catp || (itemp && itemp->isComplete()))
{
open_agent_offer->done();
// This is an offer from an agent. In this case, the back
// end has already copied the items into your inventory,
// so we can fetch it out of our inventory.
LLInventoryFetchObserver::item_ref_t items;
items.push_back(mObjectID);
LLOpenAgentOffer* open_agent_offer = new LLOpenAgentOffer(from_string);
open_agent_offer->fetchItems(items);
if(catp || (itemp && itemp->isComplete()))
{
open_agent_offer->done();
}
else
{
opener = open_agent_offer;
}
}
else
{
opener = open_agent_offer;
}
}
break;
case IM_TASK_INVENTORY_OFFERED:
case IM_GROUP_NOTICE:
case IM_GROUP_NOTICE_REQUESTED:
{
// This is an offer from a task or group.
// We don't use a new instance of an opener
// We instead use the singular observer gOpenTaskOffer
// Since it already exists, we don't need to actually do anything
}
break;
break;
default:
LL_WARNS("Messaging") << "inventory_offer_callback: unknown offer type" << LL_ENDL;
break;
} // end switch (mIM)
// Show falls through to accept.
case IOR_ACCEPT:
//don't spam them if they are getting flooded
if (check_offer_throttle(mFromName, true))
{
log_message = chatHistory_string + " " + LLTrans::getString("InvOfferGaveYou") + " " + mDesc + LLTrans::getString(".");
chat.mText = log_message;
LLFloaterChat::addChatHistory(chat);
}
break;
case IOR_BUSY:
@ -1218,31 +1229,15 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD&
case IOR_MUTE:
// MUTE falls through to decline
case IOR_DECLINE:
// DECLINE. The math for the dialog works, because the decline
// for inventory_offered, task_inventory_offer or
// group_notice_inventory is 2 greater than the offer integer value.
// Generates IM_INVENTORY_DECLINED, IM_TASK_INVENTORY_DECLINED,
// or IM_GROUP_NOTICE_INVENTORY_DECLINED
default:
// close button probably (or any of the fall-throughs from above)
msg->addU8Fast(_PREHASH_Dialog, (U8)(mIM + 2));
msg->addBinaryDataFast(_PREHASH_BinaryBucket, EMPTY_BINARY_BUCKET, EMPTY_BINARY_BUCKET_SIZE);
// send the message
msg->sendReliable(mHost);
log_message = LLTrans::getString("InvOfferYouDecline") + " " + mDesc + " " + LLTrans::getString("InvOfferFrom") + " " + mFromName +".";
chat.mText = log_message;
if( LLMuteList::getInstance()->isMuted(mFromID ) && ! LLMuteList::getInstance()->isLinden(mFromName) ) // muting for SL-42269
{
chat.mMuted = TRUE;
}
LLFloaterChat::addChatHistory(chat);
// If it's from an agent, we have to fetch the item to throw
// it away. If it's from a task or group, just denying the
// request will suffice to discard the item.
if(IM_INVENTORY_OFFERED == mIM)
{
log_message = LLTrans::getString("InvOfferYouDecline") + " " + mDesc + " " + LLTrans::getString("InvOfferFrom") + " " + mFromName +".";
chat.mText = log_message;
if( LLMuteList::getInstance()->isMuted(mFromID ) && ! LLMuteList::getInstance()->isLinden(mFromName) ) // muting for SL-42269
{
chat.mMuted = TRUE;
}
LLFloaterChat::addChatHistory(chat);
LLInventoryFetchComboObserver::folder_ref_t folders;
LLInventoryFetchComboObserver::item_ref_t items;
items.push_back(mObjectID);
@ -1258,20 +1253,21 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD&
opener = discard_agent_offer;
}
if (busy && (!mFromGroup && !mFromObject))
{
busy_message(gMessageSystem, mFromID);
}
break;
}
if (busy && (!mFromGroup && !mFromObject))
{
busy_message(msg,mFromID);
}
default:
// close button probably
// The item has already been fetched and is in your inventory, we simply won't highlight it
// OR delete it if the notification gets killed, since we don't want that to be a vector for
// losing inventory offers.
break;
}
if(IM_INVENTORY_OFFERED == mIM)
{
// add buddy to recent people list
LLRecentPeople::instance().add(mFromID);
}
if(opener)
{
gInventory.addObserver(opener);
@ -1382,7 +1378,25 @@ void inventory_offer_handler(LLOfferInfo* info, BOOL from_task)
{
p.name = "UserGiveItem";
}
// Prefetch the item into your local inventory.
LLInventoryFetchObserver::item_ref_t items;
items.push_back(info->mObjectID);
LLInventoryFetchObserver* fetch_item = new LLInventoryFetchObserver();
fetch_item->fetchItems(items);
if(fetch_item->isEverythingComplete())
{
fetch_item->done();
}
else
{
gInventory.addObserver(fetch_item);
}
// In viewer 2 we're now auto receiving inventory offers and messaging as such (not sending reject messages).
info->send_auto_receive_response();
// Pop up inv offer notification and let the user accept (keep), or reject (and silently delete) the inventory.
LLNotifications::instance().add(p);
}

View File

@ -59,7 +59,8 @@ enum InventoryOfferResponse
IOR_ACCEPT,
IOR_DECLINE,
IOR_MUTE,
IOR_BUSY
IOR_BUSY,
IOR_SHOW
};
BOOL can_afford_transaction(S32 cost);
@ -197,6 +198,7 @@ void invalid_message_callback(LLMessageSystem*, void*, EMessageException);
void process_initiate_download(LLMessageSystem* msg, void**);
void start_new_inventory_observer();
void open_inventory_offer(const std::vector<LLUUID>& items, const std::string& from_name);
struct LLOfferInfo
{
@ -218,6 +220,7 @@ struct LLOfferInfo
LLHost mHost;
LLSD asLLSD();
void send_auto_receive_response(void);
bool inventory_offer_callback(const LLSD& notification, const LLSD& response);
};

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<context_menu
layout="topleft"
name="Url Popup">
<menu_item_call
label="Show Inventory Item"
layout="topleft"
name="show_item">
<menu_item_call.on_click
function="Url.Execute" />
</menu_item_call>
<menu_item_separator
layout="topleft" />
<menu_item_call
label="Copy Name to clipboard"
layout="topleft"
name="url_copy_label">
<menu_item_call.on_click
function="Url.CopyLabel" />
</menu_item_call>
<menu_item_call
label="Copy SLurl to clipboard"
layout="topleft"
name="url_copy">
<menu_item_call.on_click
function="Url.CopyUrl" />
</menu_item_call>
</context_menu>

View File

@ -4487,7 +4487,7 @@ You don&apos;t have permission to copy this.
icon="notifytip.tga"
name="InventoryAccepted"
type="notifytip">
[NAME] accepted your inventory offer.
[NAME] received your inventory offer.
</notification>
<notification
@ -4959,17 +4959,21 @@ No valid parcel could be found.
icon="notify.tga"
name="ObjectGiveItem"
type="offer">
An object named [OBJECTFROMNAME] owned by [NAME] has offered you [OBJECTTYPE]:
An object named [OBJECTFROMNAME] owned by [NAME] has given you [OBJECTTYPE]:
[ITEM_SLURL]
<form name="form">
<button
index="0"
name="Keep"
text="OK"/>
text="Keep"/>
<button
index="4"
name="Show"
text="Show"/>
<button
index="1"
name="Discard"
text="Cancel"/>
text="Discard"/>
<button
index="2"
name="Mute"
@ -4986,11 +4990,15 @@ An object named [OBJECTFROMNAME] owned by (an unknown Resident) has given you a
<button
index="0"
name="Keep"
text="OK"/>
text="Keep"/>
<button
index="4"
name="Show"
text="Show"/>
<button
index="1"
name="Discard"
text="Cancel"/>
text="Discard"/>
<button
index="2"
name="Mute"
@ -5002,17 +5010,21 @@ An object named [OBJECTFROMNAME] owned by (an unknown Resident) has given you a
icon="notify.tga"
name="UserGiveItem"
type="offer">
[NAME] has offered you [OBJECTTYPE]:
[NAME] has given you [OBJECTTYPE]:
[ITEM_SLURL]
<form name="form">
<button
index="0"
name="Keep"
text="Accept"/>
text="Keep"/>
<button
index="4"
name="Show"
text="Show"/>
<button
index="1"
name="Discard"
text="No, thanks"/>
text="Discard"/>
</form>
</notification>
@ -5747,7 +5759,7 @@ Are you sure you want to delete your teleport history?
name="BottomTrayButtonCanNotBeShown"
type="alert">
Selected button can not be shown right now.
The button will be shown when there is enough space for it.
The button will be shown when there is enough space for it.
</notification>