-
Vadim ProductEngine authoredVadim ProductEngine authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
llassetuploadresponders.cpp 31.52 KiB
/**
* @file llassetuploadresponders.cpp
* @brief Processes responses received for asset upload requests.
*
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, 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 "llassetuploadresponders.h"
// viewer includes
#include "llagent.h"
#include "llcompilequeue.h"
#include "llbuycurrencyhtml.h"
#include "llfilepicker.h"
#include "llinventorydefines.h"
#include "llinventoryobserver.h"
#include "llinventorypanel.h"
#include "llpermissionsflags.h"
#include "llpreviewnotecard.h"
#include "llpreviewscript.h"
#include "llpreviewgesture.h"
#include "llgesturemgr.h"
#include "llstatusbar.h" // sendMoneyBalanceRequest()
#include "llsdserialize.h"
#include "lluploaddialog.h"
#include "llviewerobject.h"
#include "llviewercontrol.h"
#include "llviewerobjectlist.h"
#include "llviewermenufile.h"
#include "llviewerwindow.h"
#include "lltexlayer.h"
#include "lltrans.h"
// library includes
#include "lldir.h"
#include "lleconomy.h"
#include "llfloaterreg.h"
#include "llfocusmgr.h"
#include "llnotificationsutil.h"
#include "llscrolllistctrl.h"
#include "llsdserialize.h"
#include "llsdutil.h"
#include "llvfs.h"
// When uploading multiple files, don't display any of them when uploading more than this number.
static const S32 FILE_COUNT_DISPLAY_THRESHOLD = 5;
void dialog_refresh_all();
void on_new_single_inventory_upload_complete(
LLAssetType::EType asset_type,
LLInventoryType::EType inventory_type,
const std::string inventory_type_string,
const LLUUID& item_folder_id,
const std::string& item_name,
const std::string& item_description,
const LLSD& server_response,
S32 upload_price)
{
bool success = false;
if ( upload_price > 0 )
{
// this upload costed us L$, update our balance
// and display something saying that it cost L$
LLStatusBar::sendMoneyBalanceRequest();
LLSD args;
args["AMOUNT"] = llformat("%d", upload_price);
LLNotificationsUtil::add("UploadPayment", args);
}
if( item_folder_id.notNull() )
{
U32 everyone_perms = PERM_NONE;
U32 group_perms = PERM_NONE;
U32 next_owner_perms = PERM_ALL;
if( server_response.has("new_next_owner_mask") )
{
// The server provided creation perms so use them.
// Do not assume we got the perms we asked for in
// since the server may not have granted them all.
everyone_perms = server_response["new_everyone_mask"].asInteger();
group_perms = server_response["new_group_mask"].asInteger();
next_owner_perms = server_response["new_next_owner_mask"].asInteger();
}
else
{
// The server doesn't provide creation perms
// so use old assumption-based perms.
if( inventory_type_string != "snapshot")
{
next_owner_perms = PERM_MOVE | PERM_TRANSFER;
}
}
LLPermissions new_perms;
new_perms.init(
gAgent.getID(),
gAgent.getID(),
LLUUID::null,
LLUUID::null);
new_perms.initMasks(
PERM_ALL,
PERM_ALL,
everyone_perms,
group_perms,
next_owner_perms);
U32 inventory_item_flags = 0;
if (server_response.has("inventory_flags"))
{
inventory_item_flags = (U32) server_response["inventory_flags"].asInteger();
if (inventory_item_flags != 0)
{
llinfos << "inventory_item_flags " << inventory_item_flags << llendl;
}
}
S32 creation_date_now = time_corrected();
LLPointer<LLViewerInventoryItem> item = new LLViewerInventoryItem(
server_response["new_inventory_item"].asUUID(),
item_folder_id,
new_perms,
server_response["new_asset"].asUUID(),
asset_type,
inventory_type,
item_name,
item_description,
LLSaleInfo::DEFAULT,
inventory_item_flags,
creation_date_now);
gInventory.updateItem(item);
gInventory.notifyObservers();
success = true;
// Show the preview panel for textures and sounds to let
// user know that the image (or snapshot) arrived intact.
LLInventoryPanel* panel = LLInventoryPanel::getActiveInventoryPanel();
if ( panel )
{
LLFocusableElement* focus = gFocusMgr.getKeyboardFocus();
panel->setSelection(
server_response["new_inventory_item"].asUUID(),
TAKE_FOCUS_NO);
// restore keyboard focus
gFocusMgr.setKeyboardFocus(focus);
}
}
else
{
llwarns << "Can't find a folder to put it in" << llendl;
}
// remove the "Uploading..." message
LLUploadDialog::modalUploadFinished();
// Let the Snapshot floater know we have finished uploading a snapshot to inventory.
LLFloater* floater_snapshot = LLFloaterReg::findInstance("snapshot");
if (asset_type == LLAssetType::AT_TEXTURE && floater_snapshot)
{
floater_snapshot->notify(LLSD().with("set-finished", LLSD().with("ok", success).with("msg", "inventory")));
}
}
LLAssetUploadResponder::LLAssetUploadResponder(const LLSD &post_data,
const LLUUID& vfile_id,
LLAssetType::EType asset_type)
: LLHTTPClient::Responder(),
mPostData(post_data),
mVFileID(vfile_id),
mAssetType(asset_type)
{
if (!gVFS->getExists(vfile_id, asset_type))
{
llwarns << "LLAssetUploadResponder called with nonexistant vfile_id" << llendl;
mVFileID.setNull();
mAssetType = LLAssetType::AT_NONE;
return;
}
}
LLAssetUploadResponder::LLAssetUploadResponder(
const LLSD &post_data,
const std::string& file_name,
LLAssetType::EType asset_type)
: LLHTTPClient::Responder(),
mPostData(post_data),
mFileName(file_name),
mAssetType(asset_type)
{
}
LLAssetUploadResponder::~LLAssetUploadResponder()
{
if (!mFileName.empty())
{
// Delete temp file
LLFile::remove(mFileName);
}
}
// virtual
void LLAssetUploadResponder::error(U32 statusNum, const std::string& reason)
{
llinfos << "LLAssetUploadResponder::error " << statusNum
<< " reason: " << reason << llendl;
LLSD args;
switch(statusNum)
{
case 400:
args["FILE"] = (mFileName.empty() ? mVFileID.asString() : mFileName);
args["REASON"] = "Error in upload request. Please visit "
"http://secondlife.com/support for help fixing this problem.";
LLNotificationsUtil::add("CannotUploadReason", args);
break;
case 500:
default:
args["FILE"] = (mFileName.empty() ? mVFileID.asString() : mFileName);
args["REASON"] = "The server is experiencing unexpected "
"difficulties.";
LLNotificationsUtil::add("CannotUploadReason", args);
break;
}
LLUploadDialog::modalUploadFinished();
LLFilePicker::instance().reset(); // unlock file picker when bulk upload fails
}
//virtual
void LLAssetUploadResponder::result(const LLSD& content)
{
lldebugs << "LLAssetUploadResponder::result from capabilities" << llendl;
std::string state = content["state"];
if (state == "upload")
{
uploadUpload(content);
}
else if (state == "complete")
{
// rename file in VFS with new asset id
if (mFileName.empty())
{
// rename the file in the VFS to the actual asset id
// llinfos << "Changing uploaded asset UUID to " << content["new_asset"].asUUID() << llendl;
gVFS->renameFile(mVFileID, mAssetType, content["new_asset"].asUUID(), mAssetType);
}
uploadComplete(content);
}
else
{
uploadFailure(content);
}
}
void LLAssetUploadResponder::uploadUpload(const LLSD& content)
{
std::string uploader = content["uploader"];
if (mFileName.empty())
{
LLHTTPClient::postFile(uploader, mVFileID, mAssetType, this);
}
else
{
LLHTTPClient::postFile(uploader, mFileName, this);
}
}
void LLAssetUploadResponder::uploadFailure(const LLSD& content)
{
// remove the "Uploading..." message
LLUploadDialog::modalUploadFinished();
LLFloater* floater_snapshot = LLFloaterReg::findInstance("snapshot");
if (floater_snapshot)
{
floater_snapshot->notify(LLSD().with("set-finished", LLSD().with("ok", false).with("msg", "inventory")));
}
std::string reason = content["state"];
// deal with L$ errors
if (reason == "insufficient funds")
{
S32 price = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
LLStringUtil::format_map_t args;
args["AMOUNT"] = llformat("%d", price);
LLBuyCurrencyHTML::openCurrencyFloater( LLTrans::getString("uploading_costs", args), price );
}
else
{
LLSD args;
args["FILE"] = (mFileName.empty() ? mVFileID.asString() : mFileName);
args["REASON"] = content["message"].asString();
LLNotificationsUtil::add("CannotUploadReason", args);
}
}
void LLAssetUploadResponder::uploadComplete(const LLSD& content)
{
}
LLNewAgentInventoryResponder::LLNewAgentInventoryResponder(
const LLSD& post_data,
const LLUUID& vfile_id,
LLAssetType::EType asset_type)
: LLAssetUploadResponder(post_data, vfile_id, asset_type)
{
}
LLNewAgentInventoryResponder::LLNewAgentInventoryResponder(
const LLSD& post_data,
const std::string& file_name,
LLAssetType::EType asset_type)
: LLAssetUploadResponder(post_data, file_name, asset_type)
{
}
// virtual
void LLNewAgentInventoryResponder::error(U32 statusNum, const std::string& reason)
{
LLAssetUploadResponder::error(statusNum, reason);
//LLImportColladaAssetCache::getInstance()->assetUploaded(mVFileID, LLUUID(), FALSE);
}
//virtual
void LLNewAgentInventoryResponder::uploadFailure(const LLSD& content)
{
LLAssetUploadResponder::uploadFailure(content);
//LLImportColladaAssetCache::getInstance()->assetUploaded(mVFileID, content["new_asset"], FALSE);
}
//virtual
void LLNewAgentInventoryResponder::uploadComplete(const LLSD& content)
{
lldebugs << "LLNewAgentInventoryResponder::result from capabilities" << llendl;
//std::ostringstream llsdxml;
//LLSDSerialize::toXML(content, llsdxml);
//llinfos << "upload complete content:\n " << llsdxml.str() << llendl;
LLAssetType::EType asset_type = LLAssetType::lookup(mPostData["asset_type"].asString());
LLInventoryType::EType inventory_type = LLInventoryType::lookup(mPostData["inventory_type"].asString());
S32 expected_upload_cost = 0;
// Update L$ and ownership credit information
// since it probably changed on the server
if (asset_type == LLAssetType::AT_TEXTURE ||
asset_type == LLAssetType::AT_SOUND ||
asset_type == LLAssetType::AT_ANIMATION ||
asset_type == LLAssetType::AT_MESH)
{
expected_upload_cost =
LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
}
on_new_single_inventory_upload_complete(
asset_type,
inventory_type,
mPostData["asset_type"].asString(),
mPostData["folder_id"].asUUID(),
mPostData["name"],
mPostData["description"],
content,
expected_upload_cost);
// continue uploading for bulk uploads
// *FIX: This is a pretty big hack. What this does is check the
// file picker if there are any more pending uploads. If so,
// upload that file.
std::string next_file = LLFilePicker::instance().getNextFile();
if(!next_file.empty())
{
std::string name = gDirUtilp->getBaseFileName(next_file, true);
std::string asset_name = name;
LLStringUtil::replaceNonstandardASCII( asset_name, '?' );
LLStringUtil::replaceChar(asset_name, '|', '?');
LLStringUtil::stripNonprintable(asset_name);
LLStringUtil::trim(asset_name);
// Continuing the horrible hack above, we need to extract the originally requested permissions data, if any,
// and use them for each next file to be uploaded. Note the requested perms are not the same as the
U32 everyone_perms =
content.has("new_everyone_mask") ?
content["new_everyone_mask"].asInteger() :
PERM_NONE;
U32 group_perms =
content.has("new_group_mask") ?
content["new_group_mask"].asInteger() :
PERM_NONE;
U32 next_owner_perms =
content.has("new_next_owner_mask") ?
content["new_next_owner_mask"].asInteger() :
PERM_NONE;
std::string display_name = LLStringUtil::null;
LLAssetStorage::LLStoreAssetCallback callback = NULL;
void *userdata = NULL;
upload_new_resource(
next_file,
asset_name,
asset_name,
0,
LLFolderType::FT_NONE,
LLInventoryType::IT_NONE,
next_owner_perms,
group_perms,
everyone_perms,
display_name,
callback,
LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(),
userdata);
}
//LLImportColladaAssetCache::getInstance()->assetUploaded(mVFileID, content["new_asset"], TRUE);
}
LLSendTexLayerResponder::LLSendTexLayerResponder(const LLSD& post_data,
const LLUUID& vfile_id,
LLAssetType::EType asset_type,
LLBakedUploadData * baked_upload_data) :
LLAssetUploadResponder(post_data, vfile_id, asset_type),
mBakedUploadData(baked_upload_data)
{
}
LLSendTexLayerResponder::~LLSendTexLayerResponder()
{
// mBakedUploadData is normally deleted by calls to LLTexLayerSetBuffer::onTextureUploadComplete() below
if (mBakedUploadData)
{ // ...but delete it in the case where uploadComplete() is never called
delete mBakedUploadData;
mBakedUploadData = NULL;
}
}
// Baked texture upload completed
void LLSendTexLayerResponder::uploadComplete(const LLSD& content)
{
LLUUID item_id = mPostData["item_id"];
std::string result = content["state"];
LLUUID new_id = content["new_asset"];
llinfos << "result: " << result << " new_id: " << new_id << llendl;
if (result == "complete"
&& mBakedUploadData != NULL)
{ // Invoke
LLTexLayerSetBuffer::onTextureUploadComplete(new_id, (void*) mBakedUploadData, 0, LL_EXSTAT_NONE);
mBakedUploadData = NULL; // deleted in onTextureUploadComplete()
}
else
{ // Invoke the original callback with an error result
LLTexLayerSetBuffer::onTextureUploadComplete(new_id, (void*) mBakedUploadData, -1, LL_EXSTAT_NONE);
mBakedUploadData = NULL; // deleted in onTextureUploadComplete()
}
}
void LLSendTexLayerResponder::error(U32 statusNum, const std::string& reason)
{
llinfos << "status: " << statusNum << " reason: " << reason << llendl;
// Invoke the original callback with an error result
LLTexLayerSetBuffer::onTextureUploadComplete(LLUUID(), (void*) mBakedUploadData, -1, LL_EXSTAT_NONE);
mBakedUploadData = NULL; // deleted in onTextureUploadComplete()
}
LLUpdateAgentInventoryResponder::LLUpdateAgentInventoryResponder(
const LLSD& post_data,
const LLUUID& vfile_id,
LLAssetType::EType asset_type)
: LLAssetUploadResponder(post_data, vfile_id, asset_type)
{
}
LLUpdateAgentInventoryResponder::LLUpdateAgentInventoryResponder(
const LLSD& post_data,
const std::string& file_name,
LLAssetType::EType asset_type)
: LLAssetUploadResponder(post_data, file_name, asset_type)
{
}
//virtual
void LLUpdateAgentInventoryResponder::uploadComplete(const LLSD& content)
{
llinfos << "LLUpdateAgentInventoryResponder::result from capabilities" << llendl;
LLUUID item_id = mPostData["item_id"];
LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem(item_id);
if(!item)
{
llwarns << "Inventory item for " << mVFileID
<< " is no longer in agent inventory." << llendl;
return;
}
// Update viewer inventory item
LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
new_item->setAssetUUID(content["new_asset"].asUUID());
gInventory.updateItem(new_item);
gInventory.notifyObservers();
llinfos << "Inventory item " << item->getName() << " saved into "
<< content["new_asset"].asString() << llendl;
LLInventoryType::EType inventory_type = new_item->getInventoryType();
switch(inventory_type)
{
case LLInventoryType::IT_NOTECARD:
{
// Update the UI with the new asset.
LLPreviewNotecard* nc = LLFloaterReg::findTypedInstance<LLPreviewNotecard>("preview_notecard", LLSD(item_id));
if(nc)
{
// *HACK: we have to delete the asset in the VFS so
// that the viewer will redownload it. This is only
// really necessary if the asset had to be modified by
// the uploader, so this can be optimized away in some
// cases. A better design is to have a new uuid if the
// script actually changed the asset.
if(nc->hasEmbeddedInventory())
{
gVFS->removeFile(content["new_asset"].asUUID(), LLAssetType::AT_NOTECARD);
}
nc->refreshFromInventory(new_item->getUUID());
}
break;
}
case LLInventoryType::IT_LSL:
{
// Find our window and close it if requested.
LLPreviewLSL* preview = LLFloaterReg::findTypedInstance<LLPreviewLSL>("preview_script", LLSD(item_id));
if (preview)
{
// Bytecode save completed
if (content["compiled"])
{
preview->callbackLSLCompileSucceeded();
}
else
{
preview->callbackLSLCompileFailed(content["errors"]);
}
}
break;
}
case LLInventoryType::IT_GESTURE:
{
// If this gesture is active, then we need to update the in-memory
// active map with the new pointer.
if (LLGestureMgr::instance().isGestureActive(item_id))
{
LLUUID asset_id = new_item->getAssetUUID();
LLGestureMgr::instance().replaceGesture(item_id, asset_id);
gInventory.notifyObservers();
}
//gesture will have a new asset_id
LLPreviewGesture* previewp = LLFloaterReg::findTypedInstance<LLPreviewGesture>("preview_gesture", LLSD(item_id));
if(previewp)
{
previewp->onUpdateSucceeded();
}
break;
}
case LLInventoryType::IT_WEARABLE:
default:
break;
}
}
LLUpdateTaskInventoryResponder::LLUpdateTaskInventoryResponder(const LLSD& post_data,
const LLUUID& vfile_id,
LLAssetType::EType asset_type)
: LLAssetUploadResponder(post_data, vfile_id, asset_type)
{
}
LLUpdateTaskInventoryResponder::LLUpdateTaskInventoryResponder(const LLSD& post_data,
const std::string& file_name,
LLAssetType::EType asset_type)
: LLAssetUploadResponder(post_data, file_name, asset_type)
{
}
LLUpdateTaskInventoryResponder::LLUpdateTaskInventoryResponder(const LLSD& post_data,
const std::string& file_name,
const LLUUID& queue_id,
LLAssetType::EType asset_type)
: LLAssetUploadResponder(post_data, file_name, asset_type), mQueueId(queue_id)
{
}
//virtual
void LLUpdateTaskInventoryResponder::uploadComplete(const LLSD& content)
{
llinfos << "LLUpdateTaskInventoryResponder::result from capabilities" << llendl;
LLUUID item_id = mPostData["item_id"];
LLUUID task_id = mPostData["task_id"];
dialog_refresh_all();
switch(mAssetType)
{
case LLAssetType::AT_NOTECARD:
{
// Update the UI with the new asset.
LLPreviewNotecard* nc = LLFloaterReg::findTypedInstance<LLPreviewNotecard>("preview_notecard", LLSD(item_id));
if(nc)
{
// *HACK: we have to delete the asset in the VFS so
// that the viewer will redownload it. This is only
// really necessary if the asset had to be modified by
// the uploader, so this can be optimized away in some
// cases. A better design is to have a new uuid if the
// script actually changed the asset.
if(nc->hasEmbeddedInventory())
{
gVFS->removeFile(content["new_asset"].asUUID(),
LLAssetType::AT_NOTECARD);
}
nc->setAssetId(content["new_asset"].asUUID());
nc->refreshFromInventory();
}
break;
}
case LLAssetType::AT_LSL_TEXT:
{
if(mQueueId.notNull())
{
LLFloaterCompileQueue* queue = LLFloaterReg::findTypedInstance<LLFloaterCompileQueue>("compile_queue", mQueueId);
if(NULL != queue)
{
queue->removeItemByItemID(item_id);
}
}
else
{
LLLiveLSLEditor* preview = LLFloaterReg::findTypedInstance<LLLiveLSLEditor>("preview_scriptedit", LLSD(item_id));
if (preview)
{
// Bytecode save completed
if (content["compiled"])
{
preview->callbackLSLCompileSucceeded(task_id, item_id, mPostData["is_script_running"]);
}
else
{
preview->callbackLSLCompileFailed(content["errors"]);
}
}
}
break;
}
default:
break;
}
}
/////////////////////////////////////////////////////
// LLNewAgentInventoryVariablePriceResponder::Impl //
/////////////////////////////////////////////////////
class LLNewAgentInventoryVariablePriceResponder::Impl
{
public:
Impl(
const LLUUID& vfile_id,
LLAssetType::EType asset_type,
const LLSD& inventory_data) :
mVFileID(vfile_id),
mAssetType(asset_type),
mInventoryData(inventory_data),
mFileName("")
{
if (!gVFS->getExists(vfile_id, asset_type))
{
llwarns
<< "LLAssetUploadResponder called with nonexistant "
<< "vfile_id " << vfile_id << llendl;
mVFileID.setNull();
mAssetType = LLAssetType::AT_NONE;
}
}
Impl(
const std::string& file_name,
LLAssetType::EType asset_type,
const LLSD& inventory_data) :
mFileName(file_name),
mAssetType(asset_type),
mInventoryData(inventory_data)
{
mVFileID.setNull();
}
std::string getFilenameOrIDString() const
{
return (mFileName.empty() ? mVFileID.asString() : mFileName);
}
LLUUID getVFileID() const
{
return mVFileID;
}
std::string getFilename() const
{
return mFileName;
}
LLAssetType::EType getAssetType() const
{
return mAssetType;
}
LLInventoryType::EType getInventoryType() const
{
return LLInventoryType::lookup(
mInventoryData["inventory_type"].asString());
}
std::string getInventoryTypeString() const
{
return mInventoryData["inventory_type"].asString();
}
LLUUID getFolderID() const
{
return mInventoryData["folder_id"].asUUID();
}
std::string getItemName() const
{
return mInventoryData["name"].asString();
}
std::string getItemDescription() const
{
return mInventoryData["description"].asString();
}
void displayCannotUploadReason(const std::string& reason)
{
LLSD args;
args["FILE"] = getFilenameOrIDString();
args["REASON"] = reason;
LLNotificationsUtil::add("CannotUploadReason", args);
LLUploadDialog::modalUploadFinished();
}
void onApplicationLevelError(const LLSD& error)
{
static const std::string _IDENTIFIER = "identifier";
static const std::string _INSUFFICIENT_FUNDS =
"NewAgentInventory_InsufficientLindenDollarBalance";
static const std::string _MISSING_REQUIRED_PARAMETER =
"NewAgentInventory_MissingRequiredParamater";
static const std::string _INVALID_REQUEST_BODY =
"NewAgentInventory_InvalidRequestBody";
static const std::string _RESOURCE_COST_DIFFERS =
"NewAgentInventory_ResourceCostDiffers";
static const std::string _MISSING_PARAMETER = "missing_parameter";
static const std::string _INVALID_PARAMETER = "invalid_parameter";
static const std::string _MISSING_RESOURCE = "missing_resource";
static const std::string _INVALID_RESOURCE = "invalid_resource";
// TODO* Add the other error_identifiers
std::string error_identifier = error[_IDENTIFIER].asString();
// TODO*: Pull these user visible strings from an xml file
// to be localized
if ( _INSUFFICIENT_FUNDS == error_identifier )
{
displayCannotUploadReason("You do not have a sufficient L$ balance to complete this upload.");
}
else if ( _MISSING_REQUIRED_PARAMETER == error_identifier )
{
// Missing parameters
if (error.has(_MISSING_PARAMETER) )
{
std::string message =
"Upload request was missing required parameter '[P]'";
LLStringUtil::replaceString(
message,
"[P]",
error[_MISSING_PARAMETER].asString());
displayCannotUploadReason(message);
}
else
{
std::string message =
"Upload request was missing a required parameter";
displayCannotUploadReason(message);
}
}
else if ( _INVALID_REQUEST_BODY == error_identifier )
{
// Invalid request body, check to see if
// a particular parameter was invalid
if ( error.has(_INVALID_PARAMETER) )
{
std::string message = "Upload parameter '[P]' is invalid.";
LLStringUtil::replaceString(
message,
"[P]",
error[_INVALID_PARAMETER].asString());
// See if the server also responds with what resource
// is missing.
if ( error.has(_MISSING_RESOURCE) )
{
message += "\nMissing resource '[R]'.";
LLStringUtil::replaceString(
message,
"[R]",
error[_MISSING_RESOURCE].asString());
}
else if ( error.has(_INVALID_RESOURCE) )
{
message += "\nInvalid resource '[R]'.";
LLStringUtil::replaceString(
message,
"[R]",
error[_INVALID_RESOURCE].asString());
}
displayCannotUploadReason(message);
}
else
{
std::string message = "Upload request was malformed";
displayCannotUploadReason(message);
}
}
else if ( _RESOURCE_COST_DIFFERS == error_identifier )
{
displayCannotUploadReason("The resource cost associated with this upload is not consistent with the server.");
}
else
{
displayCannotUploadReason("Unknown Error");
}
}
void onTransportError()
{
displayCannotUploadReason(
"The server is experiencing unexpected difficulties.");
}
void onTransportError(const LLSD& error)
{
static const std::string _IDENTIFIER = "identifier";
static const std::string _SERVER_ERROR_AFTER_CHARGE =
"NewAgentInventory_ServerErrorAfterCharge";
std::string error_identifier = error[_IDENTIFIER].asString();
// TODO*: Pull the user visible strings from an xml file
// to be localized
if ( _SERVER_ERROR_AFTER_CHARGE == error_identifier )
{
displayCannotUploadReason(
"The server is experiencing unexpected difficulties. You may have been charged for the upload.");
}
else
{
displayCannotUploadReason(
"The server is experiencing unexpected difficulties.");
}
}
bool uploadConfirmationCallback(
const LLSD& notification,
const LLSD& response,
boost::intrusive_ptr<LLNewAgentInventoryVariablePriceResponder> responder)
{
S32 option;
std::string confirmation_url;
option = LLNotificationsUtil::getSelectedOption(
notification,
response);
confirmation_url =
notification["payload"]["confirmation_url"].asString();
// Yay! We are confirming or cancelling our upload
switch(option)
{
case 0:
{
confirmUpload(confirmation_url, responder);
}
break;
case 1:
default:
break;
}
return false;
}
void confirmUpload(
const std::string& confirmation_url,
boost::intrusive_ptr<LLNewAgentInventoryVariablePriceResponder> responder)
{
if ( getFilename().empty() )
{
// we have no filename, use virtual file ID instead
LLHTTPClient::postFile(
confirmation_url,
getVFileID(),
getAssetType(),
responder);
}
else
{
LLHTTPClient::postFile(
confirmation_url,
getFilename(),
responder);
}
}
private:
std::string mFileName;
LLSD mInventoryData;
LLAssetType::EType mAssetType;
LLUUID mVFileID;
};
///////////////////////////////////////////////
// LLNewAgentInventoryVariablePriceResponder //
///////////////////////////////////////////////
LLNewAgentInventoryVariablePriceResponder::LLNewAgentInventoryVariablePriceResponder(
const LLUUID& vfile_id,
LLAssetType::EType asset_type,
const LLSD& inventory_info)
{
mImpl = new Impl(
vfile_id,
asset_type,
inventory_info);
}
LLNewAgentInventoryVariablePriceResponder::LLNewAgentInventoryVariablePriceResponder(
const std::string& file_name,
LLAssetType::EType asset_type,
const LLSD& inventory_info)
{
mImpl = new Impl(
file_name,
asset_type,
inventory_info);
}
LLNewAgentInventoryVariablePriceResponder::~LLNewAgentInventoryVariablePriceResponder()
{
delete mImpl;
}
void LLNewAgentInventoryVariablePriceResponder::errorWithContent(
U32 statusNum,
const std::string& reason,
const LLSD& content)
{
lldebugs
<< "LLNewAgentInventoryVariablePrice::error " << statusNum
<< " reason: " << reason << llendl;
if ( content.has("error") )
{
static const std::string _ERROR = "error";
mImpl->onTransportError(content[_ERROR]);
}
else
{
mImpl->onTransportError();
}
}
void LLNewAgentInventoryVariablePriceResponder::result(const LLSD& content)
{
// Parse out application level errors and the appropriate
// responses for them
static const std::string _ERROR = "error";
static const std::string _STATE = "state";
static const std::string _COMPLETE = "complete";
static const std::string _CONFIRM_UPLOAD = "confirm_upload";
static const std::string _UPLOAD_PRICE = "upload_price";
static const std::string _RESOURCE_COST = "resource_cost";
static const std::string _RSVP = "rsvp";
// Check for application level errors
if ( content.has(_ERROR) )
{
onApplicationLevelError(content[_ERROR]);
return;
}
std::string state = content[_STATE];
LLAssetType::EType asset_type = mImpl->getAssetType();
if ( _COMPLETE == state )
{
// rename file in VFS with new asset id
if (mImpl->getFilename().empty())
{
// rename the file in the VFS to the actual asset id
// llinfos << "Changing uploaded asset UUID to " << content["new_asset"].asUUID() << llendl;
gVFS->renameFile(
mImpl->getVFileID(),
asset_type,
content["new_asset"].asUUID(),
asset_type);
}
on_new_single_inventory_upload_complete(
asset_type,
mImpl->getInventoryType(),
mImpl->getInventoryTypeString(),
mImpl->getFolderID(),
mImpl->getItemName(),
mImpl->getItemDescription(),
content,
content[_UPLOAD_PRICE].asInteger());
// TODO* Add bulk (serial) uploading or add
// a super class of this that does so
}
else if ( _CONFIRM_UPLOAD == state )
{
showConfirmationDialog(
content[_UPLOAD_PRICE].asInteger(),
content[_RESOURCE_COST].asInteger(),
content[_RSVP].asString());
}
else
{
onApplicationLevelError("");
}
}
void LLNewAgentInventoryVariablePriceResponder::onApplicationLevelError(
const LLSD& error)
{
mImpl->onApplicationLevelError(error);
}
void LLNewAgentInventoryVariablePriceResponder::showConfirmationDialog(
S32 upload_price,
S32 resource_cost,
const std::string& confirmation_url)
{
if ( 0 == upload_price )
{
// don't show confirmation dialog for free uploads, I mean,
// they're free!
// The creating of a new instrusive_ptr(this)
// creates a new boost::intrusive_ptr
// which is a copy of this. This code is required because
// 'this' is always of type Class* and not the intrusive_ptr,
// and thus, a reference to 'this' is not registered
// by using just plain 'this'.
// Since LLNewAgentInventoryVariablePriceResponder is a
// reference counted class, it is possible (since the
// reference to a plain 'this' would be missed here) that,
// when using plain ol' 'this', that this object
// would be deleted before the callback is triggered
// and cause sadness.
mImpl->confirmUpload(
confirmation_url,
boost::intrusive_ptr<LLNewAgentInventoryVariablePriceResponder>(this));
}
else
{
LLSD substitutions;
LLSD payload;
substitutions["PRICE"] = upload_price;
payload["confirmation_url"] = confirmation_url;
// The creating of a new instrusive_ptr(this)
// creates a new boost::intrusive_ptr
// which is a copy of this. This code is required because
// 'this' is always of type Class* and not the intrusive_ptr,
// and thus, a reference to 'this' is not registered
// by using just plain 'this'.
// Since LLNewAgentInventoryVariablePriceResponder is a
// reference counted class, it is possible (since the
// reference to a plain 'this' would be missed here) that,
// when using plain ol' 'this', that this object
// would be deleted before the callback is triggered
// and cause sadness.
LLNotificationsUtil::add(
"UploadCostConfirmation",
substitutions,
payload,
boost::bind(
&LLNewAgentInventoryVariablePriceResponder::Impl::uploadConfirmationCallback,
mImpl,
_1,
_2,
boost::intrusive_ptr<LLNewAgentInventoryVariablePriceResponder>(this)));
}
}