From 0ce01af0e0e58d81f998093f5a8a068539f56c12 Mon Sep 17 00:00:00 2001 From: Kalev Lember Date: Fri, 28 Jun 2019 17:14:36 +0200 Subject: [PATCH] info: Add subscription manager integration --- panels/info/cc-info-overview-panel.c | 157 ++++- panels/info/cc-subscription-details-dialog.c | 407 ++++++++++++ panels/info/cc-subscription-details-dialog.h | 32 + panels/info/cc-subscription-details-dialog.ui | 248 +++++++ panels/info/cc-subscription-register-dialog.c | 403 +++++++++++ panels/info/cc-subscription-register-dialog.h | 32 + .../info/cc-subscription-register-dialog.ui | 623 ++++++++++++++++++ panels/info/info-overview.ui | 138 +++- panels/info/info.gresource.xml | 2 + panels/info/meson.build | 4 + po/POTFILES.in | 4 + 11 files changed, 2038 insertions(+), 12 deletions(-) create mode 100644 panels/info/cc-subscription-details-dialog.c create mode 100644 panels/info/cc-subscription-details-dialog.h create mode 100644 panels/info/cc-subscription-details-dialog.ui create mode 100644 panels/info/cc-subscription-register-dialog.c create mode 100644 panels/info/cc-subscription-register-dialog.h create mode 100644 panels/info/cc-subscription-register-dialog.ui diff --git a/panels/info/cc-info-overview-panel.c b/panels/info/cc-info-overview-panel.c index 2256b730c..1467060f9 100644 --- a/panels/info/cc-info-overview-panel.c +++ b/panels/info/cc-info-overview-panel.c @@ -24,6 +24,8 @@ #include "shell/cc-hostname-entry.h" #include "cc-info-resources.h" +#include "cc-subscription-details-dialog.h" +#include "cc-subscription-register-dialog.h" #include "info-cleanup.h" #include @@ -68,6 +70,10 @@ typedef struct GtkWidget *disk_label; GtkWidget *graphics_label; GtkWidget *virt_type_label; + GtkWidget *subscription_stack; + GtkWidget *details_button; + GtkWidget *register_button; + GtkWidget *updates_separator; GtkWidget *updates_button; /* Virtualisation labels */ @@ -86,6 +92,8 @@ typedef struct guint64 total_bytes; GraphicsData *graphics_data; + + GDBusProxy *subscription_proxy; } CcInfoOverviewPanelPrivate; struct _CcInfoOverviewPanel @@ -586,7 +594,6 @@ get_primary_disc_info (CcInfoOverviewPanel *self) g_list_free (points); g_hash_table_destroy (hash); - priv->cancellable = g_cancellable_new (); get_primary_disc_info_start (self); } @@ -793,6 +800,137 @@ info_overview_panel_setup_overview (CcInfoOverviewPanel *self) gtk_label_set_markup (GTK_LABEL (priv->graphics_label), priv->graphics_data->hardware_string); } +typedef enum { + GSD_SUBMAN_SUBSCRIPTION_STATUS_UNKNOWN, + GSD_SUBMAN_SUBSCRIPTION_STATUS_VALID, + GSD_SUBMAN_SUBSCRIPTION_STATUS_INVALID, + GSD_SUBMAN_SUBSCRIPTION_STATUS_DISABLED, + GSD_SUBMAN_SUBSCRIPTION_STATUS_PARTIALLY_VALID, + GSD_SUBMAN_SUBSCRIPTION_STATUS_LAST +} GsdSubmanSubscriptionStatus; + +static gboolean +get_subscription_status (CcInfoOverviewPanel *self, GsdSubmanSubscriptionStatus *status) +{ + CcInfoOverviewPanelPrivate *priv = cc_info_overview_panel_get_instance_private (self); + g_autoptr(GVariant) status_variant = NULL; + guint32 u; + + status_variant = g_dbus_proxy_get_cached_property (priv->subscription_proxy, "SubscriptionStatus"); + if (!status_variant) + { + g_debug ("Unable to get SubscriptionStatus property"); + return FALSE; + } + + g_variant_get (status_variant, "u", &u); + *status = u; + + return TRUE; +} + +static void +reload_subscription_status (CcInfoOverviewPanel *self) +{ + CcInfoOverviewPanelPrivate *priv = cc_info_overview_panel_get_instance_private (self); + GsdSubmanSubscriptionStatus status; + + if (priv->subscription_proxy == NULL) + { + gtk_widget_hide (priv->subscription_stack); + return; + } + + if (!get_subscription_status (self, &status)) + { + gtk_widget_hide (priv->subscription_stack); + return; + } + + switch (status) + { + case GSD_SUBMAN_SUBSCRIPTION_STATUS_UNKNOWN: + case GSD_SUBMAN_SUBSCRIPTION_STATUS_INVALID: + case GSD_SUBMAN_SUBSCRIPTION_STATUS_DISABLED: + case GSD_SUBMAN_SUBSCRIPTION_STATUS_PARTIALLY_VALID: + gtk_stack_set_visible_child_name (GTK_STACK (priv->subscription_stack), "not-registered"); + gtk_widget_set_sensitive (priv->updates_button, FALSE); + break; + + case GSD_SUBMAN_SUBSCRIPTION_STATUS_VALID: + gtk_stack_set_visible_child_name (GTK_STACK (priv->subscription_stack), "registered"); + gtk_widget_set_sensitive (priv->updates_button, TRUE); + break; + + default: + g_assert_not_reached (); + break; + } +} + +static void +on_details_button_clicked (GtkWidget *widget, + CcInfoOverviewPanel *self) +{ + CcInfoOverviewPanelPrivate *priv = cc_info_overview_panel_get_instance_private (self); + CcSubscriptionDetailsDialog *dialog; + GtkWindow *toplevel; + + dialog = cc_subscription_details_dialog_new (priv->subscription_proxy); + toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))); + gtk_window_set_transient_for (GTK_WINDOW (dialog), toplevel); + + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (GTK_WIDGET (dialog)); + + reload_subscription_status (self); +} + +static void +on_register_button_clicked (GtkWidget *widget, + CcInfoOverviewPanel *self) +{ + CcInfoOverviewPanelPrivate *priv = cc_info_overview_panel_get_instance_private (self); + CcSubscriptionRegisterDialog *dialog; + GtkWindow *toplevel; + + dialog = cc_subscription_register_dialog_new (priv->subscription_proxy); + toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))); + gtk_window_set_transient_for (GTK_WINDOW (dialog), toplevel); + + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (GTK_WIDGET (dialog)); + + reload_subscription_status (self); +} + +static void +info_overview_panel_setup_subscriptions (CcInfoOverviewPanel *self) +{ + CcInfoOverviewPanelPrivate *priv = cc_info_overview_panel_get_instance_private (self); + g_autoptr(GError) error = NULL; + + priv->subscription_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.gnome.SettingsDaemon.Subscription", + "/org/gnome/SettingsDaemon/Subscription", + "org.gnome.SettingsDaemon.Subscription", + NULL, &error); + if (error != NULL) + { + g_debug ("Unable to create a proxy for org.gnome.SettingsDaemon.Subscription: %s", + error->message); + reload_subscription_status (self); + return; + } + + g_signal_connect (priv->details_button, "clicked", G_CALLBACK (on_details_button_clicked), self); + g_signal_connect (priv->register_button, "clicked", G_CALLBACK (on_register_button_clicked), self); + + reload_subscription_status (self); +} + static gboolean does_gnome_software_exist (void) { @@ -856,6 +994,8 @@ cc_info_overview_panel_finalize (GObject *object) g_free (priv->gnome_date); g_free (priv->gnome_distributor); + g_clear_object (&priv->subscription_proxy); + G_OBJECT_CLASS (cc_info_overview_panel_parent_class)->finalize (object); } @@ -880,6 +1020,10 @@ cc_info_overview_panel_class_init (CcInfoOverviewPanelClass *klass) gtk_widget_class_bind_template_child_private (widget_class, CcInfoOverviewPanel, disk_label); gtk_widget_class_bind_template_child_private (widget_class, CcInfoOverviewPanel, graphics_label); gtk_widget_class_bind_template_child_private (widget_class, CcInfoOverviewPanel, virt_type_label); + gtk_widget_class_bind_template_child_private (widget_class, CcInfoOverviewPanel, subscription_stack); + gtk_widget_class_bind_template_child_private (widget_class, CcInfoOverviewPanel, details_button); + gtk_widget_class_bind_template_child_private (widget_class, CcInfoOverviewPanel, register_button); + gtk_widget_class_bind_template_child_private (widget_class, CcInfoOverviewPanel, updates_separator); gtk_widget_class_bind_template_child_private (widget_class, CcInfoOverviewPanel, updates_button); gtk_widget_class_bind_template_child_private (widget_class, CcInfoOverviewPanel, label8); gtk_widget_class_bind_template_child_private (widget_class, CcInfoOverviewPanel, grid1); @@ -897,15 +1041,24 @@ cc_info_overview_panel_init (CcInfoOverviewPanel *self) g_resources_register (cc_info_get_resource ()); + priv->cancellable = g_cancellable_new (); + priv->graphics_data = get_graphics_data (); if (does_gnome_software_exist () || does_gpk_update_viewer_exist ()) g_signal_connect (priv->updates_button, "clicked", G_CALLBACK (on_updates_button_clicked), self); else - gtk_widget_destroy (priv->updates_button); + gtk_widget_hide (priv->updates_button); info_overview_panel_setup_overview (self); info_overview_panel_setup_virt (self); + info_overview_panel_setup_subscriptions (self); + + /* show separator when both items are visible */ + if (gtk_widget_get_visible (priv->subscription_stack) && gtk_widget_get_visible (priv->updates_button)) + gtk_widget_show (priv->updates_separator); + else + gtk_widget_hide (priv->updates_separator); } GtkWidget * diff --git a/panels/info/cc-subscription-details-dialog.c b/panels/info/cc-subscription-details-dialog.c new file mode 100644 index 000000000..1931ced81 --- /dev/null +++ b/panels/info/cc-subscription-details-dialog.c @@ -0,0 +1,407 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright 2019 Red Hat, Inc, + * + * 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, see . + * + * Written by: Kalev Lember + */ + +#include "config.h" + +#include +#include +#include + +#include "cc-subscription-details-dialog.h" + +#define DBUS_TIMEOUT 300000 /* 5 minutes */ + +typedef enum { + DIALOG_STATE_SHOW_DETAILS, + DIALOG_STATE_UNREGISTER, + DIALOG_STATE_UNREGISTERING +} DialogState; + +struct _CcSubscriptionDetailsDialog +{ + GtkDialog parent_instance; + + DialogState state; + GCancellable *cancellable; + GDBusProxy *subscription_proxy; + GPtrArray *products; + + /* template widgets */ + GtkButton *back_button; + GtkSpinner *spinner; + GtkButton *header_unregister_button; + GtkRevealer *notification_revealer; + GtkLabel *error_label; + GtkStack *stack; + GtkBox *products_box1; + GtkBox *products_box2; + GtkButton *unregister_button; +}; + +G_DEFINE_TYPE (CcSubscriptionDetailsDialog, cc_subscription_details_dialog, GTK_TYPE_DIALOG); + +typedef struct +{ + gchar *product_name; + gchar *product_id; + gchar *version; + gchar *arch; + gchar *status; + gchar *starts; + gchar *ends; +} ProductData; + +static void +product_data_free (ProductData *product) +{ + g_free (product->product_name); + g_free (product->product_id); + g_free (product->version); + g_free (product->arch); + g_free (product->status); + g_free (product->starts); + g_free (product->ends); + g_free (product); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (ProductData, product_data_free); + +static void +add_product_row (GtkGrid *product_grid, const gchar *name, const gchar *value, gint top_attach) +{ + GtkWidget *w; + + w = gtk_label_new (name); + gtk_style_context_add_class (gtk_widget_get_style_context (w), "dim-label"); + gtk_grid_attach (product_grid, w, 0, top_attach, 1, 1); + gtk_widget_set_halign (w, GTK_ALIGN_END); + gtk_widget_show (w); + + if (value == NULL) + value = _("Unknown"); + + w = gtk_label_new (value); + gtk_grid_attach (product_grid, w, 1, top_attach, 1, 1); + gtk_widget_set_halign (w, GTK_ALIGN_START); + gtk_widget_set_hexpand (w, TRUE); + gtk_widget_show (w); +} + +static GtkWidget * +add_product (CcSubscriptionDetailsDialog *self, ProductData *product) +{ + GtkGrid *product_grid; + const gchar *status_text; + + if (g_strcmp0 (product->status, "subscribed") == 0) + status_text = _("Subscribed"); + else + status_text = _("Not Subscribed (Not supported by a valid subscription.)"); + + product_grid = GTK_GRID (gtk_grid_new ()); + gtk_grid_set_column_spacing (product_grid, 12); + gtk_grid_set_row_spacing (product_grid, 6); + gtk_widget_set_margin_top (GTK_WIDGET (product_grid), 18); + gtk_widget_set_margin_bottom (GTK_WIDGET (product_grid), 12); + gtk_widget_show (GTK_WIDGET (product_grid)); + + add_product_row (product_grid, _("Product Name"), product->product_name, 0); + add_product_row (product_grid, _("Product ID"), product->product_id, 1); + add_product_row (product_grid, _("Version"), product->version, 2); + add_product_row (product_grid, _("Arch"), product->arch, 3); + add_product_row (product_grid, _("Status"), status_text, 4); + add_product_row (product_grid, _("Starts"), product->starts, 5); + add_product_row (product_grid, _("Ends"), product->ends, 6); + + return GTK_WIDGET (product_grid); +} + +static void +remove_all_children (GtkContainer *container) +{ + g_autoptr(GList) list = gtk_container_get_children (container); + + for (GList *l = list; l != NULL; l = l->next) + gtk_container_remove (container, (GtkWidget *) l->data); +} + +static void +dialog_reload (CcSubscriptionDetailsDialog *self) +{ + GtkHeaderBar *header = GTK_HEADER_BAR (gtk_dialog_get_header_bar (GTK_DIALOG (self))); + + switch (self->state) + { + case DIALOG_STATE_SHOW_DETAILS: + gtk_header_bar_set_show_close_button (header, TRUE); + + gtk_window_set_title (GTK_WINDOW (self), _("Registration Details")); + gtk_widget_set_sensitive (GTK_WIDGET (self->header_unregister_button), TRUE); + + gtk_widget_hide (GTK_WIDGET (self->back_button)); + gtk_widget_hide (GTK_WIDGET (self->header_unregister_button)); + + gtk_stack_set_visible_child_name (self->stack, "show-details"); + break; + + case DIALOG_STATE_UNREGISTER: + gtk_header_bar_set_show_close_button (header, FALSE); + + gtk_window_set_title (GTK_WINDOW (self), _("Unregister System")); + gtk_widget_set_sensitive (GTK_WIDGET (self->header_unregister_button), TRUE); + + gtk_widget_show (GTK_WIDGET (self->back_button)); + gtk_widget_show (GTK_WIDGET (self->header_unregister_button)); + + gtk_stack_set_visible_child_name (self->stack, "unregister"); + break; + + case DIALOG_STATE_UNREGISTERING: + gtk_header_bar_set_show_close_button (header, FALSE); + + gtk_window_set_title (GTK_WINDOW (self), _("Unregistering System…")); + gtk_widget_set_sensitive (GTK_WIDGET (self->header_unregister_button), FALSE); + + gtk_widget_show (GTK_WIDGET (self->back_button)); + gtk_widget_show (GTK_WIDGET (self->header_unregister_button)); + + gtk_stack_set_visible_child_name (self->stack, "unregister"); + break; + + default: + g_assert_not_reached (); + break; + } + + remove_all_children (GTK_CONTAINER (self->products_box1)); + remove_all_children (GTK_CONTAINER (self->products_box2)); + + if (self->products == NULL || self->products->len == 0) + { + /* the widgets are duplicate to allow sliding between two stack pages */ + GtkWidget *w1 = gtk_label_new (_("No installed products detected.")); + GtkWidget *w2 = gtk_label_new (_("No installed products detected.")); + gtk_widget_show (w1); + gtk_widget_show (w2); + gtk_container_add (GTK_CONTAINER (self->products_box1), w1); + gtk_container_add (GTK_CONTAINER (self->products_box2), w2); + return; + } + + for (guint i = 0; i < self->products->len; i++) + { + ProductData *product = g_ptr_array_index (self->products, i); + /* the widgets are duplicate to allow sliding between two stack pages */ + GtkWidget *w1 = add_product (self, product); + GtkWidget *w2 = add_product (self, product); + gtk_container_add (GTK_CONTAINER (self->products_box1), w1); + gtk_container_add (GTK_CONTAINER (self->products_box2), w2); + } +} + +static ProductData * +parse_product_variant (GVariant *product_variant) +{ + g_autoptr(ProductData) product = g_new0 (ProductData, 1); + g_auto(GVariantDict) dict; + + g_variant_dict_init (&dict, product_variant); + + g_variant_dict_lookup (&dict, "product-name", "s", &product->product_name); + g_variant_dict_lookup (&dict, "product-id", "s", &product->product_id); + g_variant_dict_lookup (&dict, "version", "s", &product->version); + g_variant_dict_lookup (&dict, "arch", "s", &product->arch); + g_variant_dict_lookup (&dict, "status", "s", &product->status); + g_variant_dict_lookup (&dict, "starts", "s", &product->starts); + g_variant_dict_lookup (&dict, "ends", "s", &product->ends); + + return g_steal_pointer (&product); +} + +static void +load_installed_products (CcSubscriptionDetailsDialog *self) +{ + GVariantIter iter_array; + GVariant *child; + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) installed_products_variant = NULL; + + installed_products_variant = g_dbus_proxy_get_cached_property (self->subscription_proxy, "InstalledProducts"); + if (installed_products_variant == NULL) + { + g_debug ("Unable to get InstalledProducts dbus property"); + return; + } + + g_ptr_array_set_size (self->products, 0); + + g_variant_iter_init (&iter_array, installed_products_variant); + while ((child = g_variant_iter_next_value (&iter_array)) != NULL) + { + g_autoptr(GVariant) product_variant = g_steal_pointer (&child); + g_ptr_array_add (self->products, parse_product_variant (product_variant)); + } +} + +static void +unregistration_done_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + CcSubscriptionDetailsDialog *self = (CcSubscriptionDetailsDialog *) user_data; + g_autoptr(GVariant) results = NULL; + g_autoptr(GError) error = NULL; + + results = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), + res, + &error); + if (results == NULL) + { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + g_dbus_error_strip_remote_error (error); + gtk_label_set_text (self->error_label, error->message); + gtk_revealer_set_reveal_child (self->notification_revealer, TRUE); + + gtk_spinner_stop (self->spinner); + + self->state = DIALOG_STATE_UNREGISTER; + dialog_reload (self); + return; + } + + gtk_spinner_stop (self->spinner); + + gtk_dialog_response (GTK_DIALOG (self), GTK_RESPONSE_ACCEPT); +} + +static void +header_unregister_button_clicked_cb (CcSubscriptionDetailsDialog *self) +{ + gtk_spinner_start (self->spinner); + + self->state = DIALOG_STATE_UNREGISTERING; + dialog_reload (self); + + g_dbus_proxy_call (self->subscription_proxy, + "Unregister", + NULL, + G_DBUS_CALL_FLAGS_NONE, + DBUS_TIMEOUT, + self->cancellable, + unregistration_done_cb, + self); +} + +static void +back_button_clicked_cb (CcSubscriptionDetailsDialog *self) +{ + gtk_spinner_stop (self->spinner); + + self->state = DIALOG_STATE_SHOW_DETAILS; + dialog_reload (self); +} + +static void +unregister_button_clicked_cb (CcSubscriptionDetailsDialog *self) +{ + self->state = DIALOG_STATE_UNREGISTER; + dialog_reload (self); +} + +static void +dismiss_notification (CcSubscriptionDetailsDialog *self) +{ + gtk_revealer_set_reveal_child (self->notification_revealer, FALSE); +} + +static void +cc_subscription_details_dialog_init (CcSubscriptionDetailsDialog *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); + + self->cancellable = g_cancellable_new (); + self->products = g_ptr_array_new_with_free_func ((GDestroyNotify) product_data_free); + self->state = DIALOG_STATE_SHOW_DETAILS; +} + +static void +cc_subscription_details_dialog_dispose (GObject *obj) +{ + CcSubscriptionDetailsDialog *self = (CcSubscriptionDetailsDialog *) obj; + + g_cancellable_cancel (self->cancellable); + g_clear_object (&self->cancellable); + g_clear_object (&self->subscription_proxy); + + G_OBJECT_CLASS (cc_subscription_details_dialog_parent_class)->dispose (obj); +} + +static void +cc_subscription_details_dialog_finalize (GObject *obj) +{ + CcSubscriptionDetailsDialog *self = (CcSubscriptionDetailsDialog *) obj; + + g_clear_pointer (&self->products, g_ptr_array_unref); + + G_OBJECT_CLASS (cc_subscription_details_dialog_parent_class)->finalize (obj); +} + +static void +cc_subscription_details_dialog_class_init (CcSubscriptionDetailsDialogClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = cc_subscription_details_dialog_dispose; + object_class->finalize = cc_subscription_details_dialog_finalize; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/info/cc-subscription-details-dialog.ui"); + + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionDetailsDialog, back_button); + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionDetailsDialog, spinner); + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionDetailsDialog, header_unregister_button); + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionDetailsDialog, notification_revealer); + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionDetailsDialog, error_label); + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionDetailsDialog, stack); + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionDetailsDialog, products_box1); + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionDetailsDialog, products_box2); + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionDetailsDialog, unregister_button); + + gtk_widget_class_bind_template_callback (widget_class, back_button_clicked_cb); + gtk_widget_class_bind_template_callback (widget_class, header_unregister_button_clicked_cb); + gtk_widget_class_bind_template_callback (widget_class, unregister_button_clicked_cb); + gtk_widget_class_bind_template_callback (widget_class, dismiss_notification); +} + +CcSubscriptionDetailsDialog * +cc_subscription_details_dialog_new (GDBusProxy *subscription_proxy) +{ + CcSubscriptionDetailsDialog *self; + + self = g_object_new (CC_TYPE_SUBSCRIPTION_DETAILS_DIALOG, "use-header-bar", TRUE, NULL); + self->subscription_proxy = g_object_ref (subscription_proxy); + + load_installed_products (self); + dialog_reload (self); + + return self; +} diff --git a/panels/info/cc-subscription-details-dialog.h b/panels/info/cc-subscription-details-dialog.h new file mode 100644 index 000000000..a61a22838 --- /dev/null +++ b/panels/info/cc-subscription-details-dialog.h @@ -0,0 +1,32 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright 2019 Red Hat, Inc, + * + * 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, see . + * + * Written by: Kalev Lember + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define CC_TYPE_SUBSCRIPTION_DETAILS_DIALOG (cc_subscription_details_dialog_get_type ()) +G_DECLARE_FINAL_TYPE (CcSubscriptionDetailsDialog, cc_subscription_details_dialog, CC, SUBSCRIPTION_DETAILS_DIALOG, GtkDialog) + +CcSubscriptionDetailsDialog *cc_subscription_details_dialog_new (GDBusProxy *subscription_proxy); + +G_END_DECLS diff --git a/panels/info/cc-subscription-details-dialog.ui b/panels/info/cc-subscription-details-dialog.ui new file mode 100644 index 000000000..6f0b16930 --- /dev/null +++ b/panels/info/cc-subscription-details-dialog.ui @@ -0,0 +1,248 @@ + + + + diff --git a/panels/info/cc-subscription-register-dialog.c b/panels/info/cc-subscription-register-dialog.c new file mode 100644 index 000000000..d7a17cc99 --- /dev/null +++ b/panels/info/cc-subscription-register-dialog.c @@ -0,0 +1,403 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright 2019 Red Hat, Inc, + * + * 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, see . + * + * Written by: Kalev Lember + */ + +#include "config.h" + +#include +#include +#include + +#include "cc-subscription-register-dialog.h" + +#define DBUS_TIMEOUT 300000 /* 5 minutes */ +#define SERVER_URL "subscription.rhsm.redhat.com" + +typedef enum { + DIALOG_STATE_REGISTER, + DIALOG_STATE_REGISTERING +} DialogState; + +static void dialog_validate (CcSubscriptionRegisterDialog *self); + +struct _CcSubscriptionRegisterDialog +{ + GtkDialog parent_instance; + + DialogState state; + GCancellable *cancellable; + GDBusProxy *subscription_proxy; + gboolean valid; + + /* template widgets */ + GtkSpinner *spinner; + GtkButton *register_button; + GtkRevealer *notification_revealer; + GtkLabel *error_label; + GtkRadioButton *default_url_radio; + GtkRadioButton *custom_url_radio; + GtkRadioButton *register_radio; + GtkRadioButton *register_with_activation_keys_radio; + GtkStack *stack; + GtkGrid *register_grid; + GtkGrid *register_with_activation_keys_grid; + GtkEntry *url_label; + GtkEntry *url_entry; + GtkEntry *login_entry; + GtkEntry *password_entry; + GtkEntry *activation_keys_entry; + GtkLabel *organization_label; + GtkEntry *organization_entry; + GtkEntry *organization_entry_with_activation_keys; +}; + +G_DEFINE_TYPE (CcSubscriptionRegisterDialog, cc_subscription_register_dialog, GTK_TYPE_DIALOG); + +static void +dialog_reload (CcSubscriptionRegisterDialog *self) +{ + gboolean sensitive; + gboolean url_entry_enabled; + + switch (self->state) + { + case DIALOG_STATE_REGISTER: + gtk_window_set_title (GTK_WINDOW (self), _("Register System")); + gtk_widget_set_sensitive (GTK_WIDGET (self->register_button), self->valid); + + sensitive = TRUE; + break; + + case DIALOG_STATE_REGISTERING: + gtk_window_set_title (GTK_WINDOW (self), _("Registering System…")); + gtk_widget_set_sensitive (GTK_WIDGET (self->register_button), FALSE); + + sensitive = FALSE; + break; + + default: + g_assert_not_reached (); + break; + } + + url_entry_enabled = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->custom_url_radio)); + gtk_widget_set_sensitive (GTK_WIDGET (self->url_entry), sensitive && url_entry_enabled); + + gtk_widget_set_sensitive (GTK_WIDGET (self->default_url_radio), sensitive); + gtk_widget_set_sensitive (GTK_WIDGET (self->custom_url_radio), sensitive); + gtk_widget_set_sensitive (GTK_WIDGET (self->register_radio), sensitive); + gtk_widget_set_sensitive (GTK_WIDGET (self->register_with_activation_keys_radio), sensitive); + gtk_widget_set_sensitive (GTK_WIDGET (self->login_entry), sensitive); + gtk_widget_set_sensitive (GTK_WIDGET (self->password_entry), sensitive); + gtk_widget_set_sensitive (GTK_WIDGET (self->activation_keys_entry), sensitive); + gtk_widget_set_sensitive (GTK_WIDGET (self->password_entry), sensitive); + gtk_widget_set_sensitive (GTK_WIDGET (self->organization_entry), sensitive); + gtk_widget_set_sensitive (GTK_WIDGET (self->organization_entry_with_activation_keys), sensitive); +} + +static void +custom_url_radio_toggled_cb (CcSubscriptionRegisterDialog *self) +{ + gboolean active; + + active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->custom_url_radio)); + if (active) + { + gtk_widget_set_sensitive (GTK_WIDGET (self->url_entry), TRUE); + gtk_widget_set_sensitive (GTK_WIDGET (self->url_label), TRUE); + + gtk_entry_set_text (self->url_entry, ""); + gtk_widget_grab_focus (GTK_WIDGET (self->url_entry)); + } + else + { + gtk_widget_set_sensitive (GTK_WIDGET (self->url_entry), FALSE); + gtk_widget_set_sensitive (GTK_WIDGET (self->url_label), FALSE); + + gtk_entry_set_text (self->url_entry, SERVER_URL); + } + + dialog_validate (self); +} + +static void +register_with_activation_keys_radio_toggled_cb (CcSubscriptionRegisterDialog *self) +{ + gint active; + + active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->register_with_activation_keys_radio)); + if (active) + { + gtk_stack_set_visible_child_name (self->stack, "register-with-activation-keys"); + gtk_widget_grab_focus (GTK_WIDGET (self->activation_keys_entry)); + } + else + { + gtk_stack_set_visible_child_name (self->stack, "register"); + gtk_widget_grab_focus (GTK_WIDGET (self->login_entry)); + } + + dialog_validate (self); +} + +static void +dialog_validate (CcSubscriptionRegisterDialog *self) +{ + gboolean valid_url = TRUE; + gboolean valid_login = TRUE; + gboolean valid_password = TRUE; + gboolean valid_activation_keys = TRUE; + gboolean valid_organization = TRUE; + + /* require url when custom url radio is selected */ + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->custom_url_radio))) + { + const gchar *url; + + url = gtk_entry_get_text (self->url_entry); + valid_url = url != NULL && strlen (url) != 0; + } + + /* activation keys radio selected */ + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->register_with_activation_keys_radio))) + { + const gchar *activation_keys; + const gchar *organization; + + /* require activation keys */ + activation_keys = gtk_entry_get_text (self->activation_keys_entry); + valid_activation_keys = activation_keys != NULL && strlen (activation_keys) != 0; + + /* organization is required when using activation keys */ + organization = gtk_entry_get_text (self->organization_entry_with_activation_keys); + valid_organization = organization != NULL && strlen (organization) != 0; + + /* username/password radio selected */ + } + else + { + const gchar *login; + const gchar *password; + + /* require login */ + login = gtk_entry_get_text (self->login_entry); + valid_login = login != NULL && strlen (login) != 0; + + /* require password */ + password = gtk_entry_get_text (self->password_entry); + valid_password = password != NULL && strlen (password) != 0; + } + + self->valid = valid_url && valid_login && valid_password && valid_activation_keys && valid_organization; + gtk_widget_set_sensitive (GTK_WIDGET (self->register_button), self->valid); +} + +static void +registration_done_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + CcSubscriptionRegisterDialog *self = (CcSubscriptionRegisterDialog *) user_data; + g_autoptr(GVariant) results = NULL; + g_autoptr(GError) error = NULL; + + results = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), + res, + &error); + if (results == NULL) + { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + g_dbus_error_strip_remote_error (error); + gtk_label_set_text (self->error_label, error->message); + gtk_revealer_set_reveal_child (self->notification_revealer, TRUE); + + gtk_spinner_stop (self->spinner); + + self->state = DIALOG_STATE_REGISTER; + dialog_reload (self); + return; + } + + gtk_spinner_stop (self->spinner); + gtk_dialog_response (GTK_DIALOG (self), GTK_RESPONSE_ACCEPT); + return; +} + +static void +subscription_register_with_activation_keys (CcSubscriptionRegisterDialog *self) +{ + g_autoptr(GVariantBuilder) options_builder = NULL; + const gchar *hostname; + const gchar *organization; + const gchar *activation_keys; + + hostname = gtk_entry_get_text (self->url_entry); + organization = gtk_entry_get_text (self->organization_entry_with_activation_keys); + activation_keys = gtk_entry_get_text (self->activation_keys_entry); + + options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (options_builder, "{sv}", "kind", g_variant_new_string ("key")); + g_variant_builder_add (options_builder, "{sv}", "hostname", g_variant_new_string (hostname)); + g_variant_builder_add (options_builder, "{sv}", "organisation", g_variant_new_string (organization)); + g_variant_builder_add (options_builder, "{sv}", "activation-key", g_variant_new_string (activation_keys)); + + g_dbus_proxy_call (self->subscription_proxy, + "Register", + g_variant_new ("(a{sv})", + options_builder), + G_DBUS_CALL_FLAGS_NONE, + DBUS_TIMEOUT, + self->cancellable, + registration_done_cb, + self); +} + +static void +subscription_register_with_username (CcSubscriptionRegisterDialog *self) +{ + g_autoptr(GVariantBuilder) options_builder = NULL; + const gchar *hostname; + const gchar *organization; + const gchar *username; + const gchar *password; + + hostname = gtk_entry_get_text (self->url_entry); + organization = gtk_entry_get_text (self->organization_entry); + username = gtk_entry_get_text (self->login_entry); + password = gtk_entry_get_text (self->password_entry); + + options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (options_builder, "{sv}", "kind", g_variant_new_string ("username")); + g_variant_builder_add (options_builder, "{sv}", "hostname", g_variant_new_string (hostname)); + g_variant_builder_add (options_builder, "{sv}", "organisation", g_variant_new_string (organization)); + g_variant_builder_add (options_builder, "{sv}", "username", g_variant_new_string (username)); + g_variant_builder_add (options_builder, "{sv}", "password", g_variant_new_string (password)); + + g_dbus_proxy_call (self->subscription_proxy, + "Register", + g_variant_new ("(a{sv})", options_builder), + G_DBUS_CALL_FLAGS_NONE, + DBUS_TIMEOUT, + self->cancellable, + registration_done_cb, + self); +} + +static void +register_button_clicked_cb (CcSubscriptionRegisterDialog *self) +{ + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->register_with_activation_keys_radio))) + subscription_register_with_activation_keys (self); + else + subscription_register_with_username (self); + + gtk_spinner_start (self->spinner); + + self->state = DIALOG_STATE_REGISTERING; + dialog_reload (self); +} + +static void +dismiss_notification (CcSubscriptionRegisterDialog *self) +{ + gtk_revealer_set_reveal_child (self->notification_revealer, FALSE); +} + +static void +cc_subscription_register_dialog_init (CcSubscriptionRegisterDialog *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); + + self->cancellable = g_cancellable_new (); + self->state = DIALOG_STATE_REGISTER; + + gtk_entry_set_text (self->url_entry, SERVER_URL); + gtk_widget_grab_focus (GTK_WIDGET (self->login_entry)); + dialog_validate (self); + dialog_reload (self); +} + +static void +cc_subscription_register_dialog_dispose (GObject *obj) +{ + CcSubscriptionRegisterDialog *self = (CcSubscriptionRegisterDialog *) obj; + + g_cancellable_cancel (self->cancellable); + g_clear_object (&self->cancellable); + g_clear_object (&self->subscription_proxy); + + G_OBJECT_CLASS (cc_subscription_register_dialog_parent_class)->dispose (obj); +} + +static void +cc_subscription_register_dialog_finalize (GObject *obj) +{ + G_OBJECT_CLASS (cc_subscription_register_dialog_parent_class)->finalize (obj); +} + +static void +cc_subscription_register_dialog_class_init (CcSubscriptionRegisterDialogClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = cc_subscription_register_dialog_dispose; + object_class->finalize = cc_subscription_register_dialog_finalize; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/info/cc-subscription-register-dialog.ui"); + + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionRegisterDialog, spinner); + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionRegisterDialog, register_button); + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionRegisterDialog, notification_revealer); + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionRegisterDialog, error_label); + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionRegisterDialog, default_url_radio); + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionRegisterDialog, custom_url_radio); + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionRegisterDialog, register_radio); + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionRegisterDialog, register_with_activation_keys_radio); + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionRegisterDialog, stack); + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionRegisterDialog, register_grid); + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionRegisterDialog, register_with_activation_keys_grid); + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionRegisterDialog, url_label); + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionRegisterDialog, url_entry); + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionRegisterDialog, login_entry); + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionRegisterDialog, password_entry); + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionRegisterDialog, activation_keys_entry); + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionRegisterDialog, organization_label); + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionRegisterDialog, organization_entry); + gtk_widget_class_bind_template_child (widget_class, CcSubscriptionRegisterDialog, organization_entry_with_activation_keys); + + gtk_widget_class_bind_template_callback (widget_class, dialog_validate); + gtk_widget_class_bind_template_callback (widget_class, register_button_clicked_cb); + gtk_widget_class_bind_template_callback (widget_class, dismiss_notification); + gtk_widget_class_bind_template_callback (widget_class, custom_url_radio_toggled_cb); + gtk_widget_class_bind_template_callback (widget_class, register_with_activation_keys_radio_toggled_cb); +} + +CcSubscriptionRegisterDialog * +cc_subscription_register_dialog_new (GDBusProxy *subscription_proxy) +{ + CcSubscriptionRegisterDialog *self; + + self = g_object_new (CC_TYPE_SUBSCRIPTION_REGISTER_DIALOG, "use-header-bar", TRUE, NULL); + self->subscription_proxy = g_object_ref (subscription_proxy); + + return self; +} diff --git a/panels/info/cc-subscription-register-dialog.h b/panels/info/cc-subscription-register-dialog.h new file mode 100644 index 000000000..c5918df9f --- /dev/null +++ b/panels/info/cc-subscription-register-dialog.h @@ -0,0 +1,32 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright 2019 Red Hat, Inc, + * + * 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, see . + * + * Written by: Kalev Lember + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define CC_TYPE_SUBSCRIPTION_REGISTER_DIALOG (cc_subscription_register_dialog_get_type ()) +G_DECLARE_FINAL_TYPE (CcSubscriptionRegisterDialog, cc_subscription_register_dialog, CC, SUBSCRIPTION_REGISTER_DIALOG, GtkDialog) + +CcSubscriptionRegisterDialog *cc_subscription_register_dialog_new (GDBusProxy *subscription_proxy); + +G_END_DECLS diff --git a/panels/info/cc-subscription-register-dialog.ui b/panels/info/cc-subscription-register-dialog.ui new file mode 100644 index 000000000..21e317b41 --- /dev/null +++ b/panels/info/cc-subscription-register-dialog.ui @@ -0,0 +1,623 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + horizontal + + + + + + diff --git a/panels/info/info-overview.ui b/panels/info/info-overview.ui index aa87fbec2..e33ba399a 100644 --- a/panels/info/info-overview.ui +++ b/panels/info/info-overview.ui @@ -4,16 +4,17 @@