Upload-to-Flickr

master
Alexie Birman 2011-04-10 15:23:44 +01:00
parent be1566309a
commit 882c80b778
21 changed files with 1497 additions and 39 deletions

View File

@ -125,6 +125,10 @@ set(VIEWER ON CACHE BOOL "Build Firestorm viewer.")
set(VIEWER_CHANNEL "FirestormPrivate" CACHE STRING "Viewer Channel Name")
set(VIEWER_LOGIN_CHANNEL ${VIEWER_CHANNEL} CACHE STRING "Fake login channel for A/B Testing")
# Flickr API keys.
set(FLICKR_API_KEY "ebc94a4d2651c33404b0fb8ee1b78958")
set(FLICKR_API_SECRET "73efdfa10ebe7625")
set(STANDALONE OFF CACHE BOOL "Do not use Linden-supplied prebuilt libraries.")
if (NOT STANDALONE AND EXISTS ${CMAKE_SOURCE_DIR}/llphysics)

View File

@ -180,6 +180,18 @@ std::string LLURI::escape(const std::string& str)
return escape(str, default_allowed, true);
}
//static
std::string LLURI::escapeQueryValue(const std::string& s)
{
return ::escapeQueryValue(s);
}
//static
std::string LLURI::escapeQueryVariable(const std::string& s)
{
return ::escapeQueryVariable(s);
}
LLURI::LLURI()
{
}
@ -595,10 +607,10 @@ std::string LLURI::mapToQueryString(const LLSD& queryMap)
{
ostr << "&";
}
ostr << escapeQueryVariable(iter->first);
ostr << ::escapeQueryVariable(iter->first);
if(iter->second.isDefined())
{
ostr << "=" << escapeQueryValue(iter->second.asString());
ostr << "=" << ::escapeQueryValue(iter->second.asString());
}
}
query_string = ostr.str();

View File

@ -134,6 +134,28 @@ public:
*/
static std::string escape(const std::string& str);
/**
* @brief The same as escape, but also does not escape:
* :@!$'()*+,=
*
* @see http://www.ietf.org/rfc/rfc1738.txt
*
* @param str The raw URI to escape.
* @return Returns the escaped uri or an empty string.
*/
static std::string escapeQueryValue(const std::string& str);
/**
* @brief The same as escape, but also does not escape:
* :@!$'()*+,
*
* @see http://www.ietf.org/rfc/rfc1738.txt
*
* @param str The raw URI to escape.
* @return Returns the escaped uri or an empty string.
*/
static std::string escapeQueryVariable(const std::string& str);
/**
* @brief Escape a string with a specified set of allowed characters.
*

View File

@ -77,6 +77,23 @@ BOOL LLXmlTree::parseFile(const std::string &path, BOOL keep_contents)
return success;
}
BOOL LLXmlTree::parseString(const std::string &string, BOOL keep_contents)
{
delete mRoot;
mRoot = NULL;
LLXmlTreeParser parser(this);
BOOL success = parser.parseString( string, &mRoot, keep_contents );
if( !success )
{
S32 line_number = parser.getCurrentLineNumber();
const char* error = parser.getErrorString();
llwarns << "LLXmlTree parse failed. Line " << line_number << ": " << error << llendl;
}
return success;
}
void LLXmlTree::dump()
{
if( mRoot )
@ -517,14 +534,35 @@ BOOL LLXmlTreeParser::parseFile(const std::string &path, LLXmlTreeNode** root, B
{
llassert( !mRoot );
llassert( !mCurrent );
mKeepContents = keep_contents;
BOOL success = LLXmlParser::parseFile(path);
*root = mRoot;
mRoot = NULL;
if( success )
{
llassert( !mCurrent );
}
mCurrent = NULL;
return success;
}
BOOL LLXmlTreeParser::parseString(const std::string &string, LLXmlTreeNode** root, BOOL keep_contents)
{
llassert( !mRoot );
llassert( !mCurrent );
mKeepContents = keep_contents;
BOOL success = LLXmlParser::parse(string.c_str(), string.length(), true);
*root = mRoot;
mRoot = NULL;
if( success )
{
llassert( !mCurrent );

View File

@ -56,6 +56,7 @@ public:
void cleanup();
virtual BOOL parseFile(const std::string &path, BOOL keep_contents = TRUE);
virtual BOOL parseString(const std::string &string, BOOL keep_contents = TRUE);
LLXmlTreeNode* getRoot() { return mRoot; }
@ -200,6 +201,7 @@ public:
virtual ~LLXmlTreeParser();
BOOL parseFile(const std::string &path, LLXmlTreeNode** root, BOOL keep_contents );
BOOL parseString(const std::string &string, LLXmlTreeNode** root, BOOL keep_contents);
protected:
const std::string& tabs();

View File

@ -66,6 +66,7 @@ include_directories(
${LSCRIPT_INCLUDE_DIRS}/lscript_compile
${LLLOGIN_INCLUDE_DIRS}
${UPDATER_INCLUDE_DIRS}
${CMAKE_BINARY_DIR}/newview
)
set(viewer_SOURCE_FILES
@ -591,6 +592,9 @@ set(viewer_SOURCE_FILES
noise.cpp
panel_prefs_firestorm.cpp
pipeline.cpp
kvflickr.cpp
kvfloaterflickrauth.cpp
kvfloaterflickrupload.cpp
qtoolalign.cpp
rlvhandler.cpp
rlvhelper.cpp
@ -1155,8 +1159,18 @@ set(viewer_HEADER_FILES
streamtitledisplay.h
VertexCache.h
VorbisFramework.h
kvflickr.h
kvfloaterflickrauth.h
kvfloaterflickrupload.h
)
# Generate the Flickr Keys header
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/kvflickrkeys.h.in
${CMAKE_CURRENT_BINARY_DIR}/kvflickrkeys.h
@ONLY
)
list(APPEND viewer_HEADER_FILES ${CMAKE_CURRENT_BINARY_DIR}/kvflickrkeys.h)
source_group("CMake Rules" FILES ViewerInstall.cmake)
if (DARWIN)

View File

@ -13425,5 +13425,49 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>Value</key>
<integer>0</integer>
</map>
<key>KittyFlickrLastRating</key>
<map>
<key>Comment</key>
<string>Last rating for Flickr upload. 1 = safe, 2 = moderate, 3 = restricted</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>S32</string>
<key>Value</key>
<integer>1</integer>
</map>
<key>KittyFlickrLastTags</key>
<map>
<key>Comment</key>
<string>Last tags used on Flickr upload</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>String</string>
<key>Value</key>
<string>"Second Life"</string>
</map>
<key>KittyFlickrShowPosition</key>
<map>
<key>Comment</key>
<string>Whether to show the position of a Flickr upload</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>1</integer>
</map>
<key>KittyFlickrIncludeSLURL</key>
<map>
<key>Comment</key>
<string>If showing the position of an image, whether an SLurl should be included in the description.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>1</integer>
</map>
</map>
</llsd>

View File

@ -196,6 +196,39 @@
<!-- End of back compatibility settings -->
<!-- Firestorm settings -->
<key>KittyFlickrToken</key>
<map>
<key>Comment</key>
<string>Token used to authenticate with Flickr</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>String</string>
<key>Value</key>
<string></string>
</map>
<key>KittyFlickrUsername</key>
<map>
<key>Comment</key>
<string>Username associated with Flickr account</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>String</string>
<key>Value</key>
<string></string>
</map>
<key>KittyFlickrNSID</key>
<map>
<key>Comment</key>
<string>NSID associated with Flickr account</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>String</string>
<key>Value</key>
<string></string>
</map>
<key>DebugLookAt</key>
<map>
<key>Comment</key>

296
indra/newview/kvflickr.cpp Normal file
View File

@ -0,0 +1,296 @@
/**
* @file kvflickr.cpp
* @brief Basic Flickr library for the client.
* @copyright Copyright (c) 2011 Katharine Berry
*
* 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; either
* version 2.1 of the License, or (at your option) any later version.
*
* 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
*/
#include "llviewerprecompiledheaders.h"
#include "llbufferstream.h"
#include "lluri.h"
#include "llmd5.h"
#include "llhttpclient.h"
#include "llxmltree.h"
#include "jsoncpp/reader.h"
#include "kvflickr.h"
class KVFlickrResponse : public LLHTTPClient::Responder
{
public:
KVFlickrResponse(KVFlickrRequest::response_callback_t &callback);
/* virtual */ void completedRaw(
U32 status,
const std::string& reason,
const LLChannelDescriptors& channels,
const LLIOPipe::buffer_ptr_t& buffer);
private:
KVFlickrRequest::response_callback_t mCallback;
};
class KVFlickrUploadResponse : public LLHTTPClient::Responder
{
public:
KVFlickrUploadResponse(KVFlickrRequest::response_callback_t &callback);
/* virtual */ void completedRaw(
U32 status,
const std::string& reason,
const LLChannelDescriptors& channels,
const LLIOPipe::buffer_ptr_t& buffer);
private:
KVFlickrRequest::response_callback_t mCallback;
};
void KVFlickrRequest::request(const std::string& method, const LLSD& args, response_callback_t callback)
{
LLSD params(args);
params["format"] = "json";
params["method"] = method;
params["api_key"] = KV_FLICKR_API_KEY;
params["nojsoncallback"] = 1;
params["api_sig"] = getSignatureForCall(params, true); // This must be the last one set.
LLHTTPClient::get("http://flickr.com/services/rest/", params, new KVFlickrResponse(callback));
}
//static
void KVFlickrRequest::uploadPhoto(const LLSD& args, LLImageFormatted *image, response_callback_t callback)
{
LLSD params(args);
params["api_key"] = KV_FLICKR_API_KEY;
params["api_sig"] = getSignatureForCall(params, false);
// It would be nice if there was an easy way to do multipart form data. Oh well.
std::string boundary = "------------" + LLUUID::generateNewID().asString();
std::ostringstream post_stream;
post_stream << "--" << boundary;
// Add all the parameters from LLSD to the query.
for(LLSD::map_const_iterator itr = params.beginMap(); itr != params.endMap(); ++itr)
{
post_stream << "\r\nContent-Disposition: form-data; name=\"" << itr->first << "\"";
post_stream << "\r\n\r\n" << itr->second.asString();
post_stream << "\r\n" << "--" << boundary;
}
// Headers for the photo
post_stream << "\r\nContent-Disposition: form-data; name=\"photo\"; filename=\"snapshot." << image->getExtension() << "\"";
post_stream << "\r\nContent-Type: ";
// Apparently LLImageFormatted doesn't know what mimetype it has.
if(image->getExtension() == "jpg")
{
post_stream << "image/jpeg";
}
else if(image->getExtension() == "png")
{
post_stream << "image/png";
}
else // This will (probably) only happen if someone decides to put the BMP entry back in the format selection floater.
{ // I wonder if Flickr would do the right thing.
post_stream << "application/x-wtf";
LL_WARNS("FlickrAPI") << "Uploading unknown image type." << LL_ENDL;
}
post_stream << "\r\n\r\n";
// Now we build the postdata array, including the photo in the middle of it.
// C memory operations abound!
std::string post_str = post_stream.str();
size_t total_data_size = image->getDataSize() + post_str.length() + boundary.length() + 6; // + 6 = "\r\n" + "--" + "--"
char* post_data = new char[total_data_size + 1];
memcpy(post_data, post_str.data(), post_str.length());
char* address = post_data + post_str.length();
memcpy(address, image->getData(), image->getDataSize());
address += image->getDataSize();
std::string post_tail = "\r\n--" + boundary + "--";
memcpy(address, post_tail.data(), post_tail.length());
address += post_tail.length();
llassert(address <= post_data + total_data_size /* After all that, check we didn't overrun */);
// We have a post body! Now we can go about building the actual request...
LLSD headers;
headers["Content-Type"] = "multipart/form-data; boundary=" + boundary;
LLHTTPClient::postRaw("http://api.flickr.com/services/upload/", (U8*)post_data, total_data_size, new KVFlickrUploadResponse(callback), headers);
// The HTTP client takes ownership of our post_data array,
// and will delete it when it's done.
}
//static
std::string KVFlickrRequest::getSignatureForCall(const LLSD& parameters, bool encoded)
{
std::vector<std::string> keys;
for(LLSD::map_const_iterator itr = parameters.beginMap(); itr != parameters.endMap(); ++itr)
{
keys.push_back(itr->first);
}
std::sort(keys.begin(), keys.end());
std::string to_hash(KV_FLICKR_API_SECRET);
for(std::vector<std::string>::const_iterator itr = keys.begin(); itr != keys.end(); ++itr)
{
to_hash += *itr;
if(encoded)
{
to_hash += LLURI::escapeQueryValue(parameters[*itr].asString());
}
else
{
to_hash += parameters[*itr].asString();
}
}
LLMD5 hashed((const unsigned char*)to_hash.c_str());
char hex_hash[MD5HEX_STR_SIZE];
hashed.hex_digest(hex_hash);
return std::string(hex_hash);
}
KVFlickrUploadResponse::KVFlickrUploadResponse(KVFlickrRequest::response_callback_t &callback)
: mCallback(callback)
{
}
void KVFlickrUploadResponse::completedRaw(
U32 status,
const std::string& reason,
const LLChannelDescriptors& channels,
const LLIOPipe::buffer_ptr_t& buffer)
{
LLBufferStream istr(channels, buffer.get());
std::stringstream strstrm;
strstrm << istr.rdbuf();
std::string result = std::string(strstrm.str());
LLSD output;
bool success;
LLXmlTree tree;
if(!tree.parseString(result))
{
LL_WARNS("FlickrAPI") << "Couldn't parse flickr response: " << result << LL_ENDL;
mCallback(false, LLSD());
return;
}
LLXmlTreeNode* root = tree.getRoot();
if(!root->hasName("rsp"))
{
LL_WARNS("FlickrAPI") << "Bad root node: " << root->getName() << LL_ENDL;
mCallback(false, LLSD());
return;
}
std::string stat;
root->getAttributeString("stat", stat);
output["stat"] = stat;
if(stat == "ok")
{
success = true;
LLXmlTreeNode* photoid_node = root->getChildByName("photoid");
if(photoid_node)
{
output["photoid"] = photoid_node->getContents();
}
}
else
{
success = false;
LLXmlTreeNode* err_node = root->getChildByName("err");
if(err_node)
{
S32 code;
std::string msg;
err_node->getAttributeS32("code", code);
err_node->getAttributeString("msg", msg);
output["code"] = code;
output["msg"] = msg;
}
}
mCallback(success, output);
}
void JsonToLLSD(const Json::Value &root, LLSD &output)
{
if(root.isObject())
{
Json::Value::Members keys = root.getMemberNames();
for(Json::Value::Members::const_iterator itr = keys.begin(); itr != keys.end(); ++itr)
{
LLSD elem;
JsonToLLSD(root[*itr], elem);
output[*itr] = elem;
}
}
else if(root.isArray())
{
for(Json::Value::const_iterator itr = root.begin(); itr != root.end(); ++itr)
{
LLSD elem;
JsonToLLSD(*itr, elem);
output.append(elem);
}
}
else
{
switch(root.type())
{
case Json::intValue:
output = root.asInt();
break;
case Json::realValue:
case Json::uintValue:
output = root.asDouble();
break;
case Json::stringValue:
output = root.asString();
break;
case Json::booleanValue:
output = root.asBool();
case Json::nullValue:
output = NULL;
default:
break;
}
}
}
KVFlickrResponse::KVFlickrResponse(KVFlickrRequest::response_callback_t &callback) :
mCallback(callback)
{
}
void KVFlickrResponse::completedRaw(
U32 status,
const std::string& reason,
const LLChannelDescriptors& channels,
const LLIOPipe::buffer_ptr_t& buffer)
{
LLBufferStream istr(channels, buffer.get());
std::stringstream strstrm;
strstrm << istr.rdbuf();
std::string result = std::string(strstrm.str());
Json::Value root;
Json::Reader reader;
bool success = reader.parse(result, root);
if(!success)
{
mCallback(false, LLSD());
return;
}
else
{
LL_INFOS("FlickrAPI") << "Got response string: " << result << LL_ENDL;
LLSD response;
JsonToLLSD(root, response);
LL_INFOS("FlickrAPI") << "As LLSD: " << response << LL_ENDL;
mCallback(isGoodStatus(status), response);
}
}

43
indra/newview/kvflickr.h Normal file
View File

@ -0,0 +1,43 @@
/**
* @file kvflickr.h
* @brief Basic Flickr library for the client.
* @copyright Copyright (c) 2011 Katharine Berry
*
* 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; either
* version 2.1 of the License, or (at your option) any later version.
*
* 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
*/
#ifndef KV_KVFLICKR_H
#define KV_KVFLICKR_H
#include "llimage.h"
#include "llsd.h"
#include <boost/bind.hpp>
// This include file defines the API key/secret.
// It is generated by cmake.
#include "kvflickrkeys.h"
class KVFlickrRequest
{
public:
typedef boost::function<void(bool success, const LLSD& response)> response_callback_t;
static void request(const std::string& method, const LLSD& args, response_callback_t callback);
static void uploadPhoto(const LLSD& args, LLImageFormatted *image, response_callback_t callback);
static std::string getSignatureForCall(const LLSD& parameters, bool encoded);
};
#endif

View File

@ -0,0 +1,5 @@
// THIS FILE IS AUTOGENERATED BY THE BUILD PROCESS.
// Set the Flickr keys in Variables.cmake or on the develop.py command line.
#define KV_FLICKR_API_KEY "@FLICKR_API_KEY@"
#define KV_FLICKR_API_SECRET "@FLICKR_API_SECRET@"

View File

@ -0,0 +1,154 @@
/**
* @file kvfloaterflickrauth.cpp
* @brief Flickr authentication floater
* @copyright Copyright (c) 2011 Katharine Berry
*
* 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; either
* version 2.1 of the License, or (at your option) any later version.
*
* 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
*/
#include "llviewerprecompiledheaders.h"
#include "llfloaterreg.h"
#include "llsd.h"
#include "llslurl.h"
#include "lluri.h"
#include "llurlaction.h"
#include "llviewercontrol.h"
#include "kvflickr.h"
#include "kvfloaterflickrauth.h"
KVFloaterFlickrAuth::KVFloaterFlickrAuth(const LLSD& key) :
LLFloater(key), mCallback(NULL), mBrowser(NULL)
{
}
BOOL KVFloaterFlickrAuth::postBuild()
{
mBrowser = getChild<LLMediaCtrl>("browser");
mBrowser->addObserver(this);
// Work out URL.
LLSD query;
query["api_key"] = std::string(KV_FLICKR_API_KEY);
query["perms"] = "write";
query["api_sig"] = KVFlickrRequest::getSignatureForCall(query, true);
std::string query_string = LLURI::mapToQueryString(query);
LL_INFOS("FlickrAPI") << "Auth query: " << query_string << LL_ENDL;
std::string full_url = "http://www.flickr.com/services/auth/" + query_string;
mBrowser->navigateTo(full_url, "text/html");
return true;
}
void KVFloaterFlickrAuth::onClose(bool app_quitting)
{
// If we still have an mCallback here, we know it wasn't successful,
// because we always set it to NULL after using it.
if(mCallback)
{
mCallback(false);
mCallback = NULL;
}
destroy(); // Die die die!
}
void KVFloaterFlickrAuth::handleMediaEvent(LLPluginClassMedia* media, EMediaEvent event)
{
if(event == MEDIA_EVENT_LOCATION_CHANGED)
{
std::string uri_string = media->getLocation();
LLURI uri(uri_string);
// We use this moronic data: hack because the internal browser crashes on
// secondlife:/// redirects, doesn't raise any events on nonexistent links,
// and gets confused by about:blank. At least this is prettier.
if(uri.scheme() == "data")
{
// Turns out we have to parse query string out ourselves because LLURI won't do it
// unless it's a http(s), ftp, secondlife or x-grid-location-info link.
std::string::size_type q = uri_string.find('?');
if(q != std::string::npos)
{
std::string query_string = uri_string.substr(q + 1);
LLSD query = LLURI::queryMap(query_string);
if(query.has("frob"))
{
std::string frob = query["frob"];
LLSD params;
params["frob"] = frob;
KVFlickrRequest::request("flickr.auth.getToken", params, boost::bind(&KVFloaterFlickrAuth::gotToken, this, _1, _2));
}
}
}
// We don't get anything if authentication is rejected; they're just redirected to the
// home page. This is mildly problematic, given the restricted view they're in.
// Therefore, if they click outside where we want them to be, we close the view.
// If they go to the homepage (because they clicked "Home", the logo, or (most importantly)
// the "Do not allow" button), it is noted that they refused permission. Otherwise,
// we open the link they clicked in a standard browser. In either case we close
// our floater.
else if(uri.hostName() == "www.flickr.com" && uri.path() != "/services/auth/")
{
LL_WARNS("FlickrAPI") << "API permission not granted." << LL_ENDL;
if(uri.path() != "/")
{
LLUrlAction::openURL(uri_string);
}
closeFloater(false);
}
}
}
void KVFloaterFlickrAuth::gotToken(bool success, const LLSD& response)
{
std::string token = response["auth"]["token"]["_content"];
std::string username = response["auth"]["user"]["username"];
std::string nsid = response["auth"]["user"]["nsid"];
LL_INFOS("FlickrAPI") << "Got token " << token << " for user " << username << " (" << nsid << ")." << LL_ENDL;
gSavedPerAccountSettings.setString("KittyFlickrToken", token);
gSavedPerAccountSettings.setString("KittyFlickrUsername", username);
gSavedPerAccountSettings.setString("KittyFlickrNSID", nsid);
if(mCallback)
{
mCallback((token != ""));
mCallback = NULL;
}
closeFloater(false);
}
//static
KVFloaterFlickrAuth* KVFloaterFlickrAuth::showFloater()
{
return KVFloaterFlickrAuth::showFloater(NULL);
}
//static
KVFloaterFlickrAuth* KVFloaterFlickrAuth::showFloater(auth_callback_t callback)
{
KVFloaterFlickrAuth *floater = dynamic_cast<KVFloaterFlickrAuth*>(LLFloaterReg::getInstance("flickr_auth"));
if(floater)
{
floater->mCallback = callback;
floater->setVisible(true);
floater->setFrontmost(true);
floater->center();
return floater;
}
else
{
LL_WARNS("FlickrAPI") << "Can't find flickr auth!" << LL_ENDL;
return NULL;
}
}

View File

@ -0,0 +1,53 @@
/**
* @file kvfloaterflickrauth.h
* @brief Flickr authentication floater
* @copyright Copyright (c) 2011 Katharine Berry
*
* 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; either
* version 2.1 of the License, or (at your option) any later version.
*
* 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
*/
#ifndef KV_KVFLOATERFLICKRAUTH_H
#define KV_KVFLOATERFLICKRAUTH_H
#include "llfloater.h"
#include "llmediactrl.h"
#include <boost/bind.hpp>
class KVFloaterFlickrAuth :
public LLFloater,
public LLViewerMediaObserver
{
public:
typedef boost::function<void(bool)> auth_callback_t;
KVFloaterFlickrAuth(const LLSD& key);
/*virtual*/ BOOL postBuild();
/*virtual*/ void onClose(bool app_quitting);
// inherited from LLViewerMediaObserver
/*virtual*/ void handleMediaEvent(LLPluginClassMedia* media, EMediaEvent event);
static KVFloaterFlickrAuth* showFloater();
static KVFloaterFlickrAuth* showFloater(auth_callback_t callback);
private:
void gotToken(bool success, const LLSD& response);
LLMediaCtrl* mBrowser;
auth_callback_t mCallback;
};
#endif // KV_KVFLOATERFLICKRAUTH_H

View File

@ -0,0 +1,325 @@
/**
* @file kvfloaterflickrupload.cpp
* @brief Flickr upload floater
* @copyright Copyright (c) 2011 Katharine Berry
*
* 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; either
* version 2.1 of the License, or (at your option) any later version.
*
* 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
*/
#include "llviewerprecompiledheaders.h"
#include "kvfloaterflickrupload.h"
#include "llagent.h"
#include "llfloaterreg.h"
#include "llfloatersnapshot.h"
#include "llgl.h"
#include "llimage.h"
#include "llnotificationsutil.h"
#include "llslurl.h"
#include "llui.h"
#include "lluploaddialog.h"
#include "llviewercontrol.h"
#include "llviewerregion.h"
#include "llviewertexture.h"
#include "llworld.h"
#include "kvflickr.h"
#include "kvfloaterflickrauth.h"
#include <boost/bind.hpp>
KVFloaterFlickrUpload::KVFloaterFlickrUpload(const LLSD& key) : LLFloater(key),
mCompressedImage(NULL),
mViewerImage(NULL)
{
}
KVFloaterFlickrUpload::~KVFloaterFlickrUpload()
{
mCompressedImage = NULL;
mViewerImage = NULL;
}
// static
KVFloaterFlickrUpload* KVFloaterFlickrUpload::showFromSnapshot(LLImageFormatted *compressed, LLViewerTexture *img, const LLVector2& img_scale, const LLVector3d& pos_taken_global)
{
// Take the images from the caller
// It's now our job to clean them up
KVFloaterFlickrUpload* instance = LLFloaterReg::showTypedInstance<KVFloaterFlickrUpload>("flickr_upload", LLSD(img->getID()));
instance->mCompressedImage = compressed;
instance->mViewerImage = img;
instance->mImageScale = img_scale;
instance->mPosTakenGlobal = pos_taken_global;
return instance;
}
BOOL KVFloaterFlickrUpload::postBuild()
{
// Set the various UI fields to their default values.
childSetValue("rating_combo", gSavedSettings.getLLSD("KittyFlickrLastRating"));
childSetValue("tags_form", gSavedSettings.getLLSD("KittyFlickrLastTags"));
childSetValue("show_position_check", gSavedSettings.getLLSD("KittyFlickrShowPosition"));
// Connect the buttons up
childSetAction("cancel_btn", onClickCancel, this);
childSetAction("upload_btn", onClickUpload, this);
// Check that we actually can do an upload.
LLSD query;
query["auth_token"] = gSavedPerAccountSettings.getLLSD("KittyFlickrToken");
KVFlickrRequest::request("flickr.auth.checkToken", query, boost::bind(&KVFloaterFlickrUpload::confirmToken, this, _1, _2));
return true;
}
void KVFloaterFlickrUpload::confirmToken(bool success, const LLSD &response)
{
if(!success)
{
LLNotificationsUtil::add("KittyFlickrHTTPFail");
closeFloater(false);
return;
}
if(response["stat"].asString() == "ok")
{
// Just in case the username changed. This can happen.
std::string username = response["auth"]["user"]["username"];
gSavedPerAccountSettings.setString("KittyFlickrUsername", username);
childSetValue("account_name", username);
childSetEnabled("upload_btn", true);
}
else
{
// Uh oh.
if(response["code"].asInteger() == 98) // Invalid auth token
{
// Mark the account as invalid
childSetValue("account_name", getString("no_account"));
// Need to authenticate.
gSavedPerAccountSettings.setString("KittyFlickrToken", "");
gSavedPerAccountSettings.setString("KittyFlickrUsername", "");
gSavedPerAccountSettings.setString("KittyFlickrNSID", "");
KVFloaterFlickrAuth *floater = KVFloaterFlickrAuth::showFloater(boost::bind(&KVFloaterFlickrUpload::authCallback, this, _1));
// Link it to us to protect it from freeze frame mode, if need be.
if(floater && getDependee()) // (if we're depending on something, so should it)
{
gFloaterView->removeChild(floater);
gSnapshotFloaterView->addChild(floater);
// Even though we don't really want this to depend on the snapshot view
// being open, if we manipulate it after closing the snapshot view,
// it will crash.
getDependee()->addDependentFloater(floater, false);
}
LLNotificationsUtil::add("KittyFlickrTokenRejected");
}
else
{
LLSD args;
args["CODE"] = response["code"];
args["ERROR"] = response["message"];
LLNotificationsUtil::add("KittyFlickrGenericFail", args);
}
}
}
void KVFloaterFlickrUpload::authCallback(bool authorised)
{
if(authorised)
{
childSetValue("account_name", gSavedPerAccountSettings.getString("KittyFlickrUsername"));
childSetEnabled("upload_btn", true);
}
else
{
LLNotificationsUtil::add("KittyFlickrUploadCancelledAuthRejected");
closeFloater(false);
}
}
void KVFloaterFlickrUpload::saveSettings()
{
gSavedSettings.setS32("KittyFlickrLastRating", childGetValue("rating_combo"));
gSavedSettings.setString("KittyFlickrLastTags", childGetValue("tags_form"));
gSavedSettings.setBOOL("KittyFlickrShowPosition", childGetValue("show_position_check"));
}
void KVFloaterFlickrUpload::uploadSnapshot()
{
mTitle = childGetValue("title_form").asString();
LLSD params;
params["title"] = childGetValue("title_form");
params["safety_level"] = childGetValue("rating_combo");
std::string tags = childGetValue("tags_form");
std::string description = childGetValue("description_form");
if(childGetValue("show_position_check").asBoolean())
{
// Work out where this was taken.
LLVector3d clamped_global = LLWorld::getInstance()->clipToVisibleRegions(gAgent.getPositionGlobal(), mPosTakenGlobal);
LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal(clamped_global);
if(!region)
{
// Clamping failed? Shouldn't happen.
// Use the agent's position instead; if the region the agent is in doesn't exist we have some serious issues,
// and crashing is an entirely reasonable thing to do.
region = gAgent.getRegion();
clamped_global = gAgent.getPositionGlobal();
}
std::string region_name = region->getName();
LLVector3 region_pos = region->getPosRegionFromGlobal(clamped_global);
std::ostringstream region_tags;
region_tags << " \"secondlife:region=" << region_name << "\"";
region_tags << " secondlife:x=" << llround(region_pos[VX]);
region_tags << " secondlife:y=" << llround(region_pos[VY]);
region_tags << " secondlife:z=" << llround(region_pos[VZ]);
// Now let's give some precise camera values.
region_tags << " secondlife:camera_pos_x=" << (mPosTakenGlobal[VX] - region->getOriginGlobal()[VX]);
region_tags << " secondlife:camera_pos_y=" << (mPosTakenGlobal[VY] - region->getOriginGlobal()[VY]);
region_tags << " secondlife:camera_pos_z=" << mPosTakenGlobal[VZ];
tags += region_tags.str();
// Include an SLurl in the description, too (maybe).
if(gSavedSettings.getBOOL("KittyFlickrIncludeSLURL"))
{
LLSLURL url(region_name, region_pos);
std::ostringstream region_desc;
region_desc << "<em><a href='" << url.getSLURLString() << "'>";
region_desc << "Taken at " << region_name << " (";
region_desc << llround(region_pos[VX]) << ", ";
region_desc << llround(region_pos[VY]) << ", ";
region_desc << llround(region_pos[VZ]) << ")";
region_desc << "</a></em>";
if(description != "")
{
description += "\n\n";
}
description += region_desc.str();
}
}
params["tags"] = tags;
params["description"] = description;
LL_INFOS("FlickrAPI") << "Uploading snapshot with metadata: " << params << LL_ENDL;
params["auth_token"] = gSavedPerAccountSettings.getLLSD("KittyFlickrToken");
LLUploadDialog::modalUploadDialog(getString("uploading"));
KVFlickrRequest::uploadPhoto(params, mCompressedImage, boost::bind(&KVFloaterFlickrUpload::imageUploaded, this, _1, _2));
}
void KVFloaterFlickrUpload::imageUploaded(bool success, const LLSD& response)
{
LLUploadDialog::modalUploadFinished();
LLSD args;
args["TITLE"] = mTitle;
if(success)
{
args["ID"] = response["photoid"];
LLNotificationsUtil::add("KittyFlickrUploadComplete", args);
}
else if(response.has("stat"))
{
args["CODE"] = response["code"];
args["ERROR"] = response["msg"];
LLNotificationsUtil::add("KittyFlickrUploadFailed", args);
}
else
{
LLNotificationsUtil::add("KittyFlickrUploadFailedNoError");
}
// We're pretty much done now.
closeFloater(false);
}
// This function stolen from LLFloaterPostcard
void KVFloaterFlickrUpload::draw()
{
LLGLSUIDefault gls_ui;
LLFloater::draw();
if(!isMinimized() && mViewerImage.notNull() && mCompressedImage.notNull())
{
LLRect rect(getRect());
// first set the max extents of our preview
rect.translate(-rect.mLeft, -rect.mBottom);
rect.mLeft += 280;
rect.mRight -= 10;
rect.mTop -= 27;
rect.mBottom = rect.mTop - 130;
// then fix the aspect ratio
F32 ratio = (F32)mCompressedImage->getWidth() / (F32)mCompressedImage->getHeight();
if ((F32)rect.getWidth() / (F32)rect.getHeight() >= ratio)
{
rect.mRight = LLRect::tCoordType((F32)rect.mLeft + ((F32)rect.getHeight() * ratio));
}
else
{
rect.mBottom = LLRect::tCoordType((F32)rect.mTop - ((F32)rect.getWidth() / ratio));
}
{
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
gl_rect_2d(rect, LLColor4(0.f, 0.f, 0.f, 1.f));
rect.stretch(-1);
}
{
glMatrixMode(GL_TEXTURE);
glPushMatrix();
{
glScalef(mImageScale.mV[VX], mImageScale.mV[VY], 1.f);
glMatrixMode(GL_MODELVIEW);
gl_draw_scaled_image(rect.mLeft,
rect.mBottom,
rect.getWidth(),
rect.getHeight(),
mViewerImage.get(),
LLColor4::white);
}
glMatrixMode(GL_TEXTURE);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
}
}
}
//static
void KVFloaterFlickrUpload::onClickCancel(void* data)
{
if(data)
{
KVFloaterFlickrUpload *self = (KVFloaterFlickrUpload*)data;
self->closeFloater(false);
}
}
//static
void KVFloaterFlickrUpload::onClickUpload(void* data)
{
if(!data)
return;
KVFloaterFlickrUpload *self = (KVFloaterFlickrUpload*)data;
self->uploadSnapshot();
self->saveSettings();
self->setVisible(false);
// Make sure that, if we were attached to anything, that we detach from it.
// Otherwise bad things happen.
LLFloater *dependee = self->getDependee();
if(dependee)
dependee->removeDependentFloater(self);
}

View File

@ -0,0 +1,60 @@
/**
* @file kvfloaterflickrupload.h
* @brief Flickr upload floater
* @copyright Copyright (c) 2011 Katharine Berry
*
* 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; either
* version 2.1 of the License, or (at your option) any later version.
*
* 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
*/
#ifndef KV_KVFLOATERFLICKRUPLOAD_H
#define KV_KVFLOATERFLICKRUPLOAD_H
#include "llfloater.h"
#include "llcheckboxctrl.h"
#include "llpointer.h"
class LLViewerTexture;
class LLImageFormatted;
class KVFloaterFlickrUpload : public LLFloater
{
public:
KVFloaterFlickrUpload(const LLSD& key);
~KVFloaterFlickrUpload();
BOOL postBuild();
void draw();
void saveSettings();
void uploadSnapshot();
void confirmToken(bool success, const LLSD &response);
void authCallback(bool authorised);
void imageUploaded(bool success, const LLSD& response);
static void onClickCancel(void* data);
static void onClickUpload(void* data);
static KVFloaterFlickrUpload* showFromSnapshot(LLImageFormatted *compressed, LLViewerTexture *img, const LLVector2& img_scale, const LLVector3d& pos_taken_global);
private:
LLPointer<LLImageFormatted> mCompressedImage;
LLPointer<LLViewerTexture> mViewerImage;
LLVector2 mImageScale;
LLVector3d mPosTakenGlobal;
std::string mTitle; // Used in the confirmation announcement.
};
#endif // KV_KVFLOATERFLICKRUPLOAD_H

View File

@ -59,6 +59,9 @@
#include "llworld.h"
#include "llagentui.h"
#include "kvfloaterflickrauth.h"
#include "kvfloaterflickrupload.h"
// Linden library includes
#include "llfontgl.h"
#include "llsys.h"
@ -113,7 +116,8 @@ public:
SNAPSHOT_POSTCARD,
SNAPSHOT_TEXTURE,
SNAPSHOT_LOCAL,
SNAPSHOT_WEB
SNAPSHOT_WEB,
SNAPSHOT_FLICKR
};
@ -161,6 +165,7 @@ public:
void updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail = FALSE, F32 delay = 0.f);
void saveWeb();
LLFloaterPostcard* savePostcard();
KVFloaterFlickrUpload* uploadToFlickr();
void saveTexture();
BOOL saveLocal();
@ -967,6 +972,24 @@ LLFloaterPostcard* LLSnapshotLivePreview::savePostcard()
return floater;
}
KVFloaterFlickrUpload* LLSnapshotLivePreview::uploadToFlickr()
{
// calculate and pass in image scale in case image data only use portion
// of viewerimage buffer
LLVector2 image_scale(1.f, 1.f);
if (!isImageScaled())
{
image_scale.setVec(llmin(1.f, (F32)mWidth[mCurImageIndex] / (F32)getCurrentImage()->getWidth()), llmin(1.f, (F32)mHeight[mCurImageIndex] / (F32)getCurrentImage()->getHeight()));
}
KVFloaterFlickrUpload* floater = KVFloaterFlickrUpload::showFromSnapshot(mFormattedImage, mViewerImage[mCurImageIndex], image_scale, mPosTakenGlobal);
mFormattedImage = NULL;
mDataSize = 0;
updateSnapshot(FALSE, FALSE);
return floater;
}
void LLSnapshotLivePreview::saveTexture()
{
// gen a new uuid for this asset
@ -1155,6 +1178,10 @@ LLSnapshotLivePreview::ESnapshotType LLFloaterSnapshot::Impl::getTypeIndex(LLFlo
{
index = LLSnapshotLivePreview::SNAPSHOT_WEB;
}
else if (id == "flickr")
{
index = LLSnapshotLivePreview::SNAPSHOT_FLICKR;
}
return index;
}
@ -1174,6 +1201,9 @@ LLSD LLFloaterSnapshot::Impl::getTypeName(LLSnapshotLivePreview::ESnapshotType i
case LLSnapshotLivePreview::SNAPSHOT_TEXTURE:
id = "texture";
break;
case LLSnapshotLivePreview::SNAPSHOT_FLICKR:
id = "flickr";
break;
case LLSnapshotLivePreview::SNAPSHOT_LOCAL:
default:
id = "local";
@ -1186,10 +1216,10 @@ LLSD LLFloaterSnapshot::Impl::getTypeName(LLSnapshotLivePreview::ESnapshotType i
LLFloaterSnapshot::ESnapshotFormat LLFloaterSnapshot::Impl::getFormatIndex(LLFloaterSnapshot* floater)
{
ESnapshotFormat index = SNAPSHOT_FORMAT_PNG;
if(floater->hasChild("local_format_combo"))
if(floater->hasChild("format_combo"))
{
LLComboBox* local_format_combo = floater->findChild<LLComboBox>("local_format_combo");
const std::string id = local_format_combo->getSelectedItemLabel();
LLComboBox* format_combo = floater->findChild<LLComboBox>("format_combo");
const std::string id = format_combo->getSelectedItemLabel();
if (id == "PNG")
index = SNAPSHOT_FORMAT_PNG;
else if (id == "JPEG")
@ -1330,12 +1360,13 @@ void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater)
floater->getChild<LLComboBox>("postcard_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotPostcardLastResolution"));
floater->getChild<LLComboBox>("texture_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotTextureLastResolution"));
floater->getChild<LLComboBox>("local_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotLocalLastResolution"));
floater->getChild<LLComboBox>("local_format_combo")->selectNthItem(gSavedSettings.getS32("SnapshotFormat"));
floater->getChild<LLComboBox>("format_combo")->selectNthItem(gSavedSettings.getS32("SnapshotFormat"));
// *TODO: Separate settings for Web images from postcards
floater->getChildView("send_btn")->setVisible( shot_type == LLSnapshotLivePreview::SNAPSHOT_POSTCARD ||
shot_type == LLSnapshotLivePreview::SNAPSHOT_WEB);
floater->getChildView("upload_btn")->setVisible(shot_type == LLSnapshotLivePreview::SNAPSHOT_TEXTURE);
floater->getChildView("upload_btn")->setVisible(shot_type == LLSnapshotLivePreview::SNAPSHOT_TEXTURE ||
shot_type == LLSnapshotLivePreview::SNAPSHOT_FLICKR);
floater->getChildView("save_btn")->setVisible( shot_type == LLSnapshotLivePreview::SNAPSHOT_LOCAL);
floater->getChildView("keep_aspect_check")->setEnabled(shot_type != LLSnapshotLivePreview::SNAPSHOT_TEXTURE && !floater->impl.mAspectRatioCheckOff);
floater->getChildView("layer_types")->setEnabled(shot_type == LLSnapshotLivePreview::SNAPSHOT_LOCAL);
@ -1347,16 +1378,17 @@ void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater)
floater->childSetEnabled("temp_check", shot_type == LLSnapshotLivePreview::SNAPSHOT_TEXTURE);
BOOL is_advance = gSavedSettings.getBOOL("AdvanceSnapshot");
BOOL is_local = shot_type == LLSnapshotLivePreview::SNAPSHOT_LOCAL;
BOOL can_choose_format = (shot_type == LLSnapshotLivePreview::SNAPSHOT_LOCAL ||
shot_type == LLSnapshotLivePreview::SNAPSHOT_FLICKR);
BOOL show_slider = (shot_type == LLSnapshotLivePreview::SNAPSHOT_POSTCARD ||
shot_type == LLSnapshotLivePreview::SNAPSHOT_WEB ||
(is_local && shot_format == LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG));
(can_choose_format && shot_format == LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG));
floater->getChildView("more_btn")->setVisible( !is_advance); // the only item hidden in advanced mode
floater->getChildView("less_btn")->setVisible( is_advance);
floater->getChildView("type_label2")->setVisible( is_advance);
floater->getChildView("format_label")->setVisible( is_advance && is_local);
floater->getChildView("local_format_combo")->setVisible( is_advance && is_local);
floater->getChildView("format_label")->setVisible(is_advance && can_choose_format);
floater->getChildView("format_combo")->setVisible(is_advance && can_choose_format);
floater->getChildView("layer_types")->setVisible( is_advance);
floater->getChildView("layer_type_label")->setVisible( is_advance);
floater->getChildView("snapshot_width")->setVisible( is_advance);
@ -1407,7 +1439,8 @@ void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater)
floater->getChildView("send_btn")->setEnabled((shot_type == LLSnapshotLivePreview::SNAPSHOT_POSTCARD ||
shot_type == LLSnapshotLivePreview::SNAPSHOT_WEB) &&
got_snap && previewp->getDataSize() <= MAX_POSTCARD_DATASIZE);
floater->getChildView("upload_btn")->setEnabled(shot_type == LLSnapshotLivePreview::SNAPSHOT_TEXTURE && got_snap);
floater->getChildView("upload_btn")->setEnabled((shot_type == LLSnapshotLivePreview::SNAPSHOT_TEXTURE ||
shot_type == LLSnapshotLivePreview::SNAPSHOT_FLICKR) && got_snap);
floater->getChildView("save_btn")->setEnabled(shot_type == LLSnapshotLivePreview::SNAPSHOT_LOCAL && got_snap);
LLLocale locale(LLLocale::USER_LOCALE);
@ -1419,7 +1452,6 @@ void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater)
S32 upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
floater->childSetVisible("temp_check", is_advance && upload_cost > 0);
floater->getChild<LLUICtrl>("texture")->setLabelArg("[AMOUNT]", llformat("%d",upload_cost));
floater->getChild<LLUICtrl>("upload_btn")->setLabelArg("[AMOUNT]", llformat("%d",upload_cost));
floater->getChild<LLUICtrl>("file_size_label")->setTextArg("[SIZE]", got_snap ? bytes_string : floater->getString("unknown"));
floater->getChild<LLUICtrl>("file_size_label")->setColor(
shot_type == LLSnapshotLivePreview::SNAPSHOT_POSTCARD
@ -1447,6 +1479,7 @@ void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater)
}
break;
case LLSnapshotLivePreview::SNAPSHOT_LOCAL:
case LLSnapshotLivePreview::SNAPSHOT_FLICKR:
if(is_advance)
{
setResolution(floater, "local_size_combo");
@ -1526,11 +1559,10 @@ void LLFloaterSnapshot::Impl::onClickKeep(void* data)
{
switch (previewp->getSnapshotType())
{
case LLSnapshotLivePreview::SNAPSHOT_WEB:
previewp->saveWeb();
break;
case LLSnapshotLivePreview::SNAPSHOT_POSTCARD:
case LLSnapshotLivePreview::SNAPSHOT_WEB:
previewp->saveWeb();
break;
case LLSnapshotLivePreview::SNAPSHOT_POSTCARD:
{
LLFloaterPostcard* floater = previewp->savePostcard();
// if still in snapshot mode, put postcard floater in snapshot floaterview
@ -1542,18 +1574,26 @@ void LLFloaterSnapshot::Impl::onClickKeep(void* data)
view->addDependentFloater(floater, FALSE);
}
}
break;
case LLSnapshotLivePreview::SNAPSHOT_TEXTURE:
previewp->saveTexture();
break;
case LLSnapshotLivePreview::SNAPSHOT_LOCAL:
previewp->saveLocal();
break;
default:
break;
break;
case LLSnapshotLivePreview::SNAPSHOT_FLICKR:
{
KVFloaterFlickrUpload* floater = previewp->uploadToFlickr();
if (floater && !gSavedSettings.getBOOL("CloseSnapshotOnKeep"))
{
gFloaterView->removeChild(floater);
gSnapshotFloaterView->addChild(floater);
view->addDependentFloater(floater, FALSE);
}
}
break;
case LLSnapshotLivePreview::SNAPSHOT_TEXTURE:
previewp->saveTexture();
break;
case LLSnapshotLivePreview::SNAPSHOT_LOCAL:
previewp->saveLocal();
break;
default:
break;
}
if (gSavedSettings.getBOOL("CloseSnapshotOnKeep"))
@ -1904,8 +1944,28 @@ void LLFloaterSnapshot::Impl::onCommitSnapshotType(LLUICtrl* ctrl, void* data)
LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
if (view)
{
gSavedSettings.setS32("LastSnapshotType", getTypeIndex(view));
getPreviewView(view)->updateSnapshot(TRUE);
int type = getTypeIndex(view);
if(type == LLSnapshotLivePreview::SNAPSHOT_FLICKR && gSavedPerAccountSettings.getString("KittyFlickrToken") == "")
{
LLNotificationsUtil::add("KittyFlickrNeedAuth");
KVFloaterFlickrAuth *floater = KVFloaterFlickrAuth::showFloater();
// This makes sure we can still use the auth floater in freeze-frame mode by attaching it
// to the snapshot floater.
if(floater)
{
if(floater->getParent() == gFloaterView)
{
gFloaterView->removeChild(floater);
gSnapshotFloaterView->addChild(floater);
view->addDependentFloater(floater, false);
}
}
}
else
{
gSavedSettings.setS32("LastSnapshotType", type);
getPreviewView(view)->updateSnapshot(TRUE);
}
updateControls(view);
}
}
@ -2139,9 +2199,17 @@ BOOL LLFloaterSnapshot::postBuild()
{
LLWebSharing::instance().init();
}
if(gSavedSettings.getS32("LastSnapshotType") == LLSnapshotLivePreview::SNAPSHOT_FLICKR)
{
if(gSavedPerAccountSettings.getString("KittyFlickrToken") == "")
{
gSavedSettings.setS32("LastSnapshotType", LLSnapshotLivePreview::SNAPSHOT_LOCAL);
}
}
childSetCommitCallback("snapshot_type_radio", Impl::onCommitSnapshotType, this);
childSetCommitCallback("local_format_combo", Impl::onCommitSnapshotFormat, this);
childSetCommitCallback("format_combo", Impl::onCommitSnapshotFormat, this);
childSetAction("new_snapshot_btn", Impl::onClickNewSnapshot, this);

View File

@ -31,6 +31,8 @@
#include "llviewerfloaterreg.h"
#include "kvfloaterflickrauth.h"
#include "kvfloaterflickrupload.h"
#include "llcompilequeue.h"
#include "llcallfloater.h"
#include "llfloaterabout.h"
@ -178,6 +180,8 @@ void LLViewerFloaterReg::registerFloaters()
// [SL:KB] - Patch : UI-ProfileGroupFloater | Checked: 2010-09-08 (Catznip-2.1.2c) | Added: Catznip-2.1.2c
LLFloaterReg::add("floater_profile_view", "floater_profile_view.xml",&LLFloaterReg::build<LLFloaterProfileView>);
// [/SL:KB]
LLFloaterReg::add("flickr_auth", "floater_flickr_auth.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<KVFloaterFlickrAuth>);
LLFloaterReg::add("flickr_upload", "floater_flickr_upload.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<KVFloaterFlickrUpload>);
LLFloaterReg::add("font_test", "floater_font_test.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterFontTest>);
LLFloaterReg::add("gestures", "floater_gesture.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterGesture>);

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<floater
can_resize="false"
can_close="true"
width="842"
height="600"
layout="topleft"
name="floater_flickr_auth"
help_topic="floater_flickr_auth"
save_rect="true"
single_instance="true"
title="FLICKR AUTHENTICATION"
>
<web_browser
follows="all"
layout="topleft"
left="5"
right="-5"
top="1"
bottom="-5"
ignore_ui_scale="true"
name="browser"/>
</floater>

View File

@ -0,0 +1,188 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<floater
legacy_header_height="18"
auto_tile="true"
can_minimize="false"
can_resize="false"
height="330"
layout="topleft"
name="Uploadr"
help_topic="flickr_uploader"
title="UPLOAD TO FLICKR"
width="450">
<floater.string
name="no_account">
(not authorised)
</floater.string>
<floater.string
name="uploading">
Uploading to Flickr...
</floater.string>
<text
type="string"
length="1"
bottom="35"
follows="top|left"
font="SansSerif"
layout="topleft"
left="12"
name="title_label">
Photo Title:
</text>
<line_editor
follows="left|top"
height="20"
layout="topleft"
left_delta="108"
name="title_form"
top_delta="-4"
value="Snapshot"
width="150" />
<text
type="string"
length="1"
bottom_delta="23"
follows="top|left"
font="SansSerif"
layout="topleft"
left="12"
name="tags_label">
Tags:
</text>
<line_editor
follows="left|top"
height="20"
layout="topleft"
left_delta="108"
name="tags_form"
top_delta="-4"
width="150" />
<text
type="string"
length="1"
bottom_delta="23"
follows="top|left"
font="SansSerif"
layout="topleft"
left="12"
name="maturity_label">
Rating:
</text>
<icons_combo_box
follows="left|top"
height="20"
label="Moderate"
layout="topleft"
left_delta="108"
name="rating_combo"
top_delta="-4"
value="2"
width="105">
<icons_combo_box.drop_down_button
image_overlay="Parcel_M_Light"
image_overlay_alignment="left"
imgoverlay_label_space="3"
pad_left="3"/>
<icons_combo_box.item
label="Adult"
name="Restricted"
value="3">
<item.columns
halign="center"
type="icon"
value="Parcel_R_Light"
width="20"/>
</icons_combo_box.item>
<icons_combo_box.item
label="Moderate"
name="Moderate"
value="2">
<item.columns
halign="center"
type="icon"
value="Parcel_M_Light"
width="20"/>
</icons_combo_box.item>
<icons_combo_box.item
label="General"
name="Safe"
value="1">
<item.columns
halign="center"
type="icon"
value="Parcel_PG_Light"
width="20"/>
</icons_combo_box.item>
</icons_combo_box>
<text
type="string"
length="1"
bottom_delta="23"
follows="top|left"
font="SansSerif"
layout="topleft"
left="12"
name="account_label">
Flickr Account:
</text>
<text
follows="left|top"
layout="topleft"
font="SansSerif"
left_delta="108"
name="account_name">
Loading...
</text>
<text
type="string"
length="1"
bottom_delta="23"
follows="top|left"
font="SansSerif"
layout="topleft"
left="12"
name="description_label">
Description:
</text>
<text_editor
type="string"
length="1"
follows="left|top|right|bottom"
height="140"
layout="topleft"
left_delta="0"
max_length="700"
name="description_form"
word_wrap="true"
top_pad="10"
width="427">
</text_editor>
<check_box
name="show_position_check"
height="15"
value="true"
layout="topleft"
follows="left|bottom"
left="10"
top="307"
label="Include location" />
<button
follows="right|bottom"
height="23"
label="Cancel"
layout="topleft"
name="cancel_btn"
right="-10"
top="300"
width="100" />
<button
follows="right|bottom"
height="23"
label="Upload"
layout="topleft"
left_delta="-106"
name="upload_btn"
top_delta="0"
enabled="false"
width="100" />
</floater>

View File

@ -50,6 +50,12 @@
layout="topleft"
name="local"
top_pad="2" />
<radio_item
height="16"
label="Upload to Flickr"
layout="topleft"
name="flickr"
top_pad="2" />
</radio_group>
<ui_ctrl
height="90"
@ -95,7 +101,7 @@
<button
follows="left|top"
height="23"
label="Save (L$[AMOUNT])"
label="Upload"
layout="topleft"
right="-5"
name="upload_btn"
@ -275,7 +281,7 @@
label="Format"
layout="topleft"
left_pad="5"
name="local_format_combo"
name="format_combo"
width="70">
<combo_box.item
label="PNG"
@ -283,9 +289,12 @@
<combo_box.item
label="JPEG"
name="JPEG" />
<!--
Disabled until someone convinces me that BMP support is actually useful.
<combo_box.item
label="BMP"
name="BMP" />
-->
</combo_box>
<spinner
allow_text_entry="false"

View File

@ -6900,6 +6900,67 @@ The site at &apos;&lt;nolink&gt;[HOST_NAME]&lt;/nolink&gt;&apos; in realm &apos;
</form>
</notification>
<notification
icon="alertmodal.tga"
name="KittyFlickrNeedAuth"
type="alertmodal">
You must authenticate [APP_NAME] before you can upload images to Flickr. Please login in and authorise [APP_NAME], then select "Upload to Flickr" again.
(If you have already authorised [APP_NAME] and are still logged in, this process may be automatic)
</notification>
<notification
icon="alertmodal.tga"
name="KittyFlickrHTTPFail"
type="alertmodal">
Communication with Flickr failed. We don't know why.
</notification>
<notification
icon="alertmodal.tga"
name="KittyFlickrTokenRejected"
type="alertmodal">
Flickr rejected your authentication token. This is probably because you revoked [APP_NAME]'s permissions. Please grant permission and try again.
</notification>
<notification
icon="alertmodal.tga"
name="KittyFlickrGenericFail"
type="alertmodal">
Communication with Flickr failed ([CODE]):
[ERROR]
</notification>
<notification
icon="alertmodal.tga"
name="KittyFlickrUploadCancelledAuthRejected"
type="notifytip">
The upload was cancelled because you did not grant [APP_NAME] permission to access your Flickr account.
</notification>
<notification
icon="alertmodal.tga"
name="KittyFlickrUploadComplete"
type="notifytip">
[http://www.flickr.com/photos/upload/edit/?ids=[ID] [TITLE]] has been successfully uploaded to Flickr.
</notification>
<notification
icon="alertmodal.tga"
name="KittyFlickrUploadFailed"
type="alert">
Failed to upload "[TITLE]" to Flickr (error [CODE]):
[ERROR]
</notification>
<notification
icon="alertmodal.tga"
name="KittyFlickrUploadFailedNoError"
type="alert">
Failed dismally to upload "[TITLE]" to Flickr. Dunno why.
</notification>
<global name="UnsupportedCPU">
- Your CPU speed does not meet the minimum requirements.