Merge ~nteodosio/+git/ubuntu-pro:ubuntupro42.0 into ~nteodosio/+git/ubuntu-pro:ubuntu/42.0.1-1ubuntu2-debian-patches-applied

Proposed by Nathan Teodosio
Status: Merged
Merge reported by: Nathan Teodosio
Merged at revision: 61fdffb080766beac8c60d9c067cbd7861db509e
Proposed branch: ~nteodosio/+git/ubuntu-pro:ubuntupro42.0
Merge into: ~nteodosio/+git/ubuntu-pro:ubuntu/42.0.1-1ubuntu2-debian-patches-applied
Diff against target: 3560 lines (+1673/-32)
22 files modified
data/meson.build (+0/-9)
dev/null (+0/-12)
gnome-initial-setup/gis-assistant.c (+8/-1)
gnome-initial-setup/gis-page.c (+15/-0)
gnome-initial-setup/gis-page.h (+2/-0)
gnome-initial-setup/gnome-initial-setup.c (+3/-3)
gnome-initial-setup/pages/meson.build (+1/-1)
gnome-initial-setup/pages/ubuntu-pro/checkmark.svg (+1/-0)
gnome-initial-setup/pages/ubuntu-pro/fail.svg (+1/-0)
gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-attach-page.ui (+214/-0)
gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-offer-page.ui (+165/-0)
gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page.c (+804/-0)
gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page.h (+110/-0)
gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page.ui (+39/-0)
gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-services-page.ui (+124/-0)
gnome-initial-setup/pages/ubuntu-pro/meson.build (+12/-0)
gnome-initial-setup/pages/ubuntu-pro/ubuntu-pro-dark.svg (+25/-0)
gnome-initial-setup/pages/ubuntu-pro/ubuntu-pro.svg (+1/-0)
gnome-initial-setup/pages/ubuntu-pro/ubuntupro.gresource.xml (+14/-0)
gnome-initial-setup/pages/ubuntu-pro/utils.c (+123/-0)
gnome-initial-setup/pages/ubuntu-pro/utils.h (+7/-0)
po/POTFILES.in (+4/-6)
Reviewer Review Type Date Requested Status
Sebastien Bacher (community) Approve
Sergio Costas Pending
Robert Ancell Pending
Review via email: mp+434628@code.launchpad.net

Description of the change

This has 42.0.1-1ubuntu2 with all debian/patches applied as its base.

This has dependencies right for Jammy (tested in a virtual machine).

Have Polkit running if you don't: /usr/lib/policykit-1-gnomeY/polkit-gnome-authentication-agent-1.

I use this script to test:

---->
#!/bin/sh
if ! [ -d build ]; then
    mkdir build
    meson build
fi
ninja -C build || exit 1
if [ "$1" = debug ]; then
    XDG_CURRENT_DESKTOP=ubuntu gdb -ex run --args ./build/gnome-initial-setup/gnome-initial-setup --existing-user
elif [ "$1" = harddebug ]; then
    G_DEBUG=fatal-warnings GTK_THEME=Adwaita XDG_CURRENT_DESKTOP=ubuntu gdb -ex run --args ./build/gnome-initial-setup/gnome-initial-setup --existing-user
else
    XDG_CURRENT_DESKTOP=ubuntu ./build/gnome-initial-setup/gnome-initial-setup --existing-user
fi
<----

To post a comment you must log in.
Revision history for this message
Nathan Teodosio (nteodosio) wrote (last edit ):

Copying Robert's comment from superseded review https://code.launchpad.net/~nteodosio/+git/ubuntu-pro/+merge/434494:

"""
I'm very confused as to what this PR is doing - it's proposing merging two branches you control, not merging into the gnome-initial-setup branch that matches a released version. The information below also shows merge conflicts, so it seems broken.

I would recommend that you squash all the merges together if this is a single change (you can make a backup branch containing all your commits if you want to refer to them later). It is much harder to review with all these commits.

Just looking at you ubuntupro branch and the files in gnome-initial-setup/pages/ubuntu-pro/:
- It would be easier to read if the three pages were in three .c files.
- The pages would be clearer if they had names that related to what they did, i.e. "gs-ubuntupro-page-1" -> "gs-ubuntupro-services-page"
- The .ui files contain unused id properties.
- The .ui files contain unused position properties (these make future changes more complicated).
- RestJSONResponse is only used inside the .c files, but is defined in the header - if this is private it doesn't need to be exposed like this.
- display_ua_services() should use g_autofree/g_autoptr for variables. This will allow you to remove the goto and just return directly from the function.
- Lots of other functions don't use g_autofree/g_autoptr.
- _GisUbuntuProPage1Private contains unused cancellable, apply_complete_callback, apply_complete_data fields.
- contractToken should be called contract_token.
- You store the PIN in a variable and then set it in the GtkLabel - this is unnecessary and would be simpler just to store in the GtkLabel directly. Otherwise you have the same data in two places (which could get out of sync).
- There's no dispose methods for the page classes, so they leak data.
- You've used 543210 as a timeout, if you want no timeout use G_MAXINT.
- The labels set in update_gui() aren't translatable.
- I haven't been able to get it to compile here or seen the designs so I haven't been able to review that.
"""

Revision history for this message
Nathan Teodosio (nteodosio) wrote :

> I'm very confused as to what this PR is doing - it's proposing merging two branches you control, not merging into the gnome-initial-setup branch that matches a released version. The information below also shows merge conflicts, so it seems broken.

This was my mistake, I targeted the wrong gnome-initial-setup version, now it's correctly rebased against Jammy's version 42.0.1-1ubuntu2.

5a0ed85... by Nathan Teodosio

UI: Remove unused ID and position properties.

5918804... by Nathan Teodosio

RestJSONResponse moved out of header file.

a785fc4... by Nathan Teodosio

Use g_autofree/g_autoptr.

3d98ca2... by Nathan Teodosio

Remove unused values from Page1Private.

1626906... by Nathan Teodosio

contractToken -> contract_token.

7945f63... by Nathan Teodosio

Don't store pin in a variable, use it directly to update GTK label.

2311da4... by Nathan Teodosio

page{1,2,3} -> {offer,attach,list_services}

Revision history for this message
Nathan Teodosio (nteodosio) wrote (last edit ):

> There's no dispose methods for the page classes, so they leak data.

So those are sort of destructors, right? Are they supposed to look like this[1]? Documentation is a bit too laconic[2]. The print statements didn't trigger when I passed the pages nor when I killed the program. I suppose they would only be triggered at program termination, since GIS allows one to go back to a previous page?

[1]: https://git.launchpad.net/~nteodosio/+git/ubuntu-pro/commit/?id=67340fcdc482e7bf89c1dc644503ca3fb7f403a6
[2]: https://docs.gtk.org/gobject/vfunc.Object.dispose.html

f405e6a... by Nathan Teodosio

Translatable strings in attach labels.

Revision history for this message
Nathan Teodosio (nteodosio) :
6b60922... by Nathan Teodosio

Introduce back button, hijack back and next actions.

6859cf1... by Nathan Teodosio

Rename: list.?services->services

cf74e20... by Nathan Teodosio

Add some comments here and there.

65de5a9... by Nathan Teodosio

Introduce utils.[ch].

28b4b04... by Nathan Teodosio

Remove set_page_completion function.

c8c8ddf... by Nathan Teodosio

Delegate page 1->2 transition to update_gui.

Avoids minor corner case whereby coming back to page 2
with an non-empty token field would have an unsensitive
Next button.

5cd7136... by Nathan Teodosio

Use Ubuntu Pro API.

This way we don't have to hard code the contracts URL.

3c6d262... by Nathan Teodosio

Only attempt attachment after clicking "Next"

Including after receiving contract_token after submitting PIN.

3d4b6e8... by Nathan Teodosio

Increase margin between PIN and status icon.

96aea7b... by Nathan Teodosio

Add dark theme detector routine.

cc5096e... by Nathan Teodosio

Use different Ubuntu SVG if dark theme in use.

While we are at it, remove display_checkmark function in favor of
the more generic map_scalable_image.

8ba928a... by Nathan Teodosio

Add placeholder ubuntu-pro-dark.svg

Revision history for this message
Sebastien Bacher (seb128) wrote :

Thanks. It's not a complete review just some notes as I got that version in the ubuntu-desktop ppa for testing

- the ubuntu-pro.svg is actually a png, we either need to convert it or rename, would be easier to handle as a svg
- we need to remove the livepatch page as well (it's currently being displayed in addition to the pro page when not using the --existing-user option)
- the services page doesn't have bullet point at the start of the lines for the services

and the current layout is better for review but we will need to include the changes in the ubuntu patch once we are ready

88a716b... by Nathan Teodosio

Add bullet points to services.

4329757... by Nathan Teodosio

Nuke livepatch.

Revision history for this message
Nathan Teodosio (nteodosio) wrote :

- ubuntu-pro.svg was indeed a PNG at some point, but now it is should be a SVG. Perhaps you had an older version?
- done.
- done.

Revision history for this message
Sergio Costas (rastersoft-gmail) wrote :

Added some comments.

4728536... by Nathan Teodosio

Make current_page an enum and use switches instead of ifs.

4b3d2d8... by Nathan Teodosio

g_print->g_warning for reporting unexpected status.

adaf545... by Nathan Teodosio

Remove make_rest_req.

No longer needed after migrating to Pro API.

Revision history for this message
Nathan Teodosio (nteodosio) wrote :

Thanks for the review Sergio!

5cc7993... by Nathan Teodosio

Implement dispose function and free tokens therein.

5fc5a92... by Nathan Teodosio

Disconnect network signal handler in destructor.

Revision history for this message
Sergio Costas (rastersoft-gmail) wrote :

I think that now I did explain it better.

Revision history for this message
Sergio Costas (rastersoft-gmail) wrote :

Ops... didn't see the last patch.

Revision history for this message
Sergio Costas (rastersoft-gmail) wrote :

Added an extra comment about disposing the objects.

6777ad7... by Nathan Teodosio

Renaming only

0cd8cd6... by Nathan Teodosio

Free token upon successful attachment.

Revision history for this message
Sebastien Bacher (seb128) wrote :

I've pushed the current version in the ppa and tested it, small notes

- indeed the svg icon is right now

- the magic token spinner is vertically aligned to the top of the pin, and same for the 'token valid' checkmark and label, could we align them with the middle instead?

- using the magic token workflow, after entering the token on the website and clicking 'next' it prompts for the password/spin for a while (expected) but then we need to click next again. Could we make it so it does the next action by itself at the end of the registration?

review: Needs Fixing
Revision history for this message
Nathan Teodosio (nteodosio) wrote (last edit ):

> Could we make it so it does the next action by itself at the end of the registration?

That means go to the next page, correct? So we'd also want that for the
manual token? I.e.,

- [magic] type pin in browser > click next > prompt > (if success) go to
next page immediately

- [token] type token > Next(or Enter) > prompt > (if success) go to next page
immediately

Revision history for this message
Sebastien Bacher (seb128) wrote :

yes, I was testing the magic workflow but the same principle is true for the manual token

b324c7f... by Nathan Teodosio

Skip to the next page directly once attachment is complete...

If the user is in the attachment page.

48d183a... by Nathan Teodosio

Adjust margins of PIN

Revision history for this message
Nathan Teodosio (nteodosio) wrote :

That is done. About

> the magic token spinner is vertically aligned to the top of the pin, and same for the 'token valid' checkmark and label, could we align them with the middle instead?

As discussed, it is centered for me in Yaru and Adwaita. Glade displays it centered too.

There was a slight difference between the PIN's top and bottom margin, which are now equal. I doubt that would account for it though...

Revision history for this message
Sebastien Bacher (seb128) wrote :

It now moves to the next page as expected and it feels more centered in the VM with the margin change. Adding one typo in a comment to fix but functionally it works as expected. I'm going to do another review on the code later before doing the ack but I think it feels ready now

e652065... by Nathan Teodosio

Typo

b5e987e... by Nathan Teodosio

Update the list of files to translate.

Patch by Sebastien Bacher.

Revision history for this message
Sebastien Bacher (seb128) wrote :

Great work Nathan, I think it's ready for upload! We need to retro-fit the changes into debian/patches now, probably as an update to 0001-Add-Ubuntu-mode-with-special-pages.patch

review: Approve
Revision history for this message
Sergio Costas (rastersoft-gmail) wrote :

Please, wait. I think that there are still some little changes that should be done, referred to the dispose.

Revision history for this message
Sebastien Bacher (seb128) wrote :

Ack, we are not going to merge before next week at this point so we can go through another review round today and on Monday

3346f62... by Nathan Teodosio

UI: Padding

Revision history for this message
Robert Ancell (robert-ancell) wrote :

The diff below shows some merge conflicts, i.e. '+<<<<<<< data/meson.build' - does this branch need updating?

Revision history for this message
Nathan Teodosio (nteodosio) wrote :

Those are spurious, I don't know why Launchpad calculates them.

Am 2023/01/17 um 00:20 schrieb Robert Ancell:
> The diff below shows some merge conflicts, i.e. '+<<<<<<< data/meson.build' - does this branch need updating?

Revision history for this message
Sergio Costas (rastersoft-gmail) wrote :

Sometimes git does odd things when doing a manual rebase... usually those odd things are fixed just by doing a "git rebase master" (or main, or the base branch from which you did the current branch).

04028f2... by Nathan Teodosio

Replace dummy by actual dark logo SVG.

Revision history for this message
Nathan Teodosio (nteodosio) wrote :

Thanks Sergio, that did get rid of the conflicts.

007b430... by Nathan Teodosio

Indentation and margins for radio buttons.

61fdffb... by Nathan Teodosio

Remove leading spaces from radio buttons.

Revision history for this message
Robert Ancell (robert-ancell) wrote :

Added some more comments inline.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/data/com.ubuntu.welcome.policy.in b/data/com.ubuntu.welcome.policy.in
2deleted file mode 100644
3index cf623d5..0000000
4--- a/data/com.ubuntu.welcome.policy.in
5+++ /dev/null
6@@ -1,21 +0,0 @@
7-<?xml version="1.0" encoding="UTF-8"?>
8-<!DOCTYPE policyconfig PUBLIC
9- "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
10- "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
11-
12-<policyconfig>
13- <vendor>Ubuntu Welcome</vendor>
14- <vendor_url>https://www.ubuntu.com/</vendor_url>
15-
16- <action id="com.ubuntu.welcome.livepatch">
17- <description>Manage Livepatch</description>
18- <message>Authentication is required to enable Livepatch</message>
19- <defaults>
20- <allow_any>no</allow_any>
21- <allow_inactive>no</allow_inactive>
22- <allow_active>auth_admin_keep</allow_active>
23- </defaults>
24- <annotate key="org.freedesktop.policykit.imply">io.snapcraft.snapd.login com.ubuntu.softwareproperties.applychanges </annotate>
25- </action>
26-
27-</policyconfig>
28\ No newline at end of file
29diff --git a/data/meson.build b/data/meson.build
30index 0fbb7d1..36c775a 100644
31--- a/data/meson.build
32+++ b/data/meson.build
33@@ -99,15 +99,6 @@ if enable_systemd
34 )
35 endif
36
37-i18n.merge_file(
38- input: 'com.ubuntu.welcome.policy.in',
39- output: 'com.ubuntu.welcome.policy',
40- install: true,
41- install_dir: join_paths(data_dir, 'polkit-1', 'actions'),
42- po_dir: po_dir,
43- type: 'xml'
44-)
45-
46 rules_dir = join_paths(data_dir, 'polkit-1', 'rules.d')
47 configure_file(
48 input: '20-gnome-initial-setup.rules.in',
49diff --git a/gnome-initial-setup/gis-assistant.c b/gnome-initial-setup/gis-assistant.c
50index b71cd07..af6061f 100644
51--- a/gnome-initial-setup/gis-assistant.c
52+++ b/gnome-initial-setup/gis-assistant.c
53@@ -175,7 +175,8 @@ void
54 gis_assistant_previous_page (GisAssistant *assistant)
55 {
56 g_return_if_fail (assistant->current_page != NULL);
57- switch_to (assistant, find_prev_page (assistant, assistant->current_page));
58+ if (!gis_page_go_back(assistant->current_page))
59+ switch_to (assistant, find_prev_page (assistant, assistant->current_page));
60 }
61
62 static void
63@@ -260,6 +261,12 @@ update_applying_state (GisAssistant *assistant)
64 {
65 applying = gis_page_get_applying (assistant->current_page);
66 is_first_page = assistant->pages->data == assistant->current_page;
67+ /* Hack to prevent the next button from Ubuntu Pro attach page from
68+ * being sensitive when the page is first mapped. */
69+ if (g_str_equal(
70+ GIS_PAGE_GET_CLASS(assistant->current_page)->page_id, "UbuntuPro"
71+ ))
72+ return;
73 }
74 gtk_widget_set_sensitive (assistant->forward, !applying);
75 gtk_widget_set_sensitive (assistant->done, !applying);
76diff --git a/gnome-initial-setup/gis-page.c b/gnome-initial-setup/gis-page.c
77index a6553c3..217b073 100644
78--- a/gnome-initial-setup/gis-page.c
79+++ b/gnome-initial-setup/gis-page.c
80@@ -202,6 +202,12 @@ gis_page_real_apply (GisPage *page,
81 return FALSE;
82 }
83
84+static gboolean
85+gis_page_real_go_back (GisPage *page)
86+{
87+ return FALSE;
88+}
89+
90 static void
91 gis_page_class_init (GisPageClass *klass)
92 {
93@@ -214,6 +220,7 @@ gis_page_class_init (GisPageClass *klass)
94 object_class->set_property = gis_page_set_property;
95
96 klass->apply = gis_page_real_apply;
97+ klass->go_back = gis_page_real_go_back;
98
99 obj_props[PROP_DRIVER] =
100 g_param_spec_object ("driver", "", "", GIS_TYPE_DRIVER,
101@@ -386,6 +393,14 @@ gis_page_apply_begin (GisPage *page,
102 g_object_notify_by_pspec (G_OBJECT (page), obj_props[PROP_APPLYING]);
103 }
104
105+gboolean
106+gis_page_go_back (GisPage *page)
107+{
108+ GisPageClass *klass;
109+ klass = GIS_PAGE_GET_CLASS (page);
110+ return klass->go_back(page);
111+}
112+
113 void
114 gis_page_apply_complete (GisPage *page,
115 gboolean valid)
116diff --git a/gnome-initial-setup/gis-page.h b/gnome-initial-setup/gis-page.h
117index 3d6b25d..03d1ccf 100644
118--- a/gnome-initial-setup/gis-page.h
119+++ b/gnome-initial-setup/gis-page.h
120@@ -57,6 +57,7 @@ struct _GisPageClass
121 void (*locale_changed) (GisPage *page);
122 gboolean (*apply) (GisPage *page,
123 GCancellable *cancellable);
124+ gboolean (*go_back) (GisPage *page);
125 gboolean (*save_data) (GisPage *page,
126 GError **error);
127 void (*shown) (GisPage *page);
128@@ -81,6 +82,7 @@ void gis_page_locale_changed (GisPage *page);
129 void gis_page_apply_begin (GisPage *page, GisPageApplyCallback callback, gpointer user_data);
130 void gis_page_apply_cancel (GisPage *page);
131 void gis_page_apply_complete (GisPage *page, gboolean valid);
132+gboolean gis_page_go_back (GisPage *page);
133 gboolean gis_page_get_applying (GisPage *page);
134 gboolean gis_page_save_data (GisPage *page,
135 GError **error);
136diff --git a/gnome-initial-setup/gnome-initial-setup.c b/gnome-initial-setup/gnome-initial-setup.c
137index 4fe6a55..00a2cfb 100644
138--- a/gnome-initial-setup/gnome-initial-setup.c
139+++ b/gnome-initial-setup/gnome-initial-setup.c
140@@ -46,7 +46,7 @@
141 #include "pages/password/gis-password-page.h"
142 #include "pages/summary/gis-summary-page.h"
143 #include "pages/ubuntu-report/gis-ubuntu-report-page.h"
144-#include "pages/livepatch/gis-livepatch-page.h"
145+#include "pages/ubuntu-pro/gis-ubuntupro-page.h"
146 #include "pages/apps/gis-apps-page.h"
147
148 #define VENDOR_PAGES_GROUP "pages"
149@@ -89,7 +89,7 @@ static PageData page_table[] = {
150
151 static PageData ubuntu_page_table[] = {
152 PAGE (goa, FALSE),
153- PAGE (livepatch, FALSE),
154+ PAGE (ubuntu_pro, FALSE),
155 PAGE (ubuntu_report, FALSE),
156 PAGE (privacy, FALSE),
157 PAGE (account, TRUE),
158@@ -99,8 +99,8 @@ static PageData ubuntu_page_table[] = {
159 };
160
161 static PageData unity_page_table[] = {
162+ PAGE (ubuntu_pro, FALSE),
163 PAGE (goa, FALSE),
164- PAGE (livepatch, FALSE),
165 PAGE (ubuntu_report, FALSE),
166 PAGE (account, TRUE),
167 PAGE (password, TRUE),
168diff --git a/gnome-initial-setup/pages/livepatch/gis-auth-dialog.c b/gnome-initial-setup/pages/livepatch/gis-auth-dialog.c
169deleted file mode 100644
170index eceb26e..0000000
171--- a/gnome-initial-setup/pages/livepatch/gis-auth-dialog.c
172+++ /dev/null
173@@ -1,528 +0,0 @@
174-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
175-/*
176- * Copyright (C) 2018 Canonical Ltd.
177- *
178- * This program is free software; you can redistribute it and/or
179- * modify it under the terms of the GNU General Public License as
180- * published by the Free Software Foundation; either version 2 of the
181- * License, or (at your option) any later version.
182- *
183- * This program is distributed in the hope that it will be useful, but
184- * WITHOUT ANY WARRANTY; without even the implied warranty of
185- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
186- * General Public License for more details.
187- *
188- * You should have received a copy of the GNU General Public License
189- * along with this program; if not, see <http://www.gnu.org/licenses/>.
190- *
191- * Written by:
192- * Andrea Azzarone <andrea.azzarone@canonical.com>
193- */
194-
195-#include "gis-auth-dialog.h"
196-
197-#include <glib/gi18n.h>
198-#define GOA_API_IS_SUBJECT_TO_CHANGE
199-#include <goa/goa.h>
200-#define GOA_BACKEND_API_IS_SUBJECT_TO_CHANGE
201-#include <goabackend/goabackend.h>
202-
203-struct _GisAuthDialog
204-{
205- GtkDialog parent_instance;
206-
207- GoaClient *goa_client;
208- GtkListStore *liststore_account;
209-
210- GtkWidget *error_bar;
211- GtkWidget *label_error;
212- GtkWidget *label_header;
213- GtkWidget *combobox_account;
214- GtkWidget *label_account;
215- GtkWidget *button_add_another;
216- GtkWidget *button_cancel;
217- GtkWidget *button_continue;
218-
219- GCancellable *cancellable;
220- guint error_timeout;
221-};
222-
223-static void gis_auth_dialog_initable_iface_init (GInitableIface *iface);
224-
225-G_DEFINE_TYPE_WITH_CODE (GisAuthDialog, gis_auth_dialog, GTK_TYPE_DIALOG,
226- G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gis_auth_dialog_initable_iface_init))
227-
228-enum {
229- COLUMN_ID,
230- COLUMN_EMAIL,
231- COLUMN_ACCOUNT,
232- N_COLUMNS
233-};
234-
235-static gboolean
236-gis_auth_dialog_ignore_account (GoaAccount *account)
237-{
238- return g_strcmp0 (goa_account_get_provider_type (account), "ubuntusso") != 0;
239-}
240-
241-static void gis_auth_dialog_set_error (GisAuthDialog *self,
242- const gchar *text);
243-
244-static gboolean
245-gis_auth_dialog_error_timeout_cb (GisAuthDialog *self)
246-{
247- /* Hide the error bar */
248- gis_auth_dialog_set_error (self, NULL);
249- return G_SOURCE_REMOVE;
250-}
251-
252-static void
253-gis_auth_dialog_set_error (GisAuthDialog *self,
254- const gchar *text)
255-{
256- /* Reset current error timeout */
257- if (self->error_timeout > 0) {
258- g_source_remove (self->error_timeout);
259- self->error_timeout = 0;
260- }
261-
262- if (!text) {
263- /* Hide the error bar if text is NULL */
264- gtk_info_bar_set_revealed (GTK_INFO_BAR (self->error_bar), FALSE);
265- gtk_widget_set_visible (self->error_bar, FALSE);
266- } else {
267- /* Reveal the error bar if text is not NULL */
268- g_autofree gchar *markup = NULL;
269- markup = g_strdup_printf (_("Failed to add an Ubuntu Single Sign-on account: %s."), text);
270- gtk_label_set_markup (GTK_LABEL (self->label_error), markup);
271- gtk_widget_set_visible (self->error_bar, TRUE);
272- gtk_info_bar_set_revealed (GTK_INFO_BAR (self->error_bar), TRUE);
273- self->error_timeout = g_timeout_add_seconds (10, (GSourceFunc) gis_auth_dialog_error_timeout_cb, self);
274- }
275-}
276-
277-static void
278-gis_auth_dialog_set_header (GisAuthDialog *self,
279- const gchar *text)
280-{
281- g_autofree gchar *markup = NULL;
282- markup = g_strdup_printf ("<span size='larger' weight='bold'>%s</span>", text);
283- gtk_label_set_markup (GTK_LABEL (self->label_header), markup);
284-}
285-
286-static gint
287-gis_auth_dialog_get_naccounts (GisAuthDialog *self)
288-{
289- return gtk_tree_model_iter_n_children (GTK_TREE_MODEL (self->liststore_account), NULL);
290-}
291-
292-static gboolean
293-gis_auth_dialog_get_nth_account_data (GisAuthDialog *self,
294- gint n,
295- ...)
296-{
297- GtkTreeIter iter;
298- va_list var_args;
299-
300- if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (self->liststore_account), &iter, NULL, n))
301- return FALSE;
302-
303- va_start (var_args, n);
304- gtk_tree_model_get_valist (GTK_TREE_MODEL (self->liststore_account), &iter, var_args);
305- va_end (var_args);
306-
307- return TRUE;
308-}
309-
310-static void
311-gis_auth_dialog_check_ui (GisAuthDialog *self,
312- gboolean select)
313-{
314- gint naccounts = gis_auth_dialog_get_naccounts (self);
315-
316- if (naccounts == 0) {
317- gis_auth_dialog_set_header (self, _("To use Livepatch, you need to use an Ubuntu One Account."));
318- gtk_widget_set_visible (self->combobox_account, FALSE);
319- gtk_widget_set_visible (self->label_account, FALSE);
320- gtk_widget_set_visible (self->button_add_another, FALSE);
321- gtk_button_set_label (GTK_BUTTON (self->button_continue), _("Sign In / Register…"));
322- } else if (naccounts == 1) {
323- g_autofree gchar *email = NULL;
324-
325- gis_auth_dialog_set_header (self, _("To use Livepatch, you need to use your Ubuntu One Account."));
326- gtk_widget_set_visible (self->combobox_account, FALSE);
327- gtk_widget_set_visible (self->label_account, TRUE);
328- gtk_widget_set_visible (self->button_add_another, TRUE);
329- gtk_button_set_label (GTK_BUTTON (self->button_continue), _("Continue"));
330- gis_auth_dialog_get_nth_account_data (self, 0, COLUMN_EMAIL, &email, -1);
331- gtk_label_set_text (GTK_LABEL (self->label_account), email);
332- } else {
333- gis_auth_dialog_set_header (self, _("To use Livepatch, you need to use an Ubuntu One Account."));
334- gtk_widget_set_visible (self->combobox_account, TRUE);
335- gtk_widget_set_visible (self->label_account, FALSE);
336- gtk_widget_set_visible (self->button_add_another, TRUE);
337- gtk_button_set_label (GTK_BUTTON (self->button_continue), _("Use"));
338-
339- if (select) {
340- gtk_combo_box_set_active (GTK_COMBO_BOX (self->combobox_account), naccounts - 1);
341- } else if (gtk_combo_box_get_active (GTK_COMBO_BOX (self->combobox_account)) == -1) {
342- gtk_combo_box_set_active (GTK_COMBO_BOX (self->combobox_account), 0);
343- }
344- }
345-}
346-
347-static gboolean
348-gis_auth_dialog_get_account_iter (GisAuthDialog *self,
349- GoaAccount *account,
350- GtkTreeIter *iter)
351-{
352- gboolean valid;
353-
354- valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (self->liststore_account), iter, NULL, 0);
355-
356- while (valid) {
357- g_autofree gchar *id;
358- gtk_tree_model_get (GTK_TREE_MODEL (self->liststore_account), iter, COLUMN_ID, &id, -1);
359- if (g_strcmp0 (id, goa_account_get_id (account)) == 0)
360- return TRUE;
361- else
362- valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (self->liststore_account), iter);
363- }
364-
365- return FALSE;
366-}
367-
368-static void
369-gis_auth_dialog_add_account (GisAuthDialog *self,
370- GoaAccount *account,
371- gboolean select)
372-{
373- GtkTreeIter iter;
374-
375- if (gis_auth_dialog_ignore_account (account) ||
376- gis_auth_dialog_get_account_iter (self, account, &iter))
377- return;
378-
379- gtk_list_store_append (self->liststore_account, &iter);
380- gtk_list_store_set (self->liststore_account, &iter,
381- COLUMN_ID, goa_account_get_id (account),
382- COLUMN_EMAIL, goa_account_get_presentation_identity (account),
383- COLUMN_ACCOUNT, account,
384- -1);
385-
386- gis_auth_dialog_check_ui (self, select);
387-}
388-
389-static void
390-gis_auth_dialog_remove_account (GisAuthDialog *self,
391- GoaAccount *account)
392-{
393- GtkTreeIter iter;
394-
395- if (gis_auth_dialog_ignore_account (account) ||
396- !gis_auth_dialog_get_account_iter (self, account, &iter))
397- return;
398-
399- gtk_list_store_remove (self->liststore_account, &iter);
400- gis_auth_dialog_check_ui (self, FALSE);
401-}
402-
403-static void
404-gis_auth_dialog_setup_model (GisAuthDialog *self)
405-{
406- g_autoptr(GList) accounts = goa_client_get_accounts (self->goa_client);
407-
408- for (GList *l = accounts; l != NULL; l = l->next) {
409- gis_auth_dialog_add_account (self, goa_object_peek_account (l->data), FALSE);
410- g_object_unref (l->data);
411- }
412-}
413-
414-static GoaAccount*
415-gis_auth_dialog_show_sso (GisAuthDialog *self,
416- GoaAccount *account)
417-{
418- g_autoptr(GoaProvider) provider = NULL;
419- g_autoptr(GError) error = NULL;
420- GoaObject *goa_object = NULL;
421- GoaAccount *ret = NULL;
422-
423- /* Check if ubuntusso accounts are supported */
424- provider = goa_provider_get_for_provider_type ("ubuntusso");
425- if (provider == NULL) {
426- gis_auth_dialog_set_error (self, _("Ubuntu Single Sign-on accounts are not supported"));
427- goto out;
428- }
429-
430- if (!account) {
431- GtkWidget *dialog = NULL;
432- /* Show the login dialog */
433- dialog = gtk_dialog_new_with_buttons (_("Add Account"),
434- GTK_WINDOW (self),
435- GTK_DIALOG_MODAL
436- | GTK_DIALOG_DESTROY_WITH_PARENT
437- | GTK_DIALOG_USE_HEADER_BAR,
438- NULL, NULL);
439-
440- /* Set dialog to not resize. */
441- gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
442-
443- goa_object = goa_provider_add_account (provider,
444- self->goa_client,
445- GTK_DIALOG (dialog),
446- GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
447- &error);
448-
449- gtk_widget_destroy (dialog);
450- } else {
451- /* Show the refresh dialog */
452- goa_object = goa_client_lookup_by_id (self->goa_client, goa_account_get_id (account));
453- goa_provider_refresh_account (provider,
454- self->goa_client,
455- goa_object,
456- GTK_WINDOW (self),
457- &error);
458- }
459-
460- if (error) {
461- if (!g_error_matches (error, GOA_ERROR, GOA_ERROR_DIALOG_DISMISSED))
462- gis_auth_dialog_set_error (self, error->message);
463- goto out;
464- }
465-
466- ret = goa_object_get_account (goa_object);
467-
468- out:
469- g_clear_object (&goa_object);
470- return ret;
471-}
472-
473-static GoaAccount*
474-gis_auth_dialog_get_selected_account (GisAuthDialog *self)
475-{
476- GoaAccount *goa_account = NULL;
477- gint naccounts = gis_auth_dialog_get_naccounts (self);
478-
479- if (naccounts == 1) {
480- gis_auth_dialog_get_nth_account_data (self, 0, COLUMN_ACCOUNT, &goa_account, -1);
481- } else if (naccounts > 1) {
482- gint active = gtk_combo_box_get_active (GTK_COMBO_BOX (self->combobox_account));
483- gis_auth_dialog_get_nth_account_data (self, active, COLUMN_ACCOUNT, &goa_account, -1);
484- }
485-
486- return goa_account;
487-}
488-
489-void
490-gis_auth_dialog_ensure_crendentials_cb (GObject *source_object,
491- GAsyncResult *res,
492- gpointer user_data)
493-{
494- GisAuthDialog *self = (GisAuthDialog*) user_data;
495- GoaAccount *account = GOA_ACCOUNT (source_object);
496- g_autoptr(GError) error = NULL;
497-
498- if (!goa_account_call_ensure_credentials_finish (account, NULL, res, &error)) {
499- if (g_error_matches (error, GOA_ERROR, GOA_ERROR_NOT_AUTHORIZED)) {
500- /* Show the refresh account is credentials are expired */
501- account = gis_auth_dialog_show_sso (self, account);
502- /* account is NULL in case of error or dismissal */
503- if (account != NULL) {
504- gtk_dialog_response (GTK_DIALOG (self), GTK_RESPONSE_OK);
505- g_object_unref (account);
506- }
507- }
508- } else {
509- gtk_dialog_response (GTK_DIALOG (self), GTK_RESPONSE_OK);
510- }
511-}
512-
513-static void
514-gis_auth_dialog_response_if_valid (GisAuthDialog *self)
515-{
516- GoaAccount *goa_account = gis_auth_dialog_get_selected_account (self);
517-
518- if (goa_account)
519- goa_account_call_ensure_credentials (goa_account,
520- self->cancellable,
521- gis_auth_dialog_ensure_crendentials_cb,
522- self);
523-
524- g_clear_object (&goa_account);
525-}
526-
527-static void
528-gis_auth_dialog_account_added_cb (GoaClient *client,
529- GoaObject *object,
530- GisAuthDialog *self)
531-{
532- GoaAccount *account = goa_object_peek_account (object);
533- gis_auth_dialog_add_account (self, account, FALSE);
534-}
535-
536-static void
537-gis_auth_dialog_account_removed_cb (GoaClient *client,
538- GoaObject *object,
539- GisAuthDialog *self)
540-{
541- GoaAccount *account = goa_object_peek_account (object);
542- gis_auth_dialog_remove_account (self, account);
543-}
544-
545-static void
546-gis_auth_dialog_button_add_another_cb (GtkButton *button,
547- GisAuthDialog *self)
548-{
549- GoaAccount *account;
550-
551- g_signal_handlers_block_by_func (self->goa_client, gis_auth_dialog_account_added_cb, self);
552-
553- account = gis_auth_dialog_show_sso (self, NULL);
554-
555- /* account is NULL in case of error or dismissal */
556- if (account != NULL) {
557- gis_auth_dialog_add_account (self, account, TRUE);
558- gis_auth_dialog_response_if_valid (self);
559- g_object_unref (account);
560- }
561-
562- g_signal_handlers_unblock_by_func (self->goa_client, gis_auth_dialog_account_added_cb, self);
563-}
564-
565-static void
566-gis_auth_dialog_button_cancel_cb (GtkButton *button,
567- GisAuthDialog *self)
568-{
569- gtk_dialog_response (GTK_DIALOG (self), GTK_RESPONSE_CANCEL);
570-}
571-
572-static void
573-gis_auth_dialog_button_continue_cb (GtkButton *button,
574- GisAuthDialog *self)
575-{
576- gint naccounts = gis_auth_dialog_get_naccounts (self);
577-
578- if (naccounts == 0)
579- gis_auth_dialog_button_add_another_cb (GTK_BUTTON (self->button_add_another), self);
580- else
581- gis_auth_dialog_response_if_valid (self);
582-}
583-
584-/* GObject */
585-
586-static void
587-gis_auth_dialog_dispose (GObject *object)
588-{
589- GisAuthDialog *self = GIS_AUTH_DIALOG (object);
590-
591- g_clear_object (&self->goa_client);
592-
593- if (self->error_timeout > 0) {
594- g_source_remove (self->error_timeout);
595- self->error_timeout = 0;
596- }
597-
598- g_cancellable_cancel (self->cancellable);
599- g_clear_object (&self->cancellable);
600-
601- G_OBJECT_CLASS (gis_auth_dialog_parent_class)->dispose (object);
602-}
603-
604-static void
605-gis_auth_dialog_class_init (GisAuthDialogClass *klass)
606-{
607- GObjectClass *object_class = G_OBJECT_CLASS (klass);
608- GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
609-
610- object_class->dispose = gis_auth_dialog_dispose;
611-
612- gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/initial-setup/gis-auth-dialog.ui");
613-
614- gtk_widget_class_bind_template_child (widget_class, GisAuthDialog, error_bar);
615- gtk_widget_class_bind_template_child (widget_class, GisAuthDialog, label_error);
616- gtk_widget_class_bind_template_child (widget_class, GisAuthDialog, label_header);
617- gtk_widget_class_bind_template_child (widget_class, GisAuthDialog, combobox_account);
618- gtk_widget_class_bind_template_child (widget_class, GisAuthDialog, label_account);
619- gtk_widget_class_bind_template_child (widget_class, GisAuthDialog, button_add_another);
620- gtk_widget_class_bind_template_child (widget_class, GisAuthDialog, button_cancel);
621- gtk_widget_class_bind_template_child (widget_class, GisAuthDialog, button_continue);
622- gtk_widget_class_bind_template_child (widget_class, GisAuthDialog, liststore_account);
623-
624-
625- gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), gis_auth_dialog_button_add_another_cb);
626- gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), gis_auth_dialog_button_cancel_cb);
627- gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), gis_auth_dialog_button_continue_cb);
628-}
629-
630-static void
631-gis_auth_dialog_init (GisAuthDialog *self)
632-{
633- self->cancellable = g_cancellable_new ();
634-
635- gtk_widget_init_template (GTK_WIDGET (self));
636- gtk_widget_grab_focus (self->button_continue);
637-}
638-
639-/* GInitable */
640-
641-static gboolean
642-gis_auth_dialog_initable_init (GInitable *initable,
643- GCancellable *cancellable,
644- GError **error)
645-{
646- GisAuthDialog *self;
647-
648- g_return_val_if_fail (GIS_IS_AUTH_DIALOG (initable), FALSE);
649-
650- self = GIS_AUTH_DIALOG (initable);
651-
652- self->goa_client = goa_client_new_sync (NULL, error);
653- if (!self->goa_client)
654- return FALSE;
655-
656- gis_auth_dialog_setup_model (self);
657- gis_auth_dialog_check_ui (self, FALSE);
658-
659- /* Be ready to other accounts */
660- g_signal_connect (self->goa_client, "account-added", G_CALLBACK (gis_auth_dialog_account_added_cb), self);
661- g_signal_connect (self->goa_client, "account-removed", G_CALLBACK (gis_auth_dialog_account_removed_cb), self);
662-
663- return TRUE;
664-}
665-
666-static void
667-gis_auth_dialog_initable_iface_init (GInitableIface *iface)
668-{
669- iface->init = gis_auth_dialog_initable_init;
670-}
671-
672-/* Public API */
673-
674-GtkWidget *
675-gis_auth_dialog_new (GError **error)
676-{
677- GisAuthDialog *dialog;
678-
679- dialog = g_initable_new (GIS_TYPE_AUTH_DIALOG,
680- NULL, error,
681- "title", "",
682- NULL);
683-
684- return GTK_WIDGET (dialog);
685-}
686-
687-gchar *
688-gis_auth_dialog_get_account_id (GisAuthDialog *self)
689-{
690- GoaAccount *goa_account;
691- gchar *account_id = NULL;
692-
693- g_return_val_if_fail (GIS_IS_AUTH_DIALOG (self), NULL);
694-
695- goa_account = gis_auth_dialog_get_selected_account (self);
696- if (goa_account)
697- account_id = goa_account_dup_id (goa_account);
698-
699- g_clear_object (&goa_account);
700- return account_id;
701-}
702diff --git a/gnome-initial-setup/pages/livepatch/gis-auth-dialog.h b/gnome-initial-setup/pages/livepatch/gis-auth-dialog.h
703deleted file mode 100644
704index 09bfb69..0000000
705--- a/gnome-initial-setup/pages/livepatch/gis-auth-dialog.h
706+++ /dev/null
707@@ -1,40 +0,0 @@
708-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
709-/*
710- * Copyright (C) 2018 Canonical Ltd.
711- *
712- * This program is free software; you can redistribute it and/or
713- * modify it under the terms of the GNU General Public License as
714- * published by the Free Software Foundation; either version 2 of the
715- * License, or (at your option) any later version.
716- *
717- * This program is distributed in the hope that it will be useful, but
718- * WITHOUT ANY WARRANTY; without even the implied warranty of
719- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
720- * General Public License for more details.
721- *
722- * You should have received a copy of the GNU General Public License
723- * along with this program; if not, see <http://www.gnu.org/licenses/>.
724- *
725- * Written by:
726- * Andrea Azzarone <andrea.azzarone@canonical.com>
727- */
728-
729-#ifndef __GIS_AUTH_DIALOG_H__
730-#define __GIS_AUTH_DIALOG_H__
731-
732-#define GOA_API_IS_SUBJECT_TO_CHANGE
733-#include <goa/goa.h>
734-#include <gtk/gtk.h>
735-
736-G_BEGIN_DECLS
737-
738-#define GIS_TYPE_AUTH_DIALOG (gis_auth_dialog_get_type ())
739-
740-G_DECLARE_FINAL_TYPE (GisAuthDialog, gis_auth_dialog, GIS, AUTH_DIALOG, GtkDialog)
741-
742-GtkWidget *gis_auth_dialog_new ();
743-gchar *gis_auth_dialog_get_account_id (GisAuthDialog *dialog);
744-
745-G_END_DECLS
746-
747-#endif /* __GIS_AUTH_DIALOG_H__ */
748diff --git a/gnome-initial-setup/pages/livepatch/gis-auth-dialog.ui b/gnome-initial-setup/pages/livepatch/gis-auth-dialog.ui
749deleted file mode 100644
750index 0fe40b2..0000000
751--- a/gnome-initial-setup/pages/livepatch/gis-auth-dialog.ui
752+++ /dev/null
753@@ -1,231 +0,0 @@
754-<?xml version="1.0" encoding="UTF-8"?>
755-<!-- Generated with glade 3.22.1 -->
756-<interface>
757- <requires lib="gtk+" version="3.10"/>
758- <object class="GtkListStore" id="liststore_account">
759- <columns>
760- <!-- column-name Id -->
761- <column type="gchararray"/>
762- <!-- column-name Email -->
763- <column type="gchararray"/>
764- <!-- column-name Account -->
765- <column type="GObject"/>
766- </columns>
767- </object>
768- <template class="GisAuthDialog" parent="GtkDialog">
769- <property name="can_focus">False</property>
770- <property name="resizable">False</property>
771- <property name="type_hint">dialog</property>
772- <child>
773- <placeholder/>
774- </child>
775- <child internal-child="vbox">
776- <object class="GtkBox">
777- <property name="can_focus">False</property>
778- <property name="orientation">vertical</property>
779- <property name="spacing">2</property>
780- <child internal-child="action_area">
781- <object class="GtkButtonBox">
782- <property name="can_focus">False</property>
783- <child>
784- <object class="GtkButton" id="button_add_another">
785- <property name="label" translatable="yes">Add another…</property>
786- <property name="visible">True</property>
787- <property name="can_focus">True</property>
788- <property name="receives_default">True</property>
789- <signal name="clicked" handler="gis_auth_dialog_button_add_another_cb" swapped="no"/>
790- </object>
791- <packing>
792- <property name="expand">True</property>
793- <property name="fill">True</property>
794- <property name="position">0</property>
795- <property name="secondary">True</property>
796- <property name="non_homogeneous">True</property>
797- </packing>
798- </child>
799- <child>
800- <object class="GtkButton" id="button_cancel">
801- <property name="label" translatable="yes">Cancel</property>
802- <property name="visible">True</property>
803- <property name="can_focus">True</property>
804- <property name="receives_default">True</property>
805- <signal name="clicked" handler="gis_auth_dialog_button_cancel_cb" swapped="no"/>
806- </object>
807- <packing>
808- <property name="expand">True</property>
809- <property name="fill">True</property>
810- <property name="position">2</property>
811- <property name="non_homogeneous">True</property>
812- </packing>
813- </child>
814- <child>
815- <object class="GtkButton" id="button_continue">
816- <property name="visible">True</property>
817- <property name="can_focus">True</property>
818- <property name="receives_default">True</property>
819- <signal name="clicked" handler="gis_auth_dialog_button_continue_cb" swapped="no"/>
820- </object>
821- <packing>
822- <property name="expand">True</property>
823- <property name="fill">True</property>
824- <property name="position">3</property>
825- <property name="non_homogeneous">True</property>
826- </packing>
827- </child>
828- </object>
829- <packing>
830- <property name="expand">True</property>
831- <property name="fill">True</property>
832- <property name="position">0</property>
833- </packing>
834- </child>
835- <child>
836- <object class="GtkInfoBar" id="error_bar">
837- <property name="can_focus">False</property>
838- <property name="no_show_all">True</property>
839- <property name="message_type">error</property>
840- <property name="revealed">False</property>
841- <child internal-child="action_area">
842- <object class="GtkButtonBox">
843- <property name="visible">True</property>
844- <property name="can_focus">False</property>
845- <property name="spacing">6</property>
846- <property name="layout_style">end</property>
847- <child>
848- <placeholder/>
849- </child>
850- </object>
851- <packing>
852- <property name="expand">True</property>
853- <property name="fill">True</property>
854- <property name="position">0</property>
855- </packing>
856- </child>
857- <child internal-child="content_area">
858- <object class="GtkBox">
859- <property name="visible">True</property>
860- <property name="can_focus">False</property>
861- <property name="spacing">16</property>
862- <child>
863- <object class="GtkLabel" id="label_error">
864- <property name="visible">True</property>
865- <property name="can_focus">False</property>
866- <property name="justify">fill</property>
867- <property name="wrap">True</property>
868- <property name="width_chars">0</property>
869- <property name="max_width_chars">50</property>
870- </object>
871- <packing>
872- <property name="expand">False</property>
873- <property name="fill">True</property>
874- <property name="position">0</property>
875- </packing>
876- </child>
877- </object>
878- <packing>
879- <property name="expand">False</property>
880- <property name="fill">False</property>
881- <property name="position">0</property>
882- </packing>
883- </child>
884- </object>
885- <packing>
886- <property name="expand">False</property>
887- <property name="fill">True</property>
888- <property name="position">0</property>
889- </packing>
890- </child>
891- <child>
892- <object class="GtkBox">
893- <property name="visible">True</property>
894- <property name="can_focus">False</property>
895- <property name="halign">start</property>
896- <property name="border_width">12</property>
897- <property name="spacing">18</property>
898- <child>
899- <object class="GtkImage">
900- <property name="visible">True</property>
901- <property name="can_focus">False</property>
902- <property name="halign">start</property>
903- <property name="valign">start</property>
904- <property name="icon_name">distributor-logo</property>
905- <property name="icon_size">6</property>
906- </object>
907- <packing>
908- <property name="expand">False</property>
909- <property name="fill">False</property>
910- <property name="position">0</property>
911- </packing>
912- </child>
913- <child>
914- <object class="GtkBox" id="box_auth">
915- <property name="visible">True</property>
916- <property name="can_focus">False</property>
917- <property name="halign">start</property>
918- <property name="orientation">vertical</property>
919- <property name="spacing">12</property>
920- <child>
921- <object class="GtkLabel" id="label_header">
922- <property name="name">label_header</property>
923- <property name="visible">True</property>
924- <property name="can_focus">False</property>
925- <property name="halign">start</property>
926- <property name="valign">start</property>
927- <property name="use_markup">True</property>
928- <property name="justify">fill</property>
929- <property name="wrap">True</property>
930- <property name="max_width_chars">40</property>
931- </object>
932- <packing>
933- <property name="expand">False</property>
934- <property name="fill">False</property>
935- <property name="position">0</property>
936- </packing>
937- </child>
938- <child>
939- <object class="GtkComboBox" id="combobox_account">
940- <property name="can_focus">False</property>
941- <property name="halign">start</property>
942- <property name="model">liststore_account</property>
943- <child>
944- <object class="GtkCellRendererText"/>
945- <attributes>
946- <attribute name="text">1</attribute>
947- </attributes>
948- </child>
949- </object>
950- <packing>
951- <property name="expand">False</property>
952- <property name="fill">True</property>
953- <property name="position">1</property>
954- </packing>
955- </child>
956- <child>
957- <object class="GtkLabel" id="label_account">
958- <property name="can_focus">False</property>
959- <property name="halign">start</property>
960- </object>
961- <packing>
962- <property name="expand">False</property>
963- <property name="fill">True</property>
964- <property name="position">2</property>
965- </packing>
966- </child>
967- </object>
968- <packing>
969- <property name="expand">False</property>
970- <property name="fill">False</property>
971- <property name="position">1</property>
972- </packing>
973- </child>
974- </object>
975- <packing>
976- <property name="expand">False</property>
977- <property name="fill">False</property>
978- <property name="position">1</property>
979- </packing>
980- </child>
981- </object>
982- </child>
983- </template>
984-</interface>
985diff --git a/gnome-initial-setup/pages/livepatch/gis-livepatch-page.c b/gnome-initial-setup/pages/livepatch/gis-livepatch-page.c
986deleted file mode 100644
987index eab2fb6..0000000
988--- a/gnome-initial-setup/pages/livepatch/gis-livepatch-page.c
989+++ /dev/null
990@@ -1,538 +0,0 @@
991-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
992-/*
993- * Copyright (C) 2018 Canonical Ltd.
994- *
995- * This program is free software; you can redistribute it and/or
996- * modify it under the terms of the GNU General Public License as
997- * published by the Free Software Foundation; either version 2 of the
998- * License, or (at your option) any later version.
999- *
1000- * This program is distributed in the hope that it will be useful, but
1001- * WITHOUT ANY WARRANTY; without even the implied warranty of
1002- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1003- * General Public License for more details.
1004- *
1005- * You should have received a copy of the GNU General Public License
1006- * along with this program; if not, see <http://www.gnu.org/licenses/>.
1007- *
1008- * Written by:
1009- * Andrea Azzarone <andrea.azzarone@canonical.com>
1010- */
1011-
1012-/* Canonical Livepatch page {{{1 */
1013-
1014-#define PAGE_ID "livepatch"
1015-
1016-#include "config.h"
1017-#include "gis-auth-dialog.h"
1018-#include "gis-livepatch-page.h"
1019-#include "livepatch-resources.h"
1020-
1021-#define GOA_API_IS_SUBJECT_TO_CHANGE
1022-#include <goa/goa.h>
1023-#define GOA_BACKEND_API_IS_SUBJECT_TO_CHANGE
1024-#include <goabackend/goabackend.h>
1025-
1026-#include <glib/gi18n.h>
1027-#include <gio/gio.h>
1028-#include <polkit/polkit.h>
1029-
1030-struct _GisLivepatchPagePrivate {
1031- GtkWidget *setup_button;
1032- GtkWidget *message_box;
1033- GtkWidget *signout_button;
1034- GtkWidget *message_label;
1035-
1036- GoaClient *goa_client;
1037- GoaAccount *goa_account;
1038- GPermission *permission;
1039-
1040- gchar *token;
1041- gboolean waiting_for_livepatch_response;
1042- gboolean user_current_choice;
1043-};
1044-typedef struct _GisLivepatchPagePrivate GisLivepatchPagePrivate;
1045-
1046-G_DEFINE_TYPE_WITH_PRIVATE (GisLivepatchPage, gis_livepatch_page, GIS_TYPE_PAGE);
1047-
1048-#define LIVEPATCH_ENABLING_MESSAGE _("You're all set: Livepatch is now being enabled.")
1049-#define LIVEPATCH_ENABLE_SUCCESS_MESSAGE _("You're all set: Livepatch is now on.")
1050-#define LIVEPATCH_ENABLE_FAILURE_MESSAGE _("Failed to setup Livepatch: please retry.")
1051-#define LIVEPATCH_DISABLE_FAILURE_MESSAGE _("Failed to disable Livepatch: please retry.")
1052-
1053-static gboolean
1054-set_livepatch_enabled (GisLivepatchPage *page,
1055- gboolean value);
1056-
1057-static gboolean
1058-is_livepatch_enabled ()
1059-{
1060- return g_file_test ("/var/snap/canonical-livepatch/common/machine-token",
1061- G_FILE_TEST_EXISTS);
1062-}
1063-
1064-static char *
1065-get_item (const char *buffer, const char *name)
1066-{
1067- g_autofree gchar *label = NULL;
1068- gchar *start = NULL, *end = NULL;
1069- gchar end_char;
1070-
1071- label = g_strconcat (name, "=", NULL);
1072- if ((start = strstr (buffer, label)) != NULL) {
1073- start += strlen (label);
1074- end_char = '\n';
1075- if (*start == '"') {
1076- start++;
1077- end_char = '"';
1078- }
1079- end = strchr (start, end_char);
1080- }
1081-
1082- if (start != NULL && end != NULL)
1083- return g_strndup (start, end - start);
1084- else
1085- return NULL;
1086-}
1087-
1088-static gboolean
1089-is_lts ()
1090-{
1091- g_autofree gchar *buffer = NULL;
1092- g_autofree gchar *version = NULL;
1093-
1094- if (g_file_get_contents ("/etc/os-release", &buffer, NULL, NULL))
1095- version = get_item (buffer, "VERSION");
1096-
1097- return version && g_strrstr (version, "LTS") != NULL;
1098-}
1099-
1100-static gboolean
1101-is_livepatch_supported ()
1102-{
1103- return is_lts ();
1104-}
1105-
1106-static void
1107-open_software_properties ()
1108-{
1109- g_autofree gchar *command = NULL;
1110- g_autoptr(GAppInfo) info = NULL;
1111- g_autoptr(GError) error = NULL;
1112-
1113- info = g_app_info_create_from_commandline ("software-properties-gtk --open-tab=2", NULL, G_APP_INFO_CREATE_NONE, &error);
1114- if (info == NULL) {
1115- g_warning ("Failed to get launch information from software-properties-gtk: %s", error->message);
1116- return;
1117- }
1118-
1119- if (!g_app_info_launch (info, NULL, NULL, &error)) {
1120- g_warning ("Failed to launch software-properties-gtk: %s", error->message);
1121- return;
1122- }
1123-}
1124-
1125-static void
1126-on_livepatch_enabled (GObject *source_object,
1127- GAsyncResult *res,
1128- gpointer data)
1129-{
1130- GisLivepatchPage *page = GIS_LIVEPATCH_PAGE (data);
1131- GisLivepatchPagePrivate *priv = gis_livepatch_page_get_instance_private (page);
1132- g_autoptr(GVariant) result = NULL;
1133- gboolean success = TRUE;
1134- gboolean current_state = is_livepatch_enabled ();
1135- g_autofree gchar *out_message = NULL;
1136- g_autoptr(GError) error = NULL;
1137-
1138- priv->waiting_for_livepatch_response = FALSE;
1139-
1140- result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error);
1141- if (result == NULL) {
1142- g_warning ("Failed to enable/disable Livepatch through DBus: %s\n", error->message);
1143- out_message = g_strdup (error->message);
1144- success = FALSE;
1145- } else {
1146- gboolean out_error;
1147-
1148- g_variant_get (result, "(bs)", &out_error, &out_message);
1149- if (out_error) {
1150- g_warning ("Failed to enable/disable Livepatch: %s\n", out_message);
1151- success = FALSE;
1152- }
1153- }
1154-
1155- if (success) {
1156- /* We succeded to enable or disable livepatch.
1157- Check if this corresponds to the current user choice. */
1158- if (current_state != priv->user_current_choice) {
1159- set_livepatch_enabled (page, priv->user_current_choice);
1160- } else if (current_state) {
1161- gtk_label_set_text (GTK_LABEL (priv->message_label), LIVEPATCH_ENABLE_SUCCESS_MESSAGE);
1162- }
1163- } else if (current_state != priv->user_current_choice) {
1164- GisAssistant *assistant = gis_driver_get_assistant (GIS_PAGE (page)->driver);
1165-
1166- /* We failed to enable or disable livepatch. Show an error message if
1167- the current state does not correpond the current user choice.
1168- Ignore the message if the call failed but the current status correponds
1169- to the user choice. */
1170- if (priv->user_current_choice) {
1171- gtk_label_set_text (GTK_LABEL (priv->message_label), LIVEPATCH_ENABLE_FAILURE_MESSAGE);
1172- gtk_widget_set_sensitive (priv->setup_button, TRUE);
1173- } else {
1174- gtk_label_set_text (GTK_LABEL (priv->message_label), LIVEPATCH_DISABLE_FAILURE_MESSAGE);
1175- }
1176-
1177- if (gis_assistant_get_current_page (assistant) != GIS_PAGE (page)) {
1178- GtkWidget *dialog;
1179-
1180- dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (page))),
1181- GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1182- GTK_MESSAGE_ERROR,
1183- GTK_BUTTONS_NONE,
1184- "<span font_size='x-large' font_weight='bold'>%s</span>",
1185- _("Sorry there's been a problem with setting up Canonical Livepatch"));
1186- gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
1187- _("The error was: %s"),
1188- out_message);
1189- gtk_dialog_add_buttons (GTK_DIALOG (dialog),
1190- _("Settings…"),
1191- GTK_RESPONSE_OK,
1192- _("Ignore"),
1193- GTK_RESPONSE_CANCEL,
1194- NULL);
1195-
1196- if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
1197- open_software_properties ();
1198-
1199- gtk_widget_destroy (dialog);
1200- }
1201- }
1202-
1203- gis_driver_uninhibit_quit (GIS_PAGE (page)->driver);
1204-}
1205-
1206-static gboolean
1207-set_livepatch_enabled (GisLivepatchPage *page,
1208- gboolean value)
1209-{
1210- GisLivepatchPagePrivate *priv = gis_livepatch_page_get_instance_private (page);
1211- g_autoptr(GDBusProxy) proxy = NULL;
1212- g_autoptr(GError) error = NULL;
1213- GVariant *args;
1214-
1215- proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
1216- G_DBUS_PROXY_FLAGS_NONE,
1217- NULL, /* info */
1218- "com.ubuntu.SoftwareProperties",
1219- "/",
1220- "com.ubuntu.SoftwareProperties",
1221- NULL, /* cancellable */
1222- &error);
1223-
1224- if (proxy == NULL) {
1225- g_warning ("Failed to get dbus proxy for com.ubuntu.SoftwareProperties: %s", error->message);
1226- return FALSE;
1227- }
1228-
1229- if (value) {
1230- args = g_variant_new ("(bs)", TRUE, priv->token);
1231- } else {
1232- args = g_variant_new ("(bs)", FALSE, "");
1233- }
1234-
1235- gis_driver_inhibit_quit (GIS_PAGE (page)->driver);
1236- priv->waiting_for_livepatch_response = TRUE;
1237- g_dbus_proxy_call (proxy,
1238- "SetLivepatchEnabled",
1239- args,
1240- /* Fallback to interactive authorization if the meta action didn't work */
1241- G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION,
1242- 1200000, /* 20 minutes timeout should be enough to install and enable livepatch */
1243- NULL, /* cancellable */
1244- on_livepatch_enabled,
1245- page);
1246-
1247- return TRUE;
1248-}
1249-
1250-static void
1251-on_livepatch_token_ready (GObject *source_object,
1252- GAsyncResult *res,
1253- gpointer data)
1254-{
1255- GisLivepatchPage *page = GIS_LIVEPATCH_PAGE (data);
1256- GisLivepatchPagePrivate *priv = gis_livepatch_page_get_instance_private (page);
1257- GoaPasswordBased *password_based = GOA_PASSWORD_BASED (source_object);
1258- g_autoptr(GError) error = NULL;
1259-
1260- if (!goa_password_based_call_get_password_finish (password_based, &priv->token, res, &error)) {
1261- g_warning ("Failed to get livepatch token: %s", error->message);
1262- gtk_widget_set_sensitive (priv->setup_button, TRUE);
1263- return;
1264- }
1265-
1266- priv->user_current_choice = TRUE;
1267- gtk_widget_set_visible (priv->message_box, TRUE);
1268- if (set_livepatch_enabled (page, TRUE)) {
1269- gtk_label_set_text (GTK_LABEL (priv->message_label), LIVEPATCH_ENABLING_MESSAGE);
1270- } else {
1271- gtk_label_set_text (GTK_LABEL (priv->message_label), LIVEPATCH_ENABLE_FAILURE_MESSAGE);
1272- gtk_widget_set_sensitive (priv->setup_button, TRUE);
1273- }
1274-}
1275-
1276-static void
1277-goa_account_store (const gchar *account_id)
1278-{
1279- GSettingsSchemaSource *source;
1280- g_autoptr(GSettingsSchema) schema = NULL;
1281- g_autoptr(GSettings) settings = NULL;
1282-
1283- /* Check if the gsettings schema is installed */
1284- source = g_settings_schema_source_get_default ();
1285- if (!source)
1286- return;
1287- schema = g_settings_schema_source_lookup (source, "com.ubuntu.SoftwareProperties", TRUE);
1288- if (!schema)
1289- return;
1290-
1291- /* If the schema is installed... */
1292- settings = g_settings_new ("com.ubuntu.SoftwareProperties");
1293- g_settings_set_string (settings, "goa-account-id", account_id);
1294-}
1295-
1296-static void
1297-login_and_enable_livepatch (GisLivepatchPage *page)
1298-{
1299- GisLivepatchPagePrivate *priv = gis_livepatch_page_get_instance_private (page);
1300- g_autofree gchar *account_id = NULL;
1301- GoaObject *goa_object = NULL;
1302- GoaPasswordBased *password_based = NULL;
1303- GtkWidget *dialog = NULL;
1304- g_autoptr(GError) error = NULL;
1305-
1306- /* show the login dialog if needed */
1307- dialog = gis_auth_dialog_new (&error);
1308- if (dialog) {
1309- gtk_window_set_transient_for (GTK_WINDOW (dialog),
1310- GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (page))));
1311-
1312- if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
1313- account_id = gis_auth_dialog_get_account_id (GIS_AUTH_DIALOG (dialog));
1314- goa_object = goa_client_lookup_by_id (priv->goa_client, account_id);
1315- }
1316- } else {
1317- g_warning ("Failed to create the authentication dialog: %s\n", error->message);
1318- }
1319-
1320- /* login dialog was dismissed */
1321- if (goa_object == NULL) {
1322- gtk_widget_set_sensitive (priv->setup_button, TRUE);
1323- goto out;
1324- }
1325-
1326- priv->goa_account = goa_object_get_account (goa_object);
1327- goa_account_store (account_id);
1328-
1329- /* retrieve livepatch token */
1330- password_based = goa_object_peek_password_based (goa_object);
1331-
1332- goa_password_based_call_get_password (password_based,
1333- "livepatch",
1334- NULL /* cancellable */,
1335- on_livepatch_token_ready,
1336- page);
1337- out:
1338- g_clear_pointer (&dialog, gtk_widget_destroy);
1339- g_clear_object (&goa_object);
1340-}
1341-
1342-static void
1343-on_livepatch_permission_acquired (GObject *source,
1344- GAsyncResult *res,
1345- gpointer data)
1346-{
1347- GisLivepatchPage *page = GIS_LIVEPATCH_PAGE (data);
1348- GisLivepatchPagePrivate *priv = gis_livepatch_page_get_instance_private (page);
1349- g_autoptr(GError) error = NULL;
1350- gboolean allowed;
1351-
1352- allowed = g_permission_acquire_finish (priv->permission, res, &error);
1353- if (error) {
1354- if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1355- g_warning ("Failed to acquire permission: %s", error->message);
1356- else
1357- gtk_widget_set_sensitive (priv->setup_button, TRUE);
1358- return;
1359- }
1360-
1361- if (allowed)
1362- login_and_enable_livepatch (page);
1363-}
1364-
1365-static void
1366-on_setup_button_clicked (GtkButton *button,
1367- gpointer data)
1368-{
1369- GisLivepatchPage *page = GIS_LIVEPATCH_PAGE (data);
1370- GisLivepatchPagePrivate *priv = gis_livepatch_page_get_instance_private (page);
1371-
1372- gtk_widget_set_sensitive (priv->setup_button, FALSE);
1373-
1374- if (G_IS_PERMISSION (priv->permission) && g_permission_get_allowed (priv->permission)) {
1375- login_and_enable_livepatch (page);
1376- }
1377- else if (G_IS_PERMISSION (priv->permission) && g_permission_get_can_acquire (priv->permission)) {
1378- g_permission_acquire_async (priv->permission,
1379- NULL,
1380- on_livepatch_permission_acquired,
1381- page);
1382- } else {
1383- g_warning ("Could not start the attempt to acquire the permission to enable Livepatch. Fallback to per-app policy.");
1384- login_and_enable_livepatch (page);
1385- }
1386-}
1387-
1388-static void
1389-on_signout_button_clicked (GtkButton *button,
1390- GisLivepatchPage *page)
1391-{
1392- GisLivepatchPagePrivate *priv = gis_livepatch_page_get_instance_private (page);
1393-
1394- /* disable livepatch */
1395- priv->user_current_choice = FALSE;
1396- if (!priv->waiting_for_livepatch_response && is_livepatch_enabled ())
1397- set_livepatch_enabled (page, FALSE);
1398-
1399- goa_account_store ("");
1400- /* remove GoaAccount from system */
1401- goa_account_call_remove (priv->goa_account, NULL, NULL, NULL);
1402-
1403- /* reset the GUI */
1404- gtk_widget_set_sensitive (priv->setup_button, TRUE);
1405- gtk_widget_set_visible (priv->message_box, FALSE);
1406-
1407- g_clear_object (&priv->goa_account);
1408- g_clear_pointer (&priv->token, g_free);
1409-}
1410-
1411-static void
1412-show_legal (GtkButton *button, GisLivepatchPage *page)
1413-{
1414- g_autofree gchar *buffer = NULL;
1415- g_autofree gchar *privacy_policy = NULL;
1416- g_autoptr(GError) error = NULL;
1417-
1418- if (g_file_get_contents ("/etc/os-release", &buffer, NULL, NULL))
1419- privacy_policy = get_item (buffer, "PRIVACY_POLICY_URL");
1420-
1421- if (!gtk_show_uri_on_window (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (page))),
1422- privacy_policy,
1423- GDK_CURRENT_TIME, &error)) {
1424- GtkWidget *dialog;
1425- dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (page))),
1426- GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1427- GTK_MESSAGE_ERROR,
1428- GTK_BUTTONS_CLOSE,
1429- _("Failed to show Livepatch policy: %s"), error->message);
1430- gtk_dialog_run (GTK_DIALOG (dialog));
1431- gtk_widget_destroy (dialog);
1432- return;
1433- }
1434-}
1435-
1436-static void
1437-gis_livepatch_page_constructed (GObject *object)
1438-{
1439- GisLivepatchPage *page = GIS_LIVEPATCH_PAGE (object);
1440- GisLivepatchPagePrivate *priv = gis_livepatch_page_get_instance_private (page);
1441- g_autoptr(GError) error = NULL;
1442-
1443- G_OBJECT_CLASS (gis_livepatch_page_parent_class)->constructed (object);
1444-
1445- gis_page_set_skippable (GIS_PAGE (page), TRUE);
1446-
1447- priv->goa_client = goa_client_new_sync (NULL, &error);
1448-
1449- if (priv->goa_client == NULL) {
1450- g_error ("Failed to get a GoaClient: %s", error->message);
1451- return;
1452- }
1453-
1454- priv->permission = polkit_permission_new_sync ("com.ubuntu.welcome.livepatch", NULL, NULL, &error);
1455- if (priv->permission == NULL) {
1456- g_warning ("Could not get 'com.ubuntu.welcome.livepatch' permission: %s",
1457- error->message);
1458- }
1459-
1460- g_signal_connect (priv->setup_button, "clicked",
1461- G_CALLBACK (on_setup_button_clicked), page);
1462-
1463- g_signal_connect (priv->signout_button, "clicked",
1464- G_CALLBACK (on_signout_button_clicked), page);
1465-
1466- gis_page_set_complete (GIS_PAGE (page), TRUE);
1467- gtk_widget_show (GTK_WIDGET (page));
1468-}
1469-
1470-static void
1471-gis_livepatch_page_dispose (GObject *object)
1472-{
1473- GisLivepatchPage *page = GIS_LIVEPATCH_PAGE (object);
1474- GisLivepatchPagePrivate *priv = gis_livepatch_page_get_instance_private (page);
1475-
1476- g_clear_object (&priv->goa_client);
1477- g_clear_object (&priv->goa_account);
1478- g_clear_object (&priv->permission);
1479- g_clear_pointer (&priv->token, g_free);
1480-
1481- G_OBJECT_CLASS (gis_livepatch_page_parent_class)->dispose (object);
1482-}
1483-
1484-static void
1485-gis_livepatch_page_locale_changed (GisPage *page)
1486-{
1487- gis_page_set_title (GIS_PAGE (page), _("Livepatch"));
1488-}
1489-
1490-static void
1491-gis_livepatch_page_class_init (GisLivepatchPageClass *klass)
1492-{
1493- GisPageClass *page_class = GIS_PAGE_CLASS (klass);
1494- GObjectClass *object_class = G_OBJECT_CLASS (klass);
1495-
1496- gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), "/org/gnome/initial-setup/gis-livepatch-page.ui");
1497-
1498- gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisLivepatchPage, setup_button);
1499- gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisLivepatchPage, message_box);
1500- gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisLivepatchPage, signout_button);
1501- gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisLivepatchPage, message_label);
1502- gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), show_legal);
1503-
1504-
1505- page_class->page_id = PAGE_ID;
1506- page_class->locale_changed = gis_livepatch_page_locale_changed;
1507- object_class->constructed = gis_livepatch_page_constructed;
1508- object_class->dispose = gis_livepatch_page_dispose;
1509-}
1510-
1511-static void
1512-gis_livepatch_page_init (GisLivepatchPage *page)
1513-{
1514- g_resources_register (livepatch_get_resource ());
1515-
1516- gtk_widget_init_template (GTK_WIDGET (page));
1517-}
1518-
1519-GisPage *
1520-gis_prepare_livepatch_page (GisDriver *driver)
1521-{
1522- if (is_livepatch_enabled () || !is_livepatch_supported ())
1523- return NULL;
1524-
1525- return g_object_new (GIS_TYPE_LIVEPATCH_PAGE,
1526- "driver", driver,
1527- NULL);
1528-}
1529diff --git a/gnome-initial-setup/pages/livepatch/gis-livepatch-page.h b/gnome-initial-setup/pages/livepatch/gis-livepatch-page.h
1530deleted file mode 100644
1531index 3356dbe..0000000
1532--- a/gnome-initial-setup/pages/livepatch/gis-livepatch-page.h
1533+++ /dev/null
1534@@ -1,52 +0,0 @@
1535-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
1536-/*
1537- * Copyright (C) 2018 Canonical Ltd.
1538- *
1539- * This program is free software; you can redistribute it and/or
1540- * modify it under the terms of the GNU General Public License as
1541- * published by the Free Software Foundation; either version 2 of the
1542- * License, or (at your option) any later version.
1543- *
1544- * This program is distributed in the hope that it will be useful, but
1545- * WITHOUT ANY WARRANTY; without even the implied warranty of
1546- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1547- * General Public License for more details.
1548- *
1549- * You should have received a copy of the GNU General Public License
1550- * along with this program; if not, see <http://www.gnu.org/licenses/>.
1551- */
1552-
1553-#ifndef __GIS_LIVEPATCH_PAGE_H__
1554-#define __GIS_LIVEPATCH_PAGE_H__
1555-
1556-#include "gnome-initial-setup.h"
1557-
1558-G_BEGIN_DECLS
1559-
1560-#define GIS_TYPE_LIVEPATCH_PAGE (gis_livepatch_page_get_type ())
1561-#define GIS_LIVEPATCH_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIS_TYPE_LIVEPATCH_PAGE, GisLivepatchPage))
1562-#define GIS_LIVEPATCH_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIS_TYPE_LIVEPATCH_PAGE, GisLivepatchPageClass))
1563-#define GIS_IS_LIVEPATCH_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIS_TYPE_LIVEPATCH_PAGE))
1564-#define GIS_IS_LIVEPATCH_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIS_TYPE_LIVEPATCH_PAGE))
1565-#define GIS_LIVEPATCH_PAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIS_TYPE_LIVEPATCH_PAGE, GisLivepatchPageClass))
1566-
1567-typedef struct _GisLivepatchPage GisLivepatchPage;
1568-typedef struct _GisLivepatchPageClass GisLivepatchPageClass;
1569-
1570-struct _GisLivepatchPage
1571-{
1572- GisPage parent;
1573-};
1574-
1575-struct _GisLivepatchPageClass
1576-{
1577- GisPageClass parent_class;
1578-};
1579-
1580-GType gis_livepatch_page_get_type (void);
1581-
1582-GisPage *gis_prepare_livepatch_page (GisDriver *driver);
1583-
1584-G_END_DECLS
1585-
1586-#endif /* __GIS_LIVEPATCH_PAGE_H__ */
1587diff --git a/gnome-initial-setup/pages/livepatch/gis-livepatch-page.ui b/gnome-initial-setup/pages/livepatch/gis-livepatch-page.ui
1588deleted file mode 100644
1589index c877ff5..0000000
1590--- a/gnome-initial-setup/pages/livepatch/gis-livepatch-page.ui
1591+++ /dev/null
1592@@ -1,156 +0,0 @@
1593-<?xml version="1.0" encoding="UTF-8"?>
1594-<interface>
1595- <!-- interface-requires gtk+ 3.0 -->
1596- <template class="GisLivepatchPage" parent="GisPage">
1597- <child>
1598- <object class="GtkGrid">
1599- <property name="visible">True</property>
1600- <property name="can_focus">False</property>
1601- <property name="row_spacing">40</property>
1602- <property name="border_width">20</property>
1603- <child>
1604- <object class="GtkLabel" id="title">
1605- <property name="visible">True</property>
1606- <property name="can_focus">False</property>
1607- <property name="halign">start</property>
1608- <property name="valign">start</property>
1609- <property name="label" translatable="yes">Livepatch</property>
1610- <attributes>
1611- <attribute name="weight" value="bold"/>
1612- <attribute name="scale" value="1.8"/>
1613- </attributes>
1614- </object>
1615- <packing>
1616- <property name="left_attach">0</property>
1617- <property name="top_attach">0</property>
1618- <property name="width">3</property>
1619- <property name="height">1</property>
1620- </packing>
1621- </child>
1622- <child>
1623- <object class="GtkImage" id="icon_image">
1624- <property name="visible">True</property>
1625- <property name="can_focus">False</property>
1626- <property name="valign">start</property>
1627- <property name="halign">start</property>
1628- <property name="resource">/org/gnome/initial-setup/livepatch.svg</property>
1629- </object>
1630- <packing>
1631- <property name="left_attach">0</property>
1632- <property name="top_attach">1</property>
1633- <property name="width">1</property>
1634- <property name="height">1</property>
1635- </packing>
1636- </child>
1637- <child>
1638- <object class="GtkLabel" id="right_padding_label">
1639- <property name="visible">True</property>
1640- </object>
1641- <packing>
1642- <property name="left_attach">2</property>
1643- <property name="top_attach">1</property>
1644- <property name="width">1</property>
1645- <property name="height">1</property>
1646- </packing>
1647- </child>
1648- <child>
1649- <object class="GtkBox">
1650- <property name="visible">True</property>
1651- <property name="can_focus">False</property>
1652- <property name="halign">center</property>
1653- <property name="hexpand">True</property>
1654- <property name="valign">start</property>
1655- <property name="vexpand">True</property>
1656- <property name="orientation">vertical</property>
1657- <child>
1658- <object class="GtkLabel">
1659- <property name="visible">True</property>
1660- <property name="can_focus">False</property>
1661- <property name="max-width-chars">50</property>
1662- <property name="xalign">0</property>
1663- <property name="halign">start</property>
1664- <property name="label" translatable="yes">Canonical Livepatch helps keep your computer secure, by applying some updates that would normally require restarting.</property>
1665- <property name="wrap">True</property>
1666- </object>
1667- </child>
1668- <child>
1669- <object class="GtkLabel">
1670- <property name="visible">True</property>
1671- <property name="xalign">0</property>
1672- <property name="margin_top">18</property>
1673- <property name="label" translatable="yes">Would you like to set up Livepatch now?</property>
1674- <attributes>
1675- <attribute name="weight" value="bold"/>
1676- </attributes>
1677- </object>
1678- </child>
1679- <child>
1680- <object class="GtkBox">
1681- <property name="visible">True</property>
1682- <property name="can_focus">False</property>
1683- <property name="halign">start</property>
1684- <property name="orientation">horizontal</property>
1685- <property name="margin_top">18</property>
1686- <property name="spacing">6</property>
1687- <child>
1688- <object class="GtkButton" id="setup_button">
1689- <property name="visible">True</property>
1690- <property name="halign">start</property>
1691- <property name="label" translatable="yes">Set Up Livepatch…</property>
1692- <style>
1693- <class name="suggested-action"/>
1694- </style>
1695- </object>
1696- </child>
1697- <child>
1698- <object class="GtkButton">
1699- <property name="visible">True</property>
1700- <property name="label" translatable="yes">Legal notice</property>
1701- <signal name="clicked" handler="show_legal"/>
1702- </object>
1703- </child>
1704- </object>
1705- </child>
1706- <child>
1707- <object class="GtkBox" id="message_box">
1708- <property name="visible">False</property>
1709- <property name="can_focus">False</property>
1710- <property name="halign">fill</property>
1711- <property name="orientation">horizontal</property>
1712- <property name="margin_top">18</property>
1713- <child>
1714- <object class="GtkLabel" id="message_label">
1715- <property name="visible">True</property>
1716- <property name="xalign">0</property>
1717- </object>
1718- </child>
1719- <child>
1720- <object class="GtkButton" id="signout_button">
1721- <property name="visible">True</property>
1722- <property name="label" translatable="yes">Sign Out</property>
1723- </object>
1724- <packing>
1725- <property name="pack-type">end</property>
1726- </packing>
1727- </child>
1728- </object>
1729- </child>
1730- </object>
1731- <packing>
1732- <property name="left_attach">1</property>
1733- <property name="top_attach">1</property>
1734- <property name="width">1</property>
1735- <property name="height">1</property>
1736- </packing>
1737- </child>
1738- </object>
1739- </child>
1740- </template>
1741- <object class="GtkSizeGroup">
1742- <property name="mode">horizontal</property>
1743- <widgets>
1744- <widget name="icon_image"/>
1745- <widget name="right_padding_label"/>
1746- </widgets>
1747- </object>
1748-</interface>
1749diff --git a/gnome-initial-setup/pages/livepatch/livepatch.gresource.xml b/gnome-initial-setup/pages/livepatch/livepatch.gresource.xml
1750deleted file mode 100644
1751index 5678958..0000000
1752--- a/gnome-initial-setup/pages/livepatch/livepatch.gresource.xml
1753+++ /dev/null
1754@@ -1,9 +0,0 @@
1755-<?xml version="1.0" encoding="UTF-8"?>
1756-<gresources>
1757- <gresource prefix="/org/gnome/initial-setup">
1758- <file preprocess="xml-stripblanks" alias="gis-livepatch-page.ui">gis-livepatch-page.ui</file>
1759- <file preprocess="xml-stripblanks" alias="gis-auth-dialog.ui">gis-auth-dialog.ui</file>
1760- <file alias="livepatch.svg">livepatch.svg</file>
1761- </gresource>
1762-</gresources>
1763-
1764diff --git a/gnome-initial-setup/pages/livepatch/livepatch.svg b/gnome-initial-setup/pages/livepatch/livepatch.svg
1765deleted file mode 100644
1766index a84bcd6..0000000
1767--- a/gnome-initial-setup/pages/livepatch/livepatch.svg
1768+++ /dev/null
1769@@ -1 +0,0 @@
1770-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="Layer_1" x="0px" y="0px" width="150px" height="150px" viewBox="0 0 400 400" style="enable-background:new 0 0 400 400;" xml:space="preserve"> <style type="text/css"> .st0{fill:#666666; fill-opacity:0.25;} </style> <g> <path class="st0" d="M73.7,168.4c0-69.7,56.7-126.3,126.3-126.3c69.7,0,126.3,56.7,126.3,126.3h42.1C368.4,75.6,292.9,0,200,0 C107.1,0,31.6,75.6,31.6,168.4v63.7h0C31.9,324.8,107.3,400,200,400c92.9,0,168.4-75.6,168.4-168.4v-21.1H73.7V168.4L73.7,168.4z M200,242.1c17.4,0,31.6,14.1,31.6,31.6c0,13.7-8.8,25.4-21,29.7v54.4c0,5.8-4.7,10.5-10.5,10.5s-10.5-4.7-10.5-10.5v-54.4 c-12.3-4.3-21.1-16-21.1-29.8C168.4,256.3,182.6,242.1,200,242.1L200,242.1z"/> </g> </svg>
1771diff --git a/gnome-initial-setup/pages/livepatch/meson.build b/gnome-initial-setup/pages/livepatch/meson.build
1772deleted file mode 100644
1773index 14d89ed..0000000
1774--- a/gnome-initial-setup/pages/livepatch/meson.build
1775+++ /dev/null
1776@@ -1,12 +0,0 @@
1777-sources += gnome.compile_resources(
1778- 'livepatch-resources',
1779- files('livepatch.gresource.xml'),
1780- c_name: 'livepatch'
1781-)
1782-
1783-sources += files(
1784- 'gis-livepatch-page.c',
1785- 'gis-livepatch-page.h',
1786- 'gis-auth-dialog.c',
1787- 'gis-auth-dialog.h',
1788-)
1789diff --git a/gnome-initial-setup/pages/meson.build b/gnome-initial-setup/pages/meson.build
1790index 32f615b..2767094 100644
1791--- a/gnome-initial-setup/pages/meson.build
1792+++ b/gnome-initial-setup/pages/meson.build
1793@@ -10,8 +10,8 @@ pages = [
1794 'software',
1795 'summary',
1796 'welcome',
1797+ 'ubuntu-pro',
1798 'ubuntu-report',
1799- 'livepatch',
1800 'apps',
1801 ]
1802
1803diff --git a/gnome-initial-setup/pages/ubuntu-pro/checkmark.svg b/gnome-initial-setup/pages/ubuntu-pro/checkmark.svg
1804new file mode 100644
1805index 0000000..a0c8602
1806--- /dev/null
1807+++ b/gnome-initial-setup/pages/ubuntu-pro/checkmark.svg
1808@@ -0,0 +1 @@
1809+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="none" fill-rule="nonzero"><path fill="#0e8420" d="M8 1a7 7 0 110 14A7 7 0 018 1zm2.83 3.502L6.863 9.884 5.174 8.096l-1.09 1.03 2.92 3.096 5.034-6.83-1.208-.89z"/><path fill="#fff" d="M10.83 4.502l1.208.89-5.033 6.83-2.922-3.096 1.091-1.03 1.689 1.789z"/></g></svg>
1810\ No newline at end of file
1811diff --git a/gnome-initial-setup/pages/ubuntu-pro/fail.svg b/gnome-initial-setup/pages/ubuntu-pro/fail.svg
1812new file mode 100644
1813index 0000000..e3c9a52
1814--- /dev/null
1815+++ b/gnome-initial-setup/pages/ubuntu-pro/fail.svg
1816@@ -0,0 +1 @@
1817+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><defs><filter id="darkreader-image-filter"><feColorMatrix type="matrix" values="-0.193 -0.490 -1.120 0.000 1.698 -0.965 0.293 -1.105 0.000 1.673 -0.946 -0.489 -0.296 0.000 1.630 0.000 0.000 0.000 1.000 0.000"/></filter></defs><image width="16" height="16" filter="url(#darkreader-image-filter)" xlink:href="data:image/svg+xml,%3Csvg width='16' height='16' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Ccircle stroke='%23c7162b' stroke-width='1.5' fill='%23c7162b' cx='8' cy='8' r='6.25'/%3E%3Cpath fill='%23FFF' fill-rule='nonzero' d='M10.282 4.638l1.06 1.06L9.05 7.99l2.293 2.292-1.06 1.06L7.99 9.05 5.7 11.343l-1.06-1.06 2.29-2.293L4.64 5.7l1.06-1.06 2.291 2.29z'/%3E%3C/g%3E%3C/svg%3E"/></svg>
1818\ No newline at end of file
1819diff --git a/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-attach-page.ui b/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-attach-page.ui
1820new file mode 100644
1821index 0000000..930d4b7
1822--- /dev/null
1823+++ b/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-attach-page.ui
1824@@ -0,0 +1,214 @@
1825+<?xml version="1.0" encoding="UTF-8"?>
1826+<interface>
1827+ <!-- interface-requires gtk+ 3.0 -->
1828+ <template class="GisUbuntuProAttachPage" parent="GtkBin">
1829+ <child>
1830+ <object class="GtkBox" id="attach_page">
1831+ <property name="visible">True</property>
1832+ <property name="can-focus">False</property>
1833+ <property name="no-show-all">True</property>
1834+ <property name="orientation">vertical</property>
1835+ <child>
1836+ <object class="GtkLabel">
1837+ <property name="visible">True</property>
1838+ <property name="can-focus">False</property>
1839+ <property name="halign">start</property>
1840+ <property name="margin-top">3</property>
1841+ <property name="margin-bottom">0</property>
1842+ <property name="label" translatable="yes">&lt;b&gt;Enable Ubuntu Pro&lt;/b&gt;</property>
1843+ <property name="use-markup">True</property>
1844+ <property name="wrap">True</property>
1845+ <property name="xalign">0</property>
1846+ <attributes>
1847+ <attribute name="scale" value="1.4"/>
1848+ </attributes>
1849+ </object>
1850+ </child>
1851+ <child>
1852+ <object class="GtkSeparator">
1853+ <property name="visible">True</property>
1854+ <property name="can-focus">False</property>
1855+ <property name="margin-top">10</property>
1856+ <property name="margin-bottom">10</property>
1857+ </object>
1858+ </child>
1859+ <child>
1860+ <object class="GtkEventBox">
1861+ <property name="visible">True</property>
1862+ <property name="can-focus">False</property>
1863+ <property name="margin-top">12</property>
1864+ <property name="margin-bottom">12</property>
1865+ <child>
1866+ <object class="GtkBox">
1867+ <property name="visible">True</property>
1868+ <property name="can-focus">False</property>
1869+ <child>
1870+ <object class="GtkImage" id="ubuntu_pro_logo">
1871+ <property name="visible">True</property>
1872+ <property name="can-focus">False</property>
1873+ <property name="margin-left">30</property>
1874+ <property name="margin-right">30</property>
1875+ <property name="yalign">0</property>
1876+ </object>
1877+ </child>
1878+ <child>
1879+ <object class="GtkLabel">
1880+ <property name="visible">True</property>
1881+ <property name="can-focus">True</property>
1882+ <property name="margin-right">30</property>
1883+ <property name="label" translatable="yes">Upgrade this machine to Ubuntu Pro for security updates on a much wider range of packages, until 2032. Fulfill FedRAMP, FIPS, STIG and HIPAA and other compliance and hardening requirements with certified tooling and crypto-modules. Free up to 5 machines.
1884+
1885+More information on &lt;a href="https://ubuntu.com/pro"&gt;ubuntu.com/pro&lt;/a&gt;.</property>
1886+ <property name="use-markup">True</property>
1887+ <property name="wrap">True</property>
1888+ <property name="max-width-chars">130</property>
1889+ </object>
1890+ </child>
1891+ </object>
1892+ </child>
1893+ </object>
1894+ </child>
1895+ <child>
1896+ <object class="GtkRadioButton" id="magic_radio">
1897+ <property name="label" translatable="yes">Enter code on ubuntu.com/pro/attach</property>
1898+ <property name="visible">True</property>
1899+ <property name="can-focus">True</property>
1900+ <property name="receives-default">False</property>
1901+ <property name="margin-top">40</property>
1902+ <property name="margin-bottom">6</property>
1903+ <property name="margin-left">30</property>
1904+ <property name="xalign">0</property>
1905+ <property name="group">magic_radio</property>
1906+ <signal name="clicked" handler="on_magic_clicked" swapped="no"/>
1907+ </object>
1908+ </child>
1909+ <child>
1910+ <object class="GtkBox">
1911+ <property name="visible">True</property>
1912+ <property name="can-focus">False</property>
1913+ <property name="margin-left">30</property>
1914+ <child>
1915+ <object class="GtkLabel" id="pin_label">
1916+ <property name="selectable">True</property>
1917+ <property name="visible">True</property>
1918+ <property name="can-focus">False</property>
1919+ <property name="margin-top">8</property>
1920+ <property name="margin-bottom">8</property>
1921+ <property name="halign">start</property>
1922+ <property name="margin-left">32</property>
1923+ <attributes>
1924+ <attribute name="scale" value="2"/>
1925+ </attributes>
1926+ </object>
1927+ </child>
1928+ <child>
1929+ <object class="GtkFixed">
1930+ <property name="visible">True</property>
1931+ <property name="valign">center</property>
1932+ <property name="margin-left">12</property>
1933+ <property name="margin-right">6</property>
1934+ <child>
1935+ <object class="GtkSpinner" id="pin_spinner">
1936+ <property name="visible">True</property>
1937+ <property name="valign">center</property>
1938+ </object>
1939+ </child>
1940+ <child>
1941+ <object class="GtkImage" id="pin_status_icon">
1942+ <property name="visible">False</property>
1943+ <property name="valign">center</property>
1944+ </object>
1945+ </child>
1946+ </object>
1947+ </child>
1948+ <child>
1949+ <object class="GtkLabel" id="pin_status">
1950+ <property name="visible">True</property>
1951+ <property name="use-markup">True</property>
1952+ <property name="halign">start</property>
1953+ </object>
1954+ </child>
1955+ </object>
1956+ </child>
1957+ <child>
1958+ <object class="GtkLabel" id="pin_hint">
1959+ <property name="visible">True</property>
1960+ <property name="can-focus">False</property>
1961+ <property name="margin-top">6</property>
1962+ <property name="margin-bottom">8</property>
1963+ <property name="halign">start</property>
1964+ <property name="margin-left">62</property>
1965+ </object>
1966+ </child>
1967+ <child>
1968+ <object class="GtkRadioButton" id="token_radio">
1969+ <property name="label" translatable="yes">Or add token manually</property>
1970+ <property name="visible">True</property>
1971+ <property name="can-focus">True</property>
1972+ <property name="receives-default">False</property>
1973+ <property name="margin-left">30</property>
1974+ <property name="margin-top">20</property>
1975+ <property name="xalign">0</property>
1976+ <property name="group">magic_radio</property>
1977+ <signal name="toggled" handler="on_radio_toggled" swapped="no"/>
1978+ </object>
1979+ </child>
1980+ <child>
1981+ <object class="GtkBox">
1982+ <property name="visible">True</property>
1983+ <property name="can-focus">False</property>
1984+ <property name="no-show-all">True</property>
1985+ <property name="margin-left">62</property>
1986+ <property name="margin-top">12</property>
1987+ <property name="margin-bottom">12</property>
1988+ <child>
1989+ <object class="GtkEntry" id="token_field">
1990+ <property name="visible">True</property>
1991+ <property name="sensitive">False</property>
1992+ <property name="can-focus">True</property>
1993+ <property name="width-chars">35</property>
1994+ <property name="placeholder-text" translatable="yes">Token</property>
1995+ <property name="valign">center</property>
1996+ <signal name="activate" handler="request_token_attach" swapped="no"/>
1997+ <signal name="changed" handler="on_token_typing" swapped="no"/>
1998+ </object>
1999+ </child>
2000+ <child>
2001+ <object class="GtkFixed">
2002+ <property name="visible">True</property>
2003+ <property name="valign">center</property>
2004+ <property name="margin">6</property>
2005+ <child>
2006+ <object class="GtkSpinner" id="token_spinner">
2007+ <property name="visible">True</property>
2008+ <property name="valign">center</property>
2009+ </object>
2010+ </child>
2011+ <child>
2012+ <object class="GtkImage" id="token_status_icon">
2013+ <property name="visible">False</property>
2014+ <property name="valign">center</property>
2015+ </object>
2016+ </child>
2017+ </object>
2018+ </child>
2019+ <child>
2020+ <object class="GtkLabel" id="token_status">
2021+ <property name="visible">True</property>
2022+ <property name="valign">center</property>
2023+ </object>
2024+ </child>
2025+ </object>
2026+ </child>
2027+ <child>
2028+ <object class="GtkLabel">
2029+ <property name="visible">True</property>
2030+ <property name="label" translatable="yes">From your admin, or from ubuntu.com/pro</property>
2031+ <property name="margin-left">62</property>
2032+ <property name="halign">start</property>
2033+ </object>
2034+ </child>
2035+ </object>
2036+ </child>
2037+</template>
2038+</interface>
2039diff --git a/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-offer-page.ui b/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-offer-page.ui
2040new file mode 100644
2041index 0000000..baf941e
2042--- /dev/null
2043+++ b/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-offer-page.ui
2044@@ -0,0 +1,165 @@
2045+<?xml version="1.0" encoding="UTF-8"?>
2046+<interface>
2047+ <!-- interface-requires gtk+ 3.0 -->
2048+ <template class="GisUbuntuProOfferPage" parent="GtkBin">
2049+ <child>
2050+ <object class="GtkBox" id="offer_page">
2051+ <property name="visible">True</property>
2052+ <property name="can-focus">False</property>
2053+ <property name="no-show-all">True</property>
2054+ <property name="orientation">vertical</property>
2055+ <child>
2056+ <object class="GtkLabel">
2057+ <property name="visible">True</property>
2058+ <property name="can-focus">False</property>
2059+ <property name="halign">start</property>
2060+ <property name="margin-top">3</property>
2061+ <property name="margin-bottom">0</property>
2062+ <property name="label" translatable="yes">&lt;b&gt;Enable Ubuntu Pro&lt;/b&gt;</property>
2063+ <property name="use-markup">True</property>
2064+ <property name="wrap">True</property>
2065+ <property name="xalign">0</property>
2066+ <attributes>
2067+ <attribute name="scale" value="1.4"/>
2068+ </attributes>
2069+ </object>
2070+ </child>
2071+ <child>
2072+ <object class="GtkSeparator">
2073+ <property name="visible">True</property>
2074+ <property name="can-focus">False</property>
2075+ <property name="margin-top">10</property>
2076+ <property name="margin-bottom">10</property>
2077+ </object>
2078+ </child>
2079+ <child>
2080+ <object class="GtkEventBox">
2081+ <property name="visible">True</property>
2082+ <property name="can-focus">False</property>
2083+ <property name="margin-top">12</property>
2084+ <property name="margin-bottom">12</property>
2085+ <child>
2086+ <object class="GtkBox">
2087+ <property name="visible">True</property>
2088+ <property name="can-focus">False</property>
2089+ <child>
2090+ <object class="GtkImage" id="ubuntu_pro_logo">
2091+ <property name="visible">True</property>
2092+ <property name="can-focus">False</property>
2093+ <property name="margin-left">30</property>
2094+ <property name="margin-right">30</property>
2095+ <property name="yalign">0</property>
2096+ </object>
2097+ </child>
2098+ <child>
2099+ <object class="GtkLabel">
2100+ <property name="visible">True</property>
2101+ <property name="can-focus">True</property>
2102+ <property name="margin-right">30</property>
2103+ <property name="label" translatable="yes">Upgrade this machine to Ubuntu Pro for security updates on a much wider range of packages, until 2032. Fulfill FedRAMP, FIPS, STIG and HIPAA and other compliance and hardening requirements with certified tooling and crypto-modules. Free up to 5 machines.
2104+
2105+More information on &lt;a href="https://ubuntu.com/pro"&gt;ubuntu.com/pro&lt;/a&gt;.</property>
2106+ <property name="use-markup">True</property>
2107+ <property name="wrap">True</property>
2108+ <property name="max-width-chars">130</property>
2109+ </object>
2110+ </child>
2111+ </object>
2112+ </child>
2113+ </object>
2114+ </child>
2115+ <child>
2116+ <object class="GtkLabel">
2117+ <property name="visible">True</property>
2118+ <property name="can-focus">False</property>
2119+ <property name="margin-top">40</property>
2120+ <property name="margin-bottom">6</property>
2121+ <property name="margin-left">30</property>
2122+ <property name="label" translatable="yes">Enable Ubuntu Pro for this installation or skip this step.</property>
2123+ <property name="xalign">0</property>
2124+ </object>
2125+ </child>
2126+ <child>
2127+ <object class="GtkBox">
2128+ <property name="visible">True</property>
2129+ <property name="can-focus">False</property>
2130+ <property name="orientation">vertical</property>
2131+ <property name="margin-left">30</property>
2132+ <property name="spacing">6</property>
2133+ <child>
2134+ <object class="GtkBox">
2135+ <property name="visible">True</property>
2136+ <property name="can-focus">False</property>
2137+ <property name="orientation">horizontal</property>
2138+ <child>
2139+ <object class="GtkRadioButton" id="enable_pro_select">
2140+ <property name="label" translatable="yes">Enable Ubuntu Pro</property>
2141+ <property name="visible">True</property>
2142+ <property name="can-focus">True</property>
2143+ <property name="receives-default">False</property>
2144+ <property name="margin-top">6</property>
2145+ <property name="xalign">0</property>
2146+ <property name="draw-indicator">True</property>
2147+ <property name="group">skip_pro_select</property>
2148+ </object>
2149+ </child>
2150+ <child>
2151+ <object class="GtkImage" id="pro_status_image">
2152+ <property name="can-focus">False</property>
2153+ <property name="stock">gtk-dialog-warning</property>
2154+ <property name="icon_size">2</property>
2155+ <property name="margin-left">30</property>
2156+ <property name="margin-right">6</property>
2157+ <property name="yalign">0.7</property>
2158+ </object>
2159+ </child>
2160+ <child>
2161+ <object class="GtkLabel" id="offline_warning">
2162+ <property name="yalign">0.8</property>
2163+ <property name="can-focus">False</property>
2164+ <property name="label" translatable="yes">An internet connection is required to enable Ubuntu Pro</property>
2165+ </object>
2166+ </child>
2167+ </object>
2168+ </child>
2169+ <child>
2170+ <object class="GtkBox">
2171+ <property name="visible">True</property>
2172+ <property name="can-focus">False</property>
2173+ <property name="orientation">vertical</property>
2174+ <child>
2175+ <object class="GtkRadioButton" id="skip_pro_select">
2176+ <property name="label" translatable="yes">Skip for now</property>
2177+ <property name="visible">True</property>
2178+ <property name="can-focus">True</property>
2179+ <property name="receives-default">False</property>
2180+ <property name="margin-top">12</property>
2181+ <property name="xalign">0</property>
2182+ <property name="active">True</property>
2183+ <property name="draw-indicator">True</property>
2184+ </object>
2185+ </child>
2186+ <child>
2187+ <object class="GtkAlignment">
2188+ <property name="visible">True</property>
2189+ <property name="can-focus">False</property>
2190+ <property name="left-padding">25</property>
2191+ <child>
2192+ <object class="GtkLabel">
2193+ <property name="visible">True</property>
2194+ <property name="can-focus">False</property>
2195+ <property name="label" translatable="yes">&lt;small&gt;You can always enable Ubuntu Pro later via the 'pro attach' command&lt;/small&gt;</property>
2196+ <property name="use-markup">True</property>
2197+ <property name="xalign">0</property>
2198+ </object>
2199+ </child>
2200+ </object>
2201+ </child>
2202+ </object>
2203+ </child>
2204+ </object>
2205+ </child>
2206+ </object>
2207+ </child>
2208+ </template>
2209+</interface>
2210diff --git a/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page.c b/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page.c
2211new file mode 100644
2212index 0000000..adc4ab2
2213--- /dev/null
2214+++ b/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page.c
2215@@ -0,0 +1,804 @@
2216+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2217+/*
2218+ * Copyright (C) 2022 Canonical Ltd.
2219+ *
2220+ * This program is free software; you can redistribute it and/or
2221+ * modify it under the terms of the GNU General Public License as
2222+ * published by the Free Software Foundation; either version 2 of the
2223+ * License, or (at your option) any later version.
2224+ *
2225+ * This program is distributed in the hope that it will be useful, but
2226+ * WITHOUT ANY WARRANTY; without even the implied warranty of
2227+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2228+ * General Public License for more details.
2229+ *
2230+ * You should have received a copy of the GNU General Public License
2231+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
2232+ */
2233+
2234+/* Canonical Ubuntu Pro page {{{1 */
2235+
2236+#define PAGE_ID "UbuntuPro"
2237+#define CHECK_ICON "/org/gnome/initial-setup/checkmark.svg"
2238+#define FAIL_ICON "/org/gnome/initial-setup/fail.svg"
2239+#define LOGO "/org/gnome/initial-setup/ubuntu-pro.svg"
2240+#define LOGO_DARK "/org/gnome/initial-setup/ubuntu-pro-dark.svg"
2241+
2242+#include "config.h"
2243+#include "gis-ubuntupro-page.h"
2244+#include "ubuntupro-resources.h"
2245+#include "utils.h"
2246+
2247+#include <glib/gi18n.h>
2248+#include <gio/gio.h>
2249+#include <polkit/polkit.h>
2250+#include <json-glib/json-glib.h>
2251+#include <time.h>
2252+#include <pthread.h>
2253+
2254+static gboolean SuccessfullyAttached = FALSE;
2255+
2256+typedef enum {
2257+ NONE = 0,
2258+ SUCCESS,
2259+ FAIL,
2260+ EXPIRED,
2261+} status_t;
2262+
2263+struct _GisUbuntuProPagePrivate {
2264+ GtkWidget *offer_page;
2265+ GtkWidget *attach_page;
2266+ GtkWidget *services_page;
2267+ GtkWidget *stack;
2268+
2269+ enum {
2270+ OFFER_PAGE,
2271+ ATTACH_PAGE,
2272+ SERVICES_PAGE,
2273+ } current_page;
2274+};
2275+typedef struct _GisUbuntuProPagePrivate GisUbuntuProPagePrivate;
2276+
2277+struct _GisUbuntuProOfferPagePrivate {
2278+ GtkWidget *ubuntu_pro_logo;
2279+ GtkWidget *enable_pro_select;
2280+ GtkWidget *skip_pro_select;
2281+ GtkWidget *offline_warning;
2282+ GtkWidget *pro_status_image;
2283+ gint network_cb;
2284+};
2285+typedef struct _GisUbuntuProOfferPagePrivate GisUbuntuProOfferPagePrivate;
2286+
2287+struct _GisUbuntuProAttachPagePrivate {
2288+ GtkWidget *ubuntu_pro_logo;
2289+ GtkWidget *pin_label;
2290+ GtkWidget *token_field;
2291+ GtkWidget *token_status;
2292+ GtkWidget *token_spinner;
2293+ GtkWidget *pin_spinner;
2294+ GtkWidget *token_status_icon;
2295+ GtkWidget *pin_hint;
2296+ GtkWidget *pin_status;
2297+ GtkWidget *pin_status_icon;
2298+ GtkWidget *token_radio;
2299+ GtkWidget *magic_radio;
2300+ /* This being here is a hack that makes changing the 2nd page's button
2301+ * from Skip to Next once the machine is attached possible. */
2302+ GisUbuntuProPage *main_page;
2303+
2304+ gboolean complete;
2305+ gboolean magic_token_polling;
2306+ gboolean attaching;
2307+ gint64 expired_by;
2308+ pthread_t poll_id;
2309+ enum active_radio_t {
2310+ MAGIC,
2311+ TOKEN,
2312+ } active_radio;
2313+ gchar *contract_token;
2314+ gchar *token;
2315+};
2316+typedef struct _GisUbuntuProAttachPagePrivate GisUbuntuProAttachPagePrivate;
2317+
2318+struct _GisUbuntuProServicesPagePrivate {
2319+ GtkWidget *enabled_services;
2320+ GtkWidget *enabled_services_header;
2321+ GtkWidget *available_services;
2322+ GtkWidget *available_services_header;
2323+ GtkWidget *contract_name;
2324+ GtkWidget *checkmark;
2325+};
2326+typedef struct _GisUbuntuProServicesPagePrivate GisUbuntuProServicesPagePrivate;
2327+
2328+struct _RestJSONResponse {
2329+ gint64 expiresIn;
2330+ gchar *token;
2331+ gchar *code;
2332+ gchar *contract_token;
2333+};
2334+
2335+typedef struct _RestJSONResponse RestJSONResponse;
2336+static void ua_attach(const gchar *, GisUbuntuProAttachPagePrivate *);
2337+static gboolean magic_parser(gchar*, RestJSONResponse*);
2338+static gboolean gis_ubuntupro_page_apply (GisPage*, GCancellable*);
2339+
2340+G_DEFINE_TYPE_WITH_PRIVATE (GisUbuntuProPage, gis_ubuntupro_page, GIS_TYPE_PAGE);
2341+G_DEFINE_TYPE_WITH_PRIVATE (GisUbuntuProOfferPage, gis_ubuntupro_offer_page, GTK_TYPE_BIN);
2342+G_DEFINE_TYPE_WITH_PRIVATE (GisUbuntuProAttachPage, gis_ubuntupro_attach_page, GTK_TYPE_BIN);
2343+G_DEFINE_TYPE_WITH_PRIVATE (GisUbuntuProServicesPage, gis_ubuntupro_services_page, GTK_TYPE_BIN);
2344+
2345+static void
2346+map_scalable_image(const gchar *path, GtkImage *widget, int x, int y)
2347+{
2348+ g_autoptr(GError) error = NULL;
2349+ GdkPixbuf *pixbuf;
2350+ pixbuf = gdk_pixbuf_new_from_resource_at_scale (path, x, y, TRUE, &error);
2351+ if (error) {
2352+ g_warning("Unable to create pixbuf: %s\n", error->message);
2353+ }
2354+
2355+ if (pixbuf != NULL) {
2356+ gtk_image_set_from_pixbuf(widget, pixbuf);
2357+ } else {
2358+ g_warning("Unable to load pixbuf: %s\n", error->message);
2359+ }
2360+}
2361+
2362+/* The user may go back any number of pages during attachment, so we can't
2363+ * blindly skip to the services list page.
2364+ * Furthermore, after attachment the user can again navigate back to any page
2365+ * and then hit the attach page again.
2366+ */
2367+static void
2368+consider_going_to_next_page(GisUbuntuProAttachPagePrivate *priv)
2369+{
2370+ static gboolean never_called = TRUE;
2371+ GisUbuntuProPage *page = GIS_UBUNTUPRO_PAGE(priv->main_page);
2372+ GisUbuntuProPagePrivate *main_priv = gis_ubuntupro_page_get_instance_private (page);
2373+
2374+ if (never_called && main_priv->current_page == ATTACH_PAGE) {
2375+ gis_page_set_complete(GIS_PAGE(priv->main_page), TRUE);
2376+ gis_ubuntupro_page_apply(GIS_PAGE(priv->main_page), NULL);
2377+ }
2378+ never_called = FALSE;
2379+}
2380+
2381+static void
2382+update_gui(GisUbuntuProAttachPagePrivate *priv, status_t status)
2383+{
2384+ GtkWidget *label, *icon, *spinner;
2385+ gchar *hint = _("Attach machine via your Ubuntu One account");
2386+ gboolean ready_to_attach;
2387+
2388+ if (priv->active_radio == MAGIC) {
2389+ icon = priv->pin_status_icon;
2390+ label = priv->pin_status;
2391+ spinner = priv->pin_spinner;
2392+ ready_to_attach = SuccessfullyAttached ||
2393+ (priv->contract_token && !priv->attaching);
2394+ } else {
2395+ icon = priv->token_status_icon;
2396+ label = priv->token_status;
2397+ spinner = priv->token_spinner;
2398+ ready_to_attach = SuccessfullyAttached || (!priv->attaching &&
2399+ gtk_entry_get_text_length(GTK_ENTRY(priv->token_field)) > 0);
2400+ }
2401+ gis_page_set_complete(GIS_PAGE(priv->main_page), ready_to_attach);
2402+
2403+ if (priv->attaching) {
2404+ gtk_spinner_start (GTK_SPINNER (spinner));
2405+ } else {
2406+ gtk_spinner_stop (GTK_SPINNER (spinner));
2407+ }
2408+
2409+ gtk_widget_set_sensitive (priv->token_radio, !priv->attaching);
2410+ gtk_widget_set_sensitive (priv->magic_radio, !priv->attaching);
2411+ gtk_widget_set_sensitive (priv->token_field, !priv->attaching &&
2412+ priv->active_radio == TOKEN);
2413+
2414+ if (SuccessfullyAttached) {
2415+ consider_going_to_next_page(priv);
2416+ status = SUCCESS;
2417+ }
2418+ switch (status) {
2419+ case SUCCESS:
2420+ gtk_label_set_markup(GTK_LABEL(label), g_strconcat(
2421+ "<span foreground=\"green\"><b>", _("Valid token"), "</b></span>", NULL
2422+ ));
2423+ gtk_image_set_from_resource(GTK_IMAGE(icon), CHECK_ICON);
2424+ gtk_widget_set_sensitive(GTK_WIDGET(priv->token_field), FALSE);
2425+ gtk_widget_set_sensitive(GTK_WIDGET(priv->magic_radio), FALSE);
2426+ gtk_widget_set_sensitive(GTK_WIDGET(priv->token_radio), FALSE);
2427+ break;
2428+ case FAIL:
2429+ gtk_label_set_markup(GTK_LABEL(label), g_strconcat(
2430+ "<span foreground=\"red\"><b>", _("Invalid token"), "</b></span>", NULL
2431+ ));
2432+ gtk_image_set_from_resource(GTK_IMAGE(icon), FAIL_ICON);
2433+ break;
2434+ case EXPIRED:
2435+ hint = "Click the button to generate a new code.";
2436+ gtk_label_set_markup(GTK_LABEL(label), _("Code expired"));
2437+ gtk_image_set_from_resource(GTK_IMAGE(icon), FAIL_ICON);
2438+ break;
2439+ default:
2440+ break;
2441+ }
2442+ gtk_label_set_text(GTK_LABEL(priv->pin_hint), hint);
2443+ gtk_widget_set_visible(GTK_WIDGET(priv->token_status_icon), FALSE);
2444+ gtk_widget_set_visible(GTK_WIDGET(priv->token_status), FALSE);
2445+ gtk_widget_set_visible(GTK_WIDGET(priv->pin_status_icon), FALSE);
2446+ gtk_widget_set_visible(GTK_WIDGET(priv->pin_status), FALSE);
2447+ gtk_widget_set_visible(GTK_WIDGET(icon), status);
2448+ gtk_widget_set_visible(GTK_WIDGET(label), status);
2449+}
2450+
2451+static void
2452+network_status_changed (GNetworkMonitor *monitor,
2453+ gboolean available,
2454+ gpointer user_data)
2455+{
2456+ GisUbuntuProOfferPagePrivate *priv = user_data;
2457+
2458+ if (!available) {
2459+ gtk_widget_set_sensitive (priv->enable_pro_select, FALSE);
2460+ gtk_widget_show (GTK_WIDGET (priv->offline_warning));
2461+ gtk_widget_show (GTK_WIDGET (priv->pro_status_image));
2462+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->skip_pro_select), TRUE);
2463+ gtk_image_set_from_icon_name (GTK_IMAGE(priv->pro_status_image), "gtk-dialog-warning", GTK_ICON_SIZE_DND);
2464+ }
2465+ else {
2466+ gtk_widget_set_sensitive (priv->enable_pro_select, TRUE);
2467+ gtk_widget_hide (GTK_WIDGET (priv->offline_warning));
2468+ gtk_widget_hide (GTK_WIDGET (priv->pro_status_image));
2469+ }
2470+}
2471+
2472+static void
2473+gis_ubuntupro_page_dispose (GObject *object)
2474+{
2475+ GisUbuntuProPage *page = GIS_UBUNTUPRO_PAGE (object);
2476+ GisUbuntuProPagePrivate *priv = gis_ubuntupro_page_get_instance_private (page);
2477+ GisUbuntuProOfferPage *offer_page = GIS_UBUNTUPRO_OFFER_PAGE (priv->offer_page);
2478+ GisUbuntuProOfferPagePrivate *offer_priv = gis_ubuntupro_offer_page_get_instance_private (offer_page);
2479+
2480+ GNetworkMonitor *network_monitor = g_network_monitor_get_default ();
2481+ g_signal_handler_disconnect(network_monitor, offer_priv->network_cb);
2482+
2483+ G_OBJECT_CLASS (gis_ubuntupro_page_parent_class)->dispose (object);
2484+}
2485+
2486+static void
2487+gis_ubuntupro_page_constructed (GObject *object)
2488+{
2489+ GisUbuntuProPage *page = GIS_UBUNTUPRO_PAGE (object);
2490+ GisUbuntuProPagePrivate *priv = gis_ubuntupro_page_get_instance_private (page);
2491+ GisUbuntuProOfferPage *offer_page = GIS_UBUNTUPRO_OFFER_PAGE (priv->offer_page);
2492+ GisUbuntuProOfferPagePrivate *offer_priv = gis_ubuntupro_offer_page_get_instance_private (offer_page);
2493+ GisUbuntuProAttachPage *attach_page = GIS_UBUNTUPRO_ATTACH_PAGE (priv->attach_page);
2494+ GisUbuntuProAttachPagePrivate *attach_priv = gis_ubuntupro_attach_page_get_instance_private (attach_page);
2495+ GisUbuntuProServicesPage *services_page = GIS_UBUNTUPRO_SERVICES_PAGE(priv->services_page);
2496+ GisUbuntuProServicesPagePrivate *services_priv = gis_ubuntupro_services_page_get_instance_private (services_page);
2497+
2498+ gchar *logo = is_dark_theme(GTK_WIDGET(page)) ? LOGO_DARK: LOGO;
2499+ map_scalable_image(logo, GTK_IMAGE(attach_priv->ubuntu_pro_logo), -1, 66);
2500+ map_scalable_image(logo, GTK_IMAGE(offer_priv->ubuntu_pro_logo), -1, 66);
2501+ map_scalable_image(CHECK_ICON, GTK_IMAGE(services_priv->checkmark), 30, -1);
2502+
2503+ G_OBJECT_CLASS (gis_ubuntupro_page_parent_class)->constructed (object);
2504+
2505+ GNetworkMonitor *network_monitor = g_network_monitor_get_default ();
2506+ offer_priv->network_cb = g_signal_connect (network_monitor, "network-changed",
2507+ G_CALLBACK (network_status_changed), priv);
2508+
2509+ if (!g_network_monitor_get_network_available (network_monitor)) {
2510+ gtk_widget_set_sensitive (offer_priv->enable_pro_select, FALSE);
2511+ gtk_widget_show (GTK_WIDGET (offer_priv->offline_warning));
2512+ gtk_widget_show (GTK_WIDGET (offer_priv->pro_status_image));
2513+ }
2514+
2515+ /* Initializate priv values */
2516+ attach_priv->magic_token_polling = FALSE;
2517+ attach_priv->active_radio = MAGIC;
2518+ attach_priv->expired_by = 0;
2519+ attach_priv->contract_token = NULL;
2520+ attach_priv->main_page = page;
2521+ priv->current_page = OFFER_PAGE;
2522+
2523+ gis_page_set_skippable (GIS_PAGE (page), FALSE);
2524+ gis_page_set_complete (GIS_PAGE (page), TRUE);
2525+ gtk_widget_show (GTK_WIDGET (page));
2526+}
2527+
2528+static void
2529+gis_ubuntupro_page_locale_changed (GisPage *page)
2530+{
2531+ gis_page_set_title (GIS_PAGE (page), _("Ubuntu Pro"));
2532+}
2533+
2534+static gboolean
2535+poll_token_attach (GisUbuntuProAttachPagePrivate *priv)
2536+{
2537+ gchar *std_out, *std_err, *cmd;
2538+ RestJSONResponse resp; /* relevant response fields */
2539+ g_autoptr(GError) error = NULL;
2540+
2541+ cmd = g_strconcat("/bin/ua api u.pro.attach.magic.wait.v1 --args magic_token=", priv->token, NULL);
2542+ if (!g_spawn_command_line_sync(cmd, &std_out, &std_err, NULL, &error)) {
2543+ g_warning ("Failed to request magic token: %s", error->message);
2544+ }
2545+ else if (!magic_parser(std_out, &resp)) {
2546+ g_warning("Couldn't parse response.");
2547+ }
2548+ else if (resp.contract_token != NULL) {
2549+ priv->contract_token = g_strdup(resp.contract_token);
2550+ return TRUE;
2551+ }
2552+ return FALSE;
2553+}
2554+
2555+static void*
2556+poll_magic_token (void *data)
2557+{
2558+ GisUbuntuProAttachPagePrivate *priv = gis_ubuntupro_attach_page_get_instance_private (data);
2559+ status_t status;
2560+
2561+ if (priv->active_radio != MAGIC) {
2562+ pthread_exit(NULL);
2563+ }
2564+ priv->magic_token_polling = TRUE;
2565+
2566+ if (time(0) > priv->expired_by) {
2567+ status = EXPIRED;
2568+ update_gui(priv, status);
2569+ priv->magic_token_polling = FALSE;
2570+ }
2571+ gboolean token_received = poll_token_attach(priv);
2572+ if (token_received) {
2573+ priv->magic_token_polling = FALSE;
2574+ status = SUCCESS;
2575+ update_gui(priv, status);
2576+ }
2577+ pthread_exit(NULL);
2578+}
2579+
2580+static gboolean
2581+magic_parser(gchar *ptr, /* pointer to actual response */
2582+ RestJSONResponse *resp) /* relevant response fields */
2583+{
2584+ g_autoptr(JsonParser) parser = json_parser_new();
2585+ JsonNode *root;
2586+ g_autoptr(GError) error = NULL;
2587+
2588+ json_parser_load_from_data(parser, ptr, -1, &error);
2589+ if (error) {
2590+ g_warning("Couldn't parse magic token JSON; %s\n", error->message);
2591+ g_print("%s\n", ptr);
2592+ return FALSE;
2593+ }
2594+ root = json_parser_get_root(parser);
2595+ if (!JSON_NODE_HOLDS_OBJECT(root)) {
2596+ g_warning("Invalid magic token JSON\n");
2597+ return FALSE;
2598+ }
2599+
2600+ JsonObject *data, *attributes;
2601+ JsonObject *response = json_node_get_object(root);
2602+ data = json_object_get_object_member(response, "data");
2603+ attributes = json_object_get_object_member(data, "attributes");
2604+
2605+ resp->expiresIn = json_object_has_member(attributes, "expires_in") ?
2606+ json_object_get_int_member(attributes, "expires_in") :
2607+ 0;
2608+ resp->token = json_object_has_member(attributes, "token") ?
2609+ g_strdup(json_object_get_string_member(attributes, "token")) :
2610+ NULL;
2611+ resp->code = json_object_has_member(attributes, "user_code") ?
2612+ g_strdup(json_object_get_string_member(attributes, "user_code")) :
2613+ NULL;
2614+ resp->contract_token = json_object_has_member(attributes, "contract_token") ?
2615+ g_strdup(json_object_get_string_member(attributes, "contract_token")) :
2616+ NULL;
2617+
2618+ return TRUE;
2619+}
2620+
2621+static void
2622+display_ua_services(GisUbuntuProServicesPagePrivate *priv)
2623+{
2624+ g_autoptr(JsonParser) parser = json_parser_new();
2625+ JsonNode *root_node;
2626+ JsonObject *root, *services, *contract;
2627+ JsonArray *services_array;
2628+ g_autoptr(GError) error = NULL;
2629+ guint i, n_services;
2630+ const char *status, *description, *available, *contract_name;
2631+ gsize len;
2632+ g_autofree gchar *str, *enabled_str, *available_str;
2633+
2634+ if (!parse_ua_status(&str, &len)) {
2635+ return;
2636+ }
2637+ available_str = (gchar *)g_malloc0(len);
2638+ enabled_str = (gchar *)g_malloc0(len);
2639+
2640+ json_parser_load_from_data(parser, str, strlen(str), &error);
2641+ if (error) {
2642+ g_warning("Couldn't parse magic token JSON; %s\n", error->message);
2643+ }
2644+ root_node = json_parser_get_root(parser);
2645+ if (!JSON_NODE_HOLDS_OBJECT(root_node)) {
2646+ g_warning("Invalid magic token JSON\n");
2647+ }
2648+
2649+ root = json_node_get_object(root_node);
2650+
2651+ contract = json_object_get_object_member(root, "contract");
2652+ contract_name = json_object_get_string_member(contract, "name");
2653+ gtk_label_set_text(GTK_LABEL(priv->contract_name), contract_name);
2654+
2655+ services_array = json_object_get_array_member(root, "services");
2656+ n_services = json_array_get_length(services_array);
2657+
2658+ /* Get services description, status and availability */
2659+ for (i = 0; i < n_services; i++) {
2660+ services = json_array_get_object_element(services_array, i);
2661+ if (
2662+ json_object_has_member(services, "description") &&
2663+ json_object_has_member(services, "status") &&
2664+ json_object_has_member(services, "available")
2665+ ) {
2666+ description = json_object_get_string_member(services, "description");
2667+ status = json_object_get_string_member(services, "status");
2668+ available = json_object_get_string_member(services, "available");
2669+ if (strcmp(status, "enabled") == 0) {
2670+ itemize(enabled_str, description, len);
2671+ } else if (strcmp(available, "yes") == 0) {
2672+ itemize(available_str, description, len);
2673+ }
2674+ }
2675+ }
2676+
2677+ /* Display enabled and disabled but available services */
2678+ if (enabled_str == NULL) {
2679+ gtk_widget_destroy(GTK_WIDGET(priv->enabled_services_header));
2680+ } else {
2681+ gtk_label_set_text(GTK_LABEL(priv->enabled_services), enabled_str);
2682+ }
2683+ if (available_str == NULL) {
2684+ gtk_widget_destroy(GTK_WIDGET(priv->available_services_header));
2685+ } else {
2686+ gtk_label_set_text(GTK_LABEL(priv->available_services), available_str);
2687+ }
2688+}
2689+
2690+static void
2691+request_magic_attach (GisUbuntuProAttachPage *page)
2692+{
2693+ GisUbuntuProAttachPagePrivate *priv = gis_ubuntupro_attach_page_get_instance_private (page);
2694+ gchar *std_out, *std_err;
2695+ g_autoptr(GError) error = NULL;
2696+ int rc;
2697+
2698+ if (priv->magic_token_polling) {
2699+ return;
2700+ }
2701+
2702+ if (!g_spawn_command_line_sync("/bin/ua api u.pro.attach.magic.initiate.v1",
2703+ &std_out, &std_err, NULL, &error)) {
2704+ g_warning ("Failed to request magic token: %s", error->message);
2705+ return;
2706+ }
2707+
2708+ RestJSONResponse resp;
2709+ if (!magic_parser(std_out, &resp)) {
2710+ g_print("%s\n",std_out);
2711+ g_warning("Couldn't parse response.\n");
2712+ return;
2713+ }
2714+ gtk_label_set_text (GTK_LABEL (priv->pin_label), resp.code);
2715+ priv->expired_by = resp.expiresIn + time(0);
2716+ priv->token = g_strdup(resp.token);
2717+ priv->magic_token_polling = TRUE;
2718+ rc = pthread_create(&priv->poll_id, NULL, poll_magic_token, (void *)page);
2719+ if (rc) {
2720+ g_warning ("Couldn't create PIN polling thread");
2721+ return;
2722+ }
2723+ update_gui(priv, 0);
2724+ g_free(resp.token);
2725+ g_free(resp.code);
2726+}
2727+
2728+static void
2729+on_ua_attach_requested (GObject *source,
2730+ GAsyncResult *result,
2731+ gpointer user_data)
2732+{
2733+ GisUbuntuProAttachPagePrivate *priv = user_data;
2734+ g_autoptr(GError) error = NULL;
2735+ g_autoptr(GVariant) retval = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
2736+ status_t status;
2737+
2738+ if (retval == NULL) {
2739+ g_warning ("Failed to attach token: %s", error->message);
2740+ status = FAIL;
2741+ } else {
2742+ SuccessfullyAttached = TRUE;
2743+ status = SUCCESS;
2744+ pthread_cancel(priv->poll_id);
2745+ gis_page_set_complete (GIS_PAGE(priv->main_page), TRUE);
2746+ g_free(priv->contract_token);
2747+ g_free(priv->token);
2748+ }
2749+ priv->attaching = FALSE;
2750+ update_gui(priv, status);
2751+}
2752+
2753+static void
2754+ua_attach(const gchar *token, GisUbuntuProAttachPagePrivate *priv)
2755+{
2756+ GDBusConnection *bus;
2757+ g_autoptr(GError) error = NULL;
2758+ status_t status = NONE;
2759+
2760+ if (priv->attaching) {
2761+ return;
2762+ }
2763+
2764+ bus = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
2765+ if (bus == NULL) {
2766+ g_warning("Failed to get system bus: %s", error->message);
2767+ } else {
2768+ priv->attaching = TRUE;
2769+ update_gui(priv, status);
2770+
2771+ g_dbus_connection_call(bus,
2772+ "com.canonical.UbuntuAdvantage",
2773+ "/com/canonical/UbuntuAdvantage/Manager",
2774+ "com.canonical.UbuntuAdvantage.Manager",
2775+ "Attach",
2776+ g_variant_new("(s)", token),
2777+ G_VARIANT_TYPE("()"),
2778+ G_DBUS_CALL_FLAGS_NONE,
2779+ 543210, /* I have observed that -1, the default timeout, is not enough. */
2780+ NULL,
2781+ on_ua_attach_requested,
2782+ priv);
2783+ }
2784+}
2785+
2786+void
2787+on_token_typing (GtkButton *button, GisUbuntuProAttachPage *page)
2788+{
2789+ GisUbuntuProAttachPagePrivate *priv = gis_ubuntupro_attach_page_get_instance_private (page);
2790+
2791+ gis_page_set_complete (
2792+ GIS_PAGE(priv->main_page),
2793+ gtk_entry_get_text_length(GTK_ENTRY(priv->token_field)) > 0
2794+ );
2795+}
2796+
2797+void
2798+request_token_attach (GtkButton *button, GisUbuntuProAttachPage *page)
2799+{
2800+ GisUbuntuProAttachPagePrivate *priv = gis_ubuntupro_attach_page_get_instance_private (page);
2801+
2802+ const gchar *token = gtk_entry_get_text(GTK_ENTRY(priv->token_field));
2803+ ua_attach(token, priv);
2804+ gtk_entry_set_text(GTK_ENTRY(priv->token_field), "");
2805+}
2806+
2807+void
2808+on_magic_clicked (GtkButton *button, GisUbuntuProAttachPage *page)
2809+{
2810+ GisUbuntuProAttachPagePrivate *priv = gis_ubuntupro_attach_page_get_instance_private (page);
2811+ priv->active_radio = MAGIC;
2812+
2813+ request_magic_attach(page);
2814+}
2815+
2816+void
2817+on_radio_toggled (GtkButton *button, GisUbuntuProAttachPage *page)
2818+{
2819+ GisUbuntuProAttachPagePrivate *priv = gis_ubuntupro_attach_page_get_instance_private (page);
2820+ status_t status = NONE;
2821+
2822+ priv->active_radio = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->token_radio)) ? TOKEN : MAGIC;
2823+ update_gui(priv, status);
2824+}
2825+
2826+/* Callback for the Previous button */
2827+static gboolean
2828+gis_ubuntupro_page_go_back (GisPage *gis_page)
2829+{
2830+ GisUbuntuProPage *page = GIS_UBUNTUPRO_PAGE (gis_page);
2831+ GisUbuntuProPagePrivate *priv = gis_ubuntupro_page_get_instance_private (page);
2832+ GisUbuntuProOfferPage *offer_page = GIS_UBUNTUPRO_OFFER_PAGE (priv->offer_page);
2833+ GisUbuntuProAttachPage *attach_page = GIS_UBUNTUPRO_ATTACH_PAGE (priv->attach_page);
2834+
2835+ switch (priv->current_page) {
2836+ case OFFER_PAGE:
2837+ return FALSE;
2838+ case ATTACH_PAGE:
2839+ gis_page_set_complete (gis_page, TRUE);
2840+ gtk_stack_set_visible_child (GTK_STACK (priv->stack), GTK_WIDGET(offer_page));
2841+ priv->current_page = OFFER_PAGE;
2842+ break;
2843+ case SERVICES_PAGE:
2844+ gtk_stack_set_visible_child (GTK_STACK (priv->stack), GTK_WIDGET(attach_page));
2845+ priv->current_page = ATTACH_PAGE;
2846+ break;
2847+ }
2848+ return TRUE;
2849+}
2850+
2851+/* Callback for the Next button */
2852+static gboolean
2853+gis_ubuntupro_page_apply (GisPage *gis_page,
2854+ GCancellable *cancellable)
2855+{
2856+ GisUbuntuProPage *page = GIS_UBUNTUPRO_PAGE (gis_page);
2857+ GisUbuntuProPagePrivate *priv = gis_ubuntupro_page_get_instance_private (page);
2858+ GisUbuntuProOfferPage *offer_page = GIS_UBUNTUPRO_OFFER_PAGE (priv->offer_page);
2859+ GisUbuntuProOfferPagePrivate *offer_priv = gis_ubuntupro_offer_page_get_instance_private (offer_page);
2860+ GisUbuntuProAttachPage *attach_page = GIS_UBUNTUPRO_ATTACH_PAGE (priv->attach_page);
2861+ GisUbuntuProAttachPagePrivate *attach_priv = gis_ubuntupro_attach_page_get_instance_private (attach_page);
2862+ switch (priv->current_page) {
2863+ case OFFER_PAGE:
2864+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (offer_priv->skip_pro_select))) {
2865+ return FALSE;
2866+ }
2867+ /* Request magic token already if didn't yet and advance to next local page */
2868+ if (!SuccessfullyAttached && !attach_priv->attaching) {
2869+ request_magic_attach(attach_page);
2870+ }
2871+ gis_page_apply_complete (GIS_PAGE (page), FALSE);
2872+ update_gui(attach_priv, 0);
2873+ gtk_stack_set_visible_child (GTK_STACK (priv->stack), GTK_WIDGET(attach_page));
2874+ priv->current_page = ATTACH_PAGE;
2875+ break;
2876+ case ATTACH_PAGE:
2877+ if (SuccessfullyAttached) {
2878+ GisUbuntuProServicesPage *services_page = GIS_UBUNTUPRO_SERVICES_PAGE(priv->services_page);
2879+ display_ua_services(gis_ubuntupro_services_page_get_instance_private (services_page));
2880+ gtk_stack_set_visible_child (GTK_STACK (priv->stack), priv->services_page);
2881+ gis_page_apply_complete (GIS_PAGE (page), FALSE);
2882+ } else {
2883+ if (attach_priv->active_radio == MAGIC) {
2884+ ua_attach(attach_priv->contract_token, attach_priv);
2885+ } else {
2886+ request_token_attach(NULL, attach_page);
2887+ }
2888+ gis_page_apply_complete (GIS_PAGE (page), FALSE);
2889+ return FALSE;
2890+ }
2891+ priv->current_page = SERVICES_PAGE;
2892+ break;
2893+ case SERVICES_PAGE:
2894+ return FALSE;
2895+ }
2896+ return TRUE;
2897+}
2898+
2899+static void
2900+gis_ubuntupro_page_class_init (GisUbuntuProPageClass *klass)
2901+{
2902+ GisPageClass *page_class = GIS_PAGE_CLASS (klass);
2903+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
2904+
2905+ gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), "/org/gnome/initial-setup/gis-ubuntupro-page.ui");
2906+
2907+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage, offer_page);
2908+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage, attach_page);
2909+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage, services_page);
2910+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage, stack);
2911+ gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), request_token_attach);
2912+ gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), request_magic_attach);
2913+ gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), on_radio_toggled);
2914+ gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), on_magic_clicked);
2915+
2916+ page_class->page_id = PAGE_ID;
2917+ page_class->locale_changed = gis_ubuntupro_page_locale_changed;
2918+ page_class->apply = gis_ubuntupro_page_apply;
2919+ page_class->go_back = gis_ubuntupro_page_go_back;
2920+ object_class->constructed = gis_ubuntupro_page_constructed;
2921+ object_class->dispose = gis_ubuntupro_page_dispose;
2922+}
2923+static void
2924+gis_ubuntupro_offer_page_class_init (GisUbuntuProOfferPageClass *klass)
2925+{
2926+ gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), "/org/gnome/initial-setup/gis-ubuntupro-offer-page.ui");
2927+
2928+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProOfferPage, ubuntu_pro_logo);
2929+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProOfferPage, enable_pro_select);
2930+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProOfferPage, skip_pro_select);
2931+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProOfferPage, offline_warning);
2932+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProOfferPage, pro_status_image);
2933+}
2934+
2935+static void
2936+gis_ubuntupro_attach_page_class_init (GisUbuntuProAttachPageClass *klass)
2937+{
2938+ gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), "/org/gnome/initial-setup/gis-ubuntupro-attach-page.ui");
2939+
2940+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProAttachPage, ubuntu_pro_logo);
2941+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProAttachPage, pin_label);
2942+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProAttachPage, token_field);
2943+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProAttachPage, token_status);
2944+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProAttachPage, token_spinner);
2945+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProAttachPage, token_status_icon);
2946+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProAttachPage, pin_status);
2947+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProAttachPage, pin_spinner);
2948+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProAttachPage, pin_status_icon);
2949+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProAttachPage, pin_hint);
2950+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProAttachPage, token_radio);
2951+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProAttachPage, magic_radio);
2952+}
2953+static void
2954+gis_ubuntupro_services_page_class_init (GisUbuntuProServicesPageClass *klass)
2955+{
2956+ gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), "/org/gnome/initial-setup/gis-ubuntupro-services-page.ui");
2957+
2958+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProServicesPage, enabled_services);
2959+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProServicesPage, enabled_services_header);
2960+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProServicesPage, available_services);
2961+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProServicesPage, available_services_header);
2962+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProServicesPage, contract_name);
2963+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProServicesPage, checkmark);
2964+}
2965+
2966+static void
2967+gis_ubuntupro_page_init (GisUbuntuProPage *page)
2968+{
2969+ g_resources_register (ubuntupro_get_resource ());
2970+
2971+ /* Magic that makes stuff compile */
2972+ gis_ubuntupro_offer_page_get_type ();
2973+ gis_ubuntupro_attach_page_get_type ();
2974+ gis_ubuntupro_services_page_get_type ();
2975+
2976+ gtk_widget_init_template (GTK_WIDGET (page));
2977+}
2978+static void
2979+gis_ubuntupro_offer_page_init (GisUbuntuProOfferPage *page)
2980+{
2981+ g_resources_register (ubuntupro_get_resource ());
2982+
2983+ gtk_widget_init_template (GTK_WIDGET (page));
2984+}
2985+static void
2986+gis_ubuntupro_attach_page_init (GisUbuntuProAttachPage *page)
2987+{
2988+ g_resources_register (ubuntupro_get_resource ());
2989+
2990+ gtk_widget_init_template (GTK_WIDGET (page));
2991+}
2992+static void
2993+gis_ubuntupro_services_page_init (GisUbuntuProServicesPage *page)
2994+{
2995+ g_resources_register (ubuntupro_get_resource ());
2996+
2997+ gtk_widget_init_template (GTK_WIDGET (page));
2998+}
2999+
3000+GisPage *
3001+gis_prepare_ubuntu_pro_page (GisDriver *driver)
3002+{
3003+ gboolean attached;
3004+ if (!get_ubuntu_advantage_attached (&attached)) {
3005+ g_warning("Couldn't fetch the Ubuntu Pro status.\n");
3006+ return NULL;
3007+ }
3008+ if (attached) {
3009+ g_warning("Ubuntu Pro is already attached. Skipping Ubuntu Pro pages.\n");
3010+ return NULL;
3011+ }
3012+
3013+ if (!is_ubuntupro_supported ())
3014+ return NULL;
3015+
3016+ return g_object_new (GIS_TYPE_UBUNTUPRO_PAGE,
3017+ "driver", driver,
3018+ NULL);
3019+}
3020diff --git a/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page.h b/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page.h
3021new file mode 100644
3022index 0000000..50676c2
3023--- /dev/null
3024+++ b/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page.h
3025@@ -0,0 +1,110 @@
3026+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
3027+/*
3028+ * Copyright (C) 2018 Canonical Ltd.
3029+ *
3030+ * This program is free software; you can redistribute it and/or
3031+ * modify it under the terms of the GNU General Public License as
3032+ * published by the Free Software Foundation; either version 2 of the
3033+ * License, or (at your option) any later version.
3034+ *
3035+ * This program is distributed in the hope that it will be useful, but
3036+ * WITHOUT ANY WARRANTY; without even the implied warranty of
3037+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3038+ * General Public License for more details.
3039+ *
3040+ * You should have received a copy of the GNU General Public License
3041+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
3042+ */
3043+
3044+#ifndef __GIS_UBUNTUPRO_PAGE_H__
3045+#define __GIS_UBUNTUPRO_PAGE_H__
3046+
3047+#include "gnome-initial-setup.h"
3048+
3049+G_BEGIN_DECLS
3050+
3051+#define GIS_TYPE_UBUNTUPRO_PAGE (gis_ubuntupro_page_get_type ())
3052+#define GIS_TYPE_UBUNTUPRO_OFFER_PAGE (gis_ubuntupro_offer_page_get_type ())
3053+#define GIS_TYPE_UBUNTUPRO_ATTACH_PAGE (gis_ubuntupro_attach_page_get_type ())
3054+#define GIS_TYPE_UBUNTUPRO_SERVICES_PAGE (gis_ubuntupro_services_page_get_type ())
3055+
3056+#define GIS_UBUNTUPRO_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIS_TYPE_UBUNTUPRO_PAGE, GisUbuntuProPage))
3057+#define GIS_UBUNTUPRO_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIS_TYPE_UBUNTUPRO_PAGE, GisUbuntuProPageClass))
3058+#define GIS_UBUNTUPRO_OFFER_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIS_TYPE_UBUNTUPRO_OFFER_PAGE, GisUbuntuProOfferPage))
3059+#define GIS_UBUNTUPRO_OFFER_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIS_TYPE_UBUNTUPRO_OFFER_PAGE, GisUbuntuProOfferPageClass))
3060+#define GIS_UBUNTUPRO_ATTACH_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIS_TYPE_UBUNTUPRO_ATTACH_PAGE, GisUbuntuProAttachPage))
3061+#define GIS_UBUNTUPRO_ATTACH_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIS_TYPE_UBUNTUPRO_ATTACH_PAGE, GisUbuntuProAttachPageClass))
3062+#define GIS_UBUNTUPRO_SERVICES_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIS_TYPE_UBUNTUPRO_SERVICES_PAGE, GisUbuntuProServicesPage))
3063+#define GIS_UBUNTUPRO_SERVICES_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIS_TYPE_UBUNTUPRO_SERVICES_PAGE, GisUbuntuProServicesPageClass))
3064+
3065+#define GIS_IS_UBUNTUPRO_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIS_TYPE_UBUNTUPRO_PAGE))
3066+#define GIS_IS_UBUNTUPRO_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIS_TYPE_UBUNTUPRO_PAGE))
3067+#define GIS_IS_UBUNTUPRO_OFFER_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIS_TYPE_UBUNTUPRO_OFFER_PAGE))
3068+#define GIS_IS_UBUNTUPRO_OFFER_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIS_TYPE_UBUNTUPRO_OFFER_PAGE))
3069+#define GIS_IS_UBUNTUPRO_ATTACH_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIS_TYPE_UBUNTUPRO_ATTACH_PAGE))
3070+#define GIS_IS_UBUNTUPRO_ATTACH_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIS_TYPE_UBUNTUPRO_ATTACH_PAGE))
3071+#define GIS_IS_UBUNTUPRO_SERVICES_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIS_TYPE_UBUNTUPRO_SERVICES_PAGE))
3072+#define GIS_IS_UBUNTUPRO_SERVICES_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIS_TYPE_UBUNTUPRO_SERVICES_PAGE))
3073+
3074+#define GIS_UBUNTUPRO_PAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIS_TYPE_UBUNTUPRO_PAGE, GisUbuntuProPageClass))
3075+#define GIS_UBUNTUPRO_OFFER_PAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIS_TYPE_UBUNTUPRO_OFFER_PAGE, GisUbuntuProOfferPageClass))
3076+#define GIS_UBUNTUPRO_ATTACH_PAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIS_TYPE_UBUNTUPRO_ATTACH_PAGE, GisUbuntuProAttachPageClass))
3077+#define GIS_UBUNTUPRO_SERVICES_PAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIS_TYPE_UBUNTUPRO_SERVICES_PAGE, GisUbuntuProServicesPageClass))
3078+
3079+typedef struct _GisUbuntuProPage GisUbuntuProPage;
3080+typedef struct _GisUbuntuProPageClass GisUbuntuProPageClass;
3081+typedef struct _GisUbuntuProOfferPage GisUbuntuProOfferPage;
3082+typedef struct _GisUbuntuProOfferPageClass GisUbuntuProOfferPageClass;
3083+typedef struct _GisUbuntuProAttachPage GisUbuntuProAttachPage;
3084+typedef struct _GisUbuntuProAttachPageClass GisUbuntuProAttachPageClass;
3085+typedef struct _GisUbuntuProServicesPage GisUbuntuProServicesPage;
3086+typedef struct _GisUbuntuProServicesPageClass GisUbuntuProServicesPageClass;
3087+
3088+struct _GisUbuntuProPage
3089+{
3090+ GisPage parent;
3091+};
3092+struct _GisUbuntuProPageClass
3093+{
3094+ GisPageClass parent_class;
3095+};
3096+
3097+struct _GisUbuntuProOfferPage
3098+{
3099+ GtkBin parent;
3100+};
3101+struct _GisUbuntuProOfferPageClass
3102+{
3103+ GtkBinClass parent_class;
3104+};
3105+
3106+struct _GisUbuntuProAttachPage
3107+{
3108+ GtkBin parent;
3109+};
3110+struct _GisUbuntuProAttachPageClass
3111+{
3112+ GtkBinClass parent_class;
3113+};
3114+
3115+struct _GisUbuntuProServicesPage
3116+{
3117+ GtkBin parent;
3118+};
3119+
3120+struct _GisUbuntuProServicesPageClass
3121+{
3122+ GtkBinClass parent_class;
3123+};
3124+
3125+GType gis_ubuntupro_page_get_type (void);
3126+GType gis_ubuntupro_offer_page_get_type (void);
3127+GType gis_ubuntupro_attach_page_get_type (void);
3128+GType gis_ubuntupro_services_page_get_type (void);
3129+
3130+GisPage *gis_prepare_ubuntu_pro_page (GisDriver *driver);
3131+
3132+
3133+G_END_DECLS
3134+
3135+#endif /* __GIS_UBUNTUPRO_PAGE_H__ */
3136diff --git a/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page.ui b/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page.ui
3137new file mode 100644
3138index 0000000..883c92c
3139--- /dev/null
3140+++ b/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page.ui
3141@@ -0,0 +1,39 @@
3142+<?xml version="1.0" encoding="UTF-8"?>
3143+<interface>
3144+ <!-- interface-requires gtk+ 3.0 -->
3145+ <template class="GisUbuntuProPage" parent="GisPage">
3146+ <child>
3147+ <object class="GtkBox" id="stepUbuntuPro">
3148+ <property name="visible">True</property>
3149+ <property name="can-focus">False</property>
3150+ <child>
3151+ <object class="GtkStack" id="stack">
3152+ <property name="visible">True</property>
3153+ <property name="valign">start</property>
3154+ <property name="vexpand">True</property>
3155+ <child>
3156+ <object class="GisUbuntuProOfferPage" id="offer_page">
3157+ <property name="visible">True</property>
3158+ </object>
3159+ </child>
3160+ <child>
3161+ <object class="GisUbuntuProAttachPage" id="attach_page">
3162+ <property name="visible">True</property>
3163+ </object>
3164+ </child>
3165+ <child>
3166+ <object class="GisUbuntuProServicesPage" id="services_page">
3167+ <property name="visible">True</property>
3168+ </object>
3169+ </child>
3170+ </object>
3171+ <packing>
3172+ <property name="expand">False</property>
3173+ <property name="fill">True</property>
3174+ <property name="position">0</property>
3175+ </packing>
3176+ </child>
3177+ </object>
3178+ </child>
3179+ </template>
3180+</interface>
3181diff --git a/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-services-page.ui b/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-services-page.ui
3182new file mode 100644
3183index 0000000..8fcbe2a
3184--- /dev/null
3185+++ b/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-services-page.ui
3186@@ -0,0 +1,124 @@
3187+<?xml version="1.0" encoding="UTF-8"?>
3188+<interface>
3189+ <!-- interface-requires gtk+ 3.0 -->
3190+ <template class="GisUbuntuProServicesPage" parent="GtkBin">
3191+ <child>
3192+ <object class="GtkBox" id="services_page">
3193+ <property name="visible">True</property>
3194+ <property name="can-focus">False</property>
3195+ <property name="no-show-all">True</property>
3196+ <property name="orientation">vertical</property>
3197+ <child>
3198+ <object class="GtkLabel">
3199+ <property name="visible">True</property>
3200+ <property name="can-focus">False</property>
3201+ <property name="halign">start</property>
3202+ <property name="margin-top">3</property>
3203+ <property name="margin-bottom">0</property>
3204+ <property name="label" translatable="yes">&lt;b&gt;Ubuntu Pro Services&lt;/b&gt;</property>
3205+ <property name="use-markup">True</property>
3206+ <property name="wrap">True</property>
3207+ <property name="xalign">0</property>
3208+ <attributes>
3209+ <attribute name="scale" value="1.4"/>
3210+ </attributes>
3211+ </object>
3212+ </child>
3213+ <child>
3214+ <object class="GtkSeparator">
3215+ <property name="visible">True</property>
3216+ <property name="can-focus">False</property>
3217+ <property name="margin-top">10</property>
3218+ <property name="margin-bottom">10</property>
3219+ </object>
3220+ </child>
3221+ <child>
3222+ <object class="GtkBox">
3223+ <property name="visible">True</property>
3224+ <property name="can-focus">False</property>
3225+ <property name="no-show-all">True</property>
3226+ <child>
3227+ <object class="GtkImage" id="checkmark">
3228+ <property name="visible">True</property>
3229+ <property name="can-focus">False</property>
3230+ <property name="margin-left">10</property>
3231+ <property name="margin-right">20</property>
3232+ <property name="yalign">0</property>
3233+ </object>
3234+ </child>
3235+ <child>
3236+ <object class="GtkBox">
3237+ <property name="visible">True</property>
3238+ <property name="can-focus">False</property>
3239+ <property name="no-show-all">True</property>
3240+ <property name="orientation">vertical</property>
3241+ <child>
3242+ <object class="GtkLabel">
3243+ <property name="label" translatable="yes">Your subscription:</property>
3244+ <property name="margin-bottom">10</property>
3245+ <property name="visible">True</property>
3246+ <property name="halign">start</property>
3247+ <attributes>
3248+ <attribute name="scale" value="1.5"/>
3249+ </attributes>
3250+ </object>
3251+ </child>
3252+ <child>
3253+ <object class="GtkLabel" id="contract_name">
3254+ <property name="visible">True</property>
3255+ <property name="margin-bottom">15</property>
3256+ <property name="halign">start</property>
3257+ </object>
3258+ </child>
3259+ <child>
3260+ <object class="GtkLabel" id="enabled_services_header">
3261+ <property name="label" translatable="yes">List of your enabled services:</property>
3262+ <property name="margin-bottom">10</property>
3263+ <property name="visible">True</property>
3264+ <property name="halign">start</property>
3265+ <attributes>
3266+ <attribute name="scale" value="1.5"/>
3267+ </attributes>
3268+ </object>
3269+ </child>
3270+ <child>
3271+ <object class="GtkLabel" id="enabled_services">
3272+ <property name="margin-left">3</property>
3273+ <property name="visible">True</property>
3274+ <property name="halign">start</property>
3275+ </object>
3276+ </child>
3277+ <child>
3278+ <object class="GtkLabel" id="available_services_header">
3279+ <property name="label" translatable="yes">Other available services:</property>
3280+ <property name="margin-bottom">10</property>
3281+ <property name="visible">True</property>
3282+ <property name="halign">start</property>
3283+ <attributes>
3284+ <attribute name="scale" value="1.5"/>
3285+ </attributes>
3286+ </object>
3287+ </child>
3288+ <child>
3289+ <object class="GtkLabel" id="available_services">
3290+ <property name="margin-left">3</property>
3291+ <property name="visible">True</property>
3292+ <property name="halign">start</property>
3293+ </object>
3294+ </child>
3295+ </object>
3296+ </child>
3297+ </object>
3298+ </child>
3299+ <child>
3300+ <object class="GtkLabel">
3301+ <property name="label" translatable="yes">You can start using Ubuntu Pro features once Ubuntu has finished installing. If you want to change the default enablements for your token you can do so via the ubuntu.com/pro web interface. Alternatively you can change enabled services by going to the Settings menu or the `ua` command-line.</property>
3302+ <property name="visible">True</property>
3303+ <property name="wrap">True</property>
3304+ <property name="halign">start</property>
3305+ </object>
3306+ </child>
3307+ </object>
3308+ </child>
3309+ </template>
3310+</interface>
3311diff --git a/gnome-initial-setup/pages/ubuntu-pro/meson.build b/gnome-initial-setup/pages/ubuntu-pro/meson.build
3312new file mode 100644
3313index 0000000..f7b700a
3314--- /dev/null
3315+++ b/gnome-initial-setup/pages/ubuntu-pro/meson.build
3316@@ -0,0 +1,12 @@
3317+sources += gnome.compile_resources(
3318+ 'ubuntupro-resources',
3319+ files('ubuntupro.gresource.xml'),
3320+ c_name: 'ubuntupro'
3321+)
3322+
3323+sources += files(
3324+ 'gis-ubuntupro-page.c',
3325+ 'gis-ubuntupro-page.h',
3326+ 'utils.c',
3327+ 'utils.h',
3328+)
3329diff --git a/gnome-initial-setup/pages/ubuntu-pro/ubuntu-pro-dark.svg b/gnome-initial-setup/pages/ubuntu-pro/ubuntu-pro-dark.svg
3330new file mode 100644
3331index 0000000..e33bf48
3332--- /dev/null
3333+++ b/gnome-initial-setup/pages/ubuntu-pro/ubuntu-pro-dark.svg
3334@@ -0,0 +1,25 @@
3335+<svg width="132" height="34" viewBox="0 0 132 34" fill="none" xmlns="http://www.w3.org/2000/svg">
3336+<g clip-path="url(#clip0_1_7165)">
3337+<path d="M32.24 29.4699C30.94 29.4699 29.91 29.2199 29.14 28.7099C28.37 28.1999 27.81 27.5199 27.47 26.6599C27.13 25.7999 26.96 24.8499 26.96 23.7899V14.6799H28.38V23.5999C28.38 25.1899 28.74 26.3499 29.45 27.0899C30.16 27.8299 31.09 28.1999 32.25 28.1999C33.41 28.1999 34.34 27.8299 35.05 27.0899C35.76 26.3499 36.12 25.1899 36.12 23.5999V14.6799H37.54V23.7899C37.54 24.8499 37.37 25.8099 37.03 26.6599C36.69 27.5099 36.13 28.1999 35.36 28.7099C34.59 29.2199 33.55 29.4699 32.26 29.4699H32.24Z" fill="white"/>
3338+<path d="M44.96 29.41C44.33 29.41 43.77 29.3799 43.28 29.32C42.79 29.26 42.35 29.18 41.99 29.09C41.62 29 41.3 28.91 41.04 28.83V13.2L42.4 12.95V19.01C42.64 18.83 43.02 18.64 43.55 18.44C44.08 18.24 44.69 18.14 45.39 18.14C46.43 18.14 47.32 18.38 48.04 18.86C48.76 19.34 49.32 20.01 49.7 20.86C50.08 21.71 50.27 22.68 50.27 23.78C50.27 24.95 50.05 25.96 49.6 26.8C49.15 27.64 48.54 28.29 47.74 28.74C46.95 29.19 46.02 29.42 44.96 29.42V29.41ZM44.94 28.18C45.72 28.18 46.4 28.01 46.98 27.67C47.56 27.33 48.01 26.83 48.34 26.17C48.67 25.51 48.83 24.71 48.83 23.78C48.83 23.18 48.77 22.62 48.64 22.09C48.51 21.56 48.31 21.09 48.03 20.68C47.75 20.27 47.39 19.95 46.93 19.72C46.47 19.49 45.92 19.38 45.25 19.38C44.58 19.38 44.03 19.49 43.52 19.71C43 19.93 42.63 20.14 42.39 20.34V27.9C42.6 27.96 42.91 28.02 43.33 28.09C43.75 28.16 44.28 28.19 44.94 28.19V28.18Z" fill="white"/>
3339+<path d="M57.2402 29.3699C56.1702 29.3699 55.3202 29.1599 54.6902 28.7499C54.0602 28.3399 53.6202 27.7599 53.3502 27.0199C53.0902 26.2699 52.9502 25.4099 52.9502 24.4199V18.3599H54.3102V23.9799C54.3102 24.9999 54.4102 25.8099 54.6102 26.4099C54.8102 27.0199 55.1402 27.4499 55.6002 27.7299C56.0602 27.9999 56.6702 28.1399 57.4402 28.1399C58.0802 28.1399 58.6402 28.0999 59.1102 28.0399C59.5802 27.9799 59.8802 27.9099 60.0102 27.8499V18.3699H61.3702V28.7899C60.9702 28.8999 60.4202 29.0299 59.7402 29.1699C59.0602 29.3099 58.2202 29.3799 57.2302 29.3799L57.2402 29.3699Z" fill="white"/>
3340+<path d="M64.7598 29.1599V18.7399C65.1598 18.6299 65.7098 18.4999 66.3998 18.3599C67.0898 18.2199 67.9198 18.1499 68.8998 18.1499C69.9998 18.1499 70.8598 18.3599 71.4798 18.7799C72.0998 19.1999 72.5398 19.7799 72.7998 20.5199C73.0598 21.2699 73.1898 22.1299 73.1898 23.1199V29.1599H71.8298V23.5599C71.8298 22.5399 71.7398 21.7299 71.5498 21.1199C71.3598 20.5099 71.0398 20.0699 70.5798 19.7899C70.1198 19.5199 69.4898 19.3799 68.6998 19.3799C68.0598 19.3799 67.5098 19.4199 67.0398 19.4799C66.5698 19.5499 66.2698 19.6099 66.1298 19.6699V29.1499H64.7698L64.7598 29.1599Z" fill="white"/>
3341+<path d="M79.8899 29.3899C78.9999 29.3899 78.2999 29.2299 77.7799 28.9199C77.2599 28.6099 76.8999 28.1399 76.6899 27.5099C76.4799 26.8799 76.3799 26.0899 76.3799 25.1299V15.1899L77.7399 14.9399V18.3699H82.0399V19.5199H77.7399V25.2599C77.7399 26.0799 77.8299 26.6999 78.0099 27.1199C78.1899 27.5399 78.4499 27.8199 78.7899 27.9499C79.1299 28.0899 79.5199 28.1599 79.9699 28.1599C80.5399 28.1599 80.9999 28.0899 81.3499 27.9499C81.6999 27.8099 81.9699 27.6899 82.1599 27.5999L82.4899 28.7299C82.2899 28.8499 81.9499 28.9999 81.4699 29.1599C80.9799 29.3199 80.4499 29.3999 79.8799 29.3999L79.8899 29.3899Z" fill="white"/>
3342+<path d="M88.5898 29.3699C87.5198 29.3699 86.6698 29.1599 86.0398 28.7499C85.4098 28.3399 84.9698 27.7599 84.6998 27.0199C84.4398 26.2699 84.2998 25.4099 84.2998 24.4199V18.3599H85.6598V23.9799C85.6598 24.9999 85.7598 25.8099 85.9598 26.4099C86.1598 27.0199 86.4898 27.4499 86.9498 27.7299C87.4098 27.9999 88.0198 28.1399 88.7898 28.1399C89.4298 28.1399 89.9898 28.0999 90.4598 28.0399C90.9298 27.9799 91.2298 27.9099 91.3598 27.8499V18.3699H92.7198V28.7899C92.3198 28.8999 91.7698 29.0299 91.0898 29.1699C90.4098 29.3099 89.5698 29.3799 88.5798 29.3799L88.5898 29.3699Z" fill="white"/>
3343+<path d="M101.17 29.1601V14.9601C101.67 14.8201 102.26 14.7201 102.94 14.6601C103.61 14.6001 104.24 14.5701 104.83 14.5701C106.86 14.5701 108.38 14.9601 109.39 15.7501C110.4 16.5401 110.9 17.6401 110.9 19.0601C110.9 20.1601 110.65 21.0401 110.14 21.7001C109.63 22.3601 108.9 22.8401 107.96 23.1301C107.01 23.4201 105.88 23.5701 104.55 23.5701H102.59V29.1701H101.17V29.1601ZM102.59 22.3301H104.39C105.41 22.3301 106.29 22.2401 107.04 22.0701C107.79 21.9001 108.38 21.5701 108.79 21.0901C109.21 20.6101 109.42 19.9201 109.42 19.0301C109.42 18.1401 109.2 17.5201 108.76 17.0601C108.32 16.5901 107.76 16.2701 107.07 16.0801C106.38 15.8901 105.65 15.8001 104.89 15.8001C104.37 15.8001 103.93 15.8201 103.55 15.8501C103.17 15.8801 102.85 15.9201 102.59 15.9401V22.3301Z" fill="white"/>
3344+<path d="M113.34 29.1599V18.8599C113.69 18.7099 114.17 18.5499 114.78 18.3899C115.39 18.2299 116.14 18.1499 117.02 18.1499C117.31 18.1499 117.6 18.1699 117.89 18.1999C118.18 18.2399 118.43 18.2799 118.64 18.3199C118.85 18.3599 119.01 18.4099 119.11 18.4599L118.84 19.6299C118.74 19.5699 118.52 19.5099 118.17 19.4499C117.82 19.3899 117.37 19.3599 116.81 19.3599C116.23 19.3599 115.76 19.3999 115.4 19.4799C115.05 19.5599 114.81 19.6299 114.7 19.6899V29.1499H113.34V29.1599Z" fill="white"/>
3345+<path d="M125.12 29.4099C124.16 29.4099 123.31 29.1799 122.57 28.7099C121.83 28.2399 121.25 27.5899 120.83 26.7399C120.41 25.8899 120.19 24.8999 120.19 23.7699C120.19 22.6399 120.4 21.6399 120.83 20.7899C121.25 19.9499 121.84 19.2899 122.57 18.8199C123.3 18.3499 124.16 18.1099 125.12 18.1099C126.08 18.1099 126.93 18.3499 127.68 18.8199C128.42 19.2899 129.01 19.9499 129.42 20.7899C129.84 21.6299 130.05 22.6199 130.05 23.7699C130.05 24.9199 129.84 25.8899 129.42 26.7399C129 27.5899 128.42 28.2499 127.68 28.7099C126.93 29.1799 126.08 29.4099 125.12 29.4099ZM125.12 28.1799C125.83 28.1799 126.45 27.9999 126.97 27.6399C127.49 27.2799 127.9 26.7699 128.18 26.0999C128.47 25.4399 128.61 24.6599 128.61 23.7699C128.61 22.8799 128.47 22.0799 128.18 21.4199C127.89 20.7599 127.49 20.2499 126.97 19.8799C126.45 19.5199 125.83 19.3399 125.12 19.3399C124.41 19.3399 123.79 19.5199 123.27 19.8799C122.75 20.2399 122.34 20.7499 122.06 21.4199C121.77 22.0799 121.63 22.8699 121.63 23.7699C121.63 24.6699 121.77 25.4399 122.06 26.0999C122.35 26.7599 122.75 27.2699 123.27 27.6399C123.79 27.9999 124.41 28.1799 125.12 28.1799Z" fill="white"/>
3346+<path d="M21.5 0H0.5V32H21.5V0Z" fill="#E95420"/>
3347+<path d="M5.29988 23.32C6.49834 23.32 7.46988 22.3484 7.46988 21.15C7.46988 19.9515 6.49834 18.98 5.29988 18.98C4.10142 18.98 3.12988 19.9515 3.12988 21.15C3.12988 22.3484 4.10142 23.32 5.29988 23.32Z" fill="white"/>
3348+<path d="M14.3599 18.56C15.5584 18.56 16.5299 17.5884 16.5299 16.39C16.5299 15.1915 15.5584 14.22 14.3599 14.22C13.1615 14.22 12.1899 15.1915 12.1899 16.39C12.1899 17.5884 13.1615 18.56 14.3599 18.56Z" fill="white"/>
3349+<path d="M10.2 26.75C8.62998 26.41 7.32998 25.41 6.58998 24C6.00998 24.26 5.35998 24.34 4.72998 24.23C5.61998 26.42 7.50998 28 9.82998 28.5C10.34 28.61 10.86 28.66 11.37 28.66C10.97 28.14 10.75 27.5 10.73 26.84C10.55 26.82 10.37 26.79 10.2 26.75Z" fill="white"/>
3350+<path d="M13.8702 28.9301C15.0687 28.9301 16.0402 27.9585 16.0402 26.7601C16.0402 25.5616 15.0687 24.5901 13.8702 24.5901C12.6717 24.5901 11.7002 25.5616 11.7002 26.7601C11.7002 27.9585 12.6717 28.9301 13.8702 28.9301Z" fill="white"/>
3351+<path d="M16.8898 25.9601C17.5698 25.1101 18.0398 24.1001 18.2698 23.0301C18.6698 21.1601 18.2998 19.2001 17.2398 17.6101C16.9898 18.2001 16.5598 18.7001 16.0198 19.0401C16.6098 20.1501 16.7898 21.4201 16.5198 22.6501C16.3898 23.2501 16.1598 23.8201 15.8398 24.3301C16.3498 24.7501 16.7098 25.3101 16.8798 25.9501L16.8898 25.9601Z" fill="white"/>
3352+<path d="M5.12988 18.04C5.18988 18.04 5.23988 18.04 5.29988 18.04C5.51988 18.04 5.73988 18.06 5.95988 18.11C6.30988 18.19 6.64988 18.32 6.94988 18.51C7.92988 17.1 9.50988 16.26 11.2199 16.23C11.2199 16.07 11.2499 15.9 11.2899 15.74C11.3899 15.27 11.5899 14.84 11.8799 14.47C9.14988 14.25 6.48988 15.66 5.13988 18.05L5.12988 18.04Z" fill="white"/>
3353+</g>
3354+<defs>
3355+<clipPath id="clip0_1_7165">
3356+<rect width="130.71" height="34" fill="white" transform="translate(0.5)"/>
3357+</clipPath>
3358+</defs>
3359+</svg>
3360diff --git a/gnome-initial-setup/pages/ubuntu-pro/ubuntu-pro.svg b/gnome-initial-setup/pages/ubuntu-pro/ubuntu-pro.svg
3361new file mode 100644
3362index 0000000..d377e41
3363--- /dev/null
3364+++ b/gnome-initial-setup/pages/ubuntu-pro/ubuntu-pro.svg
3365@@ -0,0 +1 @@
3366+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1444.23 360.27"><defs><style>.cls-1{fill:#e95420;}.cls-2{fill:#fff;}.cls-3{fill:#111;}</style></defs><rect class="cls-1" width="233.93" height="360.27"/><circle class="cls-2" cx="52.26" cy="238.51" r="24.44"/><circle class="cls-2" cx="154.06" cy="184.91" r="24.44"/><path class="cls-2" d="M107.39,301.42a59.52,59.52,0,0,1-40.6-31A35.22,35.22,0,0,1,45.89,273a80.08,80.08,0,0,0,74.65,49.8,35,35,0,0,1-7.16-20.44A60.26,60.26,0,0,1,107.39,301.42Z"/><circle class="cls-2" cx="148.55" cy="301.55" r="24.44"/><path class="cls-2" d="M182.56,292.58a79.94,79.94,0,0,0,4-93.83,35.16,35.16,0,0,1-13.74,16.09,60,60,0,0,1-2,59.53A35,35,0,0,1,182.56,292.58Z"/><path class="cls-2" d="M50.37,203.46c.62,0,1.24-.05,1.87-.05a35.36,35.36,0,0,1,7.37.78,35,35,0,0,1,11.16,4.5,59.63,59.63,0,0,1,48-25.68,36.52,36.52,0,0,1,.74-5.5,35.3,35.3,0,0,1,6.68-14.32A80.38,80.38,0,0,0,50.37,203.46Z"/><path class="cls-3" d="M347.78,331.73q-16.27,0-27.71-5a47.72,47.72,0,0,1-18.52-13.68,54.81,54.81,0,0,1-10.26-20.41,95.22,95.22,0,0,1-3.19-25.12V164.73h16V265.45a75.46,75.46,0,0,0,3.19,23.23,44.78,44.78,0,0,0,8.84,16.16,34.55,34.55,0,0,0,13.68,9.44,54.06,54.06,0,0,0,35.86,0,34.55,34.55,0,0,0,13.68-9.44,44.78,44.78,0,0,0,8.84-16.16,75.46,75.46,0,0,0,3.19-23.23V164.73h16V267.57a95.22,95.22,0,0,1-3.19,25.12A54.81,54.81,0,0,1,394,313.1a47.76,47.76,0,0,1-18.51,13.68Q364.06,331.73,347.78,331.73Z"/><path class="cls-3" d="M460.55,213.56q4-3.06,12.85-6.49a58,58,0,0,1,20.88-3.42,55.7,55.7,0,0,1,23.47,4.72,48.76,48.76,0,0,1,17.33,13.21,58.28,58.28,0,0,1,10.74,20.17,85.49,85.49,0,0,1,3.65,25.59A76.4,76.4,0,0,1,545.11,294,57.25,57.25,0,0,1,532.84,314,52.66,52.66,0,0,1,514,326.66,65.48,65.48,0,0,1,489.56,331q-16.28,0-26.89-2.12a137.63,137.63,0,0,1-17.45-4.48V148l15.33-2.83Zm0,100.25A68.84,68.84,0,0,0,471,316.05a127.35,127.35,0,0,0,18.28,1.06q19.81,0,31.85-12.86t12-36.91a84.07,84.07,0,0,0-2.13-19.23,44.87,44.87,0,0,0-6.84-15.8,34.25,34.25,0,0,0-12.38-10.73q-7.67-4-19-4a49.31,49.31,0,0,0-10.38,1.06,61.57,61.57,0,0,0-9.19,2.71,51,51,0,0,0-12.74,7.08Z"/><path class="cls-3" d="M673.56,324q-6.84,1.89-18.4,4.24a144.38,144.38,0,0,1-28.3,2.36q-13.68,0-22.88-4a37.07,37.07,0,0,1-14.86-11.32A45.88,45.88,0,0,1,581,297.53a98.64,98.64,0,0,1-2.47-22.88v-68.4h15.33V269.7a108.25,108.25,0,0,0,1.88,21.93q1.89,9,6.14,14.51a23.86,23.86,0,0,0,11,8,47.64,47.64,0,0,0,16.39,2.48,132.49,132.49,0,0,0,18.87-1.18c5.34-.79,8.73-1.5,10.14-2.13V206.25h15.33Z"/><path class="cls-3" d="M710.26,210.49q6.84-1.87,18.4-4.24a144.38,144.38,0,0,1,28.3-2.36q13.92,0,23.24,4a35.73,35.73,0,0,1,14.86,11.44,46.84,46.84,0,0,1,7.9,17.81A103.44,103.44,0,0,1,805.32,260v68.16H790V265A115.58,115.58,0,0,0,788.22,243a36.61,36.61,0,0,0-5.9-14.62,23,23,0,0,0-10.85-8.14q-6.72-2.47-16.87-2.47A128.49,128.49,0,0,0,735.85,219q-7.9,1.17-10.26,2.12V328.19H710.26Z"/><path class="cls-3" d="M855.39,206.25H904v13H855.39v64.87A65.79,65.79,0,0,0,857.16,301a23.36,23.36,0,0,0,5.07,10,16,16,0,0,0,8,4.72,42.53,42.53,0,0,0,10.38,1.18q9.66,0,15.56-2.24a67,67,0,0,0,9.2-4.13l3.78,12.74q-3.31,2.13-11.56,4.83a57.11,57.11,0,0,1-17.93,2.72q-11.32,0-19-3a27.48,27.48,0,0,1-12.26-9,36.72,36.72,0,0,1-6.49-15,104.2,104.2,0,0,1-1.88-21.23V170.4l15.33-2.83Z"/><path class="cls-3" d="M1023.33,324q-6.86,1.89-18.4,4.24a144.49,144.49,0,0,1-28.31,2.36q-13.68,0-22.88-4a37.14,37.14,0,0,1-14.86-11.32,45.87,45.87,0,0,1-8.13-17.69,98.62,98.62,0,0,1-2.48-22.88v-68.4H943.6V269.7a108.17,108.17,0,0,0,1.89,21.93q1.89,9,6.13,14.51a23.91,23.91,0,0,0,11,8A47.64,47.64,0,0,0,979,316.64a132.49,132.49,0,0,0,18.87-1.18q8-1.18,10.14-2.13V206.25h15.34Z"/><path class="cls-3" d="M1156.68,163.32q34.68,0,51.66,13.21t17,37.5q0,13.92-5,23.71a40.35,40.35,0,0,1-14.15,15.8q-9.21,6-22.53,8.73a151.33,151.33,0,0,1-30.07,2.71h-22.17v63.21h-16V167.8a115.14,115.14,0,0,1,19.93-3.42Q1146.77,163.32,1156.68,163.32Zm.71,13.92q-9,0-15.21.59t-10.73,1.06v72.17h20.28a163.68,163.68,0,0,0,23.47-1.53,52.38,52.38,0,0,0,17.93-5.66,28.63,28.63,0,0,0,11.44-11.44q4-7.31,4-18.63,0-10.85-4.37-17.93a32.14,32.14,0,0,0-11.56-11.2,50.62,50.62,0,0,0-16.39-5.78A106.64,106.64,0,0,0,1157.39,177.24Z"/><path class="cls-3" d="M1292.85,203.89a82.18,82.18,0,0,1,14.27,1.18,43.81,43.81,0,0,1,9.32,2.36l-3.07,13.21a34.1,34.1,0,0,0-7.66-2,87.42,87.42,0,0,0-15.22-1.06,71.62,71.62,0,0,0-15.92,1.42,44.62,44.62,0,0,0-7.9,2.35V328.19h-15.33V211.91a113,113,0,0,1,16.27-5.31Q1278,203.9,1292.85,203.89Z"/><path class="cls-3" d="M1438.5,267.34a78.87,78.87,0,0,1-4.13,26.18,58.25,58.25,0,0,1-11.56,20.05,52.56,52.56,0,0,1-17.57,12.85,56.86,56.86,0,0,1-44.81,0,52.6,52.6,0,0,1-17.58-12.85,58.39,58.39,0,0,1-11.55-20.05,85,85,0,0,1,0-52.36A59.72,59.72,0,0,1,1342.85,221a51.86,51.86,0,0,1,17.58-13,56.86,56.86,0,0,1,44.81,0,51.82,51.82,0,0,1,17.57,13,59.57,59.57,0,0,1,11.56,20.17A78.87,78.87,0,0,1,1438.5,267.34Zm-16.28,0q0-22.87-10.61-36.44t-28.78-13.57q-18.16,0-28.77,13.57t-10.62,36.44q0,22.88,10.62,36.32t28.77,13.45q18.17,0,28.78-13.45T1422.22,267.34Z"/></svg>
3367\ No newline at end of file
3368diff --git a/gnome-initial-setup/pages/ubuntu-pro/ubuntupro.gresource.xml b/gnome-initial-setup/pages/ubuntu-pro/ubuntupro.gresource.xml
3369new file mode 100644
3370index 0000000..9257186
3371--- /dev/null
3372+++ b/gnome-initial-setup/pages/ubuntu-pro/ubuntupro.gresource.xml
3373@@ -0,0 +1,14 @@
3374+<?xml version="1.0" encoding="UTF-8"?>
3375+<gresources>
3376+ <gresource prefix="/org/gnome/initial-setup">
3377+ <file preprocess="xml-stripblanks" alias="gis-ubuntupro-page.ui">gis-ubuntupro-page.ui</file>
3378+ <file preprocess="xml-stripblanks" alias="gis-ubuntupro-offer-page.ui">gis-ubuntupro-offer-page.ui</file>
3379+ <file preprocess="xml-stripblanks" alias="gis-ubuntupro-attach-page.ui">gis-ubuntupro-attach-page.ui</file>
3380+ <file preprocess="xml-stripblanks" alias="gis-ubuntupro-services-page.ui">gis-ubuntupro-services-page.ui</file>
3381+ <file alias="ubuntu-pro-dark.svg">ubuntu-pro-dark.svg</file>
3382+ <file alias="ubuntu-pro.svg">ubuntu-pro.svg</file>
3383+ <file alias="checkmark.svg">checkmark.svg</file>
3384+ <file alias="fail.svg">fail.svg</file>
3385+ </gresource>
3386+</gresources>
3387+
3388diff --git a/gnome-initial-setup/pages/ubuntu-pro/utils.c b/gnome-initial-setup/pages/ubuntu-pro/utils.c
3389new file mode 100644
3390index 0000000..e2f1509
3391--- /dev/null
3392+++ b/gnome-initial-setup/pages/ubuntu-pro/utils.c
3393@@ -0,0 +1,123 @@
3394+#include <libsoup/soup.h>
3395+#include "gis-ubuntupro-page.h"
3396+
3397+gboolean
3398+parse_ua_status(gchar **contents, gsize *len)
3399+{
3400+ g_autoptr(GError) error = NULL;
3401+
3402+ if (!g_file_get_contents("/var/lib/ubuntu-advantage/status.json",
3403+ contents, len, &error)) {
3404+ g_warning("Unable to read pro status: %s\n", error->message);
3405+ return FALSE;
3406+ }
3407+ return TRUE;
3408+}
3409+
3410+gboolean
3411+get_ubuntu_advantage_attached(gboolean *attached)
3412+{
3413+ g_autoptr(GError) error = NULL;
3414+ g_autoptr(GDBusConnection) bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
3415+ if (bus == NULL) {
3416+ g_warning ("Failed to get system bus: %s", error->message);
3417+ return FALSE;
3418+ }
3419+
3420+ g_autoptr(GVariant) result = g_dbus_connection_call_sync (bus,
3421+ "com.canonical.UbuntuAdvantage",
3422+ "/com/canonical/UbuntuAdvantage/Manager",
3423+ "org.freedesktop.DBus.Properties",
3424+ "Get",
3425+ g_variant_new ("(ss)", "com.canonical.UbuntuAdvantage.Manager", "Attached"),
3426+ G_VARIANT_TYPE ("(v)"),
3427+ G_DBUS_CALL_FLAGS_NONE,
3428+ -1,
3429+ NULL,
3430+ &error);
3431+ if (result == NULL) {
3432+ g_warning ("Failed to contact Ubuntu Advantage D-Bus service: %s", error->message);
3433+ return FALSE;
3434+ }
3435+
3436+ g_autoptr(GVariant) value = NULL;
3437+ g_variant_get (result, "(v)", &value);
3438+ if (!g_variant_is_of_type (value, G_VARIANT_TYPE("b"))) {
3439+ g_warning ("Attached property has wrong type");
3440+ return FALSE;
3441+ }
3442+ g_variant_get (value, "b", attached);
3443+
3444+ return TRUE;
3445+}
3446+
3447+char *
3448+get_item (const char *buffer, const char *name)
3449+{
3450+ g_autofree gchar *label = NULL;
3451+ gchar *start = NULL, *end = NULL;
3452+ gchar end_char;
3453+
3454+ label = g_strconcat (name, "=", NULL);
3455+ if ((start = strstr (buffer, label)) != NULL) {
3456+ start += strlen (label);
3457+ end_char = '\n';
3458+ if (*start == '"') {
3459+ start++;
3460+ end_char = '"';
3461+ }
3462+ end = strchr (start, end_char);
3463+ }
3464+
3465+ if (start != NULL && end != NULL)
3466+ return g_strndup (start, end - start);
3467+ else
3468+ return NULL;
3469+}
3470+
3471+gboolean
3472+is_lts ()
3473+{
3474+ g_autofree gchar *buffer = NULL;
3475+ g_autofree gchar *version = NULL;
3476+
3477+ if (g_file_get_contents ("/etc/os-release", &buffer, NULL, NULL))
3478+ version = get_item (buffer, "VERSION");
3479+
3480+ return version && g_strrstr (version, "LTS") != NULL;
3481+}
3482+
3483+gboolean
3484+is_ubuntupro_supported ()
3485+{
3486+ return is_lts ();
3487+}
3488+
3489+/* Routine from Gnome Control Center
3490+ * https://gitlab.gnome.org/GNOME/gnome-control-center/-/commit/a3b8964a */
3491+gboolean
3492+is_dark_theme (GtkWidget *widget)
3493+{
3494+ GdkScreen *screen;
3495+ GtkSettings *settings;
3496+ g_autofree char *theme_name = NULL;
3497+
3498+ theme_name = g_strdup (g_getenv ("GTK_THEME"));
3499+ if (theme_name != NULL) {
3500+ return g_str_has_suffix (theme_name, "dark");
3501+ }
3502+
3503+ screen = gtk_widget_get_screen (GTK_WIDGET (widget));
3504+ settings = gtk_settings_get_for_screen (screen);
3505+
3506+ g_object_get (settings, "gtk-theme-name", &theme_name, NULL);
3507+ return theme_name != NULL && g_str_has_suffix (theme_name, "dark");
3508+}
3509+
3510+void
3511+itemize(gchar *str, const gchar *append, gsize len)
3512+{
3513+ g_strlcat(str, "• ", len);
3514+ g_strlcat(str, append, len);
3515+ g_strlcat(str, "\n", len);
3516+}
3517diff --git a/gnome-initial-setup/pages/ubuntu-pro/utils.h b/gnome-initial-setup/pages/ubuntu-pro/utils.h
3518new file mode 100644
3519index 0000000..ceefe72
3520--- /dev/null
3521+++ b/gnome-initial-setup/pages/ubuntu-pro/utils.h
3522@@ -0,0 +1,7 @@
3523+gboolean parse_ua_status(gchar**, gsize*);
3524+gboolean get_ubuntu_advantage_attached(gboolean*);
3525+char *get_item(const char*, const char*);
3526+gboolean is_lts();
3527+gboolean is_ubuntupro_supported();
3528+gboolean is_dark_theme(GtkWidget*);
3529+void itemize(gchar*, const gchar*, gsize);
3530diff --git a/po/POTFILES.in b/po/POTFILES.in
3531index 9b1b23f..43dbabb 100644
3532--- a/po/POTFILES.in
3533+++ b/po/POTFILES.in
3534@@ -1,4 +1,3 @@
3535-data/com.ubuntu.welcome.policy.in
3536 data/gnome-initial-setup-first-login.desktop.in.in
3537 data/gnome-initial-setup.desktop.in.in
3538 gnome-initial-setup/cc-common-language.c
3539@@ -37,17 +36,16 @@ gnome-initial-setup/pages/software/gis-software-page.c
3540 gnome-initial-setup/pages/software/gis-software-page.ui
3541 gnome-initial-setup/pages/summary/gis-summary-page.c
3542 gnome-initial-setup/pages/summary/gis-summary-page.ui
3543-gnome-initial-setup/pages/timezone/gis-location-entry.c
3544 gnome-initial-setup/pages/timezone/gis-timezone-page.c
3545 gnome-initial-setup/pages/timezone/gis-timezone-page.ui
3546+gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-attach-page.ui
3547+gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-offer-page.ui
3548+gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page.c
3549+gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-services-page.ui
3550 gnome-initial-setup/pages/welcome/gis-welcome-page.c
3551 gnome-initial-setup/pages/welcome/gis-welcome-page.ui
3552 gnome-initial-setup/pages/timezone/gis-timezone-page.ui
3553 gnome-initial-setup/pages/ubuntu-report/gis-ubuntu-report-page.c
3554 gnome-initial-setup/pages/ubuntu-report/gis-ubuntu-report-page.ui
3555-gnome-initial-setup/pages/livepatch/gis-auth-dialog.c
3556-gnome-initial-setup/pages/livepatch/gis-auth-dialog.ui
3557-gnome-initial-setup/pages/livepatch/gis-livepatch-page.c
3558-gnome-initial-setup/pages/livepatch/gis-livepatch-page.ui
3559 gnome-initial-setup/pages/apps/gis-apps-page.c
3560 gnome-initial-setup/pages/apps/gis-apps-page.ui

Subscribers

People subscribed via source and target branches

to all changes: