[gnome-software/wip/william/cherry-pick-4: 2/23] Add Ubuntu One sign-in UI
- From: William Hua <williamhua src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software/wip/william/cherry-pick-4: 2/23] Add Ubuntu One sign-in UI
- Date: Fri, 3 Jun 2016 07:00:41 +0000 (UTC)
commit 5f3c3fbd872ef1f635fa23be70f1acebbd5b7a65
Author: William Hua <william hua canonical com>
Date: Thu Jun 2 11:56:32 2016 -0400
Add Ubuntu One sign-in UI
Authored by Robert Ancell <robert ancell canonical com> and William Hua
<william hua canonical com>.
po/POTFILES.in | 2 +
src/gnome-software.gresource.xml | 2 +
src/plugins/Makefile.am | 3 +
src/plugins/gs-ubuntuone-dialog.c | 666 ++++++++++++++++++++++++++++++++++++
src/plugins/gs-ubuntuone-dialog.h | 45 +++
src/plugins/gs-ubuntuone-dialog.ui | 386 +++++++++++++++++++++
src/plugins/ubuntu-one.png | Bin 0 -> 2540 bytes
7 files changed, 1104 insertions(+), 0 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 5b7e6b9..73a4f41 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -56,6 +56,8 @@ src/gs-upgrade-banner.c
src/gs-common.c
[type: gettext/glade]src/gs-menus.ui
src/org.gnome.Software.desktop.in
+src/plugins/gs-ubuntuone-dialog.c
+[type: gettext/glade]src/plugins/gs-ubuntuone-dialog.ui
src/plugins/menu-spec-common.c
[type: gettext/glade]src/gs-popular-tile.ui
src/gs-shell-loading.c
diff --git a/src/gnome-software.gresource.xml b/src/gnome-software.gresource.xml
index 773ce8e..393c255 100644
--- a/src/gnome-software.gresource.xml
+++ b/src/gnome-software.gresource.xml
@@ -34,5 +34,7 @@
<file preprocess="xml-stripblanks">org.freedesktop.PackageKit.xml</file>
<file>gtk-style.css</file>
<file>gtk-style-hc.css</file>
+ <file>plugins/ubuntu-one.png</file>
+ <file preprocess="xml-stripblanks">plugins/gs-ubuntuone-dialog.ui</file>
</gresource>
</gresources>
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
index bab4501..af56623 100644
--- a/src/plugins/Makefile.am
+++ b/src/plugins/Makefile.am
@@ -15,6 +15,7 @@ AM_CPPFLAGS = \
$(OSTREE_CFLAGS) \
$(FLATPAK_CFLAGS) \
$(RPM_CFLAGS) \
+ $(LIBSECRET_CFLAGS) \
-DI_KNOW_THE_GNOME_SOFTWARE_API_IS_SUBJECT_TO_CHANGE \
-DBINDIR=\"$(bindir)\" \
-DDATADIR=\"$(datadir)\" \
@@ -332,4 +333,6 @@ gs_self_test_CFLAGS = $(WARN_CFLAGS)
TESTS = gs-self-test
endif
+EXTRA_DIST = gs-ubuntuone-dialog.h gs-ubuntuone-dialog.ui ubuntu-one.png
+
-include $(top_srcdir)/git.mk
diff --git a/src/plugins/gs-ubuntuone-dialog.c b/src/plugins/gs-ubuntuone-dialog.c
new file mode 100644
index 0000000..7d982a7
--- /dev/null
+++ b/src/plugins/gs-ubuntuone-dialog.c
@@ -0,0 +1,666 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2016 Canonical Ltd.
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "gs-ubuntuone-dialog.h"
+#include "gs-common.h"
+#include "gs-utils.h"
+
+#include <glib/gi18n.h>
+#include <json-glib/json-glib.h>
+#include <libsoup/soup.h>
+
+#include "gs-ubuntu-snapd.h"
+
+#define UBUNTU_LOGIN_HOST "https://login.ubuntu.com"
+
+struct _GsUbuntuoneDialog
+{
+ GtkDialog parent_instance;
+
+ GtkWidget *content_box;
+ GtkWidget *cancel_button;
+ GtkWidget *next_button;
+ GtkWidget *status_stack;
+ GtkWidget *status_image;
+ GtkWidget *status_label;
+ GtkWidget *page_stack;
+ GtkWidget *prompt_label;
+ GtkWidget *login_radio;
+ GtkWidget *register_radio;
+ GtkWidget *reset_radio;
+ GtkWidget *email_entry;
+ GtkWidget *password_entry;
+ GtkWidget *remember_check;
+ GtkWidget *passcode_entry;
+
+ SoupSession *session;
+
+ gboolean get_macaroon;
+
+ GVariant *macaroon;
+ gchar *consumer_key;
+ gchar *consumer_secret;
+ gchar *token_key;
+ gchar *token_secret;
+};
+
+G_DEFINE_TYPE (GsUbuntuoneDialog, gs_ubuntuone_dialog, GTK_TYPE_DIALOG)
+
+static gboolean
+is_email_address (const gchar *text)
+{
+ text = g_utf8_strchr (text, -1, '@');
+
+ if (!text)
+ return FALSE;
+
+ text = g_utf8_strchr (text + 1, -1, '.');
+
+ if (!text)
+ return FALSE;
+
+ return text[1];
+}
+
+static void
+update_widgets (GsUbuntuoneDialog *self)
+{
+ if (g_str_equal (gtk_stack_get_visible_child_name (GTK_STACK (self->page_stack)), "page-0")) {
+ gtk_widget_set_sensitive (self->next_button,
+ !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
(self->login_radio)) ||
+ (is_email_address (gtk_entry_get_text (GTK_ENTRY
(self->email_entry))) &&
+ gtk_entry_get_text_length (GTK_ENTRY (self->password_entry)) > 0));
+ gtk_widget_set_sensitive (self->password_entry,
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
(self->login_radio)));
+ gtk_widget_set_sensitive (self->remember_check,
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
(self->login_radio)));
+ } else if (g_str_equal (gtk_stack_get_visible_child_name (GTK_STACK (self->page_stack)), "page-1")) {
+ gtk_widget_set_sensitive (self->next_button, gtk_entry_get_text_length (GTK_ENTRY
(self->passcode_entry)) > 0);
+ } else if (g_str_equal (gtk_stack_get_visible_child_name (GTK_STACK (self->page_stack)), "page-2")) {
+ gtk_widget_set_visible (self->cancel_button, FALSE);
+ gtk_widget_set_sensitive (self->cancel_button, FALSE);
+ gtk_button_set_label (GTK_BUTTON (self->next_button), _("_Continue"));
+ }
+}
+
+typedef void (*ResponseCallback) (GsUbuntuoneDialog *self,
+ guint status,
+ GVariant *response,
+ gpointer user_data);
+
+typedef struct
+{
+ GsUbuntuoneDialog *dialog;
+ ResponseCallback callback;
+ gpointer user_data;
+} RequestInfo;
+
+static void
+response_received_cb (SoupSession *session,
+ SoupMessage *message,
+ gpointer user_data)
+{
+ RequestInfo *info = user_data;
+ g_autoptr(GVariant) response = NULL;
+ guint status;
+ GBytes *bytes;
+ g_autofree gchar *body = NULL;
+ gsize length;
+
+ g_object_get (message,
+ SOUP_MESSAGE_STATUS_CODE, &status,
+ SOUP_MESSAGE_RESPONSE_BODY_DATA, &bytes,
+ NULL);
+
+ body = g_bytes_unref_to_data (bytes, &length);
+
+ if (body)
+ response = json_gvariant_deserialize_data (body, length, NULL, NULL);
+
+ if (response)
+ g_variant_ref_sink (response);
+
+ if (info->callback)
+ info->callback (info->dialog, status, response, info->user_data);
+
+ g_free (info);
+}
+
+static void
+send_request (GsUbuntuoneDialog *self,
+ const gchar *method,
+ const gchar *uri,
+ GVariant *request,
+ ResponseCallback callback,
+ gpointer user_data)
+{
+ RequestInfo *info;
+ SoupMessage *message;
+ gchar *body;
+ gsize length;
+ g_autofree gchar *url = NULL;
+
+ if (self->session == NULL)
+ self->session = soup_session_new_with_options (SOUP_SESSION_USER_AGENT,
+ gs_user_agent (),
+ NULL);
+
+ body = json_gvariant_serialize_data (g_variant_ref_sink (request), &length);
+ g_variant_unref (request);
+
+ url = g_strdup_printf ("%s%s", UBUNTU_LOGIN_HOST, uri);
+ message = soup_message_new (method, url);
+
+ info = g_new0 (RequestInfo, 1);
+ info->dialog = self;
+ info->callback = callback;
+ info->user_data = user_data;
+
+ soup_message_set_request (message, "application/json", SOUP_MEMORY_TAKE, body, length);
+ soup_session_queue_message (self->session, message, response_received_cb, info);
+}
+
+static void
+show_status (GsUbuntuoneDialog *self,
+ const gchar *text,
+ gboolean is_error)
+{
+ PangoAttrList *attributes;
+
+ gtk_widget_set_visible (self->status_stack, TRUE);
+
+ if (is_error) {
+ gtk_stack_set_visible_child_name (GTK_STACK (self->status_stack), "status-image");
+ gtk_image_set_from_icon_name (GTK_IMAGE (self->status_image), "gtk-dialog-error",
GTK_ICON_SIZE_BUTTON);
+ } else {
+ gtk_stack_set_visible_child_name (GTK_STACK (self->status_stack), "status-spinner");
+ }
+
+ attributes = pango_attr_list_new ();
+ pango_attr_list_insert (attributes, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
+ pango_attr_list_insert (attributes, pango_attr_foreground_new (is_error ? 65535 : 0, 0, 0));
+ gtk_label_set_attributes (GTK_LABEL (self->status_label), attributes);
+ pango_attr_list_unref (attributes);
+
+ gtk_label_set_text (GTK_LABEL (self->status_label), text);
+}
+
+static void
+reenable_widgets (GsUbuntuoneDialog *self)
+{
+ gtk_label_set_text (GTK_LABEL (self->status_label), NULL);
+ gtk_stack_set_visible_child_name (GTK_STACK (self->status_stack), "status-image");
+ gtk_widget_set_visible (self->status_stack, FALSE);
+
+ gtk_widget_set_sensitive (self->cancel_button, TRUE);
+ gtk_widget_set_sensitive (self->next_button, TRUE);
+ gtk_widget_set_sensitive (self->login_radio, TRUE);
+ gtk_widget_set_sensitive (self->register_radio, TRUE);
+ gtk_widget_set_sensitive (self->reset_radio, TRUE);
+ gtk_widget_set_sensitive (self->email_entry, TRUE);
+ gtk_widget_set_sensitive (self->password_entry, TRUE);
+ gtk_widget_set_sensitive (self->remember_check, TRUE);
+ gtk_widget_set_sensitive (self->passcode_entry, TRUE);
+}
+
+static void
+receive_login_response_cb (GsUbuntuoneDialog *self,
+ guint status,
+ GVariant *response,
+ gpointer user_data)
+{
+ const gchar *code;
+
+ reenable_widgets (self);
+
+ if (response) {
+ switch (status) {
+ case SOUP_STATUS_OK:
+ case SOUP_STATUS_CREATED:
+ g_clear_pointer (&self->token_secret, g_free);
+ g_clear_pointer (&self->token_key, g_free);
+ g_clear_pointer (&self->consumer_secret, g_free);
+ g_clear_pointer (&self->consumer_key, g_free);
+
+ g_variant_lookup (response, "consumer_key", "s", &self->consumer_key);
+ g_variant_lookup (response, "consumer_secret", "s", &self->consumer_secret);
+ g_variant_lookup (response, "token_key", "s", &self->token_key);
+ g_variant_lookup (response, "token_secret", "s", &self->token_secret);
+
+ gtk_stack_set_visible_child_name (GTK_STACK (self->page_stack), "page-2");
+ update_widgets (self);
+ break;
+
+ default:
+ g_variant_lookup (response, "code", "&s", &code);
+
+ if (!code)
+ code = "";
+
+ if (g_str_equal (code, "TWOFACTOR_REQUIRED")) {
+ gtk_stack_set_visible_child_name (GTK_STACK (self->page_stack), "page-1");
+ gtk_widget_grab_focus (self->passcode_entry);
+ update_widgets (self);
+ break;
+ }
+
+ update_widgets (self);
+
+ if (g_str_equal (code, "INVALID_CREDENTIALS")) {
+ show_status (self, _("Incorrect email or password"), TRUE);
+ gtk_widget_grab_focus (self->password_entry);
+ } else if (g_str_equal (code, "ACCOUNT_SUSPENDED")) {
+ show_status (self, _("Account suspended"), TRUE);
+ gtk_widget_grab_focus (self->email_entry);
+ } else if (g_str_equal (code, "ACCOUNT_DEACTIVATED")) {
+ show_status (self, _("Account deactivated"), TRUE);
+ gtk_widget_grab_focus (self->email_entry);
+ } else if (g_str_equal (code, "EMAIL_INVALIDATED")) {
+ show_status (self, _("Email invalidated"), TRUE);
+ gtk_widget_grab_focus (self->email_entry);
+ } else if (g_str_equal (code, "TWOFACTOR_FAILURE")) {
+ show_status (self, _("Two-factor authentication failed"), TRUE);
+ gtk_widget_grab_focus (self->passcode_entry);
+ } else if (g_str_equal (code, "PASSWORD_POLICY_ERROR")) {
+ show_status (self, _("Password reset required"), TRUE);
+ gtk_widget_grab_focus (self->reset_radio);
+ } else if (g_str_equal (code, "TOO_MANY_REQUESTS")) {
+ show_status (self, _("Too many requests"), TRUE);
+ gtk_widget_grab_focus (self->password_entry);
+ } else {
+ show_status (self, _("An error occurred"), TRUE);
+ gtk_widget_grab_focus (self->password_entry);
+ }
+
+ break;
+ }
+ } else {
+ update_widgets (self);
+ show_status (self, _("An error occurred"), TRUE);
+ gtk_widget_grab_focus (self->password_entry);
+ }
+}
+
+static void
+check_snapd_response (GsUbuntuoneDialog *self,
+ guint status_code,
+ gchar *reason_phrase,
+ gchar *response_type,
+ gchar *response,
+ gsize response_length)
+{
+ g_autoptr(GVariant) variant = NULL;
+ g_autoptr(GVariant) result = NULL;
+ g_autoptr(GVariant) discharges = NULL;
+ const gchar *type;
+ const gchar *kind;
+ const gchar *macaroon;
+ GVariantBuilder builder;
+ GVariantIter iter;
+ GVariant *discharge = NULL;
+
+ if (status_code == 401) {
+ /* snapd isn't giving us enough information to tell why the authentication failed... */
+ if (g_str_equal (gtk_stack_get_visible_child_name (GTK_STACK (self->page_stack)), "page-1")) {
+ show_status (self, _("Two-factor authentication failed"), TRUE);
+ gtk_widget_grab_focus (self->passcode_entry);
+ return;
+ }
+ }
+
+ variant = json_gvariant_deserialize_data (response, -1, NULL, NULL);
+
+ if (variant == NULL)
+ goto err;
+
+ g_variant_ref_sink (variant);
+
+ if (!g_variant_lookup (variant, "type", "&s", &type))
+ goto err;
+
+ result = g_variant_lookup_value (variant, "result", G_VARIANT_TYPE_DICTIONARY);
+
+ if (result == NULL)
+ goto err;
+
+ if (g_str_equal (type, "sync")) {
+ if (!g_variant_lookup (result, "macaroon", "&s", &macaroon))
+ goto err;
+
+ discharges = g_variant_lookup_value (result, "discharges", G_VARIANT_TYPE ("av"));
+
+ if (discharges == NULL)
+ goto err;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
+ g_variant_iter_init (&iter, discharges);
+
+ while (g_variant_iter_loop (&iter, "v", &discharge))
+ g_variant_builder_add (&builder, "s", g_variant_get_string (discharge, NULL));
+
+ self->macaroon = g_variant_ref_sink (g_variant_new ("(s as)", macaroon, g_variant_builder_end
(&builder)));
+
+ gtk_stack_set_visible_child_name (GTK_STACK (self->page_stack), "page-2");
+ update_widgets (self);
+ } else if (g_variant_lookup (result, "kind", "&s", &kind)) {
+ if (g_str_equal (kind, "two-factor-required")) {
+ gtk_stack_set_visible_child_name (GTK_STACK (self->page_stack), "page-1");
+ gtk_widget_grab_focus (self->passcode_entry);
+ update_widgets (self);
+ } else
+ goto err;
+ } else
+ goto err;
+
+ return;
+
+err:
+ /* snapd isn't giving us enough information to tell why the authentication failed... */
+ show_status (self, status_code == 401 ? _("Incorrect email or password") : _("An error occurred"),
TRUE);
+ gtk_widget_grab_focus (self->password_entry);
+}
+
+static void
+send_login_request (GsUbuntuoneDialog *self)
+{
+ g_autofree gchar *content = NULL;
+ g_autofree gchar *username = NULL;
+ g_autofree gchar *password = NULL;
+ g_autofree gchar *otp = NULL;
+ GVariant *request;
+ guint status_code;
+ g_autofree gchar *reason_phrase = NULL;
+ g_autofree gchar *response_type = NULL;
+ g_autofree gchar *response = NULL;
+ gsize response_length;
+ g_autoptr(GError) error = NULL;
+
+ gtk_widget_set_sensitive (self->cancel_button, FALSE);
+ gtk_widget_set_sensitive (self->next_button, FALSE);
+ gtk_widget_set_sensitive (self->login_radio, FALSE);
+ gtk_widget_set_sensitive (self->register_radio, FALSE);
+ gtk_widget_set_sensitive (self->reset_radio, FALSE);
+ gtk_widget_set_sensitive (self->email_entry, FALSE);
+ gtk_widget_set_sensitive (self->password_entry, FALSE);
+ gtk_widget_set_sensitive (self->remember_check, FALSE);
+ gtk_widget_set_sensitive (self->passcode_entry, FALSE);
+
+ show_status (self, _("Signing in…"), FALSE);
+
+ if (self->get_macaroon) {
+ username = g_strescape (gtk_entry_get_text (GTK_ENTRY (self->email_entry)), NULL);
+ password = g_strescape (gtk_entry_get_text (GTK_ENTRY (self->password_entry)), NULL);
+
+ if (gtk_entry_get_text_length (GTK_ENTRY (self->passcode_entry)) > 0) {
+ otp = g_strescape (gtk_entry_get_text (GTK_ENTRY (self->passcode_entry)), NULL);
+
+ content = g_strdup_printf ("{"
+ " \"username\" : \"%s\","
+ " \"password\" : \"%s\","
+ " \"otp\" : \"%s\""
+ "}",
+ username,
+ password,
+ otp);
+ } else {
+ content = g_strdup_printf ("{"
+ " \"username\" : \"%s\","
+ " \"password\" : \"%s\""
+ "}",
+ username,
+ password);
+ }
+
+ if (send_snapd_request (SOUP_METHOD_POST,
+ "/v2/login",
+ content,
+ FALSE,
+ NULL,
+ FALSE,
+ NULL,
+ &status_code,
+ &reason_phrase,
+ &response_type,
+ &response,
+ &response_length,
+ &error)) {
+ reenable_widgets (self);
+
+ check_snapd_response (self,
+ status_code,
+ reason_phrase,
+ response_type,
+ response,
+ response_length);
+ } else {
+ g_warning ("could not send request: %s", error->message);
+
+ reenable_widgets (self);
+ show_status (self, _("An error occurred"), TRUE);
+ gtk_widget_grab_focus (self->password_entry);
+ }
+ } else {
+ if (gtk_entry_get_text_length (GTK_ENTRY (self->passcode_entry)) > 0) {
+ request = g_variant_new_parsed ("{"
+ " 'token_name' : <'GNOME Software'>,"
+ " 'email' : <%s>,"
+ " 'password' : <%s>,"
+ " 'otp' : <%s>"
+ "}",
+ gtk_entry_get_text (GTK_ENTRY (self->email_entry)),
+ gtk_entry_get_text (GTK_ENTRY (self->password_entry)),
+ gtk_entry_get_text (GTK_ENTRY
(self->passcode_entry)));
+ } else {
+ request = g_variant_new_parsed ("{"
+ " 'token_name' : <'GNOME Software'>,"
+ " 'email' : <%s>,"
+ " 'password' : <%s>"
+ "}",
+ gtk_entry_get_text (GTK_ENTRY (self->email_entry)),
+ gtk_entry_get_text (GTK_ENTRY
(self->password_entry)));
+ }
+
+ send_request (self,
+ SOUP_METHOD_POST,
+ "/api/v2/tokens/oauth",
+ request,
+ receive_login_response_cb,
+ NULL);
+ }
+}
+
+static void
+next_button_clicked_cb (GsUbuntuoneDialog *self,
+ GtkButton *button)
+{
+ if (g_str_equal (gtk_stack_get_visible_child_name (GTK_STACK (self->page_stack)), "page-0")) {
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->login_radio))) {
+ send_login_request (self);
+ } else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->register_radio))) {
+ g_app_info_launch_default_for_uri ("https://login.ubuntu.com/+new_account", NULL,
NULL);
+ } else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->reset_radio))) {
+ g_app_info_launch_default_for_uri ("https://login.ubuntu.com/+forgot_password", NULL,
NULL);
+ }
+ } else if (g_str_equal (gtk_stack_get_visible_child_name (GTK_STACK (self->page_stack)), "page-1")) {
+ send_login_request (self);
+ } else if (g_str_equal (gtk_stack_get_visible_child_name (GTK_STACK (self->page_stack)), "page-2")) {
+ gtk_dialog_response (GTK_DIALOG (self), GTK_RESPONSE_OK);
+ }
+}
+
+static void
+radio_button_toggled_cb (GsUbuntuoneDialog *self,
+ GtkToggleButton *toggle)
+{
+ update_widgets (self);
+}
+
+static void
+entry_edited_cb (GsUbuntuoneDialog *self,
+ GParamSpec *pspec,
+ GObject *object)
+{
+ update_widgets (self);
+}
+
+static void
+gs_ubuntuone_dialog_init (GsUbuntuoneDialog *self)
+{
+ GList *focus_chain = NULL;
+
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ gtk_window_set_default (GTK_WINDOW (self), self->next_button);
+
+ focus_chain = g_list_append (focus_chain, self->email_entry);
+ focus_chain = g_list_append (focus_chain, self->password_entry);
+ focus_chain = g_list_append (focus_chain, self->remember_check);
+ focus_chain = g_list_append (focus_chain, self->login_radio);
+ focus_chain = g_list_append (focus_chain, self->register_radio);
+ focus_chain = g_list_append (focus_chain, self->reset_radio);
+ gtk_container_set_focus_chain (GTK_CONTAINER (gtk_widget_get_parent (self->email_entry)),
focus_chain);
+ g_list_free (focus_chain);
+
+ g_signal_connect_swapped (self->next_button, "clicked", G_CALLBACK (next_button_clicked_cb), self);
+ g_signal_connect_swapped (self->login_radio, "toggled", G_CALLBACK (radio_button_toggled_cb), self);
+ g_signal_connect_swapped (self->register_radio, "toggled", G_CALLBACK (radio_button_toggled_cb),
self);
+ g_signal_connect_swapped (self->reset_radio, "toggled", G_CALLBACK (radio_button_toggled_cb), self);
+ g_signal_connect_swapped (self->email_entry, "notify::text", G_CALLBACK (entry_edited_cb), self);
+ g_signal_connect_swapped (self->password_entry, "notify::text", G_CALLBACK (entry_edited_cb), self);
+ g_signal_connect_swapped (self->passcode_entry, "notify::text", G_CALLBACK (entry_edited_cb), self);
+
+ update_widgets (self);
+}
+
+static void
+gs_ubuntuone_dialog_dispose (GObject *object)
+{
+ GsUbuntuoneDialog *self = GS_UBUNTUONE_DIALOG (object);
+
+ g_clear_object (&self->session);
+
+ G_OBJECT_CLASS (gs_ubuntuone_dialog_parent_class)->dispose (object);
+}
+
+static void
+gs_ubuntuone_dialog_finalize (GObject *object)
+{
+ GsUbuntuoneDialog *self = GS_UBUNTUONE_DIALOG (object);
+
+ g_clear_pointer (&self->token_secret, g_free);
+ g_clear_pointer (&self->token_key, g_free);
+ g_clear_pointer (&self->consumer_secret, g_free);
+ g_clear_pointer (&self->consumer_key, g_free);
+ g_clear_pointer (&self->macaroon, g_variant_unref);
+
+ G_OBJECT_CLASS (gs_ubuntuone_dialog_parent_class)->finalize (object);
+}
+
+static void
+gs_ubuntuone_dialog_class_init (GsUbuntuoneDialogClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->dispose = gs_ubuntuone_dialog_dispose;
+ object_class->finalize = gs_ubuntuone_dialog_finalize;
+
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/Software/plugins/gs-ubuntuone-dialog.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, GsUbuntuoneDialog, content_box);
+ gtk_widget_class_bind_template_child (widget_class, GsUbuntuoneDialog, cancel_button);
+ gtk_widget_class_bind_template_child (widget_class, GsUbuntuoneDialog, next_button);
+ gtk_widget_class_bind_template_child (widget_class, GsUbuntuoneDialog, status_stack);
+ gtk_widget_class_bind_template_child (widget_class, GsUbuntuoneDialog, status_image);
+ gtk_widget_class_bind_template_child (widget_class, GsUbuntuoneDialog, status_label);
+ gtk_widget_class_bind_template_child (widget_class, GsUbuntuoneDialog, page_stack);
+ gtk_widget_class_bind_template_child (widget_class, GsUbuntuoneDialog, prompt_label);
+ gtk_widget_class_bind_template_child (widget_class, GsUbuntuoneDialog, login_radio);
+ gtk_widget_class_bind_template_child (widget_class, GsUbuntuoneDialog, register_radio);
+ gtk_widget_class_bind_template_child (widget_class, GsUbuntuoneDialog, reset_radio);
+ gtk_widget_class_bind_template_child (widget_class, GsUbuntuoneDialog, email_entry);
+ gtk_widget_class_bind_template_child (widget_class, GsUbuntuoneDialog, password_entry);
+ gtk_widget_class_bind_template_child (widget_class, GsUbuntuoneDialog, remember_check);
+ gtk_widget_class_bind_template_child (widget_class, GsUbuntuoneDialog, passcode_entry);
+}
+
+gboolean
+gs_ubuntuone_dialog_get_do_remember (GsUbuntuoneDialog *dialog)
+{
+ g_return_val_if_fail (GS_IS_UBUNTUONE_DIALOG (dialog), FALSE);
+ return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->remember_check));
+}
+
+GVariant *
+gs_ubuntuone_dialog_get_macaroon (GsUbuntuoneDialog *dialog)
+{
+ g_return_val_if_fail (GS_IS_UBUNTUONE_DIALOG (dialog), NULL);
+ return dialog->macaroon;
+}
+
+const gchar *
+gs_ubuntuone_dialog_get_consumer_key (GsUbuntuoneDialog *dialog)
+{
+ g_return_val_if_fail (GS_IS_UBUNTUONE_DIALOG (dialog), NULL);
+ return dialog->consumer_key;
+}
+
+const gchar *
+gs_ubuntuone_dialog_get_consumer_secret (GsUbuntuoneDialog *dialog)
+{
+ g_return_val_if_fail (GS_IS_UBUNTUONE_DIALOG (dialog), NULL);
+ return dialog->consumer_secret;
+}
+
+const gchar *
+gs_ubuntuone_dialog_get_token_key (GsUbuntuoneDialog *dialog)
+{
+ g_return_val_if_fail (GS_IS_UBUNTUONE_DIALOG (dialog), NULL);
+ return dialog->token_key;
+}
+
+const gchar *
+gs_ubuntuone_dialog_get_token_secret (GsUbuntuoneDialog *dialog)
+{
+ g_return_val_if_fail (GS_IS_UBUNTUONE_DIALOG (dialog), NULL);
+ return dialog->token_secret;
+}
+
+GtkWidget *
+gs_ubuntuone_dialog_new (gboolean get_macaroon)
+{
+ GsUbuntuoneDialog *dialog = g_object_new (GS_TYPE_UBUNTUONE_DIALOG,
+ "use-header-bar", TRUE,
+ NULL);
+
+ dialog->get_macaroon = get_macaroon;
+
+ if (dialog->get_macaroon)
+ gtk_label_set_label (GTK_LABEL (dialog->prompt_label),
+ _("To install and remove snaps, you need an Ubuntu Single Sign-On account."));
+ else
+ gtk_label_set_label (GTK_LABEL (dialog->prompt_label),
+ _("To rate and review software, you need an Ubuntu Single Sign-On account."));
+
+ return GTK_WIDGET (dialog);
+}
+
+/* vim: set noexpandtab: */
diff --git a/src/plugins/gs-ubuntuone-dialog.h b/src/plugins/gs-ubuntuone-dialog.h
new file mode 100644
index 0000000..d98404e
--- /dev/null
+++ b/src/plugins/gs-ubuntuone-dialog.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2016 Canonical Ltd.
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef GS_UBUNTUONE_DIALOG_H
+#define GS_UBUNTUONE_DIALOG_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_UBUNTUONE_DIALOG gs_ubuntuone_dialog_get_type ()
+
+G_DECLARE_FINAL_TYPE (GsUbuntuoneDialog, gs_ubuntuone_dialog, GS, UBUNTUONE_DIALOG, GtkDialog)
+
+GtkWidget *gs_ubuntuone_dialog_new (gboolean get_macaroon);
+gboolean gs_ubuntuone_dialog_get_do_remember (GsUbuntuoneDialog *dialog);
+GVariant *gs_ubuntuone_dialog_get_macaroon (GsUbuntuoneDialog *dialog);
+const gchar *gs_ubuntuone_dialog_get_consumer_key (GsUbuntuoneDialog *dialog);
+const gchar *gs_ubuntuone_dialog_get_consumer_secret (GsUbuntuoneDialog *dialog);
+const gchar *gs_ubuntuone_dialog_get_token_key (GsUbuntuoneDialog *dialog);
+const gchar *gs_ubuntuone_dialog_get_token_secret (GsUbuntuoneDialog *dialog);
+
+G_END_DECLS
+
+#endif /* GS_UBUNTUONE_DIALOG_H */
+
+/* vim: set noexpandtab: */
diff --git a/src/plugins/gs-ubuntuone-dialog.ui b/src/plugins/gs-ubuntuone-dialog.ui
new file mode 100644
index 0000000..e61c09e
--- /dev/null
+++ b/src/plugins/gs-ubuntuone-dialog.ui
@@ -0,0 +1,386 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.19.0 -->
+<interface>
+ <requires lib="gtk+" version="3.16"/>
+ <template class="GsUbuntuoneDialog" parent="GtkDialog">
+ <action-widgets>
+ <action-widget response="cancel">cancel_button</action-widget>
+ </action-widgets>
+ <child internal-child="headerbar">
+ <object class="GtkHeaderBar">
+ <property name="show_close_button">False</property>
+ <child>
+ <object class="GtkButton" id="cancel_button">
+ <property name="label" translatable="yes">_Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="pack-type">start</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="next_button">
+ <property name="label" translatable="yes">_Continue</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ <style>
+ <class name="suggested-action"/>
+ </style>
+ </object>
+ <packing>
+ <property name="pack-type">end</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="content_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">20</property>
+ <property name="margin_right">20</property>
+ <property name="margin_top">20</property>
+ <property name="margin_bottom">20</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">40</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">20</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="yalign">0</property>
+ <property name="resource">/org/gnome/Software/plugins/ubuntu-one.png</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkStack" id="page_stack">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkGrid" id="page-0">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel" id="prompt_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_bottom">20</property>
+ <property name="label" translatable="yes">To rate and review software, you need an
Ubuntu Single Sign-On account.</property>
+ <property name="wrap">True</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAccelLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">end</property>
+ <property name="margin_right">10</property>
+ <property name="margin_bottom">20</property>
+ <property name="label" translatable="yes">_Email address:</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="email_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="margin_bottom">20</property>
+ <property name="hexpand">True</property>
+ <property name="input_purpose">email</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="login_radio">
+ <property name="label" translatable="yes">I have an Ubuntu Single Sign-On
account</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="margin_bottom">5</property>
+ <property name="xalign">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAccelLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">end</property>
+ <property name="margin_left">25</property>
+ <property name="margin_right">10</property>
+ <property name="margin_bottom">5</property>
+ <property name="label" translatable="yes">_Password:</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="password_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="margin_bottom">5</property>
+ <property name="hexpand">True</property>
+ <property name="visibility">False</property>
+ <property name="invisible_char">•</property>
+ <property name="input_purpose">password</property>
+ <property name="activates_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="remember_check">
+ <property name="label" translatable="yes">Sign in automatically next time</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="margin_bottom">20</property>
+ <property name="hexpand">True</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="register_radio">
+ <property name="label" translatable="yes">I want to register for an account
now</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="margin_bottom">20</property>
+ <property name="xalign">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">login_radio</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">5</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="reset_radio">
+ <property name="label" translatable="yes">I've forgotten my password</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="xalign">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">login_radio</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">6</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="name">page-0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="page-1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_bottom">20</property>
+ <property name="label" translatable="yes">Enter your one-time password for
two-factor authentication.</property>
+ <property name="wrap">True</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAccelLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_right">10</property>
+ <property name="label" translatable="yes">One-time password:</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="passcode_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="input_purpose">pin</property>
+ <property name="activates_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="name">page-1</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="page-2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">You are now signed into Ubuntu
One.</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="name">page-2</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">10</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkStack" id="status_stack">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_right">5</property>
+ <child>
+ <object class="GtkImage" id="status_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="name">status-image</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinner" id="status_spinner">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="active">True</property>
+ </object>
+ <packing>
+ <property name="name">status-spinner</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="status_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/plugins/ubuntu-one.png b/src/plugins/ubuntu-one.png
new file mode 100644
index 0000000..a58248a
Binary files /dev/null and b/src/plugins/ubuntu-one.png differ
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]