summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
authorMerov Linden <merov@lindenlab.com>2013-12-03 11:53:31 -0800
committerMerov Linden <merov@lindenlab.com>2013-12-03 11:53:31 -0800
commit6496e5877b0996353496101433bb9154926fef8b (patch)
treef75bf4fca9d150593e1ad3623cc26364d527e040 /indra/newview
parent542864f8ea9bb5602ac76553d76898178ad79d0f (diff)
parentbf86275d78b041faffc646195bc16ecabaa3bdb7 (diff)
Pull merge from viewer-acme
Diffstat (limited to 'indra/newview')
-rwxr-xr-xindra/newview/CMakeLists.txt8
-rwxr-xr-xindra/newview/app_settings/commands.xml20
-rwxr-xr-xindra/newview/app_settings/toolbars.xml2
-rw-r--r--indra/newview/llfacebookconnect.cpp100
-rw-r--r--indra/newview/llfacebookconnect.h4
-rw-r--r--indra/newview/llflickrconnect.cpp477
-rw-r--r--indra/newview/llflickrconnect.h98
-rw-r--r--indra/newview/llfloaterflickr.cpp703
-rw-r--r--indra/newview/llfloaterflickr.h127
-rwxr-xr-xindra/newview/llfloatersnapshot.cpp8
-rw-r--r--indra/newview/llfloatersocial.cpp13
-rw-r--r--indra/newview/llfloatersocial.h2
-rw-r--r--indra/newview/llfloatertwitter.cpp697
-rw-r--r--indra/newview/llfloatertwitter.h126
-rwxr-xr-xindra/newview/llfloaterwebcontent.cpp21
-rwxr-xr-xindra/newview/llpanelsnapshotoptions.cpp57
-rw-r--r--indra/newview/llsnapshotlivepreview.cpp6
-rw-r--r--indra/newview/lltwitterconnect.cpp471
-rw-r--r--indra/newview/lltwitterconnect.h99
-rwxr-xr-xindra/newview/llviewerfloaterreg.cpp10
-rwxr-xr-xindra/newview/skins/default/textures/textures.xml2
-rw-r--r--indra/newview/skins/default/textures/toolbar_icons/flickr.pngbin0 -> 15530 bytes
-rw-r--r--indra/newview/skins/default/textures/toolbar_icons/twitter.pngbin0 -> 16051 bytes
-rw-r--r--indra/newview/skins/default/xui/en/floater_flickr.xml89
-rwxr-xr-xindra/newview/skins/default/xui/en/floater_snapshot.xml2
-rw-r--r--indra/newview/skins/default/xui/en/floater_twitter.xml89
-rwxr-xr-xindra/newview/skins/default/xui/en/menu_viewer.xml30
-rwxr-xr-xindra/newview/skins/default/xui/en/notifications.xml16
-rw-r--r--indra/newview/skins/default/xui/en/panel_flickr_account.xml75
-rw-r--r--indra/newview/skins/default/xui/en/panel_flickr_photo.xml231
-rwxr-xr-xindra/newview/skins/default/xui/en/panel_snapshot_options.xml36
-rw-r--r--indra/newview/skins/default/xui/en/panel_twitter_account.xml75
-rw-r--r--indra/newview/skins/default/xui/en/panel_twitter_photo.xml156
-rwxr-xr-xindra/newview/skins/default/xui/en/strings.xml36
34 files changed, 3808 insertions, 78 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 1fea6dea9f..746991a6f0 100755
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -199,6 +199,7 @@ set(viewer_SOURCE_FILES
llfilteredwearablelist.cpp
llfirstuse.cpp
llflexibleobject.cpp
+ llflickrconnect.cpp
llfloaterabout.cpp
llfloaterbvhpreview.cpp
llfloaterauction.cpp
@@ -228,6 +229,7 @@ set(viewer_SOURCE_FILES
llfloatereditwater.cpp
llfloaterenvironmentsettings.cpp
llfloaterevent.cpp
+ llfloaterflickr.cpp
llfloaterfonttest.cpp
llfloatergesture.cpp
llfloatergodtools.cpp
@@ -286,6 +288,7 @@ set(viewer_SOURCE_FILES
llfloatertos.cpp
llfloatertoybox.cpp
llfloatertranslationsettings.cpp
+ llfloatertwitter.cpp
llfloateruipreview.cpp
llfloaterurlentry.cpp
llfloatervoiceeffect.cpp
@@ -567,6 +570,7 @@ set(viewer_SOURCE_FILES
lltransientdockablefloater.cpp
lltransientfloatermgr.cpp
lltranslate.cpp
+ lltwitterconnect.cpp
lluilistener.cpp
lluploaddialog.cpp
lluploadfloaterobservers.cpp
@@ -787,6 +791,7 @@ set(viewer_HEADER_FILES
llfilteredwearablelist.h
llfirstuse.h
llflexibleobject.h
+ llflickrconnect.h
llfloaterabout.h
llfloaterbvhpreview.h
llfloaterauction.h
@@ -816,6 +821,7 @@ set(viewer_HEADER_FILES
llfloatereditwater.h
llfloaterenvironmentsettings.h
llfloaterevent.h
+ llfloaterflickr.h
llfloaterfonttest.h
llfloatergesture.h
llfloatergodtools.h
@@ -874,6 +880,7 @@ set(viewer_HEADER_FILES
llfloatertos.h
llfloatertoybox.h
llfloatertranslationsettings.h
+ llfloatertwitter.h
llfloateruipreview.h
llfloaterurlentry.h
llfloatervoiceeffect.h
@@ -1145,6 +1152,7 @@ set(viewer_HEADER_FILES
lltransientdockablefloater.h
lltransientfloatermgr.h
lltranslate.h
+ lltwitterconnect.h
lluiconstants.h
lluilistener.h
lluploaddialog.h
diff --git a/indra/newview/app_settings/commands.xml b/indra/newview/app_settings/commands.xml
index 60c942094a..f4e9cc0136 100755
--- a/indra/newview/app_settings/commands.xml
+++ b/indra/newview/app_settings/commands.xml
@@ -226,6 +226,26 @@
is_running_function="Floater.IsOpen"
is_running_parameters="social"
/>
+ <command name="flickr"
+ available_in_toybox="true"
+ icon="Command_Flickr_Icon"
+ label_ref="Command_Flickr_Label"
+ tooltip_ref="Command_Flickr_Tooltip"
+ execute_function="Floater.ToggleOrBringToFront"
+ execute_parameters="flickr"
+ is_running_function="Floater.IsOpen"
+ is_running_parameters="flickr"
+ />
+ <command name="twitter"
+ available_in_toybox="true"
+ icon="Command_Twitter_Icon"
+ label_ref="Command_Twitter_Label"
+ tooltip_ref="Command_Twitter_Tooltip"
+ execute_function="Floater.ToggleOrBringToFront"
+ execute_parameters="twitter"
+ is_running_function="Floater.IsOpen"
+ is_running_parameters="twitter"
+ />
<command name="speak"
available_in_toybox="true"
icon="Command_Speak_Icon"
diff --git a/indra/newview/app_settings/toolbars.xml b/indra/newview/app_settings/toolbars.xml
index 86f9912815..c65b79affb 100755
--- a/indra/newview/app_settings/toolbars.xml
+++ b/indra/newview/app_settings/toolbars.xml
@@ -6,7 +6,6 @@
<command name="speak"/>
<command name="destinations"/>
<command name="people"/>
- <command name="social"/>
<command name="profile"/>
<command name="move"/>
<command name="view"/>
@@ -22,5 +21,6 @@
<command name="voice"/>
<command name="minimap"/>
<command name="snapshot"/>
+ <command name="social"/>
</left_toolbar>
</toolbars>
diff --git a/indra/newview/llfacebookconnect.cpp b/indra/newview/llfacebookconnect.cpp
index f82c5a05a0..0a662b4671 100644
--- a/indra/newview/llfacebookconnect.cpp
+++ b/indra/newview/llfacebookconnect.cpp
@@ -28,6 +28,8 @@
#include "llviewerprecompiledheaders.h"
#include "llfacebookconnect.h"
+#include "llflickrconnect.h"
+#include "lltwitterconnect.h"
#include "llagent.h"
#include "llcallingcard.h" // for LLAvatarTracker
@@ -58,7 +60,7 @@ void log_facebook_connect_error(const std::string& request, U32 status, const st
}
}
-void toast_user_for_success()
+void toast_user_for_facebook_success()
{
LLSD args;
args["MESSAGE"] = LLTrans::getString("facebook_post_success");
@@ -74,23 +76,58 @@ public:
bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web)
{
- if (tokens.size() > 0)
+ if (tokens.size() >= 1)
{
if (tokens[0].asString() == "connect")
{
- // this command probably came from the fbc_web browser, so close it
- LLFloater* fbc_web = LLFloaterReg::getInstance("fbc_web");
- if (fbc_web)
+ if (tokens.size() >= 2 && tokens[1].asString() == "flickr")
{
- fbc_web->closeFloater();
+ // this command probably came from the flickr_web browser, so close it
+ LLFloater* flickr_web = LLFloaterReg::getInstance("flickr_web");
+ if (flickr_web)
+ {
+ flickr_web->closeFloater();
+ }
+
+ // connect to flickr
+ if (query_map.has("oauth_token"))
+ {
+ LLFlickrConnect::instance().connectToFlickr(query_map["oauth_token"], query_map.get("oauth_verifier"));
+ }
+ return true;
}
-
- // connect to facebook
- if (query_map.has("code"))
+ else if (tokens.size() >= 2 && tokens[1].asString() == "twitter")
+ {
+ // this command probably came from the twitter_web browser, so close it
+ LLFloater* twitter_web = LLFloaterReg::getInstance("twitter_web");
+ if (twitter_web)
+ {
+ twitter_web->closeFloater();
+ }
+
+ // connect to twitter
+ if (query_map.has("oauth_token"))
+ {
+ LLTwitterConnect::instance().connectToTwitter(query_map["oauth_token"], query_map.get("oauth_verifier"));
+ }
+ return true;
+ }
+ else //if (tokens.size() >= 2 && tokens[1].asString() == "facebook")
{
- LLFacebookConnect::instance().connectToFacebook(query_map["code"], query_map.get("state"));
+ // this command probably came from the fbc_web browser, so close it
+ LLFloater* fbc_web = LLFloaterReg::getInstance("fbc_web");
+ if (fbc_web)
+ {
+ fbc_web->closeFloater();
+ }
+
+ // connect to facebook
+ if (query_map.has("code"))
+ {
+ LLFacebookConnect::instance().connectToFacebook(query_map["code"], query_map.get("state"));
+ }
+ return true;
}
- return true;
}
}
return false;
@@ -150,7 +187,7 @@ public:
{
if (isGoodStatus(status))
{
- toast_user_for_success();
+ toast_user_for_facebook_success();
LL_DEBUGS("FacebookConnect") << "Post successful. content: " << content << LL_ENDL;
LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_POSTED);
@@ -371,40 +408,6 @@ std::string LLFacebookConnect::getFacebookConnectURL(const std::string& route, b
return url;
}
-std::string LLFacebookConnect::getFlickrConnectURL(const std::string& route, bool include_read_from_master)
-{
- std::string url("");
- LLViewerRegion *regionp = gAgent.getRegion();
- if (regionp)
- {
- url = regionp->getCapability("FlickrConnect");
- url += route;
-
- if (include_read_from_master && mReadFromMaster)
- {
- url += "?read_from_master=true";
- }
- }
- return url;
-}
-
-std::string LLFacebookConnect::getTwitterConnectURL(const std::string& route, bool include_read_from_master)
-{
- std::string url("");
- LLViewerRegion *regionp = gAgent.getRegion();
- if (regionp)
- {
- url = regionp->getCapability("TwitterConnect");
- url += route;
-
- if (include_read_from_master && mReadFromMaster)
- {
- url += "?read_from_master=true";
- }
- }
- return url;
-}
-
void LLFacebookConnect::connectToFacebook(const std::string& auth_code, const std::string& auth_state)
{
LLSD body;
@@ -431,11 +434,6 @@ void LLFacebookConnect::checkConnectionToFacebook(bool auto_connect)
const F32 timeout = HTTP_REQUEST_EXPIRY_SECS;
LLHTTPClient::get(getFacebookConnectURL("/connection", true), new LLFacebookConnectedResponder(auto_connect),
LLSD(), timeout, follow_redirects);
-
- // TEMPORARY FOR TESTING - CHO
- llinfos << "FlickrConnect URL: " << getFlickrConnectURL() << LL_ENDL;
- llinfos << "TwitterConnect URL: " << getTwitterConnectURL() << LL_ENDL;
-
}
void LLFacebookConnect::loadFacebookInfo()
diff --git a/indra/newview/llfacebookconnect.h b/indra/newview/llfacebookconnect.h
index c4117174c1..c157db2178 100644
--- a/indra/newview/llfacebookconnect.h
+++ b/indra/newview/llfacebookconnect.h
@@ -90,10 +90,6 @@ private:
~LLFacebookConnect() {};
std::string getFacebookConnectURL(const std::string& route = "", bool include_read_from_master = false);
- // TEMPORARY FOR TESTING - CHO
- std::string getFlickrConnectURL(const std::string& route = "", bool include_read_from_master = false);
- std::string getTwitterConnectURL(const std::string& route = "", bool include_read_from_master = false);
-
EConnectionState mConnectionState;
BOOL mConnected;
LLSD mInfo;
diff --git a/indra/newview/llflickrconnect.cpp b/indra/newview/llflickrconnect.cpp
new file mode 100644
index 0000000000..3a21933b63
--- /dev/null
+++ b/indra/newview/llflickrconnect.cpp
@@ -0,0 +1,477 @@
+/**
+ * @file llflickrconnect.h
+ * @author Merov, Cho
+ * @brief Connection to Flickr Service
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llflickrconnect.h"
+
+#include "llagent.h"
+#include "llcallingcard.h" // for LLAvatarTracker
+#include "llcommandhandler.h"
+#include "llhttpclient.h"
+#include "llnotificationsutil.h"
+#include "llurlaction.h"
+#include "llimagepng.h"
+#include "llimagejpeg.h"
+#include "lltrans.h"
+#include "llevents.h"
+#include "llviewerregion.h"
+
+#include "llfloaterwebcontent.h"
+#include "llfloaterreg.h"
+
+boost::scoped_ptr<LLEventPump> LLFlickrConnect::sStateWatcher(new LLEventStream("FlickrConnectState"));
+boost::scoped_ptr<LLEventPump> LLFlickrConnect::sInfoWatcher(new LLEventStream("FlickrConnectInfo"));
+boost::scoped_ptr<LLEventPump> LLFlickrConnect::sContentWatcher(new LLEventStream("FlickrConnectContent"));
+
+// Local functions
+void log_flickr_connect_error(const std::string& request, U32 status, const std::string& reason, const std::string& code, const std::string& description)
+{
+ // Note: 302 (redirect) is *not* an error that warrants logging
+ if (status != 302)
+ {
+ LL_WARNS("FlickrConnect") << request << " request failed with a " << status << " " << reason << ". Reason: " << code << " (" << description << ")" << LL_ENDL;
+ }
+}
+
+void toast_user_for_flickr_success()
+{
+ LLSD args;
+ args["MESSAGE"] = LLTrans::getString("flickr_post_success");
+ LLNotificationsUtil::add("FlickrConnect", args);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLFlickrConnectResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLFlickrConnectResponder);
+public:
+
+ LLFlickrConnectResponder()
+ {
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTION_IN_PROGRESS);
+ }
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (isGoodStatus(status))
+ {
+ LL_DEBUGS("FlickrConnect") << "Connect successful. content: " << content << LL_ENDL;
+
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTED);
+ }
+ else if (status != 302)
+ {
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTION_FAILED);
+ log_flickr_connect_error("Connect", status, reason, content.get("error_code"), content.get("error_description"));
+ }
+ }
+
+ void completedHeader(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (status == 302)
+ {
+ LLFlickrConnect::instance().openFlickrWeb(content["location"]);
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLFlickrShareResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLFlickrShareResponder);
+public:
+
+ LLFlickrShareResponder()
+ {
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_POSTING);
+ }
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (isGoodStatus(status))
+ {
+ toast_user_for_flickr_success();
+ LL_DEBUGS("FlickrConnect") << "Post successful. content: " << content << LL_ENDL;
+
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_POSTED);
+ }
+ else if (status == 404)
+ {
+ LLFlickrConnect::instance().connectToFlickr();
+ }
+ else
+ {
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_POST_FAILED);
+ log_flickr_connect_error("Share", status, reason, content.get("error_code"), content.get("error_description"));
+ }
+ }
+
+ void completedHeader(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (status == 302)
+ {
+ LLFlickrConnect::instance().openFlickrWeb(content["location"]);
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLFlickrDisconnectResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLFlickrDisconnectResponder);
+public:
+
+ LLFlickrDisconnectResponder()
+ {
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_DISCONNECTING);
+ }
+
+ void setUserDisconnected()
+ {
+ // Clear data
+ LLFlickrConnect::instance().clearInfo();
+
+ //Notify state change
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_NOT_CONNECTED);
+ }
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (isGoodStatus(status))
+ {
+ LL_DEBUGS("FlickrConnect") << "Disconnect successful. content: " << content << LL_ENDL;
+ setUserDisconnected();
+
+ }
+ //User not found so already disconnected
+ else if(status == 404)
+ {
+ LL_DEBUGS("FlickrConnect") << "Already disconnected. content: " << content << LL_ENDL;
+ setUserDisconnected();
+ }
+ else
+ {
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_DISCONNECT_FAILED);
+ log_flickr_connect_error("Disconnect", status, reason, content.get("error_code"), content.get("error_description"));
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLFlickrConnectedResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLFlickrConnectedResponder);
+public:
+
+ LLFlickrConnectedResponder(bool auto_connect) : mAutoConnect(auto_connect)
+ {
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTION_IN_PROGRESS);
+ }
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (isGoodStatus(status))
+ {
+ LL_DEBUGS("FlickrConnect") << "Connect successful. content: " << content << LL_ENDL;
+
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTED);
+ }
+ else
+ {
+ // show the flickr login page if not connected yet
+ if (status == 404)
+ {
+ if (mAutoConnect)
+ {
+ LLFlickrConnect::instance().connectToFlickr();
+ }
+ else
+ {
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_NOT_CONNECTED);
+ }
+ }
+ else
+ {
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTION_FAILED);
+ log_flickr_connect_error("Connected", status, reason, content.get("error_code"), content.get("error_description"));
+ }
+ }
+ }
+
+private:
+ bool mAutoConnect;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLFlickrInfoResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLFlickrInfoResponder);
+public:
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& info)
+ {
+ if (isGoodStatus(status))
+ {
+ llinfos << "Flickr: Info received" << llendl;
+ LL_DEBUGS("FlickrConnect") << "Getting Flickr info successful. info: " << info << LL_ENDL;
+ LLFlickrConnect::instance().storeInfo(info);
+ }
+ else
+ {
+ log_flickr_connect_error("Info", status, reason, info.get("error_code"), info.get("error_description"));
+ }
+ }
+
+ void completedHeader(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (status == 302)
+ {
+ LLFlickrConnect::instance().openFlickrWeb(content["location"]);
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+LLFlickrConnect::LLFlickrConnect()
+: mConnectionState(FLICKR_NOT_CONNECTED),
+ mConnected(false),
+ mInfo(),
+ mRefreshInfo(false),
+ mReadFromMaster(false)
+{
+}
+
+void LLFlickrConnect::openFlickrWeb(std::string url)
+{
+ // Open the URL in an internal browser window without navigation UI
+ LLFloaterWebContent::Params p;
+ p.url(url).show_chrome(true);
+ p.url(url).allow_address_entry(false);
+ p.url(url).allow_back_forward_navigation(false);
+ p.url(url).trusted_content(true);
+ LLFloater *floater = LLFloaterReg::showInstance("flickr_web", p);
+ //the internal web browser has a bug that prevents it from gaining focus unless a mouse event occurs first (it seems).
+ //So when showing the internal web browser, set focus to it's containing floater "flickr_web". When a mouse event
+ //occurs on the "webbrowser" panel part of the floater, a mouse cursor will properly show and the "webbrowser" will gain focus.
+ //flickr_web floater contains the "webbrowser" panel. JIRA: ACME-744
+ gFocusMgr.setKeyboardFocus( floater );
+
+ //LLUrlAction::openURLExternal(url);
+}
+
+std::string LLFlickrConnect::getFlickrConnectURL(const std::string& route, bool include_read_from_master)
+{
+ std::string url("");
+ LLViewerRegion *regionp = gAgent.getRegion();
+ if (regionp)
+ {
+ //url = "http://pdp15.lindenlab.com/flickr/agent/" + gAgentID.asString(); // TEMPORARY FOR TESTING - CHO
+ url = regionp->getCapability("FlickrConnect");
+ url += route;
+
+ if (include_read_from_master && mReadFromMaster)
+ {
+ url += "?read_from_master=true";
+ }
+ }
+ return url;
+}
+
+void LLFlickrConnect::connectToFlickr(const std::string& request_token, const std::string& oauth_verifier)
+{
+ LLSD body;
+ if (!request_token.empty())
+ body["request_token"] = request_token;
+ if (!oauth_verifier.empty())
+ body["oauth_verifier"] = oauth_verifier;
+
+ LLHTTPClient::put(getFlickrConnectURL("/connection"), body, new LLFlickrConnectResponder());
+}
+
+void LLFlickrConnect::disconnectFromFlickr()
+{
+ LLHTTPClient::del(getFlickrConnectURL("/connection"), new LLFlickrDisconnectResponder());
+}
+
+void LLFlickrConnect::checkConnectionToFlickr(bool auto_connect)
+{
+ const bool follow_redirects = false;
+ const F32 timeout = HTTP_REQUEST_EXPIRY_SECS;
+ LLHTTPClient::get(getFlickrConnectURL("/connection", true), new LLFlickrConnectedResponder(auto_connect),
+ LLSD(), timeout, follow_redirects);
+}
+
+void LLFlickrConnect::loadFlickrInfo()
+{
+ if(mRefreshInfo)
+ {
+ const bool follow_redirects = false;
+ const F32 timeout = HTTP_REQUEST_EXPIRY_SECS;
+ LLHTTPClient::get(getFlickrConnectURL("/info", true), new LLFlickrInfoResponder(),
+ LLSD(), timeout, follow_redirects);
+ }
+}
+
+void LLFlickrConnect::uploadPhoto(const std::string& image_url, const std::string& title, const std::string& description, const std::string& tags, int safety_level)
+{
+ LLSD body;
+ body["image"] = image_url;
+ body["title"] = title;
+ body["description"] = description;
+ body["tags"] = tags;
+ body["safety_level"] = safety_level;
+
+ // Note: we can use that route for different publish action. We should be able to use the same responder.
+ LLHTTPClient::post(getFlickrConnectURL("/share/photo", true), body, new LLFlickrShareResponder());
+}
+
+void LLFlickrConnect::uploadPhoto(LLPointer<LLImageFormatted> image, const std::string& title, const std::string& description, const std::string& tags, int safety_level)
+{
+ std::string imageFormat;
+ if (dynamic_cast<LLImagePNG*>(image.get()))
+ {
+ imageFormat = "png";
+ }
+ else if (dynamic_cast<LLImageJPEG*>(image.get()))
+ {
+ imageFormat = "jpg";
+ }
+ else
+ {
+ llwarns << "Image to upload is not a PNG or JPEG" << llendl;
+ return;
+ }
+
+ // All this code is mostly copied from LLWebProfile::post()
+ const std::string boundary = "----------------------------0123abcdefab";
+
+ LLSD headers;
+ headers["Content-Type"] = "multipart/form-data; boundary=" + boundary;
+
+ std::ostringstream body;
+
+ // *NOTE: The order seems to matter.
+ body << "--" << boundary << "\r\n"
+ << "Content-Disposition: form-data; name=\"title\"\r\n\r\n"
+ << title << "\r\n";
+
+ body << "--" << boundary << "\r\n"
+ << "Content-Disposition: form-data; name=\"description\"\r\n\r\n"
+ << description << "\r\n";
+
+ body << "--" << boundary << "\r\n"
+ << "Content-Disposition: form-data; name=\"tags\"\r\n\r\n"
+ << tags << "\r\n";
+
+ body << "--" << boundary << "\r\n"
+ << "Content-Disposition: form-data; name=\"safety_level\"\r\n\r\n"
+ << safety_level << "\r\n";
+
+ body << "--" << boundary << "\r\n"
+ << "Content-Disposition: form-data; name=\"image\"; filename=\"snapshot." << imageFormat << "\"\r\n"
+ << "Content-Type: image/" << imageFormat << "\r\n\r\n";
+
+ // Insert the image data.
+ // *FIX: Treating this as a string will probably screw it up ...
+ U8* image_data = image->getData();
+ for (S32 i = 0; i < image->getDataSize(); ++i)
+ {
+ body << image_data[i];
+ }
+
+ body << "\r\n--" << boundary << "--\r\n";
+
+ // postRaw() takes ownership of the buffer and releases it later.
+ size_t size = body.str().size();
+ U8 *data = new U8[size];
+ memcpy(data, body.str().data(), size);
+
+ // Note: we can use that route for different publish action. We should be able to use the same responder.
+ LLHTTPClient::postRaw(getFlickrConnectURL("/share/photo", true), data, size, new LLFlickrShareResponder(), headers);
+}
+
+void LLFlickrConnect::storeInfo(const LLSD& info)
+{
+ mInfo = info;
+ mRefreshInfo = false;
+
+ sInfoWatcher->post(info);
+}
+
+const LLSD& LLFlickrConnect::getInfo() const
+{
+ return mInfo;
+}
+
+void LLFlickrConnect::clearInfo()
+{
+ mInfo = LLSD();
+}
+
+void LLFlickrConnect::setDataDirty()
+{
+ mRefreshInfo = true;
+}
+
+void LLFlickrConnect::setConnectionState(LLFlickrConnect::EConnectionState connection_state)
+{
+ if(connection_state == FLICKR_CONNECTED)
+ {
+ mReadFromMaster = true;
+ setConnected(true);
+ setDataDirty();
+ }
+ else if(connection_state == FLICKR_NOT_CONNECTED)
+ {
+ setConnected(false);
+ }
+ else if(connection_state == FLICKR_POSTED)
+ {
+ mReadFromMaster = false;
+ }
+
+ if (mConnectionState != connection_state)
+ {
+ LLSD state_info;
+ state_info["enum"] = connection_state;
+ sStateWatcher->post(state_info);
+ }
+
+ mConnectionState = connection_state;
+}
+
+void LLFlickrConnect::setConnected(bool connected)
+{
+ mConnected = connected;
+}
diff --git a/indra/newview/llflickrconnect.h b/indra/newview/llflickrconnect.h
new file mode 100644
index 0000000000..b127e6e104
--- /dev/null
+++ b/indra/newview/llflickrconnect.h
@@ -0,0 +1,98 @@
+/**
+ * @file llflickrconnect.h
+ * @author Merov, Cho
+ * @brief Connection to Flickr Service
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLFLICKRCONNECT_H
+#define LL_LLFLICKRCONNECT_H
+
+#include "llsingleton.h"
+#include "llimage.h"
+
+class LLEventPump;
+
+/**
+ * @class LLFlickrConnect
+ *
+ * Manages authentication to, and interaction with, a web service allowing the
+ * the viewer to upload photos to Flickr.
+ */
+class LLFlickrConnect : public LLSingleton<LLFlickrConnect>
+{
+ LOG_CLASS(LLFlickrConnect);
+public:
+ enum EConnectionState
+ {
+ FLICKR_NOT_CONNECTED = 0,
+ FLICKR_CONNECTION_IN_PROGRESS = 1,
+ FLICKR_CONNECTED = 2,
+ FLICKR_CONNECTION_FAILED = 3,
+ FLICKR_POSTING = 4,
+ FLICKR_POSTED = 5,
+ FLICKR_POST_FAILED = 6,
+ FLICKR_DISCONNECTING = 7,
+ FLICKR_DISCONNECT_FAILED = 8
+ };
+
+ void connectToFlickr(const std::string& request_token = "", const std::string& oauth_verifier = ""); // Initiate the complete Flickr connection. Please use checkConnectionToFlickr() in normal use.
+ void disconnectFromFlickr(); // Disconnect from the Flickr service.
+ void checkConnectionToFlickr(bool auto_connect = false); // Check if an access token is available on the Flickr service. If not, call connectToFlickr().
+
+ void loadFlickrInfo();
+ void uploadPhoto(const std::string& image_url, const std::string& title, const std::string& description, const std::string& tags, int safety_level);
+ void uploadPhoto(LLPointer<LLImageFormatted> image, const std::string& title, const std::string& description, const std::string& tags, int safety_level);
+
+ void storeInfo(const LLSD& info);
+ const LLSD& getInfo() const;
+ void clearInfo();
+ void setDataDirty();
+
+ void setConnectionState(EConnectionState connection_state);
+ void setConnected(bool connected);
+ bool isConnected() { return mConnected; }
+ bool isTransactionOngoing() { return ((mConnectionState == FLICKR_CONNECTION_IN_PROGRESS) || (mConnectionState == FLICKR_POSTING) || (mConnectionState == FLICKR_DISCONNECTING)); }
+ EConnectionState getConnectionState() { return mConnectionState; }
+
+ void openFlickrWeb(std::string url);
+
+private:
+ friend class LLSingleton<LLFlickrConnect>;
+
+ LLFlickrConnect();
+ ~LLFlickrConnect() {};
+ std::string getFlickrConnectURL(const std::string& route = "", bool include_read_from_master = false);
+
+ EConnectionState mConnectionState;
+ BOOL mConnected;
+ LLSD mInfo;
+ bool mRefreshInfo;
+ bool mReadFromMaster;
+
+ static boost::scoped_ptr<LLEventPump> sStateWatcher;
+ static boost::scoped_ptr<LLEventPump> sInfoWatcher;
+ static boost::scoped_ptr<LLEventPump> sContentWatcher;
+};
+
+#endif // LL_LLFLICKRCONNECT_H
diff --git a/indra/newview/llfloaterflickr.cpp b/indra/newview/llfloaterflickr.cpp
new file mode 100644
index 0000000000..61ebe563a3
--- /dev/null
+++ b/indra/newview/llfloaterflickr.cpp
@@ -0,0 +1,703 @@
+/**
+* @file llfloaterflickr.cpp
+* @brief Implementation of llfloaterflickr
+* @author cho@lindenlab.com
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2013, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterflickr.h"
+
+#include "llagent.h"
+#include "llagentui.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "llflickrconnect.h"
+#include "llfloaterreg.h"
+#include "lliconctrl.h"
+#include "llresmgr.h" // LLLocale
+#include "llsdserialize.h"
+#include "llloadingindicator.h"
+#include "llplugincookiestore.h"
+#include "llslurl.h"
+#include "lltrans.h"
+#include "llsnapshotlivepreview.h"
+#include "llviewerregion.h"
+#include "llviewercontrol.h"
+#include "llviewermedia.h"
+#include "lltabcontainer.h"
+
+static LLRegisterPanelClassWrapper<LLFlickrPhotoPanel> t_panel_photo("llflickrphotopanel");
+static LLRegisterPanelClassWrapper<LLFlickrAccountPanel> t_panel_account("llflickraccountpanel");
+
+const S32 MAX_POSTCARD_DATASIZE = 1024 * 1024; // one megabyte
+const std::string DEFAULT_PHOTO_QUERY_PARAMETERS = "?sourceid=slshare_photo&utm_source=flickr&utm_medium=photo&utm_campaign=slshare";
+const std::string DEFAULT_PHOTO_LINK_TEXT = "Visit this location now";
+const std::string DEFAULT_TAG_TEXT = "secondlife ";
+
+///////////////////////////
+//LLFlickrPhotoPanel///////
+///////////////////////////
+
+LLFlickrPhotoPanel::LLFlickrPhotoPanel() :
+mSnapshotPanel(NULL),
+mResolutionComboBox(NULL),
+mRefreshBtn(NULL),
+mWorkingLabel(NULL),
+mThumbnailPlaceholder(NULL),
+mTitleTextBox(NULL),
+mDescriptionTextBox(NULL),
+mLocationCheckbox(NULL),
+mTagsTextBox(NULL),
+mRatingComboBox(NULL),
+mPostButton(NULL)
+{
+ mCommitCallbackRegistrar.add("SocialSharing.SendPhoto", boost::bind(&LLFlickrPhotoPanel::onSend, this));
+ mCommitCallbackRegistrar.add("SocialSharing.RefreshPhoto", boost::bind(&LLFlickrPhotoPanel::onClickNewSnapshot, this));
+}
+
+LLFlickrPhotoPanel::~LLFlickrPhotoPanel()
+{
+ if(mPreviewHandle.get())
+ {
+ mPreviewHandle.get()->die();
+ }
+}
+
+BOOL LLFlickrPhotoPanel::postBuild()
+{
+ setVisibleCallback(boost::bind(&LLFlickrPhotoPanel::onVisibilityChange, this, _2));
+
+ mSnapshotPanel = getChild<LLUICtrl>("snapshot_panel");
+ mResolutionComboBox = getChild<LLUICtrl>("resolution_combobox");
+ mResolutionComboBox->setCommitCallback(boost::bind(&LLFlickrPhotoPanel::updateResolution, this, TRUE));
+ mRefreshBtn = getChild<LLUICtrl>("new_snapshot_btn");
+ mWorkingLabel = getChild<LLUICtrl>("working_lbl");
+ mThumbnailPlaceholder = getChild<LLUICtrl>("thumbnail_placeholder");
+ mTitleTextBox = getChild<LLUICtrl>("photo_title");
+ mDescriptionTextBox = getChild<LLUICtrl>("photo_description");
+ mLocationCheckbox = getChild<LLUICtrl>("add_location_cb");
+ mTagsTextBox = getChild<LLUICtrl>("photo_tags");
+ mTagsTextBox->setValue(DEFAULT_TAG_TEXT);
+ mRatingComboBox = getChild<LLUICtrl>("rating_combobox");
+ mPostButton = getChild<LLUICtrl>("post_photo_btn");
+ mCancelButton = getChild<LLUICtrl>("cancel_photo_btn");
+
+ return LLPanel::postBuild();
+}
+
+void LLFlickrPhotoPanel::draw()
+{
+ LLSnapshotLivePreview * previewp = static_cast<LLSnapshotLivePreview *>(mPreviewHandle.get());
+
+ // Enable interaction only if no transaction with the service is on-going (prevent duplicated posts)
+ bool no_ongoing_connection = !(LLFlickrConnect::instance().isTransactionOngoing());
+ mCancelButton->setEnabled(no_ongoing_connection);
+ mTitleTextBox->setEnabled(no_ongoing_connection);
+ mDescriptionTextBox->setEnabled(no_ongoing_connection);
+ mTagsTextBox->setEnabled(no_ongoing_connection);
+ mRatingComboBox->setEnabled(no_ongoing_connection);
+ mResolutionComboBox->setEnabled(no_ongoing_connection);
+ mRefreshBtn->setEnabled(no_ongoing_connection);
+ mLocationCheckbox->setEnabled(no_ongoing_connection);
+
+ // Display the preview if one is available
+ if (previewp && previewp->getThumbnailImage())
+ {
+ const LLRect& thumbnail_rect = mThumbnailPlaceholder->getRect();
+ const S32 thumbnail_w = previewp->getThumbnailWidth();
+ const S32 thumbnail_h = previewp->getThumbnailHeight();
+
+ // calc preview offset within the preview rect
+ const S32 local_offset_x = (thumbnail_rect.getWidth() - thumbnail_w) / 2 ;
+ const S32 local_offset_y = (thumbnail_rect.getHeight() - thumbnail_h) / 2 ;
+
+ // calc preview offset within the floater rect
+ // Hack : To get the full offset, we need to take into account each and every offset of each widgets up to the floater.
+ // This is almost as arbitrary as using a fixed offset so that's what we do here for the sake of simplicity.
+ // *TODO : Get the offset looking through the hierarchy of widgets, should be done in postBuild() so to avoid traversing the hierarchy each time.
+ S32 offset_x = thumbnail_rect.mLeft + local_offset_x - 1;
+ S32 offset_y = thumbnail_rect.mBottom + local_offset_y - 39;
+
+ mSnapshotPanel->localPointToOtherView(offset_x, offset_y, &offset_x, &offset_y, getParentByType<LLFloater>());
+
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+ // Apply floater transparency to the texture unless the floater is focused.
+ F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency();
+ LLColor4 color = LLColor4::white;
+ gl_draw_scaled_image(offset_x, offset_y,
+ thumbnail_w, thumbnail_h,
+ previewp->getThumbnailImage(), color % alpha);
+
+ previewp->drawPreviewRect(offset_x, offset_y) ;
+ }
+
+ // Update the visibility of the working (computing preview) label
+ mWorkingLabel->setVisible(!(previewp && previewp->getSnapshotUpToDate()));
+
+ // Enable Post if we have a preview to send and no on going connection being processed
+ mPostButton->setEnabled(no_ongoing_connection && (previewp && previewp->getSnapshotUpToDate()) && (mRatingComboBox && mRatingComboBox->getValue().isDefined()));
+
+ // Draw the rest of the panel on top of it
+ LLPanel::draw();
+}
+
+LLSnapshotLivePreview* LLFlickrPhotoPanel::getPreviewView()
+{
+ LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)mPreviewHandle.get();
+ return previewp;
+}
+
+void LLFlickrPhotoPanel::onVisibilityChange(const LLSD& new_visibility)
+{
+ bool visible = new_visibility.asBoolean();
+ if (visible)
+ {
+ if (mPreviewHandle.get())
+ {
+ LLSnapshotLivePreview* preview = getPreviewView();
+ if(preview)
+ {
+ lldebugs << "opened, updating snapshot" << llendl;
+ preview->updateSnapshot(TRUE);
+ }
+ }
+ else
+ {
+ LLRect full_screen_rect = getRootView()->getRect();
+ LLSnapshotLivePreview::Params p;
+ p.rect(full_screen_rect);
+ LLSnapshotLivePreview* previewp = new LLSnapshotLivePreview(p);
+ mPreviewHandle = previewp->getHandle();
+
+ previewp->setSnapshotType(previewp->SNAPSHOT_WEB);
+ previewp->setSnapshotFormat(LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG);
+ //previewp->setSnapshotQuality(98);
+ previewp->setThumbnailPlaceholderRect(mThumbnailPlaceholder->getRect());
+
+ updateControls();
+ }
+ }
+}
+
+void LLFlickrPhotoPanel::onClickNewSnapshot()
+{
+ LLSnapshotLivePreview* previewp = getPreviewView();
+ if (previewp)
+ {
+ //setStatus(Impl::STATUS_READY);
+ lldebugs << "updating snapshot" << llendl;
+ previewp->updateSnapshot(TRUE);
+ }
+}
+
+void LLFlickrPhotoPanel::onSend()
+{
+ LLEventPumps::instance().obtain("FlickrConnectState").stopListening("LLFlickrPhotoPanel"); // just in case it is already listening
+ LLEventPumps::instance().obtain("FlickrConnectState").listen("LLFlickrPhotoPanel", boost::bind(&LLFlickrPhotoPanel::onFlickrConnectStateChange, this, _1));
+
+ // Connect to Flickr if necessary and then post
+ if (LLFlickrConnect::instance().isConnected())
+ {
+ sendPhoto();
+ }
+ else
+ {
+ LLFlickrConnect::instance().checkConnectionToFlickr(true);
+ }
+}
+
+bool LLFlickrPhotoPanel::onFlickrConnectStateChange(const LLSD& data)
+{
+ switch (data.get("enum").asInteger())
+ {
+ case LLFlickrConnect::FLICKR_CONNECTED:
+ sendPhoto();
+ break;
+
+ case LLFlickrConnect::FLICKR_POSTED:
+ LLEventPumps::instance().obtain("FlickrConnectState").stopListening("LLFlickrPhotoPanel");
+ clearAndClose();
+ break;
+ }
+
+ return false;
+}
+
+void LLFlickrPhotoPanel::sendPhoto()
+{
+ // Get the title, description, and tags
+ std::string title = mTitleTextBox->getValue().asString();
+ std::string description = mDescriptionTextBox->getValue().asString();
+ std::string tags = mTagsTextBox->getValue().asString();
+
+ // Add the location if required
+ bool add_location = mLocationCheckbox->getValue().asBoolean();
+ if (add_location)
+ {
+ // Get the SLURL for the location
+ LLSLURL slurl;
+ LLAgentUI::buildSLURL(slurl);
+ std::string slurl_string = slurl.getSLURLString();
+
+ // Add query parameters so Google Analytics can track incoming clicks!
+ slurl_string += DEFAULT_PHOTO_QUERY_PARAMETERS;
+
+ slurl_string = "<a href=\"" + slurl_string + "\">" + DEFAULT_PHOTO_LINK_TEXT + "</a>";
+
+ // Add it to the description (pretty crude, but we don't have a better option with photos)
+ if (description.empty())
+ description = slurl_string;
+ else
+ description = description + "\n\n" + slurl_string;
+ }
+
+ // Get the content rating
+ int content_rating = mRatingComboBox->getValue().asInteger();
+
+ // Get the image
+ LLSnapshotLivePreview* previewp = getPreviewView();
+
+ // Post to Flickr
+ LLFlickrConnect::instance().uploadPhoto(previewp->getFormattedImage(), title, description, tags, content_rating);
+
+ updateControls();
+}
+
+void LLFlickrPhotoPanel::clearAndClose()
+{
+ mTitleTextBox->setValue("");
+ mDescriptionTextBox->setValue("");
+ mTagsTextBox->setValue(DEFAULT_TAG_TEXT);
+
+ LLFloater* floater = getParentByType<LLFloater>();
+ if (floater)
+ {
+ floater->closeFloater();
+ }
+}
+
+void LLFlickrPhotoPanel::updateControls()
+{
+ LLSnapshotLivePreview* previewp = getPreviewView();
+ BOOL got_bytes = previewp && previewp->getDataSize() > 0;
+ BOOL got_snap = previewp && previewp->getSnapshotUpToDate();
+ LLSnapshotLivePreview::ESnapshotType shot_type = (previewp ? previewp->getSnapshotType() : LLSnapshotLivePreview::SNAPSHOT_POSTCARD);
+
+ // *TODO: Separate maximum size for Web images from postcards
+ lldebugs << "Is snapshot up-to-date? " << got_snap << llendl;
+
+ LLLocale locale(LLLocale::USER_LOCALE);
+ std::string bytes_string;
+ if (got_snap)
+ {
+ LLResMgr::getInstance()->getIntegerString(bytes_string, (previewp->getDataSize()) >> 10 );
+ }
+
+ //getChild<LLUICtrl>("file_size_label")->setTextArg("[SIZE]", got_snap ? bytes_string : getString("unknown")); <---uses localized string
+ getChild<LLUICtrl>("file_size_label")->setTextArg("[SIZE]", got_snap ? bytes_string : "unknown");
+ getChild<LLUICtrl>("file_size_label")->setColor(
+ shot_type == LLSnapshotLivePreview::SNAPSHOT_POSTCARD
+ && got_bytes
+ && previewp->getDataSize() > MAX_POSTCARD_DATASIZE ? LLUIColor(LLColor4::red) : LLUIColorTable::instance().getColor( "LabelTextColor" ));
+
+ updateResolution(FALSE);
+}
+
+void LLFlickrPhotoPanel::updateResolution(BOOL do_update)
+{
+ LLComboBox* combobox = static_cast<LLComboBox *>(mResolutionComboBox);
+
+ std::string sdstring = combobox->getSelectedValue();
+ LLSD sdres;
+ std::stringstream sstream(sdstring);
+ LLSDSerialize::fromNotation(sdres, sstream, sdstring.size());
+
+ S32 width = sdres[0];
+ S32 height = sdres[1];
+
+ LLSnapshotLivePreview * previewp = static_cast<LLSnapshotLivePreview *>(mPreviewHandle.get());
+ if (previewp && combobox->getCurrentIndex() >= 0)
+ {
+ S32 original_width = 0 , original_height = 0 ;
+ previewp->getSize(original_width, original_height) ;
+
+ if (width == 0 || height == 0)
+ {
+ // take resolution from current window size
+ lldebugs << "Setting preview res from window: " << gViewerWindow->getWindowWidthRaw() << "x" << gViewerWindow->getWindowHeightRaw() << llendl;
+ previewp->setSize(gViewerWindow->getWindowWidthRaw(), gViewerWindow->getWindowHeightRaw());
+ }
+ else
+ {
+ // use the resolution from the selected pre-canned drop-down choice
+ lldebugs << "Setting preview res selected from combo: " << width << "x" << height << llendl;
+ previewp->setSize(width, height);
+ }
+
+ checkAspectRatio(width);
+
+ previewp->getSize(width, height);
+
+ if(original_width != width || original_height != height)
+ {
+ previewp->setSize(width, height);
+
+ // hide old preview as the aspect ratio could be wrong
+ lldebugs << "updating thumbnail" << llendl;
+
+ previewp->updateSnapshot(FALSE, TRUE);
+ if(do_update)
+ {
+ lldebugs << "Will update controls" << llendl;
+ updateControls();
+ LLFlickrPhotoPanel::onClickNewSnapshot();
+ }
+ }
+
+ }
+}
+
+void LLFlickrPhotoPanel::checkAspectRatio(S32 index)
+{
+ LLSnapshotLivePreview *previewp = getPreviewView() ;
+
+ BOOL keep_aspect = FALSE;
+
+ if (0 == index) // current window size
+ {
+ keep_aspect = TRUE;
+ }
+ else // predefined resolution
+ {
+ keep_aspect = FALSE;
+ }
+
+ if (previewp)
+ {
+ previewp->mKeepAspectRatio = keep_aspect;
+ }
+}
+
+LLUICtrl* LLFlickrPhotoPanel::getRefreshBtn()
+{
+ return mRefreshBtn;
+}
+
+///////////////////////////
+//LLFlickrAccountPanel//////
+///////////////////////////
+
+LLFlickrAccountPanel::LLFlickrAccountPanel() :
+mAccountCaptionLabel(NULL),
+mAccountNameLabel(NULL),
+mPanelButtons(NULL),
+mConnectButton(NULL),
+mDisconnectButton(NULL)
+{
+ mCommitCallbackRegistrar.add("SocialSharing.Connect", boost::bind(&LLFlickrAccountPanel::onConnect, this));
+ mCommitCallbackRegistrar.add("SocialSharing.Disconnect", boost::bind(&LLFlickrAccountPanel::onDisconnect, this));
+
+ setVisibleCallback(boost::bind(&LLFlickrAccountPanel::onVisibilityChange, this, _2));
+}
+
+BOOL LLFlickrAccountPanel::postBuild()
+{
+ mAccountCaptionLabel = getChild<LLTextBox>("account_caption_label");
+ mAccountNameLabel = getChild<LLTextBox>("account_name_label");
+ mPanelButtons = getChild<LLUICtrl>("panel_buttons");
+ mConnectButton = getChild<LLUICtrl>("connect_btn");
+ mDisconnectButton = getChild<LLUICtrl>("disconnect_btn");
+
+ return LLPanel::postBuild();
+}
+
+void LLFlickrAccountPanel::draw()
+{
+ LLFlickrConnect::EConnectionState connection_state = LLFlickrConnect::instance().getConnectionState();
+
+ //Disable the 'disconnect' button and the 'use another account' button when disconnecting in progress
+ bool disconnecting = connection_state == LLFlickrConnect::FLICKR_DISCONNECTING;
+ mDisconnectButton->setEnabled(!disconnecting);
+
+ //Disable the 'connect' button when a connection is in progress
+ bool connecting = connection_state == LLFlickrConnect::FLICKR_CONNECTION_IN_PROGRESS;
+ mConnectButton->setEnabled(!connecting);
+
+ LLPanel::draw();
+}
+
+void LLFlickrAccountPanel::onVisibilityChange(const LLSD& new_visibility)
+{
+ bool visible = new_visibility.asBoolean();
+
+ if(visible)
+ {
+ LLEventPumps::instance().obtain("FlickrConnectState").stopListening("LLFlickrAccountPanel");
+ LLEventPumps::instance().obtain("FlickrConnectState").listen("LLFlickrAccountPanel", boost::bind(&LLFlickrAccountPanel::onFlickrConnectStateChange, this, _1));
+
+ LLEventPumps::instance().obtain("FlickrConnectInfo").stopListening("LLFlickrAccountPanel");
+ LLEventPumps::instance().obtain("FlickrConnectInfo").listen("LLFlickrAccountPanel", boost::bind(&LLFlickrAccountPanel::onFlickrConnectInfoChange, this));
+
+ //Connected
+ if(LLFlickrConnect::instance().isConnected())
+ {
+ showConnectedLayout();
+ }
+ //Check if connected (show disconnected layout in meantime)
+ else
+ {
+ showDisconnectedLayout();
+ }
+ if ((LLFlickrConnect::instance().getConnectionState() == LLFlickrConnect::FLICKR_NOT_CONNECTED) ||
+ (LLFlickrConnect::instance().getConnectionState() == LLFlickrConnect::FLICKR_CONNECTION_FAILED))
+ {
+ LLFlickrConnect::instance().checkConnectionToFlickr();
+ }
+ }
+ else
+ {
+ LLEventPumps::instance().obtain("FlickrConnectState").stopListening("LLFlickrAccountPanel");
+ LLEventPumps::instance().obtain("FlickrConnectInfo").stopListening("LLFlickrAccountPanel");
+ }
+}
+
+bool LLFlickrAccountPanel::onFlickrConnectStateChange(const LLSD& data)
+{
+ if(LLFlickrConnect::instance().isConnected())
+ {
+ //In process of disconnecting so leave the layout as is
+ if(data.get("enum").asInteger() != LLFlickrConnect::FLICKR_DISCONNECTING)
+ {
+ showConnectedLayout();
+ }
+ }
+ else
+ {
+ showDisconnectedLayout();
+ }
+
+ return false;
+}
+
+bool LLFlickrAccountPanel::onFlickrConnectInfoChange()
+{
+ LLSD info = LLFlickrConnect::instance().getInfo();
+ std::string clickable_name;
+
+ //Strings of format [http://www.somewebsite.com Click Me] become clickable text
+ if(info.has("link") && info.has("name"))
+ {
+ clickable_name = "[" + info["link"].asString() + " " + info["name"].asString() + "]";
+ }
+
+ mAccountNameLabel->setText(clickable_name);
+
+ return false;
+}
+
+void LLFlickrAccountPanel::showConnectButton()
+{
+ if(!mConnectButton->getVisible())
+ {
+ mConnectButton->setVisible(TRUE);
+ mDisconnectButton->setVisible(FALSE);
+ }
+}
+
+void LLFlickrAccountPanel::hideConnectButton()
+{
+ if(mConnectButton->getVisible())
+ {
+ mConnectButton->setVisible(FALSE);
+ mDisconnectButton->setVisible(TRUE);
+ }
+}
+
+void LLFlickrAccountPanel::showDisconnectedLayout()
+{
+ mAccountCaptionLabel->setText(getString("flickr_disconnected"));
+ mAccountNameLabel->setText(std::string(""));
+ showConnectButton();
+}
+
+void LLFlickrAccountPanel::showConnectedLayout()
+{
+ LLFlickrConnect::instance().loadFlickrInfo();
+
+ mAccountCaptionLabel->setText(getString("flickr_connected"));
+ hideConnectButton();
+}
+
+void LLFlickrAccountPanel::onConnect()
+{
+ LLFlickrConnect::instance().checkConnectionToFlickr(true);
+
+ //Clear only the flickr browser cookies so that the flickr login screen appears
+ LLViewerMedia::getCookieStore()->removeCookiesByDomain(".flickr.com");
+}
+
+void LLFlickrAccountPanel::onDisconnect()
+{
+ LLFlickrConnect::instance().disconnectFromFlickr();
+
+ LLViewerMedia::getCookieStore()->removeCookiesByDomain(".flickr.com");
+}
+
+////////////////////////
+//LLFloaterFlickr///////
+////////////////////////
+
+LLFloaterFlickr::LLFloaterFlickr(const LLSD& key) : LLFloater(key),
+ mSocialPhotoPanel(NULL),
+ mStatusErrorText(NULL),
+ mStatusLoadingText(NULL),
+ mStatusLoadingIndicator(NULL)
+{
+ mCommitCallbackRegistrar.add("SocialSharing.Cancel", boost::bind(&LLFloaterFlickr::onCancel, this));
+}
+
+void LLFloaterFlickr::onCancel()
+{
+ closeFloater();
+}
+
+BOOL LLFloaterFlickr::postBuild()
+{
+ // Keep tab of the Photo Panel
+ mSocialPhotoPanel = static_cast<LLFlickrPhotoPanel*>(getChild<LLUICtrl>("panel_flickr_photo"));
+ // Connection status widgets
+ mStatusErrorText = getChild<LLTextBox>("connection_error_text");
+ mStatusLoadingText = getChild<LLTextBox>("connection_loading_text");
+ mStatusLoadingIndicator = getChild<LLUICtrl>("connection_loading_indicator");
+ return LLFloater::postBuild();
+}
+
+void LLFloaterFlickr::showPhotoPanel()
+{
+ LLTabContainer* parent = dynamic_cast<LLTabContainer*>(mSocialPhotoPanel->getParent());
+ if (!parent)
+ {
+ llwarns << "Cannot find panel container" << llendl;
+ return;
+ }
+
+ parent->selectTabPanel(mSocialPhotoPanel);
+}
+
+// static
+void LLFloaterFlickr::preUpdate()
+{
+ LLFloaterFlickr* instance = LLFloaterReg::findTypedInstance<LLFloaterFlickr>("flickr");
+ if (instance)
+ {
+ //Will set file size text to 'unknown'
+ instance->mSocialPhotoPanel->updateControls();
+ }
+}
+
+// static
+void LLFloaterFlickr::postUpdate()
+{
+ LLFloaterFlickr* instance = LLFloaterReg::findTypedInstance<LLFloaterFlickr>("flickr");
+ if (instance)
+ {
+ //Will set the file size text
+ instance->mSocialPhotoPanel->updateControls();
+
+ // The refresh button is initially hidden. We show it after the first update,
+ // i.e. after snapshot is taken
+ LLUICtrl * refresh_button = instance->mSocialPhotoPanel->getRefreshBtn();
+
+ if (!refresh_button->getVisible())
+ {
+ refresh_button->setVisible(true);
+ }
+
+ }
+}
+
+void LLFloaterFlickr::draw()
+{
+ if (mStatusErrorText && mStatusLoadingText && mStatusLoadingIndicator)
+ {
+ mStatusErrorText->setVisible(false);
+ mStatusLoadingText->setVisible(false);
+ mStatusLoadingIndicator->setVisible(false);
+ LLFlickrConnect::EConnectionState connection_state = LLFlickrConnect::instance().getConnectionState();
+ std::string status_text;
+
+ switch (connection_state)
+ {
+ case LLFlickrConnect::FLICKR_NOT_CONNECTED:
+ // No status displayed when first opening the panel and no connection done
+ case LLFlickrConnect::FLICKR_CONNECTED:
+ // When successfully connected, no message is displayed
+ case LLFlickrConnect::FLICKR_POSTED:
+ // No success message to show since we actually close the floater after successful posting completion
+ break;
+ case LLFlickrConnect::FLICKR_CONNECTION_IN_PROGRESS:
+ // Connection loading indicator
+ mStatusLoadingText->setVisible(true);
+ status_text = LLTrans::getString("SocialFlickrConnecting");
+ mStatusLoadingText->setValue(status_text);
+ mStatusLoadingIndicator->setVisible(true);
+ break;
+ case LLFlickrConnect::FLICKR_POSTING:
+ // Posting indicator
+ mStatusLoadingText->setVisible(true);
+ status_text = LLTrans::getString("SocialFlickrPosting");
+ mStatusLoadingText->setValue(status_text);
+ mStatusLoadingIndicator->setVisible(true);
+ break;
+ case LLFlickrConnect::FLICKR_CONNECTION_FAILED:
+ // Error connecting to the service
+ mStatusErrorText->setVisible(true);
+ status_text = LLTrans::getString("SocialFlickrErrorConnecting");
+ mStatusErrorText->setValue(status_text);
+ break;
+ case LLFlickrConnect::FLICKR_POST_FAILED:
+ // Error posting to the service
+ mStatusErrorText->setVisible(true);
+ status_text = LLTrans::getString("SocialFlickrErrorPosting");
+ mStatusErrorText->setValue(status_text);
+ break;
+ case LLFlickrConnect::FLICKR_DISCONNECTING:
+ // Disconnecting loading indicator
+ mStatusLoadingText->setVisible(true);
+ status_text = LLTrans::getString("SocialFlickrDisconnecting");
+ mStatusLoadingText->setValue(status_text);
+ mStatusLoadingIndicator->setVisible(true);
+ break;
+ case LLFlickrConnect::FLICKR_DISCONNECT_FAILED:
+ // Error disconnecting from the service
+ mStatusErrorText->setVisible(true);
+ status_text = LLTrans::getString("SocialFlickrErrorDisconnecting");
+ mStatusErrorText->setValue(status_text);
+ break;
+ }
+ }
+ LLFloater::draw();
+}
+
diff --git a/indra/newview/llfloaterflickr.h b/indra/newview/llfloaterflickr.h
new file mode 100644
index 0000000000..e9005444d8
--- /dev/null
+++ b/indra/newview/llfloaterflickr.h
@@ -0,0 +1,127 @@
+/**
+* @file llfloaterflickr.h
+* @brief Header file for llfloaterflickr
+* @author cho@lindenlab.com
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2013, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+#ifndef LL_LLFLOATERFLICKR_H
+#define LL_LLFLOATERFLICKR_H
+
+#include "llfloater.h"
+#include "lltextbox.h"
+#include "llviewertexture.h"
+
+class LLIconCtrl;
+class LLCheckBoxCtrl;
+class LLSnapshotLivePreview;
+
+class LLFlickrPhotoPanel : public LLPanel
+{
+public:
+ LLFlickrPhotoPanel();
+ ~LLFlickrPhotoPanel();
+
+ BOOL postBuild();
+ void draw();
+
+ LLSnapshotLivePreview* getPreviewView();
+ void onVisibilityChange(const LLSD& new_visibility);
+ void onClickNewSnapshot();
+ void onSend();
+ bool onFlickrConnectStateChange(const LLSD& data);
+
+ void sendPhoto();
+ void clearAndClose();
+
+ void updateControls();
+ void updateResolution(BOOL do_update);
+ void checkAspectRatio(S32 index);
+ LLUICtrl* getRefreshBtn();
+
+private:
+ LLHandle<LLView> mPreviewHandle;
+
+ LLUICtrl * mSnapshotPanel;
+ LLUICtrl * mResolutionComboBox;
+ LLUICtrl * mRefreshBtn;
+ LLUICtrl * mWorkingLabel;
+ LLUICtrl * mThumbnailPlaceholder;
+ LLUICtrl * mTitleTextBox;
+ LLUICtrl * mDescriptionTextBox;
+ LLUICtrl * mLocationCheckbox;
+ LLUICtrl * mTagsTextBox;
+ LLUICtrl * mRatingComboBox;
+ LLUICtrl * mPostButton;
+ LLUICtrl* mCancelButton;
+};
+
+class LLFlickrAccountPanel : public LLPanel
+{
+public:
+ LLFlickrAccountPanel();
+ BOOL postBuild();
+ void draw();
+
+private:
+ void onVisibilityChange(const LLSD& new_visibility);
+ bool onFlickrConnectStateChange(const LLSD& data);
+ bool onFlickrConnectInfoChange();
+ void onConnect();
+ void onUseAnotherAccount();
+ void onDisconnect();
+
+ void showConnectButton();
+ void hideConnectButton();
+ void showDisconnectedLayout();
+ void showConnectedLayout();
+
+ LLTextBox * mAccountCaptionLabel;
+ LLTextBox * mAccountNameLabel;
+ LLUICtrl * mPanelButtons;
+ LLUICtrl * mConnectButton;
+ LLUICtrl * mDisconnectButton;
+};
+
+
+class LLFloaterFlickr : public LLFloater
+{
+public:
+ LLFloaterFlickr(const LLSD& key);
+ BOOL postBuild();
+ void draw();
+ void onCancel();
+
+ void showPhotoPanel();
+
+ static void preUpdate();
+ static void postUpdate();
+
+private:
+ LLFlickrPhotoPanel* mSocialPhotoPanel;
+ LLTextBox* mStatusErrorText;
+ LLTextBox* mStatusLoadingText;
+ LLUICtrl* mStatusLoadingIndicator;
+};
+
+#endif // LL_LLFLOATERFLICKR_H
+
diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp
index ea385d7baf..4701e128d3 100755
--- a/indra/newview/llfloatersnapshot.cpp
+++ b/indra/newview/llfloatersnapshot.cpp
@@ -32,6 +32,8 @@
#include "llfacebookconnect.h"
#include "llfloaterreg.h"
#include "llfloatersocial.h"
+#include "llfloaterflickr.h"
+#include "llfloatertwitter.h"
#include "llcheckboxctrl.h"
#include "llcombobox.h"
#include "llpostcard.h"
@@ -1264,9 +1266,11 @@ S32 LLFloaterSnapshot::notify(const LLSD& info)
void LLFloaterSnapshot::update()
{
LLFloaterSnapshot* inst = LLFloaterReg::findTypedInstance<LLFloaterSnapshot>("snapshot");
- LLFloaterSocial* floater_social = LLFloaterReg::findTypedInstance<LLFloaterSocial>("social");
+ LLFloaterSocial* floater_social = LLFloaterReg::findTypedInstance<LLFloaterSocial>("social");
+ LLFloaterFlickr* floater_flickr = LLFloaterReg::findTypedInstance<LLFloaterFlickr>("flickr");
+ LLFloaterTwitter* floater_twitter = LLFloaterReg::findTypedInstance<LLFloaterTwitter>("twitter");
- if (!inst && !floater_social)
+ if (!inst && !floater_social && !floater_flickr && !floater_twitter)
return;
BOOL changed = FALSE;
diff --git a/indra/newview/llfloatersocial.cpp b/indra/newview/llfloatersocial.cpp
index 2a74c8e3ea..31404da7d3 100644
--- a/indra/newview/llfloatersocial.cpp
+++ b/indra/newview/llfloatersocial.cpp
@@ -46,6 +46,7 @@
#include "llviewerregion.h"
#include "llviewercontrol.h"
#include "llviewermedia.h"
+#include "lltabcontainer.h"
static LLRegisterPanelClassWrapper<LLSocialStatusPanel> t_panel_status("llsocialstatuspanel");
static LLRegisterPanelClassWrapper<LLSocialPhotoPanel> t_panel_photo("llsocialphotopanel");
@@ -823,6 +824,18 @@ BOOL LLFloaterSocial::postBuild()
return LLFloater::postBuild();
}
+void LLFloaterSocial::showPhotoPanel()
+{
+ LLTabContainer* parent = dynamic_cast<LLTabContainer*>(mSocialPhotoPanel->getParent());
+ if (!parent)
+ {
+ llwarns << "Cannot find panel container" << llendl;
+ return;
+ }
+
+ parent->selectTabPanel(mSocialPhotoPanel);
+}
+
// static
void LLFloaterSocial::preUpdate()
{
diff --git a/indra/newview/llfloatersocial.h b/indra/newview/llfloatersocial.h
index bbe07c9704..041ae8a268 100644
--- a/indra/newview/llfloatersocial.h
+++ b/indra/newview/llfloatersocial.h
@@ -150,6 +150,8 @@ public:
BOOL postBuild();
void draw();
void onCancel();
+
+ void showPhotoPanel();
static void preUpdate();
static void postUpdate();
diff --git a/indra/newview/llfloatertwitter.cpp b/indra/newview/llfloatertwitter.cpp
new file mode 100644
index 0000000000..06d0fb5542
--- /dev/null
+++ b/indra/newview/llfloatertwitter.cpp
@@ -0,0 +1,697 @@
+/**
+* @file llfloatertwitter.cpp
+* @brief Implementation of llfloatertwitter
+* @author cho@lindenlab.com
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2013, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatertwitter.h"
+
+#include "llagent.h"
+#include "llagentui.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "lltwitterconnect.h"
+#include "llfloaterreg.h"
+#include "lliconctrl.h"
+#include "llresmgr.h" // LLLocale
+#include "llsdserialize.h"
+#include "llloadingindicator.h"
+#include "llplugincookiestore.h"
+#include "llslurl.h"
+#include "lltrans.h"
+#include "llsnapshotlivepreview.h"
+#include "llviewerregion.h"
+#include "llviewercontrol.h"
+#include "llviewermedia.h"
+#include "lltabcontainer.h"
+#include "lltexteditor.h"
+
+static LLRegisterPanelClassWrapper<LLTwitterPhotoPanel> t_panel_photo("lltwitterphotopanel");
+static LLRegisterPanelClassWrapper<LLTwitterAccountPanel> t_panel_account("lltwitteraccountpanel");
+
+const S32 MAX_POSTCARD_DATASIZE = 1024 * 1024; // one megabyte
+const std::string DEFAULT_PHOTO_QUERY_PARAMETERS = "?sourceid=slshare_photo&utm_source=twitter&utm_medium=photo&utm_campaign=slshare";
+
+///////////////////////////
+//LLTwitterPhotoPanel///////
+///////////////////////////
+
+LLTwitterPhotoPanel::LLTwitterPhotoPanel() :
+mSnapshotPanel(NULL),
+mResolutionComboBox(NULL),
+mRefreshBtn(NULL),
+mWorkingLabel(NULL),
+mThumbnailPlaceholder(NULL),
+mStatusTextBox(NULL),
+mPhotoCheckbox(NULL),
+mPostButton(NULL)
+{
+ mCommitCallbackRegistrar.add("SocialSharing.SendPhoto", boost::bind(&LLTwitterPhotoPanel::onSend, this));
+ mCommitCallbackRegistrar.add("SocialSharing.RefreshPhoto", boost::bind(&LLTwitterPhotoPanel::onClickNewSnapshot, this));
+}
+
+LLTwitterPhotoPanel::~LLTwitterPhotoPanel()
+{
+ if(mPreviewHandle.get())
+ {
+ mPreviewHandle.get()->die();
+ }
+}
+
+BOOL LLTwitterPhotoPanel::postBuild()
+{
+ setVisibleCallback(boost::bind(&LLTwitterPhotoPanel::onVisibilityChange, this, _2));
+
+ mSnapshotPanel = getChild<LLUICtrl>("snapshot_panel");
+ mResolutionComboBox = getChild<LLUICtrl>("resolution_combobox");
+ mResolutionComboBox->setCommitCallback(boost::bind(&LLTwitterPhotoPanel::updateResolution, this, TRUE));
+ mRefreshBtn = getChild<LLUICtrl>("new_snapshot_btn");
+ mWorkingLabel = getChild<LLUICtrl>("working_lbl");
+ mThumbnailPlaceholder = getChild<LLUICtrl>("thumbnail_placeholder");
+ mStatusTextBox = getChild<LLUICtrl>("photo_status");
+ mPhotoCheckbox = getChild<LLUICtrl>("add_photo_cb");
+ mPostButton = getChild<LLUICtrl>("post_photo_btn");
+ mCancelButton = getChild<LLUICtrl>("cancel_photo_btn");
+
+ return LLPanel::postBuild();
+}
+
+void LLTwitterPhotoPanel::draw()
+{
+ LLSnapshotLivePreview * previewp = static_cast<LLSnapshotLivePreview *>(mPreviewHandle.get());
+
+ // Enable interaction only if no transaction with the service is on-going (prevent duplicated posts)
+ bool no_ongoing_connection = !(LLTwitterConnect::instance().isTransactionOngoing());
+ mCancelButton->setEnabled(no_ongoing_connection);
+ mStatusTextBox->setEnabled(no_ongoing_connection);
+ mResolutionComboBox->setEnabled(no_ongoing_connection && mPhotoCheckbox->getValue().asBoolean());
+ mRefreshBtn->setEnabled(no_ongoing_connection && mPhotoCheckbox->getValue().asBoolean());
+ mPhotoCheckbox->setEnabled(no_ongoing_connection);
+
+ bool add_photo = mPhotoCheckbox->getValue().asBoolean();
+
+ // Restrict the status text length to Twitter's character limit
+ LLTextEditor* status_text_box = dynamic_cast<LLTextEditor*>(mStatusTextBox);
+ if (status_text_box)
+ {
+ int max_status_length = add_photo ? 100 : 140;
+ status_text_box->setMaxTextLength(max_status_length);
+ if (!add_photo)
+ {
+ if (mOldStatusText.length() > status_text_box->getText().length() && status_text_box->getText() == mOldStatusText.substr(0, status_text_box->getText().length()))
+ {
+ status_text_box->setText(mOldStatusText);
+ }
+ mOldStatusText = "";
+ }
+ if (status_text_box->getText().length() > max_status_length)
+ {
+ mOldStatusText = status_text_box->getText();
+ status_text_box->setText(mOldStatusText.substr(0, max_status_length));
+ }
+ }
+
+ // Display the preview if one is available
+ if (previewp && previewp->getThumbnailImage())
+ {
+ const LLRect& thumbnail_rect = mThumbnailPlaceholder->getRect();
+ const S32 thumbnail_w = previewp->getThumbnailWidth();
+ const S32 thumbnail_h = previewp->getThumbnailHeight();
+
+ // calc preview offset within the preview rect
+ const S32 local_offset_x = (thumbnail_rect.getWidth() - thumbnail_w) / 2 ;
+ const S32 local_offset_y = (thumbnail_rect.getHeight() - thumbnail_h) / 2 ;
+
+ // calc preview offset within the floater rect
+ // Hack : To get the full offset, we need to take into account each and every offset of each widgets up to the floater.
+ // This is almost as arbitrary as using a fixed offset so that's what we do here for the sake of simplicity.
+ // *TODO : Get the offset looking through the hierarchy of widgets, should be done in postBuild() so to avoid traversing the hierarchy each time.
+ S32 offset_x = thumbnail_rect.mLeft + local_offset_x - 1;
+ S32 offset_y = thumbnail_rect.mBottom + local_offset_y - 39;
+
+ mSnapshotPanel->localPointToOtherView(offset_x, offset_y, &offset_x, &offset_y, getParentByType<LLFloater>());
+
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+ // Apply floater transparency to the texture unless the floater is focused.
+ F32 alpha = (add_photo ? (getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency()) : 0.5f);
+ LLColor4 color = LLColor4::white;
+ gl_draw_scaled_image(offset_x, offset_y,
+ thumbnail_w, thumbnail_h,
+ previewp->getThumbnailImage(), color % alpha);
+
+ previewp->drawPreviewRect(offset_x, offset_y) ;
+ }
+
+ // Update the visibility of the working (computing preview) label
+ mWorkingLabel->setVisible(!(previewp && previewp->getSnapshotUpToDate()));
+
+ // Enable Post if we have a preview to send and no on going connection being processed
+ mPostButton->setEnabled(no_ongoing_connection && ((add_photo && previewp && previewp->getSnapshotUpToDate()) || !mStatusTextBox->getValue().asString().empty()));
+
+ // Draw the rest of the panel on top of it
+ LLPanel::draw();
+}
+
+LLSnapshotLivePreview* LLTwitterPhotoPanel::getPreviewView()
+{
+ LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)mPreviewHandle.get();
+ return previewp;
+}
+
+void LLTwitterPhotoPanel::onVisibilityChange(const LLSD& new_visibility)
+{
+ bool visible = new_visibility.asBoolean();
+ if (visible)
+ {
+ if (mPreviewHandle.get())
+ {
+ LLSnapshotLivePreview* preview = getPreviewView();
+ if(preview)
+ {
+ lldebugs << "opened, updating snapshot" << llendl;
+ preview->updateSnapshot(TRUE);
+ }
+ }
+ else
+ {
+ LLRect full_screen_rect = getRootView()->getRect();
+ LLSnapshotLivePreview::Params p;
+ p.rect(full_screen_rect);
+ LLSnapshotLivePreview* previewp = new LLSnapshotLivePreview(p);
+ mPreviewHandle = previewp->getHandle();
+
+ previewp->setSnapshotType(previewp->SNAPSHOT_WEB);
+ previewp->setSnapshotFormat(LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG);
+ //previewp->setSnapshotQuality(98);
+ previewp->setThumbnailPlaceholderRect(mThumbnailPlaceholder->getRect());
+
+ updateControls();
+ }
+ }
+}
+
+void LLTwitterPhotoPanel::onClickNewSnapshot()
+{
+ LLSnapshotLivePreview* previewp = getPreviewView();
+ if (previewp)
+ {
+ //setStatus(Impl::STATUS_READY);
+ lldebugs << "updating snapshot" << llendl;
+ previewp->updateSnapshot(TRUE);
+ }
+}
+
+void LLTwitterPhotoPanel::onSend()
+{
+ LLEventPumps::instance().obtain("TwitterConnectState").stopListening("LLTwitterPhotoPanel"); // just in case it is already listening
+ LLEventPumps::instance().obtain("TwitterConnectState").listen("LLTwitterPhotoPanel", boost::bind(&LLTwitterPhotoPanel::onTwitterConnectStateChange, this, _1));
+
+ // Connect to Twitter if necessary and then post
+ if (LLTwitterConnect::instance().isConnected())
+ {
+ sendPhoto();
+ }
+ else
+ {
+ LLTwitterConnect::instance().checkConnectionToTwitter(true);
+ }
+}
+
+bool LLTwitterPhotoPanel::onTwitterConnectStateChange(const LLSD& data)
+{
+ switch (data.get("enum").asInteger())
+ {
+ case LLTwitterConnect::TWITTER_CONNECTED:
+ sendPhoto();
+ break;
+
+ case LLTwitterConnect::TWITTER_POSTED:
+ LLEventPumps::instance().obtain("TwitterConnectState").stopListening("LLTwitterPhotoPanel");
+ clearAndClose();
+ break;
+ }
+
+ return false;
+}
+
+void LLTwitterPhotoPanel::sendPhoto()
+{
+ // Get the status text
+ std::string status = mStatusTextBox->getValue().asString();
+
+ // Add the photo if required
+ bool add_photo = mPhotoCheckbox->getValue().asBoolean();
+ if (add_photo)
+ {
+ // Get the image
+ LLSnapshotLivePreview* previewp = getPreviewView();
+
+ // Post to Twitter
+ LLTwitterConnect::instance().uploadPhoto(previewp->getFormattedImage(), status);
+ }
+ else
+ {
+ // Just post the status to Twitter
+ LLTwitterConnect::instance().updateStatus(status);
+ }
+
+ updateControls();
+}
+
+void LLTwitterPhotoPanel::clearAndClose()
+{
+ mStatusTextBox->setValue("");
+
+ LLFloater* floater = getParentByType<LLFloater>();
+ if (floater)
+ {
+ floater->closeFloater();
+ }
+}
+
+void LLTwitterPhotoPanel::updateControls()
+{
+ LLSnapshotLivePreview* previewp = getPreviewView();
+ BOOL got_bytes = previewp && previewp->getDataSize() > 0;
+ BOOL got_snap = previewp && previewp->getSnapshotUpToDate();
+ LLSnapshotLivePreview::ESnapshotType shot_type = (previewp ? previewp->getSnapshotType() : LLSnapshotLivePreview::SNAPSHOT_POSTCARD);
+
+ // *TODO: Separate maximum size for Web images from postcards
+ lldebugs << "Is snapshot up-to-date? " << got_snap << llendl;
+
+ LLLocale locale(LLLocale::USER_LOCALE);
+ std::string bytes_string;
+ if (got_snap)
+ {
+ LLResMgr::getInstance()->getIntegerString(bytes_string, (previewp->getDataSize()) >> 10 );
+ }
+
+ //getChild<LLUICtrl>("file_size_label")->setTextArg("[SIZE]", got_snap ? bytes_string : getString("unknown")); <---uses localized string
+ getChild<LLUICtrl>("file_size_label")->setTextArg("[SIZE]", got_snap ? bytes_string : "unknown");
+ getChild<LLUICtrl>("file_size_label")->setColor(
+ shot_type == LLSnapshotLivePreview::SNAPSHOT_POSTCARD
+ && got_bytes
+ && previewp->getDataSize() > MAX_POSTCARD_DATASIZE ? LLUIColor(LLColor4::red) : LLUIColorTable::instance().getColor( "LabelTextColor" ));
+
+ updateResolution(FALSE);
+}
+
+void LLTwitterPhotoPanel::updateResolution(BOOL do_update)
+{
+ LLComboBox* combobox = static_cast<LLComboBox *>(mResolutionComboBox);
+
+ std::string sdstring = combobox->getSelectedValue();
+ LLSD sdres;
+ std::stringstream sstream(sdstring);
+ LLSDSerialize::fromNotation(sdres, sstream, sdstring.size());
+
+ S32 width = sdres[0];
+ S32 height = sdres[1];
+
+ LLSnapshotLivePreview * previewp = static_cast<LLSnapshotLivePreview *>(mPreviewHandle.get());
+ if (previewp && combobox->getCurrentIndex() >= 0)
+ {
+ S32 original_width = 0 , original_height = 0 ;
+ previewp->getSize(original_width, original_height) ;
+
+ if (width == 0 || height == 0)
+ {
+ // take resolution from current window size
+ lldebugs << "Setting preview res from window: " << gViewerWindow->getWindowWidthRaw() << "x" << gViewerWindow->getWindowHeightRaw() << llendl;
+ previewp->setSize(gViewerWindow->getWindowWidthRaw(), gViewerWindow->getWindowHeightRaw());
+ }
+ else
+ {
+ // use the resolution from the selected pre-canned drop-down choice
+ lldebugs << "Setting preview res selected from combo: " << width << "x" << height << llendl;
+ previewp->setSize(width, height);
+ }
+
+ checkAspectRatio(width);
+
+ previewp->getSize(width, height);
+
+ if(original_width != width || original_height != height)
+ {
+ previewp->setSize(width, height);
+
+ // hide old preview as the aspect ratio could be wrong
+ lldebugs << "updating thumbnail" << llendl;
+
+ previewp->updateSnapshot(FALSE, TRUE);
+ if(do_update)
+ {
+ lldebugs << "Will update controls" << llendl;
+ updateControls();
+ LLTwitterPhotoPanel::onClickNewSnapshot();
+ }
+ }
+
+ }
+}
+
+void LLTwitterPhotoPanel::checkAspectRatio(S32 index)
+{
+ LLSnapshotLivePreview *previewp = getPreviewView() ;
+
+ BOOL keep_aspect = FALSE;
+
+ if (0 == index) // current window size
+ {
+ keep_aspect = TRUE;
+ }
+ else // predefined resolution
+ {
+ keep_aspect = FALSE;
+ }
+
+ if (previewp)
+ {
+ previewp->mKeepAspectRatio = keep_aspect;
+ }
+}
+
+LLUICtrl* LLTwitterPhotoPanel::getRefreshBtn()
+{
+ return mRefreshBtn;
+}
+
+///////////////////////////
+//LLTwitterAccountPanel//////
+///////////////////////////
+
+LLTwitterAccountPanel::LLTwitterAccountPanel() :
+mAccountCaptionLabel(NULL),
+mAccountNameLabel(NULL),
+mPanelButtons(NULL),
+mConnectButton(NULL),
+mDisconnectButton(NULL)
+{
+ mCommitCallbackRegistrar.add("SocialSharing.Connect", boost::bind(&LLTwitterAccountPanel::onConnect, this));
+ mCommitCallbackRegistrar.add("SocialSharing.Disconnect", boost::bind(&LLTwitterAccountPanel::onDisconnect, this));
+
+ setVisibleCallback(boost::bind(&LLTwitterAccountPanel::onVisibilityChange, this, _2));
+}
+
+BOOL LLTwitterAccountPanel::postBuild()
+{
+ mAccountCaptionLabel = getChild<LLTextBox>("account_caption_label");
+ mAccountNameLabel = getChild<LLTextBox>("account_name_label");
+ mPanelButtons = getChild<LLUICtrl>("panel_buttons");
+ mConnectButton = getChild<LLUICtrl>("connect_btn");
+ mDisconnectButton = getChild<LLUICtrl>("disconnect_btn");
+
+ return LLPanel::postBuild();
+}
+
+void LLTwitterAccountPanel::draw()
+{
+ LLTwitterConnect::EConnectionState connection_state = LLTwitterConnect::instance().getConnectionState();
+
+ //Disable the 'disconnect' button and the 'use another account' button when disconnecting in progress
+ bool disconnecting = connection_state == LLTwitterConnect::TWITTER_DISCONNECTING;
+ mDisconnectButton->setEnabled(!disconnecting);
+
+ //Disable the 'connect' button when a connection is in progress
+ bool connecting = connection_state == LLTwitterConnect::TWITTER_CONNECTION_IN_PROGRESS;
+ mConnectButton->setEnabled(!connecting);
+
+ LLPanel::draw();
+}
+
+void LLTwitterAccountPanel::onVisibilityChange(const LLSD& new_visibility)
+{
+ bool visible = new_visibility.asBoolean();
+
+ if(visible)
+ {
+ LLEventPumps::instance().obtain("TwitterConnectState").stopListening("LLTwitterAccountPanel");
+ LLEventPumps::instance().obtain("TwitterConnectState").listen("LLTwitterAccountPanel", boost::bind(&LLTwitterAccountPanel::onTwitterConnectStateChange, this, _1));
+
+ LLEventPumps::instance().obtain("TwitterConnectInfo").stopListening("LLTwitterAccountPanel");
+ LLEventPumps::instance().obtain("TwitterConnectInfo").listen("LLTwitterAccountPanel", boost::bind(&LLTwitterAccountPanel::onTwitterConnectInfoChange, this));
+
+ //Connected
+ if(LLTwitterConnect::instance().isConnected())
+ {
+ showConnectedLayout();
+ }
+ //Check if connected (show disconnected layout in meantime)
+ else
+ {
+ showDisconnectedLayout();
+ }
+ if ((LLTwitterConnect::instance().getConnectionState() == LLTwitterConnect::TWITTER_NOT_CONNECTED) ||
+ (LLTwitterConnect::instance().getConnectionState() == LLTwitterConnect::TWITTER_CONNECTION_FAILED))
+ {
+ LLTwitterConnect::instance().checkConnectionToTwitter();
+ }
+ }
+ else
+ {
+ LLEventPumps::instance().obtain("TwitterConnectState").stopListening("LLTwitterAccountPanel");
+ LLEventPumps::instance().obtain("TwitterConnectInfo").stopListening("LLTwitterAccountPanel");
+ }
+}
+
+bool LLTwitterAccountPanel::onTwitterConnectStateChange(const LLSD& data)
+{
+ if(LLTwitterConnect::instance().isConnected())
+ {
+ //In process of disconnecting so leave the layout as is
+ if(data.get("enum").asInteger() != LLTwitterConnect::TWITTER_DISCONNECTING)
+ {
+ showConnectedLayout();
+ }
+ }
+ else
+ {
+ showDisconnectedLayout();
+ }
+
+ return false;
+}
+
+bool LLTwitterAccountPanel::onTwitterConnectInfoChange()
+{
+ LLSD info = LLTwitterConnect::instance().getInfo();
+ std::string clickable_name;
+
+ //Strings of format [http://www.somewebsite.com Click Me] become clickable text
+ if(info.has("link") && info.has("name"))
+ {
+ clickable_name = "[" + info["link"].asString() + " " + info["name"].asString() + "]";
+ }
+
+ mAccountNameLabel->setText(clickable_name);
+
+ return false;
+}
+
+void LLTwitterAccountPanel::showConnectButton()
+{
+ if(!mConnectButton->getVisible())
+ {
+ mConnectButton->setVisible(TRUE);
+ mDisconnectButton->setVisible(FALSE);
+ }
+}
+
+void LLTwitterAccountPanel::hideConnectButton()
+{
+ if(mConnectButton->getVisible())
+ {
+ mConnectButton->setVisible(FALSE);
+ mDisconnectButton->setVisible(TRUE);
+ }
+}
+
+void LLTwitterAccountPanel::showDisconnectedLayout()
+{
+ mAccountCaptionLabel->setText(getString("twitter_disconnected"));
+ mAccountNameLabel->setText(std::string(""));
+ showConnectButton();
+}
+
+void LLTwitterAccountPanel::showConnectedLayout()
+{
+ LLTwitterConnect::instance().loadTwitterInfo();
+
+ mAccountCaptionLabel->setText(getString("twitter_connected"));
+ hideConnectButton();
+}
+
+void LLTwitterAccountPanel::onConnect()
+{
+ LLTwitterConnect::instance().checkConnectionToTwitter(true);
+
+ //Clear only the twitter browser cookies so that the twitter login screen appears
+ LLViewerMedia::getCookieStore()->removeCookiesByDomain(".twitter.com");
+}
+
+void LLTwitterAccountPanel::onDisconnect()
+{
+ LLTwitterConnect::instance().disconnectFromTwitter();
+
+ LLViewerMedia::getCookieStore()->removeCookiesByDomain(".twitter.com");
+}
+
+////////////////////////
+//LLFloaterTwitter///////
+////////////////////////
+
+LLFloaterTwitter::LLFloaterTwitter(const LLSD& key) : LLFloater(key),
+ mSocialPhotoPanel(NULL),
+ mStatusErrorText(NULL),
+ mStatusLoadingText(NULL),
+ mStatusLoadingIndicator(NULL)
+{
+ mCommitCallbackRegistrar.add("SocialSharing.Cancel", boost::bind(&LLFloaterTwitter::onCancel, this));
+}
+
+void LLFloaterTwitter::onCancel()
+{
+ closeFloater();
+}
+
+BOOL LLFloaterTwitter::postBuild()
+{
+ // Keep tab of the Photo Panel
+ mSocialPhotoPanel = static_cast<LLTwitterPhotoPanel*>(getChild<LLUICtrl>("panel_twitter_photo"));
+ // Connection status widgets
+ mStatusErrorText = getChild<LLTextBox>("connection_error_text");
+ mStatusLoadingText = getChild<LLTextBox>("connection_loading_text");
+ mStatusLoadingIndicator = getChild<LLUICtrl>("connection_loading_indicator");
+ return LLFloater::postBuild();
+}
+
+void LLFloaterTwitter::showPhotoPanel()
+{
+ LLTabContainer* parent = dynamic_cast<LLTabContainer*>(mSocialPhotoPanel->getParent());
+ if (!parent)
+ {
+ llwarns << "Cannot find panel container" << llendl;
+ return;
+ }
+
+ parent->selectTabPanel(mSocialPhotoPanel);
+}
+
+// static
+void LLFloaterTwitter::preUpdate()
+{
+ LLFloaterTwitter* instance = LLFloaterReg::findTypedInstance<LLFloaterTwitter>("twitter");
+ if (instance)
+ {
+ //Will set file size text to 'unknown'
+ instance->mSocialPhotoPanel->updateControls();
+ }
+}
+
+// static
+void LLFloaterTwitter::postUpdate()
+{
+ LLFloaterTwitter* instance = LLFloaterReg::findTypedInstance<LLFloaterTwitter>("twitter");
+ if (instance)
+ {
+ //Will set the file size text
+ instance->mSocialPhotoPanel->updateControls();
+
+ // The refresh button is initially hidden. We show it after the first update,
+ // i.e. after snapshot is taken
+ LLUICtrl * refresh_button = instance->mSocialPhotoPanel->getRefreshBtn();
+
+ if (!refresh_button->getVisible())
+ {
+ refresh_button->setVisible(true);
+ }
+
+ }
+}
+
+void LLFloaterTwitter::draw()
+{
+ if (mStatusErrorText && mStatusLoadingText && mStatusLoadingIndicator)
+ {
+ mStatusErrorText->setVisible(false);
+ mStatusLoadingText->setVisible(false);
+ mStatusLoadingIndicator->setVisible(false);
+ LLTwitterConnect::EConnectionState connection_state = LLTwitterConnect::instance().getConnectionState();
+ std::string status_text;
+
+ switch (connection_state)
+ {
+ case LLTwitterConnect::TWITTER_NOT_CONNECTED:
+ // No status displayed when first opening the panel and no connection done
+ case LLTwitterConnect::TWITTER_CONNECTED:
+ // When successfully connected, no message is displayed
+ case LLTwitterConnect::TWITTER_POSTED:
+ // No success message to show since we actually close the floater after successful posting completion
+ break;
+ case LLTwitterConnect::TWITTER_CONNECTION_IN_PROGRESS:
+ // Connection loading indicator
+ mStatusLoadingText->setVisible(true);
+ status_text = LLTrans::getString("SocialTwitterConnecting");
+ mStatusLoadingText->setValue(status_text);
+ mStatusLoadingIndicator->setVisible(true);
+ break;
+ case LLTwitterConnect::TWITTER_POSTING:
+ // Posting indicator
+ mStatusLoadingText->setVisible(true);
+ status_text = LLTrans::getString("SocialTwitterPosting");
+ mStatusLoadingText->setValue(status_text);
+ mStatusLoadingIndicator->setVisible(true);
+ break;
+ case LLTwitterConnect::TWITTER_CONNECTION_FAILED:
+ // Error connecting to the service
+ mStatusErrorText->setVisible(true);
+ status_text = LLTrans::getString("SocialTwitterErrorConnecting");
+ mStatusErrorText->setValue(status_text);
+ break;
+ case LLTwitterConnect::TWITTER_POST_FAILED:
+ // Error posting to the service
+ mStatusErrorText->setVisible(true);
+ status_text = LLTrans::getString("SocialTwitterErrorPosting");
+ mStatusErrorText->setValue(status_text);
+ break;
+ case LLTwitterConnect::TWITTER_DISCONNECTING:
+ // Disconnecting loading indicator
+ mStatusLoadingText->setVisible(true);
+ status_text = LLTrans::getString("SocialTwitterDisconnecting");
+ mStatusLoadingText->setValue(status_text);
+ mStatusLoadingIndicator->setVisible(true);
+ break;
+ case LLTwitterConnect::TWITTER_DISCONNECT_FAILED:
+ // Error disconnecting from the service
+ mStatusErrorText->setVisible(true);
+ status_text = LLTrans::getString("SocialTwitterErrorDisconnecting");
+ mStatusErrorText->setValue(status_text);
+ break;
+ }
+ }
+ LLFloater::draw();
+}
+
diff --git a/indra/newview/llfloatertwitter.h b/indra/newview/llfloatertwitter.h
new file mode 100644
index 0000000000..686e167b1f
--- /dev/null
+++ b/indra/newview/llfloatertwitter.h
@@ -0,0 +1,126 @@
+/**
+* @file llfloatertwitter.h
+* @brief Header file for llfloatertwitter
+* @author cho@lindenlab.com
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2013, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+#ifndef LL_LLFLOATERTWITTER_H
+#define LL_LLFLOATERTWITTER_H
+
+#include "llfloater.h"
+#include "lltextbox.h"
+#include "llviewertexture.h"
+
+class LLIconCtrl;
+class LLCheckBoxCtrl;
+class LLSnapshotLivePreview;
+
+class LLTwitterPhotoPanel : public LLPanel
+{
+public:
+ LLTwitterPhotoPanel();
+ ~LLTwitterPhotoPanel();
+
+ BOOL postBuild();
+ void draw();
+
+ LLSnapshotLivePreview* getPreviewView();
+ void onVisibilityChange(const LLSD& new_visibility);
+ void onClickNewSnapshot();
+ void onSend();
+ bool onTwitterConnectStateChange(const LLSD& data);
+
+ void sendPhoto();
+ void clearAndClose();
+
+ void updateControls();
+ void updateResolution(BOOL do_update);
+ void checkAspectRatio(S32 index);
+ LLUICtrl* getRefreshBtn();
+
+private:
+ LLHandle<LLView> mPreviewHandle;
+
+ LLUICtrl * mSnapshotPanel;
+ LLUICtrl * mResolutionComboBox;
+ LLUICtrl * mRefreshBtn;
+ LLUICtrl * mWorkingLabel;
+ LLUICtrl * mThumbnailPlaceholder;
+ LLUICtrl * mStatusTextBox;
+ LLUICtrl * mPhotoCheckbox;
+ LLUICtrl * mPostButton;
+ LLUICtrl* mCancelButton;
+
+ std::string mOldStatusText;
+};
+
+class LLTwitterAccountPanel : public LLPanel
+{
+public:
+ LLTwitterAccountPanel();
+ BOOL postBuild();
+ void draw();
+
+private:
+ void onVisibilityChange(const LLSD& new_visibility);
+ bool onTwitterConnectStateChange(const LLSD& data);
+ bool onTwitterConnectInfoChange();
+ void onConnect();
+ void onUseAnotherAccount();
+ void onDisconnect();
+
+ void showConnectButton();
+ void hideConnectButton();
+ void showDisconnectedLayout();
+ void showConnectedLayout();
+
+ LLTextBox * mAccountCaptionLabel;
+ LLTextBox * mAccountNameLabel;
+ LLUICtrl * mPanelButtons;
+ LLUICtrl * mConnectButton;
+ LLUICtrl * mDisconnectButton;
+};
+
+
+class LLFloaterTwitter : public LLFloater
+{
+public:
+ LLFloaterTwitter(const LLSD& key);
+ BOOL postBuild();
+ void draw();
+ void onCancel();
+
+ void showPhotoPanel();
+
+ static void preUpdate();
+ static void postUpdate();
+
+private:
+ LLTwitterPhotoPanel* mSocialPhotoPanel;
+ LLTextBox* mStatusErrorText;
+ LLTextBox* mStatusLoadingText;
+ LLUICtrl* mStatusLoadingIndicator;
+};
+
+#endif // LL_LLFLOATERTWITTER_H
+
diff --git a/indra/newview/llfloaterwebcontent.cpp b/indra/newview/llfloaterwebcontent.cpp
index 5c569b9bf0..814c91ef6c 100755
--- a/indra/newview/llfloaterwebcontent.cpp
+++ b/indra/newview/llfloaterwebcontent.cpp
@@ -30,6 +30,8 @@
#include "lliconctrl.h"
#include "llfloaterreg.h"
#include "llfacebookconnect.h"
+#include "llflickrconnect.h"
+#include "lltwitterconnect.h"
#include "lllayoutstack.h"
#include "llpluginclassmedia.h"
#include "llprogressbar.h"
@@ -298,7 +300,24 @@ void LLFloaterWebContent::onClose(bool app_quitting)
LLFacebookConnect::instance().setConnectionState(LLFacebookConnect::FB_CONNECTION_FAILED);
}
}
-
+ // Same with Flickr
+ LLFloater* flickr_web = LLFloaterReg::getInstance("flickr_web");
+ if (flickr_web == this)
+ {
+ if (!LLFlickrConnect::instance().isConnected())
+ {
+ LLFlickrConnect::instance().setConnectionState(LLFlickrConnect::FLICKR_CONNECTION_FAILED);
+ }
+ }
+ // And Twitter
+ LLFloater* twitter_web = LLFloaterReg::getInstance("twitter_web");
+ if (twitter_web == this)
+ {
+ if (!LLTwitterConnect::instance().isConnected())
+ {
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_FAILED);
+ }
+ }
LLViewerMedia::proxyWindowClosed(mUUID);
destroy();
}
diff --git a/indra/newview/llpanelsnapshotoptions.cpp b/indra/newview/llpanelsnapshotoptions.cpp
index 554fabe5b3..4cadd837d1 100755
--- a/indra/newview/llpanelsnapshotoptions.cpp
+++ b/indra/newview/llpanelsnapshotoptions.cpp
@@ -31,6 +31,10 @@
#include "llsidetraypanelcontainer.h"
#include "llfloatersnapshot.h" // FIXME: create a snapshot model
+#include "llfloaterreg.h"
+#include "llfloatersocial.h"
+#include "llfloaterflickr.h"
+#include "llfloatertwitter.h"
/**
* Provides several ways to save a snapshot.
@@ -44,6 +48,7 @@ class LLPanelSnapshotOptions
public:
LLPanelSnapshotOptions();
~LLPanelSnapshotOptions();
+ /*virtual*/ BOOL postBuild();
/*virtual*/ void onOpen(const LLSD& key);
/*virtual*/ void onEconomyDataChange() { updateUploadCost(); }
@@ -54,6 +59,9 @@ private:
void onSaveToEmail();
void onSaveToInventory();
void onSaveToComputer();
+ void onSendToFacebook();
+ void onSendToTwitter();
+ void onSendToFlickr();
};
static LLRegisterPanelClassWrapper<LLPanelSnapshotOptions> panel_class("llpanelsnapshotoptions");
@@ -74,6 +82,19 @@ LLPanelSnapshotOptions::~LLPanelSnapshotOptions()
}
// virtual
+BOOL LLPanelSnapshotOptions::postBuild()
+{
+ LLTextBox* sendToFacebookTextBox = getChild<LLTextBox>("send_to_facebook_textbox");
+ sendToFacebookTextBox->setURLClickedCallback(boost::bind(&LLPanelSnapshotOptions::onSendToFacebook, this));
+ LLTextBox* sendToTwitterTextBox = getChild<LLTextBox>("send_to_twitter_textbox");
+ sendToTwitterTextBox->setURLClickedCallback(boost::bind(&LLPanelSnapshotOptions::onSendToTwitter, this));
+ LLTextBox* sendToFlickrTextBox = getChild<LLTextBox>("send_to_flickr_textbox");
+ sendToFlickrTextBox->setURLClickedCallback(boost::bind(&LLPanelSnapshotOptions::onSendToFlickr, this));
+
+ return LLPanel::postBuild();
+}
+
+// virtual
void LLPanelSnapshotOptions::onOpen(const LLSD& key)
{
updateUploadCost();
@@ -118,3 +139,39 @@ void LLPanelSnapshotOptions::onSaveToComputer()
{
openPanel("panel_snapshot_local");
}
+
+void LLPanelSnapshotOptions::onSendToFacebook()
+{
+ LLFloaterReg::hideInstance("snapshot");
+
+ LLFloaterSocial* social_floater = dynamic_cast<LLFloaterSocial*>(LLFloaterReg::getInstance("social"));
+ if (social_floater)
+ {
+ social_floater->showPhotoPanel();
+ }
+ LLFloaterReg::showInstance("social");
+}
+
+void LLPanelSnapshotOptions::onSendToTwitter()
+{
+ LLFloaterReg::hideInstance("snapshot");
+
+ LLFloaterTwitter* twitter_floater = dynamic_cast<LLFloaterTwitter*>(LLFloaterReg::getInstance("twitter"));
+ if (twitter_floater)
+ {
+ twitter_floater->showPhotoPanel();
+ }
+ LLFloaterReg::showInstance("twitter");
+}
+
+void LLPanelSnapshotOptions::onSendToFlickr()
+{
+ LLFloaterReg::hideInstance("snapshot");
+
+ LLFloaterFlickr* flickr_floater = dynamic_cast<LLFloaterFlickr*>(LLFloaterReg::getInstance("flickr"));
+ if (flickr_floater)
+ {
+ flickr_floater->showPhotoPanel();
+ }
+ LLFloaterReg::showInstance("flickr");
+}
diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp
index 7532ebfc57..67952f83c7 100644
--- a/indra/newview/llsnapshotlivepreview.cpp
+++ b/indra/newview/llsnapshotlivepreview.cpp
@@ -35,6 +35,8 @@
#include "llfloaterperms.h"
#include "llfloaterreg.h"
#include "llfloatersocial.h"
+#include "llfloaterflickr.h"
+#include "llfloatertwitter.h"
#include "llimagebmp.h"
#include "llimagej2c.h"
#include "llimagejpeg.h"
@@ -209,6 +211,8 @@ void LLSnapshotLivePreview::updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail
mSnapshotDelayTimer.setTimerExpirySec(delay);
LLFloaterSnapshot::preUpdate();
LLFloaterSocial::preUpdate();
+ LLFloaterFlickr::preUpdate();
+ LLFloaterTwitter::preUpdate();
}
// Update thumbnail if requested.
@@ -766,6 +770,8 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview )
lldebugs << "done creating snapshot" << llendl;
LLFloaterSnapshot::postUpdate();
LLFloaterSocial::postUpdate();
+ LLFloaterFlickr::postUpdate();
+ LLFloaterTwitter::postUpdate();
return TRUE;
}
diff --git a/indra/newview/lltwitterconnect.cpp b/indra/newview/lltwitterconnect.cpp
new file mode 100644
index 0000000000..e015867df2
--- /dev/null
+++ b/indra/newview/lltwitterconnect.cpp
@@ -0,0 +1,471 @@
+/**
+ * @file lltwitterconnect.h
+ * @author Merov, Cho
+ * @brief Connection to Twitter Service
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltwitterconnect.h"
+
+#include "llagent.h"
+#include "llcallingcard.h" // for LLAvatarTracker
+#include "llcommandhandler.h"
+#include "llhttpclient.h"
+#include "llnotificationsutil.h"
+#include "llurlaction.h"
+#include "llimagepng.h"
+#include "llimagejpeg.h"
+#include "lltrans.h"
+#include "llevents.h"
+#include "llviewerregion.h"
+
+#include "llfloaterwebcontent.h"
+#include "llfloaterreg.h"
+
+boost::scoped_ptr<LLEventPump> LLTwitterConnect::sStateWatcher(new LLEventStream("TwitterConnectState"));
+boost::scoped_ptr<LLEventPump> LLTwitterConnect::sInfoWatcher(new LLEventStream("TwitterConnectInfo"));
+boost::scoped_ptr<LLEventPump> LLTwitterConnect::sContentWatcher(new LLEventStream("TwitterConnectContent"));
+
+// Local functions
+void log_twitter_connect_error(const std::string& request, U32 status, const std::string& reason, const std::string& code, const std::string& description)
+{
+ // Note: 302 (redirect) is *not* an error that warrants logging
+ if (status != 302)
+ {
+ LL_WARNS("TwitterConnect") << request << " request failed with a " << status << " " << reason << ". Reason: " << code << " (" << description << ")" << LL_ENDL;
+ }
+}
+
+void toast_user_for_twitter_success()
+{
+ LLSD args;
+ args["MESSAGE"] = LLTrans::getString("twitter_post_success");
+ LLNotificationsUtil::add("TwitterConnect", args);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLTwitterConnectResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLTwitterConnectResponder);
+public:
+
+ LLTwitterConnectResponder()
+ {
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_IN_PROGRESS);
+ }
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (isGoodStatus(status))
+ {
+ LL_DEBUGS("TwitterConnect") << "Connect successful. content: " << content << LL_ENDL;
+
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTED);
+ }
+ else if (status != 302)
+ {
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_FAILED);
+ log_twitter_connect_error("Connect", status, reason, content.get("error_code"), content.get("error_description"));
+ }
+ }
+
+ void completedHeader(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (status == 302)
+ {
+ LLTwitterConnect::instance().openTwitterWeb(content["location"]);
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLTwitterShareResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLTwitterShareResponder);
+public:
+
+ LLTwitterShareResponder()
+ {
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_POSTING);
+ }
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (isGoodStatus(status))
+ {
+ toast_user_for_twitter_success();
+ LL_DEBUGS("TwitterConnect") << "Post successful. content: " << content << LL_ENDL;
+
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_POSTED);
+ }
+ else if (status == 404)
+ {
+ LLTwitterConnect::instance().connectToTwitter();
+ }
+ else
+ {
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_POST_FAILED);
+ log_twitter_connect_error("Share", status, reason, content.get("error_code"), content.get("error_description"));
+ }
+ }
+
+ void completedHeader(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (status == 302)
+ {
+ LLTwitterConnect::instance().openTwitterWeb(content["location"]);
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLTwitterDisconnectResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLTwitterDisconnectResponder);
+public:
+
+ LLTwitterDisconnectResponder()
+ {
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_DISCONNECTING);
+ }
+
+ void setUserDisconnected()
+ {
+ // Clear data
+ LLTwitterConnect::instance().clearInfo();
+
+ //Notify state change
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_NOT_CONNECTED);
+ }
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (isGoodStatus(status))
+ {
+ LL_DEBUGS("TwitterConnect") << "Disconnect successful. content: " << content << LL_ENDL;
+ setUserDisconnected();
+
+ }
+ //User not found so already disconnected
+ else if(status == 404)
+ {
+ LL_DEBUGS("TwitterConnect") << "Already disconnected. content: " << content << LL_ENDL;
+ setUserDisconnected();
+ }
+ else
+ {
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_DISCONNECT_FAILED);
+ log_twitter_connect_error("Disconnect", status, reason, content.get("error_code"), content.get("error_description"));
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLTwitterConnectedResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLTwitterConnectedResponder);
+public:
+
+ LLTwitterConnectedResponder(bool auto_connect) : mAutoConnect(auto_connect)
+ {
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_IN_PROGRESS);
+ }
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (isGoodStatus(status))
+ {
+ LL_DEBUGS("TwitterConnect") << "Connect successful. content: " << content << LL_ENDL;
+
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTED);
+ }
+ else
+ {
+ // show the twitter login page if not connected yet
+ if (status == 404)
+ {
+ if (mAutoConnect)
+ {
+ LLTwitterConnect::instance().connectToTwitter();
+ }
+ else
+ {
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_NOT_CONNECTED);
+ }
+ }
+ else
+ {
+ LLTwitterConnect::instance().setConnectionState(LLTwitterConnect::TWITTER_CONNECTION_FAILED);
+ log_twitter_connect_error("Connected", status, reason, content.get("error_code"), content.get("error_description"));
+ }
+ }
+ }
+
+private:
+ bool mAutoConnect;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class LLTwitterInfoResponder : public LLHTTPClient::Responder
+{
+ LOG_CLASS(LLTwitterInfoResponder);
+public:
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& info)
+ {
+ if (isGoodStatus(status))
+ {
+ llinfos << "Twitter: Info received" << llendl;
+ LL_DEBUGS("TwitterConnect") << "Getting Twitter info successful. info: " << info << LL_ENDL;
+ LLTwitterConnect::instance().storeInfo(info);
+ }
+ else
+ {
+ log_twitter_connect_error("Info", status, reason, info.get("error_code"), info.get("error_description"));
+ }
+ }
+
+ void completedHeader(U32 status, const std::string& reason, const LLSD& content)
+ {
+ if (status == 302)
+ {
+ LLTwitterConnect::instance().openTwitterWeb(content["location"]);
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+LLTwitterConnect::LLTwitterConnect()
+: mConnectionState(TWITTER_NOT_CONNECTED),
+ mConnected(false),
+ mInfo(),
+ mRefreshInfo(false),
+ mReadFromMaster(false)
+{
+}
+
+void LLTwitterConnect::openTwitterWeb(std::string url)
+{
+ // Open the URL in an internal browser window without navigation UI
+ LLFloaterWebContent::Params p;
+ p.url(url).show_chrome(true);
+ p.url(url).allow_address_entry(false);
+ p.url(url).allow_back_forward_navigation(false);
+ p.url(url).trusted_content(true);
+ LLFloater *floater = LLFloaterReg::showInstance("twitter_web", p);
+ //the internal web browser has a bug that prevents it from gaining focus unless a mouse event occurs first (it seems).
+ //So when showing the internal web browser, set focus to it's containing floater "twitter_web". When a mouse event
+ //occurs on the "webbrowser" panel part of the floater, a mouse cursor will properly show and the "webbrowser" will gain focus.
+ //twitter_web floater contains the "webbrowser" panel. JIRA: ACME-744
+ gFocusMgr.setKeyboardFocus( floater );
+
+ //LLUrlAction::openURLExternal(url);
+}
+
+std::string LLTwitterConnect::getTwitterConnectURL(const std::string& route, bool include_read_from_master)
+{
+ std::string url("");
+ LLViewerRegion *regionp = gAgent.getRegion();
+ if (regionp)
+ {
+ //url = "http://pdp15.lindenlab.com/twitter/agent/" + gAgentID.asString(); // TEMPORARY FOR TESTING - CHO
+ url = regionp->getCapability("TwitterConnect");
+ url += route;
+
+ if (include_read_from_master && mReadFromMaster)
+ {
+ url += "?read_from_master=true";
+ }
+ }
+ return url;
+}
+
+void LLTwitterConnect::connectToTwitter(const std::string& request_token, const std::string& oauth_verifier)
+{
+ LLSD body;
+ if (!request_token.empty())
+ body["request_token"] = request_token;
+ if (!oauth_verifier.empty())
+ body["oauth_verifier"] = oauth_verifier;
+
+ LLHTTPClient::put(getTwitterConnectURL("/connection"), body, new LLTwitterConnectResponder());
+}
+
+void LLTwitterConnect::disconnectFromTwitter()
+{
+ LLHTTPClient::del(getTwitterConnectURL("/connection"), new LLTwitterDisconnectResponder());
+}
+
+void LLTwitterConnect::checkConnectionToTwitter(bool auto_connect)
+{
+ const bool follow_redirects = false;
+ const F32 timeout = HTTP_REQUEST_EXPIRY_SECS;
+ LLHTTPClient::get(getTwitterConnectURL("/connection", true), new LLTwitterConnectedResponder(auto_connect),
+ LLSD(), timeout, follow_redirects);
+}
+
+void LLTwitterConnect::loadTwitterInfo()
+{
+ if(mRefreshInfo)
+ {
+ const bool follow_redirects = false;
+ const F32 timeout = HTTP_REQUEST_EXPIRY_SECS;
+ LLHTTPClient::get(getTwitterConnectURL("/info", true), new LLTwitterInfoResponder(),
+ LLSD(), timeout, follow_redirects);
+ }
+}
+
+void LLTwitterConnect::uploadPhoto(const std::string& image_url, const std::string& status)
+{
+ LLSD body;
+ body["image"] = image_url;
+ body["status"] = status;
+
+ // Note: we can use that route for different publish action. We should be able to use the same responder.
+ LLHTTPClient::post(getTwitterConnectURL("/share/photo", true), body, new LLTwitterShareResponder());
+}
+
+void LLTwitterConnect::uploadPhoto(LLPointer<LLImageFormatted> image, const std::string& status)
+{
+ std::string imageFormat;
+ if (dynamic_cast<LLImagePNG*>(image.get()))
+ {
+ imageFormat = "png";
+ }
+ else if (dynamic_cast<LLImageJPEG*>(image.get()))
+ {
+ imageFormat = "jpg";
+ }
+ else
+ {
+ llwarns << "Image to upload is not a PNG or JPEG" << llendl;
+ return;
+ }
+
+ // All this code is mostly copied from LLWebProfile::post()
+ const std::string boundary = "----------------------------0123abcdefab";
+
+ LLSD headers;
+ headers["Content-Type"] = "multipart/form-data; boundary=" + boundary;
+
+ std::ostringstream body;
+
+ // *NOTE: The order seems to matter.
+ body << "--" << boundary << "\r\n"
+ << "Content-Disposition: form-data; name=\"status\"\r\n\r\n"
+ << status << "\r\n";
+
+ body << "--" << boundary << "\r\n"
+ << "Content-Disposition: form-data; name=\"image\"; filename=\"snapshot." << imageFormat << "\"\r\n"
+ << "Content-Type: image/" << imageFormat << "\r\n\r\n";
+
+ // Insert the image data.
+ // *FIX: Treating this as a string will probably screw it up ...
+ U8* image_data = image->getData();
+ for (S32 i = 0; i < image->getDataSize(); ++i)
+ {
+ body << image_data[i];
+ }
+
+ body << "\r\n--" << boundary << "--\r\n";
+
+ // postRaw() takes ownership of the buffer and releases it later.
+ size_t size = body.str().size();
+ U8 *data = new U8[size];
+ memcpy(data, body.str().data(), size);
+
+ // Note: we can use that route for different publish action. We should be able to use the same responder.
+ LLHTTPClient::postRaw(getTwitterConnectURL("/share/photo", true), data, size, new LLTwitterShareResponder(), headers);
+}
+
+void LLTwitterConnect::updateStatus(const std::string& status)
+{
+ LLSD body;
+ body["status"] = status;
+
+ // Note: we can use that route for different publish action. We should be able to use the same responder.
+ LLHTTPClient::post(getTwitterConnectURL("/share/status", true), body, new LLTwitterShareResponder());
+}
+
+void LLTwitterConnect::storeInfo(const LLSD& info)
+{
+ mInfo = info;
+ mRefreshInfo = false;
+
+ sInfoWatcher->post(info);
+}
+
+const LLSD& LLTwitterConnect::getInfo() const
+{
+ return mInfo;
+}
+
+void LLTwitterConnect::clearInfo()
+{
+ mInfo = LLSD();
+}
+
+void LLTwitterConnect::setDataDirty()
+{
+ mRefreshInfo = true;
+}
+
+void LLTwitterConnect::setConnectionState(LLTwitterConnect::EConnectionState connection_state)
+{
+ if(connection_state == TWITTER_CONNECTED)
+ {
+ mReadFromMaster = true;
+ setConnected(true);
+ setDataDirty();
+ }
+ else if(connection_state == TWITTER_NOT_CONNECTED)
+ {
+ setConnected(false);
+ }
+ else if(connection_state == TWITTER_POSTED)
+ {
+ mReadFromMaster = false;
+ }
+
+ if (mConnectionState != connection_state)
+ {
+ LLSD state_info;
+ state_info["enum"] = connection_state;
+ sStateWatcher->post(state_info);
+ }
+
+ mConnectionState = connection_state;
+}
+
+void LLTwitterConnect::setConnected(bool connected)
+{
+ mConnected = connected;
+}
diff --git a/indra/newview/lltwitterconnect.h b/indra/newview/lltwitterconnect.h
new file mode 100644
index 0000000000..c1df13f18c
--- /dev/null
+++ b/indra/newview/lltwitterconnect.h
@@ -0,0 +1,99 @@
+/**
+ * @file lltwitterconnect.h
+ * @author Merov, Cho
+ * @brief Connection to Twitter Service
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTWITTERCONNECT_H
+#define LL_LLTWITTERCONNECT_H
+
+#include "llsingleton.h"
+#include "llimage.h"
+
+class LLEventPump;
+
+/**
+ * @class LLTwitterConnect
+ *
+ * Manages authentication to, and interaction with, a web service allowing the
+ * the viewer to post status updates and upload photos to Twitter.
+ */
+class LLTwitterConnect : public LLSingleton<LLTwitterConnect>
+{
+ LOG_CLASS(LLTwitterConnect);
+public:
+ enum EConnectionState
+ {
+ TWITTER_NOT_CONNECTED = 0,
+ TWITTER_CONNECTION_IN_PROGRESS = 1,
+ TWITTER_CONNECTED = 2,
+ TWITTER_CONNECTION_FAILED = 3,
+ TWITTER_POSTING = 4,
+ TWITTER_POSTED = 5,
+ TWITTER_POST_FAILED = 6,
+ TWITTER_DISCONNECTING = 7,
+ TWITTER_DISCONNECT_FAILED = 8
+ };
+
+ void connectToTwitter(const std::string& request_token = "", const std::string& oauth_verifier = ""); // Initiate the complete Twitter connection. Please use checkConnectionToTwitter() in normal use.
+ void disconnectFromTwitter(); // Disconnect from the Twitter service.
+ void checkConnectionToTwitter(bool auto_connect = false); // Check if an access token is available on the Twitter service. If not, call connectToTwitter().
+
+ void loadTwitterInfo();
+ void uploadPhoto(const std::string& image_url, const std::string& status);
+ void uploadPhoto(LLPointer<LLImageFormatted> image, const std::string& status);
+ void updateStatus(const std::string& status);
+
+ void storeInfo(const LLSD& info);
+ const LLSD& getInfo() const;
+ void clearInfo();
+ void setDataDirty();
+
+ void setConnectionState(EConnectionState connection_state);
+ void setConnected(bool connected);
+ bool isConnected() { return mConnected; }
+ bool isTransactionOngoing() { return ((mConnectionState == TWITTER_CONNECTION_IN_PROGRESS) || (mConnectionState == TWITTER_POSTING) || (mConnectionState == TWITTER_DISCONNECTING)); }
+ EConnectionState getConnectionState() { return mConnectionState; }
+
+ void openTwitterWeb(std::string url);
+
+private:
+ friend class LLSingleton<LLTwitterConnect>;
+
+ LLTwitterConnect();
+ ~LLTwitterConnect() {};
+ std::string getTwitterConnectURL(const std::string& route = "", bool include_read_from_master = false);
+
+ EConnectionState mConnectionState;
+ BOOL mConnected;
+ LLSD mInfo;
+ bool mRefreshInfo;
+ bool mReadFromMaster;
+
+ static boost::scoped_ptr<LLEventPump> sStateWatcher;
+ static boost::scoped_ptr<LLEventPump> sInfoWatcher;
+ static boost::scoped_ptr<LLEventPump> sContentWatcher;
+};
+
+#endif // LL_LLTWITTERCONNECT_H
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index 4ce049df03..1e07aaf5ec 100755
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -53,13 +53,14 @@
#include "llfloaterconversationlog.h"
#include "llfloaterconversationpreview.h"
#include "llfloaterdeleteenvpreset.h"
+#include "llfloaterdestinations.h"
#include "llfloaterdisplayname.h"
#include "llfloatereditdaycycle.h"
#include "llfloatereditsky.h"
#include "llfloatereditwater.h"
#include "llfloaterenvironmentsettings.h"
#include "llfloaterevent.h"
-#include "llfloaterdestinations.h"
+#include "llfloaterflickr.h"
#include "llfloaterfonttest.h"
#include "llfloatergesture.h"
#include "llfloatergodtools.h"
@@ -115,6 +116,7 @@
#include "llfloatertopobjects.h"
#include "llfloatertoybox.h"
#include "llfloatertranslationsettings.h"
+#include "llfloatertwitter.h"
#include "llfloateruipreview.h"
#include "llfloatervoiceeffect.h"
#include "llfloatervoicevolume.h"
@@ -305,6 +307,8 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("settings_debug", "floater_settings_debug.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSettingsDebug>);
LLFloaterReg::add("sound_devices", "floater_sound_devices.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSoundDevices>);
LLFloaterReg::add("social", "floater_social.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSocial>);
+ LLFloaterReg::add("flickr", "floater_flickr.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterFlickr>);
+ LLFloaterReg::add("twitter", "floater_twitter.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterTwitter>);
LLFloaterReg::add("stats", "floater_stats.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloater>);
LLFloaterReg::add("start_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterRunQueue>);
LLFloaterReg::add("stop_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterNotRunQueue>);
@@ -312,8 +316,10 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("search", "floater_search.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSearch>);
LLFloaterReg::add("my_profile", "floater_my_web_profile.xml", (LLFloaterBuildFunc)&LLFloaterWebProfile::create);
LLFloaterReg::add("profile", "floater_web_profile.xml", (LLFloaterBuildFunc)&LLFloaterWebProfile::create);
- LLFloaterReg::add("how_to", "floater_how_to.xml", (LLFloaterBuildFunc)&LLFloaterWebContent::create);
+ LLFloaterReg::add("how_to", "floater_how_to.xml", (LLFloaterBuildFunc)&LLFloaterWebContent::create);
LLFloaterReg::add("fbc_web", "floater_fbc_web.xml", (LLFloaterBuildFunc)&LLFloaterWebContent::create);
+ LLFloaterReg::add("flickr_web", "floater_fbc_web.xml", (LLFloaterBuildFunc)&LLFloaterWebContent::create);
+ LLFloaterReg::add("twitter_web", "floater_fbc_web.xml", (LLFloaterBuildFunc)&LLFloaterWebContent::create);
LLFloaterUIPreviewUtil::registerFloater();
LLFloaterReg::add("upload_anim_bvh", "floater_animation_bvh_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterBvhPreview>, "upload");
diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml
index 94c187e21a..e42743824e 100755
--- a/indra/newview/skins/default/textures/textures.xml
+++ b/indra/newview/skins/default/textures/textures.xml
@@ -149,6 +149,8 @@ with the same filename but different name
<texture name="Command_Profile_Icon" file_name="toolbar_icons/profile.png" preload="true" />
<texture name="Command_Search_Icon" file_name="toolbar_icons/search.png" preload="true" />
<texture name="Command_Social_Icon" file_name="toolbar_icons/facebook.png" preload="true" />
+ <texture name="Command_Flickr_Icon" file_name="toolbar_icons/flickr.png" preload="true" />
+ <texture name="Command_Twitter_Icon" file_name="toolbar_icons/twitter.png" preload="true" />
<texture name="Command_Snapshot_Icon" file_name="toolbar_icons/snapshot.png" preload="true" />
<texture name="Command_Speak_Icon" file_name="toolbar_icons/speak.png" preload="true" />
<texture name="Command_View_Icon" file_name="toolbar_icons/view.png" preload="true" />
diff --git a/indra/newview/skins/default/textures/toolbar_icons/flickr.png b/indra/newview/skins/default/textures/toolbar_icons/flickr.png
new file mode 100644
index 0000000000..7fce9f0df2
--- /dev/null
+++ b/indra/newview/skins/default/textures/toolbar_icons/flickr.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/toolbar_icons/twitter.png b/indra/newview/skins/default/textures/toolbar_icons/twitter.png
new file mode 100644
index 0000000000..a99c490887
--- /dev/null
+++ b/indra/newview/skins/default/textures/toolbar_icons/twitter.png
Binary files differ
diff --git a/indra/newview/skins/default/xui/en/floater_flickr.xml b/indra/newview/skins/default/xui/en/floater_flickr.xml
new file mode 100644
index 0000000000..57014f8427
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_flickr.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<floater
+ positioning="cascading"
+ can_close="true"
+ can_resize="false"
+ help_topic="floater_flickr"
+ layout="topleft"
+ name="floater_flickr"
+ save_rect="true"
+ single_instance="true"
+ reuse_instance="true"
+ title="UPLOAD TO FLICKR"
+ height="622"
+ width="304">
+ <panel
+ height="622"
+ width="304"
+ visible="true"
+ name="background"
+ follows="all"
+ top="0"
+ left="0">
+ <tab_container
+ name="tabs"
+ tab_group="1"
+ tab_min_width="70"
+ tab_height="30"
+ tab_position="top"
+ top="7"
+ height="577"
+ halign="center">
+ <panel
+ filename="panel_flickr_photo.xml"
+ class="llflickrphotopanel"
+ follows="all"
+ label="PHOTO"
+ name="panel_flickr_photo"/>
+ <panel
+ filename="panel_flickr_account.xml"
+ class="llflickraccountpanel"
+ follows="all"
+ label="ACCOUNT"
+ name="panel_flickr_account"/>
+ </tab_container>
+ <panel
+ name="connection_status_panel"
+ follows="left|bottom|right"
+ height="24">
+ <text
+ name="connection_error_text"
+ type="string"
+ follows="left|bottom|right"
+ top="5"
+ left="9"
+ width="250"
+ height="20"
+ wrap="true"
+ halign="left"
+ valign="center"
+ text_color="DrYellow"
+ font="SansSerif">
+ Error
+ </text>
+ <loading_indicator
+ follows="left|bottom|right"
+ height="24"
+ width="24"
+ name="connection_loading_indicator"
+ top="2"
+ left="9"
+ visible="true"/>
+ <text
+ name="connection_loading_text"
+ type="string"
+ follows="left|bottom|right"
+ top="5"
+ left_pad="5"
+ width="250"
+ height="20"
+ wrap="true"
+ halign="left"
+ valign="center"
+ text_color="EmphasisColor"
+ font="SansSerif">
+ Loading...
+ </text>
+ </panel>
+ </panel>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_snapshot.xml b/indra/newview/skins/default/xui/en/floater_snapshot.xml
index 853c209bca..019ddad33c 100755
--- a/indra/newview/skins/default/xui/en/floater_snapshot.xml
+++ b/indra/newview/skins/default/xui/en/floater_snapshot.xml
@@ -10,7 +10,7 @@
help_topic="snapshot"
save_rect="true"
save_visibility="false"
- title="SNAPSHOT PREVIEW"
+ title="SNAPSHOT"
width="470">
<floater.string
name="unknown">
diff --git a/indra/newview/skins/default/xui/en/floater_twitter.xml b/indra/newview/skins/default/xui/en/floater_twitter.xml
new file mode 100644
index 0000000000..7007a14cdb
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_twitter.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<floater
+ positioning="cascading"
+ can_close="true"
+ can_resize="false"
+ help_topic="floater_twitter"
+ layout="topleft"
+ name="floater_twitter"
+ save_rect="true"
+ single_instance="true"
+ reuse_instance="true"
+ title="TWITTER"
+ height="482"
+ width="304">
+ <panel
+ height="482"
+ width="304"
+ visible="true"
+ name="background"
+ follows="all"
+ top="0"
+ left="0">
+ <tab_container
+ name="tabs"
+ tab_group="1"
+ tab_min_width="70"
+ tab_height="30"
+ tab_position="top"
+ top="7"
+ height="437"
+ halign="center">
+ <panel
+ filename="panel_twitter_photo.xml"
+ class="lltwitterphotopanel"
+ follows="all"
+ label="COMPOSE"
+ name="panel_twitter_photo"/>
+ <panel
+ filename="panel_twitter_account.xml"
+ class="lltwitteraccountpanel"
+ follows="all"
+ label="ACCOUNT"
+ name="panel_twitter_account"/>
+ </tab_container>
+ <panel
+ name="connection_status_panel"
+ follows="left|bottom|right"
+ height="24">
+ <text
+ name="connection_error_text"
+ type="string"
+ follows="left|bottom|right"
+ top="5"
+ left="9"
+ width="250"
+ height="20"
+ wrap="true"
+ halign="left"
+ valign="center"
+ text_color="DrYellow"
+ font="SansSerif">
+ Error
+ </text>
+ <loading_indicator
+ follows="left|bottom|right"
+ height="24"
+ width="24"
+ name="connection_loading_indicator"
+ top="2"
+ left="9"
+ visible="true"/>
+ <text
+ name="connection_loading_text"
+ type="string"
+ follows="left|bottom|right"
+ top="5"
+ left_pad="5"
+ width="250"
+ height="20"
+ wrap="true"
+ halign="left"
+ valign="center"
+ text_color="EmphasisColor"
+ font="SansSerif">
+ Loading...
+ </text>
+ </panel>
+ </panel>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index d635b8ee93..79adb4e8bb 100755
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -16,14 +16,6 @@
parameter="agent" />
</menu_item_call>
<menu_item_call
- label="Post to Facebook..."
- name="PostToFacebook">
- <menu_item_call.on_click
- function="Floater.Toggle"
- parameter="social"/>
- </menu_item_call>
- <menu_item_separator/>
- <menu_item_call
label="Appearance..."
name="ChangeOutfit">
<menu_item_call.on_click
@@ -288,6 +280,28 @@
parameter="conversation" />
</menu_item_check>
<menu_item_separator/>
+ <menu_item_call
+ label="Facebook..."
+ name="Facebook">
+ <menu_item_call.on_click
+ function="Floater.Toggle"
+ parameter="social"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Twitter..."
+ name="Twitter">
+ <menu_item_call.on_click
+ function="Floater.Toggle"
+ parameter="twitter"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Flickr..."
+ name="Flickr">
+ <menu_item_call.on_click
+ function="Floater.Toggle"
+ parameter="flickr"/>
+ </menu_item_call>
+ <menu_item_separator/>
<menu
label="Voice morphing"
name="VoiceMorphing"
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index e7c89db069..a3c0682aea 100755
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -6044,7 +6044,21 @@ Please select at least one type of content to search (General, Moderate, or Adul
type="notifytip">
[MESSAGE]
</notification>
-
+
+ <notification
+ icon="notify.tga"
+ name="FlickrConnect"
+ type="notifytip">
+ [MESSAGE]
+ </notification>
+
+ <notification
+ icon="notify.tga"
+ name="TwitterConnect"
+ type="notifytip">
+ [MESSAGE]
+ </notification>
+
<notification
icon="notify.tga"
name="PaymentReceived"
diff --git a/indra/newview/skins/default/xui/en/panel_flickr_account.xml b/indra/newview/skins/default/xui/en/panel_flickr_account.xml
new file mode 100644
index 0000000000..3a38852049
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_flickr_account.xml
@@ -0,0 +1,75 @@
+<panel
+ height="540"
+ width="304"
+ layout="topleft"
+ name="panel_flickr_account">
+ <string
+ name="flickr_connected"
+ value="You are connected to Flickr as:" />
+ <string
+ name="flickr_disconnected"
+ value="Not connected to Flickr" />
+ <text
+ layout="topleft"
+ length="1"
+ follows="top|left"
+ font="SansSerif"
+ height="16"
+ left="9"
+ name="account_caption_label"
+ top="21"
+ type="string">
+ Not connected to Flickr.
+ </text>
+ <text
+ layout="topleft"
+ top_pad="2"
+ length="1"
+ follows="top|left"
+ font="SansSerif"
+ height="16"
+ left="9"
+ name="account_name_label"
+ parse_urls="true"
+ type="string"/>
+ <panel
+ layout="topleft"
+ name="panel_buttons"
+ height="345"
+ left="9">
+ <button
+ layout="topleft"
+ follows="left|top"
+ top_pad="9"
+ visible="true"
+ height="23"
+ label="Connect..."
+ name="connect_btn"
+ width="210">
+ <commit_callback function="SocialSharing.Connect"/>
+ </button>
+
+ <button
+ layout="topleft"
+ follows="left|top"
+ top_delta="0"
+ height="23"
+ label="Disconnect"
+ name="disconnect_btn"
+ width="210"
+ visible="false">
+ <commit_callback function="SocialSharing.Disconnect"/>
+ </button>
+ <text
+ layout="topleft"
+ length="1"
+ follows="top|left"
+ height="16"
+ left="0"
+ name="account_learn_more_label"
+ top_pad="20"
+ type="string">
+ [http://community.secondlife.com/t5/English-Knowledge-Base/Second-Life-Share/ta-p/2149711 Learn about posting to Flickr]
+ </text>
+ </panel>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_flickr_photo.xml b/indra/newview/skins/default/xui/en/panel_flickr_photo.xml
new file mode 100644
index 0000000000..b3af271f34
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_flickr_photo.xml
@@ -0,0 +1,231 @@
+ <panel
+ height="540"
+ width="304"
+ layout="topleft"
+ name="panel_flickr_photo">
+ <layout_stack
+ layout="topleft"
+ border_size="0"
+ height="532"
+ follows="all"
+ orientation="vertical"
+ name="stack_photo"
+ top="8">
+ <layout_panel
+ name="snapshot_panel"
+ height="507">
+ <combo_box
+ control_name="SocialPhotoResolution"
+ follows="left|top"
+ top="6"
+ left="9"
+ name="resolution_combobox"
+ tool_tip="Image resolution"
+ height="21"
+ width="135">
+ <combo_box.item
+ label="Current Window"
+ name="CurrentWindow"
+ value="[i0,i0]" />
+ <combo_box.item
+ label="640x480"
+ name="640x480"
+ value="[i640,i480]" />
+ <combo_box.item
+ label="800x600"
+ name="800x600"
+ value="[i800,i600]" />
+ <combo_box.item
+ label="1024x768"
+ name="1024x768"
+ value="[i1024,i768]" />
+ </combo_box>
+ <text
+ follows="left|top"
+ font="SansSerifSmall"
+ height="14"
+ left="208"
+ length="1"
+ halign="right"
+ name="file_size_label"
+ top="9"
+ type="string"
+ width="50">
+ [SIZE] KB
+ </text>
+ <panel
+ height="150"
+ width="250"
+ visible="true"
+ name="thumbnail_placeholder"
+ top="33"
+ follows="left|top"
+ left="9">
+ </panel>
+ <button
+ follows="left|top"
+ height="23"
+ label="Refresh"
+ left="9"
+ top_pad="5"
+ name="new_snapshot_btn"
+ tool_tip="Click to refresh"
+ visible="true"
+ width="100" >
+ <button.commit_callback
+ function="SocialSharing.RefreshPhoto" />
+ </button>
+ <text
+ follows="left|top"
+ font="SansSerif"
+ text_color="EmphasisColor"
+ height="14"
+ top_pad="-19"
+ left_pad="-20"
+ length="1"
+ halign="center"
+ name="working_lbl"
+ translate="false"
+ type="string"
+ visible="true"
+ width="150">
+ Refreshing...
+ </text>
+ <text
+ length="1"
+ follows="top|left|right"
+ font="SansSerif"
+ height="16"
+ left="9"
+ name="title_label"
+ top_pad="20"
+ type="string">
+ Title:
+ </text>
+ <line_editor
+ follows="left|top"
+ height="20"
+ width="250"
+ left="9"
+ length="1"
+ max_length="256"
+ name="photo_title"
+ type="string">
+ </line_editor>
+ <text
+ length="1"
+ follows="top|left|right"
+ font="SansSerif"
+ height="16"
+ left="9"
+ name="description_label"
+ top_pad="10"
+ type="string">
+ Description:
+ </text>
+ <text_editor
+ follows="left|top"
+ height="50"
+ width="250"
+ left="9"
+ length="1"
+ max_length="700"
+ name="photo_description"
+ type="string"
+ word_wrap="true">
+ </text_editor>
+ <check_box
+ follows="left|top"
+ initial_value="true"
+ label="Include SL location at end of description"
+ name="add_location_cb"
+ left="9"
+ height="16"
+ top_pad="8"/>
+ <text
+ length="1"
+ follows="top|left|right"
+ font="SansSerif"
+ height="16"
+ left="9"
+ name="tags_label"
+ top_pad="10"
+ type="string">
+ Tags:
+ </text>
+ <text
+ length="1"
+ follows="top|left"
+ font="SansSerifSmall"
+ text_color="White_50"
+ height="30"
+ name="tags_help_label"
+ left="50"
+ top_pad="-16"
+ type="string">
+Separate tags with spaces
+Use "" for multi-word tags
+ </text>
+ <text_editor
+ follows="left|top"
+ height="50"
+ width="250"
+ left="9"
+ length="1"
+ max_length="700"
+ name="photo_tags"
+ type="string"
+ word_wrap="true">
+ </text_editor>
+ <combo_box
+ control_name="FlickrPhotoRating"
+ follows="left|top"
+ top_pad="16"
+ left="9"
+ label="Choose Flickr rating (required)..."
+ name="rating_combobox"
+ tool_tip="Flickr content rating"
+ height="21"
+ width="250">
+ <combo_box.item
+ label="Safe content rating"
+ name="SafeRating"
+ value="1" />
+ <combo_box.item
+ label="Moderate content rating"
+ name="ModerateRating"
+ value="2" />
+ <combo_box.item
+ label="Restricted content rating"
+ name="RestrictedRating"
+ value="3" />
+ </combo_box>
+ </layout_panel>
+ <layout_panel
+ name="photo_button_panel"
+ height="25">
+ <button
+ follows="left|top"
+ top="0"
+ left="9"
+ height="23"
+ label="Upload"
+ name="post_photo_btn"
+ width="100">
+ <button.commit_callback
+ function="SocialSharing.SendPhoto" />
+ </button>
+ <button
+ follows="left|top"
+ height="23"
+ label="Cancel"
+ name="cancel_photo_btn"
+ left_pad="15"
+ top_delta="0"
+ width="100">
+ <button.commit_callback
+ function="SocialSharing.Cancel" />
+ </button>
+ </layout_panel>
+ </layout_stack>
+ </panel>
diff --git a/indra/newview/skins/default/xui/en/panel_snapshot_options.xml b/indra/newview/skins/default/xui/en/panel_snapshot_options.xml
index 61c8c971c2..eff60f8228 100755
--- a/indra/newview/skins/default/xui/en/panel_snapshot_options.xml
+++ b/indra/newview/skins/default/xui/en/panel_snapshot_options.xml
@@ -81,4 +81,40 @@
<button.commit_callback
function="Snapshot.SaveToComputer" />
</button>
+ <text
+ font="SansSerif"
+ layout="topleft"
+ length="1"
+ follows="top|left"
+ height="16"
+ left="10"
+ name="send_to_facebook_textbox"
+ top_pad="10"
+ type="string">
+ Send to: [secondlife:/// Facebook]
+ </text>
+ <text
+ font="SansSerif"
+ layout="topleft"
+ length="1"
+ follows="top|left"
+ height="16"
+ left="140"
+ name="send_to_twitter_textbox"
+ top_pad="-16"
+ type="string">
+ [secondlife:/// Twitter]
+ </text>
+ <text
+ font="SansSerif"
+ layout="topleft"
+ length="1"
+ follows="top|left"
+ height="16"
+ left="190"
+ name="send_to_flickr_textbox"
+ top_pad="-16"
+ type="string">
+ [secondlife:/// Flickr]
+ </text>
</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_twitter_account.xml b/indra/newview/skins/default/xui/en/panel_twitter_account.xml
new file mode 100644
index 0000000000..4a413bd711
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_twitter_account.xml
@@ -0,0 +1,75 @@
+<panel
+ height="400"
+ width="304"
+ layout="topleft"
+ name="panel_twitter_account">
+ <string
+ name="twitter_connected"
+ value="You are connected to Twitter as:" />
+ <string
+ name="twitter_disconnected"
+ value="Not connected to Twitter" />
+ <text
+ layout="topleft"
+ length="1"
+ follows="top|left"
+ font="SansSerif"
+ height="16"
+ left="9"
+ name="account_caption_label"
+ top="21"
+ type="string">
+ Not connected to Twitter.
+ </text>
+ <text
+ layout="topleft"
+ top_pad="2"
+ length="1"
+ follows="top|left"
+ font="SansSerif"
+ height="16"
+ left="9"
+ name="account_name_label"
+ parse_urls="true"
+ type="string"/>
+ <panel
+ layout="topleft"
+ name="panel_buttons"
+ height="345"
+ left="9">
+ <button
+ layout="topleft"
+ follows="left|top"
+ top_pad="9"
+ visible="true"
+ height="23"
+ label="Connect..."
+ name="connect_btn"
+ width="210">
+ <commit_callback function="SocialSharing.Connect"/>
+ </button>
+
+ <button
+ layout="topleft"
+ follows="left|top"
+ top_delta="0"
+ height="23"
+ label="Disconnect"
+ name="disconnect_btn"
+ width="210"
+ visible="false">
+ <commit_callback function="SocialSharing.Disconnect"/>
+ </button>
+ <text
+ layout="topleft"
+ length="1"
+ follows="top|left"
+ height="16"
+ left="0"
+ name="account_learn_more_label"
+ top_pad="20"
+ type="string">
+ [http://community.secondlife.com/t5/English-Knowledge-Base/Second-Life-Share/ta-p/2149711 Learn about posting to Twitter]
+ </text>
+ </panel>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_twitter_photo.xml b/indra/newview/skins/default/xui/en/panel_twitter_photo.xml
new file mode 100644
index 0000000000..8e2412c84e
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_twitter_photo.xml
@@ -0,0 +1,156 @@
+ <panel
+ height="400"
+ width="304"
+ layout="topleft"
+ name="panel_twitter_photo">
+ <layout_stack
+ layout="topleft"
+ border_size="0"
+ height="392"
+ follows="all"
+ orientation="vertical"
+ name="stack_photo"
+ top="8">
+ <layout_panel
+ name="text_panel"
+ height="140">
+ <text
+ length="1"
+ follows="top|left|right"
+ font="SansSerif"
+ height="16"
+ left="9"
+ name="status_label"
+ top="3"
+ type="string">
+ What's happening?
+ </text>
+ <text_editor
+ follows="left|top"
+ height="87"
+ width="250"
+ left="9"
+ length="1"
+ max_length="140"
+ name="photo_status"
+ type="string"
+ word_wrap="true">
+ </text_editor>
+ <check_box
+ follows="left|top"
+ initial_value="true"
+ label="Include a photo"
+ name="add_photo_cb"
+ left="9"
+ height="16"
+ top_pad="10"/>
+ </layout_panel>
+ <layout_panel
+ name="snapshot_panel"
+ height="227">
+ <combo_box
+ control_name="SocialPhotoResolution"
+ follows="left|top"
+ top="6"
+ left="9"
+ name="resolution_combobox"
+ tool_tip="Image resolution"
+ height="21"
+ width="135">
+ <combo_box.item
+ label="Current Window"
+ name="CurrentWindow"
+ value="[i0,i0]" />
+ <combo_box.item
+ label="640x480"
+ name="640x480"
+ value="[i640,i480]" />
+ <combo_box.item
+ label="800x600"
+ name="800x600"
+ value="[i800,i600]" />
+ <combo_box.item
+ label="1024x768"
+ name="1024x768"
+ value="[i1024,i768]" />
+ </combo_box>
+ <text
+ follows="left|top"
+ font="SansSerifSmall"
+ height="14"
+ left="208"
+ length="1"
+ halign="right"
+ name="file_size_label"
+ top="9"
+ type="string"
+ width="50">
+ [SIZE] KB
+ </text>
+ <panel
+ height="150"
+ width="250"
+ visible="true"
+ name="thumbnail_placeholder"
+ top="33"
+ follows="left|top"
+ left="9">
+ </panel>
+ <button
+ follows="left|top"
+ height="23"
+ label="Refresh"
+ left="9"
+ top_pad="5"
+ name="new_snapshot_btn"
+ tool_tip="Click to refresh"
+ visible="true"
+ width="100" >
+ <button.commit_callback
+ function="SocialSharing.RefreshPhoto" />
+ </button>
+ <text
+ follows="left|top"
+ font="SansSerif"
+ text_color="EmphasisColor"
+ height="14"
+ top_pad="-19"
+ left_pad="-20"
+ length="1"
+ halign="center"
+ name="working_lbl"
+ translate="false"
+ type="string"
+ visible="true"
+ width="150">
+ Refreshing...
+ </text>
+ </layout_panel>
+ <layout_panel
+ name="photo_button_panel"
+ height="25">
+ <button
+ follows="left|top"
+ top="0"
+ left="9"
+ height="23"
+ label="Tweet"
+ name="post_photo_btn"
+ width="100">
+ <button.commit_callback
+ function="SocialSharing.SendPhoto" />
+ </button>
+ <button
+ follows="left|top"
+ height="23"
+ label="Cancel"
+ name="cancel_photo_btn"
+ left_pad="15"
+ top_delta="0"
+ width="100">
+ <button.commit_callback
+ function="SocialSharing.Cancel" />
+ </button>
+ </layout_panel>
+ </layout_stack>
+ </panel>
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index 8ac95beddb..b24cca588e 100755
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -148,13 +148,25 @@ Please try logging in again in a minute.</string>
<string name="SentToInvalidRegion">You were sent to an invalid region.</string>
<string name="TestingDisconnect">Testing viewer disconnect</string>
- <!-- Facebook Connect and, eventually, other Social Network -->
- <string name="SocialFacebookConnecting">Connecting to Facebook...</string>
- <string name="SocialFacebookPosting">Posting...</string>
- <string name="SocialFacebookDisconnecting">Disconnecting from Facebook...</string>
- <string name="SocialFacebookErrorConnecting">Problem connecting to Facebook</string>
- <string name="SocialFacebookErrorPosting">Problem posting to Facebook</string>
- <string name="SocialFacebookErrorDisconnecting">Problem disconnecting from Facebook</string>
+ <!-- SLShare: Facebook, Flickr, and Twitter -->
+ <string name="SocialFacebookConnecting">Connecting to Facebook...</string>
+ <string name="SocialFacebookPosting">Posting...</string>
+ <string name="SocialFacebookDisconnecting">Disconnecting from Facebook...</string>
+ <string name="SocialFacebookErrorConnecting">Problem connecting to Facebook</string>
+ <string name="SocialFacebookErrorPosting">Problem posting to Facebook</string>
+ <string name="SocialFacebookErrorDisconnecting">Problem disconnecting from Facebook</string>
+ <string name="SocialFlickrConnecting">Connecting to Flickr...</string>
+ <string name="SocialFlickrPosting">Posting...</string>
+ <string name="SocialFlickrDisconnecting">Disconnecting from Flickr...</string>
+ <string name="SocialFlickrErrorConnecting">Problem connecting to Flickr</string>
+ <string name="SocialFlickrErrorPosting">Problem posting to Flickr</string>
+ <string name="SocialFlickrErrorDisconnecting">Problem disconnecting from Flickr</string>
+ <string name="SocialTwitterConnecting">Connecting to Twitter...</string>
+ <string name="SocialTwitterPosting">Posting...</string>
+ <string name="SocialTwitterDisconnecting">Disconnecting from Twitter...</string>
+ <string name="SocialTwitterErrorConnecting">Problem connecting to Twitter</string>
+ <string name="SocialTwitterErrorPosting">Problem posting to Twitter</string>
+ <string name="SocialTwitterErrorDisconnecting">Problem disconnecting from Twitter</string>
<!-- Tooltip -->
<string name="TooltipPerson">Person</string><!-- Object under mouse pointer is an avatar -->
@@ -3442,6 +3454,12 @@ If you continue to receive this message, contact the [SUPPORT_SITE].
<string name="facebook_post_success">
You posted to Facebook.
</string>
+ <string name="flickr_post_success">
+ You posted to Flickr.
+ </string>
+ <string name="twitter_post_success">
+ You posted to Twitter.
+ </string>
<string name="no_session_message">
(IM Session Doesn't Exist)
@@ -3875,6 +3893,8 @@ Try enclosing path to the editor with double quotes.
<string name="Command_Search_Label">Search</string>
<string name="Command_Snapshot_Label">Snapshot</string>
<string name="Command_Social_Label">Facebook</string>
+ <string name="Command_Flickr_Label">Flickr</string>
+ <string name="Command_Twitter_Label">Twitter</string>
<string name="Command_Speak_Label">Speak</string>
<string name="Command_View_Label">Camera controls</string>
<string name="Command_Voice_Label">Voice settings</string>
@@ -3903,6 +3923,8 @@ Try enclosing path to the editor with double quotes.
<string name="Command_Search_Tooltip">Find places, events, people</string>
<string name="Command_Snapshot_Tooltip">Take a picture</string>
<string name="Command_Social_Tooltip">Post to Facebook</string>
+ <string name="Command_Flickr_Tooltip">Upload to Flickr</string>
+ <string name="Command_Twitter_Tooltip">Twitter</string>
<string name="Command_Speak_Tooltip">Speak with people nearby using your microphone</string>
<string name="Command_View_Tooltip">Changing camera angle</string>
<string name="Command_Voice_Tooltip">Volume controls for calls and people near you in world</string>