Merge lp:~mardy/shotwell/uoa-update into lp:ubuntu/quantal/shotwell

Proposed by Alberto Mardegan
Status: Superseded
Proposed branch: lp:~mardy/shotwell/uoa-update
Merge into: lp:ubuntu/quantal/shotwell
Diff against target: 3935 lines (+3831/-0) (has conflicts)
20 files modified
.bzr-builddeb/default.conf (+3/-0)
debian/changelog (+424/-0)
debian/compat (+1/-0)
debian/control (+47/-0)
debian/copyright (+435/-0)
debian/docs (+2/-0)
debian/manpages (+2/-0)
debian/menu (+2/-0)
debian/patches/02_desktop_translations.patch (+25/-0)
debian/patches/03_appmenu_no_stubs.patch (+9/-0)
debian/patches/04_no_resize_grip.patch (+26/-0)
debian/patches/05-gomp-linking.patch (+16/-0)
debian/patches/06_uoa.patch (+2790/-0)
debian/patches/series (+6/-0)
debian/rules (+10/-0)
debian/shotwell-video-thumbnailer.1 (+13/-0)
debian/shotwell.1 (+14/-0)
debian/shotwell.lintian-overrides (+2/-0)
debian/source/format (+1/-0)
debian/watch (+3/-0)
Conflict adding file debian.  Moved existing file to debian.moved.
To merge this branch: bzr merge lp:~mardy/shotwell/uoa-update
Reviewer Review Type Date Requested Status
Ubuntu branches Pending
Review via email: mp+124638@code.launchpad.net

Description of the change

Update the UOA patch
* Support multiple accounts per service
* Attempt automatic login
* Remove logout buttons

To post a comment you must log in.
lp:~mardy/shotwell/uoa-update updated
85. By Alberto Mardegan

i18n update

* Allow translating the shotwell.application file
* Add the "Add more accounts..." string for translations.

86. By Alberto Mardegan

Avoid critical warnings

Vala was generating an enumerated type, but all we want is an int.

87. By Alberto Mardegan

Do not make warnings fatal when building plugins

Gtk.Table, which is used all over the place, has been deprecated.

88. By Alberto Mardegan

* New upstream bugfix release
* debian/watch:
  - Watch for stable versions

Unmerged revisions

88. By Alberto Mardegan

* New upstream bugfix release
* debian/watch:
  - Watch for stable versions

87. By Alberto Mardegan

Do not make warnings fatal when building plugins

Gtk.Table, which is used all over the place, has been deprecated.

86. By Alberto Mardegan

Avoid critical warnings

Vala was generating an enumerated type, but all we want is an int.

85. By Alberto Mardegan

i18n update

* Allow translating the shotwell.application file
* Add the "Add more accounts..." string for translations.

84. By Alberto Mardegan

Update UOA patch

* Support multiple accounts per service
* Remove login/logout buttons

83. By Alberto Mardegan

UOA: handle multiple accounts per service

82. By Sebastien Bacher

releasing version 0.12.90-0ubuntu1

81. By Sebastien Bacher

[ Alberto Mardegan ]
* debian/patches/06_uoa.patch:
  - updated for the new version

80. By Robert Ancell

New upstream release (LP: #1041011)

79. By Robert Ancell

* New upstream release
* debian/watch:
  - Watch for unstable versions
  - Releases now in .xz format

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory '.bzr-builddeb'
2=== added file '.bzr-builddeb/default.conf'
3--- .bzr-builddeb/default.conf 1970-01-01 00:00:00 +0000
4+++ .bzr-builddeb/default.conf 2012-09-17 10:22:22 +0000
5@@ -0,0 +1,3 @@
6+[BUILDDEB]
7+merge = True
8+ignore-unknowns = True
9
10=== added directory 'debian'
11=== renamed directory 'debian' => 'debian.moved'
12=== added file 'debian/changelog'
13--- debian/changelog 1970-01-01 00:00:00 +0000
14+++ debian/changelog 2012-09-17 10:22:22 +0000
15@@ -0,0 +1,424 @@
16+shotwell (0.12.90-0ubuntu1) quantal; urgency=low
17+
18+ [ Robert Ancell ]
19+ * New upstream release (LP: #1041011)
20+ * debian/watch:
21+ - Watch for unstable versions
22+ - Releases now in .xz format
23+
24+ [ Alberto Mardegan ]
25+ * debian/patches/06_uoa.patch:
26+ - updated for the new version
27+
28+ -- Sebastien Bacher <seb128@ubuntu.com> Tue, 28 Aug 2012 16:57:19 +0200
29+
30+shotwell (0.12.3-0ubuntu2) quantal; urgency=low
31+
32+ * debian/patches/06_uoa.patch
33+ - Added UOA support
34+ * debian/control
35+ - added build depends libsignon-glib-dev and libaccounts-glib-dev
36+
37+ -- Ken VanDine <ken.vandine@canonical.com> Tue, 21 Aug 2012 15:40:58 -0400
38+
39+shotwell (0.12.3-0ubuntu1) quantal; urgency=low
40+
41+ * New upstream version, fixes:
42+ - Shotwell deletes tags at random (lp: #999108)
43+ - shotwell's unity progress bar hangs halfway across (lp: #987046)
44+ - Shotwell will not detect mts moviefiles on memory card (lp: #990725)
45+
46+ -- Sebastien Bacher <seb128@ubuntu.com> Wed, 16 May 2012 15:18:16 +0200
47+
48+shotwell (0.12.2-0ubuntu3) quantal; urgency=low
49+
50+ * debian/control:
51+ - Use standards version 3.9.3
52+ - Drop dependency on liblaunchpad-integration-3.0-dev
53+ * debian/patches/01_lpi.patch:
54+ - Dropped, we no longer do Launchpad integration
55+
56+ -- Robert Ancell <robert.ancell@canonical.com> Mon, 14 May 2012 16:14:15 +1200
57+
58+shotwell (0.12.2-0ubuntu2) precise; urgency=low
59+
60+ * debian/control:
61+ - Add build-depends on libunity-dev
62+ * debian/rules:
63+ - Build with --unity-support (LP: #980532)
64+
65+ -- Robert Ancell <robert.ancell@canonical.com> Fri, 13 Apr 2012 09:11:15 +1000
66+
67+shotwell (0.12.2-0ubuntu1) precise; urgency=low
68+
69+ * New upstream release:
70+ - Corrects an issue where graphical corruption of the UI could
71+ occur if the user navigated away from a camera page while camera
72+ previews were being generated.
73+ - Adds enhanced support for the Unity desktop environment.
74+ - Clarified several strings.
75+ - The application now recovers gracefully from corrupted tag data.
76+
77+ -- Robert Ancell <robert.ancell@canonical.com> Thu, 12 Apr 2012 10:44:07 +1000
78+
79+shotwell (0.12.1-0ubuntu2) precise; urgency=low
80+
81+ * debian/control:
82+ - build with current gexiv2 to pick the current soname (lp: #971468)
83+
84+ -- Sebastien Bacher <seb128@ubuntu.com> Mon, 02 Apr 2012 15:35:03 +0200
85+
86+shotwell (0.12.1-0ubuntu1) precise-proposed; urgency=low
87+
88+ * New upstream release:
89+ - Corrects a critical issue where the application could not be started in
90+ direct-edit mode.
91+
92+ -- Robert Ancell <robert.ancell@canonical.com> Thu, 29 Mar 2012 11:37:01 +1100
93+
94+shotwell (0.12.0-0ubuntu1) precise-proposed; urgency=low
95+
96+ * New upstream release:
97+ (Changes since 0.11.6)
98+ - Adds straighten tool
99+ - Ported from GTK 2 to GTK 3
100+ - Better handling of importing from Android devices
101+ - Facebook and Flickr login now use OAuth authentication
102+ - Numerous enhancements to the plugin subsystem
103+ - The F-Spot importer is now implemented as a plugin
104+ - Many, many bugfixes and stability enhancements
105+ * debian/control:
106+ - Bump build-depends on gexiv2
107+
108+ -- Robert Ancell <robert.ancell@canonical.com> Wed, 28 Mar 2012 11:59:51 +1100
109+
110+shotwell (0.11.93-0ubuntu2) precise; urgency=low
111+
112+ * debian/control: build-depends on librest-dev, the publishing makefile
113+ requires it
114+
115+ -- Sebastien Bacher <seb128@ubuntu.com> Wed, 07 Mar 2012 16:08:00 +0100
116+
117+shotwell (0.11.93-0ubuntu1) precise; urgency=low
118+
119+ * New upstream release
120+
121+ -- Robert Ancell <robert.ancell@canonical.com> Wed, 07 Mar 2012 10:27:48 +1100
122+
123+shotwell (0.11.92-0ubuntu1) precise; urgency=low
124+
125+ * New upstream release
126+
127+ -- Robert Ancell <robert.ancell@canonical.com> Tue, 21 Feb 2012 13:52:58 +1100
128+
129+shotwell (0.11.91-0ubuntu2) precise; urgency=low
130+
131+ * debian/control: use the correct liblaunchpad-integration version
132+
133+ -- Sebastien Bacher <seb128@ubuntu.com> Thu, 12 Jan 2012 11:58:24 +0100
134+
135+shotwell (0.11.91-0ubuntu1) precise; urgency=low
136+
137+ * New upstream version using GTK3 (lp: #871034),
138+ should fix lp: #800459, #881896, #887357, #888363, #898028
139+ * Refreshed patches for the new version
140+ * debian/control:
141+ - updated build-dependencies for GTK3
142+ * debian/patches/99git_libraw_api.patch:
143+ - dropped, the patch is in the new version
144+
145+ -- Sebastien Bacher <seb128@ubuntu.com> Wed, 11 Jan 2012 15:59:04 +0100
146+
147+shotwell (0.11.6-0ubuntu2) precise; urgency=low
148+
149+ * debian/patches/99git_libraw_api.patch: cherry-pick changes from git to work
150+ with libraw 0.14.0.
151+ * debian/patches/05-gomp-linking.patch: we also need to link shotwell against
152+ GOMP now, apparently, so add -fopenmp to the Makefile's relevant rule.
153+ * debian/shotwell-video-thumbnailer.1: added, it was missing from the previous
154+ upload and referred to by debian/manpages.
155+
156+ -- Mathieu Trudel-Lapierre <mathieu-tl@ubuntu.com> Fri, 25 Nov 2011 12:31:19 -0500
157+
158+shotwell (0.11.6-0ubuntu1) precise; urgency=low
159+
160+ * New upstream version
161+ * Resynchronize on Debian
162+ * debian/control.in:
163+ - build-depends on python-scour, liblaunchpad-integration-dev
164+ - don't build-depends on locales-all
165+ * debian/rules:
166+ - use configure option --disable-icon-update and dh option --with-scour
167+
168+ -- Sebastien Bacher <seb128@ubuntu.com> Wed, 23 Nov 2011 21:19:35 +0100
169+
170+shotwell (0.11.5-1) unstable; urgency=low
171+
172+ * New upstream release (Closes: 645942).
173+ * Show translations in GNOME menu, thanks to Hideki Yamane
174+ <henrich@debian.or.jp> for this (Closes: 644905).
175+
176+ [ Luca Falavigna ]
177+ * Enable DM upload.
178+
179+ -- Devid Antonio Filoni <d.filoni@ubuntu.com> Sun, 23 Oct 2011 01:39:38 +0200
180+
181+shotwell (0.11.2-1) unstable; urgency=low
182+
183+ * Team upload.
184+ * New upstream bugfix release.
185+ * debian/rules:
186+ - Do not compile GConf schema (Closes: #641677).
187+
188+ -- Luca Falavigna <dktrkranz@debian.org> Sat, 24 Sep 2011 14:27:33 +0200
189+
190+shotwell (0.11.1-1) unstable; urgency=low
191+
192+ * Team upload.
193+ * New upstream bugfix release.
194+ * debian/patches/sidebar-bg-color.patch:
195+ - Refresh for new upstream release.
196+
197+ -- Luca Falavigna <dktrkranz@debian.org> Tue, 13 Sep 2011 20:53:25 +0200
198+
199+shotwell (0.11.0-1) unstable; urgency=low
200+
201+ * Team upload.
202+ * New upstream release (Closes: #639863).
203+ * debian/patches/sidebar-bg-color.patch:
204+ - Refresh for new upstream release.
205+ * debian/control:
206+ - Build-depend on libgstreamer-plugins-base0.10-dev.
207+
208+ -- Luca Falavigna <dktrkranz@debian.org> Thu, 01 Sep 2011 19:59:07 +0200
209+
210+shotwell (0.10.1-1) unstable; urgency=low
211+
212+ * Team upload
213+ * New upstream release (Closes: #629311).
214+ * debian/patches/non-linux-fixes.patch:
215+ - Removed, waiting for proper upstream porting efforts.
216+ * debian/patches/sidebar-bg-color.patch:
217+ - Refresh for new upstream release.
218+ * debian/control:
219+ - Add Debian Shotwell Maintainers to Maintainers.
220+ - Move Devid to Uploaders.
221+ - libwebkitgtk-dev transition (Closes: #635426).
222+ - Set Architecture to linux-any, shotwell is currently unsupported
223+ outside of Linux platform, and require some porting.
224+ * debian/shotwell-video-thumbnailer.1:
225+ - Provide a minimal man page for shotwell-video-thumbnailer
226+
227+ -- Luca Falavigna <dktrkranz@debian.org> Sun, 21 Aug 2011 15:38:31 +0200
228+
229+shotwell (0.9.3-1) unstable; urgency=low
230+
231+ * New upstream release (Closes: #622705).
232+ * Update debian/patches/non-linux-fixes.patch patch.
233+ * Bump Standards-Version to 3.9.2.
234+
235+ -- Devid Antonio Filoni <d.filoni@ubuntu.com> Sun, 01 May 2011 13:09:48 +0200
236+
237+shotwell (0.9.1-1) unstable; urgency=low
238+
239+ * Upload to unstable.
240+ * New upstream release (Closes: #620765).
241+ * debian/control: modify libgexiv2-dev (>= 0.3.1) to libgexiv2-dev
242+ (>= 0.2.2-4) in Build-Depends field.
243+ * Update debian/patches/non-linux-fixes.patch patch.
244+
245+ -- Devid Antonio Filoni <d.filoni@ubuntu.com> Fri, 08 Apr 2011 23:28:01 +0200
246+
247+shotwell (0.9.0-1) experimental; urgency=low
248+
249+ * New upstream release (Closes: #614730, #593660, #619478).
250+ * debian/control: modify valac-0.10 (>= 0.10.4) to valac-0.12 (>= 0.11.7)
251+ in Build-Depends field.
252+ * debian/control: add m4 and libgnomevfs2-dev (>= 2.24.2) to Build-Depends
253+ field.
254+ * debian/control: modify libgexiv2-dev (>= 0.2.0) to libgexiv2-dev
255+ (>= 0.3.1) in Build-Depends field.
256+ * Remove debian/patches/vala-0_10_4.patch patch, fixed upstream.
257+ * Update debian/patches/non-linux-fixes.patch patch.
258+ * Update debian/copyright file.
259+ * Override library-not-linked-against-libc lintian warnings.
260+ * Override image-file-in-usr-lib lintian warnings.
261+
262+ -- Devid Antonio Filoni <d.filoni@ubuntu.com> Sun, 27 Mar 2011 15:07:13 +0200
263+
264+shotwell (0.8.1-4) unstable; urgency=low
265+
266+ * A patch was reverting some changes introduced in 0.8.1-3, apply them.
267+
268+ -- Devid Antonio Filoni <d.filoni@ubuntu.com> Sun, 27 Mar 2011 12:42:49 +0200
269+
270+shotwell (0.8.1-3) unstable; urgency=low
271+
272+ * Update debian/patches/non-linux-fixes.patch patch (Closes: #619682).
273+ * Add debian/patches/vala-0_10_4.patch patch, fix FTBFS caused by
274+ valac 0.10.4.
275+ * debian/control: modify valac-0.10 (>= 0.9.8) to valac-0.10 (>= 0.10.4)
276+ in Build-Depends field.
277+
278+ -- Devid Antonio Filoni <d.filoni@ubuntu.com> Sat, 26 Mar 2011 23:45:32 +0100
279+
280+shotwell (0.8.1-2) unstable; urgency=low
281+
282+ * Upload to unstable (Closes: #614445).
283+ * debian/control: modify libjson-glib-dev (>= 0.10.28) to libjson-glib-dev
284+ (>= 0.7.6)
285+ * debian/control: modify libgstreamer0.10-dev (>= 0.7.6) to
286+ libgstreamer0.10-dev (>= 0.10.28)
287+ * debian/control: modify libraw-dev to libraw-dev (>= 0.13.1-2).
288+
289+ -- Devid Antonio Filoni <d.filoni@ubuntu.com> Mon, 28 Feb 2011 22:16:51 +0100
290+
291+shotwell (0.8.1-1) experimental; urgency=low
292+
293+ * New upstream release.
294+ * debian/control: modify valac-0.10 (>= 0.9.7) to valac-0.10 (>= 0.9.8) in
295+ Build-Depends field.
296+ * Update debian/copyright.
297+
298+ -- Devid Antonio Filoni <d.filoni@ubuntu.com> Thu, 27 Jan 2011 23:05:40 +0100
299+
300+shotwell (0.8.0-1) experimental; urgency=low
301+
302+ * New upstream release.
303+ * debian/control: modify valac (>= 0.9.5) to valac-0.10 (>= 0.9.7) in
304+ Build-Depends field.
305+ * debian/control: add libjson-glib-dev and libgstreamer0.10-dev to
306+ Build-Depends field.
307+ * Update debian/patches/sidebar-bg-color.patch patch.
308+ * Update debian/patches/non-linux-fixes.patch patch.
309+
310+ -- Devid Antonio Filoni <d.filoni@ubuntu.com> Fri, 07 Jan 2011 15:14:41 +0100
311+
312+shotwell (0.7.2-1) experimental; urgency=low
313+
314+ * New upstream release.
315+ * Update debian/patches/sidebar-bg-color.patch patch.
316+
317+ -- Devid Antonio Filoni <d.filoni@ubuntu.com> Wed, 15 Sep 2010 14:56:23 +0200
318+
319+shotwell (0.7.0-1) experimental; urgency=low
320+
321+ * New upstream release.
322+ * debian/control: remove Luca Falavigna from Uploaders field.
323+ * debian/control: update valac version to >= 0.9.5.
324+ * debian/control: update libgexiv2-dev version to >= 0.2.0.
325+ * Update debian/patches/non-linux-fixes.patch patch.
326+ * debian/patches/sidebar-bg-color.patch: don't set sidebar background color
327+ (Closes: #594170).
328+ * Bump Standards-Version to 3.9.1.
329+
330+ -- Devid Antonio Filoni <d.filoni@ubuntu.com> Tue, 24 Aug 2010 17:34:23 +0200
331+
332+shotwell (0.6.1-1) unstable; urgency=low
333+
334+ * New upstream release.
335+ * debian/control:
336+ - Build-depend on libusb-dev.
337+ - Bump libgexiv2-dev version to be at least 0.1.0.
338+
339+ -- Luca Falavigna <dktrkranz@debian.org> Mon, 05 Jul 2010 20:05:18 +0200
340+
341+shotwell (0.6.0-1) unstable; urgency=low
342+
343+ * New upstream release.
344+ * debian/patches/non-linux-fixes.patch:
345+ - Refresh for new upstream release.
346+ * debian/control:
347+ - Bump Standards-Version to 3.9.0, no changes required.
348+
349+ -- Luca Falavigna <dktrkranz@debian.org> Wed, 30 Jun 2010 20:48:44 +0200
350+
351+shotwell (0.5.91-2) experimental; urgency=low
352+
353+ * debian/patches/non-linux-fixes.patch:
354+ - Let libraw to be available under non-Linux architectures, fix FTBFS
355+ on kFreeBSD (Closes: #586634).
356+
357+ -- Luca Falavigna <dktrkranz@debian.org> Mon, 21 Jun 2010 23:24:16 +0200
358+
359+shotwell (0.5.91-1) experimental; urgency=low
360+
361+ * New upstream development release.
362+ * debian/patches/non-linux-fixes.patch:
363+ - Refresh for new upstream release.
364+ * debian/control:
365+ - Build-depend on libgexiv2-dev and libraw-dev.
366+ - No longer build-depend on libhal-dev.
367+ - Adjust build-dependencies versioning.
368+ * debian/README.source:
369+ - Upstream clarified licensing of some icons with their authors, now
370+ they are released under CC-BY-SA-3.0, so there is no need to repack
371+ upstream tarball anymore, thus removing this file.
372+
373+ -- Luca Falavigna <dktrkranz@debian.org> Sun, 20 Jun 2010 16:09:43 +0200
374+
375+shotwell (0.5.2+dfsg-2) unstable; urgency=low
376+
377+ * debian/patches/non-linux-fixes.patch:
378+ - Add Hurd bits to allow build on that architecture.
379+
380+ -- Luca Falavigna <dktrkranz@debian.org> Mon, 07 Jun 2010 23:41:52 +0200
381+
382+shotwell (0.5.2+dfsg-1) unstable; urgency=low
383+
384+ * New upstream release (Closes: #578903).
385+ * Drop vala_0.8.0.patch patch, already applied by upstream.
386+ * debian/control: add Luca Falavigna to Uploaders field.
387+ * Add debian/patches/non-linux-fixes.patch patch from Peter Green to add
388+ support for kfreebsd and hurd (Closes: #581662).
389+ * debian/control: replace "libgudev-1.0-dev (>= 145)" with "libgudev-1.0-dev
390+ (>= 145) [!kfreebsd-i386 !kfreebsd-amd64 !hurd-i386]" as suggested by
391+ Peter Green.
392+
393+ -- Devid Antonio Filoni <d.filoni@ubuntu.com> Sun, 23 May 2010 18:40:49 +0200
394+
395+shotwell (0.5.0+dfsg-1.1) unstable; urgency=low
396+
397+ * Non-maintainer upload.
398+ * debian/patches/vala_0.8.0.patch:
399+ - Build with vala 0.8.0 (Closes: #577913).
400+ * debian/control:
401+ - Depend on librsvg2-common and dbus-x11 (Closes: #574112).
402+ - Bump minimum valac version to 0.8.0.
403+
404+ -- Luca Falavigna <dktrkranz@debian.org> Sun, 18 Apr 2010 23:16:32 +0200
405+
406+shotwell (0.5.0+dfsg-1) unstable; urgency=low
407+
408+ * New upstream release.
409+ * Update debian/copyright.
410+ * Remove debian/patches directory, patches already applied upstream.
411+ * debian/control: add libgudev-1.0-dev (>= 145) to Build-Depends field.
412+ * Bump Standards-Version to 3.8.4.
413+
414+ -- Devid Antonio Filoni <d.filoni@ubuntu.com> Mon, 15 Mar 2010 15:59:23 +0100
415+
416+shotwell (0.4.3+dfsg-1.1) unstable; urgency=low
417+
418+ * Non-maintainer upload.
419+ * debian/patches/02_vala_0.7.10.patch:
420+ - Fix build with vala 0.7.10 (Closes: #569370).
421+ * debian/control:
422+ - Bump minimum valac version to 0.7.10.
423+
424+ -- Luca Falavigna <dktrkranz@debian.org> Sat, 20 Feb 2010 18:03:45 +0100
425+
426+shotwell (0.4.3+dfsg-1) unstable; urgency=low
427+
428+ * New upstream release.
429+ * Switch to format 3.0 (quilt).
430+ * Add debian/patches/01_GNU-kFreeBSD.diff patch from Cyril Brulebois to fix
431+ FTBFS on GNU/kFreeBSD (Closes: #564306).
432+
433+ -- Devid Antonio Filoni <d.filoni@ubuntu.com> Sat, 23 Jan 2010 18:56:33 +0100
434+
435+shotwell (0.4.2+dfsg-1) unstable; urgency=low
436+
437+ * Initial release (Closes: #561788).
438+
439+ -- Devid Antonio Filoni <d.filoni@ubuntu.com> Wed, 06 Jan 2010 14:08:43 +0100
440
441=== added file 'debian/compat'
442--- debian/compat 1970-01-01 00:00:00 +0000
443+++ debian/compat 2012-09-17 10:22:22 +0000
444@@ -0,0 +1,1 @@
445+7
446
447=== added file 'debian/control'
448--- debian/control 1970-01-01 00:00:00 +0000
449+++ debian/control 2012-09-17 10:22:22 +0000
450@@ -0,0 +1,47 @@
451+Source: shotwell
452+Section: gnome
453+Priority: optional
454+Maintainer: Ubuntu Desktop Team <ubuntu-desktop@lists.ubuntu.com>
455+XSBC-Original-Maintainer: Debian Shotwell Maintainers <pkg-shotwell-maint@lists.alioth.debian.org>
456+Uploaders: Devid Antonio Filoni <d.filoni@ubuntu.com>
457+Build-Depends: debhelper (>= 7.0.50~),
458+ m4,
459+ valac-0.18 (>= 0.17.2),
460+ libgee-dev (>= 0.5.0),
461+ libglib2.0-dev (>= 2.30.0),
462+ libgtk-3-dev (>= 3.0.11),
463+ libexif-dev (>= 0.6.16),
464+ libsqlite3-dev (>= 3.5.9),
465+ libgexiv2-dev (>= 0.4.1-1build1),
466+ libgnomevfs2-dev (>= 2.24.2),
467+ libgphoto2-2-dev (>= 2.4.2),
468+ libsoup2.4-dev (>= 2.26.0),
469+ libxml2 (>= 2.6.32),
470+ libunique-3.0-dev (>= 1.0.0),
471+ libwebkitgtk-3.0-dev (>= 1.1.5),
472+ libgudev-1.0-dev (>= 145),
473+ libdbus-glib-1-dev (>= 0.80),
474+ libraw-dev (>= 0.13.2),
475+ libusb-dev,
476+ libjson-glib-dev (>= 0.7.6),
477+ libgstreamer0.10-dev (>= 0.10.28),
478+ libgstreamer-plugins-base0.10-dev (>= 0.10.32),
479+ python-scour,
480+ libunity-dev,
481+ librest-dev,
482+ libsignon-glib-dev,
483+ libaccounts-glib-dev
484+DM-Upload-Allowed: yes
485+Standards-Version: 3.9.3
486+Homepage: http://yorba.org/shotwell/
487+Vcs-Bzr: https://code.launchpad.net/~ubuntu-desktop/shotwell/ubuntu
488+
489+Package: shotwell
490+Architecture: linux-any
491+Depends: ${shlibs:Depends}, ${misc:Depends}, librsvg2-common, dbus-x11
492+Recommends: account-plugin-facebook, account-plugin-google, account-plugin-flickr
493+Description: digital photo organizer
494+ Shotwell is a digital photo organizer designed for the GNOME desktop
495+ environment. It allows you to import photos from disk or camera, organize
496+ them in various ways, view them in full-window or fullscreen mode, and
497+ export them to share with others.
498
499=== added file 'debian/copyright'
500--- debian/copyright 1970-01-01 00:00:00 +0000
501+++ debian/copyright 2012-09-17 10:22:22 +0000
502@@ -0,0 +1,435 @@
503+This work was packaged for Debian by:
504+
505+ Devid Antonio Filoni <d.filoni@ubuntu.com> on Wed, 06 Jan 2010 14:08:43 +0100
506+
507+It was downloaded from http://yorba.org/download/shotwell/
508+
509+
510+
511+Upstream Authors:
512+
513+ Jim Nelson <jim@yorba.org>
514+ Lucas Beeler <lucas@yorba.org>
515+ Allison Barlow <allison@yorba.org>
516+
517+Copyright:
518+
519+ Copyright © 2009-2011 Yorba Foundation
520+ Copyright © 2010 Evgeniy Polyakov
521+ Copyright © 2010 Guillaume Viguier-Just
522+ Copyright © 2010 Maxim Kartashev
523+
524+License:
525+
526+ This package is free software; you can redistribute it and/or
527+ modify it under the terms of the GNU Lesser General Public
528+ License as published by the Free Software Foundation; either
529+ version 2.1 of the License, or (at your option) any later version.
530+
531+ This library is distributed in the hope that it will be useful,
532+ but WITHOUT ANY WARRANTY; without even the implied warranty of
533+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
534+ Lesser General Public License for more details.
535+
536+ You should have received a copy of the GNU Lesser General Public
537+ License along with this package; if not, write to the Free Software
538+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
539+
540+On Debian systems, the complete text of the GNU Lesser General
541+Public License can be found in `/usr/share/common-licenses/LGPL-2.1'.
542+
543+
544+
545+The Debian packaging is:
546+
547+ Copyright © 2009-2011 Devid Antonio Filoni <d.filoni@ubuntu.com>
548+
549+and is licensed under the GPL version 2,
550+see `/usr/share/common-licenses/GPL-2'.
551+
552+
553+
554+Authors of vapi/ige-mac-integration.vapi:
555+
556+ Michael Natterer <mitch@imendio.com>
557+ Richard Hult <richard@imendio.com>
558+ Mikael Hallendal <micke@imendio.com>
559+ John Ralls <jralls@ceridwen.us>
560+
561+
562+
563+icons/pin-toolbar.png, icons/hidden.svg, icons/crop.* and icons/import.* are
564+taken from Breathe Icon Set (https://launchpad.net/breathe-icon-set).
565+
566+icons/import-all.png is a derivate work of icons/import.svg and is licensed
567+under Creative Commons Attribution-ShareAlike 3.0.
568+
569+Upstream Authors of Breathe Icon Set:
570+
571+ Sebastian Porta <sebastianporta@gmail.com>
572+ Cory Kontros <coryisatm@ubuntu.com>
573+ Oliver Scholtz <scholli_tz@yahoo.de>
574+ Daniel Planas <daniplanas.a@gmail.com>
575+
576+Copyright of Breathe Icon Set:
577+
578+ © 2009, Sebastian Porta <sebastianporta@gmail.com>
579+ © 2009, Cory Kontros <coryisatm@ubuntu.com>
580+ © 2009, Oliver Scholtz <scholli_tz@yahoo.de>
581+ © 2009, Daniel Planas <daniplanas.a@gmail.com>
582+ © 2007, David Vignoni <david@icon-king.com>
583+ © 2007, Johann Ollivier Lapeyre <johann@oxygen-icons.org>
584+ © 2007, Kenneth Wimer <kwwii@bootsplash.org>
585+ © 2007, Nuno Fernades Pinheiro <nf.pinheiro@gmail.com>
586+ © 2007, Riccardo Iaconelli <riccardo@oxygen-icons.org>
587+ © 2007, David Miller <miller@oxygen-icons.org>
588+ © 2009, Rosetta Contributors and Canonical Ltd.
589+
590+License of Breathe Icon Set:
591+
592+ Creative Commons Attribution-ShareAlike 3.0
593+
594+ THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS
595+ CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS
596+ PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE
597+ WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS
598+ PROHIBITED.
599+
600+ BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND
601+ AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS
602+ LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU
603+ THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH
604+ TERMS AND CONDITIONS.
605+
606+ 1. Definitions
607+
608+ 1. "Adaptation" means a work based upon the Work, or upon the Work and
609+ other pre-existing works, such as a translation, adaptation, derivative
610+ work, arrangement of music or other alterations of a literary or artistic
611+ work, or phonogram or performance and includes cinematographic adaptations
612+ or any other form in which the Work may be recast, transformed, or adapted
613+ including in any form recognizably derived from the original, except that a
614+ work that constitutes a Collection will not be considered an Adaptation for
615+ the purpose of this License. For the avoidance of doubt, where the Work is
616+ a musical work, performance or phonogram, the synchronization of the Work
617+ in timed-relation with a moving image ("synching") will be considered an
618+ Adaptation for the purpose of this License.
619+
620+ 2. "Collection" means a collection of literary or artistic works, such as
621+ encyclopedias and anthologies, or performances, phonograms or broadcasts,
622+ or other works or subject matter other than works listed in Section 1(f)
623+ below, which, by reason of the selection and arrangement of their contents,
624+ constitute intellectual creations, in which the Work is included in its
625+ entirety in unmodified form along with one or more other contributions,
626+ each constituting separate and independent works in themselves, which
627+ together are assembled into a collective whole. A work that constitutes a
628+ Collection will not be considered an Adaptation (as defined above) for the
629+ purposes of this License.
630+
631+ 3. "Creative Commons Compatible License" means a license that is listed at
632+ http://creativecommons.org/compatiblelicenses that has been approved by
633+ Creative Commons as being essentially equivalent to this License, including,
634+ at a minimum, because that license: (i) contains terms that have the same
635+ purpose, meaning and effect as the License Elements of this License; and,
636+ (ii) explicitly permits the relicensing of adaptations of works made
637+ available under that license under this License or a Creative Commons
638+ jurisdiction license with the same License Elements as this License.
639+
640+ 4. "Distribute" means to make available to the public the original and
641+ copies of the Work or Adaptation, as appropriate, through sale or other
642+ transfer of ownership.
643+
644+ 5. "License Elements" means the following high-level license attributes
645+ as selected by Licensor and indicated in the title of this License:
646+ Attribution, ShareAlike.
647+
648+ 6. "Licensor" means the individual, individuals, entity or entities that
649+ offer(s) the Work under the terms of this License.
650+
651+ 7. "Original Author" means, in the case of a literary or artistic work,
652+ the individual, individuals, entity or entities who created the Work or
653+ if no individual or entity can be identified, the publisher; and in
654+ addition (i) in the case of a performance the actors, singers, musicians,
655+ dancers, and other persons who act, sing, deliver, declaim, play in,
656+ interpret or otherwise perform literary or artistic works or expressions
657+ of folklore; (ii) in the case of a phonogram the producer being the person
658+ or legal entity who first fixes the sounds of a performance or other
659+ sounds; and, (iii) in the case of broadcasts, the organization that
660+ transmits the broadcast.
661+
662+ 8. "Work" means the literary and/or artistic work offered under the
663+ terms of this License including without limitation any production in
664+ the literary, scientific and artistic domain, whatever may be the mode
665+ or form of its expression including digital form, such as a book,
666+ pamphlet and other writing; a lecture, address, sermon or other
667+ work of the same nature; a dramatic or dramatico-musical work; a
668+ choreographic work or entertainment in dumb show; a musical composition
669+ with or without words; a cinematographic work to which are assimilated
670+ works expressed by a process analogous to cinematography; a work of
671+ drawing, painting, architecture, sculpture, engraving or lithography; a
672+ photographic work to which are assimilated works expressed by a
673+ process analogous to photography; a work of applied art; an illustration,
674+ map, plan, sketch or three-dimensional work relative to geography,
675+ topography, architecture or science; a performance; a broadcast; a
676+ phonogram; a compilation of data to the extent it is protected as a
677+ copyrightable work; or a work performed by a variety or circus performer
678+ to the extent it is not otherwise considered a literary or artistic work.
679+
680+ 9. "You" means an individual or entity exercising rights under this
681+ License who has not previously violated the terms of this License with
682+ respect to the Work, or who has received express permission from the
683+ Licensor to exercise rights under this License despite a previous
684+ violation.
685+
686+ 10. "Publicly Perform" means to perform public recitations of the Work
687+ and to communicate to the public those public recitations, by any means
688+ or process, including by wire or wireless means or public digital
689+ performances; to make available to the public Works in such a way that
690+ members of the public may access these Works from a place and at a place
691+ individually chosen by them; to perform the Work to the public by any
692+ means or process and the communication to the public of the performances
693+ of the Work, including by public digital performance; to broadcast and
694+ rebroadcast the Work by any means including signs, sounds or images.
695+
696+ 11. "Reproduce" means to make copies of the Work by any means including
697+ without limitation by sound or visual recordings and the right of
698+ fixation and reproducing fixations of the Work, including storage of
699+ a protected performance or phonogram in digital form or other electronic
700+ medium.
701+
702+ 2. Fair Dealing Rights. Nothing in this License is intended to reduce,
703+ limit, or restrict any uses free from copyright or rights arising from
704+ limitations or exceptions that are provided for in connection with the
705+ copyright protection under copyright law or other applicable laws.
706+
707+ 3. License Grant. Subject to the terms and conditions of this License,
708+ Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
709+ perpetual (for the duration of the applicable copyright) license to
710+ exercise the rights in the Work as stated below:
711+
712+ 1. to Reproduce the Work, to incorporate the Work into one or more
713+ Collections, and to Reproduce the Work as incorporated in the Collections;
714+ 2. to create and Reproduce Adaptations provided that any such Adaptation,
715+ including any translation in any medium, takes reasonable steps to
716+ clearly label, demarcate or otherwise identify that changes were made to
717+ the original Work. For example, a translation could be marked "The
718+ original work was translated from English to Spanish," or a modification
719+ could indicate "The original work has been modified.";
720+ 3. to Distribute and Publicly Perform the Work including as incorporated
721+ in Collections; and,
722+ 4. to Distribute and Publicly Perform Adaptations.
723+ 5. For the avoidance of doubt:
724+ 1. Non-waivable Compulsory License Schemes. In those jurisdictions
725+ in which the right to collect royalties through any statutory or
726+ compulsory licensing scheme cannot be waived, the Licensor reserves
727+ the exclusive right to collect such royalties for any exercise by You
728+ of the rights granted under this License;
729+ 2. Waivable Compulsory License Schemes. In those jurisdictions in
730+ which the right to collect royalties through any statutory or compulsory
731+ licensing scheme can be waived, the Licensor waives the exclusive right
732+ to collect such royalties for any exercise by You of the rights granted
733+ under this License; and,
734+ 3. Voluntary License Schemes. The Licensor waives the right to
735+ collect royalties, whether individually or, in the event that the Licensor
736+ is a member of a collecting society that administers voluntary licensing
737+ schemes, via that society, from any exercise by You of the rights granted
738+ under this License.
739+
740+ The above rights may be exercised in all media and formats whether now
741+ known or hereafter devised. The above rights include the right to make
742+ such modifications as are technically necessary to exercise the rights
743+ in other media and formats. Subject to Section 8(f), all rights not
744+ expressly granted by Licensor are hereby reserved.
745+
746+ 4. Restrictions. The license granted in Section 3 above is expressly
747+ made subject to and limited by the following restrictions:
748+
749+ 1. You may Distribute or Publicly Perform the Work only under the terms
750+ of this License. You must include a copy of, or the Uniform Resource
751+ Identifier (URI) for, this License with every copy of the Work You
752+ Distribute or Publicly Perform. You may not offer or impose any terms on
753+ the Work that restrict the terms of this License or the ability of the
754+ recipient of the Work to exercise the rights granted to that recipient
755+ under the terms of the License. You may not sublicense the Work. You must
756+ keep intact all notices that refer to this License and to the disclaimer of
757+ warranties with every copy of the Work You Distribute or Publicly Perform.
758+ When You Distribute or Publicly Perform the Work, You may not impose any
759+ effective technological measures on the Work that restrict the ability of a
760+ recipient of the Work from You to exercise the rights granted to that
761+ recipient under the terms of the License. This Section 4(a) applies to the
762+ Work as incorporated in a Collection, but this does not require the
763+ Collection apart from the Work itself to be made subject to the terms of
764+ this License. If You create a Collection, upon notice from any Licensor You
765+ must, to the extent practicable, remove from the Collection any credit as
766+ required by Section 4(c), as requested. If You create an Adaptation, upon
767+ notice from any Licensor You must, to the extent practicable, remove from
768+ the Adaptation any credit as required by Section 4(c), as requested.
769+
770+ 2. You may Distribute or Publicly Perform an Adaptation only under the
771+ terms of: (i) this License; (ii) a later version of this License with the
772+ same License Elements as this License; (iii) a Creative Commons
773+ jurisdiction license (either this or a later license version) that contains
774+ the same License Elements as this License (e.g., Attribution-ShareAlike
775+ 3.0 US)); (iv) a Creative Commons Compatible License. If you license the
776+ Adaptation under one of the licenses mentioned in (iv), you must comply
777+ with the terms of that license. If you license the Adaptation under the
778+ terms of any of the licenses mentioned in (i), (ii) or (iii) (the
779+ "Applicable License"), you must comply with the terms of the Applicable
780+ License generally and the following provisions: (I) You must include a
781+ copy of, or the URI for, the Applicable License with every copy of each
782+ Adaptation You Distribute or Publicly Perform; (II) You may not offer or
783+ impose any terms on the Adaptation that restrict the terms of the
784+ Applicable License or the ability of the recipient of the Adaptation to
785+ exercise the rights granted to that recipient under the terms of the
786+ Applicable License; (III) You must keep intact all notices that refer to
787+ the Applicable License and to the disclaimer of warranties with every copy
788+ of the Work as included in the Adaptation You Distribute or Publicly
789+ Perform; (IV) when You Distribute or Publicly Perform the Adaptation, You
790+ may not impose any effective technological measures on the Adaptation that
791+ restrict the ability of a recipient of the Adaptation from You to exercise
792+ the rights granted to that recipient under the terms of the Applicable
793+ License. This Section 4(b) applies to the Adaptation as incorporated in a
794+ Collection, but this does not require the Collection apart from the
795+ Adaptation itself to be made subject to the terms of the Applicable
796+ License.
797+
798+ 3. If You Distribute, or Publicly Perform the Work or any Adaptations or
799+ Collections, You must, unless a request has been made pursuant to Section
800+ 4(a), keep intact all copyright notices for the Work and provide,
801+ reasonable to the medium or means You are utilizing: (i) the name of the
802+ Original Author (or pseudonym, if applicable) if supplied, and/or if the
803+ Original Author and/or Licensor designate another party or parties (e.g.,
804+ a sponsor institute, publishing entity, journal) for attribution
805+ ("Attribution Parties") in Licensor's copyright notice, terms of service
806+ or by other reasonable means, the name of such party or parties; (ii) the
807+ title of the Work if supplied; (iii) to the extent reasonably practicable,
808+ the URI, if any, that Licensor specifies to be associated with the Work,
809+ unless such URI does not refer to the copyright notice or licensing
810+ information for the Work; and (iv) , consistent with Ssection 3(b), in the
811+ case of an Adaptation, a credit identifying the use of the Work in the
812+ Adaptation (e.g., "French translation of the Work by Original Author," or
813+ "Screenplay based on original Work by Original Author"). The credit
814+ required by this Section 4(c) may be implemented in any reasonable manner;
815+ provided, however, that in the case of a Adaptation or Collection, at a
816+ minimum such credit will appear, if a credit for all contributing authors
817+ of the Adaptation or Collection appears, then as part of these credits and
818+ in a manner at least as prominent as the credits for the other contributing
819+ authors. For the avoidance of doubt, You may only use the credit required by
820+ this Section for the purpose of attribution in the manner set out above and,
821+ by exercising Your rights under this License, You may not implicitly or
822+ explicitly assert or imply any connection with, sponsorship or endorsement
823+ by the Original Author, Licensor and/or Attribution Parties, as
824+ appropriate, of You or Your use of the Work, without the separate, express
825+ prior written permission of the Original Author, Licensor and/or
826+ Attribution Parties.
827+
828+ 4. Except as otherwise agreed in writing by the Licensor or as may be
829+ otherwise permitted by applicable law, if You Reproduce, Distribute or
830+ Publicly Perform the Work either by itself or as part of any Adaptations
831+ or Collections, You must not distort, mutilate, modify or take other
832+ derogatory action in relation to the Work which would be prejudicial to
833+ the Original Author's honor or reputation. Licensor agrees that in those
834+ jurisdictions (e.g. Japan), in which any exercise of the right granted in
835+ Section 3(b) of this License (the right to make Adaptations) would be
836+ deemed to be a distortion, mutilation, modification or other derogatory
837+ action prejudicial to the Original Author's honor and reputation, the
838+ Licensor will waive or not assert, as appropriate, this Section, to the
839+ fullest extent permitted by the applicable national law, to enable You to
840+ reasonably exercise Your right under Section 3(b) of this License (right
841+ to make Adaptations) but not otherwise.
842+
843+ 5. Representations, Warranties and Disclaimer
844+
845+ UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR
846+ OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
847+ KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
848+ INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
849+ FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
850+ LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS,
851+ WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE
852+ EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
853+
854+ 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
855+ LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
856+ ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
857+ ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
858+ BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
859+
860+ 7. Termination
861+
862+ 1. This License and the rights granted hereunder will terminate
863+ automatically upon any breach by You of the terms of this License.
864+ Individuals or entities who have received Adaptations or Collections
865+ from You under this License, however, will not have their licenses
866+ terminated provided such individuals or entities remain in full
867+ compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will
868+ survive any termination of this License.
869+ 2. Subject to the above terms and conditions, the license granted
870+ here is perpetual (for the duration of the applicable copyright in
871+ the Work). Notwithstanding the above, Licensor reserves the right to
872+ release the Work under different license terms or to stop distributing
873+ the Work at any time; provided, however that any such election will
874+ not serve to withdraw this License (or any other license that has been,
875+ or is required to be, granted under the terms of this License), and this
876+ License will continue in full force and effect unless terminated as stated
877+ above.
878+
879+ 8. Miscellaneous
880+
881+ 1. Each time You Distribute or Publicly Perform the Work or a Collection,
882+ the Licensor offers to the recipient a license to the Work on the same
883+ terms and conditions as the license granted to You under this License.
884+ 2. Each time You Distribute or Publicly Perform an Adaptation, Licensor
885+ offers to the recipient a license to the original Work on the same terms
886+ and conditions as the license granted to You under this License.
887+ 3. If any provision of this License is invalid or unenforceable under
888+ applicable law, it shall not affect the validity or enforceability of
889+ the remainder of the terms of this License, and without further action
890+ by the parties to this agreement, such provision shall be reformed to
891+ the minimum extent necessary to make such provision valid and enforceable.
892+ 4. No term or provision of this License shall be deemed waived and no
893+ breach consented to unless such waiver or consent shall be in writing
894+ and signed by the party to be charged with such waiver or consent.
895+ 5. This License constitutes the entire agreement between the parties
896+ with respect to the Work licensed here. There are no understandings,
897+ agreements or representations with respect to the Work not specified
898+ here. Licensor shall not be bound by any additional provisions that
899+ may appear in any communication from You. This License may not be
900+ modified without the mutual written agreement of the Licensor and You.
901+ 6. The rights granted under, and the subject matter referenced, in this
902+ License were drafted utilizing the terminology of the Berne Convention
903+ for the Protection of Literary and Artistic Works (as amended on
904+ September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
905+ Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
906+ and the Universal Copyright Convention (as revised on July 24, 1971).
907+ These rights and subject matter take effect in the relevant jurisdiction
908+ in which the License terms are sought to be enforced according to the
909+ corresponding provisions of the implementation of those treaty
910+ provisions in the applicable national law. If the standard suite of
911+ rights granted under applicable copyright law includes additional rights
912+ not granted under this License, such additional rights are deemed to be
913+ included in the License; this License is not intended to restrict the
914+ license of any rights under applicable law.
915+
916+ Creative Commons Notice
917+
918+ Creative Commons is not a party to this License, and makes no warranty
919+ whatsoever in connection with the Work. Creative Commons will not be
920+ liable to You or any party on any legal theory for any damages whatsoever,
921+ vincluding without limitation any general, special, incidental or
922+ consequential damages arising in connection to this license.
923+ Notwithstanding the foregoing two (2) sentences, if Creative Commons has
924+ expressly identified itself as the Licensor hereunder, it shall have all
925+ rights and obligations of Licensor.
926+
927+ Except for the limited purpose of indicating to the public that the
928+ Work is licensed under the CCPL, Creative Commons does not authorize
929+ the use by either party of the trademark "Creative Commons" or any
930+ related trademark or logo of Creative Commons without the prior written
931+ consent of Creative Commons. Any permitted use will be in compliance
932+ with Creative Commons' then-current trademark usage guidelines, as may
933+ be published on its website or otherwise made available upon request
934+ from time to time. For the avoidance of doubt, this trademark restriction
935+ does not form part of this License.
936+
937+ Creative Commons may be contacted at http://creativecommons.org/.
938
939=== added file 'debian/docs'
940--- debian/docs 1970-01-01 00:00:00 +0000
941+++ debian/docs 2012-09-17 10:22:22 +0000
942@@ -0,0 +1,2 @@
943+README
944+THANKS
945
946=== added file 'debian/manpages'
947--- debian/manpages 1970-01-01 00:00:00 +0000
948+++ debian/manpages 2012-09-17 10:22:22 +0000
949@@ -0,0 +1,2 @@
950+debian/shotwell.1
951+debian/shotwell-video-thumbnailer.1
952
953=== added file 'debian/menu'
954--- debian/menu 1970-01-01 00:00:00 +0000
955+++ debian/menu 2012-09-17 10:22:22 +0000
956@@ -0,0 +1,2 @@
957+?package(shotwell):needs="X11" section="Applications/Viewers"\
958+ title="Shotwell" command="/usr/bin/shotwell"
959
960=== added directory 'debian/patches'
961=== added file 'debian/patches/02_desktop_translations.patch'
962--- debian/patches/02_desktop_translations.patch 1970-01-01 00:00:00 +0000
963+++ debian/patches/02_desktop_translations.patch 2012-09-17 10:22:22 +0000
964@@ -0,0 +1,25 @@
965+Description: Don't translate the .desktop files, they work with gettext and the build system translates them all into English
966+Bug-Ubuntu: https://bugs.launchpad.net/bugs/760978
967+
968+Index: shotwell-0.12.90/Makefile
969+===================================================================
970+--- shotwell-0.12.90.orig/Makefile 2012-08-24 10:38:39.000000000 +1200
971++++ shotwell-0.12.90/Makefile 2012-08-24 15:03:05.212013701 +1200
972+@@ -536,17 +536,6 @@
973+ install:
974+ cp misc/shotwell.desktop.head misc/shotwell.desktop
975+ cp misc/shotwell-viewer.desktop.head misc/shotwell-viewer.desktop
976+- $(foreach lang,$(CORE_SUPPORTED_LANGUAGES), echo X-GNOME-FullName[$(lang)]=`TEXTDOMAINDIR=locale-langpack \
977+- LANGUAGE=$(lang) gettext --domain=shotwell $(DESKTOP_APP_FULL_NAME)` \
978+- >> misc/shotwell.desktop ; \
979+- echo GenericName[$(lang)]=`TEXTDOMAINDIR=locale-langpack LANGUAGE=$(lang) \
980+- gettext --domain=shotwell $(DESKTOP_APPLICATION_CLASS)` >> misc/shotwell.desktop ; \
981+- echo Comment[$(lang)]=`TEXTDOMAINDIR=locale-langpack LANGUAGE=$(lang) gettext \
982+- --domain=shotwell $(DESKTOP_APPLICATION_COMMENT)` >> misc/shotwell.desktop ; \
983+- echo X-GNOME-FullName[$(lang)]=`TEXTDOMAINDIR=locale-langpack LANGUAGE=$(lang) gettext \
984+- --domain=shotwell $(DIRECT_EDIT_DESKTOP_APP_FULL_NAME)` >> misc/shotwell-viewer.desktop ; \
985+- echo GenericName[$(lang)]=`TEXTDOMAINDIR=locale-langpack LANGUAGE=$(lang) gettext \
986+- --domain=shotwell $(DIRECT_EDIT_DESKTOP_APPLICATION_CLASS)` >> misc/shotwell-viewer.desktop ;)
987+ touch $(LANG_STAMP)
988+ mkdir -p $(DESTDIR)$(PREFIX)/bin
989+ $(INSTALL_PROGRAM) $(PROGRAM) $(DESTDIR)$(PREFIX)/bin
990
991=== added file 'debian/patches/03_appmenu_no_stubs.patch'
992--- debian/patches/03_appmenu_no_stubs.patch 1970-01-01 00:00:00 +0000
993+++ debian/patches/03_appmenu_no_stubs.patch 2012-09-17 10:22:22 +0000
994@@ -0,0 +1,9 @@
995+Index: shotwell-0.11.91/misc/shotwell.desktop.head
996+===================================================================
997+--- shotwell-0.11.91.orig/misc/shotwell.desktop.head 2012-01-06 02:26:06.000000000 +0100
998++++ shotwell-0.11.91/misc/shotwell.desktop.head 2012-01-11 15:11:14.909408671 +0100
999+@@ -12,3 +12,4 @@
1000+ X-GIO-NoFuse=true
1001+ X-GNOME-Gettext-Domain=shotwell
1002+ X-GNOME-FullName=Shotwell Photo Manager
1003++X-Ayatana-Appmenu-Show-Stubs=false
1004
1005=== added file 'debian/patches/04_no_resize_grip.patch'
1006--- debian/patches/04_no_resize_grip.patch 1970-01-01 00:00:00 +0000
1007+++ debian/patches/04_no_resize_grip.patch 2012-09-17 10:22:22 +0000
1008@@ -0,0 +1,26 @@
1009+Description: Disable the resize grip, it conflicts with widgets in the bottom corner
1010+Author: Robert Ancell <robert.ancell@canonical.com>
1011+Bug: https://launchpad.net/bugs/740274
1012+
1013+Index: shotwell-0.12.0/src/AppWindow.vala
1014+===================================================================
1015+--- shotwell-0.12.0.orig/src/AppWindow.vala 2012-03-28 05:43:09.000000000 +1100
1016++++ shotwell-0.12.0/src/AppWindow.vala 2012-03-28 12:05:48.691285125 +1100
1017+@@ -413,6 +413,8 @@
1018+
1019+ }
1020+
1021++extern static void gtk_window_set_has_resize_grip (Gtk.Window window, bool has_grip);
1022++
1023+ // AppWindow is the parent window for most windows in Shotwell (FullscreenWindow is the exception).
1024+ // There are multiple types of AppWindows (LibraryWindow, DirectWindow) for different tasks, but only
1025+ // one AppWindow may exist per process. Thus, if the user closes an AppWindow, the program exits.
1026+@@ -442,6 +444,8 @@
1027+ assert(instance == null);
1028+ instance = this;
1029+
1030++ gtk_window_set_has_resize_grip (this, false);
1031++
1032+ title = Resources.APP_TITLE;
1033+
1034+ GLib.List<Gdk.Pixbuf> pixbuf_list = new GLib.List<Gdk.Pixbuf>();
1035
1036=== added file 'debian/patches/05-gomp-linking.patch'
1037--- debian/patches/05-gomp-linking.patch 1970-01-01 00:00:00 +0000
1038+++ debian/patches/05-gomp-linking.patch 2012-09-17 10:22:22 +0000
1039@@ -0,0 +1,16 @@
1040+From: Mathieu Trudel-Lapierre <mathieu.trudel-lapierre@canonical.com>
1041+Subject: Link against gomp as well (-lgomp)
1042+
1043+Index: shotwell-0.12.90/Makefile
1044+===================================================================
1045+--- shotwell-0.12.90.orig/Makefile 2012-08-24 15:03:05.000000000 +1200
1046++++ shotwell-0.12.90/Makefile 2012-08-24 15:04:46.356010199 +1200
1047+@@ -679,7 +679,7 @@
1048+ $(CC) -c $(VALA_CFLAGS) $(CFLAGS) -o $@ $<
1049+
1050+ $(PROGRAM): $(EXPANDED_OBJ_FILES) $(RESOURCES) $(LANG_STAMP) $(THUMBNAILER_BIN)
1051+- $(CC) $(EXPANDED_OBJ_FILES) $(CFLAGS) $(LDFLAGS) $(RESOURCES) $(VALA_LDFLAGS) $(EXPORT_FLAGS) -o $@
1052++ $(CC) $(EXPANDED_OBJ_FILES) $(CFLAGS) $(LDFLAGS) $(RESOURCES) -fopenmp $(VALA_LDFLAGS) $(EXPORT_FLAGS) -o $@
1053+ glib-compile-schemas misc
1054+
1055+ $(THUMBNAILER_BIN): $(EXPANDED_THUMBNAILER_SRC_FILES)
1056
1057=== added file 'debian/patches/06_uoa.patch'
1058--- debian/patches/06_uoa.patch 1970-01-01 00:00:00 +0000
1059+++ debian/patches/06_uoa.patch 2012-09-17 10:22:22 +0000
1060@@ -0,0 +1,2790 @@
1061+Description: use Ubuntu Online Accounts for Picasa, Facebook and Flickr export plugins
1062+Author: Alberto Mardegan <alberto.mardegan@canonical.com>
1063+
1064+---
1065+diff --git a/Makefile b/Makefile
1066+index ff15b71..b848b7f 100644
1067+--- a/Makefile
1068++++ b/Makefile
1069+@@ -151,6 +151,7 @@ RESOURCE_FILES = \
1070+ trash.ui
1071+
1072+ SYS_INTEGRATION_FILES = \
1073++ shotwell.application \
1074+ shotwell.desktop.head \
1075+ shotwell-viewer.desktop.head \
1076+ org.yorba.shotwell.gschema.xml \
1077+@@ -540,6 +541,8 @@ install:
1078+ mkdir -p $(DESTDIR)$(PREFIX)/bin
1079+ $(INSTALL_PROGRAM) $(PROGRAM) $(DESTDIR)$(PREFIX)/bin
1080+ $(INSTALL_PROGRAM) $(THUMBNAILER_BIN) $(DESTDIR)$(PREFIX)/bin
1081++ mkdir -p $(DESTDIR)$(PREFIX)/share/accounts/applications
1082++ $(INSTALL_DATA) misc/shotwell.application $(DESTDIR)$(PREFIX)/share/accounts/applications
1083+ mkdir -p $(DESTDIR)$(PREFIX)/share/shotwell/icons
1084+ $(INSTALL_DATA) icons/* $(DESTDIR)$(PREFIX)/share/shotwell/icons
1085+ mkdir -p $(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps
1086+diff --git a/misc/shotwell.application b/misc/shotwell.application
1087+new file mode 100644
1088+index 0000000..b121198
1089+--- /dev/null
1090++++ b/misc/shotwell.application
1091+@@ -0,0 +1,18 @@
1092++<?xml version="1.0" encoding="UTF-8" ?>
1093++<application id="shotwell">
1094++ <description>Shotwell</description>
1095++ <desktop-entry>shotwell.desktop</desktop-entry>
1096++
1097++ <services>
1098++ <service id="picasa">
1099++ <description>Publish your pictures to Picasa</description>
1100++ </service>
1101++ <service id="facebook-sharing">
1102++ <description>Publish your pictures to Facebook</description>
1103++ </service>
1104++ <service id="flickr-sharing">
1105++ <description>Publish your pictures to Flickr</description>
1106++ </service>
1107++ </services>
1108++
1109++</application>
1110+diff --git a/plugins/Makefile b/plugins/Makefile
1111+index c622063..255e9cc 100644
1112+--- a/plugins/Makefile
1113++++ b/plugins/Makefile
1114+@@ -4,7 +4,10 @@ include plugins.mk
1115+ DIST_FILES := \
1116+ Makefile \
1117+ Makefile.plugin.mk \
1118+- plugins.mk
1119++ plugins.mk \
1120++ Accounts.vapi \
1121++ Accounts.deps \
1122++ Signon.vapi
1123+
1124+ .PHONY: all
1125+ all: $(ALL_PLUGINS)
1126+diff --git a/plugins/Makefile.plugin.mk b/plugins/Makefile.plugin.mk
1127+index 51a0426..d82cc0f 100644
1128+--- a/plugins/Makefile.plugin.mk
1129++++ b/plugins/Makefile.plugin.mk
1130+@@ -27,6 +27,8 @@ PKGS := $(shell sed ':a;N;$$!ba;s/\n/ /g' ../shotwell-plugin-dev-1.0.deps) $(PKG
1131+ # automatically include the shotwell-plugin-dev-1.0 package as a local dependency
1132+ EXT_PKGS := $(PKGS)
1133+ PKGS := shotwell-plugin-dev-1.0 $(PKGS) $(PLUGIN_PKGS)
1134++EXT_PKGS := libsignon-glib libaccounts-glib $(EXT_PKGS)
1135++PKGS := signon accounts $(PKGS)
1136+
1137+ # automatically include the Resources.vala common file
1138+ SRC_FILES := ../common/Resources.vala $(SRC_FILES)
1139+diff --git a/plugins/common/RESTSupport.vala b/plugins/common/RESTSupport.vala
1140+index 12b4180..d4be236 100644
1141+--- a/plugins/common/RESTSupport.vala
1142++++ b/plugins/common/RESTSupport.vala
1143+@@ -314,7 +314,7 @@ public class Transaction {
1144+ old_url = message.get_uri().to_string(false);
1145+ url_with_query = get_endpoint_url() + "?" + formdata_string;
1146+ message.set_uri(new Soup.URI(url_with_query));
1147+- } else {
1148++ } else if (get_method() == HttpMethod.POST) {
1149+ message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY,
1150+ formdata_string.data);
1151+ }
1152+diff --git a/plugins/common/accounts.vala b/plugins/common/accounts.vala
1153+new file mode 100644
1154+index 0000000..d614be6
1155+--- /dev/null
1156++++ b/plugins/common/accounts.vala
1157+@@ -0,0 +1,226 @@
1158++/* Copyright 2009-2011 Yorba Foundation
1159++ *
1160++ * This software is licensed under the GNU Lesser General Public License
1161++ * (version 2.1 or later). See the COPYING file in this distribution.
1162++ */
1163++
1164++namespace Publishing.Accounts {
1165++
1166++public class SharingAccount {
1167++ private Ag.AccountService account_service = null;
1168++
1169++ public SharingAccount(Ag.AccountService account_service) {
1170++ this.account_service = account_service;
1171++ }
1172++
1173++ public Signon.AuthSession create_auth_session() throws GLib.Error {
1174++ var auth_data = account_service.get_auth_data();
1175++
1176++ debug("Signon-id: %u", auth_data.get_credentials_id());
1177++
1178++ return new Signon.AuthSession(auth_data.get_credentials_id(),
1179++ auth_data.get_method());
1180++ }
1181++
1182++ public HashTable<string,Value?> get_session_parameters(out string mechanism) {
1183++ var auth_data = account_service.get_auth_data();
1184++ mechanism = auth_data.get_mechanism();
1185++ return auth_data.get_parameters();
1186++ }
1187++}
1188++
1189++public class SharingAccounts {
1190++ private static Ag.Manager manager = null;
1191++ private string provider_name;
1192++ private Ag.AccountService[] all_accounts;
1193++
1194++ public SharingAccounts(string provider_name) {
1195++ if (manager == null) {
1196++ manager = new Ag.Manager.for_service_type("sharing");
1197++ }
1198++ manager.enabled_event.connect(on_account_enabled);
1199++
1200++ this.provider_name = provider_name;
1201++ all_accounts = get_accounts();
1202++ }
1203++
1204++ private void on_account_enabled(uint account_id) {
1205++ /* To keep the implementation simple, just rebuild the account
1206++ * list from scratch */
1207++ all_accounts = get_accounts();
1208++ }
1209++
1210++ private Ag.AccountService[] get_accounts() {
1211++ GLib.List<Ag.AccountService> accounts =
1212++ manager.get_enabled_account_services();
1213++
1214++ Ag.AccountService[] list = {};
1215++
1216++ foreach (Ag.AccountService account_service in accounts) {
1217++ Ag.Account account = account_service.get_account();
1218++ if (account.get_provider_name() == provider_name) {
1219++ list += account_service;
1220++ }
1221++ }
1222++ return list;
1223++ }
1224++
1225++ public bool has_enabled_accounts() {
1226++ return all_accounts.length > 0;
1227++ }
1228++
1229++ public string[] list_account_names() {
1230++ string[] names = {};
1231++ foreach (Ag.AccountService account_service in all_accounts) {
1232++ names += account_service.get_account().get_display_name();
1233++ }
1234++ return names;
1235++ }
1236++
1237++ public SharingAccount? find_account(string? account_name) {
1238++ foreach (Ag.AccountService account_service in all_accounts)
1239++ if (account_service.get_account().get_display_name() == account_name) {
1240++ return new SharingAccount(account_service);
1241++ }
1242++
1243++ return null;
1244++ }
1245++}
1246++
1247++public abstract class UOAPublishingService : Object, Spit.Pluggable, Spit.Publishing.Service {
1248++ private SharingAccounts account_manager;
1249++
1250++ public UOAPublishingService(string provider_name) {
1251++ account_manager = new SharingAccounts(provider_name);
1252++ }
1253++
1254++ public virtual int get_pluggable_interface(int min_host_interface, int max_host_interface) {
1255++ return Spit.negotiate_interfaces(min_host_interface, max_host_interface,
1256++ Spit.Publishing.CURRENT_INTERFACE);
1257++ }
1258++
1259++ public abstract unowned string get_id();
1260++
1261++ public abstract unowned string get_pluggable_name();
1262++
1263++ public abstract void get_info(ref Spit.PluggableInfo info);
1264++
1265++ public virtual void activation(bool enabled) {
1266++ }
1267++
1268++ public abstract Spit.Publishing.Publisher create_publisher(string? account_name, Spit.Publishing.PluginHost host);
1269++
1270++ public abstract Spit.Publishing.Publisher.MediaType get_supported_media();
1271++
1272++ public SharingAccount? find_account(string? account_name) {
1273++ return account_manager.find_account(account_name);
1274++ }
1275++
1276++ public bool is_enabled() {
1277++ return account_manager.has_enabled_accounts();
1278++ }
1279++
1280++ public string[] list_account_names() {
1281++ return account_manager.list_account_names();
1282++ }
1283++}
1284++
1285++public class UOAPublisherAuthenticator : Object {
1286++ private weak Spit.Publishing.PluginHost host = null;
1287++ private Signon.AuthSession auth_session = null;
1288++ private SharingAccount account = null;
1289++ private bool firstLoginAttempt = true;
1290++ private string welcome_message = null;
1291++
1292++ public UOAPublisherAuthenticator(SharingAccount account,
1293++ Spit.Publishing.PluginHost host,
1294++ string welcome_message)
1295++ {
1296++ this.host = host;
1297++ this.account = account;
1298++ this.welcome_message = welcome_message;
1299++ }
1300++
1301++ public signal void authenticated(owned HashTable<string,Value?> session_data);
1302++
1303++ public void authenticate() {
1304++ debug("ACTION: authentication requested.");
1305++
1306++ do_authentication();
1307++ }
1308++
1309++ public HashTable<string,Value?>? get_authentication_data() {
1310++ if (account == null) return null;
1311++ string mechanism = null;
1312++ return account.get_session_parameters(out mechanism);
1313++ }
1314++
1315++ private void do_authentication() {
1316++ debug("ACTION: authenticating.");
1317++
1318++ HashTable<string,Value?> data = null;
1319++ string mechanism = null;
1320++
1321++ if (account != null) {
1322++ try {
1323++ auth_session = account.create_auth_session();
1324++ data = account.get_session_parameters(out mechanism);
1325++ debug("Got account data");
1326++ } catch (GLib.Error e) {
1327++ warning("EVENT: couldn't create session for account: %s",
1328++ e.message);
1329++ }
1330++ }
1331++
1332++ if (data == null) {
1333++ warning ("No account authentication data");
1334++ host.post_error(new Spit.Publishing.PublishingError.SERVICE_ERROR(
1335++ "Error while accessing the account"));
1336++ return;
1337++ }
1338++
1339++ if (firstLoginAttempt) {
1340++ firstLoginAttempt = false;
1341++ data.insert("UiPolicy", Signon.SessionDataUiPolicy.NO_USER_INTERACTION);
1342++ } else {
1343++ var windowId = host.get_dialog_xid();
1344++ if (windowId != 0) {
1345++ data.insert("WindowId", (uint)windowId);
1346++ }
1347++ }
1348++
1349++ auth_session.process(data, mechanism, on_processed);
1350++ host.set_service_locked(true);
1351++ }
1352++
1353++ private void on_processed(Signon.AuthSession self, owned HashTable<string,Value?>? session_data, GLib.Error error){
1354++ host.set_service_locked(false);
1355++ if (error != null) {
1356++ debug("got error: %s", error.message);
1357++ if (error is Signon.Error.USER_INTERACTION) {
1358++ debug("User interaction!");
1359++ do_show_service_welcome_pane();
1360++ } else {
1361++ host.post_error(new Spit.Publishing.PublishingError.SERVICE_ERROR("Authentication failed"));
1362++ }
1363++ return;
1364++ }
1365++
1366++ authenticated(session_data);
1367++ }
1368++
1369++ private void do_show_service_welcome_pane() {
1370++ debug("ACTION: showing service welcome pane.");
1371++
1372++ host.install_welcome_pane(welcome_message, on_service_welcome_login);
1373++ }
1374++
1375++ private void on_service_welcome_login() {
1376++ debug("EVENT: user clicked 'Login' in welcome pane.");
1377++
1378++ do_authentication();
1379++ }
1380++}
1381++
1382++}
1383++
1384+diff --git a/plugins/shotwell-publishing-extras/YandexPublishing.vala b/plugins/shotwell-publishing-extras/YandexPublishing.vala
1385+index 1370690..760a8f3 100644
1386+--- a/plugins/shotwell-publishing-extras/YandexPublishing.vala
1387++++ b/plugins/shotwell-publishing-extras/YandexPublishing.vala
1388+@@ -30,7 +30,7 @@ public class YandexService : Object, Spit.Pluggable, Spit.Publishing.Service {
1389+ info.license = Resources.LICENSE;
1390+ }
1391+
1392+- public Spit.Publishing.Publisher create_publisher(Spit.Publishing.PluginHost host) {
1393++ public Spit.Publishing.Publisher create_publisher(string? account, Spit.Publishing.PluginHost host) {
1394+ return new Publishing.Yandex.YandexPublisher(this, host);
1395+ }
1396+
1397+diff --git a/plugins/shotwell-publishing/FacebookPublishing.vala b/plugins/shotwell-publishing/FacebookPublishing.vala
1398+index fa9d8f2..bd3cd08 100644
1399+--- a/plugins/shotwell-publishing/FacebookPublishing.vala
1400++++ b/plugins/shotwell-publishing/FacebookPublishing.vala
1401+@@ -4,30 +4,28 @@
1402+ * (version 2.1 or later). See the COPYING file in this distribution.
1403+ */
1404+
1405+-public class FacebookService : Object, Spit.Pluggable, Spit.Publishing.Service {
1406++using Publishing.Accounts;
1407++
1408++public class FacebookService : UOAPublishingService, Spit.Pluggable, Spit.Publishing.Service {
1409+ private const string ICON_FILENAME = "facebook.png";
1410+
1411+ private static Gdk.Pixbuf[] icon_pixbuf_set = null;
1412+
1413+ public FacebookService(GLib.File resource_directory) {
1414++ base("facebook");
1415+ if (icon_pixbuf_set == null)
1416+ icon_pixbuf_set = Resources.load_icon_set(resource_directory.get_child(ICON_FILENAME));
1417+ }
1418+
1419+- public int get_pluggable_interface(int min_host_interface, int max_host_interface) {
1420+- return Spit.negotiate_interfaces(min_host_interface, max_host_interface,
1421+- Spit.Publishing.CURRENT_INTERFACE);
1422+- }
1423+-
1424+- public unowned string get_id() {
1425++ public override unowned string get_id() {
1426+ return "org.yorba.shotwell.publishing.facebook";
1427+ }
1428+
1429+- public unowned string get_pluggable_name() {
1430++ public override unowned string get_pluggable_name() {
1431+ return "Facebook";
1432+ }
1433+
1434+- public void get_info(ref Spit.PluggableInfo info) {
1435++ public override void get_info(ref Spit.PluggableInfo info) {
1436+ info.authors = "Lucas Beeler";
1437+ info.copyright = _("Copyright 2009-2012 Yorba Foundation");
1438+ info.translators = Resources.TRANSLATORS;
1439+@@ -39,14 +37,12 @@ public class FacebookService : Object, Spit.Pluggable, Spit.Publishing.Service {
1440+ info.icons = icon_pixbuf_set;
1441+ }
1442+
1443+- public void activation(bool enabled) {
1444++ public override Spit.Publishing.Publisher create_publisher(string? account_name, Spit.Publishing.PluginHost host) {
1445++ SharingAccount account = find_account(account_name);
1446++ return new Publishing.Facebook.FacebookPublisher(this, account, host);
1447+ }
1448+
1449+- public Spit.Publishing.Publisher create_publisher(Spit.Publishing.PluginHost host) {
1450+- return new Publishing.Facebook.FacebookPublisher(this, host);
1451+- }
1452+-
1453+- public Spit.Publishing.Publisher.MediaType get_supported_media() {
1454++ public override Spit.Publishing.Publisher.MediaType get_supported_media() {
1455+ return (Spit.Publishing.Publisher.MediaType.PHOTO |
1456+ Spit.Publishing.Publisher.MediaType.VIDEO);
1457+ }
1458+@@ -66,8 +62,6 @@ internal const string PHOTO_ENDPOINT_URL = "https://api.facebook.com/restserver.
1459+ internal const string VIDEO_ENDPOINT_URL = "https://api-video.facebook.com/restserver.php";
1460+ internal const string SERVICE_WELCOME_MESSAGE =
1461+ _("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.");
1462+-internal const string RESTART_ERROR_MESSAGE =
1463+- _("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.");
1464+ // as of mid-November 2010, the privacy the simple string "SELF" is no longer a valid
1465+ // privacy value; "SELF" must be simulated by a "CUSTOM" setting; see the discussion
1466+ // http://forum.developers.facebook.net/viewtopic.php?pid=289287
1467+@@ -163,19 +157,23 @@ public class FacebookPublisher : Spit.Publishing.Publisher, GLib.Object {
1468+ private int publish_to_album = NO_ALBUM;
1469+ private weak Spit.Publishing.PluginHost host = null;
1470+ private FacebookRESTSession session = null;
1471+- private WebAuthenticationPane web_auth_pane = null;
1472+ private Spit.Publishing.ProgressCallback progress_reporter = null;
1473+ private weak Spit.Publishing.Service service = null;
1474+ private bool strip_metadata = false;
1475+ private bool running = false;
1476++ private UOAPublisherAuthenticator authenticator = null;
1477+
1478+ private Resolution target_resolution = Resolution.HIGH;
1479+
1480+ public FacebookPublisher(Spit.Publishing.Service service,
1481++ SharingAccount account,
1482+ Spit.Publishing.PluginHost host) {
1483+ debug("FacebookPublisher instantiated.");
1484+ this.service = service;
1485+ this.host = host;
1486++ authenticator = new UOAPublisherAuthenticator(account, host,
1487++ SERVICE_WELCOME_MESSAGE);
1488++ authenticator.authenticated.connect(on_authenticator_authenticated);
1489+ }
1490+
1491+ private bool is_running() {
1492+@@ -190,49 +188,10 @@ public class FacebookPublisher : Spit.Publishing.Publisher, GLib.Object {
1493+ return NO_ALBUM;
1494+ }
1495+
1496+- private bool is_persistent_session_valid() {
1497+- string? access_token = get_persistent_access_token();
1498+- string? uid = get_persistent_uid();
1499+- string? user_name = get_persistent_user_name();
1500+-
1501+- bool valid = ((access_token != null) && (uid != null) && (user_name != null));
1502+-
1503+- if (valid)
1504+- debug("existing Facebook session for user = '%s' found in configuration database; using it.", user_name);
1505+- else
1506+- debug("no persisted Facebook session exists.");
1507+-
1508+- return valid;
1509+- }
1510+-
1511+- private string? get_persistent_access_token() {
1512+- return host.get_config_string("access_token", null);
1513+- }
1514+-
1515+- private string? get_persistent_uid() {
1516+- return host.get_config_string("uid", null);
1517+- }
1518+-
1519+- private string? get_persistent_user_name() {
1520+- return host.get_config_string("user_name", null);
1521+- }
1522+-
1523+ private bool get_persistent_strip_metadata() {
1524+ return host.get_config_bool("strip_metadata", false);
1525+ }
1526+
1527+- private void set_persistent_access_token(string access_token) {
1528+- host.set_config_string("access_token", access_token);
1529+- }
1530+-
1531+- private void set_persistent_uid(string uid) {
1532+- host.set_config_string("uid", uid);
1533+- }
1534+-
1535+- private void set_persistent_user_name(string user_name) {
1536+- host.set_config_string("user_name", user_name);
1537+- }
1538+-
1539+ private void set_persistent_strip_metadata(bool strip_metadata) {
1540+ host.set_config_bool("strip_metadata", strip_metadata);
1541+ }
1542+@@ -247,39 +206,6 @@ public class FacebookPublisher : Spit.Publishing.Publisher, GLib.Object {
1543+ host.set_config_int("default_size", size);
1544+ }
1545+
1546+- private void invalidate_persistent_session() {
1547+- debug("invalidating persisted Facebook session.");
1548+-
1549+- set_persistent_access_token("");
1550+- set_persistent_uid("");
1551+- set_persistent_user_name("");
1552+- }
1553+-
1554+- private void do_show_service_welcome_pane() {
1555+- debug("ACTION: showing service welcome pane.");
1556+-
1557+- host.install_welcome_pane(SERVICE_WELCOME_MESSAGE, on_login_clicked);
1558+- host.set_service_locked(false);
1559+- }
1560+-
1561+- private void do_test_connection_to_endpoint() {
1562+- debug("ACTION: testing connection to Facebook endpoint.");
1563+- host.set_service_locked(true);
1564+-
1565+- host.install_static_message_pane(_("Testing connection to Facebook..."));
1566+-
1567+- FacebookEndpointTestTransaction txn = new FacebookEndpointTestTransaction(session);
1568+- txn.completed.connect(on_endpoint_test_completed);
1569+- txn.network_error.connect(on_endpoint_test_error);
1570+-
1571+- try {
1572+- txn.execute();
1573+- } catch (Spit.Publishing.PublishingError err) {
1574+- if (is_running())
1575+- host.post_error(err);
1576+- }
1577+- }
1578+-
1579+ private void do_fetch_album_descriptions() {
1580+ debug("ACTION: fetching album descriptions from remote endpoint.");
1581+
1582+@@ -387,7 +313,6 @@ public class FacebookPublisher : Spit.Publishing.Publisher, GLib.Object {
1583+ PublishingOptionsPane publishing_options_pane = new PublishingOptionsPane(
1584+ session.get_user_name(), albums, host.get_publishable_media_type(), this, builder,
1585+ get_persistent_strip_metadata());
1586+- publishing_options_pane.logout.connect(on_publishing_options_pane_logout);
1587+ publishing_options_pane.publish.connect(on_publishing_options_pane_publish);
1588+ host.install_dialog_pane(publishing_options_pane,
1589+ Spit.Publishing.PluginHost.ButtonMode.CANCEL);
1590+@@ -396,51 +321,20 @@ public class FacebookPublisher : Spit.Publishing.Publisher, GLib.Object {
1591+ private void do_logout() {
1592+ debug("ACTION: clearing persistent session information and restaring interaction.");
1593+
1594+- invalidate_persistent_session();
1595+-
1596+ running = false;
1597+ start();
1598+ }
1599+
1600+- private void do_hosted_web_authentication() {
1601+- debug("ACTION: doing hosted web authentication.");
1602+-
1603+- host.set_service_locked(false);
1604+-
1605+- web_auth_pane = new WebAuthenticationPane();
1606+- web_auth_pane.login_succeeded.connect(on_web_auth_pane_login_succeeded);
1607+- web_auth_pane.login_failed.connect(on_web_auth_pane_login_failed);
1608+-
1609+- host.install_dialog_pane(web_auth_pane,
1610+- Spit.Publishing.PluginHost.ButtonMode.CANCEL);
1611+- }
1612+-
1613+- private void do_authenticate_session(string success_url) {
1614+- debug("ACTION: preparing to extract session information encoded in url = '%s'",
1615+- success_url);
1616+-
1617++ public void on_authenticator_authenticated(owned HashTable<string,Value?> session_data) {
1618+ host.set_service_locked(true);
1619+ host.install_account_fetch_wait_pane();
1620+
1621+ session.authenticated.connect(on_session_authenticated);
1622+ session.authentication_failed.connect(on_session_authentication_failed);
1623+
1624+- try {
1625+- session.authenticate_from_uri(success_url);
1626+- } catch (Spit.Publishing.PublishingError err) {
1627+- // only post an error if we're running; errors tend to come in groups, so it's possible
1628+- // another error has already posted and caused us to stop
1629+- if (is_running())
1630+- host.post_error(err);
1631+- }
1632+- }
1633+-
1634+- private void do_save_session_information() {
1635+- debug("ACTION: saving session information to configuration system.");
1636+-
1637+- set_persistent_access_token(session.get_access_token());
1638+- set_persistent_uid(session.get_user_id());
1639+- set_persistent_user_name(session.get_user_name());
1640++ string token = session_data.lookup("AccessToken").get_string();
1641++ debug("Access Token: %s", token);
1642++ session.authenticate_with_parameters(token, null, null);
1643+ }
1644+
1645+ private void do_upload(bool strip_metadata) {
1646+@@ -528,64 +422,6 @@ public class FacebookPublisher : Spit.Publishing.Publisher, GLib.Object {
1647+ host.install_success_pane();
1648+ }
1649+
1650+- private void on_login_clicked() {
1651+- if (!is_running())
1652+- return;
1653+-
1654+- debug("EVENT: user clicked 'Login' on welcome pane.");
1655+-
1656+- do_test_connection_to_endpoint();
1657+- }
1658+-
1659+- private void on_endpoint_test_completed(FacebookRESTTransaction txn) {
1660+- txn.completed.disconnect(on_endpoint_test_completed);
1661+- txn.network_error.disconnect(on_endpoint_test_error);
1662+-
1663+- if (!is_running())
1664+- return;
1665+-
1666+- debug("EVENT: endpoint test transaction detected that the Facebook endpoint is alive.");
1667+-
1668+- do_hosted_web_authentication();
1669+- }
1670+-
1671+- private void on_endpoint_test_error(FacebookRESTTransaction bad_txn,
1672+- Spit.Publishing.PublishingError err) {
1673+- bad_txn.completed.disconnect(on_endpoint_test_completed);
1674+- bad_txn.network_error.disconnect(on_endpoint_test_error);
1675+-
1676+- if (!is_running())
1677+- return;
1678+-
1679+- debug("EVENT: endpoint test transaction failed to detect a connection to the Facebook endpoint");
1680+-
1681+- host.post_error(err);
1682+- }
1683+-
1684+- private void on_web_auth_pane_login_succeeded(string success_url) {
1685+- if (!is_running())
1686+- return;
1687+-
1688+- debug("EVENT: hosted web login succeeded.");
1689+-
1690+- do_authenticate_session(success_url);
1691+- }
1692+-
1693+- private void on_web_auth_pane_login_failed() {
1694+- if (!is_running())
1695+- return;
1696+-
1697+- debug("EVENT: hosted web login failed.");
1698+-
1699+- // In this case, "failed" doesn't mean that the user didn't enter the right username and
1700+- // password -- Facebook handles that case inside the Facebook Connect web control. Instead,
1701+- // it means that no session was initiated in response to our login request. The only
1702+- // way this happens is if the user clicks the "Cancel" button that appears inside
1703+- // the web control. In this case, the correct behavior is to return the user to the
1704+- // service welcome pane so that they can start the web interaction again.
1705+- do_show_service_welcome_pane();
1706+- }
1707+-
1708+ private void on_session_authenticated() {
1709+ if (!is_running())
1710+ return;
1711+@@ -593,7 +429,6 @@ public class FacebookPublisher : Spit.Publishing.Publisher, GLib.Object {
1712+ assert(session.is_authenticated());
1713+ debug("EVENT: an authenticated session has become available.");
1714+
1715+- do_save_session_information();
1716+ do_fetch_album_descriptions();
1717+ }
1718+
1719+@@ -638,15 +473,6 @@ public class FacebookPublisher : Spit.Publishing.Publisher, GLib.Object {
1720+ do_show_publishing_options_pane();
1721+ }
1722+
1723+- public void on_publishing_options_pane_logout() {
1724+- if (!is_running())
1725+- return;
1726+-
1727+- debug("EVENT: user clicked 'Logout' in publishing options pane.");
1728+-
1729+- do_logout();
1730+- }
1731+-
1732+ public void on_publishing_options_pane_publish(string? target_album, string privacy_setting,
1733+ Resolution target_resolution, bool strip_metadata) {
1734+ if (!is_running())
1735+@@ -762,26 +588,8 @@ public class FacebookPublisher : Spit.Publishing.Publisher, GLib.Object {
1736+ albums = null;
1737+ publish_to_album = NO_ALBUM;
1738+
1739+- // determine whether a user is logged in; if so, then show the publishing options pane
1740+- // for that user; otherwise, show the welcome pane
1741+- if (is_persistent_session_valid()) {
1742+- // if valid session information has been saved in the configuration system, build
1743+- // a Session object and pre-authenticate it with the saved information, then simulate an
1744+- // on_session_authenticated event to drive the rest of the interaction
1745+- session = new FacebookRESTSession(PHOTO_ENDPOINT_URL, USER_AGENT);
1746+- session.authenticate_with_parameters(get_persistent_access_token(),
1747+- get_persistent_uid(), get_persistent_user_name());
1748+- on_session_authenticated();
1749+- } else {
1750+- if (WebAuthenticationPane.is_cache_dirty()) {
1751+- host.set_service_locked(false);
1752+- host.install_static_message_pane(RESTART_ERROR_MESSAGE,
1753+- Spit.Publishing.PluginHost.ButtonMode.CANCEL);
1754+- } else {
1755+- session = new FacebookRESTSession(PHOTO_ENDPOINT_URL, USER_AGENT);
1756+- do_show_service_welcome_pane();
1757+- }
1758+- }
1759++ session = new FacebookRESTSession(PHOTO_ENDPOINT_URL, USER_AGENT);
1760++ authenticator.authenticate();
1761+ }
1762+
1763+ public void stop() {
1764+@@ -909,39 +717,14 @@ internal class FacebookRESTSession {
1765+ return (access_token != null && uid != null && user_name != null);
1766+ }
1767+
1768+- public void authenticate_with_parameters(string access_token, string uid, string user_name) {
1769+- this.access_token = access_token;
1770+- this.uid = uid;
1771+- this.user_name = user_name;
1772+- }
1773+-
1774+- public void authenticate_from_uri(string good_login_uri) throws Spit.Publishing.PublishingError {
1775+- // the raw uri is percent-encoded, so decode it
1776+- string decoded_uri = Soup.URI.decode(good_login_uri);
1777+-
1778+- // locate the access token within the URI
1779+- string? access_token = null;
1780+- int index = decoded_uri.index_of("#access_token=");
1781+- if (index >= 0)
1782+- access_token = decoded_uri[index:decoded_uri.length];
1783+- if (access_token == null)
1784+- throw new Spit.Publishing.PublishingError.MALFORMED_RESPONSE("Server redirect URL contained no access token");
1785+-
1786+- // remove any trailing parameters from the session description string
1787+- string? trailing_params = null;
1788+- index = access_token.index_of_char('&');
1789+- if (index >= 0)
1790+- trailing_params = access_token[index:access_token.length];
1791+- if (trailing_params != null)
1792+- access_token = access_token.replace(trailing_params, "");
1793+-
1794+- // remove the key from the session description string
1795+- access_token = access_token.replace("#access_token=", "");
1796+-
1797+- // we've got an access token!
1798++ public void authenticate_with_parameters(string access_token, string? uid, string? user_name) {
1799+ this.access_token = access_token;
1800+-
1801+- do_user_id_fetch_transaction();
1802++ if (uid == null) {
1803++ do_user_id_fetch_transaction();
1804++ } else {
1805++ this.uid = uid;
1806++ this.user_name = user_name;
1807++ }
1808+ }
1809+
1810+ public string get_endpoint_url() {
1811+@@ -1335,190 +1118,6 @@ internal class FacebookCreateAlbumTransaction : FacebookRESTTransaction {
1812+ }
1813+ }
1814+
1815+-internal class WebAuthenticationPane : Spit.Publishing.DialogPane, Object {
1816+- private WebKit.WebView webview = null;
1817+- private Gtk.Box pane_widget = null;
1818+- private Gtk.ScrolledWindow webview_frame = null;
1819+- private static bool cache_dirty = false;
1820+-
1821+- public signal void login_succeeded(string success_url);
1822+- public signal void login_failed();
1823+-
1824+- public WebAuthenticationPane() {
1825+- pane_widget = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
1826+-
1827+- webview_frame = new Gtk.ScrolledWindow(null, null);
1828+- webview_frame.set_shadow_type(Gtk.ShadowType.ETCHED_IN);
1829+- webview_frame.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
1830+-
1831+- webview = new WebKit.WebView();
1832+- webview.get_settings().enable_plugins = false;
1833+- webview.get_settings().enable_default_context_menu = false;
1834+-
1835+- webview.load_finished.connect(on_page_load);
1836+- webview.load_started.connect(on_load_started);
1837+-
1838+- webview_frame.add(webview);
1839+- pane_widget.pack_start(webview_frame, true, true, 0);
1840+- }
1841+-
1842+- private class LocaleLookup {
1843+- public string prefix;
1844+- public string translation;
1845+- public string? exception_code;
1846+- public string? exception_translation;
1847+- public string? exception_code_2;
1848+- public string? exception_translation_2;
1849+-
1850+- public LocaleLookup(string prefix, string translation, string? exception_code = null,
1851+- string? exception_translation = null, string? exception_code_2 = null,
1852+- string? exception_translation_2 = null) {
1853+- this.prefix = prefix;
1854+- this.translation = translation;
1855+- this.exception_code = exception_code;
1856+- this.exception_translation = exception_translation;
1857+- this.exception_code_2 = exception_code_2;
1858+- this.exception_translation_2 = exception_translation_2;
1859+- }
1860+-
1861+- }
1862+-
1863+- private LocaleLookup[] locale_lookup_table = {
1864+- new LocaleLookup( "es", "es-la", "ES", "es-es" ),
1865+- new LocaleLookup( "en", "en-gb", "US", "en-us" ),
1866+- new LocaleLookup( "fr", "fr-fr", "CA", "fr-ca" ),
1867+- new LocaleLookup( "pt", "pt-br", "PT", "pt-pt" ),
1868+- new LocaleLookup( "zh", "zh-cn", "HK", "zh-hk", "TW", "zh-tw" ),
1869+- new LocaleLookup( "af", "af-za" ),
1870+- new LocaleLookup( "ar", "ar-ar" ),
1871+- new LocaleLookup( "nb", "nb-no" ),
1872+- new LocaleLookup( "no", "nb-no" ),
1873+- new LocaleLookup( "id", "id-id" ),
1874+- new LocaleLookup( "ms", "ms-my" ),
1875+- new LocaleLookup( "ca", "ca-es" ),
1876+- new LocaleLookup( "cs", "cs-cz" ),
1877+- new LocaleLookup( "cy", "cy-gb" ),
1878+- new LocaleLookup( "da", "da-dk" ),
1879+- new LocaleLookup( "de", "de-de" ),
1880+- new LocaleLookup( "tl", "tl-ph" ),
1881+- new LocaleLookup( "ko", "ko-kr" ),
1882+- new LocaleLookup( "hr", "hr-hr" ),
1883+- new LocaleLookup( "it", "it-it" ),
1884+- new LocaleLookup( "lt", "lt-lt" ),
1885+- new LocaleLookup( "hu", "hu-hu" ),
1886+- new LocaleLookup( "nl", "nl-nl" ),
1887+- new LocaleLookup( "ja", "ja-jp" ),
1888+- new LocaleLookup( "nb", "nb-no" ),
1889+- new LocaleLookup( "no", "nb-no" ),
1890+- new LocaleLookup( "pl", "pl-pl" ),
1891+- new LocaleLookup( "ro", "ro-ro" ),
1892+- new LocaleLookup( "ru", "ru-ru" ),
1893+- new LocaleLookup( "sk", "sk-sk" ),
1894+- new LocaleLookup( "sl", "sl-si" ),
1895+- new LocaleLookup( "sv", "sv-se" ),
1896+- new LocaleLookup( "th", "th-th" ),
1897+- new LocaleLookup( "vi", "vi-vn" ),
1898+- new LocaleLookup( "tr", "tr-tr" ),
1899+- new LocaleLookup( "el", "el-gr" ),
1900+- new LocaleLookup( "bg", "bg-bg" ),
1901+- new LocaleLookup( "sr", "sr-rs" ),
1902+- new LocaleLookup( "he", "he-il" ),
1903+- new LocaleLookup( "hi", "hi-in" ),
1904+- new LocaleLookup( "bn", "bn-in" ),
1905+- new LocaleLookup( "pa", "pa-in" ),
1906+- new LocaleLookup( "ta", "ta-in" ),
1907+- new LocaleLookup( "te", "te-in" ),
1908+- new LocaleLookup( "ml", "ml-in" )
1909+- };
1910+-
1911+- private string get_system_locale_as_facebook_locale() {
1912+- unowned string? raw_system_locale = Intl.setlocale(LocaleCategory.ALL, "");
1913+- if (raw_system_locale == null || raw_system_locale == "")
1914+- return "www";
1915+-
1916+- string system_locale = raw_system_locale.split(".")[0];
1917+-
1918+- foreach (LocaleLookup locale_lookup in locale_lookup_table) {
1919+- if (!system_locale.has_prefix(locale_lookup.prefix))
1920+- continue;
1921+-
1922+- if (locale_lookup.exception_code != null) {
1923+- assert(locale_lookup.exception_translation != null);
1924+-
1925+- if (system_locale.contains(locale_lookup.exception_code))
1926+- return locale_lookup.exception_translation;
1927+- }
1928+-
1929+- if (locale_lookup.exception_code_2 != null) {
1930+- assert(locale_lookup.exception_translation_2 != null);
1931+-
1932+- if (system_locale.contains(locale_lookup.exception_code_2))
1933+- return locale_lookup.exception_translation_2;
1934+- }
1935+-
1936+- return locale_lookup.translation;
1937+- }
1938+-
1939+- // default
1940+- return "www";
1941+- }
1942+-
1943+- private string get_login_url() {
1944+- string facebook_locale = get_system_locale_as_facebook_locale();
1945+-
1946+- return "https://%s.facebook.com/dialog/oauth?client_id=%s&redirect_uri=https://www.facebook.com/connect/login_success.html&scope=offline_access,publish_stream,user_photos,user_videos&response_type=token".printf(facebook_locale, APPLICATION_ID);
1947+- }
1948+-
1949+- private void on_page_load(WebKit.WebFrame origin_frame) {
1950+- pane_widget.get_window().set_cursor(new Gdk.Cursor(Gdk.CursorType.LEFT_PTR));
1951+-
1952+- string loaded_url = origin_frame.get_uri().dup();
1953+-
1954+- // strip parameters from the loaded url
1955+- if (loaded_url.contains("?")) {
1956+- int index = loaded_url.index_of_char('?');
1957+- string params = loaded_url[index:loaded_url.length];
1958+- loaded_url = loaded_url.replace(params, "");
1959+- }
1960+-
1961+- // were we redirected to the facebook login success page?
1962+- if (loaded_url.contains("login_success")) {
1963+- cache_dirty = true;
1964+- login_succeeded(origin_frame.get_uri());
1965+- return;
1966+- }
1967+-
1968+- // were we redirected to the login total failure page?
1969+- if (loaded_url.contains("login_failure")) {
1970+- login_failed();
1971+- return;
1972+- }
1973+- }
1974+-
1975+- private void on_load_started(WebKit.WebFrame frame) {
1976+- pane_widget.get_window().set_cursor(new Gdk.Cursor(Gdk.CursorType.WATCH));
1977+- }
1978+-
1979+- public static bool is_cache_dirty() {
1980+- return cache_dirty;
1981+- }
1982+-
1983+- public Gtk.Widget get_widget() {
1984+- return pane_widget;
1985+- }
1986+-
1987+- public Spit.Publishing.DialogPane.GeometryOptions get_preferred_geometry() {
1988+- return Spit.Publishing.DialogPane.GeometryOptions.COLOSSAL_SIZE;
1989+- }
1990+-
1991+- public void on_pane_installed() {
1992+- webview.open(get_login_url());
1993+- }
1994+-
1995+- public void on_pane_uninstalled() {
1996+- }
1997+-}
1998+-
1999+ internal class PublishingOptionsPane : Spit.Publishing.DialogPane, GLib.Object {
2000+ private Gtk.Builder builder;
2001+ private Gtk.Box pane_widget = null;
2002+@@ -1529,7 +1128,6 @@ internal class PublishingOptionsPane : Spit.Publishing.DialogPane, GLib.Object {
2003+ private Gtk.Entry new_album_entry = null;
2004+ private Gtk.CheckButton strip_metadata_check = null;
2005+ private Gtk.Button publish_button = null;
2006+- private Gtk.Button logout_button = null;
2007+ private Gtk.Label how_to_label = null;
2008+ private FacebookAlbum[] albums = null;
2009+ private FacebookPublisher publisher = null;
2010+@@ -1546,7 +1144,6 @@ internal class PublishingOptionsPane : Spit.Publishing.DialogPane, GLib.Object {
2011+ private const int CONTENT_GROUP_SPACING = 32;
2012+ private const int STANDARD_ACTION_BUTTON_WIDTH = 128;
2013+
2014+- public signal void logout();
2015+ public signal void publish(string? target_album, string privacy_setting, Resolution target_resolution, bool strip_metadata);
2016+
2017+ private class PrivacyDescription {
2018+@@ -1586,7 +1183,6 @@ internal class PublishingOptionsPane : Spit.Publishing.DialogPane, GLib.Object {
2019+ existing_albums_combo = (Gtk.ComboBoxText) this.builder.get_object("existing_albums_combo");
2020+ visibility_combo = (Gtk.ComboBoxText) this.builder.get_object("visibility_combo");
2021+ publish_button = (Gtk.Button) this.builder.get_object("publish_button");
2022+- logout_button = (Gtk.Button) this.builder.get_object("logout_button");
2023+ new_album_entry = (Gtk.Entry) this.builder.get_object("new_album_entry");
2024+ resolution_combo = (Gtk.ComboBoxText) this.builder.get_object("resolution_combo");
2025+ how_to_label = (Gtk.Label) this.builder.get_object("how_to_label");
2026+@@ -1605,7 +1201,6 @@ internal class PublishingOptionsPane : Spit.Publishing.DialogPane, GLib.Object {
2027+ visibility_combo.set_active(0);
2028+
2029+ publish_button.clicked.connect(on_publish_button_clicked);
2030+- logout_button.clicked.connect(on_logout_button_clicked);
2031+
2032+ // Ticket #2916 - if the user is uploading photographs, allow
2033+ // them to choose what resolution they should be uploaded at.
2034+@@ -1669,10 +1264,6 @@ internal class PublishingOptionsPane : Spit.Publishing.DialogPane, GLib.Object {
2035+ publisher.set_persistent_default_size(resolution_combo.get_active());
2036+ }
2037+
2038+- private void on_logout_button_clicked() {
2039+- logout();
2040+- }
2041+-
2042+ private void on_publish_button_clicked() {
2043+ string album_name;
2044+ string privacy_setting = privacy_descriptions[visibility_combo.get_active()].privacy_setting;
2045+@@ -1747,10 +1338,6 @@ internal class PublishingOptionsPane : Spit.Publishing.DialogPane, GLib.Object {
2046+ publish_button.grab_focus();
2047+ }
2048+
2049+- private void notify_logout() {
2050+- logout();
2051+- }
2052+-
2053+ private void notify_publish(string? target_album, string privacy_setting, Resolution target_resolution) {
2054+ publish(target_album, privacy_setting, target_resolution, strip_metadata_check.get_active());
2055+ }
2056+@@ -1764,14 +1351,12 @@ internal class PublishingOptionsPane : Spit.Publishing.DialogPane, GLib.Object {
2057+ }
2058+
2059+ public void on_pane_installed() {
2060+- logout.connect(notify_logout);
2061+ publish.connect(notify_publish);
2062+
2063+ installed();
2064+ }
2065+
2066+ public void on_pane_uninstalled() {
2067+- logout.disconnect(notify_logout);
2068+ publish.disconnect(notify_publish);
2069+ }
2070+ }
2071+diff --git a/plugins/shotwell-publishing/FlickrPublishing.vala b/plugins/shotwell-publishing/FlickrPublishing.vala
2072+index a6f8b45..abaf0b2 100644
2073+--- a/plugins/shotwell-publishing/FlickrPublishing.vala
2074++++ b/plugins/shotwell-publishing/FlickrPublishing.vala
2075+@@ -4,32 +4,30 @@
2076+ * (version 2.1 or later). See the COPYING file in this distribution.
2077+ */
2078+
2079++using Publishing.Accounts;
2080++
2081+ extern string hmac_sha1(string key, string message);
2082+
2083+-public class FlickrService : Object, Spit.Pluggable, Spit.Publishing.Service {
2084++public class FlickrService : UOAPublishingService, Spit.Pluggable, Spit.Publishing.Service {
2085+ private const string ICON_FILENAME = "flickr.png";
2086+
2087+ private static Gdk.Pixbuf[] icon_pixbuf_set = null;
2088+
2089+ public FlickrService(GLib.File resource_directory) {
2090++ base("flickr");
2091+ if (icon_pixbuf_set == null)
2092+ icon_pixbuf_set = Resources.load_icon_set(resource_directory.get_child(ICON_FILENAME));
2093+ }
2094+
2095+- public int get_pluggable_interface(int min_host_interface, int max_host_interface) {
2096+- return Spit.negotiate_interfaces(min_host_interface, max_host_interface,
2097+- Spit.Publishing.CURRENT_INTERFACE);
2098+- }
2099+-
2100+- public unowned string get_id() {
2101++ public override unowned string get_id() {
2102+ return "org.yorba.shotwell.publishing.flickr";
2103+ }
2104+
2105+- public unowned string get_pluggable_name() {
2106++ public override unowned string get_pluggable_name() {
2107+ return "Flickr";
2108+ }
2109+
2110+- public void get_info(ref Spit.PluggableInfo info) {
2111++ public override void get_info(ref Spit.PluggableInfo info) {
2112+ info.authors = "Lucas Beeler";
2113+ info.copyright = _("Copyright 2009-2012 Yorba Foundation");
2114+ info.translators = Resources.TRANSLATORS;
2115+@@ -41,14 +39,12 @@ public class FlickrService : Object, Spit.Pluggable, Spit.Publishing.Service {
2116+ info.icons = icon_pixbuf_set;
2117+ }
2118+
2119+- public void activation(bool enabled) {
2120+- }
2121+-
2122+- public Spit.Publishing.Publisher create_publisher(Spit.Publishing.PluginHost host) {
2123+- return new Publishing.Flickr.FlickrPublisher(this, host);
2124++ public override Spit.Publishing.Publisher create_publisher(string? account_name, Spit.Publishing.PluginHost host) {
2125++ SharingAccount account = find_account(account_name);
2126++ return new Publishing.Flickr.FlickrPublisher(this, account, host);
2127+ }
2128+
2129+- public Spit.Publishing.Publisher.MediaType get_supported_media() {
2130++ public override Spit.Publishing.Publisher.MediaType get_supported_media() {
2131+ return (Spit.Publishing.Publisher.MediaType.PHOTO |
2132+ Spit.Publishing.Publisher.MediaType.VIDEO);
2133+ }
2134+@@ -105,60 +101,33 @@ public class FlickrPublisher : Spit.Publishing.Publisher, GLib.Object {
2135+ private bool was_started = false;
2136+ private Session session = null;
2137+ private PublishingOptionsPane publishing_options_pane = null;
2138++ private UOAPublisherAuthenticator authenticator = null;
2139+
2140+ private PublishingParameters parameters = null;
2141+
2142+ public FlickrPublisher(Spit.Publishing.Service service,
2143++ SharingAccount account,
2144+ Spit.Publishing.PluginHost host) {
2145+ debug("FlickrPublisher instantiated.");
2146+ this.service = service;
2147+ this.host = host;
2148+ this.session = new Session();
2149++ authenticator = new UOAPublisherAuthenticator(account, host,
2150++ SERVICE_WELCOME_MESSAGE);
2151+ this.parameters = new PublishingParameters();
2152+
2153+ session.authenticated.connect(on_session_authenticated);
2154++ HashTable<string,Value?> data =
2155++ authenticator.get_authentication_data();
2156++ session.set_api_credentials(data.lookup("ConsumerKey").get_string(),
2157++ data.lookup("ConsumerSecret").get_string());
2158++ authenticator.authenticated.connect(on_authenticator_authenticated);
2159+ }
2160+
2161+ ~FlickrPublisher() {
2162+ session.authenticated.disconnect(on_session_authenticated);
2163+ }
2164+
2165+- private void invalidate_persistent_session() {
2166+- set_persistent_access_phase_token("");
2167+- set_persistent_access_phase_token_secret("");
2168+- set_persistent_access_phase_username("");
2169+- }
2170+-
2171+- private bool is_persistent_session_valid() {
2172+- return (get_persistent_access_phase_username() != null &&
2173+- get_persistent_access_phase_token() != null &&
2174+- get_persistent_access_phase_token_secret() != null);
2175+- }
2176+-
2177+- private string? get_persistent_access_phase_username() {
2178+- return host.get_config_string("access_phase_username", null);
2179+- }
2180+-
2181+- private void set_persistent_access_phase_username(string username) {
2182+- host.set_config_string("access_phase_username", username);
2183+- }
2184+-
2185+- private string? get_persistent_access_phase_token() {
2186+- return host.get_config_string("access_phase_token", null);
2187+- }
2188+-
2189+- private void set_persistent_access_phase_token(string token) {
2190+- host.set_config_string("access_phase_token", token);
2191+- }
2192+-
2193+- private string? get_persistent_access_phase_token_secret() {
2194+- return host.get_config_string("access_phase_token_secret", null);
2195+- }
2196+-
2197+- private void set_persistent_access_phase_token_secret(string secret) {
2198+- host.set_config_string("access_phase_token_secret", secret);
2199+- }
2200+-
2201+ private bool get_persistent_strip_metadata() {
2202+ return host.get_config_bool("strip_metadata", false);
2203+ }
2204+@@ -167,94 +136,17 @@ public class FlickrPublisher : Spit.Publishing.Publisher, GLib.Object {
2205+ host.set_config_bool("strip_metadata", strip_metadata);
2206+ }
2207+
2208+- private void on_welcome_pane_login_clicked() {
2209+- if (!running)
2210+- return;
2211+-
2212+- debug("EVENT: user clicked 'Login' button in the welcome pane");
2213+-
2214+- do_run_authentication_request_transaction();
2215+- }
2216+-
2217+- private void on_auth_request_txn_completed(Publishing.RESTSupport.Transaction txn) {
2218+- txn.completed.disconnect(on_auth_request_txn_completed);
2219+- txn.network_error.disconnect(on_auth_request_txn_error);
2220+-
2221+- if (!is_running())
2222+- return;
2223+-
2224+- debug("EVENT: OAuth authentication request transaction completed; response = '%s'",
2225+- txn.get_response());
2226+-
2227+- do_parse_token_info_from_auth_request(txn.get_response());
2228+- }
2229+-
2230+- private void on_auth_request_txn_error(Publishing.RESTSupport.Transaction txn,
2231+- Spit.Publishing.PublishingError err) {
2232+- txn.completed.disconnect(on_auth_request_txn_completed);
2233+- txn.network_error.disconnect(on_auth_request_txn_error);
2234+-
2235+- if (!is_running())
2236+- return;
2237+-
2238+- debug("EVENT: OAuth authentication request transaction caused a network error");
2239+- host.post_error(err);
2240+- }
2241+-
2242+- private void on_authentication_token_available(string token, string token_secret) {
2243+- debug("EVENT: OAuth authentication token (%s) and token secret (%s) available",
2244+- token, token_secret);
2245+-
2246+- session.set_request_phase_credentials(token, token_secret);
2247+-
2248+- do_launch_system_browser(token);
2249+- }
2250+-
2251+- private void on_system_browser_launched() {
2252+- if (!is_running())
2253+- return;
2254+-
2255+- debug("EVENT: system browser launched.");
2256+-
2257+- do_show_pin_entry_pane();
2258+- }
2259+-
2260+- private void on_pin_entry_proceed(PinEntryPane sender, string pin) {
2261+- sender.proceed.disconnect(on_pin_entry_proceed);
2262+-
2263+- if (!is_running())
2264+- return;
2265+-
2266+- debug("EVENT: user clicked 'Continue' in PIN entry pane.");
2267+-
2268+- do_verify_pin(pin);
2269+- }
2270++ public void on_authenticator_authenticated(owned HashTable<string,Value?> session_data) {
2271++ host.set_service_locked(true);
2272++ host.install_account_fetch_wait_pane();
2273+
2274+- private void on_access_token_fetch_txn_completed(Publishing.RESTSupport.Transaction txn) {
2275+- txn.completed.disconnect(on_access_token_fetch_txn_completed);
2276+- txn.network_error.disconnect(on_access_token_fetch_error);
2277+-
2278+- if (!is_running())
2279+- return;
2280+-
2281+- debug("EVENT: fetching OAuth access token over the network succeeded");
2282+-
2283+- do_extract_access_phase_credentials_from_reponse(txn.get_response());
2284++ string token = session_data.lookup("AccessToken").get_string();
2285++ string token_secret = session_data.lookup("TokenSecret").get_string();
2286++ string username = session_data.lookup("username").get_string();
2287++ debug("Access Token: %s, %s, %s", token, token_secret, username);
2288++ session.set_access_phase_credentials(token, token_secret, username);
2289+ }
2290+
2291+- private void on_access_token_fetch_error(Publishing.RESTSupport.Transaction txn,
2292+- Spit.Publishing.PublishingError err) {
2293+- txn.completed.disconnect(on_access_token_fetch_txn_completed);
2294+- txn.network_error.disconnect(on_access_token_fetch_error);
2295+-
2296+- if (!is_running())
2297+- return;
2298+-
2299+- debug("EVENT: fetching OAuth access token over the network caused an error.");
2300+-
2301+- host.post_error(err);
2302+- }
2303+-
2304+ private void on_session_authenticated() {
2305+ if (!is_running())
2306+ return;
2307+@@ -263,10 +155,6 @@ public class FlickrPublisher : Spit.Publishing.Publisher, GLib.Object {
2308+
2309+ parameters.username = session.get_username();
2310+
2311+- set_persistent_access_phase_token(session.get_access_phase_token());
2312+- set_persistent_access_phase_token_secret(session.get_access_phase_token_secret());
2313+- set_persistent_access_phase_username(session.get_username());
2314+-
2315+ do_fetch_account_info();
2316+ }
2317+
2318+@@ -303,7 +191,6 @@ public class FlickrPublisher : Spit.Publishing.Publisher, GLib.Object {
2319+
2320+ private void on_publishing_options_pane_publish(bool strip_metadata) {
2321+ publishing_options_pane.publish.disconnect(on_publishing_options_pane_publish);
2322+- publishing_options_pane.logout.disconnect(on_publishing_options_pane_logout);
2323+
2324+ if (!is_running())
2325+ return;
2326+@@ -312,18 +199,6 @@ public class FlickrPublisher : Spit.Publishing.Publisher, GLib.Object {
2327+ do_publish(strip_metadata);
2328+ }
2329+
2330+- private void on_publishing_options_pane_logout() {
2331+- publishing_options_pane.publish.disconnect(on_publishing_options_pane_publish);
2332+- publishing_options_pane.logout.disconnect(on_publishing_options_pane_logout);
2333+-
2334+- if (!is_running())
2335+- return;
2336+-
2337+- debug("EVENT: user clicked the 'Logout' button in the publishing options pane");
2338+-
2339+- do_logout();
2340+- }
2341+-
2342+ private void on_upload_status_updated(int file_number, double completed_fraction) {
2343+ if (!is_running())
2344+ return;
2345+@@ -361,148 +236,6 @@ public class FlickrPublisher : Spit.Publishing.Publisher, GLib.Object {
2346+ host.post_error(err);
2347+ }
2348+
2349+- private void do_show_login_welcome_pane() {
2350+- debug("ACTION: installing login welcome pane");
2351+-
2352+- host.set_service_locked(false);
2353+- host.install_welcome_pane(SERVICE_WELCOME_MESSAGE, on_welcome_pane_login_clicked);
2354+- }
2355+-
2356+- private void do_run_authentication_request_transaction() {
2357+- debug("ACTION: running authentication request transaction");
2358+-
2359+- host.set_service_locked(true);
2360+- host.install_static_message_pane(_("Preparing for login..."));
2361+-
2362+- AuthenticationRequestTransaction txn = new AuthenticationRequestTransaction(session);
2363+- txn.completed.connect(on_auth_request_txn_completed);
2364+- txn.network_error.connect(on_auth_request_txn_error);
2365+-
2366+- try {
2367+- txn.execute();
2368+- } catch (Spit.Publishing.PublishingError err) {
2369+- host.post_error(err);
2370+- }
2371+- }
2372+-
2373+- private void do_parse_token_info_from_auth_request(string response) {
2374+- debug("ACTION: parsing authorization request response '%s' into token and secret", response);
2375+-
2376+- string? oauth_token = null;
2377+- string? oauth_token_secret = null;
2378+-
2379+- string[] key_value_pairs = response.split("&");
2380+- foreach (string pair in key_value_pairs) {
2381+- string[] split_pair = pair.split("=");
2382+-
2383+- if (split_pair.length != 2)
2384+- host.post_error(new Spit.Publishing.PublishingError.MALFORMED_RESPONSE(
2385+- "'%s' isn't a valid response to an OAuth authentication request"));
2386+-
2387+- if (split_pair[0] == "oauth_token")
2388+- oauth_token = split_pair[1];
2389+- else if (split_pair[0] == "oauth_token_secret")
2390+- oauth_token_secret = split_pair[1];
2391+- }
2392+-
2393+- if (oauth_token == null || oauth_token_secret == null)
2394+- host.post_error(new Spit.Publishing.PublishingError.MALFORMED_RESPONSE(
2395+- "'%s' isn't a valid response to an OAuth authentication request"));
2396+-
2397+-
2398+- on_authentication_token_available(oauth_token, oauth_token_secret);
2399+- }
2400+-
2401+- private void do_launch_system_browser(string token) {
2402+- string login_uri = "http://www.flickr.com/services/oauth/authorize?oauth_token=" + token +
2403+- "&perms=write";
2404+-
2405+- debug("ACTION: launching system browser with uri = '%s'", login_uri);
2406+-
2407+- try {
2408+- Process.spawn_command_line_async("xdg-open " + login_uri);
2409+- } catch (SpawnError e) {
2410+- host.post_error(new Spit.Publishing.PublishingError.LOCAL_FILE_ERROR(
2411+- "couldn't launch system web browser to complete Flickr login"));
2412+- return;
2413+- }
2414+-
2415+- on_system_browser_launched();
2416+- }
2417+-
2418+- private void do_show_pin_entry_pane() {
2419+- debug("ACTION: showing PIN entry pane");
2420+-
2421+- Gtk.Builder builder = new Gtk.Builder();
2422+-
2423+- try {
2424+- builder.add_from_file(host.get_module_file().get_parent().get_child("flickr_pin_entry_pane.glade").get_path());
2425+- } catch (Error e) {
2426+- warning("Could not parse UI file! Error: %s.", e.message);
2427+- host.post_error(
2428+- new Spit.Publishing.PublishingError.LOCAL_FILE_ERROR(
2429+- _("A file required for publishing is unavailable. Publishing to Flickr can't continue.")));
2430+- return;
2431+- }
2432+-
2433+- PinEntryPane pin_entry_pane = new PinEntryPane(builder);
2434+- pin_entry_pane.proceed.connect(on_pin_entry_proceed);
2435+- host.install_dialog_pane(pin_entry_pane);
2436+- }
2437+-
2438+- private void do_verify_pin(string pin) {
2439+- debug("ACTION: validating authorization PIN %s", pin);
2440+-
2441+- host.set_service_locked(true);
2442+- host.install_static_message_pane(_("Verifying authorization..."));
2443+-
2444+- AccessTokenFetchTransaction txn = new AccessTokenFetchTransaction(session, pin);
2445+- txn.completed.connect(on_access_token_fetch_txn_completed);
2446+- txn.network_error.connect(on_access_token_fetch_error);
2447+-
2448+- try {
2449+- txn.execute();
2450+- } catch (Spit.Publishing.PublishingError err) {
2451+- host.post_error(err);
2452+- }
2453+- }
2454+-
2455+- private void do_extract_access_phase_credentials_from_reponse(string response) {
2456+- debug("ACTION: extracting access phase credentials from '%s'", response);
2457+-
2458+- string[] key_value_pairs = response.split("&");
2459+-
2460+- string? token = null;
2461+- string? token_secret = null;
2462+- string? username = null;
2463+- foreach (string key_value_pair in key_value_pairs) {
2464+- string[] split_pair = key_value_pair.split("=");
2465+-
2466+- if (split_pair.length != 2)
2467+- continue;
2468+-
2469+- string key = split_pair[0];
2470+- string value = split_pair[1];
2471+-
2472+- if (key == "oauth_token")
2473+- token = value;
2474+- else if (key == "oauth_token_secret")
2475+- token_secret = value;
2476+- else if (key == "username")
2477+- username = value;
2478+- }
2479+-
2480+- debug("access phase credentials: { token = '%s'; token_secret = '%s'; username = '%s' }",
2481+- token, token_secret, username);
2482+-
2483+- if (token == null || token_secret == null || username == null)
2484+- host.post_error(new Spit.Publishing.PublishingError.MALFORMED_RESPONSE("expected " +
2485+- "access phase credentials to contain token, token secret, and username but at " +
2486+- "least one of these is absent"));
2487+-
2488+- session.set_access_phase_credentials(token, token_secret, username);
2489+- }
2490+-
2491+ private void do_fetch_account_info() {
2492+ debug("ACTION: running network transaction to fetch account information");
2493+
2494+@@ -568,7 +301,6 @@ public class FlickrPublisher : Spit.Publishing.Publisher, GLib.Object {
2495+ debug("ACTION: logging user out, deauthenticating session, and erasing stored credentials");
2496+
2497+ session.deauthenticate();
2498+- invalidate_persistent_session();
2499+
2500+ running = false;
2501+
2502+@@ -599,7 +331,6 @@ public class FlickrPublisher : Spit.Publishing.Publisher, GLib.Object {
2503+ publishing_options_pane = new PublishingOptionsPane(this, parameters,
2504+ host.get_publishable_media_type(), builder, get_persistent_strip_metadata());
2505+ publishing_options_pane.publish.connect(on_publishing_options_pane_publish);
2506+- publishing_options_pane.logout.connect(on_publishing_options_pane_logout);
2507+ host.install_dialog_pane(publishing_options_pane);
2508+ }
2509+
2510+@@ -674,16 +405,7 @@ public class FlickrPublisher : Spit.Publishing.Publisher, GLib.Object {
2511+ running = true;
2512+ was_started = true;
2513+
2514+- if (is_persistent_session_valid()) {
2515+- debug("attempt start: a persistent session is available; using it");
2516+-
2517+- session.authenticate_from_persistent_credentials(get_persistent_access_phase_token(),
2518+- get_persistent_access_phase_token_secret(), get_persistent_access_phase_username());
2519+- } else {
2520+- debug("attempt start: no persistent session available; showing login welcome pane");
2521+-
2522+- do_show_login_welcome_pane();
2523+- }
2524++ authenticator.authenticate();
2525+ }
2526+
2527+ public void start() {
2528+@@ -772,7 +494,7 @@ internal class Transaction : Publishing.RESTSupport.Transaction {
2529+ add_argument("oauth_version", "1.0");
2530+ add_argument("oauth_callback", "oob");
2531+ add_argument("oauth_timestamp", session.get_oauth_timestamp());
2532+- add_argument("oauth_consumer_key", API_KEY);
2533++ add_argument("oauth_consumer_key", session.get_api_key());
2534+ }
2535+
2536+ public Transaction.with_uri(Session session, string uri,
2537+@@ -784,7 +506,7 @@ internal class Transaction : Publishing.RESTSupport.Transaction {
2538+ add_argument("oauth_version", "1.0");
2539+ add_argument("oauth_callback", "oob");
2540+ add_argument("oauth_timestamp", session.get_oauth_timestamp());
2541+- add_argument("oauth_consumer_key", API_KEY);
2542++ add_argument("oauth_consumer_key", session.get_api_key());
2543+ }
2544+
2545+ public override void execute() throws Spit.Publishing.PublishingError {
2546+@@ -842,22 +564,6 @@ internal class Transaction : Publishing.RESTSupport.Transaction {
2547+ }
2548+ }
2549+
2550+-internal class AuthenticationRequestTransaction : Transaction {
2551+- public AuthenticationRequestTransaction(Session session) {
2552+- base.with_uri(session, "http://www.flickr.com/services/oauth/request_token",
2553+- Publishing.RESTSupport.HttpMethod.GET);
2554+- }
2555+-}
2556+-
2557+-internal class AccessTokenFetchTransaction : Transaction {
2558+- public AccessTokenFetchTransaction(Session session, string user_verifier) {
2559+- base.with_uri(session, "http://www.flickr.com/services/oauth/access_token",
2560+- Publishing.RESTSupport.HttpMethod.GET);
2561+- add_argument("oauth_verifier", user_verifier);
2562+- add_argument("oauth_token", session.get_request_phase_token());
2563+- }
2564+-}
2565+-
2566+ internal class AccountInfoFetchTransaction : Transaction {
2567+ public AccountInfoFetchTransaction(Session session) {
2568+ base(session, Publishing.RESTSupport.HttpMethod.GET);
2569+@@ -884,7 +590,7 @@ private class UploadTransaction : Publishing.RESTSupport.UploadTransaction {
2570+ add_authorization_header_field("oauth_version", "1.0");
2571+ add_authorization_header_field("oauth_callback", "oob");
2572+ add_authorization_header_field("oauth_timestamp", session.get_oauth_timestamp());
2573+- add_authorization_header_field("oauth_consumer_key", API_KEY);
2574++ add_authorization_header_field("oauth_consumer_key", session.get_api_key());
2575+ add_authorization_header_field("oauth_token", session.get_access_phase_token());
2576+
2577+ add_argument("is_public", ("%d".printf(parameters.visibility_specification.everyone_level)));
2578+@@ -944,11 +650,11 @@ private class UploadTransaction : Publishing.RESTSupport.UploadTransaction {
2579+ }
2580+
2581+ internal class Session : Publishing.RESTSupport.Session {
2582+- private string? request_phase_token = null;
2583+- private string? request_phase_token_secret = null;
2584+ private string? access_phase_token = null;
2585+ private string? access_phase_token_secret = null;
2586+ private string? username = null;
2587++ private string? api_key = API_KEY;
2588++ private string? api_secret = API_SECRET;
2589+
2590+ public Session() {
2591+ base(ENDPOINT_URL);
2592+@@ -959,15 +665,6 @@ internal class Session : Publishing.RESTSupport.Session {
2593+ username != null);
2594+ }
2595+
2596+- public void authenticate_from_persistent_credentials(string token, string secret,
2597+- string username) {
2598+- this.access_phase_token = token;
2599+- this.access_phase_token_secret = secret;
2600+- this.username = username;
2601+-
2602+- authenticated();
2603+- }
2604+-
2605+ public void deauthenticate() {
2606+ access_phase_token = null;
2607+ access_phase_token_secret = null;
2608+@@ -1008,16 +705,12 @@ internal class Session : Publishing.RESTSupport.Session {
2609+ if (access_phase_token_secret != null) {
2610+ debug("access phase token secret available; using it as signing key");
2611+
2612+- signing_key = API_SECRET + "&" + access_phase_token_secret;
2613+- } else if (request_phase_token_secret != null) {
2614+- debug("request phase token secret available; using it as signing key");
2615+-
2616+- signing_key = API_SECRET + "&" + request_phase_token_secret;
2617++ signing_key = api_secret + "&" + access_phase_token_secret;
2618+ } else {
2619+- debug("neither access phase nor request phase token secrets available; using API " +
2620++ debug("access token secret not available; using API " +
2621+ "key as signing key");
2622+
2623+- signing_key = API_SECRET + "&";
2624++ signing_key = api_secret + "&";
2625+ }
2626+
2627+ string signature_base_string = http_method + "&" + Soup.URI.encode(
2628+@@ -1040,11 +733,11 @@ internal class Session : Publishing.RESTSupport.Session {
2629+ txn.add_argument("oauth_signature", signature);
2630+ }
2631+
2632+- public void set_request_phase_credentials(string token, string secret) {
2633+- this.request_phase_token = token;
2634+- this.request_phase_token_secret = secret;
2635++ public void set_api_credentials(string api_key, string api_secret) {
2636++ this.api_key = api_key;
2637++ this.api_secret = api_secret;
2638+ }
2639+-
2640++
2641+ public void set_access_phase_credentials(string token, string secret, string username) {
2642+ this.access_phase_token = token;
2643+ this.access_phase_token_secret = secret;
2644+@@ -1065,21 +758,16 @@ internal class Session : Publishing.RESTSupport.Session {
2645+ return GLib.get_real_time().to_string().substring(0, 10);
2646+ }
2647+
2648+- public string get_request_phase_token() {
2649+- assert(request_phase_token != null);
2650+- return request_phase_token;
2651++ public string get_api_key() {
2652++ assert(api_key != null);
2653++ return api_key;
2654+ }
2655+-
2656++
2657+ public string get_access_phase_token() {
2658+ assert(access_phase_token != null);
2659+ return access_phase_token;
2660+ }
2661+
2662+- public string get_access_phase_token_secret() {
2663+- assert(access_phase_token_secret != null);
2664+- return access_phase_token_secret;
2665+- }
2666+-
2667+ public string get_username() {
2668+ assert(is_authenticated());
2669+ return username;
2670+@@ -1112,7 +800,6 @@ internal class PublishingOptionsPane : Spit.Publishing.DialogPane, GLib.Object {
2671+ private Gtk.Label visibility_label = null;
2672+ private Gtk.Label upload_info_label = null;
2673+ private Gtk.Label size_label = null;
2674+- private Gtk.Button logout_button = null;
2675+ private Gtk.Button publish_button = null;
2676+ private Gtk.ComboBoxText visibility_combo = null;
2677+ private Gtk.ComboBoxText size_combo = null;
2678+@@ -1124,7 +811,6 @@ internal class PublishingOptionsPane : Spit.Publishing.DialogPane, GLib.Object {
2679+ private Spit.Publishing.Publisher.MediaType media_type;
2680+
2681+ public signal void publish(bool strip_metadata);
2682+- public signal void logout();
2683+
2684+ public PublishingOptionsPane(FlickrPublisher publisher, PublishingParameters parameters,
2685+ Spit.Publishing.Publisher.MediaType media_type, Gtk.Builder builder, bool strip_metadata) {
2686+@@ -1136,7 +822,6 @@ internal class PublishingOptionsPane : Spit.Publishing.DialogPane, GLib.Object {
2687+ pane_widget = (Gtk.Box) this.builder.get_object("flickr_pane");
2688+ visibility_label = (Gtk.Label) this.builder.get_object("visibility_label");
2689+ upload_info_label = (Gtk.Label) this.builder.get_object("upload_info_label");
2690+- logout_button = (Gtk.Button) this.builder.get_object("logout_button");
2691+ publish_button = (Gtk.Button) this.builder.get_object("publish_button");
2692+ visibility_combo = (Gtk.ComboBoxText) this.builder.get_object("visibility_combo");
2693+ size_combo = (Gtk.ComboBoxText) this.builder.get_object("size_combo");
2694+@@ -1183,14 +868,9 @@ internal class PublishingOptionsPane : Spit.Publishing.DialogPane, GLib.Object {
2695+
2696+ strip_metadata_check.set_active(strip_metadata);
2697+
2698+- logout_button.clicked.connect(on_logout_clicked);
2699+ publish_button.clicked.connect(on_publish_clicked);
2700+ }
2701+
2702+- private void on_logout_clicked() {
2703+- logout();
2704+- }
2705+-
2706+ private void on_publish_clicked() {
2707+ parameters.visibility_specification =
2708+ visibilities[visibility_combo.get_active()].specification;
2709+@@ -1257,10 +937,6 @@ internal class PublishingOptionsPane : Spit.Publishing.DialogPane, GLib.Object {
2710+ publish(strip_metadata_check.get_active());
2711+ }
2712+
2713+- protected void notify_logout() {
2714+- logout();
2715+- }
2716+-
2717+ public Gtk.Widget get_widget() {
2718+ return pane_widget;
2719+ }
2720+@@ -1271,12 +947,10 @@ internal class PublishingOptionsPane : Spit.Publishing.DialogPane, GLib.Object {
2721+
2722+ public void on_pane_installed() {
2723+ publish.connect(notify_publish);
2724+- logout.connect(notify_logout);
2725+ }
2726+
2727+ public void on_pane_uninstalled() {
2728+ publish.disconnect(notify_publish);
2729+- logout.disconnect(notify_logout);
2730+ }
2731+ }
2732+
2733+diff --git a/plugins/shotwell-publishing/Makefile b/plugins/shotwell-publishing/Makefile
2734+index e939a77..bacfff4 100644
2735+--- a/plugins/shotwell-publishing/Makefile
2736++++ b/plugins/shotwell-publishing/Makefile
2737+@@ -18,6 +18,7 @@ SRC_FILES := \
2738+ FlickrPublishing.vala \
2739+ YouTubePublishing.vala \
2740+ PiwigoPublishing.vala \
2741++ ../common/accounts.vala \
2742+ ../../src/util/string.vala \
2743+ ../common/RESTSupport.vala \
2744+ ../common/ui.vala
2745+diff --git a/plugins/shotwell-publishing/PicasaPublishing.vala b/plugins/shotwell-publishing/PicasaPublishing.vala
2746+index 397503d..a428a30 100644
2747+--- a/plugins/shotwell-publishing/PicasaPublishing.vala
2748++++ b/plugins/shotwell-publishing/PicasaPublishing.vala
2749+@@ -4,30 +4,28 @@
2750+ * (version 2.1 or later). See the COPYING file in this distribution.
2751+ */
2752+
2753+-public class PicasaService : Object, Spit.Pluggable, Spit.Publishing.Service {
2754++using Publishing.Accounts;
2755++
2756++public class PicasaService : UOAPublishingService, Spit.Pluggable, Spit.Publishing.Service {
2757+ private const string ICON_FILENAME = "picasa.png";
2758+
2759+ private static Gdk.Pixbuf[] icon_pixbuf_set = null;
2760+
2761+ public PicasaService(GLib.File resource_directory) {
2762++ base("google");
2763+ if (icon_pixbuf_set == null)
2764+ icon_pixbuf_set = Resources.load_icon_set(resource_directory.get_child(ICON_FILENAME));
2765+ }
2766+
2767+- public int get_pluggable_interface(int min_host_interface, int max_host_interface) {
2768+- return Spit.negotiate_interfaces(min_host_interface, max_host_interface,
2769+- Spit.Publishing.CURRENT_INTERFACE);
2770+- }
2771+-
2772+- public unowned string get_id() {
2773++ public override unowned string get_id() {
2774+ return "org.yorba.shotwell.publishing.picasa";
2775+ }
2776+
2777+- public unowned string get_pluggable_name() {
2778++ public override unowned string get_pluggable_name() {
2779+ return "Picasa Web Albums";
2780+ }
2781+
2782+- public void get_info(ref Spit.PluggableInfo info) {
2783++ public override void get_info(ref Spit.PluggableInfo info) {
2784+ info.authors = "Lucas Beeler";
2785+ info.copyright = _("Copyright 2009-2012 Yorba Foundation");
2786+ info.translators = Resources.TRANSLATORS;
2787+@@ -39,17 +37,15 @@ public class PicasaService : Object, Spit.Pluggable, Spit.Publishing.Service {
2788+ info.icons = icon_pixbuf_set;
2789+ }
2790+
2791+- public Spit.Publishing.Publisher create_publisher(Spit.Publishing.PluginHost host) {
2792+- return new Publishing.Picasa.PicasaPublisher(this, host);
2793++ public override Spit.Publishing.Publisher create_publisher(string? account_name, Spit.Publishing.PluginHost host) {
2794++ SharingAccount account = find_account(account_name);
2795++ return new Publishing.Picasa.PicasaPublisher(this, account, host);
2796+ }
2797+
2798+- public Spit.Publishing.Publisher.MediaType get_supported_media() {
2799++ public override Spit.Publishing.Publisher.MediaType get_supported_media() {
2800+ return (Spit.Publishing.Publisher.MediaType.PHOTO |
2801+ Spit.Publishing.Publisher.MediaType.VIDEO);
2802+ }
2803+-
2804+- public void activation(bool enabled) {
2805+- }
2806+ }
2807+
2808+ namespace Publishing.Picasa {
2809+@@ -72,29 +68,22 @@ public class PicasaPublisher : Spit.Publishing.Publisher, GLib.Object {
2810+ private PublishingParameters parameters = null;
2811+ private Spit.Publishing.Publisher.MediaType media_type =
2812+ Spit.Publishing.Publisher.MediaType.NONE;
2813++ private UOAPublisherAuthenticator authenticator = null;
2814+
2815+ public PicasaPublisher(Spit.Publishing.Service service,
2816++ SharingAccount account,
2817+ Spit.Publishing.PluginHost host) {
2818+ this.service = service;
2819+ this.host = host;
2820+ this.session = new Session();
2821++ authenticator = new UOAPublisherAuthenticator(account, host,
2822++ SERVICE_WELCOME_MESSAGE);
2823++ authenticator.authenticated.connect(on_authenticator_authenticated);
2824+
2825+ foreach(Spit.Publishing.Publishable p in host.get_publishables())
2826+ media_type |= p.get_media_type();
2827+ }
2828+
2829+- private string get_user_authorization_url() {
2830+- return "https://accounts.google.com/o/oauth2/auth?" +
2831+- "response_type=code&" +
2832+- "client_id=" + OAUTH_CLIENT_ID + "&" +
2833+- "redirect_uri=" + Soup.URI.encode("urn:ietf:wg:oauth:2.0:oob", null) + "&" +
2834+- "scope=" + Soup.URI.encode("http://picasaweb.google.com/data/", null) + "+" +
2835+- Soup.URI.encode("https://www.googleapis.com/auth/userinfo.profile", null) + "&" +
2836+- "state=connect&" +
2837+- "access_type=offline&" +
2838+- "approval_prompt=force";
2839+- }
2840+-
2841+ private Album[] extract_albums(Xml.Node* document_root) throws Spit.Publishing.PublishingError {
2842+ Album[] result = new Album[0];
2843+
2844+@@ -133,14 +122,6 @@ public class PicasaPublisher : Spit.Publishing.Publisher, GLib.Object {
2845+ return result;
2846+ }
2847+
2848+- internal string? get_persistent_refresh_token() {
2849+- return host.get_config_string("refresh_token", null);
2850+- }
2851+-
2852+- internal void set_persistent_refresh_token(string token) {
2853+- host.set_config_string("refresh_token", token);
2854+- }
2855+-
2856+ internal bool get_persistent_strip_metadata() {
2857+ return host.get_config_bool("strip_metadata", false);
2858+ }
2859+@@ -149,16 +130,6 @@ public class PicasaPublisher : Spit.Publishing.Publisher, GLib.Object {
2860+ host.set_config_bool("strip_metadata", strip_metadata);
2861+ }
2862+
2863+- internal void invalidate_persistent_session() {
2864+- debug("invalidating persisted Picasa Web Albums session.");
2865+-
2866+- host.unset_config_key("refresh_token");
2867+- }
2868+-
2869+- internal bool is_persistent_session_available() {
2870+- return get_persistent_refresh_token() != null;
2871+- }
2872+-
2873+ public bool is_running() {
2874+ return running;
2875+ }
2876+@@ -167,98 +138,22 @@ public class PicasaPublisher : Spit.Publishing.Publisher, GLib.Object {
2877+ return service;
2878+ }
2879+
2880+- private void on_service_welcome_login() {
2881+- if (!is_running())
2882+- return;
2883+-
2884+- debug("EVENT: user clicked 'Login' in welcome pane.");
2885+-
2886+- do_launch_browser_for_authorization();
2887+- }
2888+-
2889+- private void on_auth_code_entry_pane_proceed(AuthCodeEntryPane sender, string code) {
2890+- debug("EVENT: user clicked 'Continue' in authorization code entry pane.");
2891+-
2892+- sender.proceed.disconnect(on_auth_code_entry_pane_proceed);
2893+-
2894+- do_get_access_tokens(code);
2895+- }
2896+-
2897+- private void on_browser_launched() {
2898+- debug("EVENT: system web browser launched to solicit user authorization.");
2899+-
2900+- do_show_auth_code_entry_pane();
2901+- }
2902+-
2903+- private void on_get_access_tokens_completed(Publishing.RESTSupport.Transaction txn) {
2904+- txn.completed.disconnect(on_get_access_tokens_completed);
2905+- txn.network_error.disconnect(on_get_access_tokens_error);
2906+-
2907+- debug("EVENT: network transaction to exchange authorization code for access tokens " +
2908+- "completed successfully.");
2909+-
2910+- do_extract_tokens(txn.get_response());
2911+- }
2912+-
2913+- private void on_get_access_tokens_error(Publishing.RESTSupport.Transaction txn,
2914+- Spit.Publishing.PublishingError err) {
2915+- txn.completed.disconnect(on_get_access_tokens_completed);
2916+- txn.network_error.disconnect(on_get_access_tokens_error);
2917+-
2918+- debug("EVENT: network transaction to exchange authorization code for access tokens " +
2919+- "failed; response = '%s'", txn.get_response());
2920+- }
2921+-
2922+- private void on_refresh_token_available(string token) {
2923+- debug("EVENT: an OAuth refresh token has become available.");
2924+-
2925+- do_save_refresh_token_to_configuration_system(token);
2926+- }
2927+-
2928+- private void on_access_token_available(string token) {
2929+- debug("EVENT: an OAuth access token has become available.");
2930+-
2931+- do_authenticate_session(token);
2932+- }
2933+-
2934+- private void on_not_set_up_pane_proceed(NotSetUpMessagePane sender) {
2935+- debug("EVENT: user clicked 'Continue' in Account Not Set Up Message Pane.");
2936+-
2937+- sender.proceed.disconnect(on_not_set_up_pane_proceed);
2938+-
2939+- do_launch_browser_for_authorization();
2940+- }
2941+-
2942+- private void on_refresh_access_token_transaction_completed(Publishing.RESTSupport.Transaction
2943+- txn) {
2944+- txn.completed.disconnect(on_refresh_access_token_transaction_completed);
2945+- txn.network_error.disconnect(on_refresh_access_token_transaction_error);
2946+-
2947+- if (!is_running())
2948+- return;
2949+-
2950+- if (session.is_authenticated()) // ignore these events if the session is already auth'd
2951+- return;
2952+-
2953+- debug("EVENT: refresh access token transaction completed successfully.");
2954+-
2955+- do_extract_tokens(txn.get_response());
2956+- }
2957+-
2958+- private void on_refresh_access_token_transaction_error(Publishing.RESTSupport.Transaction txn,
2959+- Spit.Publishing.PublishingError err) {
2960+- txn.completed.disconnect(on_refresh_access_token_transaction_completed);
2961+- txn.network_error.disconnect(on_refresh_access_token_transaction_error);
2962+-
2963+- if (!is_running())
2964+- return;
2965++ public void on_authenticator_authenticated(owned HashTable<string,Value?> session_data) {
2966++ host.install_account_fetch_wait_pane();
2967+
2968+- if (session.is_authenticated()) // ignore these events if the session is already auth'd
2969+- return;
2970++ session.authenticated.connect(on_session_authenticated);
2971+
2972+- debug("EVENT: refresh access token transaction caused a network error.");
2973+-
2974+- host.post_error(err);
2975++ if (session_data.lookup("AccessToken") != null) {
2976++ string token = session_data.lookup("AccessToken").get_string();
2977++ debug("OAuth Access Token: %s", token);
2978++ session.authenticate(token, "OAuth2");
2979++ } else if (session_data.lookup("AuthToken") != null) {
2980++ string token = session_data.lookup("AuthToken").get_string();
2981++ debug("ClientLogin Access Token: %s", token);
2982++ session.authenticate(token, "ClientLogin");
2983++ } else {
2984++ debug("Access token not present!");
2985++ }
2986+ }
2987+
2988+ private void on_session_authenticated() {
2989+@@ -313,31 +208,7 @@ public class PicasaPublisher : Spit.Publishing.Publisher, GLib.Object {
2990+ debug("EVENT: fetching account and album information failed; response = '%s'.",
2991+ bad_txn.get_response());
2992+
2993+- if (bad_txn.get_status_code() == 404) {
2994+- // if we get a 404 error (resource not found) on the initial album fetch, then the
2995+- // user's album feed doesn't exist -- this occurs when the user has a valid Google
2996+- // account but it hasn't yet been set up for use with Picasa. In this case, we
2997+- // display an informational pane with an "account not set up" message. In addition, we
2998+- // deauthenticate the session. Deauth is neccessary because must've previously auth'd
2999+- // the user's account to even be able to query the album feed.
3000+- session.deauthenticate();
3001+- do_show_not_set_up_pane();
3002+- } else {
3003+- // If we get any other kind of error, we can't recover, so just post it to the user
3004+- host.post_error(err);
3005+- }
3006+- }
3007+-
3008+- private void on_publishing_options_logout() {
3009+- if (!is_running())
3010+- return;
3011+-
3012+- debug("EVENT: user clicked 'Logout' in the publishing options pane.");
3013+-
3014+- session.deauthenticate();
3015+- invalidate_persistent_session();
3016+-
3017+- do_show_service_welcome_pane();
3018++ host.post_error(err);
3019+ }
3020+
3021+ private void on_publishing_options_publish(PublishingParameters parameters,
3022+@@ -447,100 +318,6 @@ public class PicasaPublisher : Spit.Publishing.Publisher, GLib.Object {
3023+ host.post_error(err);
3024+ }
3025+
3026+- private void do_show_service_welcome_pane() {
3027+- debug("ACTION: showing service welcome pane.");
3028+-
3029+- host.install_welcome_pane(SERVICE_WELCOME_MESSAGE, on_service_welcome_login);
3030+- }
3031+-
3032+- private void do_launch_browser_for_authorization() {
3033+- string auth_url = get_user_authorization_url();
3034+-
3035+- debug("ACTION: launching external web browser to get user authorization; " +
3036+- "authorization URL = '%s'", auth_url);
3037+-
3038+- try {
3039+- Process.spawn_command_line_async("xdg-open " + auth_url);
3040+- } catch (SpawnError e) {
3041+- host.post_error(new Spit.Publishing.PublishingError.LOCAL_FILE_ERROR(
3042+- _("couldn't launch system web browser to complete Picasa Web Albums login")));
3043+- return;
3044+- }
3045+-
3046+- on_browser_launched();
3047+- }
3048+-
3049+- private void do_show_auth_code_entry_pane() {
3050+- debug("ACTION: showing OAuth authorization code entry pane.");
3051+-
3052+- Gtk.Builder builder = new Gtk.Builder();
3053+-
3054+- try {
3055+- builder.add_from_file(host.get_module_file().get_parent().get_child(
3056+- "picasa_auth_code_entry_pane.glade").get_path());
3057+- } catch (Error e) {
3058+- warning("Could not parse UI file! Error: %s.", e.message);
3059+- host.post_error(
3060+- new Spit.Publishing.PublishingError.LOCAL_FILE_ERROR(
3061+- _("A file required for publishing is unavailable. Publishing to Picasa Web Albums can't continue.")));
3062+- return;
3063+- }
3064+-
3065+- AuthCodeEntryPane pane = new AuthCodeEntryPane(builder);
3066+- pane.proceed.connect(on_auth_code_entry_pane_proceed);
3067+- host.install_dialog_pane(pane);
3068+- }
3069+-
3070+- private void do_get_access_tokens(string code) {
3071+- debug("ACTION: exchanging OAuth authorization code '%s' for access token.", code);
3072+-
3073+- GetAccessTokensTransaction txn = new GetAccessTokensTransaction(session, code);
3074+- txn.completed.connect(on_get_access_tokens_completed);
3075+- txn.network_error.connect(on_get_access_tokens_error);
3076+-
3077+- try {
3078+- txn.execute();
3079+- } catch (Spit.Publishing.PublishingError err) {
3080+- host.post_error(err);
3081+- }
3082+- }
3083+-
3084+- private void do_extract_tokens(string response_body) {
3085+- debug("ACTION: extracting OAuth tokens from body of server response");
3086+-
3087+- Json.Parser parser = new Json.Parser();
3088+-
3089+- try {
3090+- parser.load_from_data(response_body);
3091+- } catch (Error err) {
3092+- host.post_error(new Spit.Publishing.PublishingError.MALFORMED_RESPONSE(
3093+- "Couldn't parse JSON response: " + err.message));
3094+- return;
3095+- }
3096+-
3097+- Json.Object response_obj = parser.get_root().get_object();
3098+-
3099+- if ((!response_obj.has_member("access_token")) && (!response_obj.has_member("refresh_token"))) {
3100+- host.post_error(new Spit.Publishing.PublishingError.MALFORMED_RESPONSE(
3101+- "neither access_token nor refresh_token not present in server response"));
3102+- return;
3103+- }
3104+-
3105+- if (response_obj.has_member("refresh_token")) {
3106+- string refresh_token = response_obj.get_string_member("refresh_token");
3107+-
3108+- if (refresh_token != "")
3109+- on_refresh_token_available(refresh_token);
3110+- }
3111+-
3112+- if (response_obj.has_member("access_token")) {
3113+- string access_token = response_obj.get_string_member("access_token");
3114+-
3115+- if (access_token != "")
3116+- on_access_token_available(access_token);
3117+- }
3118+- }
3119+-
3120+ private void do_extract_username(string response_body) {
3121+ debug("ACTION: extracting username from body of server response");
3122+
3123+@@ -562,66 +339,8 @@ public class PicasaPublisher : Spit.Publishing.Publisher, GLib.Object {
3124+ if (username != "")
3125+ this.username = username;
3126+ }
3127+-
3128+- if (response_obj.has_member("access_token")) {
3129+- string access_token = response_obj.get_string_member("access_token");
3130+-
3131+- if (access_token != "")
3132+- on_access_token_available(access_token);
3133+- }
3134+ }
3135+
3136+- private void do_save_refresh_token_to_configuration_system(string token) {
3137+- debug("ACTION: saving OAuth refresh token to configuration system");
3138+-
3139+- set_persistent_refresh_token(token);
3140+- }
3141+-
3142+- private void do_refresh_session(string refresh_token) {
3143+- debug("ACTION: using OAuth refresh token to refresh session.");
3144+-
3145+- host.install_login_wait_pane();
3146+-
3147+- RefreshAccessTokenTransaction txn = new RefreshAccessTokenTransaction(session, refresh_token);
3148+-
3149+- txn.completed.connect(on_refresh_access_token_transaction_completed);
3150+- txn.network_error.connect(on_refresh_access_token_transaction_error);
3151+-
3152+- try {
3153+- txn.execute();
3154+- } catch (Spit.Publishing.PublishingError err) {
3155+- host.post_error(err);
3156+- }
3157+- }
3158+-
3159+- private void do_authenticate_session(string token) {
3160+- debug("ACTION: authenticating session.");
3161+-
3162+- session.authenticated.connect(on_session_authenticated);
3163+- session.authenticate(token);
3164+- }
3165+-
3166+- private void do_show_not_set_up_pane() {
3167+- debug("ACTION: showing account not set up message pane");
3168+-
3169+- Gtk.Builder builder = new Gtk.Builder();
3170+-
3171+- try {
3172+- builder.add_from_file(host.get_module_file().get_parent().get_child(
3173+- "picasa_not_set_up_pane.glade").get_path());
3174+- } catch (Error e) {
3175+- warning("Could not parse UI file! Error: %s.", e.message);
3176+- host.post_error(
3177+- new Spit.Publishing.PublishingError.LOCAL_FILE_ERROR(
3178+- _("A file required for publishing is unavailable. Publishing to Picasa Web Albums can't continue.")));
3179+- return;
3180+- }
3181+-
3182+- NotSetUpMessagePane pane = new NotSetUpMessagePane(builder);
3183+- pane.proceed.connect(on_not_set_up_pane_proceed);
3184+- host.install_dialog_pane(pane);
3185+- }
3186+-
3187+ private void do_fetch_username() {
3188+ debug("ACTION: running network transaction to fetch username.");
3189+
3190+@@ -701,7 +420,6 @@ public class PicasaPublisher : Spit.Publishing.Publisher, GLib.Object {
3191+ PublishingOptionsPane opts_pane = new PublishingOptionsPane(host, username, albums, media_type, builder,
3192+ get_persistent_strip_metadata());
3193+ opts_pane.publish.connect(on_publishing_options_publish);
3194+- opts_pane.logout.connect(on_publishing_options_logout);
3195+ host.install_dialog_pane(opts_pane);
3196+
3197+ host.set_service_locked(false);
3198+@@ -770,12 +488,7 @@ public class PicasaPublisher : Spit.Publishing.Publisher, GLib.Object {
3199+ debug("PicasaPublisher: starting interaction.");
3200+
3201+ running = true;
3202+-
3203+- if (is_persistent_session_available()) {
3204+- do_refresh_session(get_persistent_refresh_token());
3205+- } else {
3206+- do_show_service_welcome_pane();
3207+- }
3208++ authenticator.authenticate();
3209+ }
3210+
3211+ public void stop() {
3212+@@ -801,6 +514,7 @@ internal class Album {
3213+
3214+ internal class Session : Publishing.RESTSupport.Session {
3215+ private string? auth_token = null;
3216++ private string? auth_method = null;
3217+
3218+ public Session() {
3219+ }
3220+@@ -809,45 +523,19 @@ internal class Session : Publishing.RESTSupport.Session {
3221+ return (auth_token != null);
3222+ }
3223+
3224+- public void authenticate(string auth_token) {
3225++ public void authenticate(string auth_token, string auth_method) {
3226+ this.auth_token = auth_token;
3227++ this.auth_method = auth_method;
3228+
3229+ notify_authenticated();
3230+ }
3231+
3232+- public void deauthenticate() {
3233+- auth_token = null;
3234+- }
3235+-
3236+ public string? get_auth_token() {
3237+ return auth_token;
3238+ }
3239+-}
3240+
3241+-internal class GetAccessTokensTransaction : Publishing.RESTSupport.Transaction {
3242+- private const string ENDPOINT_URL = "https://accounts.google.com/o/oauth2/token";
3243+-
3244+- public GetAccessTokensTransaction(Session session, string auth_code) {
3245+- base.with_endpoint_url(session, ENDPOINT_URL);
3246+-
3247+- add_argument("code", auth_code);
3248+- add_argument("client_id", OAUTH_CLIENT_ID);
3249+- add_argument("client_secret", OAUTH_CLIENT_SECRET);
3250+- add_argument("redirect_uri", "urn:ietf:wg:oauth:2.0:oob");
3251+- add_argument("grant_type", "authorization_code");
3252+- }
3253+-}
3254+-
3255+-internal class RefreshAccessTokenTransaction : Publishing.RESTSupport.Transaction {
3256+- private const string ENDPOINT_URL = "https://accounts.google.com/o/oauth2/token";
3257+-
3258+- public RefreshAccessTokenTransaction(Session session, string refresh_token) {
3259+- base.with_endpoint_url(session, ENDPOINT_URL);
3260+-
3261+- add_argument("client_id", OAUTH_CLIENT_ID);
3262+- add_argument("client_secret", OAUTH_CLIENT_SECRET);
3263+- add_argument("refresh_token", refresh_token);
3264+- add_argument("grant_type", "refresh_token");
3265++ public string? get_auth_method() {
3266++ return auth_method;
3267+ }
3268+ }
3269+
3270+@@ -862,7 +550,13 @@ internal class AuthenticatedTransaction : Publishing.RESTSupport.Transaction {
3271+ base.with_endpoint_url(session, endpoint_url, method);
3272+ assert(session.is_authenticated());
3273+
3274+- add_header("Authorization", "Bearer " + session.get_auth_token());
3275++ if (session.get_auth_method() == "ClientLogin") {
3276++ add_header("Authorization",
3277++ "GoogleLogin auth=" + session.get_auth_token());
3278++ } else {
3279++ add_header("Authorization",
3280++ "Bearer " + session.get_auth_token());
3281++ }
3282+ }
3283+ }
3284+
3285+@@ -875,7 +569,7 @@ internal class UsernameFetchTransaction : AuthenticatedTransaction {
3286+ }
3287+
3288+ internal class AlbumDirectoryTransaction : AuthenticatedTransaction {
3289+- private const string ENDPOINT_URL = "http://picasaweb.google.com/data/feed/api/user/" +
3290++ private const string ENDPOINT_URL = "https://picasaweb.google.com/data/feed/api/user/" +
3291+ "default";
3292+
3293+ public AlbumDirectoryTransaction(Session session) {
3294+@@ -892,7 +586,7 @@ internal class AlbumDirectoryTransaction : AuthenticatedTransaction {
3295+ }
3296+
3297+ private class AlbumCreationTransaction : AuthenticatedTransaction {
3298+- private const string ENDPOINT_URL = "http://picasaweb.google.com/data/feed/api/user/" +
3299++ private const string ENDPOINT_URL = "https://picasaweb.google.com/data/feed/api/user/" +
3300+ "default";
3301+ private const string ALBUM_ENTRY_TEMPLATE = "<?xml version='1.0' encoding='utf-8'?><entry xmlns='http://www.w3.org/2005/Atom' xmlns:gphoto='http://schemas.google.com/photos/2007'><title type='text'>%s</title><gphoto:access>%s</gphoto:access><category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/photos/2007#album'></category></entry>";
3302+
3303+@@ -971,9 +665,14 @@ internal class UploadTransaction : AuthenticatedTransaction {
3304+ // that we've been building up
3305+ Soup.Message outbound_message =
3306+ soup_form_request_new_from_multipart(get_endpoint_url(), message_parts);
3307+- outbound_message.request_headers.append("Authorization", "Bearer " +
3308+- session.get_auth_token());
3309+ set_message(outbound_message);
3310++ if (session.get_auth_method() == "ClientLogin") {
3311++ add_header("Authorization",
3312++ "GoogleLogin auth=" + session.get_auth_token());
3313++ } else {
3314++ add_header("Authorization",
3315++ "Bearer " + session.get_auth_token());
3316++ }
3317+
3318+ // send the message and get its response
3319+ set_is_executed(true);
3320+@@ -981,95 +680,6 @@ internal class UploadTransaction : AuthenticatedTransaction {
3321+ }
3322+ }
3323+
3324+-internal class AuthCodeEntryPane : Spit.Publishing.DialogPane, GLib.Object {
3325+- private Gtk.Box pane_widget = null;
3326+- private Gtk.Button continue_button = null;
3327+- private Gtk.Entry entry = null;
3328+- private Gtk.Label entry_caption = null;
3329+- private Gtk.Label explanatory_text = null;
3330+-
3331+- public signal void proceed(AuthCodeEntryPane sender, string authorization_code);
3332+-
3333+- public AuthCodeEntryPane(Gtk.Builder builder) {
3334+- assert(builder != null);
3335+- assert(builder.get_objects().length() > 0);
3336+-
3337+- explanatory_text = builder.get_object("explanatory_text") as Gtk.Label;
3338+- entry_caption = builder.get_object("entry_caption") as Gtk.Label;
3339+- entry = builder.get_object("entry") as Gtk.Entry;
3340+- continue_button = builder.get_object("continue_button") as Gtk.Button;
3341+-
3342+- pane_widget = builder.get_object("pane_widget") as Gtk.Box;
3343+-
3344+- pane_widget.show_all();
3345+-
3346+- on_entry_contents_changed();
3347+- }
3348+-
3349+- private void on_continue_clicked() {
3350+- proceed(this, entry.get_text());
3351+- }
3352+-
3353+- private void on_entry_contents_changed() {
3354+- continue_button.set_sensitive(entry.text_length > 0);
3355+- }
3356+-
3357+- public Gtk.Widget get_widget() {
3358+- return pane_widget;
3359+- }
3360+-
3361+- public Spit.Publishing.DialogPane.GeometryOptions get_preferred_geometry() {
3362+- return Spit.Publishing.DialogPane.GeometryOptions.NONE;
3363+- }
3364+-
3365+- public void on_pane_installed() {
3366+- continue_button.clicked.connect(on_continue_clicked);
3367+- entry.changed.connect(on_entry_contents_changed);
3368+- }
3369+-
3370+- public void on_pane_uninstalled() {
3371+- continue_button.clicked.disconnect(on_continue_clicked);
3372+- entry.changed.disconnect(on_entry_contents_changed);
3373+- }
3374+-}
3375+-
3376+-internal class NotSetUpMessagePane : Spit.Publishing.DialogPane, GLib.Object {
3377+- private Gtk.Box pane_widget = null;
3378+- private Gtk.Button continue_button = null;
3379+-
3380+- public signal void proceed(NotSetUpMessagePane sender);
3381+-
3382+- public NotSetUpMessagePane(Gtk.Builder builder) {
3383+- assert(builder != null);
3384+- assert(builder.get_objects().length() > 0);
3385+-
3386+- continue_button = builder.get_object("continue_button") as Gtk.Button;
3387+- pane_widget = builder.get_object("pane_widget") as Gtk.Box;
3388+-
3389+- pane_widget.show_all();
3390+- }
3391+-
3392+- private void on_continue_clicked() {
3393+- proceed(this);
3394+- }
3395+-
3396+- public Gtk.Widget get_widget() {
3397+- return pane_widget;
3398+- }
3399+-
3400+- public Spit.Publishing.DialogPane.GeometryOptions get_preferred_geometry() {
3401+- return Spit.Publishing.DialogPane.GeometryOptions.NONE;
3402+- }
3403+-
3404+- public void on_pane_installed() {
3405+- continue_button.clicked.connect(on_continue_clicked);
3406+- }
3407+-
3408+- public void on_pane_uninstalled() {
3409+- continue_button.clicked.disconnect(on_continue_clicked);
3410+- }
3411+-}
3412+-
3413+ internal class PublishingOptionsPane : Spit.Publishing.DialogPane, GLib.Object {
3414+ private class SizeDescription {
3415+ public string name;
3416+@@ -1097,7 +707,6 @@ internal class PublishingOptionsPane : Spit.Publishing.DialogPane, GLib.Object {
3417+ private Gtk.ComboBoxText size_combo = null;
3418+ private Gtk.CheckButton strip_metadata_check = null;
3419+ private Gtk.Button publish_button = null;
3420+- private Gtk.Button logout_button = null;
3421+
3422+ private Album[] albums;
3423+ private SizeDescription[] size_descriptions;
3424+@@ -1105,7 +714,6 @@ internal class PublishingOptionsPane : Spit.Publishing.DialogPane, GLib.Object {
3425+ private weak Spit.Publishing.PluginHost host;
3426+
3427+ public signal void publish(PublishingParameters parameters, bool strip_metadata);
3428+- public signal void logout();
3429+
3430+ public PublishingOptionsPane(Spit.Publishing.PluginHost host, string username,
3431+ Album[] albums, Spit.Publishing.Publisher.MediaType media_type, Gtk.Builder builder,
3432+@@ -1131,7 +739,6 @@ internal class PublishingOptionsPane : Spit.Publishing.DialogPane, GLib.Object {
3433+ size_combo = (Gtk.ComboBoxText) builder.get_object("size_combo");
3434+ strip_metadata_check = (Gtk.CheckButton) this.builder.get_object("strip_metadata_check");
3435+ publish_button = (Gtk.Button) builder.get_object("publish_button");
3436+- logout_button = (Gtk.Button) builder.get_object("logout_button");
3437+
3438+ // populate any widgets whose contents are programmatically-generated.
3439+ login_identity_label.set_label(_("You are logged into Picasa Web Albums as %s.").printf(username));
3440+@@ -1157,7 +764,6 @@ internal class PublishingOptionsPane : Spit.Publishing.DialogPane, GLib.Object {
3441+ use_existing_radio.clicked.connect(on_use_existing_radio_clicked);
3442+ create_new_radio.clicked.connect(on_create_new_radio_clicked);
3443+ new_album_entry.changed.connect(on_new_album_entry_changed);
3444+- logout_button.clicked.connect(on_logout_clicked);
3445+ publish_button.clicked.connect(on_publish_clicked);
3446+ }
3447+
3448+@@ -1200,10 +806,6 @@ internal class PublishingOptionsPane : Spit.Publishing.DialogPane, GLib.Object {
3449+ public_check.set_sensitive(true);
3450+ }
3451+
3452+- private void on_logout_clicked() {
3453+- logout();
3454+- }
3455+-
3456+ private void update_publish_button_sensitivity() {
3457+ string album_name = new_album_entry.get_text();
3458+ publish_button.set_sensitive(!(album_name.strip() == "" &&
3459+@@ -1263,10 +865,6 @@ internal class PublishingOptionsPane : Spit.Publishing.DialogPane, GLib.Object {
3460+ publish(parameters, strip_metadata_check.get_active());
3461+ }
3462+
3463+- protected void notify_logout() {
3464+- logout();
3465+- }
3466+-
3467+ public Gtk.Widget get_widget() {
3468+ return pane_widget;
3469+ }
3470+@@ -1279,12 +877,10 @@ internal class PublishingOptionsPane : Spit.Publishing.DialogPane, GLib.Object {
3471+ installed();
3472+
3473+ publish.connect(notify_publish);
3474+- logout.connect(notify_logout);
3475+ }
3476+
3477+ public void on_pane_uninstalled() {
3478+ publish.disconnect(notify_publish);
3479+- logout.disconnect(notify_logout);
3480+ }
3481+ }
3482+
3483+diff --git a/plugins/shotwell-publishing/PiwigoPublishing.vala b/plugins/shotwell-publishing/PiwigoPublishing.vala
3484+index 772536c..0c96d84 100644
3485+--- a/plugins/shotwell-publishing/PiwigoPublishing.vala
3486++++ b/plugins/shotwell-publishing/PiwigoPublishing.vala
3487+@@ -42,7 +42,7 @@ public class PiwigoService : Object, Spit.Pluggable, Spit.Publishing.Service {
3488+ public void activation(bool enabled) {
3489+ }
3490+
3491+- public Spit.Publishing.Publisher create_publisher(Spit.Publishing.PluginHost host) {
3492++ public Spit.Publishing.Publisher create_publisher(string? account, Spit.Publishing.PluginHost host) {
3493+ return new Publishing.Piwigo.PiwigoPublisher(this, host);
3494+ }
3495+
3496+diff --git a/plugins/shotwell-publishing/YouTubePublishing.vala b/plugins/shotwell-publishing/YouTubePublishing.vala
3497+index 9319eb9..fc03fe7 100644
3498+--- a/plugins/shotwell-publishing/YouTubePublishing.vala
3499++++ b/plugins/shotwell-publishing/YouTubePublishing.vala
3500+@@ -39,7 +39,7 @@ public class YouTubeService : Object, Spit.Pluggable, Spit.Publishing.Service {
3501+ info.icons = icon_pixbuf_set;
3502+ }
3503+
3504+- public Spit.Publishing.Publisher create_publisher(Spit.Publishing.PluginHost host) {
3505++ public Spit.Publishing.Publisher create_publisher(string? account, Spit.Publishing.PluginHost host) {
3506+ return new Publishing.YouTube.YouTubePublisher(this, host);
3507+ }
3508+
3509+diff --git a/plugins/shotwell-publishing/facebook_publishing_options_pane.glade b/plugins/shotwell-publishing/facebook_publishing_options_pane.glade
3510+index 0ba57ad..567b8c8 100644
3511+--- a/plugins/shotwell-publishing/facebook_publishing_options_pane.glade
3512++++ b/plugins/shotwell-publishing/facebook_publishing_options_pane.glade
3513+@@ -196,29 +196,14 @@ anything put into this field won't display)</property>
3514+ <object class="GtkBox" id="box2">
3515+ <property name="visible">True</property>
3516+ <property name="can_focus">False</property>
3517++ <property name="halign">center</property>
3518+ <property name="spacing">32</property>
3519+ <property name="homogeneous">True</property>
3520+ <child>
3521+- <object class="GtkButton" id="logout_button">
3522+- <property name="label" translatable="yes">_Logout</property>
3523+- <property name="use_action_appearance">False</property>
3524+- <property name="visible">True</property>
3525+- <property name="can_focus">True</property>
3526+- <property name="receives_default">True</property>
3527+- <property name="use_action_appearance">False</property>
3528+- <property name="use_underline">True</property>
3529+- </object>
3530+- <packing>
3531+- <property name="expand">False</property>
3532+- <property name="fill">True</property>
3533+- <property name="padding">80</property>
3534+- <property name="position">0</property>
3535+- </packing>
3536+- </child>
3537+- <child>
3538+ <object class="GtkButton" id="publish_button">
3539+ <property name="label" translatable="yes">_Publish</property>
3540+ <property name="use_action_appearance">False</property>
3541++ <property name="width_request">96</property>
3542+ <property name="visible">True</property>
3543+ <property name="can_focus">True</property>
3544+ <property name="receives_default">True</property>
3545+@@ -229,13 +214,13 @@ anything put into this field won't display)</property>
3546+ <property name="expand">False</property>
3547+ <property name="fill">True</property>
3548+ <property name="padding">80</property>
3549+- <property name="position">1</property>
3550++ <property name="position">0</property>
3551+ </packing>
3552+ </child>
3553+ </object>
3554+ <packing>
3555+ <property name="expand">False</property>
3556+- <property name="fill">True</property>
3557++ <property name="fill">False</property>
3558+ <property name="padding">2</property>
3559+ <property name="position">3</property>
3560+ </packing>
3561+diff --git a/plugins/shotwell-publishing/flickr_publishing_options_pane.glade b/plugins/shotwell-publishing/flickr_publishing_options_pane.glade
3562+index e39a52e..b9edd04 100644
3563+--- a/plugins/shotwell-publishing/flickr_publishing_options_pane.glade
3564++++ b/plugins/shotwell-publishing/flickr_publishing_options_pane.glade
3565+@@ -135,24 +135,6 @@ so changes made here will not display)</property>
3566+ <property name="spacing">64</property>
3567+ <property name="homogeneous">True</property>
3568+ <child>
3569+- <object class="GtkButton" id="logout_button">
3570+- <property name="label" translatable="yes">_Logout</property>
3571+- <property name="use_action_appearance">False</property>
3572+- <property name="width_request">96</property>
3573+- <property name="visible">True</property>
3574+- <property name="can_focus">True</property>
3575+- <property name="receives_default">True</property>
3576+- <property name="use_action_appearance">False</property>
3577+- <property name="use_underline">True</property>
3578+- </object>
3579+- <packing>
3580+- <property name="expand">False</property>
3581+- <property name="fill">True</property>
3582+- <property name="padding">24</property>
3583+- <property name="position">0</property>
3584+- </packing>
3585+- </child>
3586+- <child>
3587+ <object class="GtkButton" id="publish_button">
3588+ <property name="label" translatable="yes">_Publish</property>
3589+ <property name="use_action_appearance">False</property>
3590+@@ -167,7 +149,7 @@ so changes made here will not display)</property>
3591+ <property name="expand">False</property>
3592+ <property name="fill">True</property>
3593+ <property name="padding">24</property>
3594+- <property name="position">1</property>
3595++ <property name="position">0</property>
3596+ </packing>
3597+ </child>
3598+ </object>
3599+diff --git a/plugins/shotwell-publishing/picasa_publishing_options_pane.glade b/plugins/shotwell-publishing/picasa_publishing_options_pane.glade
3600+index 07af67d..4d2cfb4 100644
3601+--- a/plugins/shotwell-publishing/picasa_publishing_options_pane.glade
3602++++ b/plugins/shotwell-publishing/picasa_publishing_options_pane.glade
3603+@@ -226,32 +226,16 @@
3604+ <object class="GtkBox" id="button_area_box">
3605+ <property name="visible">True</property>
3606+ <property name="can_focus">False</property>
3607+- <property name="margin_left">112</property>
3608+- <property name="margin_right">112</property>
3609++ <property name="halign">center</property>
3610+ <property name="margin_top">48</property>
3611+ <property name="margin_bottom">24</property>
3612+ <property name="spacing">128</property>
3613+ <property name="homogeneous">True</property>
3614+ <child>
3615+- <object class="GtkButton" id="logout_button">
3616+- <property name="label" translatable="yes">_Logout</property>
3617+- <property name="use_action_appearance">False</property>
3618+- <property name="visible">True</property>
3619+- <property name="can_focus">True</property>
3620+- <property name="receives_default">True</property>
3621+- <property name="use_action_appearance">False</property>
3622+- <property name="use_underline">True</property>
3623+- </object>
3624+- <packing>
3625+- <property name="expand">False</property>
3626+- <property name="fill">True</property>
3627+- <property name="position">0</property>
3628+- </packing>
3629+- </child>
3630+- <child>
3631+ <object class="GtkButton" id="publish_button">
3632+ <property name="label" translatable="yes">_Publish</property>
3633+ <property name="use_action_appearance">False</property>
3634++ <property name="width_request">96</property>
3635+ <property name="visible">True</property>
3636+ <property name="can_focus">True</property>
3637+ <property name="receives_default">True</property>
3638+@@ -261,13 +245,13 @@
3639+ <packing>
3640+ <property name="expand">False</property>
3641+ <property name="fill">True</property>
3642+- <property name="position">1</property>
3643++ <property name="position">0</property>
3644+ </packing>
3645+ </child>
3646+ </object>
3647+ <packing>
3648+ <property name="expand">False</property>
3649+- <property name="fill">True</property>
3650++ <property name="fill">False</property>
3651+ <property name="position">4</property>
3652+ </packing>
3653+ </child>
3654+diff --git a/src/plugins/PublishingInterfaces.vala b/src/plugins/PublishingInterfaces.vala
3655+index 339cc9d..4ed2bec 100644
3656+--- a/src/plugins/PublishingInterfaces.vala
3657++++ b/src/plugins/PublishingInterfaces.vala
3658+@@ -494,6 +494,7 @@ public interface PluginHost : GLib.Object, Spit.HostInterface {
3659+ */
3660+ public abstract Spit.Publishing.Publisher.MediaType get_publishable_media_type();
3661+
3662++ public abstract ulong get_dialog_xid();
3663+ //
3664+ // For future expansion.
3665+ //
3666+@@ -579,18 +580,26 @@ public interface Service : Object, Spit.Pluggable {
3667+ * A factory method that instantiates and returns a new {@link Publisher} object that
3668+ * encapsulates a connection to the remote publishing service that this Service describes.
3669+ */
3670+- public abstract Spit.Publishing.Publisher create_publisher(Spit.Publishing.PluginHost host);
3671++ public abstract Spit.Publishing.Publisher create_publisher(string? account, Spit.Publishing.PluginHost host);
3672+
3673+ /**
3674+ * Returns the kinds of media that this service can work with.
3675+ */
3676+ public abstract Spit.Publishing.Publisher.MediaType get_supported_media();
3677+
3678++ /**
3679++ * Checks whether the service is enabled.
3680++ */
3681++ public virtual bool is_enabled() { return true; }
3682++
3683++ /**
3684++ * List the accounts available for this service.
3685++ */
3686++ public virtual string[] list_account_names() { return {}; }
3687++
3688+ //
3689+ // For future expansion.
3690+ //
3691+- protected virtual void reserved0() {}
3692+- protected virtual void reserved1() {}
3693+ protected virtual void reserved2() {}
3694+ protected virtual void reserved3() {}
3695+ protected virtual void reserved4() {}
3696+diff --git a/src/publishing/PublishingPluginHost.vala b/src/publishing/PublishingPluginHost.vala
3697+index 7be7bed..c0417f8 100644
3698+--- a/src/publishing/PublishingPluginHost.vala
3699++++ b/src/publishing/PublishingPluginHost.vala
3700+@@ -21,7 +21,8 @@ public class ConcretePublishingHost : Plugins.StandardHostInterface,
3701+ private Spit.Publishing.Publisher.MediaType media_type =
3702+ Spit.Publishing.Publisher.MediaType.NONE;
3703+
3704+- public ConcretePublishingHost(Service service, PublishingUI.PublishingDialog dialog,
3705++ public ConcretePublishingHost(Service service, string? account,
3706++ PublishingUI.PublishingDialog dialog,
3707+ Publishable[] publishables) {
3708+ base(service, "sharing");
3709+ this.dialog = dialog;
3710+@@ -30,7 +31,7 @@ public class ConcretePublishingHost : Plugins.StandardHostInterface,
3711+ foreach (Publishable curr_publishable in publishables)
3712+ this.media_type |= curr_publishable.get_media_type();
3713+
3714+- this.active_publisher = service.create_publisher(this);
3715++ this.active_publisher = service.create_publisher(account, this);
3716+ }
3717+
3718+ private void on_login_clicked() {
3719+@@ -232,6 +233,13 @@ public class ConcretePublishingHost : Plugins.StandardHostInterface,
3720+
3721+ return report_plugin_upload_progress;
3722+ }
3723++
3724++ public ulong get_dialog_xid() {
3725++ if (dialog == null) return 0;
3726++
3727++ Gdk.Window window = dialog.get_window();
3728++ return Gdk.X11Window.get_xid(window);
3729++ }
3730+ }
3731+
3732+ }
3733+diff --git a/src/publishing/PublishingUI.vala b/src/publishing/PublishingUI.vala
3734+index b7f7f75..37f2713 100644
3735+--- a/src/publishing/PublishingUI.vala
3736++++ b/src/publishing/PublishingUI.vala
3737+@@ -130,6 +130,11 @@ public class LoginWaitPane : StaticMessagePane {
3738+ }
3739+
3740+ public class PublishingDialog : Gtk.Dialog {
3741++ private struct AccountService {
3742++ Spit.Publishing.Service service;
3743++ string? account;
3744++ }
3745++
3746+ private const int LARGE_WINDOW_WIDTH = 860;
3747+ private const int LARGE_WINDOW_HEIGHT = 688;
3748+ private const int COLOSSAL_WINDOW_WIDTH = 1024;
3749+@@ -151,6 +156,8 @@ public class PublishingDialog : Gtk.Dialog {
3750+ private Spit.Publishing.DialogPane active_pane;
3751+ private Spit.Publishing.Publishable[] publishables;
3752+ private Spit.Publishing.ConcretePublishingHost host;
3753++ private Gee.HashMap<string,AccountService?> services;
3754++ private int service_selector_add_accounts_index;
3755+
3756+ protected PublishingDialog(Gee.Collection<MediaSource> to_publish) {
3757+ assert(to_publish.size > 0);
3758+@@ -158,6 +165,8 @@ public class PublishingDialog : Gtk.Dialog {
3759+ resizable = false;
3760+ delete_event.connect(on_window_close);
3761+
3762++ services = new Gee.HashMap<string,AccountService?>();
3763++
3764+ publishables = new Spit.Publishing.Publishable[0];
3765+ bool has_photos = false;
3766+ bool has_videos = false;
3767+@@ -206,9 +215,28 @@ public class PublishingDialog : Gtk.Dialog {
3768+ if (last_used_service != null && last_used_service == curr_service_id)
3769+ last_used_index = ticker;
3770+
3771+- service_selector_box.append_text(service.get_pluggable_name());
3772+- ticker++;
3773++ var account_names = service.list_account_names();
3774++ if (account_names.length == 0) {
3775++ account_names += null;
3776++ }
3777++
3778++ foreach (string? account_name in account_names) {
3779++ string service_name = service.get_pluggable_name();
3780++ if (account_name != null) {
3781++ service_name += " (%s)".printf(account_name);
3782++ }
3783++
3784++ AccountService account_service = {
3785++ service: service,
3786++ account: account_name
3787++ };
3788++ services.set(service_name, account_service);
3789++ service_selector_box.append_text(service_name);
3790++ ticker++;
3791++ }
3792+ }
3793++ service_selector_box.append_text(_("Add more accounts…"));
3794++ service_selector_add_accounts_index = ticker;
3795+ if (last_used_index >= 0)
3796+ service_selector_box.set_active(last_used_index);
3797+ else
3798+@@ -296,6 +324,7 @@ public class PublishingDialog : Gtk.Dialog {
3799+ Spit.Publishing.Service[] all_services = load_all_services();
3800+
3801+ foreach (Spit.Publishing.Service service in all_services) {
3802++ if (!service.is_enabled()) continue;
3803+
3804+ if (has_photos && !has_videos) {
3805+ if ((service.get_supported_media() & Spit.Publishing.Publisher.MediaType.PHOTO) != 0)
3806+@@ -384,21 +413,34 @@ public class PublishingDialog : Gtk.Dialog {
3807+ }
3808+
3809+ private void on_service_changed() {
3810+- string service_name = service_selector_box.get_active_text();
3811+-
3812+- Spit.Publishing.Service? selected_service = null;
3813+- Spit.Publishing.Service[] services = load_all_services();
3814+- foreach (Spit.Publishing.Service service in services) {
3815+- if (service.get_pluggable_name() == service_name) {
3816+- selected_service = service;
3817+- break;
3818++ if (service_selector_box.get_active() ==
3819++ service_selector_add_accounts_index) {
3820++ debug("Starting Online Accounts...");
3821++ try
3822++ {
3823++ DesktopAppInfo app_info =
3824++ new DesktopAppInfo ("gnome-credentials-panel.desktop");
3825++ GLib.Process.spawn_command_line_async(app_info.get_commandline() + " application=shotwell");
3826++ }
3827++ catch (Error e)
3828++ {
3829++ warning ("Error launching Online Accounts: %s", e.message);
3830+ }
3831++
3832++ on_close_cancel_clicked();
3833++ return;
3834+ }
3835+- assert(selected_service != null);
3836++
3837++ string service_name = service_selector_box.get_active_text();
3838++
3839++ AccountService account_service = services.get(service_name);
3840++ Spit.Publishing.Service? selected_service = account_service.service;
3841+
3842+ Config.Facade.get_instance().set_last_used_service(selected_service.get_id());
3843+
3844+- host = new Spit.Publishing.ConcretePublishingHost(selected_service, this, publishables);
3845++ host = new Spit.Publishing.ConcretePublishingHost(selected_service,
3846++ account_service.account,
3847++ this, publishables);
3848+ host.start_publishing();
3849+ }
3850+
3851
3852=== added file 'debian/patches/series'
3853--- debian/patches/series 1970-01-01 00:00:00 +0000
3854+++ debian/patches/series 2012-09-17 10:22:22 +0000
3855@@ -0,0 +1,6 @@
3856+02_desktop_translations.patch
3857+03_appmenu_no_stubs.patch
3858+04_no_resize_grip.patch
3859+05-gomp-linking.patch
3860+
3861+06_uoa.patch
3862
3863=== added file 'debian/rules'
3864--- debian/rules 1970-01-01 00:00:00 +0000
3865+++ debian/rules 2012-09-17 10:22:22 +0000
3866@@ -0,0 +1,10 @@
3867+#!/usr/bin/make -f
3868+
3869+%:
3870+ dh $@ --with-scour
3871+
3872+override_dh_auto_configure:
3873+ dh_auto_configure -- --disable-schemas-compile --disable-icon-update --unity-support
3874+
3875+override_dh_installchangelogs:
3876+ dh_installchangelogs NEWS
3877
3878=== added file 'debian/shotwell-video-thumbnailer.1'
3879--- debian/shotwell-video-thumbnailer.1 1970-01-01 00:00:00 +0000
3880+++ debian/shotwell-video-thumbnailer.1 2012-09-17 10:22:22 +0000
3881@@ -0,0 +1,13 @@
3882+.TH shotwell-video-thumbnailer 1 "August 21, 2011"
3883+.SH NAME
3884+shotwell-video-thumbnailer \- writes video thumbnail to stdout
3885+.SH DESCRIPTION
3886+Writes video thumbnail to stdout.
3887+.PP
3888+This program is designed to be called by shotwell directly, normally it is not
3889+of any use for shotwell users.
3890+.SH COPYRIGHT
3891+This manual page is Copyright 2011 Luca Falavigna <dktrkranz@debian.org>.
3892+Permission is granted to copy, distribute and/or modify this document
3893+under the terms of the GNU Lesser General Public License, Version 2.1 or any
3894+later version published by the Free Software Foundation.
3895
3896=== added file 'debian/shotwell.1'
3897--- debian/shotwell.1 1970-01-01 00:00:00 +0000
3898+++ debian/shotwell.1 2012-09-17 10:22:22 +0000
3899@@ -0,0 +1,14 @@
3900+.TH shotwell 1 "December 30, 2009"
3901+.SH NAME
3902+shotwell \- a digital photo organizer
3903+.SH DESCRIPTION
3904+.B shotwell
3905+is a digital photo organizer designed for the GNOME desktop environment. It
3906+allows you to import photos from disk or camera, organize them in various ways,
3907+view them in full-window or fullscreen mode, and export them to share with
3908+others.
3909+.SH AUTHOR
3910+shotwell was written by Jim Nelson, Lucas Beeler and Allison Barlow.
3911+.PP
3912+This manual page was written by Devid Antonio Filoni <d.filoni@ubuntu.com>,
3913+for the Debian project (and may be used by others).
3914
3915=== added file 'debian/shotwell.lintian-overrides'
3916--- debian/shotwell.lintian-overrides 1970-01-01 00:00:00 +0000
3917+++ debian/shotwell.lintian-overrides 2012-09-17 10:22:22 +0000
3918@@ -0,0 +1,2 @@
3919+library-not-linked-against-libc
3920+image-file-in-usr-lib
3921
3922=== added directory 'debian/source'
3923=== added file 'debian/source/format'
3924--- debian/source/format 1970-01-01 00:00:00 +0000
3925+++ debian/source/format 2012-09-17 10:22:22 +0000
3926@@ -0,0 +1,1 @@
3927+3.0 (quilt)
3928
3929=== added file 'debian/watch'
3930--- debian/watch 1970-01-01 00:00:00 +0000
3931+++ debian/watch 2012-09-17 10:22:22 +0000
3932@@ -0,0 +1,3 @@
3933+version=3
3934+opts=dversionmangle=s/\+dfsg// \
3935+ http://yorba.org/download/shotwell/([\d\.]+)/unstable/shotwell-(.*)\.tar\.xz

Subscribers

People subscribed via source and target branches