Merge lp:~ken-vandine/shotwell/no_unity_no_uoa into lp:~ubuntu-desktop/shotwell/ubuntu

Proposed by Ken VanDine
Status: Work in progress
Proposed branch: lp:~ken-vandine/shotwell/no_unity_no_uoa
Merge into: lp:~ubuntu-desktop/shotwell/ubuntu
Diff against target: 2581 lines (+17/-2501)
6 files modified
debian/changelog (+13/-0)
debian/control (+1/-5)
debian/patches/06_uoa.patch (+0/-2492)
debian/patches/series (+0/-1)
debian/rules (+2/-2)
debian/shotwell-dbg.README.Debian (+1/-1)
To merge this branch: bzr merge lp:~ken-vandine/shotwell/no_unity_no_uoa
Reviewer Review Type Date Requested Status
Ubuntu Desktop Pending
Review via email: mp+323461@code.launchpad.net

Description of the change

  * Dropped UOA integration
    -debian/06_uoa.patch
  * debian/control
    - Removed libsignon-glib-dev and libaccounts-glib-dev build depends
    - Use the latest standards version, 3.9.8
  * debian/rules
    - Don't build with unity support

To post a comment you must log in.
188. By Ken VanDine

Removed libunity-dev, libsignon-glib-dev and libaccounts-glib-dev build
depends

Unmerged revisions

188. By Ken VanDine

Removed libunity-dev, libsignon-glib-dev and libaccounts-glib-dev build
depends

187. By Ken VanDine

fixed spelling

186. By Ken VanDine

* debian/rules
  - Don't build with unity support

185. By Ken VanDine

* Dropped UOA integration
  -debian/06_uoa.patch
* debian/control
  - Removed libsignon-glib-dev and libaccounts-glib-dev build depends
  - Use the latest standards version, 3.9.8

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/changelog'
2--- debian/changelog 2017-05-01 14:34:16 +0000
3+++ debian/changelog 2017-05-03 18:07:14 +0000
4@@ -1,3 +1,16 @@
5+shotwell (0.22.0+git20160108.r1.f2fb1f7-0ubuntu4) UNRELEASED; urgency=medium
6+
7+ * Dropped UOA integration
8+ -debian/06_uoa.patch
9+ * debian/control
10+ - Removed libunity-dev, libsignon-glib-dev and libaccounts-glib-dev build
11+ depends
12+ - Use the latest standards version, 3.9.8
13+ * debian/rules
14+ - Don't build with unity support
15+
16+ -- Ken VanDine <ken.vandine@canonical.com> Mon, 01 May 2017 10:36:51 -0400
17+
18 shotwell (0.22.0+git20160108.r1.f2fb1f7-0ubuntu3) zesty; urgency=medium
19
20 * No-change rebuild against latest libraw
21
22=== modified file 'debian/control'
23--- debian/control 2017-05-01 14:34:16 +0000
24+++ debian/control 2017-05-03 18:07:14 +0000
25@@ -25,12 +25,9 @@
26 libxml2 (>= 2.6.32),
27 m4,
28 valac (>= 0.22.0),
29- libaccounts-glib-dev,
30- libsignon-glib-dev (>= 1.12),
31- libunity-dev,
32 python-scour,
33 intltool
34-Standards-Version: 3.9.6
35+Standards-Version: 3.9.8
36 Homepage: https://wiki.gnome.org/Apps/Shotwell
37 Vcs-Browser: http://anonscm.debian.org/cgit/collab-maint/shotwell.git/
38 Vcs-Git: git://anonscm.debian.org/collab-maint/shotwell.git
39@@ -44,7 +41,6 @@
40 dconf-cli,
41 default-dbus-session-bus | dbus-session-bus,
42 librsvg2-common
43-Suggests: account-plugin-facebook, account-plugin-google, account-plugin-flickr, unity-control-center-signon
44 Description: digital photo organizer
45 Shotwell is a digital photo organizer designed for the GNOME desktop
46 environment. It allows you to import photos from disk or camera, organize
47
48=== removed file 'debian/patches/06_uoa.patch'
49--- debian/patches/06_uoa.patch 2017-05-01 14:34:16 +0000
50+++ debian/patches/06_uoa.patch 1970-01-01 00:00:00 +0000
51@@ -1,2492 +0,0 @@
52-Description: Use Ubuntu Online Accounts for Picasa, Facebook and Flickr export plugins.
53-Author: Alberto Mardegan <alberto.mardegan@canonical.com>
54-
55----
56-Index: b/Makefile
57-===================================================================
58---- a/Makefile
59-+++ b/Makefile
60-@@ -161,7 +161,11 @@
61-
62- SYS_INTEGRATION_FILES = \
63- shotwell.appdata.xml \
64-+ shotwell.application \
65- shotwell.desktop.head \
66-+ shotwell-sharing-facebook.service \
67-+ shotwell-sharing-flickr.service \
68-+ shotwell-sharing-picasa.service \
69- shotwell-viewer.desktop.head \
70- org.yorba.shotwell.gschema.xml \
71- org.yorba.shotwell-extras.gschema.xml \
72-@@ -252,6 +256,7 @@
73- EXT_PKGS = \
74- atk \
75- gdk-3.0 \
76-+ gdk-x11-3.0 \
77- gee-0.8 \
78- gexiv2 \
79- gio-unix-2.0 \
80-@@ -513,6 +518,12 @@
81- mkdir -p $(DESTDIR)$(LIBEXECDIR)
82- $(INSTALL_PROGRAM) $(THUMBNAILER_BIN) $(DESTDIR)$(LIBEXECDIR)
83- $(INSTALL_PROGRAM) $(MIGRATOR_BIN) $(DESTDIR)$(LIBEXECDIR)
84-+ mkdir -p $(DESTDIR)$(PREFIX)/share/accounts/applications
85-+ $(INSTALL_DATA) misc/shotwell.application $(DESTDIR)$(PREFIX)/share/accounts/applications
86-+ mkdir -p $(DESTDIR)$(PREFIX)/share/accounts/services
87-+ $(INSTALL_DATA) misc/shotwell-sharing-facebook.service $(DESTDIR)$(PREFIX)/share/accounts/services
88-+ $(INSTALL_DATA) misc/shotwell-sharing-flickr.service $(DESTDIR)$(PREFIX)/share/accounts/services
89-+ $(INSTALL_DATA) misc/shotwell-sharing-picasa.service $(DESTDIR)$(PREFIX)/share/accounts/services
90- mkdir -p $(DESTDIR)$(PREFIX)/share/shotwell/icons
91- $(INSTALL_DATA) icons/* $(DESTDIR)$(PREFIX)/share/shotwell/icons
92- mkdir -p $(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps
93-Index: b/misc/shotwell.application
94-===================================================================
95---- /dev/null
96-+++ b/misc/shotwell.application
97-@@ -0,0 +1,19 @@
98-+<?xml version="1.0" encoding="UTF-8" ?>
99-+<application id="shotwell">
100-+ <description>Shotwell</description>
101-+ <desktop-entry>shotwell.desktop</desktop-entry>
102-+ <translations>shotwell</translations>
103-+
104-+ <services>
105-+ <service id="shotwell-sharing-picasa">
106-+ <description>Publish your pictures to Picasa</description>
107-+ </service>
108-+ <service id="shotwell-sharing-facebook">
109-+ <description>Publish your pictures to Facebook</description>
110-+ </service>
111-+ <service id="shotwell-sharing-flickr">
112-+ <description>Publish your pictures to Flickr</description>
113-+ </service>
114-+ </services>
115-+
116-+</application>
117-Index: b/plugins/Makefile
118-===================================================================
119---- a/plugins/Makefile
120-+++ b/plugins/Makefile
121-@@ -4,7 +4,10 @@
122- DIST_FILES := \
123- Makefile \
124- Makefile.plugin.mk \
125-- plugins.mk
126-+ plugins.mk \
127-+ Accounts.vapi \
128-+ Accounts.deps \
129-+ Signon.vapi
130-
131- .PHONY: all
132- all: $(ALL_PLUGINS)
133-Index: b/plugins/Makefile.plugin.mk
134-===================================================================
135---- a/plugins/Makefile.plugin.mk
136-+++ b/plugins/Makefile.plugin.mk
137-@@ -27,6 +27,8 @@
138- # automatically include the shotwell-plugin-dev-1.0 package as a local dependency
139- EXT_PKGS := $(PKGS)
140- PKGS := shotwell-plugin-dev-1.0 $(PKGS) $(PLUGIN_PKGS)
141-+EXT_PKGS := libsignon-glib libaccounts-glib $(EXT_PKGS)
142-+PKGS := signon libaccounts-glib $(PKGS)
143-
144- # automatically include the Resources.vala common file
145- SRC_FILES := ../common/Resources.vala $(SRC_FILES)
146-Index: b/plugins/common/RESTSupport.vala
147-===================================================================
148---- a/plugins/common/RESTSupport.vala
149-+++ b/plugins/common/RESTSupport.vala
150-@@ -312,7 +312,7 @@
151- old_url = message.get_uri().to_string(false);
152- url_with_query = get_endpoint_url() + "?" + formdata_string;
153- message.set_uri(new Soup.URI(url_with_query));
154-- } else {
155-+ } else if (get_method() == HttpMethod.POST) {
156- message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY,
157- formdata_string.data);
158- }
159-@@ -875,6 +875,21 @@
160- do_get_access_tokens(auth_code);
161- }
162-
163-+ protected void on_authenticator_authenticated(owned Variant session_data) {
164-+ host.install_account_fetch_wait_pane();
165-+
166-+ Variant? v_token = session_data.lookup_value("AccessToken", null);
167-+ if (v_token != null) {
168-+ string token = v_token.get_string();
169-+ debug("OAuth Access Token: %s", token);
170-+ session.access_token = token;
171-+
172-+ do_fetch_username();
173-+ } else {
174-+ debug("Access token not present!");
175-+ }
176-+ }
177-+
178- private void on_get_access_tokens_complete(Publishing.RESTSupport.Transaction txn) {
179- txn.completed.disconnect(on_get_access_tokens_complete);
180- txn.network_error.disconnect(on_get_access_tokens_error);
181-Index: b/plugins/common/accounts.vala
182-===================================================================
183---- /dev/null
184-+++ b/plugins/common/accounts.vala
185-@@ -0,0 +1,238 @@
186-+/* Copyright 2009-2011 Yorba Foundation
187-+ *
188-+ * This software is licensed under the GNU Lesser General Public License
189-+ * (version 2.1 or later). See the COPYING file in this distribution.
190-+ */
191-+
192-+namespace Publishing.Accounts {
193-+
194-+public class SharingAccount {
195-+ private Ag.AccountService account_service = null;
196-+
197-+ public SharingAccount(Ag.AccountService account_service) {
198-+ this.account_service = account_service;
199-+ }
200-+
201-+ public Signon.AuthSession create_auth_session() throws GLib.Error {
202-+ var auth_data = account_service.get_auth_data();
203-+ debug("Signon-id: %u", auth_data.get_credentials_id());
204-+
205-+ return new Signon.AuthSession(auth_data.get_credentials_id(),
206-+ auth_data.get_method());
207-+ }
208-+
209-+ public Variant get_session_parameters(Variant? extra, out string mechanism) {
210-+ var auth_data = account_service.get_auth_data();
211-+ mechanism = auth_data.get_mechanism();
212-+ Variant data = auth_data.get_login_parameters(extra);
213-+ return data;
214-+ }
215-+}
216-+
217-+public class SharingAccounts {
218-+ private static Ag.Manager manager = null;
219-+ private string provider_name;
220-+ private Ag.AccountService[] all_accounts;
221-+
222-+ public SharingAccounts(string provider_name) {
223-+ if (manager == null) {
224-+ manager = new Ag.Manager.for_service_type("shotwell-sharing");
225-+ }
226-+ manager.enabled_event.connect(on_account_enabled);
227-+
228-+ this.provider_name = provider_name;
229-+ all_accounts = get_accounts();
230-+ }
231-+
232-+ private void on_account_enabled(uint account_id) {
233-+ /* To keep the implementation simple, just rebuild the account
234-+ * list from scratch */
235-+ all_accounts = get_accounts();
236-+ }
237-+
238-+ private Ag.AccountService[] get_accounts() {
239-+ GLib.List<Ag.AccountService> accounts =
240-+ manager.get_enabled_account_services();
241-+
242-+ Ag.AccountService[] list = {};
243-+
244-+ foreach (Ag.AccountService account_service in accounts) {
245-+ Ag.Account account = account_service.get_account();
246-+ if (account.get_provider_name() == provider_name) {
247-+ list += account_service;
248-+ }
249-+ }
250-+ return list;
251-+ }
252-+
253-+ public bool has_enabled_accounts() {
254-+ return all_accounts.length > 0;
255-+ }
256-+
257-+ public string[] list_account_names() {
258-+ string[] names = {};
259-+ foreach (Ag.AccountService account_service in all_accounts) {
260-+ names += account_service.get_account().get_display_name();
261-+ }
262-+ return names;
263-+ }
264-+
265-+ public SharingAccount? find_account(string? account_name) {
266-+ foreach (Ag.AccountService account_service in all_accounts)
267-+ if (account_service.get_account().get_display_name() == account_name) {
268-+ return new SharingAccount(account_service);
269-+ }
270-+
271-+ return null;
272-+ }
273-+}
274-+
275-+public abstract class UOAPublishingService : Object, Spit.Pluggable, Spit.Publishing.Service {
276-+ private SharingAccounts account_manager;
277-+
278-+ public UOAPublishingService(string provider_name) {
279-+ account_manager = new SharingAccounts(provider_name);
280-+ }
281-+
282-+ public virtual int get_pluggable_interface(int min_host_interface, int max_host_interface) {
283-+ return Spit.negotiate_interfaces(min_host_interface, max_host_interface,
284-+ Spit.Publishing.CURRENT_INTERFACE);
285-+ }
286-+
287-+ public abstract unowned string get_id();
288-+
289-+ public abstract unowned string get_pluggable_name();
290-+
291-+ public abstract void get_info(ref Spit.PluggableInfo info);
292-+
293-+ public virtual void activation(bool enabled) {
294-+ }
295-+
296-+ public abstract Spit.Publishing.Publisher create_publisher(string? account_name, Spit.Publishing.PluginHost host);
297-+
298-+ public abstract Spit.Publishing.Publisher.MediaType get_supported_media();
299-+
300-+ public SharingAccount? find_account(string? account_name) {
301-+ return account_manager.find_account(account_name);
302-+ }
303-+
304-+ public bool is_enabled() {
305-+ return account_manager.has_enabled_accounts();
306-+ }
307-+
308-+ public string[] list_account_names() {
309-+ return account_manager.list_account_names();
310-+ }
311-+}
312-+
313-+public class UOAPublisherAuthenticator : Object {
314-+ private weak Spit.Publishing.PluginHost host = null;
315-+ private Signon.AuthSession auth_session = null;
316-+ private SharingAccount account = null;
317-+ private bool firstLoginAttempt = true;
318-+ private bool invalidate_session = false;
319-+ private string welcome_message = null;
320-+
321-+ public UOAPublisherAuthenticator(SharingAccount account,
322-+ Spit.Publishing.PluginHost host,
323-+ string welcome_message)
324-+ {
325-+ this.host = host;
326-+ this.account = account;
327-+ this.welcome_message = welcome_message;
328-+ }
329-+
330-+ public signal void authenticated(owned Variant session_data);
331-+
332-+ public void authenticate() {
333-+ debug("ACTION: authentication requested.");
334-+
335-+ do_authentication();
336-+ }
337-+
338-+ public Variant? get_authentication_data() {
339-+ if (account == null) return null;
340-+ string mechanism = null;
341-+ return account.get_session_parameters(null, out mechanism);
342-+ }
343-+
344-+ public void invalidate_persistent_session() {
345-+ this.invalidate_session = true;
346-+ }
347-+
348-+ private void do_authentication() {
349-+ debug("ACTION: authenticating.");
350-+
351-+ if (account != null) {
352-+ try {
353-+ auth_session = account.create_auth_session();
354-+ } catch (GLib.Error e) {
355-+ warning("EVENT: couldn't create session for account: %s",
356-+ e.message);
357-+ }
358-+ }
359-+
360-+ VariantBuilder builder = new VariantBuilder(new VariantType("a{sv}"));
361-+
362-+ if (firstLoginAttempt) {
363-+ firstLoginAttempt = false;
364-+ builder.add ("{sv}", "UiPolicy",
365-+ new Variant.int32((int)Signon.SessionDataUiPolicy.NO_USER_INTERACTION));
366-+ } else {
367-+ var windowId = host.get_dialog_xid();
368-+ if (windowId != 0) {
369-+ builder.add ("{sv}", "WindowId",
370-+ new Variant.uint32((uint)windowId));
371-+ }
372-+ }
373-+
374-+ if (invalidate_session) {
375-+ invalidate_session = false;
376-+ builder.add("{sv}", "ForceTokenRefresh", new Variant.boolean(true));
377-+ }
378-+
379-+ string mechanism = null;
380-+ Variant data = account.get_session_parameters(builder.end(), out mechanism);
381-+ if (data == null) {
382-+ warning ("No account authentication data");
383-+ host.post_error(new Spit.Publishing.PublishingError.SERVICE_ERROR(
384-+ "Error while accessing the account"));
385-+ return;
386-+ }
387-+
388-+ debug("Got account data");
389-+
390-+ host.set_service_locked(true);
391-+ auth_session.process_async.begin(data, mechanism, null,
392-+ (obj, res) => {
393-+ host.set_service_locked(false);
394-+ try {
395-+ Variant reply = auth_session.process_async.end(res);
396-+ authenticated(reply);
397-+ } catch (GLib.Error error) {
398-+ debug("got error: %s", error.message);
399-+ if (error is Signon.Error.USER_INTERACTION) {
400-+ debug("User interaction!");
401-+ do_show_service_welcome_pane();
402-+ } else {
403-+ host.post_error(new Spit.Publishing.PublishingError.SERVICE_ERROR("Authentication failed"));
404-+ }
405-+ }
406-+ });
407-+ }
408-+
409-+ private void do_show_service_welcome_pane() {
410-+ debug("ACTION: showing service welcome pane.");
411-+
412-+ host.install_welcome_pane(welcome_message, on_service_welcome_login);
413-+ }
414-+
415-+ private void on_service_welcome_login() {
416-+ debug("EVENT: user clicked 'Login' in welcome pane.");
417-+
418-+ do_authentication();
419-+ }
420-+}
421-+
422-+}
423-+
424-Index: b/plugins/shotwell-publishing-extras/TumblrPublishing.vala
425-===================================================================
426---- a/plugins/shotwell-publishing-extras/TumblrPublishing.vala
427-+++ b/plugins/shotwell-publishing-extras/TumblrPublishing.vala
428-@@ -45,7 +45,7 @@
429- public void activation(bool enabled) {
430- }
431-
432-- public Spit.Publishing.Publisher create_publisher(Spit.Publishing.PluginHost host) {
433-+ public Spit.Publishing.Publisher create_publisher(string? account, Spit.Publishing.PluginHost host) {
434- return new Publishing.Tumblr.TumblrPublisher(this, host);
435- }
436-
437-Index: b/plugins/shotwell-publishing-extras/YandexPublishing.vala
438-===================================================================
439---- a/plugins/shotwell-publishing-extras/YandexPublishing.vala
440-+++ b/plugins/shotwell-publishing-extras/YandexPublishing.vala
441-@@ -28,7 +28,7 @@
442- info.license = Resources.LICENSE;
443- }
444-
445-- public Spit.Publishing.Publisher create_publisher(Spit.Publishing.PluginHost host) {
446-+ public Spit.Publishing.Publisher create_publisher(string? account, Spit.Publishing.PluginHost host) {
447- return new Publishing.Yandex.YandexPublisher(this, host);
448- }
449-
450-Index: b/plugins/shotwell-publishing/FacebookPublishing.vala
451-===================================================================
452---- a/plugins/shotwell-publishing/FacebookPublishing.vala
453-+++ b/plugins/shotwell-publishing/FacebookPublishing.vala
454-@@ -4,30 +4,28 @@
455- * (version 2.1 or later). See the COPYING file in this distribution.
456- */
457-
458--public class FacebookService : Object, Spit.Pluggable, Spit.Publishing.Service {
459-+using Publishing.Accounts;
460-+
461-+public class FacebookService : UOAPublishingService {
462- private const string ICON_FILENAME = "facebook.png";
463-
464- private static Gdk.Pixbuf[] icon_pixbuf_set = null;
465-
466- public FacebookService(GLib.File resource_directory) {
467-+ base("facebook");
468- if (icon_pixbuf_set == null)
469- icon_pixbuf_set = Resources.load_icon_set(resource_directory.get_child(ICON_FILENAME));
470- }
471-
472-- public int get_pluggable_interface(int min_host_interface, int max_host_interface) {
473-- return Spit.negotiate_interfaces(min_host_interface, max_host_interface,
474-- Spit.Publishing.CURRENT_INTERFACE);
475-- }
476--
477-- public unowned string get_id() {
478-+ public override unowned string get_id() {
479- return "org.yorba.shotwell.publishing.facebook";
480- }
481-
482-- public unowned string get_pluggable_name() {
483-+ public override unowned string get_pluggable_name() {
484- return "Facebook";
485- }
486-
487-- public void get_info(ref Spit.PluggableInfo info) {
488-+ public override void get_info(ref Spit.PluggableInfo info) {
489- info.authors = "Lucas Beeler";
490- info.copyright = _("Copyright 2009-2015 Yorba Foundation");
491- info.translators = Resources.TRANSLATORS;
492-@@ -39,14 +37,12 @@
493- info.icons = icon_pixbuf_set;
494- }
495-
496-- public void activation(bool enabled) {
497-- }
498--
499-- public Spit.Publishing.Publisher create_publisher(Spit.Publishing.PluginHost host) {
500-- return new Publishing.Facebook.FacebookPublisher(this, host);
501-+ public override Spit.Publishing.Publisher create_publisher(string? account_name, Spit.Publishing.PluginHost host) {
502-+ SharingAccount account = find_account(account_name);
503-+ return new Publishing.Facebook.FacebookPublisher(this, account, host);
504- }
505-
506-- public Spit.Publishing.Publisher.MediaType get_supported_media() {
507-+ public override Spit.Publishing.Publisher.MediaType get_supported_media() {
508- return (Spit.Publishing.Publisher.MediaType.PHOTO |
509- Spit.Publishing.Publisher.MediaType.VIDEO);
510- }
511-@@ -57,12 +53,9 @@
512- // truly, deep-down know what you're doing)
513- public const string SERVICE_NAME = "facebook";
514- internal const string USER_VISIBLE_NAME = "Facebook";
515--internal const string APPLICATION_ID = "162702932093";
516- internal const string DEFAULT_ALBUM_NAME = _("Shotwell Connect");
517- internal const string SERVICE_WELCOME_MESSAGE =
518- _("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.");
519--internal const string RESTART_ERROR_MESSAGE =
520-- _("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.");
521- internal const string USER_AGENT = "Java/1.6.0_16";
522- internal const int EXPIRED_SESSION_STATUS_CODE = 400;
523-
524-@@ -173,7 +166,6 @@
525- public class FacebookPublisher : Spit.Publishing.Publisher, GLib.Object {
526- private PublishingParameters publishing_params;
527- private weak Spit.Publishing.PluginHost host = null;
528-- private WebAuthenticationPane web_auth_pane = null;
529- private Spit.Publishing.ProgressCallback progress_reporter = null;
530- private weak Spit.Publishing.Service service = null;
531- private bool running = false;
532-@@ -182,8 +174,10 @@
533- private Uploader? uploader = null;
534- private string? uid = null;
535- private string? username = null;
536-+ private UOAPublisherAuthenticator authenticator = null;
537-
538- public FacebookPublisher(Spit.Publishing.Service service,
539-+ SharingAccount account,
540- Spit.Publishing.PluginHost host) {
541- debug("FacebookPublisher instantiated.");
542-
543-@@ -194,32 +188,16 @@
544-
545- this.graph_session = new GraphSession();
546- graph_session.authenticated.connect(on_session_authenticated);
547-- }
548--
549-- private bool is_persistent_session_valid() {
550-- string? token = get_persistent_access_token();
551-
552-- if (token != null)
553-- debug("existing Facebook session found in configuration database (access_token = %s).",
554-- token);
555-- else
556-- debug("no existing Facebook session available.");
557--
558-- return token != null;
559-+ authenticator = new UOAPublisherAuthenticator(account, host,
560-+ SERVICE_WELCOME_MESSAGE);
561-+ authenticator.authenticated.connect(on_authenticator_authenticated);
562- }
563-
564-- private string? get_persistent_access_token() {
565-- return host.get_config_string("access_token", null);
566-- }
567--
568- private bool get_persistent_strip_metadata() {
569- return host.get_config_bool("strip_metadata", false);
570- }
571-
572-- private void set_persistent_access_token(string access_token) {
573-- host.set_config_string("access_token", access_token);
574-- }
575--
576- private void set_persistent_strip_metadata(bool strip_metadata) {
577- host.set_config_bool("strip_metadata", strip_metadata);
578- }
579-@@ -237,29 +215,9 @@
580- private void invalidate_persistent_session() {
581- debug("invalidating saved Facebook session.");
582-
583-- set_persistent_access_token("");
584-+ authenticator.invalidate_persistent_session();
585- }
586-
587-- private void do_show_service_welcome_pane() {
588-- debug("ACTION: showing service welcome pane.");
589--
590-- host.install_welcome_pane(SERVICE_WELCOME_MESSAGE, on_login_clicked);
591-- host.set_service_locked(false);
592-- }
593--
594-- private void do_test_connection_to_endpoint() {
595-- debug("ACTION: testing connection to Facebook endpoint.");
596-- host.set_service_locked(true);
597--
598-- host.install_static_message_pane(_("Testing connection to Facebook..."));
599--
600-- GraphMessage endpoint_test_message = graph_session.new_endpoint_test();
601-- endpoint_test_message.completed.connect(on_endpoint_test_completed);
602-- endpoint_test_message.failed.connect(on_endpoint_test_error);
603--
604-- graph_session.send_message(endpoint_test_message);
605-- }
606--
607- private void do_fetch_user_info() {
608- debug("ACTION: fetching user information.");
609-
610-@@ -377,7 +335,6 @@
611-
612- publishing_options_pane = new PublishingOptionsPane(username, publishing_params.albums,
613- host.get_publishable_media_type(), this, builder, get_persistent_strip_metadata());
614-- publishing_options_pane.logout.connect(on_publishing_options_pane_logout);
615- publishing_options_pane.publish.connect(on_publishing_options_pane_publish);
616- host.install_dialog_pane(publishing_options_pane,
617- Spit.Publishing.PluginHost.ButtonMode.CANCEL);
618-@@ -411,60 +368,14 @@
619- do_upload();
620- }
621-
622-- private void do_hosted_web_authentication() {
623-- debug("ACTION: doing hosted web authentication.");
624--
625-- host.set_service_locked(false);
626--
627-- web_auth_pane = new WebAuthenticationPane();
628-- web_auth_pane.login_succeeded.connect(on_web_auth_pane_login_succeeded);
629-- web_auth_pane.login_failed.connect(on_web_auth_pane_login_failed);
630--
631-- host.install_dialog_pane(web_auth_pane,
632-- Spit.Publishing.PluginHost.ButtonMode.CANCEL);
633--
634-- }
635--
636-- private void do_authenticate_session(string good_login_uri) {
637-- debug("ACTION: preparing to extract session information encoded in uri = '%s'",
638-- good_login_uri);
639--
640-- // the raw uri is percent-encoded, so decode it
641-- string decoded_uri = Soup.URI.decode(good_login_uri);
642--
643-- // locate the access token within the URI
644-- string? access_token = null;
645-- int index = decoded_uri.index_of("#access_token=");
646-- if (index >= 0)
647-- access_token = decoded_uri[index:decoded_uri.length];
648-- if (access_token == null) {
649-- host.post_error(new Spit.Publishing.PublishingError.MALFORMED_RESPONSE(
650-- "Server redirect URL contained no access token"));
651-- return;
652-- }
653--
654-- // remove any trailing parameters from the session description string
655-- string? trailing_params = null;
656-- index = access_token.index_of_char('&');
657-- if (index >= 0)
658-- trailing_params = access_token[index:access_token.length];
659-- if (trailing_params != null)
660-- access_token = access_token.replace(trailing_params, "");
661--
662-- // remove the key from the session description string
663-- access_token = access_token.replace("#access_token=", "");
664--
665-- // we've got an access token!
666-+ public void on_authenticator_authenticated(owned Variant session_data) {
667- graph_session.authenticated.connect(on_session_authenticated);
668-+ string access_token =
669-+ session_data.lookup_value("AccessToken", null).get_string();
670-+ debug("Access Token: %s", access_token);
671- graph_session.authenticate(access_token);
672- }
673-
674-- private void do_save_session_information() {
675-- debug("ACTION: saving session information to configuration system.");
676--
677-- set_persistent_access_token(graph_session.get_access_token());
678-- }
679--
680- private void do_upload() {
681- debug("ACTION: uploading photos to album '%s'",
682- publishing_params.target_album == PublishingParameters.UNKNOWN_ALBUM ? "(none)" :
683-@@ -505,67 +416,6 @@
684- host.post_error(error);
685- }
686-
687-- private void on_login_clicked() {
688-- if (!is_running())
689-- return;
690--
691-- debug("EVENT: user clicked 'Login' on welcome pane.");
692--
693-- do_test_connection_to_endpoint();
694-- }
695--
696-- private void on_endpoint_test_completed(GraphMessage message) {
697-- message.completed.disconnect(on_endpoint_test_completed);
698-- message.failed.disconnect(on_endpoint_test_error);
699--
700-- if (!is_running())
701-- return;
702--
703-- debug("EVENT: endpoint test transaction detected that the Facebook endpoint is alive.");
704--
705-- do_hosted_web_authentication();
706-- }
707--
708-- private void on_endpoint_test_error(GraphMessage message,
709-- Spit.Publishing.PublishingError error) {
710-- message.completed.disconnect(on_endpoint_test_completed);
711-- message.failed.disconnect(on_endpoint_test_error);
712--
713-- if (!is_running())
714-- return;
715--
716-- debug("EVENT: endpoint test transaction failed to detect a connection to the Facebook " +
717-- "endpoint" + error.message);
718--
719-- on_generic_error(error);
720-- }
721--
722-- private void on_web_auth_pane_login_succeeded(string success_url) {
723-- if (!is_running())
724-- return;
725--
726-- debug("EVENT: hosted web login succeeded.");
727--
728-- do_authenticate_session(success_url);
729-- }
730--
731--
732--
733-- private void on_web_auth_pane_login_failed() {
734-- if (!is_running())
735-- return;
736--
737-- debug("EVENT: hosted web login failed.");
738--
739-- // In this case, "failed" doesn't mean that the user didn't enter the right username and
740-- // password -- Facebook handles that case inside the Facebook Connect web control. Instead,
741-- // it means that no session was initiated in response to our login request. The only
742-- // way this happens is if the user clicks the "Cancel" button that appears inside
743-- // the web control. In this case, the correct behavior is to return the user to the
744-- // service welcome pane so that they can start the web interaction again.
745-- do_show_service_welcome_pane();
746-- }
747--
748- private void on_session_authenticated() {
749- graph_session.authenticated.disconnect(on_session_authenticated);
750-
751-@@ -575,7 +425,6 @@
752- assert(graph_session.is_authenticated());
753- debug("EVENT: an authenticated session has become available.");
754-
755-- do_save_session_information();
756- do_fetch_user_info();
757- }
758-
759-@@ -649,22 +498,9 @@
760- do_show_publishing_options_pane();
761- }
762-
763-- private void on_publishing_options_pane_logout() {
764-- publishing_options_pane.publish.disconnect(on_publishing_options_pane_publish);
765-- publishing_options_pane.logout.disconnect(on_publishing_options_pane_logout);
766--
767-- if (!is_running())
768-- return;
769--
770-- debug("EVENT: user clicked 'Logout' in publishing options pane.");
771--
772-- do_logout();
773-- }
774--
775- private void on_publishing_options_pane_publish(string? target_album, string privacy_setting,
776- Resolution resolution, bool strip_metadata) {
777- publishing_options_pane.publish.disconnect(on_publishing_options_pane_publish);
778-- publishing_options_pane.logout.disconnect(on_publishing_options_pane_logout);
779-
780- if (!is_running())
781- return;
782-@@ -780,20 +616,7 @@
783- // actually a restart
784- publishing_params = new PublishingParameters();
785-
786-- // Do we have saved user credentials? If so, go ahead and authenticate the session
787-- // with the saved credentials and proceed with the publishing interaction. Otherwise, show
788-- // the Welcome pane
789-- if (is_persistent_session_valid()) {
790-- graph_session.authenticate(get_persistent_access_token());
791-- } else {
792-- if (WebAuthenticationPane.is_cache_dirty()) {
793-- host.set_service_locked(false);
794-- host.install_static_message_pane(RESTART_ERROR_MESSAGE,
795-- Spit.Publishing.PluginHost.ButtonMode.CANCEL);
796-- } else {
797-- do_show_service_welcome_pane();
798-- }
799-- }
800-+ authenticator.authenticate();
801- }
802-
803- public void stop() {
804-@@ -811,204 +634,6 @@
805- }
806- }
807-
808--internal class WebAuthenticationPane : Spit.Publishing.DialogPane, Object {
809-- private WebKit.WebView webview = null;
810-- private Gtk.Box pane_widget = null;
811-- private Gtk.ScrolledWindow webview_frame = null;
812-- private static bool cache_dirty = false;
813--
814-- public signal void login_succeeded(string success_url);
815-- public signal void login_failed();
816--
817-- public WebAuthenticationPane() {
818-- pane_widget = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
819--
820-- webview_frame = new Gtk.ScrolledWindow(null, null);
821-- webview_frame.set_shadow_type(Gtk.ShadowType.ETCHED_IN);
822-- webview_frame.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
823--
824-- webview = new WebKit.WebView();
825-- webview.get_settings().enable_plugins = false;
826--
827-- webview.load_changed.connect(on_page_load_changed);
828-- webview.context_menu.connect(() => { return true; });
829--
830-- webview_frame.add(webview);
831-- pane_widget.pack_start(webview_frame, true, true, 0);
832-- }
833--
834-- private class LocaleLookup {
835-- public string prefix;
836-- public string translation;
837-- public string? exception_code;
838-- public string? exception_translation;
839-- public string? exception_code_2;
840-- public string? exception_translation_2;
841--
842-- public LocaleLookup(string prefix, string translation, string? exception_code = null,
843-- string? exception_translation = null, string? exception_code_2 = null,
844-- string? exception_translation_2 = null) {
845-- this.prefix = prefix;
846-- this.translation = translation;
847-- this.exception_code = exception_code;
848-- this.exception_translation = exception_translation;
849-- this.exception_code_2 = exception_code_2;
850-- this.exception_translation_2 = exception_translation_2;
851-- }
852--
853-- }
854--
855-- private LocaleLookup[] locale_lookup_table = {
856-- new LocaleLookup( "es", "es-la", "ES", "es-es" ),
857-- new LocaleLookup( "en", "en-gb", "US", "en-us" ),
858-- new LocaleLookup( "fr", "fr-fr", "CA", "fr-ca" ),
859-- new LocaleLookup( "pt", "pt-br", "PT", "pt-pt" ),
860-- new LocaleLookup( "zh", "zh-cn", "HK", "zh-hk", "TW", "zh-tw" ),
861-- new LocaleLookup( "af", "af-za" ),
862-- new LocaleLookup( "ar", "ar-ar" ),
863-- new LocaleLookup( "nb", "nb-no" ),
864-- new LocaleLookup( "no", "nb-no" ),
865-- new LocaleLookup( "id", "id-id" ),
866-- new LocaleLookup( "ms", "ms-my" ),
867-- new LocaleLookup( "ca", "ca-es" ),
868-- new LocaleLookup( "cs", "cs-cz" ),
869-- new LocaleLookup( "cy", "cy-gb" ),
870-- new LocaleLookup( "da", "da-dk" ),
871-- new LocaleLookup( "de", "de-de" ),
872-- new LocaleLookup( "tl", "tl-ph" ),
873-- new LocaleLookup( "ko", "ko-kr" ),
874-- new LocaleLookup( "hr", "hr-hr" ),
875-- new LocaleLookup( "it", "it-it" ),
876-- new LocaleLookup( "lt", "lt-lt" ),
877-- new LocaleLookup( "hu", "hu-hu" ),
878-- new LocaleLookup( "nl", "nl-nl" ),
879-- new LocaleLookup( "ja", "ja-jp" ),
880-- new LocaleLookup( "nb", "nb-no" ),
881-- new LocaleLookup( "no", "nb-no" ),
882-- new LocaleLookup( "pl", "pl-pl" ),
883-- new LocaleLookup( "ro", "ro-ro" ),
884-- new LocaleLookup( "ru", "ru-ru" ),
885-- new LocaleLookup( "sk", "sk-sk" ),
886-- new LocaleLookup( "sl", "sl-si" ),
887-- new LocaleLookup( "sv", "sv-se" ),
888-- new LocaleLookup( "th", "th-th" ),
889-- new LocaleLookup( "vi", "vi-vn" ),
890-- new LocaleLookup( "tr", "tr-tr" ),
891-- new LocaleLookup( "el", "el-gr" ),
892-- new LocaleLookup( "bg", "bg-bg" ),
893-- new LocaleLookup( "sr", "sr-rs" ),
894-- new LocaleLookup( "he", "he-il" ),
895-- new LocaleLookup( "hi", "hi-in" ),
896-- new LocaleLookup( "bn", "bn-in" ),
897-- new LocaleLookup( "pa", "pa-in" ),
898-- new LocaleLookup( "ta", "ta-in" ),
899-- new LocaleLookup( "te", "te-in" ),
900-- new LocaleLookup( "ml", "ml-in" )
901-- };
902--
903-- private string get_system_locale_as_facebook_locale() {
904-- unowned string? raw_system_locale = Intl.setlocale(LocaleCategory.ALL, "");
905-- if (raw_system_locale == null || raw_system_locale == "")
906-- return "www";
907--
908-- string system_locale = raw_system_locale.split(".")[0];
909--
910-- foreach (LocaleLookup locale_lookup in locale_lookup_table) {
911-- if (!system_locale.has_prefix(locale_lookup.prefix))
912-- continue;
913--
914-- if (locale_lookup.exception_code != null) {
915-- assert(locale_lookup.exception_translation != null);
916--
917-- if (system_locale.contains(locale_lookup.exception_code))
918-- return locale_lookup.exception_translation;
919-- }
920--
921-- if (locale_lookup.exception_code_2 != null) {
922-- assert(locale_lookup.exception_translation_2 != null);
923--
924-- if (system_locale.contains(locale_lookup.exception_code_2))
925-- return locale_lookup.exception_translation_2;
926-- }
927--
928-- return locale_lookup.translation;
929-- }
930--
931-- // default
932-- return "www";
933-- }
934--
935-- private string get_login_url() {
936-- string facebook_locale = get_system_locale_as_facebook_locale();
937--
938-- return "https://%s.facebook.com/dialog/oauth?client_id=%s&redirect_uri=https://www.facebook.com/connect/login_success.html&scope=publish_actions,user_photos,user_videos&response_type=token".printf(facebook_locale, APPLICATION_ID);
939-- }
940--
941-- private void on_page_load() {
942-- pane_widget.get_window().set_cursor(new Gdk.Cursor(Gdk.CursorType.LEFT_PTR));
943--
944-- string loaded_url = webview.uri.dup();
945-- debug("loaded url: " + loaded_url);
946--
947-- // strip parameters from the loaded url
948-- if (loaded_url.contains("?")) {
949-- int index = loaded_url.index_of_char('?');
950-- string params = loaded_url[index:loaded_url.length];
951-- loaded_url = loaded_url.replace(params, "");
952-- }
953--
954-- // were we redirected to the facebook login success page?
955-- if (loaded_url.contains("login_success")) {
956-- cache_dirty = true;
957-- login_succeeded(webview.uri);
958-- return;
959-- }
960--
961-- // were we redirected to the login total failure page?
962-- if (loaded_url.contains("login_failure")) {
963-- login_failed();
964-- return;
965-- }
966-- }
967--
968-- private void on_load_started() {
969-- pane_widget.get_window().set_cursor(new Gdk.Cursor(Gdk.CursorType.WATCH));
970-- }
971--
972-- private void on_page_load_changed (WebKit.LoadEvent load_event) {
973-- switch (load_event) {
974-- case WebKit.LoadEvent.STARTED:
975-- case WebKit.LoadEvent.REDIRECTED:
976-- on_load_started();
977-- break;
978-- case WebKit.LoadEvent.FINISHED:
979-- on_page_load();
980-- break;
981-- }
982--
983-- return;
984-- }
985--
986-- public static bool is_cache_dirty() {
987-- return cache_dirty;
988-- }
989--
990-- public Gtk.Widget get_widget() {
991-- return pane_widget;
992-- }
993--
994-- public Spit.Publishing.DialogPane.GeometryOptions get_preferred_geometry() {
995-- return Spit.Publishing.DialogPane.GeometryOptions.NONE;
996-- }
997--
998-- public void on_pane_installed() {
999-- webview.load_uri(get_login_url());
1000-- }
1001--
1002-- public void on_pane_uninstalled() {
1003-- }
1004--}
1005--
1006- internal class PublishingOptionsPane : Spit.Publishing.DialogPane, GLib.Object {
1007- private Gtk.Builder builder;
1008- private Gtk.Box pane_widget = null;
1009-@@ -1019,7 +644,6 @@
1010- private Gtk.Entry new_album_entry = null;
1011- private Gtk.CheckButton strip_metadata_check = null;
1012- private Gtk.Button publish_button = null;
1013-- private Gtk.Button logout_button = null;
1014- private Gtk.Label how_to_label = null;
1015- private Album[] albums = null;
1016- private FacebookPublisher publisher = null;
1017-@@ -1036,7 +660,6 @@
1018- private const int CONTENT_GROUP_SPACING = 32;
1019- private const int STANDARD_ACTION_BUTTON_WIDTH = 128;
1020-
1021-- public signal void logout();
1022- public signal void publish(string? target_album, string privacy_setting,
1023- Resolution target_resolution, bool strip_metadata);
1024-
1025-@@ -1075,7 +698,6 @@
1026- existing_albums_combo = (Gtk.ComboBoxText) this.builder.get_object("existing_albums_combo");
1027- visibility_combo = (Gtk.ComboBoxText) this.builder.get_object("visibility_combo");
1028- publish_button = (Gtk.Button) this.builder.get_object("publish_button");
1029-- logout_button = (Gtk.Button) this.builder.get_object("logout_button");
1030- new_album_entry = (Gtk.Entry) this.builder.get_object("new_album_entry");
1031- resolution_combo = (Gtk.ComboBoxText) this.builder.get_object("resolution_combo");
1032- how_to_label = (Gtk.Label) this.builder.get_object("how_to_label");
1033-@@ -1094,7 +716,6 @@
1034- visibility_combo.set_active(0);
1035-
1036- publish_button.clicked.connect(on_publish_button_clicked);
1037-- logout_button.clicked.connect(on_logout_button_clicked);
1038-
1039- setup_resolution_combo();
1040- resolution_combo.set_active(publisher.get_persistent_default_size());
1041-@@ -1163,10 +784,6 @@
1042- publisher.set_persistent_default_size(resolution_combo.get_active());
1043- }
1044-
1045-- private void on_logout_button_clicked() {
1046-- logout();
1047-- }
1048--
1049- private void on_publish_button_clicked() {
1050- string album_name;
1051- string privacy_setting = privacy_descriptions[visibility_combo.get_active()].privacy_setting;
1052-@@ -1240,10 +857,6 @@
1053- publish_button.grab_focus();
1054- }
1055-
1056-- private void notify_logout() {
1057-- logout();
1058-- }
1059--
1060- private void notify_publish(string? target_album, string privacy_setting, Resolution target_resolution) {
1061- publish(target_album, privacy_setting, target_resolution, strip_metadata_check.get_active());
1062- }
1063-@@ -1257,14 +870,12 @@
1064- }
1065-
1066- public void on_pane_installed() {
1067-- logout.connect(notify_logout);
1068- publish.connect(notify_publish);
1069-
1070- installed();
1071- }
1072-
1073- public void on_pane_uninstalled() {
1074-- logout.disconnect(notify_logout);
1075- publish.disconnect(notify_publish);
1076- }
1077- }
1078-@@ -1571,15 +1182,6 @@
1079- return access_token != null;
1080- }
1081-
1082-- public string get_access_token() {
1083-- assert(is_authenticated());
1084-- return access_token;
1085-- }
1086--
1087-- public GraphMessage new_endpoint_test() {
1088-- return new GraphEndpointProbeMessage(this);
1089-- }
1090--
1091- public GraphMessage new_query(string resource_path) {
1092- return new GraphQueryMessage(this, resource_path, access_token);
1093- }
1094-Index: b/plugins/shotwell-publishing/FlickrPublishing.vala
1095-===================================================================
1096---- a/plugins/shotwell-publishing/FlickrPublishing.vala
1097-+++ b/plugins/shotwell-publishing/FlickrPublishing.vala
1098-@@ -4,32 +4,30 @@
1099- * (version 2.1 or later). See the COPYING file in this distribution.
1100- */
1101-
1102-+using Publishing.Accounts;
1103-+
1104- extern string hmac_sha1(string key, string message);
1105-
1106--public class FlickrService : Object, Spit.Pluggable, Spit.Publishing.Service {
1107-+public class FlickrService : UOAPublishingService {
1108- private const string ICON_FILENAME = "flickr.png";
1109-
1110- private static Gdk.Pixbuf[] icon_pixbuf_set = null;
1111-
1112- public FlickrService(GLib.File resource_directory) {
1113-+ base("flickr");
1114- if (icon_pixbuf_set == null)
1115- icon_pixbuf_set = Resources.load_icon_set(resource_directory.get_child(ICON_FILENAME));
1116- }
1117-
1118-- public int get_pluggable_interface(int min_host_interface, int max_host_interface) {
1119-- return Spit.negotiate_interfaces(min_host_interface, max_host_interface,
1120-- Spit.Publishing.CURRENT_INTERFACE);
1121-- }
1122--
1123-- public unowned string get_id() {
1124-+ public override unowned string get_id() {
1125- return "org.yorba.shotwell.publishing.flickr";
1126- }
1127-
1128-- public unowned string get_pluggable_name() {
1129-+ public override unowned string get_pluggable_name() {
1130- return "Flickr";
1131- }
1132-
1133-- public void get_info(ref Spit.PluggableInfo info) {
1134-+ public override void get_info(ref Spit.PluggableInfo info) {
1135- info.authors = "Lucas Beeler";
1136- info.copyright = _("Copyright 2009-2015 Yorba Foundation");
1137- info.translators = Resources.TRANSLATORS;
1138-@@ -41,14 +39,12 @@
1139- info.icons = icon_pixbuf_set;
1140- }
1141-
1142-- public void activation(bool enabled) {
1143-- }
1144--
1145-- public Spit.Publishing.Publisher create_publisher(Spit.Publishing.PluginHost host) {
1146-- return new Publishing.Flickr.FlickrPublisher(this, host);
1147-+ public override Spit.Publishing.Publisher create_publisher(string? account_name, Spit.Publishing.PluginHost host) {
1148-+ SharingAccount account = find_account(account_name);
1149-+ return new Publishing.Flickr.FlickrPublisher(this, account, host);
1150- }
1151-
1152-- public Spit.Publishing.Publisher.MediaType get_supported_media() {
1153-+ public override Spit.Publishing.Publisher.MediaType get_supported_media() {
1154- return (Spit.Publishing.Publisher.MediaType.PHOTO |
1155- Spit.Publishing.Publisher.MediaType.VIDEO);
1156- }
1157-@@ -62,8 +58,6 @@
1158- internal const string RESTART_ERROR_MESSAGE =
1159- _("You have already logged in and out of Flickr during this Shotwell session.\nTo continue publishing to Flickr, quit and restart Shotwell, then try publishing again.");
1160- internal const string ENDPOINT_URL = "https://api.flickr.com/services/rest";
1161--internal const string API_KEY = "60dd96d4a2ad04888b09c9e18d82c26f";
1162--internal const string API_SECRET = "d0960565e03547c1";
1163- internal const int ORIGINAL_SIZE = -1;
1164- internal const string EXPIRED_SESSION_ERROR_CODE = "98";
1165- internal const string ENCODE_RFC_3986_EXTRA = "!*'();:@&=+$,/?%#[] \\";
1166-@@ -105,60 +99,32 @@
1167- private bool was_started = false;
1168- private Session session = null;
1169- private PublishingOptionsPane publishing_options_pane = null;
1170-+ private UOAPublisherAuthenticator authenticator = null;
1171-
1172- private PublishingParameters parameters = null;
1173-
1174- public FlickrPublisher(Spit.Publishing.Service service,
1175-+ SharingAccount account,
1176- Spit.Publishing.PluginHost host) {
1177- debug("FlickrPublisher instantiated.");
1178- this.service = service;
1179- this.host = host;
1180- this.session = new Session();
1181-+ authenticator = new UOAPublisherAuthenticator(account, host,
1182-+ SERVICE_WELCOME_MESSAGE);
1183- this.parameters = new PublishingParameters();
1184-
1185- session.authenticated.connect(on_session_authenticated);
1186-+ Variant data = authenticator.get_authentication_data();
1187-+ session.set_api_credentials(data.lookup_value("ConsumerKey", null).get_string(),
1188-+ data.lookup_value("ConsumerSecret", null).get_string());
1189-+ authenticator.authenticated.connect(on_authenticator_authenticated);
1190- }
1191-
1192- ~FlickrPublisher() {
1193- session.authenticated.disconnect(on_session_authenticated);
1194- }
1195-
1196-- private void invalidate_persistent_session() {
1197-- set_persistent_access_phase_token("");
1198-- set_persistent_access_phase_token_secret("");
1199-- set_persistent_access_phase_username("");
1200-- }
1201--
1202-- private bool is_persistent_session_valid() {
1203-- return (get_persistent_access_phase_username() != null &&
1204-- get_persistent_access_phase_token() != null &&
1205-- get_persistent_access_phase_token_secret() != null);
1206-- }
1207--
1208-- private string? get_persistent_access_phase_username() {
1209-- return host.get_config_string("access_phase_username", null);
1210-- }
1211--
1212-- private void set_persistent_access_phase_username(string username) {
1213-- host.set_config_string("access_phase_username", username);
1214-- }
1215--
1216-- private string? get_persistent_access_phase_token() {
1217-- return host.get_config_string("access_phase_token", null);
1218-- }
1219--
1220-- private void set_persistent_access_phase_token(string token) {
1221-- host.set_config_string("access_phase_token", token);
1222-- }
1223--
1224-- private string? get_persistent_access_phase_token_secret() {
1225-- return host.get_config_string("access_phase_token_secret", null);
1226-- }
1227--
1228-- private void set_persistent_access_phase_token_secret(string secret) {
1229-- host.set_config_string("access_phase_token_secret", secret);
1230-- }
1231--
1232- private bool get_persistent_strip_metadata() {
1233- return host.get_config_bool("strip_metadata", false);
1234- }
1235-@@ -167,94 +133,17 @@
1236- host.set_config_bool("strip_metadata", strip_metadata);
1237- }
1238-
1239-- private void on_welcome_pane_login_clicked() {
1240-- if (!running)
1241-- return;
1242--
1243-- debug("EVENT: user clicked 'Login' button in the welcome pane");
1244--
1245-- do_run_authentication_request_transaction();
1246-- }
1247--
1248-- private void on_auth_request_txn_completed(Publishing.RESTSupport.Transaction txn) {
1249-- txn.completed.disconnect(on_auth_request_txn_completed);
1250-- txn.network_error.disconnect(on_auth_request_txn_error);
1251--
1252-- if (!is_running())
1253-- return;
1254--
1255-- debug("EVENT: OAuth authentication request transaction completed; response = '%s'",
1256-- txn.get_response());
1257--
1258-- do_parse_token_info_from_auth_request(txn.get_response());
1259-- }
1260--
1261-- private void on_auth_request_txn_error(Publishing.RESTSupport.Transaction txn,
1262-- Spit.Publishing.PublishingError err) {
1263-- txn.completed.disconnect(on_auth_request_txn_completed);
1264-- txn.network_error.disconnect(on_auth_request_txn_error);
1265--
1266-- if (!is_running())
1267-- return;
1268--
1269-- debug("EVENT: OAuth authentication request transaction caused a network error");
1270-- host.post_error(err);
1271-- }
1272--
1273-- private void on_authentication_token_available(string token, string token_secret) {
1274-- debug("EVENT: OAuth authentication token (%s) and token secret (%s) available",
1275-- token, token_secret);
1276--
1277-- session.set_request_phase_credentials(token, token_secret);
1278--
1279-- do_launch_system_browser(token);
1280-- }
1281--
1282-- private void on_system_browser_launched() {
1283-- if (!is_running())
1284-- return;
1285--
1286-- debug("EVENT: system browser launched.");
1287--
1288-- do_show_pin_entry_pane();
1289-- }
1290--
1291-- private void on_pin_entry_proceed(PinEntryPane sender, string pin) {
1292-- sender.proceed.disconnect(on_pin_entry_proceed);
1293--
1294-- if (!is_running())
1295-- return;
1296--
1297-- debug("EVENT: user clicked 'Continue' in PIN entry pane.");
1298--
1299-- do_verify_pin(pin);
1300-- }
1301-+ public void on_authenticator_authenticated(owned Variant session_data) {
1302-+ host.set_service_locked(true);
1303-+ host.install_account_fetch_wait_pane();
1304-
1305-- private void on_access_token_fetch_txn_completed(Publishing.RESTSupport.Transaction txn) {
1306-- txn.completed.disconnect(on_access_token_fetch_txn_completed);
1307-- txn.network_error.disconnect(on_access_token_fetch_error);
1308--
1309-- if (!is_running())
1310-- return;
1311--
1312-- debug("EVENT: fetching OAuth access token over the network succeeded");
1313--
1314-- do_extract_access_phase_credentials_from_reponse(txn.get_response());
1315-+ string token = session_data.lookup_value("AccessToken", null).get_string();
1316-+ string token_secret = session_data.lookup_value("TokenSecret", null).get_string();
1317-+ string username = session_data.lookup_value("username", null).get_string();
1318-+ debug("Access Token: %s, %s, %s", token, token_secret, username);
1319-+ session.set_access_phase_credentials(token, token_secret, username);
1320- }
1321-
1322-- private void on_access_token_fetch_error(Publishing.RESTSupport.Transaction txn,
1323-- Spit.Publishing.PublishingError err) {
1324-- txn.completed.disconnect(on_access_token_fetch_txn_completed);
1325-- txn.network_error.disconnect(on_access_token_fetch_error);
1326--
1327-- if (!is_running())
1328-- return;
1329--
1330-- debug("EVENT: fetching OAuth access token over the network caused an error.");
1331--
1332-- host.post_error(err);
1333-- }
1334--
1335- private void on_session_authenticated() {
1336- if (!is_running())
1337- return;
1338-@@ -263,10 +152,6 @@
1339-
1340- parameters.username = session.get_username();
1341-
1342-- set_persistent_access_phase_token(session.get_access_phase_token());
1343-- set_persistent_access_phase_token_secret(session.get_access_phase_token_secret());
1344-- set_persistent_access_phase_username(session.get_username());
1345--
1346- do_fetch_account_info();
1347- }
1348-
1349-@@ -303,7 +188,6 @@
1350-
1351- private void on_publishing_options_pane_publish(bool strip_metadata) {
1352- publishing_options_pane.publish.disconnect(on_publishing_options_pane_publish);
1353-- publishing_options_pane.logout.disconnect(on_publishing_options_pane_logout);
1354-
1355- if (!is_running())
1356- return;
1357-@@ -312,18 +196,6 @@
1358- do_publish(strip_metadata);
1359- }
1360-
1361-- private void on_publishing_options_pane_logout() {
1362-- publishing_options_pane.publish.disconnect(on_publishing_options_pane_publish);
1363-- publishing_options_pane.logout.disconnect(on_publishing_options_pane_logout);
1364--
1365-- if (!is_running())
1366-- return;
1367--
1368-- debug("EVENT: user clicked the 'Logout' button in the publishing options pane");
1369--
1370-- do_logout();
1371-- }
1372--
1373- private void on_upload_status_updated(int file_number, double completed_fraction) {
1374- if (!is_running())
1375- return;
1376-@@ -361,148 +233,6 @@
1377- host.post_error(err);
1378- }
1379-
1380-- private void do_show_login_welcome_pane() {
1381-- debug("ACTION: installing login welcome pane");
1382--
1383-- host.set_service_locked(false);
1384-- host.install_welcome_pane(SERVICE_WELCOME_MESSAGE, on_welcome_pane_login_clicked);
1385-- }
1386--
1387-- private void do_run_authentication_request_transaction() {
1388-- debug("ACTION: running authentication request transaction");
1389--
1390-- host.set_service_locked(true);
1391-- host.install_static_message_pane(_("Preparing for login..."));
1392--
1393-- AuthenticationRequestTransaction txn = new AuthenticationRequestTransaction(session);
1394-- txn.completed.connect(on_auth_request_txn_completed);
1395-- txn.network_error.connect(on_auth_request_txn_error);
1396--
1397-- try {
1398-- txn.execute();
1399-- } catch (Spit.Publishing.PublishingError err) {
1400-- host.post_error(err);
1401-- }
1402-- }
1403--
1404-- private void do_parse_token_info_from_auth_request(string response) {
1405-- debug("ACTION: parsing authorization request response '%s' into token and secret", response);
1406--
1407-- string? oauth_token = null;
1408-- string? oauth_token_secret = null;
1409--
1410-- string[] key_value_pairs = response.split("&");
1411-- foreach (string pair in key_value_pairs) {
1412-- string[] split_pair = pair.split("=");
1413--
1414-- if (split_pair.length != 2)
1415-- host.post_error(new Spit.Publishing.PublishingError.MALFORMED_RESPONSE(
1416-- "'%s' isn't a valid response to an OAuth authentication request", response));
1417--
1418-- if (split_pair[0] == "oauth_token")
1419-- oauth_token = split_pair[1];
1420-- else if (split_pair[0] == "oauth_token_secret")
1421-- oauth_token_secret = split_pair[1];
1422-- }
1423--
1424-- if (oauth_token == null || oauth_token_secret == null)
1425-- host.post_error(new Spit.Publishing.PublishingError.MALFORMED_RESPONSE(
1426-- "'%s' isn't a valid response to an OAuth authentication request", response));
1427--
1428--
1429-- on_authentication_token_available(oauth_token, oauth_token_secret);
1430-- }
1431--
1432-- private void do_launch_system_browser(string token) {
1433-- string login_uri = "https://www.flickr.com/services/oauth/authorize?oauth_token=" + token +
1434-- "&perms=write";
1435--
1436-- debug("ACTION: launching system browser with uri = '%s'", login_uri);
1437--
1438-- try {
1439-- Process.spawn_command_line_async("xdg-open " + login_uri);
1440-- } catch (SpawnError e) {
1441-- host.post_error(new Spit.Publishing.PublishingError.LOCAL_FILE_ERROR(
1442-- "couldn't launch system web browser to complete Flickr login"));
1443-- return;
1444-- }
1445--
1446-- on_system_browser_launched();
1447-- }
1448--
1449-- private void do_show_pin_entry_pane() {
1450-- debug("ACTION: showing PIN entry pane");
1451--
1452-- Gtk.Builder builder = new Gtk.Builder();
1453--
1454-- try {
1455-- builder.add_from_file(host.get_module_file().get_parent().get_child("flickr_pin_entry_pane.glade").get_path());
1456-- } catch (Error e) {
1457-- warning("Could not parse UI file! Error: %s.", e.message);
1458-- host.post_error(
1459-- new Spit.Publishing.PublishingError.LOCAL_FILE_ERROR(
1460-- _("A file required for publishing is unavailable. Publishing to Flickr can't continue.")));
1461-- return;
1462-- }
1463--
1464-- PinEntryPane pin_entry_pane = new PinEntryPane(builder);
1465-- pin_entry_pane.proceed.connect(on_pin_entry_proceed);
1466-- host.install_dialog_pane(pin_entry_pane);
1467-- }
1468--
1469-- private void do_verify_pin(string pin) {
1470-- debug("ACTION: validating authorization PIN %s", pin);
1471--
1472-- host.set_service_locked(true);
1473-- host.install_static_message_pane(_("Verifying authorization..."));
1474--
1475-- AccessTokenFetchTransaction txn = new AccessTokenFetchTransaction(session, pin);
1476-- txn.completed.connect(on_access_token_fetch_txn_completed);
1477-- txn.network_error.connect(on_access_token_fetch_error);
1478--
1479-- try {
1480-- txn.execute();
1481-- } catch (Spit.Publishing.PublishingError err) {
1482-- host.post_error(err);
1483-- }
1484-- }
1485--
1486-- private void do_extract_access_phase_credentials_from_reponse(string response) {
1487-- debug("ACTION: extracting access phase credentials from '%s'", response);
1488--
1489-- string[] key_value_pairs = response.split("&");
1490--
1491-- string? token = null;
1492-- string? token_secret = null;
1493-- string? username = null;
1494-- foreach (string key_value_pair in key_value_pairs) {
1495-- string[] split_pair = key_value_pair.split("=");
1496--
1497-- if (split_pair.length != 2)
1498-- continue;
1499--
1500-- string key = split_pair[0];
1501-- string value = split_pair[1];
1502--
1503-- if (key == "oauth_token")
1504-- token = value;
1505-- else if (key == "oauth_token_secret")
1506-- token_secret = value;
1507-- else if (key == "username")
1508-- username = value;
1509-- }
1510--
1511-- debug("access phase credentials: { token = '%s'; token_secret = '%s'; username = '%s' }",
1512-- token, token_secret, username);
1513--
1514-- if (token == null || token_secret == null || username == null)
1515-- host.post_error(new Spit.Publishing.PublishingError.MALFORMED_RESPONSE("expected " +
1516-- "access phase credentials to contain token, token secret, and username but at " +
1517-- "least one of these is absent"));
1518--
1519-- session.set_access_phase_credentials(token, token_secret, username);
1520-- }
1521--
1522- private void do_fetch_account_info() {
1523- debug("ACTION: running network transaction to fetch account information");
1524-
1525-@@ -568,7 +298,6 @@
1526- debug("ACTION: logging user out, deauthenticating session, and erasing stored credentials");
1527-
1528- session.deauthenticate();
1529-- invalidate_persistent_session();
1530-
1531- running = false;
1532-
1533-@@ -599,7 +328,6 @@
1534- publishing_options_pane = new PublishingOptionsPane(this, parameters,
1535- host.get_publishable_media_type(), builder, get_persistent_strip_metadata());
1536- publishing_options_pane.publish.connect(on_publishing_options_pane_publish);
1537-- publishing_options_pane.logout.connect(on_publishing_options_pane_logout);
1538- host.install_dialog_pane(publishing_options_pane);
1539- }
1540-
1541-@@ -674,16 +402,7 @@
1542- running = true;
1543- was_started = true;
1544-
1545-- if (is_persistent_session_valid()) {
1546-- debug("attempt start: a persistent session is available; using it");
1547--
1548-- session.authenticate_from_persistent_credentials(get_persistent_access_phase_token(),
1549-- get_persistent_access_phase_token_secret(), get_persistent_access_phase_username());
1550-- } else {
1551-- debug("attempt start: no persistent session available; showing login welcome pane");
1552--
1553-- do_show_login_welcome_pane();
1554-- }
1555-+ authenticator.authenticate();
1556- }
1557-
1558- public void start() {
1559-@@ -772,7 +491,7 @@
1560- add_argument("oauth_version", "1.0");
1561- add_argument("oauth_callback", "oob");
1562- add_argument("oauth_timestamp", session.get_oauth_timestamp());
1563-- add_argument("oauth_consumer_key", API_KEY);
1564-+ add_argument("oauth_consumer_key", session.get_api_key());
1565- }
1566-
1567- public Transaction.with_uri(Session session, string uri,
1568-@@ -784,7 +503,7 @@
1569- add_argument("oauth_version", "1.0");
1570- add_argument("oauth_callback", "oob");
1571- add_argument("oauth_timestamp", session.get_oauth_timestamp());
1572-- add_argument("oauth_consumer_key", API_KEY);
1573-+ add_argument("oauth_consumer_key", session.get_api_key());
1574- }
1575-
1576- public override void execute() throws Spit.Publishing.PublishingError {
1577-@@ -842,22 +561,6 @@
1578- }
1579- }
1580-
1581--internal class AuthenticationRequestTransaction : Transaction {
1582-- public AuthenticationRequestTransaction(Session session) {
1583-- base.with_uri(session, "https://www.flickr.com/services/oauth/request_token",
1584-- Publishing.RESTSupport.HttpMethod.GET);
1585-- }
1586--}
1587--
1588--internal class AccessTokenFetchTransaction : Transaction {
1589-- public AccessTokenFetchTransaction(Session session, string user_verifier) {
1590-- base.with_uri(session, "https://www.flickr.com/services/oauth/access_token",
1591-- Publishing.RESTSupport.HttpMethod.GET);
1592-- add_argument("oauth_verifier", user_verifier);
1593-- add_argument("oauth_token", session.get_request_phase_token());
1594-- }
1595--}
1596--
1597- internal class AccountInfoFetchTransaction : Transaction {
1598- public AccountInfoFetchTransaction(Session session) {
1599- base(session, Publishing.RESTSupport.HttpMethod.GET);
1600-@@ -884,7 +587,7 @@
1601- add_authorization_header_field("oauth_version", "1.0");
1602- add_authorization_header_field("oauth_callback", "oob");
1603- add_authorization_header_field("oauth_timestamp", session.get_oauth_timestamp());
1604-- add_authorization_header_field("oauth_consumer_key", API_KEY);
1605-+ add_authorization_header_field("oauth_consumer_key", session.get_api_key());
1606- add_authorization_header_field("oauth_token", session.get_access_phase_token());
1607-
1608- add_argument("is_public", ("%d".printf(parameters.visibility_specification.everyone_level)));
1609-@@ -944,11 +647,11 @@
1610- }
1611-
1612- internal class Session : Publishing.RESTSupport.Session {
1613-- private string? request_phase_token = null;
1614-- private string? request_phase_token_secret = null;
1615- private string? access_phase_token = null;
1616- private string? access_phase_token_secret = null;
1617- private string? username = null;
1618-+ private string? api_key = null;
1619-+ private string? api_secret = null;
1620-
1621- public Session() {
1622- base(ENDPOINT_URL);
1623-@@ -959,15 +662,6 @@
1624- username != null);
1625- }
1626-
1627-- public void authenticate_from_persistent_credentials(string token, string secret,
1628-- string username) {
1629-- this.access_phase_token = token;
1630-- this.access_phase_token_secret = secret;
1631-- this.username = username;
1632--
1633-- authenticated();
1634-- }
1635--
1636- public void deauthenticate() {
1637- access_phase_token = null;
1638- access_phase_token_secret = null;
1639-@@ -1008,16 +702,12 @@
1640- if (access_phase_token_secret != null) {
1641- debug("access phase token secret available; using it as signing key");
1642-
1643-- signing_key = API_SECRET + "&" + access_phase_token_secret;
1644-- } else if (request_phase_token_secret != null) {
1645-- debug("request phase token secret available; using it as signing key");
1646--
1647-- signing_key = API_SECRET + "&" + request_phase_token_secret;
1648-+ signing_key = api_secret + "&" + access_phase_token_secret;
1649- } else {
1650-- debug("neither access phase nor request phase token secrets available; using API " +
1651-+ debug("access token secret not available; using API " +
1652- "key as signing key");
1653-
1654-- signing_key = API_SECRET + "&";
1655-+ signing_key = api_secret + "&";
1656- }
1657-
1658- string signature_base_string = http_method + "&" + Soup.URI.encode(
1659-@@ -1040,11 +730,11 @@
1660- txn.add_argument("oauth_signature", signature);
1661- }
1662-
1663-- public void set_request_phase_credentials(string token, string secret) {
1664-- this.request_phase_token = token;
1665-- this.request_phase_token_secret = secret;
1666-+ public void set_api_credentials(string api_key, string api_secret) {
1667-+ this.api_key = api_key;
1668-+ this.api_secret = api_secret;
1669- }
1670--
1671-+
1672- public void set_access_phase_credentials(string token, string secret, string username) {
1673- this.access_phase_token = token;
1674- this.access_phase_token_secret = secret;
1675-@@ -1065,21 +755,16 @@
1676- return GLib.get_real_time().to_string().substring(0, 10);
1677- }
1678-
1679-- public string get_request_phase_token() {
1680-- assert(request_phase_token != null);
1681-- return request_phase_token;
1682-+ public string get_api_key() {
1683-+ assert(api_key != null);
1684-+ return api_key;
1685- }
1686--
1687-+
1688- public string get_access_phase_token() {
1689- assert(access_phase_token != null);
1690- return access_phase_token;
1691- }
1692-
1693-- public string get_access_phase_token_secret() {
1694-- assert(access_phase_token_secret != null);
1695-- return access_phase_token_secret;
1696-- }
1697--
1698- public string get_username() {
1699- assert(is_authenticated());
1700- return username;
1701-@@ -1112,7 +797,6 @@
1702- private Gtk.Label visibility_label = null;
1703- private Gtk.Label upload_info_label = null;
1704- private Gtk.Label size_label = null;
1705-- private Gtk.Button logout_button = null;
1706- private Gtk.Button publish_button = null;
1707- private Gtk.ComboBoxText visibility_combo = null;
1708- private Gtk.ComboBoxText size_combo = null;
1709-@@ -1124,7 +808,6 @@
1710- private Spit.Publishing.Publisher.MediaType media_type;
1711-
1712- public signal void publish(bool strip_metadata);
1713-- public signal void logout();
1714-
1715- public PublishingOptionsPane(FlickrPublisher publisher, PublishingParameters parameters,
1716- Spit.Publishing.Publisher.MediaType media_type, Gtk.Builder builder, bool strip_metadata) {
1717-@@ -1136,7 +819,6 @@
1718- pane_widget = (Gtk.Box) this.builder.get_object("flickr_pane");
1719- visibility_label = (Gtk.Label) this.builder.get_object("visibility_label");
1720- upload_info_label = (Gtk.Label) this.builder.get_object("upload_info_label");
1721-- logout_button = (Gtk.Button) this.builder.get_object("logout_button");
1722- publish_button = (Gtk.Button) this.builder.get_object("publish_button");
1723- visibility_combo = (Gtk.ComboBoxText) this.builder.get_object("visibility_combo");
1724- size_combo = (Gtk.ComboBoxText) this.builder.get_object("size_combo");
1725-@@ -1186,14 +868,9 @@
1726-
1727- strip_metadata_check.set_active(strip_metadata);
1728-
1729-- logout_button.clicked.connect(on_logout_clicked);
1730- publish_button.clicked.connect(on_publish_clicked);
1731- }
1732-
1733-- private void on_logout_clicked() {
1734-- logout();
1735-- }
1736--
1737- private void on_publish_clicked() {
1738- parameters.visibility_specification =
1739- visibilities[visibility_combo.get_active()].specification;
1740-@@ -1260,10 +937,6 @@
1741- publish(strip_metadata_check.get_active());
1742- }
1743-
1744-- protected void notify_logout() {
1745-- logout();
1746-- }
1747--
1748- public Gtk.Widget get_widget() {
1749- return pane_widget;
1750- }
1751-@@ -1274,12 +947,10 @@
1752-
1753- public void on_pane_installed() {
1754- publish.connect(notify_publish);
1755-- logout.connect(notify_logout);
1756- }
1757-
1758- public void on_pane_uninstalled() {
1759- publish.disconnect(notify_publish);
1760-- logout.disconnect(notify_logout);
1761- }
1762- }
1763-
1764-Index: b/plugins/shotwell-publishing/Makefile
1765-===================================================================
1766---- a/plugins/shotwell-publishing/Makefile
1767-+++ b/plugins/shotwell-publishing/Makefile
1768-@@ -18,6 +18,7 @@
1769- FlickrPublishing.vala \
1770- YouTubePublishing.vala \
1771- PiwigoPublishing.vala \
1772-+ ../common/accounts.vala \
1773- ../../src/util/string.vala \
1774- ../common/RESTSupport.vala
1775-
1776-Index: b/plugins/shotwell-publishing/PicasaPublishing.vala
1777-===================================================================
1778---- a/plugins/shotwell-publishing/PicasaPublishing.vala
1779-+++ b/plugins/shotwell-publishing/PicasaPublishing.vala
1780-@@ -4,30 +4,28 @@
1781- * (version 2.1 or later). See the COPYING file in this distribution.
1782- */
1783-
1784--public class PicasaService : Object, Spit.Pluggable, Spit.Publishing.Service {
1785-+using Publishing.Accounts;
1786-+
1787-+public class PicasaService : UOAPublishingService {
1788- private const string ICON_FILENAME = "picasa.png";
1789-
1790- private static Gdk.Pixbuf[] icon_pixbuf_set = null;
1791-
1792- public PicasaService(GLib.File resource_directory) {
1793-+ base("google");
1794- if (icon_pixbuf_set == null)
1795- icon_pixbuf_set = Resources.load_icon_set(resource_directory.get_child(ICON_FILENAME));
1796- }
1797-
1798-- public int get_pluggable_interface(int min_host_interface, int max_host_interface) {
1799-- return Spit.negotiate_interfaces(min_host_interface, max_host_interface,
1800-- Spit.Publishing.CURRENT_INTERFACE);
1801-- }
1802--
1803-- public unowned string get_id() {
1804-+ public override unowned string get_id() {
1805- return "org.yorba.shotwell.publishing.picasa";
1806- }
1807-
1808-- public unowned string get_pluggable_name() {
1809-+ public override unowned string get_pluggable_name() {
1810- return "Picasa Web Albums";
1811- }
1812-
1813-- public void get_info(ref Spit.PluggableInfo info) {
1814-+ public override void get_info(ref Spit.PluggableInfo info) {
1815- info.authors = "Lucas Beeler";
1816- info.copyright = _("Copyright 2009-2015 Yorba Foundation");
1817- info.translators = Resources.TRANSLATORS;
1818-@@ -39,17 +37,15 @@
1819- info.icons = icon_pixbuf_set;
1820- }
1821-
1822-- public Spit.Publishing.Publisher create_publisher(Spit.Publishing.PluginHost host) {
1823-- return new Publishing.Picasa.PicasaPublisher(this, host);
1824-+ public override Spit.Publishing.Publisher create_publisher(string? account_name, Spit.Publishing.PluginHost host) {
1825-+ SharingAccount account = find_account(account_name);
1826-+ return new Publishing.Picasa.PicasaPublisher(this, account, host);
1827- }
1828-
1829-- public Spit.Publishing.Publisher.MediaType get_supported_media() {
1830-+ public override Spit.Publishing.Publisher.MediaType get_supported_media() {
1831- return (Spit.Publishing.Publisher.MediaType.PHOTO |
1832- Spit.Publishing.Publisher.MediaType.VIDEO);
1833- }
1834--
1835-- public void activation(bool enabled) {
1836-- }
1837- }
1838-
1839- namespace Publishing.Picasa {
1840-@@ -62,21 +58,25 @@
1841- private bool running;
1842- private Spit.Publishing.ProgressCallback progress_reporter;
1843- private PublishingParameters publishing_parameters;
1844-- private string? refresh_token;
1845-+ private UOAPublisherAuthenticator authenticator = null;
1846-
1847- public PicasaPublisher(Spit.Publishing.Service service,
1848-+ SharingAccount account,
1849- Spit.Publishing.PluginHost host) {
1850- base(service, host, "http://picasaweb.google.com/data/");
1851-
1852- this.publishing_parameters = new PublishingParameters();
1853- load_parameters_from_configuration_system(publishing_parameters);
1854-
1855-+ authenticator = new UOAPublisherAuthenticator(account, host,
1856-+ SERVICE_WELCOME_MESSAGE);
1857-+ authenticator.authenticated.connect(on_authenticator_authenticated);
1858-+
1859- Spit.Publishing.Publisher.MediaType media_type = Spit.Publishing.Publisher.MediaType.NONE;
1860- foreach(Spit.Publishing.Publishable p in host.get_publishables())
1861- media_type |= p.get_media_type();
1862- publishing_parameters.set_media_type(media_type);
1863-
1864-- this.refresh_token = host.get_config_string("refresh_token", null);
1865- this.progress_reporter = null;
1866- }
1867-
1868-@@ -131,20 +131,9 @@
1869- get_host().set_config_string("last-album", parameters.get_target_album_name());
1870- }
1871-
1872-- private void on_service_welcome_login() {
1873-- debug("EVENT: user clicked 'Login' in welcome pane.");
1874--
1875-- if (!is_running())
1876-- return;
1877--
1878-- start_oauth_flow(refresh_token);
1879-- }
1880--
1881- protected override void on_login_flow_complete() {
1882- debug("EVENT: OAuth login flow complete.");
1883-
1884-- get_host().set_config_string("refresh_token", get_session().get_refresh_token());
1885--
1886- publishing_parameters.set_user_name(get_session().get_user_name());
1887-
1888- do_fetch_account_information();
1889-@@ -173,21 +162,7 @@
1890- debug("EVENT: fetching account and album information failed; response = '%s'.",
1891- bad_txn.get_response());
1892-
1893-- if (bad_txn.get_status_code() == 403 || bad_txn.get_status_code() == 404) {
1894-- do_logout();
1895-- } else {
1896-- // If we get any other kind of error, we can't recover, so just post it to the user
1897-- get_host().post_error(err);
1898-- }
1899-- }
1900--
1901-- private void on_publishing_options_logout() {
1902-- if (!is_running())
1903-- return;
1904--
1905-- debug("EVENT: user clicked 'Logout' in the publishing options pane.");
1906--
1907-- do_logout();
1908-+ get_host().post_error(err);
1909- }
1910-
1911- private void on_publishing_options_publish() {
1912-@@ -295,12 +270,6 @@
1913- get_host().post_error(err);
1914- }
1915-
1916-- private void do_show_service_welcome_pane() {
1917-- debug("ACTION: showing service welcome pane.");
1918--
1919-- get_host().install_welcome_pane(SERVICE_WELCOME_MESSAGE, on_service_welcome_login);
1920-- }
1921--
1922- private void do_fetch_account_information() {
1923- debug("ACTION: fetching account and album information.");
1924-
1925-@@ -364,7 +333,6 @@
1926-
1927- PublishingOptionsPane opts_pane = new PublishingOptionsPane(builder, publishing_parameters);
1928- opts_pane.publish.connect(on_publishing_options_publish);
1929-- opts_pane.logout.connect(on_publishing_options_logout);
1930- get_host().install_dialog_pane(opts_pane);
1931-
1932- get_host().set_service_locked(false);
1933-@@ -425,13 +393,6 @@
1934-
1935- protected override void do_logout() {
1936- debug("ACTION: logging out user.");
1937--
1938-- get_session().deauthenticate();
1939-- refresh_token = null;
1940-- get_host().unset_config_key("refresh_token");
1941--
1942--
1943-- do_show_service_welcome_pane();
1944- }
1945-
1946- public override bool is_running() {
1947-@@ -446,10 +407,7 @@
1948-
1949- running = true;
1950-
1951-- if (refresh_token == null)
1952-- do_show_service_welcome_pane();
1953-- else
1954-- start_oauth_flow(refresh_token);
1955-+ authenticator.authenticate();
1956- }
1957-
1958- public override void stop() {
1959-@@ -625,7 +583,6 @@
1960- private Gtk.ComboBoxText size_combo = null;
1961- private Gtk.CheckButton strip_metadata_check = null;
1962- private Gtk.Button publish_button = null;
1963-- private Gtk.Button logout_button = null;
1964- private SizeDescription[] size_descriptions;
1965- private PublishingParameters parameters;
1966-
1967-@@ -653,7 +610,6 @@
1968- size_combo = (Gtk.ComboBoxText) builder.get_object("size_combo");
1969- strip_metadata_check = (Gtk.CheckButton) this.builder.get_object("strip_metadata_check");
1970- publish_button = (Gtk.Button) builder.get_object("publish_button");
1971-- logout_button = (Gtk.Button) builder.get_object("logout_button");
1972-
1973- // populate any widgets whose contents are programmatically-generated.
1974- login_identity_label.set_label(_("You are logged into Picasa Web Albums as %s.").printf(
1975-@@ -680,7 +636,6 @@
1976- use_existing_radio.clicked.connect(on_use_existing_radio_clicked);
1977- create_new_radio.clicked.connect(on_create_new_radio_clicked);
1978- new_album_entry.changed.connect(on_new_album_entry_changed);
1979-- logout_button.clicked.connect(on_logout_clicked);
1980- publish_button.clicked.connect(on_publish_clicked);
1981- }
1982-
1983-@@ -726,10 +681,6 @@
1984- public_check.set_sensitive(true);
1985- }
1986-
1987-- private void on_logout_clicked() {
1988-- logout();
1989-- }
1990--
1991- private void update_publish_button_sensitivity() {
1992- string album_name = new_album_entry.get_text();
1993- publish_button.set_sensitive(!(album_name.strip() == "" &&
1994-Index: b/plugins/shotwell-publishing/PiwigoPublishing.vala
1995-===================================================================
1996---- a/plugins/shotwell-publishing/PiwigoPublishing.vala
1997-+++ b/plugins/shotwell-publishing/PiwigoPublishing.vala
1998-@@ -42,7 +42,7 @@
1999- public void activation(bool enabled) {
2000- }
2001-
2002-- public Spit.Publishing.Publisher create_publisher(Spit.Publishing.PluginHost host) {
2003-+ public Spit.Publishing.Publisher create_publisher(string? account, Spit.Publishing.PluginHost host) {
2004- return new Publishing.Piwigo.PiwigoPublisher(this, host);
2005- }
2006-
2007-Index: b/plugins/shotwell-publishing/YouTubePublishing.vala
2008-===================================================================
2009---- a/plugins/shotwell-publishing/YouTubePublishing.vala
2010-+++ b/plugins/shotwell-publishing/YouTubePublishing.vala
2011-@@ -39,7 +39,7 @@
2012- info.icons = icon_pixbuf_set;
2013- }
2014-
2015-- public Spit.Publishing.Publisher create_publisher(Spit.Publishing.PluginHost host) {
2016-+ public Spit.Publishing.Publisher create_publisher(string? account, Spit.Publishing.PluginHost host) {
2017- return new Publishing.YouTube.YouTubePublisher(this, host);
2018- }
2019-
2020-Index: b/plugins/shotwell-publishing/facebook_publishing_options_pane.glade
2021-===================================================================
2022---- a/plugins/shotwell-publishing/facebook_publishing_options_pane.glade
2023-+++ b/plugins/shotwell-publishing/facebook_publishing_options_pane.glade
2024-@@ -190,42 +190,29 @@
2025- <object class="GtkBox" id="box2">
2026- <property name="visible">True</property>
2027- <property name="can_focus">False</property>
2028-+ <property name="halign">center</property>
2029- <property name="spacing">32</property>
2030- <property name="homogeneous">True</property>
2031- <child>
2032-- <object class="GtkButton" id="logout_button">
2033-- <property name="label" translatable="yes">_Logout</property>
2034-- <property name="visible">True</property>
2035-- <property name="can_focus">True</property>
2036-- <property name="receives_default">True</property>
2037-- <property name="use_underline">True</property>
2038-- </object>
2039-- <packing>
2040-- <property name="expand">False</property>
2041-- <property name="fill">True</property>
2042-- <property name="padding">80</property>
2043-- <property name="position">0</property>
2044-- </packing>
2045-- </child>
2046-- <child>
2047- <object class="GtkButton" id="publish_button">
2048- <property name="label" translatable="yes">_Publish</property>
2049- <property name="visible">True</property>
2050- <property name="can_focus">True</property>
2051- <property name="receives_default">True</property>
2052- <property name="use_underline">True</property>
2053-+ <property name="width_request">96</property>
2054- </object>
2055- <packing>
2056- <property name="expand">False</property>
2057- <property name="fill">True</property>
2058- <property name="padding">80</property>
2059-- <property name="position">1</property>
2060-+ <property name="position">0</property>
2061- </packing>
2062- </child>
2063- </object>
2064- <packing>
2065- <property name="expand">False</property>
2066-- <property name="fill">True</property>
2067-+ <property name="fill">False</property>
2068- <property name="padding">2</property>
2069- <property name="position">3</property>
2070- </packing>
2071-Index: b/plugins/shotwell-publishing/picasa_publishing_options_pane.glade
2072-===================================================================
2073---- a/plugins/shotwell-publishing/picasa_publishing_options_pane.glade
2074-+++ b/plugins/shotwell-publishing/picasa_publishing_options_pane.glade
2075-@@ -218,28 +218,14 @@
2076- <object class="GtkBox" id="button_area_box">
2077- <property name="visible">True</property>
2078- <property name="can_focus">False</property>
2079-- <property name="margin_left">112</property>
2080-- <property name="margin_right">112</property>
2081-+ <property name="halign">center</property>
2082- <property name="margin_top">48</property>
2083- <property name="margin_bottom">24</property>
2084- <property name="spacing">128</property>
2085- <property name="homogeneous">True</property>
2086- <child>
2087-- <object class="GtkButton" id="logout_button">
2088-- <property name="label" translatable="yes">_Logout</property>
2089-- <property name="visible">True</property>
2090-- <property name="can_focus">True</property>
2091-- <property name="receives_default">True</property>
2092-- <property name="use_underline">True</property>
2093-- </object>
2094-- <packing>
2095-- <property name="expand">False</property>
2096-- <property name="fill">True</property>
2097-- <property name="position">0</property>
2098-- </packing>
2099-- </child>
2100-- <child>
2101- <object class="GtkButton" id="publish_button">
2102-+ <property name="width_request">96</property>
2103- <property name="label" translatable="yes">_Publish</property>
2104- <property name="visible">True</property>
2105- <property name="can_focus">True</property>
2106-@@ -249,13 +235,13 @@
2107- <packing>
2108- <property name="expand">False</property>
2109- <property name="fill">True</property>
2110-- <property name="position">1</property>
2111-+ <property name="position">0</property>
2112- </packing>
2113- </child>
2114- </object>
2115- <packing>
2116- <property name="expand">False</property>
2117-- <property name="fill">True</property>
2118-+ <property name="fill">False</property>
2119- <property name="position">4</property>
2120- </packing>
2121- </child>
2122-Index: b/src/plugins/PublishingInterfaces.vala
2123-===================================================================
2124---- a/src/plugins/PublishingInterfaces.vala
2125-+++ b/src/plugins/PublishingInterfaces.vala
2126-@@ -494,6 +494,7 @@
2127- */
2128- public abstract Spit.Publishing.Publisher.MediaType get_publishable_media_type();
2129-
2130-+ public abstract ulong get_dialog_xid();
2131- //
2132- // For future expansion.
2133- //
2134-@@ -581,18 +582,26 @@
2135- * A factory method that instantiates and returns a new {@link Publisher} object that
2136- * encapsulates a connection to the remote publishing service that this Service describes.
2137- */
2138-- public abstract Spit.Publishing.Publisher create_publisher(Spit.Publishing.PluginHost host);
2139-+ public abstract Spit.Publishing.Publisher create_publisher(string? account, Spit.Publishing.PluginHost host);
2140-
2141- /**
2142- * Returns the kinds of media that this service can work with.
2143- */
2144- public abstract Spit.Publishing.Publisher.MediaType get_supported_media();
2145-
2146-+ /**
2147-+ * Checks whether the service is enabled.
2148-+ */
2149-+ public virtual bool is_enabled() { return true; }
2150-+
2151-+ /**
2152-+ * List the accounts available for this service.
2153-+ */
2154-+ public virtual string[] list_account_names() { return {}; }
2155-+
2156- //
2157- // For future expansion.
2158- //
2159-- protected virtual void reserved0() {}
2160-- protected virtual void reserved1() {}
2161- protected virtual void reserved2() {}
2162- protected virtual void reserved3() {}
2163- protected virtual void reserved4() {}
2164-Index: b/src/publishing/PublishingPluginHost.vala
2165-===================================================================
2166---- a/src/publishing/PublishingPluginHost.vala
2167-+++ b/src/publishing/PublishingPluginHost.vala
2168-@@ -21,7 +21,8 @@
2169- private Spit.Publishing.Publisher.MediaType media_type =
2170- Spit.Publishing.Publisher.MediaType.NONE;
2171-
2172-- public ConcretePublishingHost(Service service, PublishingUI.PublishingDialog dialog,
2173-+ public ConcretePublishingHost(Service service, string? account,
2174-+ PublishingUI.PublishingDialog dialog,
2175- Publishable[] publishables) {
2176- base(service, "sharing");
2177- this.dialog = dialog;
2178-@@ -30,7 +31,7 @@
2179- foreach (Publishable curr_publishable in publishables)
2180- this.media_type |= curr_publishable.get_media_type();
2181-
2182-- this.active_publisher = service.create_publisher(this);
2183-+ this.active_publisher = service.create_publisher(account, this);
2184- }
2185-
2186- private void on_login_clicked() {
2187-@@ -232,6 +233,13 @@
2188-
2189- return report_plugin_upload_progress;
2190- }
2191-+
2192-+ public ulong get_dialog_xid() {
2193-+ if (dialog == null) return 0;
2194-+
2195-+ Gdk.Window window = dialog.get_window();
2196-+ return Gdk.X11Window.get_xid(window);
2197-+ }
2198- }
2199-
2200- }
2201-Index: b/src/publishing/PublishingUI.vala
2202-===================================================================
2203---- a/src/publishing/PublishingUI.vala
2204-+++ b/src/publishing/PublishingUI.vala
2205-@@ -130,6 +130,11 @@
2206- }
2207-
2208- public class PublishingDialog : Gtk.Dialog {
2209-+ private struct AccountService {
2210-+ Spit.Publishing.Service service;
2211-+ string? account;
2212-+ }
2213-+
2214- private const int LARGE_WINDOW_WIDTH = 860;
2215- private const int LARGE_WINDOW_HEIGHT = 688;
2216- private const int COLOSSAL_WINDOW_WIDTH = 1024;
2217-@@ -152,6 +157,7 @@
2218- private Spit.Publishing.Publishable[] publishables;
2219- private Spit.Publishing.ConcretePublishingHost host;
2220- private Spit.PluggableInfo info;
2221-+ private Gee.HashMap<string,AccountService?> services;
2222-
2223- protected PublishingDialog(Gee.Collection<MediaSource> to_publish) {
2224- assert(to_publish.size > 0);
2225-@@ -162,6 +168,8 @@
2226- resizable = false;
2227- delete_event.connect(on_window_close);
2228-
2229-+ services = new Gee.HashMap<string,AccountService?>();
2230-+
2231- publishables = new Spit.Publishing.Publishable[0];
2232- bool has_photos = false;
2233- bool has_videos = false;
2234-@@ -209,38 +217,61 @@
2235- // get the name of the service the user last used
2236- string? last_used_service = Config.Facade.get_instance().get_last_used_service();
2237-
2238-- Spit.Publishing.Service[] loaded_services = load_services(has_photos, has_videos);
2239-+ Spit.Publishing.Service[] loaded_services = load_services(has_photos, has_videos, null);
2240-
2241- Gtk.TreeIter iter;
2242-
2243- foreach (Spit.Publishing.Service service in loaded_services) {
2244-- service_selector_box_model.append(out iter);
2245--
2246- string curr_service_id = service.get_id();
2247-
2248- service.get_info(ref info);
2249-
2250-- if (null != info.icons && 0 < info.icons.length) {
2251-- // check if the icons object is set -- if set use that icon
2252-- service_selector_box_model.set(iter, 0, info.icons[0], 1,
2253-- service.get_pluggable_name());
2254--
2255-- // in case the icons object is not set on the next iteration
2256-- info.icons[0] = Resources.get_icon(Resources.ICON_GENERIC_PLUGIN);
2257-- } else {
2258-- // if icons object is null or zero length use a generic icon
2259-- service_selector_box_model.set(iter, 0, Resources.get_icon(
2260-- Resources.ICON_GENERIC_PLUGIN), 1, service.get_pluggable_name());
2261-+ var account_names = service.list_account_names();
2262-+ if (account_names.length == 0) {
2263-+ account_names += null;
2264- }
2265-+
2266-+ foreach (string? account_name in account_names) {
2267-+ service_selector_box_model.append(out iter);
2268-+
2269-+ string service_name = service.get_pluggable_name();
2270-+ if (account_name != null) {
2271-+ service_name += " (%s)".printf(account_name);
2272-+ }
2273-+
2274-+ AccountService account_service = {
2275-+ service: service,
2276-+ account: account_name
2277-+ };
2278-+ services.set(service_name, account_service);
2279-+
2280-+ if (null != info.icons && 0 < info.icons.length) {
2281-+ // check if the icons object is set -- if set use that icon
2282-+ service_selector_box_model.set(iter, 0, info.icons[0], 1,
2283-+ service_name);
2284-+
2285-+ // in case the icons object is not set on the next iteration
2286-+ info.icons[0] = Resources.get_icon(Resources.ICON_GENERIC_PLUGIN);
2287-+ } else {
2288-+ // if icons object is null or zero length use a generic icon
2289-+ service_selector_box_model.set(iter, 0, Resources.get_icon(
2290-+ Resources.ICON_GENERIC_PLUGIN), 1, service_name);
2291-+ }
2292-
2293-- if (last_used_service == null) {
2294-- service_selector_box.set_active_iter(iter);
2295-- last_used_service = service.get_id();
2296-- } else if (last_used_service == curr_service_id) {
2297-- service_selector_box.set_active_iter(iter);
2298-+ if (last_used_service == null) {
2299-+ service_selector_box.set_active_iter(iter);
2300-+ last_used_service = service.get_id();
2301-+ } else if (last_used_service == curr_service_id) {
2302-+ service_selector_box.set_active_iter(iter);
2303-+ }
2304- }
2305- }
2306-
2307-+ service_selector_box_model.append(out iter);
2308-+ service_selector_box_model.set(iter,
2309-+ 0, Resources.get_icon(Resources.ICON_GENERIC_PLUGIN),
2310-+ 1, _("Add more accounts..."));
2311-+
2312- service_selector_box.changed.connect(on_service_changed);
2313-
2314- central_area_layouter = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
2315-@@ -295,24 +326,35 @@
2316- return loaded_services;
2317- }
2318-
2319-- private static Spit.Publishing.Service[] load_services(bool has_photos, bool has_videos) {
2320-+ private static Spit.Publishing.Service[] load_services(bool has_photos, bool has_videos,
2321-+ out bool has_disabled_services) {
2322- assert (has_photos || has_videos);
2323-
2324- Spit.Publishing.Service[] filtered_services = new Spit.Publishing.Service[0];
2325- Spit.Publishing.Service[] all_services = load_all_services();
2326-
2327-+ has_disabled_services = false;
2328- foreach (Spit.Publishing.Service service in all_services) {
2329-+ bool supports_media = false;
2330-
2331- if (has_photos && !has_videos) {
2332- if ((service.get_supported_media() & Spit.Publishing.Publisher.MediaType.PHOTO) != 0)
2333-- filtered_services += service;
2334-+ supports_media = true;
2335- } else if (!has_photos && has_videos) {
2336- if ((service.get_supported_media() & Spit.Publishing.Publisher.MediaType.VIDEO) != 0)
2337-- filtered_services += service;
2338-+ supports_media = true;
2339- } else {
2340- if (((service.get_supported_media() & Spit.Publishing.Publisher.MediaType.PHOTO) != 0) &&
2341- ((service.get_supported_media() & Spit.Publishing.Publisher.MediaType.VIDEO) != 0))
2342-+ supports_media = true;
2343-+ }
2344-+
2345-+ if (supports_media) {
2346-+ if (service.is_enabled()) {
2347- filtered_services += service;
2348-+ } else {
2349-+ has_disabled_services = true;
2350-+ }
2351- }
2352- }
2353-
2354-@@ -351,15 +393,26 @@
2355- Gee.ArrayList<Video> videos = new Gee.ArrayList<Video>();
2356- MediaSourceCollection.filter_media(to_publish, photos, videos);
2357-
2358-+ bool has_disabled_services = false;
2359- Spit.Publishing.Service[] avail_services =
2360-- load_services((photos.size > 0), (videos.size > 0));
2361-+ load_services((photos.size > 0), (videos.size > 0), out has_disabled_services);
2362-
2363- if (avail_services.length == 0) {
2364-- // There are no enabled publishing services that accept this media type,
2365-- // warn the user.
2366-- AppWindow.error_message_with_title(_("Unable to publish"),
2367-- _("Shotwell cannot publish the selected items because you do not have a compatible publishing plugin enabled. To correct this, choose <b>Edit %s Preferences</b> and enable one or more of the publishing plugins on the <b>Plugins</b> tab.").printf("▸"),
2368-- null, false);
2369-+ if (has_disabled_services) {
2370-+ try {
2371-+ DesktopAppInfo app_info =
2372-+ new DesktopAppInfo ("unity-credentials-panel.desktop");
2373-+ GLib.Process.spawn_command_line_async(app_info.get_commandline() + " application=shotwell");
2374-+ } catch (Error e) {
2375-+ warning ("Error launching Online Accounts: %s", e.message);
2376-+ }
2377-+ } else {
2378-+ // There are no enabled publishing services that accept this media type,
2379-+ // warn the user.
2380-+ AppWindow.error_message_with_title(_("Unable to publish"),
2381-+ _("Shotwell cannot publish the selected items because you do not have a compatible publishing plugin enabled. To correct this, choose <b>Edit %s Preferences</b> and enable one or more of the publishing plugins on the <b>Plugins</b> tab.").printf("▸"),
2382-+ null, false);
2383-+ }
2384-
2385- return;
2386- }
2387-@@ -408,19 +461,29 @@
2388-
2389- string service_name = (string) service_name_val;
2390-
2391-- Spit.Publishing.Service? selected_service = null;
2392-- Spit.Publishing.Service[] services = load_all_services();
2393-- foreach (Spit.Publishing.Service service in services) {
2394-- if (service.get_pluggable_name() == service_name) {
2395-- selected_service = service;
2396-- break;
2397-+ if (!services.has_key(service_name)) {
2398-+ debug("Starting Online Accounts...");
2399-+ try {
2400-+ DesktopAppInfo app_info =
2401-+ new DesktopAppInfo ("unity-credentials-panel.desktop");
2402-+ GLib.Process.spawn_command_line_async(app_info.get_commandline() + " application=shotwell");
2403-+ } catch (Error e) {
2404-+ warning ("Error launching Online Accounts: %s", e.message);
2405- }
2406-+
2407-+ on_close_cancel_clicked();
2408-+ return;
2409-+
2410- }
2411-- assert(selected_service != null);
2412-+
2413-+ AccountService account_service = services.get(service_name);
2414-+ Spit.Publishing.Service? selected_service = account_service.service;
2415-
2416- Config.Facade.get_instance().set_last_used_service(selected_service.get_id());
2417-
2418-- host = new Spit.Publishing.ConcretePublishingHost(selected_service, this, publishables);
2419-+ host = new Spit.Publishing.ConcretePublishingHost(selected_service,
2420-+ account_service.account,
2421-+ this, publishables);
2422- host.start_publishing();
2423- }
2424-
2425-Index: b/misc/shotwell-sharing-facebook.service
2426-===================================================================
2427---- /dev/null
2428-+++ b/misc/shotwell-sharing-facebook.service
2429-@@ -0,0 +1,13 @@
2430-+<?xml version="1.0" encoding="UTF-8"?>
2431-+<service id="shotwell-sharing-facebook">
2432-+ <type>shotwell-sharing</type>
2433-+ <name>Facebook</name>
2434-+ <icon>facebook</icon>
2435-+ <provider>facebook</provider>
2436-+ <template>
2437-+ <group name="auth/oauth2/user_agent">
2438-+ <setting type="as" name="Scope">['publish_actions','user_photos','user_videos']</setting>
2439-+ <setting name="ClientId">1612018629063184</setting>
2440-+ </group>
2441-+ </template>
2442-+</service>
2443-Index: b/misc/shotwell-sharing-flickr.service
2444-===================================================================
2445---- /dev/null
2446-+++ b/misc/shotwell-sharing-flickr.service
2447-@@ -0,0 +1,7 @@
2448-+<?xml version="1.0" encoding="UTF-8"?>
2449-+<service id="shotwell-sharing-flickr">
2450-+ <type>shotwell-sharing</type>
2451-+ <name>Flickr</name>
2452-+ <icon>flickr</icon>
2453-+ <provider>flickr</provider>
2454-+</service>
2455-Index: b/misc/shotwell-sharing-picasa.service
2456-===================================================================
2457---- /dev/null
2458-+++ b/misc/shotwell-sharing-picasa.service
2459-@@ -0,0 +1,22 @@
2460-+<?xml version="1.0" encoding="UTF-8"?>
2461-+<service id="shotwell-sharing-picasa">
2462-+ <type>shotwell-sharing</type>
2463-+ <name>Picasa</name>
2464-+ <icon>icon_picasa</icon>
2465-+ <provider>google</provider>
2466-+ <template>
2467-+ <group name="auth/oauth2">
2468-+ <group name="user_agent">
2469-+ <setting type="as" name="Scope">['http://picasaweb.google.com/data/','https://picasaweb.google.com/data/','https://www.googleapis.com/auth/userinfo.profile']</setting>
2470-+ <setting name="ClientId">1073902228337-nv1s75sfvrt75lfcq4pg3pn7gee8e1d5.apps.googleusercontent.com</setting>
2471-+ <setting name="RedirectUri">https://wiki.ubuntu.com/</setting>
2472-+ </group>
2473-+ <group name="web_server">
2474-+ <setting type="as" name="Scope">['http://picasaweb.google.com/data/','https://picasaweb.google.com/data/','https://www.googleapis.com/auth/userinfo.profile']</setting>
2475-+ <setting name="ClientId">1073902228337-nv1s75sfvrt75lfcq4pg3pn7gee8e1d5.apps.googleusercontent.com</setting>
2476-+ <setting name="ClientSecret">jWoGL5PDGk55ZfpBYNOgG43g</setting>
2477-+ <setting name="RedirectUri">https://wiki.ubuntu.com/</setting>
2478-+ </group>
2479-+ </group>
2480-+ </template>
2481-+</service>
2482-Index: b/misc/shotwell.application.in
2483-===================================================================
2484---- /dev/null
2485-+++ b/misc/shotwell.application.in
2486-@@ -0,0 +1,19 @@
2487-+<?xml version="1.0" encoding="UTF-8" ?>
2488-+<application id="shotwell">
2489-+ <description>Shotwell</description>
2490-+ <desktop-entry>shotwell.desktop</desktop-entry>
2491-+ <translations>shotwell</translations>
2492-+
2493-+ <services>
2494-+ <service id="shotwell-sharing-picasa">
2495-+ <_description>Publish your pictures to Picasa</_description>
2496-+ </service>
2497-+ <service id="shotwell-sharing-facebook">
2498-+ <_description>Publish your pictures to Facebook</_description>
2499-+ </service>
2500-+ <service id="shotwell-sharing-flickr">
2501-+ <_description>Publish your pictures to Flickr</_description>
2502-+ </service>
2503-+ </services>
2504-+
2505-+</application>
2506-Index: b/po/POTFILES.in
2507-===================================================================
2508---- a/po/POTFILES.in
2509-+++ b/po/POTFILES.in
2510-@@ -51,6 +51,7 @@
2511- plugins/shotwell-transitions/SquaresEffect.vala
2512- plugins/shotwell-transitions/StripesEffect.vala
2513- plugins/shotwell-transitions/shotwell-transitions.vala
2514-+[type: gettext/xml]misc/shotwell.application.in
2515- src/AppDirs.vala
2516- src/AppWindow.vala
2517- src/Application.vala
2518-Index: b/plugins/shotwell-publishing-extras/RajcePublishing.vala
2519-===================================================================
2520---- a/plugins/shotwell-publishing-extras/RajcePublishing.vala
2521-+++ b/plugins/shotwell-publishing-extras/RajcePublishing.vala
2522-@@ -45,7 +45,7 @@
2523- info.icons = icon_pixbuf_set;
2524- }
2525-
2526-- public Spit.Publishing.Publisher create_publisher(Spit.Publishing.PluginHost host)
2527-+ public Spit.Publishing.Publisher create_publisher(string? account, Spit.Publishing.PluginHost host)
2528- {
2529- return new Publishing.Rajce.RajcePublisher(this, host);
2530- }
2531-Index: b/plugins/shotwell-publishing-extras/GalleryConnector.vala
2532-===================================================================
2533---- a/plugins/shotwell-publishing-extras/GalleryConnector.vala
2534-+++ b/plugins/shotwell-publishing-extras/GalleryConnector.vala
2535-@@ -97,7 +97,7 @@
2536- }
2537-
2538- public Spit.Publishing.Publisher create_publisher(
2539-- Spit.Publishing.PluginHost host) {
2540-+ string? account, Spit.Publishing.PluginHost host) {
2541- return new Publishing.Gallery3.GalleryPublisher(this, host);
2542- }
2543-
2544
2545=== modified file 'debian/patches/series'
2546--- debian/patches/series 2015-05-26 06:33:54 +0000
2547+++ debian/patches/series 2017-05-03 18:07:14 +0000
2548@@ -1,5 +1,4 @@
2549 hardening.patch
2550 02_desktop_translations.patch
2551-06_uoa.patch
2552 update_translation_list.patch
2553 no_headerbar.patch
2554
2555=== modified file 'debian/rules'
2556--- debian/rules 2015-10-28 16:11:35 +0000
2557+++ debian/rules 2017-05-03 18:07:14 +0000
2558@@ -8,11 +8,11 @@
2559 dh $@ --with-scour
2560
2561 override_dh_auto_configure:
2562- ./configure --prefix=/usr --libexec=/usr/lib/shotwell --disable-schemas-compile --disable-icon-update --unity-support
2563+ ./configure --prefix=/usr --libexec=/usr/lib/shotwell --disable-schemas-compile --disable-icon-update
2564
2565 override_dh_install:
2566 dh_install --fail-missing
2567- # install temporary c files for debuging
2568+ # install temporary c files for debugging
2569 mkdir -p debian/shotwell-dbg/usr/share/doc/shotwell-dbg/temp-source
2570 find -type f -name '*.c' -exec cp --parent '{}' debian/shotwell-dbg/usr/share/doc/shotwell-dbg/temp-source/ ';'
2571 cd debian/shotwell-dbg/usr/share/doc/shotwell-dbg && \
2572
2573=== modified file 'debian/shotwell-dbg.README.Debian'
2574--- debian/shotwell-dbg.README.Debian 2015-10-28 16:11:35 +0000
2575+++ debian/shotwell-dbg.README.Debian 2017-05-03 18:07:14 +0000
2576@@ -1,5 +1,5 @@
2577
2578-For debuging you can copy the files from temp-source.tar.xz into
2579+For debugging you can copy the files from temp-source.tar.xz into
2580 your source directory (//usr/src/shotwell-VERSION) or you can
2581 set the dircetory for gdb with the commandline parameter --directory=DIR.
2582

Subscribers

People subscribed via source and target branches

to all changes: