From 882c80b7789d2f5a2c6bd2113bf489ec322d0eb3 Mon Sep 17 00:00:00 2001 From: Alexie Birman Date: Sun, 10 Apr 2011 15:23:44 +0100 Subject: [PATCH] Upload-to-Flickr --- indra/cmake/Variables.cmake | 4 + indra/llcommon/lluri.cpp | 16 +- indra/llcommon/lluri.h | 22 ++ indra/llxml/llxmltree.cpp | 44 ++- indra/llxml/llxmltree.h | 2 + indra/newview/CMakeLists.txt | 14 + indra/newview/app_settings/settings.xml | 44 +++ .../app_settings/settings_per_account.xml | 33 ++ indra/newview/kvflickr.cpp | 296 ++++++++++++++++ indra/newview/kvflickr.h | 43 +++ indra/newview/kvflickrkeys.h.in | 5 + indra/newview/kvfloaterflickrauth.cpp | 154 +++++++++ indra/newview/kvfloaterflickrauth.h | 53 +++ indra/newview/kvfloaterflickrupload.cpp | 325 ++++++++++++++++++ indra/newview/kvfloaterflickrupload.h | 60 ++++ indra/newview/llfloatersnapshot.cpp | 132 +++++-- indra/newview/llviewerfloaterreg.cpp | 4 + .../default/xui/en/floater_flickr_auth.xml | 23 ++ .../default/xui/en/floater_flickr_upload.xml | 188 ++++++++++ .../skins/default/xui/en/floater_snapshot.xml | 13 +- .../skins/default/xui/en/notifications.xml | 61 ++++ 21 files changed, 1497 insertions(+), 39 deletions(-) create mode 100644 indra/newview/kvflickr.cpp create mode 100644 indra/newview/kvflickr.h create mode 100644 indra/newview/kvflickrkeys.h.in create mode 100644 indra/newview/kvfloaterflickrauth.cpp create mode 100644 indra/newview/kvfloaterflickrauth.h create mode 100644 indra/newview/kvfloaterflickrupload.cpp create mode 100644 indra/newview/kvfloaterflickrupload.h create mode 100644 indra/newview/skins/default/xui/en/floater_flickr_auth.xml create mode 100644 indra/newview/skins/default/xui/en/floater_flickr_upload.xml diff --git a/indra/cmake/Variables.cmake b/indra/cmake/Variables.cmake index 2333a587c6..ba0287844c 100644 --- a/indra/cmake/Variables.cmake +++ b/indra/cmake/Variables.cmake @@ -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) diff --git a/indra/llcommon/lluri.cpp b/indra/llcommon/lluri.cpp index b39ea0c6f2..910fe6cf0a 100644 --- a/indra/llcommon/lluri.cpp +++ b/indra/llcommon/lluri.cpp @@ -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(); diff --git a/indra/llcommon/lluri.h b/indra/llcommon/lluri.h index c82a666e48..576a3d73b0 100644 --- a/indra/llcommon/lluri.h +++ b/indra/llcommon/lluri.h @@ -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. * diff --git a/indra/llxml/llxmltree.cpp b/indra/llxml/llxmltree.cpp index f2386700a1..85b2f18be6 100644 --- a/indra/llxml/llxmltree.cpp +++ b/indra/llxml/llxmltree.cpp @@ -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 ); diff --git a/indra/llxml/llxmltree.h b/indra/llxml/llxmltree.h index bdcb56f1f3..0379cb7106 100644 --- a/indra/llxml/llxmltree.h +++ b/indra/llxml/llxmltree.h @@ -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(); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 00bba71edc..f938d535a0 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -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) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index f16a09c062..afa4620e32 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -13425,5 +13425,49 @@ Change of this parameter will affect the layout of buttons in notification toast Value 0 + KittyFlickrLastRating + + Comment + Last rating for Flickr upload. 1 = safe, 2 = moderate, 3 = restricted + Persist + 1 + Type + S32 + Value + 1 + + KittyFlickrLastTags + + Comment + Last tags used on Flickr upload + Persist + 1 + Type + String + Value + "Second Life" + + KittyFlickrShowPosition + + Comment + Whether to show the position of a Flickr upload + Persist + 1 + Type + Boolean + Value + 1 + + KittyFlickrIncludeSLURL + + Comment + If showing the position of an image, whether an SLurl should be included in the description. + Persist + 1 + Type + Boolean + Value + 1 + diff --git a/indra/newview/app_settings/settings_per_account.xml b/indra/newview/app_settings/settings_per_account.xml index 13e59e8f63..1553f1a67b 100644 --- a/indra/newview/app_settings/settings_per_account.xml +++ b/indra/newview/app_settings/settings_per_account.xml @@ -196,6 +196,39 @@ + KittyFlickrToken + + Comment + Token used to authenticate with Flickr + Persist + 1 + Type + String + Value + + + KittyFlickrUsername + + Comment + Username associated with Flickr account + Persist + 1 + Type + String + Value + + + KittyFlickrNSID + + Comment + NSID associated with Flickr account + Persist + 1 + Type + String + Value + + DebugLookAt Comment diff --git a/indra/newview/kvflickr.cpp b/indra/newview/kvflickr.cpp new file mode 100644 index 0000000000..805cfed1ab --- /dev/null +++ b/indra/newview/kvflickr.cpp @@ -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 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::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); + } +} + diff --git a/indra/newview/kvflickr.h b/indra/newview/kvflickr.h new file mode 100644 index 0000000000..569a9cde21 --- /dev/null +++ b/indra/newview/kvflickr.h @@ -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 + +// This include file defines the API key/secret. +// It is generated by cmake. +#include "kvflickrkeys.h" + +class KVFlickrRequest +{ +public: + typedef boost::function 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 diff --git a/indra/newview/kvflickrkeys.h.in b/indra/newview/kvflickrkeys.h.in new file mode 100644 index 0000000000..023675cee1 --- /dev/null +++ b/indra/newview/kvflickrkeys.h.in @@ -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@" diff --git a/indra/newview/kvfloaterflickrauth.cpp b/indra/newview/kvfloaterflickrauth.cpp new file mode 100644 index 0000000000..e590b70ec7 --- /dev/null +++ b/indra/newview/kvfloaterflickrauth.cpp @@ -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("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(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; + } +} diff --git a/indra/newview/kvfloaterflickrauth.h b/indra/newview/kvfloaterflickrauth.h new file mode 100644 index 0000000000..bb77319179 --- /dev/null +++ b/indra/newview/kvfloaterflickrauth.h @@ -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 + +class KVFloaterFlickrAuth : +public LLFloater, +public LLViewerMediaObserver +{ +public: + typedef boost::function 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 diff --git a/indra/newview/kvfloaterflickrupload.cpp b/indra/newview/kvfloaterflickrupload.cpp new file mode 100644 index 0000000000..98db3e5b6d --- /dev/null +++ b/indra/newview/kvfloaterflickrupload.cpp @@ -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 + +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("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 << ""; + 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 << ""; + 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); +} diff --git a/indra/newview/kvfloaterflickrupload.h b/indra/newview/kvfloaterflickrupload.h new file mode 100644 index 0000000000..1dbf35d151 --- /dev/null +++ b/indra/newview/kvfloaterflickrupload.h @@ -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 mCompressedImage; + LLPointer mViewerImage; + LLVector2 mImageScale; + LLVector3d mPosTakenGlobal; + std::string mTitle; // Used in the confirmation announcement. +}; + + +#endif // KV_KVFLOATERFLICKRUPLOAD_H diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp index a4a95d3db8..32ba3b302f 100644 --- a/indra/newview/llfloatersnapshot.cpp +++ b/indra/newview/llfloatersnapshot.cpp @@ -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("local_format_combo"); - const std::string id = local_format_combo->getSelectedItemLabel(); + LLComboBox* format_combo = floater->findChild("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("postcard_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotPostcardLastResolution")); floater->getChild("texture_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotTextureLastResolution")); floater->getChild("local_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotLocalLastResolution")); - floater->getChild("local_format_combo")->selectNthItem(gSavedSettings.getS32("SnapshotFormat")); + floater->getChild("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("texture")->setLabelArg("[AMOUNT]", llformat("%d",upload_cost)); - floater->getChild("upload_btn")->setLabelArg("[AMOUNT]", llformat("%d",upload_cost)); floater->getChild("file_size_label")->setTextArg("[SIZE]", got_snap ? bytes_string : floater->getString("unknown")); floater->getChild("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); diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index af41a91b66..f3a634e6b6 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -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); // [/SL:KB] + LLFloaterReg::add("flickr_auth", "floater_flickr_auth.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("flickr_upload", "floater_flickr_upload.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("font_test", "floater_font_test.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("gestures", "floater_gesture.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); diff --git a/indra/newview/skins/default/xui/en/floater_flickr_auth.xml b/indra/newview/skins/default/xui/en/floater_flickr_auth.xml new file mode 100644 index 0000000000..a9516d281f --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_flickr_auth.xml @@ -0,0 +1,23 @@ + + + + diff --git a/indra/newview/skins/default/xui/en/floater_flickr_upload.xml b/indra/newview/skins/default/xui/en/floater_flickr_upload.xml new file mode 100644 index 0000000000..972d316a15 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_flickr_upload.xml @@ -0,0 +1,188 @@ + + + + (not authorised) + + + Uploading to Flickr... + + + Photo Title: + + + + Tags: + + + + Rating: + + + + + + + + + + + + + + + Flickr Account: + + + Loading... + + + Description: + + + + +