Commit 7b5aeadb authored by Jens Georg's avatar Jens Georg

plugins: Remove facebook publisher

It is broken and cannot be fixed.
parent 4137f29c
Pipeline #55037 passed with stage
in 11 minutes and 7 seconds
/* Copyright 2016 Software Freedom Conservancy Inc.
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
*/
using Shotwell;
using Shotwell.Plugins;
namespace Publishing.Authenticator.Shotwell.Facebook {
private const string APPLICATION_ID = "1612018629063184";
private class WebAuthenticationPane : Common.WebAuthenticationPane {
private static bool cache_dirty = false;
public signal void login_succeeded(string success_url);
public signal void login_failed();
public WebAuthenticationPane() {
Object (login_uri : get_login_url ());
}
private class LocaleLookup {
public string prefix;
public string translation;
public string? exception_code;
public string? exception_translation;
public string? exception_code_2;
public string? exception_translation_2;
public LocaleLookup(string prefix, string translation, string? exception_code = null,
string? exception_translation = null, string? exception_code_2 = null,
string? exception_translation_2 = null) {
this.prefix = prefix;
this.translation = translation;
this.exception_code = exception_code;
this.exception_translation = exception_translation;
this.exception_code_2 = exception_code_2;
this.exception_translation_2 = exception_translation_2;
}
}
private static LocaleLookup[] locale_lookup_table = {
new LocaleLookup( "es", "es-la", "ES", "es-es" ),
new LocaleLookup( "en", "en-gb", "US", "en-us" ),
new LocaleLookup( "fr", "fr-fr", "CA", "fr-ca" ),
new LocaleLookup( "pt", "pt-br", "PT", "pt-pt" ),
new LocaleLookup( "zh", "zh-cn", "HK", "zh-hk", "TW", "zh-tw" ),
new LocaleLookup( "af", "af-za" ),
new LocaleLookup( "ar", "ar-ar" ),
new LocaleLookup( "nb", "nb-no" ),
new LocaleLookup( "no", "nb-no" ),
new LocaleLookup( "id", "id-id" ),
new LocaleLookup( "ms", "ms-my" ),
new LocaleLookup( "ca", "ca-es" ),
new LocaleLookup( "cs", "cs-cz" ),
new LocaleLookup( "cy", "cy-gb" ),
new LocaleLookup( "da", "da-dk" ),
new LocaleLookup( "de", "de-de" ),
new LocaleLookup( "tl", "tl-ph" ),
new LocaleLookup( "ko", "ko-kr" ),
new LocaleLookup( "hr", "hr-hr" ),
new LocaleLookup( "it", "it-it" ),
new LocaleLookup( "lt", "lt-lt" ),
new LocaleLookup( "hu", "hu-hu" ),
new LocaleLookup( "nl", "nl-nl" ),
new LocaleLookup( "ja", "ja-jp" ),
new LocaleLookup( "nb", "nb-no" ),
new LocaleLookup( "no", "nb-no" ),
new LocaleLookup( "pl", "pl-pl" ),
new LocaleLookup( "ro", "ro-ro" ),
new LocaleLookup( "ru", "ru-ru" ),
new LocaleLookup( "sk", "sk-sk" ),
new LocaleLookup( "sl", "sl-si" ),
new LocaleLookup( "sv", "sv-se" ),
new LocaleLookup( "th", "th-th" ),
new LocaleLookup( "vi", "vi-vn" ),
new LocaleLookup( "tr", "tr-tr" ),
new LocaleLookup( "el", "el-gr" ),
new LocaleLookup( "bg", "bg-bg" ),
new LocaleLookup( "sr", "sr-rs" ),
new LocaleLookup( "he", "he-il" ),
new LocaleLookup( "hi", "hi-in" ),
new LocaleLookup( "bn", "bn-in" ),
new LocaleLookup( "pa", "pa-in" ),
new LocaleLookup( "ta", "ta-in" ),
new LocaleLookup( "te", "te-in" ),
new LocaleLookup( "ml", "ml-in" )
};
private static string get_system_locale_as_facebook_locale() {
unowned string? raw_system_locale = Intl.setlocale(LocaleCategory.ALL, "");
if (raw_system_locale == null || raw_system_locale == "")
return "www";
string system_locale = raw_system_locale.split(".")[0];
foreach (LocaleLookup locale_lookup in locale_lookup_table) {
if (!system_locale.has_prefix(locale_lookup.prefix))
continue;
if (locale_lookup.exception_code != null) {
assert(locale_lookup.exception_translation != null);
if (system_locale.contains(locale_lookup.exception_code))
return locale_lookup.exception_translation;
}
if (locale_lookup.exception_code_2 != null) {
assert(locale_lookup.exception_translation_2 != null);
if (system_locale.contains(locale_lookup.exception_code_2))
return locale_lookup.exception_translation_2;
}
return locale_lookup.translation;
}
// default
return "www";
}
private static string get_login_url() {
var facebook_locale = get_system_locale_as_facebook_locale();
return "https://%s.facebook.com/dialog/oauth?client_id=%s&redirect_uri=https://www.facebook.com/connect/login_success.html&display=popup&scope=publish_actions,user_photos,user_videos&response_type=token".printf(facebook_locale, APPLICATION_ID);
}
public override void on_page_load() {
string loaded_url = get_view ().uri.dup();
debug("loaded url: " + loaded_url);
// strip parameters from the loaded url
if (loaded_url.contains("?")) {
int index = loaded_url.index_of_char('?');
string params = loaded_url[index:loaded_url.length];
loaded_url = loaded_url.replace(params, "");
}
// were we redirected to the facebook login success page?
if (loaded_url.contains("login_success")) {
cache_dirty = true;
login_succeeded(get_view ().uri);
return;
}
// were we redirected to the login total failure page?
if (loaded_url.contains("login_failure")) {
login_failed();
return;
}
}
public static bool is_cache_dirty() {
return cache_dirty;
}
}
internal class Facebook : Spit.Publishing.Authenticator, GLib.Object {
private Spit.Publishing.PluginHost host;
private Publishing.Authenticator.Shotwell.Facebook.WebAuthenticationPane web_auth_pane = null;
private GLib.HashTable<string, Variant> params;
private const string SERVICE_WELCOME_MESSAGE =
_("You are not currently logged into Facebook.\n\nIf you don’t yet have a Facebook account, you can create one during the login process. During login, Shotwell Connect may ask you for permission to upload photos and publish to your feed. These permissions are required for Shotwell Connect to function.");
private const string RESTART_ERROR_MESSAGE =
_("You have already logged in and out of Facebook during this Shotwell session.\nTo continue publishing to Facebook, quit and restart Shotwell, then try publishing again.");
/* Interface functions */
public Facebook(Spit.Publishing.PluginHost host) {
this.host = host;
this.params = new GLib.HashTable<string, Variant>(str_hash, str_equal);
}
public void authenticate() {
// Do we have saved user credentials? If so, go ahead and authenticate the session
// with the saved credentials and proceed with the publishing interaction. Otherwise, show
// the Welcome pane
if (is_persistent_session_valid()) {
var access_token = get_persistent_access_token();
this.params.insert("AccessToken", new Variant.string(access_token));
this.authenticated();
return;
}
// FIXME: Find a way for a proper logout
if (WebAuthenticationPane.is_cache_dirty()) {
host.set_service_locked(false);
host.install_static_message_pane(RESTART_ERROR_MESSAGE,
Spit.Publishing.PluginHost.ButtonMode.CANCEL);
} else {
this.do_show_service_welcome_pane();
}
}
public bool can_logout() {
return true;
}
public GLib.HashTable<string, Variant> get_authentication_parameter() {
return this.params;
}
public void invalidate_persistent_session() {
debug("invalidating saved Facebook session.");
set_persistent_access_token("");
}
public void logout() {
invalidate_persistent_session();
}
public void refresh() {
// No-Op with Flickr
}
/* Private functions */
private bool is_persistent_session_valid() {
string? token = get_persistent_access_token();
if (token != null)
debug("existing Facebook session found in configuration database (access_token = %s).",
token);
else
debug("no existing Facebook session available.");
return token != null;
}
private string? get_persistent_access_token() {
return host.get_config_string("access_token", null);
}
private void set_persistent_access_token(string access_token) {
host.set_config_string("access_token", access_token);
}
private void do_show_service_welcome_pane() {
debug("ACTION: showing service welcome pane.");
host.install_welcome_pane(SERVICE_WELCOME_MESSAGE, on_login_clicked);
host.set_service_locked(false);
}
private void on_login_clicked() {
debug("EVENT: user clicked 'Login' on welcome pane.");
do_hosted_web_authentication();
}
private void do_hosted_web_authentication() {
debug("ACTION: doing hosted web authentication.");
this.host.set_service_locked(false);
this.web_auth_pane = new WebAuthenticationPane();
this.web_auth_pane.login_succeeded.connect(on_web_auth_pane_login_succeeded);
this.web_auth_pane.login_failed.connect(on_web_auth_pane_login_failed);
this.host.install_dialog_pane(this.web_auth_pane,
Spit.Publishing.PluginHost.ButtonMode.CANCEL);
}
private void on_web_auth_pane_login_succeeded(string success_url) {
debug("EVENT: hosted web login succeeded.");
do_authenticate_session(success_url);
}
private void on_web_auth_pane_login_failed() {
debug("EVENT: hosted web login failed.");
// In this case, "failed" doesn't mean that the user didn't enter the right username and
// password -- Facebook handles that case inside the Facebook Connect web control. Instead,
// it means that no session was initiated in response to our login request. The only
// way this happens is if the user clicks the "Cancel" button that appears inside
// the web control. In this case, the correct behavior is to return the user to the
// service welcome pane so that they can start the web interaction again.
do_show_service_welcome_pane();
}
private void do_authenticate_session(string good_login_uri) {
debug("ACTION: preparing to extract session information encoded in uri = '%s'",
good_login_uri);
// the raw uri is percent-encoded, so decode it
string decoded_uri = Soup.URI.decode(good_login_uri);
// locate the access token within the URI
string? access_token = null;
int index = decoded_uri.index_of("#access_token=");
if (index >= 0)
access_token = decoded_uri[index:decoded_uri.length];
if (access_token == null) {
host.post_error(new Spit.Publishing.PublishingError.MALFORMED_RESPONSE(
"Server redirect URL contained no access token"));
return;
}
// remove any trailing parameters from the session description string
string? trailing_params = null;
index = access_token.index_of_char('&');
if (index >= 0)
trailing_params = access_token[index:access_token.length];
if (trailing_params != null)
access_token = access_token.replace(trailing_params, "");
// remove the key from the session description string
access_token = access_token.replace("#access_token=", "");
this.params.insert("AccessToken", new Variant.string(access_token));
set_persistent_access_token(access_token);
this.authenticated();
}
}
} // namespace Publishing.Facebook;
......@@ -26,8 +26,6 @@ namespace Publishing.Authenticator {
switch (provider) {
case "flickr":
return new Shotwell.Flickr.Flickr(host);
case "facebook":
return new Shotwell.Facebook.Facebook(host);
case "youtube":
return new Shotwell.Google.Google("https://gdata.youtube.com/", _("You are not currently logged into YouTube.\n\nYou must have already signed up for a Google account and set it up for use with YouTube to continue. You can set up most accounts by using your browser to log into the YouTube site at least once."), host);
case "tumblr":
......
authenticator_shotwell_sources = [
'ShotwellAuthenticatorFactory.vala',
'FacebookPublishingAuthenticator.vala',
'FlickrPublishingAuthenticator.vala',
'GoogleAuthenticator.vala',
'OAuth1Authenticator.vala',
......
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface domain="shotwell">
<requires lib="gtk+" version="3.14"/>
<object class="GtkBox" id="facebook_pane_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="homogeneous">True</property>
<child>
<object class="GtkBox" id="facebook_pane_inner_box">
<property name="width_request">1</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">16</property>
<child>
<object class="GtkLabel" id="how_to_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">16</property>
<property name="margin_right">16</property>
<property name="margin_top">16</property>
<property name="margin_bottom">16</property>
<property name="label"> (text depends on fb username and is modified in the app -
anything put into this field won't display)</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="grid1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="row_spacing">8</property>
<property name="column_spacing">32</property>
<property name="column_homogeneous">True</property>
<child>
<object class="GtkRadioButton" id="use_existing_radio">
<property name="label" translatable="yes">Publish to an e_xisting album:</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="halign">start</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<property name="group">create_new_radio</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="create_new_radio">
<property name="label" translatable="yes">Create a _new album named:</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="halign">start</property>
<property name="margin_bottom">8</property>
<property name="use_underline">True</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="new_album_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="margin_bottom">8</property>
<property name="invisible_char"></property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="existing_albums_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="size_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Upload _size:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">resolution_combo</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="resolution_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="visibility_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Videos and new photo albums _visible to:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">visibility_combo</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="visibility_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">4</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="strip_metadata_check">
<property name="label" translatable="yes">_Remove location, camera, and other identifying information before uploading</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="halign">start</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">32</property>
<property name="homogeneous">True</property>
<child>
<object class="GtkButton" id="logout_button">
<property name="label" translatable="yes">_Logout</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">80</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="publish_button">
<property name="label" translatable="yes">_Publish</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">80</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">2</property>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="padding">8</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
</object>
</interface>
shotwell_publishing_sources = [
'shotwell-publishing.vala',
'FacebookPublishing.vala',
'FlickrPublishing.vala',
'TumblrPublishing.vala',
'YouTubePublishing.vala',
......
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/gnome/Shotwell/Publishing">
<file>facebook.png</file>
<file>piwigo.png</file>
<file>youtube.png</file>
<file>tumblr.png</file>
<file>google-photos.svg</file>
<file>facebook_publishing_options_pane.ui</file>
<file>flickr_publishing_options_pane.ui</file>
<file>google_photos_publishing_options_pane.ui</file>
<file>piwigo_authentication_pane.ui</file>
......
......@@ -22,12 +22,6 @@ private class ShotwellPublishingCoreServices : Object, Spit.Module {
debug("Looking for resources in %s", resource_directory.get_path());
debug("Found %d authenicators", authenicators.size);
#if HAVE_FACEBOOK
if (authenicators.contains("facebook")) {
pluggables += new FacebookService(resource_directory);
}
#endif
#if HAVE_GOOGLEPHOTOS
if (authenicators.contains("google-photos")) {
pluggables += new Publishing.GooglePhotos.Service(resource_directory);
......
......@@ -9,7 +9,7 @@
*
* The Shotwell Pluggable Publishing API allows you to write plugins that upload
* photos and videos to web services. The Shotwell distribution includes publishing
* support for four core services: Facebook, Flickr, Picasa Web Albums, and YouTube.
* support for three core services: Flickr, Google Photos, and YouTube.
* To enable Shotwell to connect to additional services, developers like you write
* publishing plugins, dynamically-loadable shared objects that are linked into the
* Shotwell process at runtime. Publishing plugins are just one of several kinds of
......
......@@ -23,9 +23,6 @@ public class StandardHostInterface : Object, Spit.HostInterface {
// special case: legacy plugins (Web publishers moved into SPIT) have special names
// new plugins will use their full ID
switch (id) {
case "org.yorba.shotwell.publishing.facebook":
return "facebook";
case "org.yorba.shotwell.publishing.flickr":
return "flickr";
......
......@@ -8,7 +8,6 @@ namespace Publishing {
public void init() throws Error {
string[] core_ids = new string[0];
core_ids += "org.yorba.shotwell.publishing.facebook";
core_ids += "org.yorba.shotwell.publishing.flickr";
core_ids += "org.yorba.shotwell.publishing.youtube";
core_ids += "org.yorba.shotwell.publishing.gnome-photos";
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment