Merge lp:~azzar1/software-properties/add-canonical-livepatch into lp:software-properties

Proposed by Andrea Azzarone
Status: Merged
Merged at revision: 1011
Proposed branch: lp:~azzar1/software-properties/add-canonical-livepatch
Merge into: lp:software-properties
Diff against target: 2008 lines (+1176/-122)
13 files modified
data/gtkbuilder/dialog-auth.ui (+114/-0)
data/gtkbuilder/dialog-livepatch-error.ui (+58/-0)
data/gtkbuilder/main.ui (+90/-21)
debian/changelog (+14/-0)
debian/control (+4/-2)
po/POTFILES.in (+2/-1)
po/software-properties.pot (+166/-89)
softwareproperties/GoaAuth.py (+113/-0)
softwareproperties/SoftwareProperties.py (+153/-1)
softwareproperties/dbus/SoftwarePropertiesDBus.py (+11/-0)
softwareproperties/gtk/DialogAuth.py (+228/-0)
softwareproperties/gtk/DialogLivepatchError.py (+57/-0)
softwareproperties/gtk/SoftwarePropertiesGtk.py (+166/-8)
To merge this branch: bzr merge lp:~azzar1/software-properties/add-canonical-livepatch
Reviewer Review Type Date Requested Status
Didier Roche-Tolomelli Approve
Review via email: mp+341427@code.launchpad.net

Commit message

Add a "Enable Canonical Livepatch..." switch to software-properties-gtk. This will retrieve the livepatch auth token using gnome-online-accounts.

Description of the change

Add a "Enable Canonical Livepatch..." switch to software-properties-gtk. This will retrieve the livepatch auth token using gnome-online-accounts. The login workflow will work slightly better once this https://bugs.launchpad.net/ubuntu/+source/gnome-control-center/+bug/1754651/ gets fixed. For futher rleases we might want to speak with upstream to get a better gnome-online-accout API to improve the login workflow client-side.

To post a comment you must log in.
Revision history for this message
Andrea Azzarone (azzar1) wrote :

At the moment bionic in the stable canonical-livepatch snap. We might want to install the edge snap for the moment or:
1) let the enabling fail
2) hide the ui

Revision history for this message
Brian Murray (brian-murray) wrote :

I'm not sure I'm the best person to evaluate all the gtk code so I've identified some other things mostly nit-picks but I do think using python3-distro-info would quite a good improvement.

Revision history for this message
Brian Murray (brian-murray) wrote :

This may also require a Feature Freeze Exception given where we are in the release cycle.

Revision history for this message
Didier Roche-Tolomelli (didrocks) wrote :

Good work! I'm having some remarks in addition to Brian's comment, please see them inline.

My major concern is error handling, which is using strings (empty strings or none) to tell that no error happened instead of using python exceptions. In some cases (see my inline reviews), it means that you even don't make difference between legit "no error executing this command" and "error without any output of the command", which is problematic IMHO.

The rest is majoritaly some syntax suggestions to make the code more readable and pythonish, not all required, but some changes may be beneficials IMHO.

Also:
* You should refresh the translation template file in po/software-properties.pot and check that every added strings is indeed in it.
* Now that seb opened a FFe bug, refer to it in debian/changelog

Note that I didn't test it yet, will do once the changes are committed here.

Revision history for this message
Didier Roche-Tolomelli (didrocks) :
review: Needs Fixing
Revision history for this message
Andrea Azzarone (azzar1) :
Revision history for this message
Andrea Azzarone (azzar1) wrote :

I pushed the fixes. Regarding class decorators I tried to use them but they fails to properly pass around the 'self' argument.

Revision history for this message
Didier Roche-Tolomelli (didrocks) wrote :

Nice work!

I guess there are still some things to fix before getting it mergeable. I'll post the 3 followups here (in the previous commit, as answers) and there is one on the new commit (incoming).

Once those fixed, I'll do a test run ;)

Revision history for this message
Didier Roche-Tolomelli (didrocks) wrote :

And here are the small comments on the new commit :)

Revision history for this message
Andrea Azzarone (azzar1) wrote :

Two comments on the old commit.

1043. By Andrea Azzarone

Make title translatable.

1044. By Andrea Azzarone

Simplify the calls to SetLivepatchEnabled.

Revision history for this message
Andrea Azzarone (azzar1) wrote :

Pushed!

1045. By Andrea Azzarone

Refresh line numbers in po/software-properties.pot.

Revision history for this message
Didier Roche-Tolomelli (didrocks) wrote :

+1 on old diff changes

1046. By Andrea Azzarone

Set enabled to False by default.

Revision history for this message
Didier Roche-Tolomelli (didrocks) wrote :

As discussed on IRC, thanks for the followup fix (yeah peer review, isn't it? ;)).

So, it's +1 from my side, just need some proper manual testing (as you can see, the typo was easily made :p).
Good work!

Revision history for this message
Didier Roche-Tolomelli (didrocks) wrote :

Ok, trying it now:
- layout looks weird here (https://imgur.com/a/qWpRD)
- is https://imgur.com/a/RZfch the intended layout? There is a button "sign in" which doesn't look like a button, and a COG menu. Both are launching g-c-c in UOA panel, I find that confusing. Can you check with design?
- once I end up in UAO, I don't really know what to do, I have 2 accounts (my personal Google one and my canonical one) which are already subscribed… Clicking on them doesn't mention livepatch or anything at all, what should I do?

review: Needs Information
Revision history for this message
Didier Roche-Tolomelli (didrocks) wrote :

For reference, here is on what I end up clicking on "sign in" or cog menu: https://imgur.com/a/y3JdU.
My canonical account is a SSO one.

After some debugging, it seems we need the g-o-a version (and daemon restart) from -proposed. So, a versioned dep in debian/control should be added.

I was then able to sign in. Note that (as known), once you put the email/password, it asks for the admin password, then *twice* the passcode for SSO (Note that it doesn't empty the field when asking for the second passcode, which it should, IMHO?) and then another sudo password for installing packages.

Then, sign in in first dialog is getting the correct status. However, after some seconds, I get an excl error dialog with Settings/Ignore. Clicking on Settings do nothing. Once closing that dialog the checked "Use Canonical Livepatch…" checkbox is unchecked. Andrea says it's due to livepatch. I can't go further in that direction thus.

Clicking sign out/sign in again, I see my SSO account in the list of "Use another account" selection, which the same "strange" styling.
Clicking on use another popups the dialog as before, clicking on mly sso account sent me back to previous state, signed in, as expected.

Note that even after restart g-o-a, I got multiple times this traceback:
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/softwareproperties/gtk/DialogAuth.py", line 199, in
_listbox_accounts_row_activated_cb
    self._spawn_goa_with_args('add', 'ubuntusso')
  File "/usr/lib/python3/dist-packages/softwareproperties/gtk/DialogAuth.py", line 138, in
_spawn_goa_with_args
    proxy.call_sync('Activate', param, Gio.DBusCallFlags.NONE, -1, None)
GLib.Error: g-io-error-quark: Le délai d’attente est dépassé (24)

Anyway, there are some fixes needed, marking as such. We can then see if we upload that, but I want some design reviews or we'll need an UIFe anyway for those changes.

review: Needs Fixing
1047. By Andrea Azzarone

Remove settings button.

1048. By Andrea Azzarone

Don't show the "Settings..." button if software-properties-window has not been closed.

1049. By Andrea Azzarone

Wrap dbus in try/except and show warning instead of crash.

Revision history for this message
Andrea Azzarone (azzar1) wrote :

> For reference, here is on what I end up clicking on "sign in" or cog menu:
> https://imgur.com/a/y3JdU.
> My canonical account is a SSO one.
>
> After some debugging, it seems we need the g-o-a version (and daemon restart)
> from -proposed. So, a versioned dep in debian/control should be added.

Fixed.

>
> I was then able to sign in. Note that (as known), once you put the
> email/password, it asks for the admin password, then *twice* the passcode for
> SSO (Note that it doesn't empty the field when asking for the second passcode,
> which it should, IMHO?) and then another sudo password for installing
> packages.

Proposed a fix here: https://bugs.launchpad.net/ubuntu/+source/gnome-online-accounts/+bug/1757444/comments/1

>
> Then, sign in in first dialog is getting the correct status. However, after
> some seconds, I get an excl error dialog with Settings/Ignore. Clicking on
> Settings do nothing. Once closing that dialog the checked "Use Canonical
> Livepatch…" checkbox is unchecked. Andrea says it's due to livepatch. I can't
> go further in that direction thus.

This is likely the bug we're getting: https://bugs.launchpad.net/ubuntu/+source/snapd/+bug/1756793

>
> Clicking sign out/sign in again, I see my SSO account in the list of "Use
> another account" selection, which the same "strange" styling.
> Clicking on use another popups the dialog as before, clicking on mly sso
> account sent me back to previous state, signed in, as expected.
>
> Note that even after restart g-o-a, I got multiple times this traceback:
> Traceback (most recent call last):
> File "/usr/lib/python3/dist-packages/softwareproperties/gtk/DialogAuth.py",
> line 199, in
> _listbox_accounts_row_activated_cb
> self.('add', 'ubuntusso')
> File "/usr/lib/python3/dist-packages/softwareproperties/gtk/DialogAuth.py",
> line 138, in
> _spawn_goa_with_args
> proxy.call_sync('Activate', param, Gio.DBusCallFlags.NONE, -1, None)
> GLib.Error: g-io-error-quark: Le délai d’attente est dépassé (24)
>

I try/except wrapped the calls to "_spawn_goa_with_args". Now you should get an warning error instead of a traceback. Not sure why you're getting timeout error on an dbus sync call (a call that is not waiting for an answer).

>
> Anyway, there are some fixes needed, marking as such. We can then see if we
> upload that, but I want some design reviews or we'll need an UIFe anyway for
> those changes.

1050. By Andrea Azzarone

Bump dep for gir1.2-goa-1.0

1051. By Andrea Azzarone

Use a 10 minutes timeout and improve layout.

Revision history for this message
Andrea Azzarone (azzar1) wrote :

I improved a little bit the layout and added a 10 minutes layout on that call too.

Revision history for this message
Didier Roche-Tolomelli (didrocks) wrote :

Excellent work Andrea! I just had another test run, and things mostly LGTM, apart from the design issues we discussed and need a followup with design team + the g-o-a and livepatch known issues :)

For now, let's get that in to get wider testing.
Merging and sponsoring it for you

review: Needs Fixing
Revision history for this message
Didier Roche-Tolomelli (didrocks) wrote :

My chaosmonkey personal instance said "Needs fixing", but I say "Approve" :)

review: Approve
Revision history for this message
Matthew Paul Thomas (mpt) wrote :

> - layout looks weird here (https://imgur.com/a/qWpRD)

Well spotted Didier. This is the UI equivalent of technical debt: the current implementation lags almost five years behind the current design <https://wiki.ubuntu.com/SoftwareUpdates#settings>, and three of the ways it lags have combined to make this look bad:

(a) There are too many tabs, which makes the whole window wider, which makes the Livepatch options look much more “floaty” than they would be otherwise.

(b) “Install updates from:” is still checkboxes, rather than a menu, which makes it odd that the Livepatch checkbox isn’t lined up with those ones.

(c) All the menus are needlessly wide, so it’s not obvious whether/that the block of menus is centered in the tab, which makes the Livepatch controls appear to be the only block that is centered in the tab, which makes it look out of place.

Fixing (a) or (b) probably seems like far too much work, five weeks before release, just to make Livepatch options look less weird. (Though of course that’s not the only reason to fix them.) Fixing (c) might be practical here, since it’s just changing widths and alignments.

Another way to improve the layout temporarily would be to remove the indentation from the “Install updates from:” checkboxes (which, even if they were supposed to still exist, would not be supposed to be indented anyway!), and then left-align the whole Livepatch block. That way all the checkboxes in the tab would line up.

Separately from all of that:

(d) “To use Livepatch you need to sign in” should be indented from the checkbox, not centered independently — that is, the word “To” should line up exactly with “Use” immediately above.

(e) As shown in the wireframes, the “Sign Out” button should always be exactly the same position and width as the “Sign In…” button.

(f) Therefore, the status text block should be fixed width, regardless of whether the text is “To use Livepatch you need to sign in.”, “Signed in as <email address hidden>.”, or “…<email address hidden> isn’t authorised to use Livepatch.”.

Revision history for this message
Andrea Azzarone (azzar1) wrote :

With gtk is hard to implement (d). The problem is that it's not possible to align the label of the checkbox with the label below. One possible solution is to hard-code the paddings but this would break the layout for other themes.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'data/gtkbuilder/dialog-auth.ui'
2--- data/gtkbuilder/dialog-auth.ui 1970-01-01 00:00:00 +0000
3+++ data/gtkbuilder/dialog-auth.ui 2018-03-21 15:53:10 +0000
4@@ -0,0 +1,114 @@
5+<?xml version="1.0" encoding="UTF-8"?>
6+<!-- Generated with glade 3.20.4 -->
7+<interface>
8+ <requires lib="gtk+" version="3.10"/>
9+ <object class="GtkDialog" id="dialog_auth">
10+ <property name="can_focus">False</property>
11+ <property name="resizable">False</property>
12+ <property name="modal">True</property>
13+ <property name="destroy_with_parent">True</property>
14+ <property name="type_hint">dialog</property>
15+ <property name="deletable">False</property>
16+ <child internal-child="vbox">
17+ <object class="GtkBox" id="box_dialog">
18+ <property name="can_focus">False</property>
19+ <property name="orientation">vertical</property>
20+ <property name="spacing">2</property>
21+ <child internal-child="action_area">
22+ <object class="GtkButtonBox" id="dialog-action_area1">
23+ <property name="can_focus">False</property>
24+ </object>
25+ <packing>
26+ <property name="expand">False</property>
27+ <property name="fill">False</property>
28+ <property name="position">0</property>
29+ </packing>
30+ </child>
31+ <child>
32+ <object class="GtkGrid" id="main_grid">
33+ <property name="visible">True</property>
34+ <property name="can_focus">False</property>
35+ <property name="border_width">12</property>
36+ <property name="row_spacing">12</property>
37+ <property name="column_spacing">12</property>
38+ <child>
39+ <object class="GtkLabel" id="label_title">
40+ <property name="visible">True</property>
41+ <property name="can_focus">False</property>
42+ <property name="hexpand">True</property>
43+ <property name="label" translatable="yes">To enable Livepatch choose an Ubuntu Single Sign-on account.</property>
44+ <property name="wrap">True</property>
45+ <property name="xalign">0</property>
46+ </object>
47+ <packing>
48+ <property name="left_attach">0</property>
49+ <property name="top_attach">0</property>
50+ </packing>
51+ </child>
52+ <child>
53+ <object class="GtkFrame" id="main_frame">
54+ <property name="visible">True</property>
55+ <property name="can_focus">False</property>
56+ <property name="label_xalign">0</property>
57+ <child>
58+ <object class="GtkListBox" id="listbox_accounts">
59+ <property name="visible">True</property>
60+ <property name="can_focus">False</property>
61+ <property name="selection_mode">none</property>
62+ <child>
63+ <object class="GtkListBoxRow" id="listboxrow_new_account">
64+ <property name="visible">True</property>
65+ <property name="can_focus">False</property>
66+ <child>
67+ <object class="GtkLabel" id="label_new_account">
68+ <property name="height_request">48</property>
69+ <property name="visible">True</property>
70+ <property name="can_focus">False</property>
71+ <property name="halign">center</property>
72+ <property name="valign">center</property>
73+ <property name="label" translatable="yes">&lt;b&gt;Use another account...&lt;/b&gt;</property>
74+ <property name="use_markup">True</property>
75+ <property name="justify">center</property>
76+ </object>
77+ </child>
78+ </object>
79+ </child>
80+ </object>
81+ </child>
82+ </object>
83+ <packing>
84+ <property name="left_attach">0</property>
85+ <property name="top_attach">1</property>
86+ <property name="width">2</property>
87+ </packing>
88+ </child>
89+ </object>
90+ <packing>
91+ <property name="expand">False</property>
92+ <property name="fill">True</property>
93+ <property name="position">1</property>
94+ </packing>
95+ </child>
96+ </object>
97+ </child>
98+ <child type="titlebar">
99+ <object class="GtkHeaderBar">
100+ <property name="visible">True</property>
101+ <property name="can_focus">False</property>
102+ <property name="title" translatable="yes">Choose an account</property>
103+ <child>
104+ <object class="GtkButton" id="button_cancel">
105+ <property name="label">gtk-cancel</property>
106+ <property name="visible">True</property>
107+ <property name="can_focus">True</property>
108+ <property name="receives_default">True</property>
109+ <property name="use_stock">True</property>
110+ </object>
111+ </child>
112+ </object>
113+ </child>
114+ <action-widgets>
115+ <action-widget response="-6">button_cancel</action-widget>
116+ </action-widgets>
117+ </object>
118+</interface>
119
120=== added file 'data/gtkbuilder/dialog-livepatch-error.ui'
121--- data/gtkbuilder/dialog-livepatch-error.ui 1970-01-01 00:00:00 +0000
122+++ data/gtkbuilder/dialog-livepatch-error.ui 2018-03-21 15:53:10 +0000
123@@ -0,0 +1,58 @@
124+<?xml version="1.0" encoding="UTF-8"?>
125+<!-- Generated with glade 3.18.3 -->
126+<interface>
127+ <requires lib="gtk+" version="3.12"/>
128+ <object class="GtkMessageDialog" id="messagedialog_livepatch">
129+ <property name="can_focus">False</property>
130+ <property name="type_hint">dialog</property>
131+ <property name="message_type">error</property>
132+ <property name="text" translatable="yes">Sorry, there’s been a problem in setting up Canonical Livepatch.</property>
133+ <child internal-child="vbox">
134+ <object class="GtkBox" id="messagedialog-vbox1">
135+ <property name="can_focus">False</property>
136+ <property name="orientation">vertical</property>
137+ <property name="spacing">2</property>
138+ <child internal-child="action_area">
139+ <object class="GtkButtonBox" id="messagedialog-action_area1">
140+ <property name="can_focus">False</property>
141+ <property name="layout_style">end</property>
142+ <child>
143+ <object class="GtkButton" id="button_settings">
144+ <property name="label" translatable="yes">Settings…</property>
145+ <property name="visible">True</property>
146+ <property name="can_focus">True</property>
147+ <property name="receives_default">True</property>
148+ <signal name="clicked" handler="on_button_settings_clicked" swapped="no"/>
149+ </object>
150+ <packing>
151+ <property name="expand">True</property>
152+ <property name="fill">True</property>
153+ <property name="position">0</property>
154+ </packing>
155+ </child>
156+ <child>
157+ <object class="GtkButton" id="button_ignore">
158+ <property name="label" translatable="yes">Ignore</property>
159+ <property name="visible">True</property>
160+ <property name="can_focus">True</property>
161+ <property name="receives_default">True</property>
162+ <property name="yalign">0.51999998092651367</property>
163+ <signal name="clicked" handler="on_button_ignore_clicked" swapped="no"/>
164+ </object>
165+ <packing>
166+ <property name="expand">True</property>
167+ <property name="fill">True</property>
168+ <property name="position">1</property>
169+ </packing>
170+ </child>
171+ </object>
172+ <packing>
173+ <property name="expand">False</property>
174+ <property name="fill">False</property>
175+ <property name="position">0</property>
176+ </packing>
177+ </child>
178+ </object>
179+ </child>
180+ </object>
181+</interface>
182
183=== modified file 'data/gtkbuilder/main.ui'
184--- data/gtkbuilder/main.ui 2016-08-17 09:34:22 +0000
185+++ data/gtkbuilder/main.ui 2018-03-21 15:53:10 +0000
186@@ -1,6 +1,7 @@
187 <?xml version="1.0" encoding="UTF-8"?>
188+<!-- Generated with glade 3.18.3 -->
189 <interface>
190- <!-- interface-requires gtk+ 3.0 -->
191+ <requires lib="gtk+" version="3.0"/>
192 <object class="GtkListStore" id="model_normal_updates_display">
193 <columns>
194 <!-- column-name text -->
195@@ -160,7 +161,6 @@
196 <property name="visible">True</property>
197 <property name="can_focus">True</property>
198 <property name="receives_default">False</property>
199- <property name="use_action_appearance">False</property>
200 <property name="use_underline">True</property>
201 <property name="xalign">0</property>
202 <property name="draw_indicator">True</property>
203@@ -396,7 +396,6 @@
204 <property name="can_focus">True</property>
205 <property name="can_default">True</property>
206 <property name="receives_default">True</property>
207- <property name="use_action_appearance">False</property>
208 <signal name="clicked" handler="on_add_clicked" swapped="no"/>
209 </object>
210 <packing>
211@@ -413,7 +412,6 @@
212 <property name="can_focus">True</property>
213 <property name="can_default">True</property>
214 <property name="receives_default">True</property>
215- <property name="use_action_appearance">False</property>
216 <signal name="clicked" handler="on_edit_clicked" swapped="no"/>
217 </object>
218 <packing>
219@@ -430,7 +428,6 @@
220 <property name="can_focus">True</property>
221 <property name="can_default">True</property>
222 <property name="receives_default">True</property>
223- <property name="use_action_appearance">False</property>
224 <property name="use_stock">True</property>
225 <signal name="clicked" handler="on_remove_clicked" swapped="no"/>
226 </object>
227@@ -459,7 +456,6 @@
228 <property name="visible">True</property>
229 <property name="can_focus">True</property>
230 <property name="receives_default">True</property>
231- <property name="use_action_appearance">False</property>
232 <signal name="clicked" handler="on_button_add_cdrom_clicked" swapped="no"/>
233 </object>
234 <packing>
235@@ -570,8 +566,8 @@
236 <object class="GtkLabel" id="label3">
237 <property name="visible">True</property>
238 <property name="can_focus">False</property>
239- <property name="xalign">1</property>
240 <property name="label" translatable="yes">Automatically check for updates:</property>
241+ <property name="xalign">1</property>
242 </object>
243 <packing>
244 <property name="expand">False</property>
245@@ -613,8 +609,8 @@
246 <object class="GtkLabel" id="label4">
247 <property name="visible">True</property>
248 <property name="can_focus">False</property>
249- <property name="xalign">1</property>
250 <property name="label" translatable="yes">When there are security updates:</property>
251+ <property name="xalign">1</property>
252 </object>
253 <packing>
254 <property name="expand">False</property>
255@@ -656,8 +652,8 @@
256 <object class="GtkLabel" id="label5">
257 <property name="visible">True</property>
258 <property name="can_focus">False</property>
259- <property name="xalign">1</property>
260 <property name="label" translatable="yes">When there are other updates:</property>
261+ <property name="xalign">1</property>
262 </object>
263 <packing>
264 <property name="expand">False</property>
265@@ -700,6 +696,84 @@
266 </packing>
267 </child>
268 <child>
269+ <object class="GtkAlignment" id="alignment3">
270+ <property name="visible">True</property>
271+ <property name="can_focus">False</property>
272+ <property name="left_padding">12</property>
273+ <child>
274+ <object class="GtkGrid" id="grid_livepatch">
275+ <property name="visible">True</property>
276+ <property name="can_focus">False</property>
277+ <property name="halign">center</property>
278+ <property name="hexpand">False</property>
279+ <property name="vexpand">False</property>
280+ <property name="row_spacing">6</property>
281+ <property name="column_spacing">6</property>
282+ <child>
283+ <object class="GtkBox" id="hbox_livepatch">
284+ <property name="visible">True</property>
285+ <property name="can_focus">False</property>
286+ <property name="halign">center</property>
287+ <property name="spacing">6</property>
288+ <child>
289+ <object class="GtkLabel" id="label_livepatch_login">
290+ <property name="visible">True</property>
291+ <property name="can_focus">False</property>
292+ </object>
293+ <packing>
294+ <property name="expand">False</property>
295+ <property name="fill">True</property>
296+ <property name="position">0</property>
297+ </packing>
298+ </child>
299+ <child>
300+ <object class="GtkButton" id="button_ubuntuone">
301+ <property name="visible">True</property>
302+ <property name="can_focus">True</property>
303+ <property name="receives_default">True</property>
304+ <property name="xalign">0</property>
305+ </object>
306+ <packing>
307+ <property name="expand">False</property>
308+ <property name="fill">True</property>
309+ <property name="position">2</property>
310+ </packing>
311+ </child>
312+ </object>
313+ <packing>
314+ <property name="left_attach">1</property>
315+ <property name="top_attach">1</property>
316+ </packing>
317+ </child>
318+ <child>
319+ <object class="GtkCheckButton" id="checkbutton_livepatch">
320+ <property name="label" translatable="yes">Use Canonical Livepatch to increase security between restarts</property>
321+ <property name="use_action_appearance">False</property>
322+ <property name="visible">True</property>
323+ <property name="can_focus">True</property>
324+ <property name="receives_default">False</property>
325+ <property name="use_underline">True</property>
326+ <property name="xalign">0</property>
327+ <property name="draw_indicator">True</property>
328+ </object>
329+ <packing>
330+ <property name="left_attach">1</property>
331+ <property name="top_attach">0</property>
332+ </packing>
333+ </child>
334+ <child>
335+ <placeholder/>
336+ </child>
337+ </object>
338+ </child>
339+ </object>
340+ <packing>
341+ <property name="expand">True</property>
342+ <property name="fill">True</property>
343+ <property name="position">2</property>
344+ </packing>
345+ </child>
346+ <child>
347 <object class="GtkAlignment" id="alignment15">
348 <property name="visible">True</property>
349 <property name="can_focus">False</property>
350@@ -713,8 +787,8 @@
351 <object class="GtkLabel" id="label29">
352 <property name="visible">True</property>
353 <property name="can_focus">False</property>
354+ <property name="label" translatable="yes">Notify me of a new Ubuntu version:</property>
355 <property name="xalign">1</property>
356- <property name="label" translatable="yes">Notify me of a new Ubuntu version:</property>
357 </object>
358 <packing>
359 <property name="expand">False</property>
360@@ -746,7 +820,7 @@
361 <packing>
362 <property name="expand">False</property>
363 <property name="fill">False</property>
364- <property name="position">2</property>
365+ <property name="position">3</property>
366 </packing>
367 </child>
368 </object>
369@@ -775,9 +849,9 @@
370 <object class="GtkLabel" id="label27">
371 <property name="visible">True</property>
372 <property name="can_focus">False</property>
373- <property name="xalign">0</property>
374 <property name="label" translatable="yes">&lt;b&gt;Trusted software providers&lt;/b&gt;</property>
375 <property name="use_markup">True</property>
376+ <property name="xalign">0</property>
377 </object>
378 <packing>
379 <property name="expand">False</property>
380@@ -851,7 +925,6 @@
381 <property name="has_tooltip">True</property>
382 <property name="tooltip_markup" translatable="yes">Import the public key from a trusted software provider</property>
383 <property name="tooltip_text" translatable="yes">Import the public key from a trusted software provider</property>
384- <property name="use_action_appearance">False</property>
385 <property name="use_underline">True</property>
386 <signal name="clicked" handler="add_key_clicked" swapped="no"/>
387 </object>
388@@ -868,7 +941,6 @@
389 <property name="visible">True</property>
390 <property name="can_focus">True</property>
391 <property name="receives_default">True</property>
392- <property name="use_action_appearance">False</property>
393 <property name="use_stock">True</property>
394 <signal name="clicked" handler="remove_key_clicked" swapped="no"/>
395 </object>
396@@ -900,7 +972,6 @@
397 <property name="has_tooltip">True</property>
398 <property name="tooltip_markup" translatable="yes">Restore the default keys of your distribution</property>
399 <property name="tooltip_text" translatable="yes">Restore the default keys of your distribution</property>
400- <property name="use_action_appearance">False</property>
401 <property name="use_underline">True</property>
402 <signal name="clicked" handler="on_restore_clicked" swapped="no"/>
403 </object>
404@@ -1001,8 +1072,8 @@
405 <property name="visible">True</property>
406 <property name="can_focus">False</property>
407 <property name="halign">start</property>
408+ <property name="label" translatable="yes">No proprietary drivers are in use.</property>
409 <property name="xalign">0</property>
410- <property name="label" translatable="yes">No proprietary drivers are in use.</property>
411 </object>
412 <packing>
413 <property name="expand">True</property>
414@@ -1024,11 +1095,11 @@
415 <object class="GtkLabel" id="label_disc">
416 <property name="visible">True</property>
417 <property name="can_focus">False</property>
418- <property name="xalign">0</property>
419 <property name="label" translatable="yes">&lt;small&gt;A proprietary driver has private code that Ubuntu developers can't review or improve. Security and other updates are dependent on the driver vendor.&lt;/small&gt;</property>
420 <property name="use_markup">True</property>
421 <property name="wrap">True</property>
422- <property name="max-width-chars">50</property>
423+ <property name="max_width_chars">50</property>
424+ <property name="xalign">0</property>
425 </object>
426 <packing>
427 <property name="expand">False</property>
428@@ -1090,7 +1161,7 @@
429 <property name="label" translatable="yes">Use proposed updates if you’re willing to report bugs on any problems that occur.</property>
430 <property name="use_markup">True</property>
431 <property name="wrap">True</property>
432- <property name="max-width-chars">110</property>
433+ <property name="max_width_chars">110</property>
434 </object>
435 </child>
436 </object>
437@@ -1138,7 +1209,6 @@
438 <property name="can_focus">True</property>
439 <property name="can_default">True</property>
440 <property name="receives_default">True</property>
441- <property name="use_action_appearance">False</property>
442 <property name="use_underline">True</property>
443 <signal name="clicked" handler="on_button_revert_clicked" swapped="no"/>
444 </object>
445@@ -1157,7 +1227,6 @@
446 <property name="can_focus">True</property>
447 <property name="can_default">True</property>
448 <property name="receives_default">True</property>
449- <property name="use_action_appearance">False</property>
450 <property name="use_stock">True</property>
451 <signal name="clicked" handler="on_close_button" swapped="no"/>
452 </object>
453
454=== modified file 'debian/changelog'
455--- debian/changelog 2018-03-06 16:06:38 +0000
456+++ debian/changelog 2018-03-21 15:53:10 +0000
457@@ -1,3 +1,17 @@
458+software-properties (0.96.24.25) UNRELEASED; urgency=medium
459+
460+ * DialogAuth.py: Implement a dialog to choose between an ubuntu sso account or
461+ login into a new one. This interacts with gnome-online-accounts (LP: 1756364)
462+ * DialogLivepatchError: Implement a dialog to show livepatch related errors.
463+ * GoaAuth.py: Implement an utility function to store manage livepatch credentials.
464+ * SoftwareProperties.py: Implement functions used by the dbus API to
465+ enable/disable livepatch.
466+ * SoftwarePropertiesDbus.py: Add SetLivepatchEnabled to the dbus API.
467+ * SoftwarePropertiesGtk.py: Add a livepatch switch.
468+ * debian/control: Depends on gir1.2-goa-1.0, gir1.2-snapd-1 and gir1.2-secret-1.
469+
470+ -- Andrea Azzarone <andrea.azzarone@canonical.com> Wed, 14 Mar 2018 20:24:31 +0100
471+
472 software-properties (0.96.24.23) bionic; urgency=medium
473
474 * SoftwarePropertiesGtk.py: After installing proprietary drivers provide the
475
476=== modified file 'debian/control'
477--- debian/control 2018-03-06 15:47:27 +0000
478+++ debian/control 2018-03-21 15:53:10 +0000
479@@ -41,8 +41,9 @@
480 Package: software-properties-common
481 Architecture: all
482 Depends: ${python3:Depends}, ${misc:Depends}, python3,
483- python3-gi, gir1.2-glib-2.0, python-apt-common (>= 0.9), python3-dbus,
484- python3-software-properties (= ${binary:Version}), ca-certificates
485+ python3-gi, gir1.2-glib-2.0, gir1.2-goa-1.0 (>= 3.27.92-1ubuntu1), gir1.2-secret-1, gir1.2-snapd-1,
486+ python-apt-common (>= 0.9), python3-dbus, python3-software-properties (= ${binary:Version}),
487+ ca-certificates
488 Breaks: python-software-properties (<< 0.85), python3-software-properties (<< 0.85)
489 Replaces: python-software-properties (<< 0.85), python3-software-properties (<< 0.85)
490 Description: manage the repositories that you install software from (common)
491@@ -60,6 +61,7 @@
492 python3-gi,
493 gir1.2-gtk-3.0,
494 python3-aptdaemon.gtk3widgets,
495+ python3-distro-info,
496 software-properties-common,
497 ubuntu-drivers-common (>= 1:0.2.75),
498 python3-gi,
499
500=== modified file 'po/POTFILES.in'
501--- po/POTFILES.in 2014-04-04 06:52:18 +0000
502+++ po/POTFILES.in 2018-03-21 15:53:10 +0000
503@@ -17,6 +17,7 @@
504 softwareproperties/kde/DialogEdit.py
505 softwareproperties/__init__.py
506 softwareproperties/SoftwareProperties.py
507+softwareproperties/gtk/DialogAuth.py
508 softwareproperties/gtk/DialogMirror.py
509 softwareproperties/gtk/CdromProgress.py
510 softwareproperties/gtk/SoftwarePropertiesGtk.py
511@@ -38,4 +39,4 @@
512 [type: gettext/glade]data/gtkbuilder/dialog-cache-outofdate.ui
513 [type: gettext/glade]data/gtkbuilder/dialog-mirror.ui
514 [type: gettext/glade]data/gtkbuilder/dialog-add.ui
515-
516+[type: gettext/glade]data/gtkbuilder/dialog-auth.ui
517
518=== modified file 'po/software-properties.pot'
519--- po/software-properties.pot 2016-09-09 07:14:08 +0000
520+++ po/software-properties.pot 2018-03-21 15:53:10 +0000
521@@ -8,7 +8,7 @@
522 msgstr ""
523 "Project-Id-Version: PACKAGE VERSION\n"
524 "Report-Msgid-Bugs-To: Sebastian Heinlein <sebi@glatzor.de>\n"
525-"POT-Creation-Date: 2016-09-09 03:13-0400\n"
526+"POT-Creation-Date: 2018-03-21 15:05+0100\n"
527 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
528 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
529 "Language-Team: LANGUAGE <LL@li.org>\n"
530@@ -41,8 +41,8 @@
531 msgstr ""
532
533 #: ../data/software-properties-kde.desktop.in.h:1
534-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:674
535-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:693
536+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:685
537+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:704
538 msgid "Software Sources"
539 msgstr ""
540
541@@ -104,60 +104,64 @@
542 msgstr ""
543
544 #: ../add-apt-repository:74
545-msgid "Update package cache after adding"
546-msgstr ""
547-
548-#: ../add-apt-repository:78
549+msgid "Do not update package cache after adding"
550+msgstr ""
551+
552+#: ../add-apt-repository:77
553+msgid "Update package cache after adding (legacy option)"
554+msgstr ""
555+
556+#: ../add-apt-repository:90
557 msgid "Error: must run as root"
558 msgstr ""
559
560-#: ../add-apt-repository:82
561+#: ../add-apt-repository:94
562 msgid "Error: need a repository as argument"
563 msgstr ""
564
565-#: ../add-apt-repository:85
566+#: ../add-apt-repository:97
567 msgid "Error: need a single repository as argument"
568 msgstr ""
569
570-#: ../add-apt-repository:105
571+#: ../add-apt-repository:117
572 #, c-format
573 msgid "'%s' distribution component disabled for all sources."
574 msgstr ""
575
576-#: ../add-apt-repository:107
577+#: ../add-apt-repository:119
578 #, c-format
579 msgid "'%s' distribution component is already disabled for all sources."
580 msgstr ""
581
582-#: ../add-apt-repository:112
583+#: ../add-apt-repository:124
584 #, c-format
585 msgid "'%s' distribution component enabled for all sources."
586 msgstr ""
587
588-#: ../add-apt-repository:114
589+#: ../add-apt-repository:126
590 #, c-format
591 msgid "'%s' distribution component is already enabled for all sources."
592 msgstr ""
593
594-#: ../add-apt-repository:136
595+#: ../add-apt-repository:150
596 #, c-format
597 msgid " More info: %s"
598 msgstr ""
599
600-#: ../add-apt-repository:140
601-msgid "Press [ENTER] to continue or ctrl-c to cancel removing it"
602-msgstr ""
603-
604-#: ../add-apt-repository:142
605-msgid "Press [ENTER] to continue or ctrl-c to cancel adding it"
606-msgstr ""
607-
608-#: ../add-apt-repository:160 ../add-apt-repository:164
609+#: ../add-apt-repository:154
610+msgid "Press [ENTER] to continue or Ctrl-c to cancel removing it."
611+msgstr ""
612+
613+#: ../add-apt-repository:156
614+msgid "Press [ENTER] to continue or Ctrl-c to cancel adding it."
615+msgstr ""
616+
617+#: ../add-apt-repository:178 ../add-apt-repository:182
618 #, c-format
619 msgid "Error: '%s' doesn't exist in a sourcelist file"
620 msgstr ""
621
622-#: ../add-apt-repository:169
623+#: ../add-apt-repository:187
624 #, c-format
625 msgid "Error: '%s' invalid"
626 msgstr ""
627@@ -211,6 +215,7 @@
628
629 #: ../softwareproperties/kde/DialogMirror.py:220
630 #: ../softwareproperties/gtk/DialogMirror.py:338
631+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1536
632 msgid "Please check your Internet connection."
633 msgstr ""
634
635@@ -240,7 +245,7 @@
636 msgstr ""
637
638 #: ../softwareproperties/kde/SoftwarePropertiesKDE.py:172
639-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:247
640+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:258
641 #, python-format
642 msgid "Every %s days"
643 msgstr ""
644@@ -253,7 +258,7 @@
645
646 #. TRANS: %s stands for the distribution name e.g. Debian or Ubuntu
647 #: ../softwareproperties/kde/SoftwarePropertiesKDE.py:218
648-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:307
649+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:318
650 #, python-format
651 msgid "%s Software"
652 msgstr ""
653@@ -262,7 +267,7 @@
654 #. first %s is the description of the component
655 #. second %s is the code name of the comp, eg main, universe
656 #: ../softwareproperties/kde/SoftwarePropertiesKDE.py:237
657-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:320
658+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:331
659 #, python-format
660 msgid "%s (%s)"
661 msgstr ""
662@@ -270,33 +275,33 @@
663 #. add a separator and the option to choose another mirror from the list
664 #. #FIXME server_store.append(["sep", None, True])
665 #: ../softwareproperties/kde/SoftwarePropertiesKDE.py:300
666-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:450
667-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:543
668+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:461
669+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:554
670 msgid "Other..."
671 msgstr ""
672
673 #: ../softwareproperties/kde/SoftwarePropertiesKDE.py:668
674-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:966
675+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:977
676 msgid "Import key"
677 msgstr ""
678
679 #: ../softwareproperties/kde/SoftwarePropertiesKDE.py:671
680-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:980
681+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:991
682 msgid "Error importing selected file"
683 msgstr ""
684
685 #: ../softwareproperties/kde/SoftwarePropertiesKDE.py:672
686-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:981
687+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:992
688 msgid "The selected file may not be a GPG key file or it might be corrupt."
689 msgstr ""
690
691 #: ../softwareproperties/kde/SoftwarePropertiesKDE.py:685
692-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:997
693+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1008
694 msgid "Error removing the key"
695 msgstr ""
696
697 #: ../softwareproperties/kde/SoftwarePropertiesKDE.py:686
698-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:998
699+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1009
700 msgid "The key you selected could not be removed. Please report this as a bug."
701 msgstr ""
702
703@@ -317,7 +322,7 @@
704 msgstr ""
705
706 #: ../softwareproperties/kde/SoftwarePropertiesKDE.py:731
707-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:832
708+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:843
709 msgid "Error scanning the CD"
710 msgstr ""
711
712@@ -359,15 +364,49 @@
713 msgid "Source code"
714 msgstr ""
715
716-#: ../softwareproperties/SoftwareProperties.py:502
717-#: ../softwareproperties/SoftwareProperties.py:509
718+#: ../softwareproperties/SoftwareProperties.py:521
719+#: ../softwareproperties/SoftwareProperties.py:528
720 msgid "(Source Code)"
721 msgstr ""
722
723-#: ../softwareproperties/SoftwareProperties.py:515
724+#: ../softwareproperties/SoftwareProperties.py:534
725 msgid "Source Code"
726 msgstr ""
727
728+#: ../softwareproperties/SoftwareProperties.py:935
729+#: ../softwareproperties/SoftwareProperties.py:983
730+msgid "Canonical Livepatch snap cannot be installed."
731+msgstr ""
732+
733+#: ../softwareproperties/SoftwareProperties.py:972
734+msgid "Canonical Livepatch snap cannot be enabled."
735+msgstr ""
736+
737+#: ../softwareproperties/SoftwareProperties.py:986
738+msgid "Canonical Livepatch cannot be enabled."
739+msgstr ""
740+
741+#: ../softwareproperties/SoftwareProperties.py:1001
742+msgid "Canonical Livepatch cannot be disabled."
743+msgstr ""
744+
745+#: ../softwareproperties/gtk/DialogAuth.py:73
746+msgid "To continue choose an Ubuntu Single Sign-On account."
747+msgstr ""
748+
749+#: ../softwareproperties/gtk/DialogAuth.py:74
750+msgid "Use another account…"
751+msgstr ""
752+
753+#: ../softwareproperties/gtk/DialogAuth.py:76
754+msgid "To continue you need an Ubuntu Single Sign-On account."
755+msgstr ""
756+
757+#: ../softwareproperties/gtk/DialogAuth.py:77
758+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1514
759+msgid "Sign In…"
760+msgstr ""
761+
762 #: ../softwareproperties/gtk/DialogMirror.py:250
763 msgid "New mirror"
764 msgstr ""
765@@ -377,137 +416,159 @@
766 msgid "Completed %s of %s tests"
767 msgstr ""
768
769-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:684
770-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:703
771+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:695
772+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:714
773 msgid "Active"
774 msgstr ""
775
776-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:742
777+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:753
778 msgid "Key"
779 msgstr ""
780
781-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:759
782+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:770
783 msgid "_Add key from paste data"
784 msgstr ""
785
786-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:772
787 #: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:783
788+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:794
789 msgid "Error importing key"
790 msgstr ""
791
792-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:773
793 #: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:784
794+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:795
795 msgid "The selected data may not be a GPG key file or it might be corrupt."
796 msgstr ""
797
798-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:833
799+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:844
800 msgid "Could not find a suitable CD."
801 msgstr ""
802
803-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1043
804+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1059
805 msgid "Applying changes..."
806 msgstr ""
807
808-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1124
809-#: ../data/gtkbuilder/main.ui.h:42
810+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1158
811+#: ../data/gtkbuilder/main.ui.h:43
812 msgid "Re_vert"
813 msgstr ""
814
815-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1126
816+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1160
817 msgid "_Apply Changes"
818 msgstr ""
819
820-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1128
821+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1162
822 msgid "_Cancel"
823 msgstr ""
824
825-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1130
826+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1164
827 msgid "_Restart..."
828 msgstr ""
829
830-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1145
831+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1179
832 msgid "Searching for available drivers..."
833 msgstr ""
834
835-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1175
836+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1209
837 msgid "An error occurred while searching for drivers."
838 msgstr ""
839
840-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1249
841+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1283
842 msgid "This device is using the recommended driver."
843 msgstr ""
844
845-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1250
846+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1284
847 msgid "This device is using an alternative driver."
848 msgstr ""
849
850-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1251
851+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1285
852 msgid "This device is using a manually-installed driver."
853 msgstr ""
854
855-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1252
856+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1286
857 msgid "This device is not working."
858 msgstr ""
859
860-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1261
861+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1295
862 msgid "Continue using a manually installed driver"
863 msgstr ""
864
865-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1288
866+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1322
867 msgid "Using {} from {}"
868 msgstr ""
869
870-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1290
871+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1324
872 msgid "Using {}"
873 msgstr ""
874
875-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1297
876+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1331
877 msgid "open source"
878 msgstr ""
879
880-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1299
881+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1333
882 msgid "proprietary"
883 msgstr ""
884
885-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1302
886+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1336
887 #, python-brace-format
888 msgid "{base_description} ({licence}, tested)"
889 msgstr ""
890
891-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1304
892+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1338
893 #, python-brace-format
894 msgid "{base_description} ({licence})"
895 msgstr ""
896
897-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1324
898+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1358
899 msgid "Do not use the device"
900 msgstr ""
901
902 #. No drivers found.
903-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1344
904+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1378
905 msgid "No additional drivers available."
906 msgstr ""
907
908-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1362
909+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1396
910 msgid "Unknown"
911 msgstr ""
912
913-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1411
914+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1445
915 msgid "You need to restart the computer to complete the driver changes."
916 msgstr ""
917
918-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1425
919+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1459
920 #, python-format
921 msgid "%(count)d proprietary driver in use."
922 msgid_plural "%(count)d proprietary drivers in use."
923 msgstr[0] ""
924 msgstr[1] ""
925
926-#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1430
927-#: ../data/gtkbuilder/main.ui.h:37
928+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1464
929+#: ../data/gtkbuilder/main.ui.h:38
930 msgid "No proprietary drivers are in use."
931 msgstr ""
932
933+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1502
934+msgid "Sign Out"
935+msgstr ""
936+
937+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1506
938+#, python-format
939+msgid "Signed in as %s"
940+msgstr ""
941+
942+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1509
943+#, python-format
944+msgid "%s isn't authorized to use Livepatch."
945+msgstr ""
946+
947+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1515
948+msgid "To use Livepatch you need to sign in."
949+msgstr ""
950+
951+#: ../softwareproperties/gtk/SoftwarePropertiesGtk.py:1535
952+msgid "Error enabling Canonical Livepatch"
953+msgstr ""
954+
955 #: ../softwareproperties/gtk/DialogAddSourcesList.py:46
956 msgid "Add Software Channels"
957 msgstr ""
958@@ -542,23 +603,23 @@
959 msgstr ""
960
961 #. some known keys
962-#: ../softwareproperties/AptAuth.py:39
963+#: ../softwareproperties/AptAuth.py:40
964 msgid "Ubuntu Archive Automatic Signing Key <ftpmaster@ubuntu.com>"
965 msgstr ""
966
967-#: ../softwareproperties/AptAuth.py:40
968+#: ../softwareproperties/AptAuth.py:41
969 msgid "Ubuntu CD Image Automatic Signing Key <cdimage@ubuntu.com>"
970 msgstr ""
971
972-#: ../softwareproperties/AptAuth.py:41
973+#: ../softwareproperties/AptAuth.py:42
974 msgid "Ubuntu Archive Automatic Signing Key (2012) <ftpmaster@ubuntu.com>"
975 msgstr ""
976
977-#: ../softwareproperties/AptAuth.py:42
978+#: ../softwareproperties/AptAuth.py:43
979 msgid "Ubuntu CD Image Automatic Signing Key (2012) <cdimage@ubuntu.com>"
980 msgstr ""
981
982-#: ../softwareproperties/AptAuth.py:43
983+#: ../softwareproperties/AptAuth.py:44
984 msgid "Ubuntu Extras Archive Automatic Signing Key <ftpmaster@ubuntu.com>"
985 msgstr ""
986
987@@ -679,65 +740,69 @@
988 msgstr ""
989
990 #: ../data/gtkbuilder/main.ui.h:27
991+msgid "Use Canonical Livepatch to increase security between restarts"
992+msgstr ""
993+
994+#: ../data/gtkbuilder/main.ui.h:28
995 msgid "Notify me of a new Ubuntu version:"
996 msgstr ""
997
998-#: ../data/gtkbuilder/main.ui.h:28
999+#: ../data/gtkbuilder/main.ui.h:29
1000 msgid "Updates"
1001 msgstr ""
1002
1003-#: ../data/gtkbuilder/main.ui.h:29
1004+#: ../data/gtkbuilder/main.ui.h:30
1005 msgid "<b>Trusted software providers</b>"
1006 msgstr ""
1007
1008-#: ../data/gtkbuilder/main.ui.h:30
1009+#: ../data/gtkbuilder/main.ui.h:31
1010 msgid " "
1011 msgstr ""
1012
1013-#: ../data/gtkbuilder/main.ui.h:31
1014+#: ../data/gtkbuilder/main.ui.h:32
1015 msgid ""
1016 "Keys are used to authenticate the correct source of software and so protect "
1017 "your computer from malicious software"
1018 msgstr ""
1019
1020-#: ../data/gtkbuilder/main.ui.h:32
1021+#: ../data/gtkbuilder/main.ui.h:33
1022 msgid "_Import Key File..."
1023 msgstr ""
1024
1025-#: ../data/gtkbuilder/main.ui.h:33
1026+#: ../data/gtkbuilder/main.ui.h:34
1027 msgid "Import the public key from a trusted software provider"
1028 msgstr ""
1029
1030-#: ../data/gtkbuilder/main.ui.h:34
1031+#: ../data/gtkbuilder/main.ui.h:35
1032 msgid "Restore _Defaults"
1033 msgstr ""
1034
1035-#: ../data/gtkbuilder/main.ui.h:35
1036+#: ../data/gtkbuilder/main.ui.h:36
1037 msgid "Restore the default keys of your distribution"
1038 msgstr ""
1039
1040-#: ../data/gtkbuilder/main.ui.h:36
1041+#: ../data/gtkbuilder/main.ui.h:37
1042 msgid "Authentication"
1043 msgstr ""
1044
1045-#: ../data/gtkbuilder/main.ui.h:38
1046+#: ../data/gtkbuilder/main.ui.h:39
1047 msgid ""
1048 "<small>A proprietary driver has private code that Ubuntu developers can't "
1049 "review or improve. Security and other updates are dependent on the driver "
1050 "vendor.</small>"
1051 msgstr ""
1052
1053-#: ../data/gtkbuilder/main.ui.h:39
1054+#: ../data/gtkbuilder/main.ui.h:40
1055 msgid "Additional Drivers"
1056 msgstr ""
1057
1058-#: ../data/gtkbuilder/main.ui.h:40
1059+#: ../data/gtkbuilder/main.ui.h:41
1060 msgid ""
1061 "Use proposed updates if you’re willing to report bugs on any problems that "
1062 "occur."
1063 msgstr ""
1064
1065-#: ../data/gtkbuilder/main.ui.h:41
1066+#: ../data/gtkbuilder/main.ui.h:42
1067 msgid "Developer Options"
1068 msgstr ""
1069
1070@@ -801,3 +866,15 @@
1071 #: ../data/gtkbuilder/dialog-add.ui.h:3
1072 msgid "_Add Source"
1073 msgstr ""
1074+
1075+#: ../data/gtkbuilder/dialog-auth.ui.h:1
1076+msgid "To enable Livepatch choose an Ubuntu Single Sign-on account."
1077+msgstr ""
1078+
1079+#: ../data/gtkbuilder/dialog-auth.ui.h:2
1080+msgid "<b>Use another account...</b>"
1081+msgstr ""
1082+
1083+#: ../data/gtkbuilder/dialog-auth.ui.h:3
1084+msgid "Choose an account"
1085+msgstr ""
1086
1087=== added file 'softwareproperties/GoaAuth.py'
1088--- softwareproperties/GoaAuth.py 1970-01-01 00:00:00 +0000
1089+++ softwareproperties/GoaAuth.py 2018-03-21 15:53:10 +0000
1090@@ -0,0 +1,113 @@
1091+#
1092+# Copyright (c) 2018 Canonical
1093+#
1094+# Authors:
1095+# Andrea Azzarone <andrea.azzarone@canonical.com>
1096+#
1097+# This program is free software; you can redistribute it and/or
1098+# modify it under the terms of the GNU General Public License as
1099+# published by the Free Software Foundation; either version 2 of the
1100+# License, or (at your option) any later version.
1101+#
1102+# This program is distributed in the hope that it will be useful,
1103+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1104+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1105+# GNU General Public License for more details.
1106+#
1107+# You should have received a copy of the GNU General Public License
1108+# along with this program; if not, write to the Free Software
1109+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
1110+# USA
1111+
1112+import gi
1113+gi.require_version('Goa', '1.0')
1114+gi.require_version('Secret', '1')
1115+from gi.repository import Goa, GObject, Secret
1116+
1117+SECRETS_SCHEMA = Secret.Schema.new('com.ubuntu.SotwareProperties',
1118+ Secret.SchemaFlags.NONE,
1119+ {'key': Secret.SchemaAttributeType.STRING})
1120+
1121+class GoaAuth(GObject.GObject):
1122+
1123+ # Properties
1124+ logged = GObject.Property(type=bool, default=False)
1125+
1126+ def __init__(self):
1127+ GObject.GObject.__init__(self)
1128+
1129+ self.goa_client = Goa.Client.new_sync(None)
1130+ self.account = None
1131+ self._load()
1132+
1133+ def login(self, account):
1134+ assert(account)
1135+ self._update_state(account)
1136+ self._store()
1137+
1138+ def logout(self):
1139+ self._update_state(None)
1140+ self._store()
1141+
1142+ @GObject.Property
1143+ def username(self):
1144+ if self.account is None:
1145+ return None
1146+ return self.account.props.presentation_identity
1147+
1148+ @GObject.Property
1149+ def token(self):
1150+ if self.account is None:
1151+ return None
1152+
1153+ obj = self.goa_client.lookup_by_id(self.account.props.id)
1154+ if obj is None:
1155+ return None
1156+
1157+ pbased = obj.get_password_based()
1158+ if pbased is None:
1159+ return None
1160+
1161+ return pbased.call_get_password_sync('livepatch')
1162+
1163+ def _update_state_from_account_id(self, account_id):
1164+ if account_id:
1165+ # Make sure the account-id is valid
1166+ obj = self.goa_client.lookup_by_id(account_id)
1167+ if obj is None:
1168+ self._update_state(None)
1169+ return
1170+
1171+ account = obj.get_account()
1172+ if account is None:
1173+ self._update_state(None)
1174+ return
1175+
1176+ self._update_state(account)
1177+ else:
1178+ self._update_state(None)
1179+
1180+ def _update_state(self, account):
1181+ self.account = account
1182+ if self.account is None:
1183+ self.logged = False
1184+ else:
1185+ try:
1186+ account.call_ensure_credentials_sync(None)
1187+ except Exception:
1188+ self.logged = False
1189+ else:
1190+ self.account.connect('notify::attention-needed', lambda o, v: self.logout())
1191+ self.logged = True
1192+
1193+ def _load(self):
1194+ # Retrieve the stored account-id
1195+ account_id = Secret.password_lookup_sync(SECRETS_SCHEMA, {'key': 'account-id'}, None)
1196+ self._update_state_from_account_id(account_id)
1197+
1198+ def _store(self):
1199+ if self.logged:
1200+ account_id = self.account.props.id
1201+ Secret.password_store(SECRETS_SCHEMA, {'key': 'account-id'}, None, 'com.ubuntu.SoftwareProperties', account_id)
1202+ else:
1203+ Secret.password_clear(SECRETS_SCHEMA, {'key': 'account-id'}, None, None, None)
1204
1205=== modified file 'softwareproperties/SoftwareProperties.py'
1206--- softwareproperties/SoftwareProperties.py 2017-03-01 17:35:37 +0000
1207+++ softwareproperties/SoftwareProperties.py 2018-03-21 15:53:10 +0000
1208@@ -1,11 +1,12 @@
1209 # software-properties backend
1210 #
1211-# Copyright (c) 2004-2007 Canonical Ltd.
1212+# Copyright (c) 2004-2018 Canonical Ltd.
1213 # 2004-2005 Michiel Sikkes
1214 #
1215 # Author: Michiel Sikkes <michiel@eyesopened.nl>
1216 # Michael Vogt <mvo@debian.org>
1217 # Sebastian Heinlein <glatzor@ubuntu.com>
1218+# Andrea Azzarone <andrea.azzarone@canonical.com>
1219 #
1220 # This program is free software; you can redistribute it and/or
1221 # modify it under the terms of the GNU General Public License as
1222@@ -31,6 +32,7 @@
1223 import os
1224 import glob
1225 import shutil
1226+import subprocess
1227 import threading
1228 import atexit
1229 import tempfile
1230@@ -63,6 +65,10 @@
1231 from . import ppa
1232 from . import cloudarchive
1233
1234+import gi
1235+gi.require_version('Snapd', '1')
1236+from gi.repository import Gio, Snapd
1237+
1238 _SHORTCUT_FACTORIES = [
1239 ppa.shortcut_handler,
1240 cloudarchive.shortcut_handler,
1241@@ -88,6 +94,9 @@
1242 RELEASE_UPGRADES_LTS : 'lts',
1243 RELEASE_UPGRADES_NEVER : 'never',
1244 }
1245+
1246+ # file to monitor canonical-livepatch status
1247+ LIVEPATCH_RUNNING_FILE = '/var/snap/canonical-livepatch/common/machine-token'
1248
1249 def __init__(self, datadir=None, options=None, rootdir="/"):
1250 """ Provides the core functionality to configure the used software
1251@@ -126,6 +135,8 @@
1252 # apt-key stuff
1253 self.apt_key = AptAuth(rootdir=rootdir)
1254
1255+ self.cancellable = Gio.Cancellable()
1256+
1257 atexit.register(self.wait_for_threads)
1258
1259 def wait_for_threads(self):
1260@@ -858,6 +869,147 @@
1261 except:
1262 return False
1263
1264+ #
1265+ # Livepatch
1266+ #
1267+ def init_snapd(self):
1268+ self.snapd_client = Snapd.Client()
1269+
1270+ def get_livepatch_snap_async(self, callback):
1271+ assert self.snapd_client
1272+ self.snapd_client.list_one_async('canonical-livepatch',
1273+ self.cancellable,
1274+ self.on_list_one_ready_cb,
1275+ callback)
1276+
1277+ def on_list_one_ready_cb(self, source_object, result, user_data):
1278+ callback = user_data
1279+ try:
1280+ snap = source_object.list_one_finish(result)
1281+ except:
1282+ snap = None
1283+ if snap:
1284+ if callback:
1285+ callback(snap)
1286+ return
1287+ else:
1288+ assert self.snapd_client
1289+ self.snapd_client.find_async(Snapd.FindFlags.MATCH_NAME,
1290+ 'canonical-livepatch',
1291+ self.cancellable,
1292+ self.on_find_ready_cb,
1293+ callback)
1294+
1295+ def on_find_ready_cb(self, source_object, result, user_data):
1296+ callback = user_data
1297+ try:
1298+ snaps = source_object.find_finish(result)[0]
1299+ except:
1300+ snaps = list()
1301+ snap = snaps[0] if len(snaps) else None
1302+ if callback:
1303+ callback(snap)
1304+
1305+ def get_livepatch_snap_status(self, snap):
1306+ if snap is None:
1307+ return Snapd.SnapStatus.UNKNOWN
1308+ return snap.get_status()
1309+
1310+ # glib-snapd does not keep track of the status of the snap. Use this decorator
1311+ # to make it easy to write async functions that will always have an updated
1312+ # snap object.
1313+ def require_livepatch_snap(func):
1314+ def get_livepatch_snap_and_call(*args, **kwargs):
1315+ return args[0].get_livepatch_snap_async(lambda snap: func(snap=snap, *args, **kwargs))
1316+ return get_livepatch_snap_and_call
1317+
1318+ def is_livepatch_enabled(self):
1319+ file = Gio.File.new_for_path(path=self.LIVEPATCH_RUNNING_FILE)
1320+ return file.query_exists(None)
1321+
1322+ @require_livepatch_snap
1323+ def set_livepatch_enabled_async(self, enabled, token, callback, snap=None):
1324+ status = self.get_livepatch_snap_status(snap)
1325+ if status == Snapd.SnapStatus.UNKNOWN:
1326+ if callback:
1327+ callback(True, _("Canonical Livepatch snap cannot be installed."))
1328+ elif status == Snapd.SnapStatus.ACTIVE:
1329+ if enabled:
1330+ error = self.enable_livepatch_service(token)
1331+ else:
1332+ error = self.disable_livepatch_service()
1333+ if callback:
1334+ callback(len(error) > 0, error)
1335+ elif status == Snapd.SnapStatus.INSTALLED:
1336+ if enabled:
1337+ self.snapd_client.enable_async(name='canonical-livepatch',
1338+ cancellable=self.cancellable,
1339+ callback=self.livepatch_enable_snap_cb,
1340+ user_data=(callback, token))
1341+ else:
1342+ if callback:
1343+ callback(False, "")
1344+ elif status == Snapd.SnapStatus.AVAILABLE:
1345+ if enabled:
1346+ self.snapd_client.install_async(name='canonical-livepatch',
1347+ channel='edge', # Remove this once bionic is officialy supported.
1348+ cancellable=self.cancellable,
1349+ callback=self.livepatch_install_snap_cb,
1350+ user_data=(callback, token))
1351+ else:
1352+ if callback:
1353+ callback(False, "")
1354+
1355+ def livepatch_enable_snap_cb(self, source_object, result, user_data):
1356+ (callback, token) = user_data
1357+ try:
1358+ if source_object.enable_finish(result):
1359+ error = self.enable_livepatch_service(token)
1360+ if callback:
1361+ callback(len(error) > 0, error)
1362+ except Exception:
1363+ if callback:
1364+ callback(True, _("Canonical Livepatch snap cannot be enabled."))
1365+
1366+ def livepatch_install_snap_cb(self, source_object, result, user_data):
1367+ (callback, token) = user_data
1368+ try:
1369+ if source_object.install_finish(result):
1370+ error = self.enable_livepatch_service(token)
1371+ if callback:
1372+ callback(len(error) > 0, error)
1373+ except Exception:
1374+ if callback:
1375+ callback(True, _("Canonical Livepatch snap cannot be installed."))
1376+
1377+ def enable_livepatch_service(self, token):
1378+ generic_error = _("Canonical Livepatch cannot be enabled.")
1379+
1380+ if self.is_livepatch_enabled():
1381+ return ""
1382+
1383+ try:
1384+ subprocess.check_output(['/snap/bin/canonical-livepatch', 'enable', token], stderr=subprocess.STDOUT)
1385+ return ""
1386+ except subprocess.CalledProcessError as e:
1387+ return e.output if e.output else generic_error
1388+ except:
1389+ return generic_error
1390+
1391+
1392+ def disable_livepatch_service(self):
1393+ generic_error = _("Canonical Livepatch cannot be disabled.")
1394+
1395+ if not self.is_livepatch_enabled():
1396+ return ""
1397+
1398+ try:
1399+ subprocess.check_output(['/snap/bin/canonical-livepatch', 'disable'], stderr=subprocess.STDOUT)
1400+ return ""
1401+ except subprocess.CalledProcessError as e:
1402+ return e.output if e.output else generic_error
1403+ except:
1404+ return generic_error
1405
1406 def shortcut_handler(shortcut):
1407 for factory in _SHORTCUT_FACTORIES:
1408
1409=== modified file 'softwareproperties/dbus/SoftwarePropertiesDBus.py'
1410--- softwareproperties/dbus/SoftwarePropertiesDBus.py 2015-11-01 12:39:02 +0000
1411+++ softwareproperties/dbus/SoftwarePropertiesDBus.py 2018-03-21 15:53:10 +0000
1412@@ -61,6 +61,8 @@
1413 self.enforce_polkit = True
1414 logging.debug("waiting for connections")
1415
1416+ self.init_snapd()
1417+
1418 # override set_modified_sourceslist to emit a signal
1419 def save_sourceslist(self):
1420 super(SoftwarePropertiesDBus, self).save_sourceslist()
1421@@ -317,6 +319,15 @@
1422 sender, conn, "com.ubuntu.softwareproperties.applychanges")
1423 return self.update_keys()
1424
1425+ # LivePatch
1426+ @dbus.service.method(DBUS_INTERFACE_NAME,
1427+ sender_keyword="sender", connection_keyword="conn",
1428+ in_signature='bs', out_signature='bs', async_callbacks=('reply_handler', 'error_handler'))
1429+ def SetLivepatchEnabled(self, enabled, token, reply_handler, error_handler, sender=None, conn=None):
1430+ self._check_policykit_privilege(
1431+ sender, conn, "com.ubuntu.softwareproperties.applychanges")
1432+ self.set_livepatch_enabled_async(enabled, token, reply_handler)
1433+
1434 # helper from jockey
1435 def _check_policykit_privilege(self, sender, conn, privilege):
1436 '''Verify that sender has a given PolicyKit privilege.
1437
1438=== added file 'softwareproperties/gtk/DialogAuth.py'
1439--- softwareproperties/gtk/DialogAuth.py 1970-01-01 00:00:00 +0000
1440+++ softwareproperties/gtk/DialogAuth.py 2018-03-21 15:53:10 +0000
1441@@ -0,0 +1,228 @@
1442+#
1443+# Copyright (c) 2018 Canonical
1444+#
1445+# Authors:
1446+# Andrea Azzarone <andrea.azzarone@canonical.com>
1447+#
1448+# This program is free software; you can redistribute it and/or
1449+# modify it under the terms of the GNU General Public License as
1450+# published by the Free Software Foundation; either version 2 of the
1451+# License, or (at your option) any later version.
1452+#
1453+# This program is distributed in the hope that it will be useful,
1454+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1455+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1456+# GNU General Public License for more details.
1457+#
1458+# You should have received a copy of the GNU General Public License
1459+# along with this program; if not, write to the Free Software
1460+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
1461+# USA
1462+
1463+import os
1464+
1465+from gettext import gettext as _
1466+from softwareproperties.gtk.utils import (
1467+ setup_ui,
1468+)
1469+
1470+import gi
1471+gi.require_version('Goa', '1.0')
1472+from gi.repository import Gio, GLib, Goa, GObject, Gtk
1473+import logging
1474+
1475+
1476+class DialogAuth:
1477+
1478+ def __init__(self, parent, datadir):
1479+ """setup up the gtk dialog"""
1480+ self.parent = parent
1481+
1482+ setup_ui(self, os.path.join(datadir, "gtkbuilder",
1483+ "dialog-auth.ui"), domain="software-properties")
1484+ self.label_title.set_max_width_chars(50)
1485+
1486+ self.dialog = self.dialog_auth
1487+ self.dialog.use_header_bar = True
1488+ self.dialog.set_transient_for(parent)
1489+
1490+ self.listboxrow_new_account.account = None
1491+
1492+ self.account = None
1493+ self.dispose_on_new_account = False
1494+ self.goa_client = Goa.Client.new_sync(None)
1495+
1496+ self.listbox_accounts.connect('row-activated', self._listbox_accounts_row_activated_cb)
1497+
1498+ # Be ready to other accounts
1499+ self.goa_client.connect('account-added', self._account_added_cb)
1500+ self.goa_client.connect('account-removed', self._account_removed_cb)
1501+
1502+ self._setup_listbox_accounts()
1503+ self._check_ui()
1504+
1505+ def run(self):
1506+ res = self.dialog.run()
1507+ self.dialog.hide()
1508+ return res
1509+
1510+ def _check_ui(self):
1511+ rows = self.listbox_accounts.get_children()
1512+ has_accounts = len(rows) > 1
1513+
1514+ if has_accounts:
1515+ title = _('To continue choose an Ubuntu Single Sign-On account.')
1516+ new_account = _('Use another account…')
1517+ else:
1518+ title = _('To continue you need an Ubuntu Single Sign-On account.')
1519+ new_account = _('Sign In…')
1520+
1521+ self.label_title.set_text(title)
1522+ self.label_new_account.set_markup('<b>{}</b>'.format(new_account))
1523+
1524+ def _setup_listbox_accounts(self):
1525+ for obj in self.goa_client.get_accounts():
1526+ account = obj.get_account()
1527+ if self._is_account_supported(account):
1528+ self._add_account(account)
1529+
1530+ def _is_account_supported(self, account):
1531+ return account.props.provider_type == 'ubuntusso'
1532+
1533+ def _add_account(self, account):
1534+ row = self._create_row(account)
1535+ self.listbox_accounts.prepend(row)
1536+ self._check_ui()
1537+
1538+ def _remove_account(self, account):
1539+ for row in self.listbox_accounts.get_children():
1540+ if row.account == account:
1541+ row.destroy()
1542+ self._check_ui()
1543+ break
1544+
1545+ def _build_dbus_params(self, action, arg):
1546+ builder = GLib.VariantBuilder.new(GLib.VariantType.new('av'))
1547+
1548+ if action is None and arg is None:
1549+ s = GLib.Variant.new_string('')
1550+ v = GLib.Variant.new_variant(s)
1551+ builder.add_value(v)
1552+ else:
1553+ if action is not None:
1554+ s = GLib.Variant.new_string(action)
1555+ v = GLib.Variant.new_variant(s)
1556+ builder.add_value(v)
1557+ if arg is not None:
1558+ s = GLib.Variant.new_string(arg)
1559+ v = GLib.Variant.new_variant(s)
1560+ builder.add_value(v)
1561+
1562+ array = GLib.Variant.new_tuple(GLib.Variant.new_string('online-accounts'), builder.end())
1563+ array = GLib.Variant.new_variant(array)
1564+
1565+ param = GLib.Variant.new_tuple(
1566+ GLib.Variant.new_string('launch-panel'),
1567+ GLib.Variant.new_array(GLib.VariantType.new('v'), [array]),
1568+ GLib.Variant.new_array(GLib.VariantType.new('{sv}'), None))
1569+ return param
1570+
1571+ def _spawn_goa_with_args(self, action, arg):
1572+ proxy = Gio.DBusProxy.new_for_bus_sync(Gio.BusType.SESSION,
1573+ Gio.DBusProxyFlags.NONE, None,
1574+ 'org.gnome.ControlCenter',
1575+ '/org/gnome/ControlCenter',
1576+ 'org.gtk.Actions', None)
1577+
1578+ param = self._build_dbus_params(action, arg)
1579+ timeout = 10*60*1000 # 10 minutes should be enough to create an account
1580+ proxy.call_sync('Activate', param, Gio.DBusCallFlags.NONE, timeout, None)
1581+
1582+ def _create_row(self, account):
1583+ identity = account.props.presentation_identity
1584+ provider_name = account.props.provider_name
1585+
1586+ row = Gtk.ListBoxRow.new()
1587+ row.show()
1588+
1589+ hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 6)
1590+ hbox.set_hexpand(True)
1591+ hbox.show()
1592+
1593+ image = Gtk.Image.new_from_icon_name('avatar-default', Gtk.IconSize.DIALOG)
1594+ image.set_pixel_size(48)
1595+ image.show()
1596+ hbox.pack_start(image, False, False, 0)
1597+
1598+ vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 2)
1599+ vbox.set_valign(Gtk.Align.CENTER)
1600+ vbox.show()
1601+ hbox.pack_start(vbox, False, False, 0)
1602+
1603+ if identity:
1604+ ilabel = Gtk.Label.new()
1605+ ilabel.set_halign(Gtk.Align.START)
1606+ ilabel.set_markup('<b>{}</b>'.format(identity))
1607+ ilabel.show()
1608+ vbox.pack_start(ilabel, True, True, 0)
1609+
1610+ if provider_name:
1611+ plabel = Gtk.Label.new()
1612+ plabel.set_halign(Gtk.Align.START)
1613+ plabel.set_markup('<small><span foreground=\"#555555\">{}</span></small>'.format(provider_name))
1614+ plabel.show()
1615+ vbox.pack_start(plabel, True, True, 0)
1616+
1617+ warning_icon = Gtk.Image.new_from_icon_name('dialog-warning-symbolic', Gtk.IconSize.BUTTON)
1618+ warning_icon.set_no_show_all(True)
1619+ warning_icon.set_margin_end (15)
1620+ hbox.pack_end(warning_icon, False, False, 0)
1621+
1622+ row.add(hbox)
1623+
1624+ account.bind_property('attention-needed', warning_icon, 'visible',
1625+ GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE)
1626+
1627+ row.account = account
1628+ return row
1629+
1630+ # Signals handlers
1631+ def _listbox_accounts_row_activated_cb(self, listbox, row):
1632+ account = row.account
1633+
1634+ if account is None:
1635+ # TODO (azzar1): there is no easy way to put this to false
1636+ # if the user close the windows without adding an account.
1637+ # We need to discuss with goa's upstream to support such usercases
1638+ try:
1639+ self._spawn_goa_with_args('add', 'ubuntusso')
1640+ self.dispose_on_new_account = True
1641+ except GLib.Error as e:
1642+ logging.warning ('Failed to spawing gnome-control-center: %s', e.message)
1643+ else:
1644+ if account.props.attention_needed:
1645+ try:
1646+ self._spawn_goa_with_args(account.props.id, None)
1647+ except GLib.Error as e:
1648+ logging.warning ('Failed to spawing gnome-control-center: %s', e.message)
1649+ else:
1650+ self.account = account
1651+ self.dialog.response(Gtk.ResponseType.OK)
1652+
1653+ def _account_added_cb(self, goa_client, goa_object):
1654+ account = goa_object.get_account()
1655+ if not self._is_account_supported(account):
1656+ return
1657+ if not self.dispose_on_new_account:
1658+ self._add_account(account)
1659+ else:
1660+ self.account = account
1661+ self.dialog.response(Gtk.ResponseType.OK)
1662+
1663+ def _account_removed_cb(self, goa_client, goa_object):
1664+ account = goa_object.get_account()
1665+ if self._is_account_supported(account):
1666+ self._remove_account(account)
1667+
1668+
1669+
1670
1671=== added file 'softwareproperties/gtk/DialogLivepatchError.py'
1672--- softwareproperties/gtk/DialogLivepatchError.py 1970-01-01 00:00:00 +0000
1673+++ softwareproperties/gtk/DialogLivepatchError.py 2018-03-21 15:53:10 +0000
1674@@ -0,0 +1,57 @@
1675+#
1676+# Copyright (c) 2017-2018 Canonical
1677+#
1678+# Authors:
1679+# Andrea Azzarone <andrea.azzarone@canonical.com>
1680+#
1681+# This program is free software; you can redistribute it and/or
1682+# modify it under the terms of the GNU General Public License as
1683+# published by the Free Software Foundation; either version 2 of the
1684+# License, or (at your option) any later version.
1685+#
1686+# This program is distributed in the hope that it will be useful,
1687+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1688+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1689+# GNU General Public License for more details.
1690+#
1691+# You should have received a copy of the GNU General Public License
1692+# along with this program; if not, write to the Free Software
1693+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
1694+# USA
1695+
1696+import os
1697+
1698+from softwareproperties.gtk.utils import (
1699+ setup_ui,
1700+)
1701+
1702+
1703+class DialogLivepatchError:
1704+
1705+ RESPONSE_SETTINGS = 100
1706+ RESPONSE_IGNORE = 101
1707+
1708+ def __init__(self, parent, datadir):
1709+ """setup up the gtk dialog"""
1710+ self.parent = parent
1711+
1712+ setup_ui(self, os.path.join(datadir, "gtkbuilder",
1713+ "dialog-livepatch-error.ui"), domain="software-properties")
1714+
1715+ self.dialog = self.messagedialog_livepatch
1716+ self.dialog.use_header_bar = True
1717+ self.dialog.set_transient_for(parent)
1718+
1719+ def run(self, error, show_settings_button):
1720+ self.dialog.format_secondary_markup(
1721+ "The error was: \"%s\"" % error.strip())
1722+ self.button_settings.set_visible(show_settings_button)
1723+ res = self.dialog.run()
1724+ self.dialog.hide()
1725+ return res
1726+
1727+ def on_button_settings_clicked(self, b, d=None):
1728+ self.dialog.response(self.RESPONSE_SETTINGS)
1729+
1730+ def on_button_ignore_clicked(self, b, d=None):
1731+ self.dialog.response(self.RESPONSE_IGNORE)
1732
1733=== modified file 'softwareproperties/gtk/SoftwarePropertiesGtk.py'
1734--- softwareproperties/gtk/SoftwarePropertiesGtk.py 2018-03-06 15:47:27 +0000
1735+++ softwareproperties/gtk/SoftwarePropertiesGtk.py 2018-03-21 15:53:10 +0000
1736@@ -1,11 +1,12 @@
1737 # GTK+ based frontend to software-properties
1738 #
1739-# Copyright (c) 2004-2007 Canonical Ltd.
1740+# Copyright (c) 2004-2018 Canonical Ltd.
1741 # 2004-2005 Michiel Sikkes
1742 #
1743 # Author: Michiel Sikkes <michiel@eyesopened.nl>
1744 # Michael Vogt <mvo@debian.org>
1745 # Sebastian Heinlein <glatzor@ubuntu.com>
1746+# Andrea Azzarone <andrea.azzarone@canonical.com>
1747 #
1748 # This program is free software; you can redistribute it and/or
1749 # modify it under the terms of the GNU General Public License as
1750@@ -26,6 +27,9 @@
1751
1752 import apt
1753 import apt_pkg
1754+import aptsources.distro
1755+from datetime import datetime
1756+import distro_info
1757 import dbus
1758 from gettext import gettext as _
1759 import gettext
1760@@ -48,8 +52,11 @@
1761 from .DialogEdit import DialogEdit
1762 from .DialogCacheOutdated import DialogCacheOutdated
1763 from .DialogAddSourcesList import DialogAddSourcesList
1764+from .DialogLivepatchError import DialogLivepatchError
1765+from .DialogAuth import DialogAuth
1766
1767 import softwareproperties
1768+from softwareproperties.GoaAuth import GoaAuth
1769 import softwareproperties.distro
1770 from softwareproperties.SoftwareProperties import SoftwareProperties
1771 import softwareproperties.SoftwareProperties
1772@@ -78,6 +85,8 @@
1773 STORE_VISIBLE
1774 ) = list(range(5))
1775
1776+LIVEPATCH_TIMEOUT = 1200
1777+
1778
1779 def error(parent_window, summary, msg):
1780 """ show a error dialog """
1781@@ -182,6 +191,8 @@
1782 self.show_distro()
1783 # Setup and show the Additonal Drivers tab
1784 self.init_drivers()
1785+ # Setup and show the LivePatch tab
1786+ self.init_livepatch()
1787
1788 # Connect to switch-page before setting initial tab. Otherwise the
1789 # first switch goes unnoticed.
1790@@ -283,7 +294,7 @@
1791
1792 def set_security_update_level(self):
1793 """Fetch the security level, Enable/Disable and set the value appropriately"""
1794-
1795+
1796 # Security Updates
1797 level_sec = self.get_update_automation_level()
1798 if level_sec == None:
1799@@ -298,7 +309,7 @@
1800 self.combobox_security_updates.set_active(1) # Download automatically
1801 elif level_sec == softwareproperties.UPDATE_INST_SEC:
1802 self.combobox_security_updates.set_active(2) # Download and install automatically
1803-
1804+
1805
1806 def init_distro(self):
1807 """Setup the user interface elements to represent the distro"""
1808@@ -493,7 +504,7 @@
1809 except dbus.DBusException as e:
1810 if e._dbus_error_name == 'com.ubuntu.SoftwareProperties.PermissionDeniedByPolicy':
1811 logging.error("Authentication canceled, changes have not been saved")
1812-
1813+
1814 combo_handler = self.handlers[self.combobox_security_updates]
1815 self.combobox_security_updates.handler_block(combo_handler)
1816 self.set_security_update_level()
1817@@ -520,13 +531,13 @@
1818 except dbus.DBusException as e:
1819 if e._dbus_error_name == 'com.ubuntu.SoftwareProperties.PermissionDeniedByPolicy':
1820 logging.error("Authentication canceled, changes have not been saved")
1821-
1822+
1823 combo_handler = self.handlers[self.combobox_release_upgrades]
1824 self.combobox_release_upgrades.handler_block(combo_handler)
1825 i = self.get_release_upgrades_policy()
1826 self.combobox_release_upgrades.set_active(i)
1827 self.combobox_release_upgrades.handler_unblock(combo_handler)
1828-
1829+
1830
1831 def on_combobox_server_changed(self, combobox):
1832 """
1833@@ -880,7 +891,7 @@
1834 except dbus.DBusException as e:
1835 if e._dbus_error_name == 'com.ubuntu.SoftwareProperties.PermissionDeniedByPolicy':
1836 logging.error("Authentication canceled, changes have not been saved")
1837-
1838+
1839 update_days = self.get_update_interval()
1840 combo_handler = self.handlers[self.combobox_update_interval]
1841 for key in self.combobox_interval_mapping:
1842@@ -1012,6 +1023,7 @@
1843 def on_delete_event(self, widget, args):
1844 """Close the window if requested"""
1845 self.on_close_button(widget)
1846+ return self.quit_when_livepatch_responds
1847
1848 def on_close_button(self, widget):
1849 """Show a dialog that a reload of the channel information is required
1850@@ -1021,7 +1033,11 @@
1851 d = DialogCacheOutdated(self.window_main,
1852 self.datadir)
1853 d.run()
1854- self.quit()
1855+ if self.waiting_livepatch_response:
1856+ self.quit_when_livepatch_responds = True
1857+ self.hide()
1858+ else:
1859+ self.quit()
1860
1861 def on_button_add_cdrom_clicked(self, widget):
1862 """ when a cdrom is requested for adding """
1863@@ -1446,3 +1462,145 @@
1864 % {'count': self.nonfree_drivers})
1865 else:
1866 self.label_driver_action.set_label(_("No proprietary drivers are in use."))
1867+
1868+ #
1869+ # Livepatch
1870+ #
1871+ def init_livepatch(self):
1872+ self.goa_auth = GoaAuth()
1873+ self.waiting_livepatch_response = False
1874+ self.quit_when_livepatch_responds = False
1875+
1876+ if not self.is_livepatch_supported():
1877+ self.grid_livepatch.set_visible(False)
1878+ return
1879+
1880+ self.checkbutton_livepatch.set_active(self.is_livepatch_enabled())
1881+ self.on_goa_auth_changed()
1882+
1883+ # hacky way to monitor if livepatch is enabled or not
1884+ file = Gio.File.new_for_path(path=self.LIVEPATCH_RUNNING_FILE)
1885+ self.lp_monitor = file.monitor_file(Gio.FileMonitorFlags.NONE)
1886+
1887+ # connect to signals
1888+ self.handlers[self.goa_auth] = \
1889+ self.goa_auth.connect('notify::logged', lambda o, p: self.on_goa_auth_changed())
1890+ self.handlers[self.checkbutton_livepatch] = \
1891+ self.checkbutton_livepatch.connect('toggled', self.on_checkbutton_livepatch_toggled)
1892+ self.handlers[self.button_ubuntuone] = \
1893+ self.button_ubuntuone.connect('clicked', self.on_button_ubuntuone_clicked)
1894+ self.handlers[self.lp_monitor] = \
1895+ self.lp_monitor.connect('changed', self.on_livepatch_status_changed)
1896+
1897+ def is_livepatch_supported(self):
1898+ distro = aptsources.distro.get_distro()
1899+ di = distro_info.UbuntuDistroInfo()
1900+ return di.is_lts(distro.codename) and distro.codename in di.supported(datetime.now().date())
1901+
1902+ def on_goa_auth_changed(self):
1903+ if self.goa_auth.logged:
1904+ self.button_ubuntuone.set_label(_('Sign Out'))
1905+
1906+ if self.goa_auth.token:
1907+ self.checkbutton_livepatch.set_sensitive(True)
1908+ self.label_livepatch_login.set_label(_('Signed in as %s' % self.goa_auth.username))
1909+ else:
1910+ self.checkbutton_livepatch.set_sensitive(False)
1911+ text = _('%s isn\'t authorized to use Livepatch.' % self.goa_auth.username)
1912+ text = "<span color='red'>" + text + "</span>"
1913+ self.label_livepatch_login.set_markup(text)
1914+ else:
1915+ self.checkbutton_livepatch.set_sensitive(False)
1916+ self.button_ubuntuone.set_label(_('Sign In…'))
1917+ self.label_livepatch_login.set_label(_('To use Livepatch you need to sign in.'))
1918+
1919+ def on_livepatch_status_changed(self, file_monitor, file, other_file, event_type):
1920+ if not self.waiting_livepatch_response:
1921+ self.checkbutton_livepatch.set_active(self.is_livepatch_enabled())
1922+
1923+ def on_button_ubuntuone_clicked(self, button):
1924+ if self.goa_auth.logged:
1925+ self.do_logout()
1926+ else:
1927+ self.do_login()
1928+
1929+ def do_login(self):
1930+ try:
1931+ # Show login dialog!
1932+ dialog = DialogAuth(self.window_main, self.datadir)
1933+ response = dialog.run()
1934+ except Exception as e:
1935+ logging.error(e)
1936+ error(self.window_main,
1937+ _("Error enabling Canonical Livepatch"),
1938+ _("Please check your Internet connection."))
1939+ else:
1940+ if response == Gtk.ResponseType.OK:
1941+ self.goa_auth.login(dialog.account)
1942+ if self.goa_auth.logged:
1943+ self.checkbutton_livepatch.set_active(True)
1944+
1945+ def do_logout(self):
1946+ self.goa_auth.logout()
1947+ self.checkbutton_livepatch.set_active(False)
1948+
1949+ def on_checkbutton_livepatch_toggled(self, checkbutton):
1950+ if self.waiting_livepatch_response:
1951+ return
1952+
1953+ self.waiting_livepatch_response = True
1954+
1955+ token = ''
1956+ enabled = False
1957+ if self.checkbutton_livepatch.get_active():
1958+ enabled = True
1959+ token = self.goa_auth.token if self.goa_auth.token else ''
1960+ self.backend.SetLivepatchEnabled(enabled, token,
1961+ reply_handler=self.livepatch_enabled_reply_handler,
1962+ error_handler=self.livepatch_enabled_error_handler,
1963+ timeout=LIVEPATCH_TIMEOUT)
1964+
1965+ def livepatch_enabled_reply_handler(self, is_error, prompt):
1966+ self.sync_checkbutton_livepatch(is_error, prompt)
1967+
1968+ def livepatch_enabled_error_handler(self, e):
1969+ if e._dbus_error_name == 'com.ubuntu.SoftwareProperties.PermissionDeniedByPolicy':
1970+ logging.error("Authentication canceled, changes have not been saved")
1971+ self.sync_checkbutton_livepatch(is_error=True, prompt=None)
1972+ else:
1973+ self.sync_checkbutton_livepatch(is_error=True, prompt=str(e))
1974+
1975+ def sync_checkbutton_livepatch(self, is_error, prompt):
1976+ if is_error:
1977+ self.waiting_livepatch_response = False
1978+ self.checkbutton_livepatch.handler_block(self.handlers[self.checkbutton_livepatch])
1979+ self.checkbutton_livepatch.set_active(self.is_livepatch_enabled())
1980+ self.checkbutton_livepatch.handler_unblock(self.handlers[self.checkbutton_livepatch])
1981+
1982+ if prompt:
1983+ dialog = DialogLivepatchError(self.window_main, self.datadir)
1984+ response = dialog.run(prompt, show_settings_button=self.quit_when_livepatch_responds)
1985+ if response == DialogLivepatchError.RESPONSE_SETTINGS:
1986+ self.window_main.show()
1987+ self.quit_when_livepatch_responds = False
1988+ else:
1989+ do_dbus_call = False
1990+ if self.is_livepatch_enabled() and not self.checkbutton_livepatch.get_active():
1991+ do_dbus_call = True
1992+ enabled = False
1993+ token = ''
1994+ elif not self.is_livepatch_enabled() and self.checkbutton_livepatch.get_active():
1995+ do_dbus_call = True
1996+ enabled = True
1997+ token = self.goa_auth.token if self.goa_auth.token else ''
1998+ else:
1999+ self.waiting_livepatch_response = False
2000+
2001+ if do_dbus_call:
2002+ self.backend.SetLivepatchEnabled(enabled, token,
2003+ reply_handler=self.livepatch_enabled_reply_handler,
2004+ error_handler=self.livepatch_enabled_error_handler,
2005+ timeout=LIVEPATCH_TIMEOUT)
2006+
2007+ if self.quit_when_livepatch_responds:
2008+ self.on_close_button(self.button_close)

Subscribers

People subscribed via source and target branches

to status/vote changes: