1143 lines
48 KiB
Diff
1143 lines
48 KiB
Diff
From 015fd59e65295e8507f7df389833a6ba443c463f Mon Sep 17 00:00:00 2001
|
|
From: Felipe Borges <felipeborges@gnome.org>
|
|
Date: Thu, 30 Jan 2025 13:53:29 +0100
|
|
Subject: [PATCH] system: Add subscription manager (Registration) page
|
|
|
|
https://issues.redhat.com/browse/RHEL-26117
|
|
---
|
|
panels/system/cc-system-panel.c | 19 +
|
|
panels/system/cc-system-panel.ui | 13 +
|
|
panels/system/gnome-system-panel.desktop.in | 2 +-
|
|
.../icons/scalable/actions/key-symbolic.svg | 4 +
|
|
panels/system/meson.build | 4 +
|
|
panels/system/subman/cc-subman-page.c | 629 ++++++++++++++++++
|
|
panels/system/subman/cc-subman-page.h | 38 ++
|
|
panels/system/subman/cc-subman-page.ui | 254 +++++++
|
|
.../subman/gnome-subman-panel.desktop.in | 14 +
|
|
panels/system/subman/meson.build | 9 +
|
|
panels/system/system.gresource.xml | 3 +
|
|
11 files changed, 988 insertions(+), 1 deletion(-)
|
|
create mode 100644 panels/system/icons/scalable/actions/key-symbolic.svg
|
|
create mode 100644 panels/system/subman/cc-subman-page.c
|
|
create mode 100644 panels/system/subman/cc-subman-page.h
|
|
create mode 100644 panels/system/subman/cc-subman-page.ui
|
|
create mode 100644 panels/system/subman/gnome-subman-panel.desktop.in
|
|
create mode 100644 panels/system/subman/meson.build
|
|
|
|
diff --git a/panels/system/cc-system-panel.c b/panels/system/cc-system-panel.c
|
|
index f28a96372..e7ff45bdf 100644
|
|
--- a/panels/system/cc-system-panel.c
|
|
+++ b/panels/system/cc-system-panel.c
|
|
@@ -35,6 +35,8 @@
|
|
#include "secure-shell/cc-secure-shell-page.h"
|
|
#include "users/cc-users-page.h"
|
|
|
|
+#include "subman/cc-subman-page.h"
|
|
+
|
|
struct _CcSystemPanel
|
|
{
|
|
CcPanel parent_instance;
|
|
@@ -47,6 +49,8 @@ struct _CcSystemPanel
|
|
|
|
CcSecureShellPage *secure_shell_dialog;
|
|
AdwNavigationPage *software_updates_group;
|
|
+
|
|
+ AdwActionRow *subman_row;
|
|
};
|
|
|
|
CC_PANEL_REGISTER (CcSystemPanel, cc_system_panel)
|
|
@@ -145,6 +149,8 @@ cc_system_panel_class_init (CcSystemPanelClass *klass)
|
|
gtk_widget_class_bind_template_child (widget_class, CcSystemPanel, users_row);
|
|
gtk_widget_class_bind_template_child (widget_class, CcSystemPanel, software_updates_group);
|
|
|
|
+ gtk_widget_class_bind_template_child (widget_class, CcSystemPanel, subman_row);
|
|
+
|
|
gtk_widget_class_bind_template_callback (widget_class, cc_system_page_open_software_update);
|
|
gtk_widget_class_bind_template_callback (widget_class, on_secure_shell_row_clicked);
|
|
|
|
@@ -161,6 +167,7 @@ static void
|
|
cc_system_panel_init (CcSystemPanel *self)
|
|
{
|
|
CcServiceState service_state;
|
|
+ CcSubmanPage *subman_page;
|
|
|
|
g_resources_register (cc_system_get_resource ());
|
|
gtk_widget_init_template (GTK_WIDGET (self));
|
|
@@ -176,4 +183,16 @@ cc_system_panel_init (CcSystemPanel *self)
|
|
cc_panel_add_static_subpage (CC_PANEL (self), "region", CC_TYPE_REGION_PAGE);
|
|
cc_panel_add_static_subpage (CC_PANEL (self), "remote-desktop", CC_TYPE_REMOTE_DESKTOP_PAGE);
|
|
cc_panel_add_static_subpage (CC_PANEL (self), "users", CC_TYPE_USERS_PAGE);
|
|
+
|
|
+ /* Subscription Manager */
|
|
+ subman_page = g_object_new (CC_TYPE_SUBMAN_PAGE, NULL);
|
|
+ if (cc_subman_page_is_available (subman_page))
|
|
+ {
|
|
+ cc_panel_add_subpage (CC_PANEL (self), "subman", ADW_NAVIGATION_PAGE (subman_page));
|
|
+ g_object_bind_property (subman_page,
|
|
+ "is-available",
|
|
+ self->subman_row,
|
|
+ "visible",
|
|
+ G_BINDING_SYNC_CREATE | G_BINDING_DEFAULT);
|
|
+ }
|
|
}
|
|
diff --git a/panels/system/cc-system-panel.ui b/panels/system/cc-system-panel.ui
|
|
index 6de8b22e8..507c6efc3 100644
|
|
--- a/panels/system/cc-system-panel.ui
|
|
+++ b/panels/system/cc-system-panel.ui
|
|
@@ -70,6 +70,19 @@
|
|
</object>
|
|
</child>
|
|
|
|
+ <child>
|
|
+ <object class="CcListRow" id="subman_row">
|
|
+ <property name="visible">False</property>
|
|
+ <property name="title" translatable="yes">Registration</property>
|
|
+ <property name="subtitle" translatable="yes">Enable Red Hat updates, content, and services</property>
|
|
+ <property name="use-markup">True</property>
|
|
+ <property name="icon-name">key-symbolic</property>
|
|
+ <property name="show-arrow">True</property>
|
|
+ <property name="action-name">navigation.push</property>
|
|
+ <property name="action-target">'subman'</property>
|
|
+ </object>
|
|
+ </child>
|
|
+
|
|
<child>
|
|
<object class="CcListRow" id="about_row">
|
|
<property name="title" translatable="yes">_About</property>
|
|
diff --git a/panels/system/gnome-system-panel.desktop.in b/panels/system/gnome-system-panel.desktop.in
|
|
index a437f8b20..45e3d9a50 100644
|
|
--- a/panels/system/gnome-system-panel.desktop.in
|
|
+++ b/panels/system/gnome-system-panel.desktop.in
|
|
@@ -11,4 +11,4 @@ StartupNotify=true
|
|
Categories=GNOME;GTK;Settings;X-GNOME-Settings-Panel;X-GNOME-DetailsSettings;
|
|
OnlyShowIn=GNOME;Unity;
|
|
# Translators: Search terms to find the System panel. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!
|
|
-Keywords=device;system;information;details;hostname;memory;processor;version;software;operating;os;model;language;region;country;formats;numbers;units;clock;timezone;date;location;remote;desktop;rdp;vnc;ssh;
|
|
+Keywords=device;system;information;details;hostname;memory;processor;version;software;operating;os;model;language;region;country;formats;numbers;units;clock;timezone;date;location;remote;desktop;rdp;vnc;ssh;Registration;Subscription;Red Hat;Products;Updates;Registry;Account;Activation;Register;Subscribe;
|
|
diff --git a/panels/system/icons/scalable/actions/key-symbolic.svg b/panels/system/icons/scalable/actions/key-symbolic.svg
|
|
new file mode 100644
|
|
index 000000000..7a5e13619
|
|
--- /dev/null
|
|
+++ b/panels/system/icons/scalable/actions/key-symbolic.svg
|
|
@@ -0,0 +1,4 @@
|
|
+<?xml version="1.0" encoding="UTF-8"?>
|
|
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
|
+ <path d="m 6 1 c -2.761719 0 -5 2.238281 -5 5 s 2.238281 5 5 5 c 0.832031 -0.003906 1.652344 -0.214844 2.382812 -0.617188 l 0.617188 0.617188 v 2 h 2 v 2 h 4 v -3 l -4.308594 -4.308594 c 0.199219 -0.542968 0.304688 -1.113281 0.308594 -1.691406 c 0 -2.761719 -2.238281 -5 -5 -5 z m -1 3 c 0.550781 0 1 0.449219 1 1 s -0.449219 1 -1 1 s -1 -0.449219 -1 -1 s 0.449219 -1 1 -1 z m 0 0" fill="#2e3436"/>
|
|
+</svg>
|
|
diff --git a/panels/system/meson.build b/panels/system/meson.build
|
|
index bbe004837..7ff9646c3 100644
|
|
--- a/panels/system/meson.build
|
|
+++ b/panels/system/meson.build
|
|
@@ -58,6 +58,8 @@ sources = files(
|
|
'users/pw-utils.c',
|
|
'users/run-passwd.c',
|
|
'users/user-utils.c',
|
|
+
|
|
+ 'subman/cc-subman-page.c',
|
|
)
|
|
|
|
sources += gnome.compile_resources(
|
|
@@ -136,6 +138,8 @@ subdir('remote-desktop')
|
|
subdir('secure-shell')
|
|
subdir('users')
|
|
|
|
+subdir('subman')
|
|
+
|
|
panels_libs += static_library(
|
|
cappletname,
|
|
sources: sources,
|
|
diff --git a/panels/system/subman/cc-subman-page.c b/panels/system/subman/cc-subman-page.c
|
|
new file mode 100644
|
|
index 000000000..7b29c3f72
|
|
--- /dev/null
|
|
+++ b/panels/system/subman/cc-subman-page.c
|
|
@@ -0,0 +1,629 @@
|
|
+/*
|
|
+ * cc-subman-page.c
|
|
+ *
|
|
+ * Copyright 2025 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 3 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 <http://www.gnu.org/licenses/>.
|
|
+ *
|
|
+ * SPDX-License-Identifier: GPL-3.0-or-later
|
|
+ *
|
|
+ * Author(s):
|
|
+ * Felipe Borges <felipeborges@gnome.org>
|
|
+ */
|
|
+
|
|
+#undef G_LOG_DOMAIN
|
|
+#define G_LOG_DOMAIN "cc-subman-page"
|
|
+
|
|
+#ifdef HAVE_CONFIG_H
|
|
+# include "config.h"
|
|
+#endif
|
|
+
|
|
+#include "cc-subman-page.h"
|
|
+
|
|
+#include "cc-list-row.h"
|
|
+#include "cc-list-row-info-button.h"
|
|
+
|
|
+#include <locale.h>
|
|
+#include <glib/gi18n.h>
|
|
+#include <gio/gio.h>
|
|
+#include <gtk/gtk.h>
|
|
+
|
|
+#define SERVER_URL "subscription.rhsm.redhat.com"
|
|
+#define DBUS_TIMEOUT 300000 /* 5 minutes */
|
|
+
|
|
+typedef enum {
|
|
+ GSD_SUBMAN_SUBSCRIPTION_STATUS_NOT_READ = -1,
|
|
+ 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_NO_INSTALLED_PRODUCTS,
|
|
+ GSD_SUBMAN_SUBSCRIPTION_STATUS_LAST
|
|
+} GsdSubmanSubscriptionStatus;
|
|
+
|
|
+struct _CcSubmanPage {
|
|
+ AdwNavigationPage parent_instance;
|
|
+
|
|
+ AdwNavigationView *navigation;
|
|
+
|
|
+ GtkStack *registration_page_stack;
|
|
+ GtkWidget *register_button;
|
|
+
|
|
+ GtkCheckButton *username_method;
|
|
+ AdwEntryRow *server_row;
|
|
+
|
|
+ AdwEntryRow *username_row;
|
|
+ AdwPasswordEntryRow *password_row;
|
|
+ AdwEntryRow *activation_keys_row;
|
|
+ AdwEntryRow *organization_row;
|
|
+ CcListRowInfoButton *organization_info_text;
|
|
+
|
|
+ AdwPreferencesGroup *products_group;
|
|
+
|
|
+ /* Backend */
|
|
+ GDBusProxy *proxy;
|
|
+ gboolean is_registered;
|
|
+ gboolean is_available;
|
|
+
|
|
+ GPtrArray *products;
|
|
+
|
|
+ GCancellable *cancellable;
|
|
+};
|
|
+
|
|
+enum {
|
|
+ PROP_0,
|
|
+ PROP_IS_AVAILABLE,
|
|
+ PROP_IS_REGISTERED,
|
|
+ N_PROPERTIES
|
|
+};
|
|
+
|
|
+static GParamSpec *properties[N_PROPERTIES] = { NULL };
|
|
+
|
|
+G_DEFINE_TYPE (CcSubmanPage, cc_subman_page, ADW_TYPE_NAVIGATION_PAGE)
|
|
+
|
|
+typedef struct
|
|
+{
|
|
+ gchar *product_name;
|
|
+ gchar *product_id;
|
|
+ gchar *version;
|
|
+ gchar *arch;
|
|
+ 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->starts);
|
|
+ g_free (product->ends);
|
|
+ g_free (product);
|
|
+}
|
|
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (ProductData, product_data_free);
|
|
+
|
|
+static void get_subscription_status (CcSubmanPage *self);
|
|
+
|
|
+static void
|
|
+show_error (CcSubmanPage *self,
|
|
+ const gchar *message,
|
|
+ const gchar *description)
|
|
+{
|
|
+ AdwDialog *dialog = adw_alert_dialog_new (message, description);
|
|
+
|
|
+ adw_alert_dialog_add_responses (ADW_ALERT_DIALOG (dialog),
|
|
+ "close", _("Close"), NULL);
|
|
+
|
|
+ adw_alert_dialog_set_default_response (ADW_ALERT_DIALOG (dialog), "close");
|
|
+ adw_alert_dialog_set_close_response (ADW_ALERT_DIALOG (dialog), "close");
|
|
+
|
|
+ g_signal_connect (dialog, "response", G_CALLBACK (adw_dialog_close), NULL);
|
|
+
|
|
+ adw_dialog_present (dialog, GTK_WIDGET (self));
|
|
+}
|
|
+
|
|
+static void
|
|
+unregistration_done_cb (GObject *source_object,
|
|
+ GAsyncResult *result,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ CcSubmanPage *self = CC_SUBMAN_PAGE (user_data);
|
|
+ g_autoptr(GVariant) variant_results = NULL;
|
|
+ g_autoptr(GError) error = NULL;
|
|
+
|
|
+ gtk_widget_set_sensitive (GTK_WIDGET (self), TRUE);
|
|
+
|
|
+ variant_results = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), result, &error);
|
|
+ if (variant_results == NULL) {
|
|
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
|
+ return;
|
|
+
|
|
+ g_dbus_error_strip_remote_error (error);
|
|
+ show_error (self, _("Failed to Unregister System"), error->message);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ g_object_set (self, "is-registered", FALSE, NULL);
|
|
+ g_debug ("System is no longer registered");
|
|
+}
|
|
+
|
|
+static void
|
|
+on_unregister_response_cb (AdwAlertDialog *dialog,
|
|
+ GAsyncResult *result,
|
|
+ CcSubmanPage *self)
|
|
+{
|
|
+ const gchar *response = adw_alert_dialog_choose_finish (dialog, result);
|
|
+
|
|
+ if (g_strcmp0 (response, "unregister") == 0) {
|
|
+ g_dbus_proxy_call (self->proxy,
|
|
+ "Unregister",
|
|
+ NULL,
|
|
+ G_DBUS_CALL_FLAGS_NONE,
|
|
+ DBUS_TIMEOUT,
|
|
+ self->cancellable,
|
|
+ unregistration_done_cb,
|
|
+ self);
|
|
+ } else {
|
|
+ gtk_widget_set_sensitive (GTK_WIDGET (self), TRUE);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+unregister (CcSubmanPage *self)
|
|
+{
|
|
+ AdwDialog *dialog = adw_alert_dialog_new (_("Remove Registration?"),
|
|
+ _("Subscriptions will be removed and the device will no longer receive software updates"));
|
|
+ adw_alert_dialog_add_responses (ADW_ALERT_DIALOG (dialog),
|
|
+ "cancel", _("Cancel"),
|
|
+ "unregister", _("Remove Registration"), NULL);
|
|
+ adw_alert_dialog_set_response_appearance (ADW_ALERT_DIALOG (dialog),
|
|
+ "unregister",
|
|
+ ADW_RESPONSE_DESTRUCTIVE);
|
|
+ adw_alert_dialog_set_default_response (ADW_ALERT_DIALOG (dialog), "cancel");
|
|
+ adw_alert_dialog_set_close_response (ADW_ALERT_DIALOG (dialog), "cancel");
|
|
+
|
|
+ gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
|
|
+
|
|
+ adw_alert_dialog_choose (ADW_ALERT_DIALOG (dialog),
|
|
+ GTK_WIDGET (self),
|
|
+ self->cancellable,
|
|
+ (GAsyncReadyCallback) on_unregister_response_cb,
|
|
+ self);
|
|
+}
|
|
+
|
|
+static void
|
|
+add_product_row (GtkWidget *expander,
|
|
+ const gchar *title,
|
|
+ const gchar *value)
|
|
+{
|
|
+ CcListRow *row = g_object_new (CC_TYPE_LIST_ROW, NULL);
|
|
+
|
|
+ adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row), title);
|
|
+ cc_list_row_set_secondary_label (row, value);
|
|
+
|
|
+ adw_expander_row_add_row (ADW_EXPANDER_ROW (expander), GTK_WIDGET (row));
|
|
+}
|
|
+
|
|
+static void
|
|
+add_product (CcSubmanPage *self,
|
|
+ ProductData *product)
|
|
+{
|
|
+ GtkWidget *product_row;
|
|
+
|
|
+ product_row = adw_expander_row_new ();
|
|
+ adw_preferences_row_set_title (ADW_PREFERENCES_ROW (product_row), product->product_name);
|
|
+
|
|
+ add_product_row (product_row, _("Product ID"), product->product_id);
|
|
+ add_product_row (product_row, _("Version"), product->version);
|
|
+ add_product_row (product_row, _("Arch"), product->arch);
|
|
+
|
|
+ if (product->starts[0] != '\0' && product->ends[0] != '\0') {
|
|
+ add_product_row (product_row, _("Starts"), product->starts);
|
|
+ add_product_row (product_row, _("Ends"), product->ends);
|
|
+ }
|
|
+
|
|
+ adw_preferences_group_add (self->products_group, product_row);
|
|
+}
|
|
+
|
|
+static ProductData *
|
|
+parse_product_variant (GVariant *product_variant)
|
|
+{
|
|
+ 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, "starts", "s", &product->starts);
|
|
+ g_variant_dict_lookup (&dict, "ends", "s", &product->ends);
|
|
+
|
|
+ return g_steal_pointer (&product);
|
|
+}
|
|
+
|
|
+static void
|
|
+fetch_installed_products (CcSubmanPage *self)
|
|
+{
|
|
+ g_autoptr(GVariant) installed_products_variant = NULL;
|
|
+ GVariantIter iter_array;
|
|
+ GVariant *child;
|
|
+
|
|
+ installed_products_variant = g_dbus_proxy_get_cached_property (self->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));
|
|
+ }
|
|
+
|
|
+ if (self->products == NULL || self->products->len == 0) {
|
|
+ // Show a no-products page?
|
|
+ }
|
|
+
|
|
+ for (guint i = 0; i < self->products->len; i++) {
|
|
+ ProductData *product = g_ptr_array_index (self->products, i);
|
|
+
|
|
+ add_product (self, product);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+registration_done_cb (GObject *source_object,
|
|
+ GAsyncResult *result,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ CcSubmanPage *self = CC_SUBMAN_PAGE (user_data);
|
|
+ g_autoptr(GVariant) results = NULL;
|
|
+ g_autoptr(GError) error = NULL;
|
|
+
|
|
+ gtk_widget_set_sensitive (GTK_WIDGET (self), TRUE);
|
|
+
|
|
+ results = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), result, &error);
|
|
+ if (results == NULL) {
|
|
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
|
+ return;
|
|
+
|
|
+ g_dbus_error_strip_remote_error (error);
|
|
+
|
|
+ show_error (self, _("Failed to Register System"), error->message);
|
|
+
|
|
+ g_warning ("Failed to register: %s", error->message);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ g_debug ("Registration successful");
|
|
+ get_subscription_status (self);
|
|
+}
|
|
+
|
|
+static void
|
|
+update_register_button (CcSubmanPage *self)
|
|
+{
|
|
+ gboolean is_registering = !gtk_widget_get_sensitive (GTK_WIDGET (self));
|
|
+
|
|
+ gtk_widget_set_visible (self->register_button, !is_registering);
|
|
+}
|
|
+
|
|
+static void
|
|
+register_subscription (CcSubmanPage *self)
|
|
+{
|
|
+ gboolean register_with_username = gtk_check_button_get_active (self->username_method);
|
|
+ g_autoptr(GVariantBuilder) options_builder = NULL;
|
|
+ const gchar *hostname;
|
|
+ const gchar *organization;
|
|
+ const gchar *username;
|
|
+ const gchar *password;
|
|
+ const gchar *activation_keys;
|
|
+
|
|
+ gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
|
|
+
|
|
+ hostname = gtk_editable_get_text (GTK_EDITABLE (self->server_row));
|
|
+ organization = gtk_editable_get_text (GTK_EDITABLE (self->organization_row));
|
|
+
|
|
+ if (register_with_username) {
|
|
+ username = gtk_editable_get_text (GTK_EDITABLE (self->username_row));
|
|
+ password = gtk_editable_get_text (GTK_EDITABLE (self->password_row));
|
|
+ } else {
|
|
+ activation_keys = gtk_editable_get_text (GTK_EDITABLE (self->activation_keys_row));
|
|
+ }
|
|
+
|
|
+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
|
+ 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));
|
|
+ if (register_with_username) {
|
|
+ g_variant_builder_add (options_builder, "{sv}", "kind", g_variant_new_string ("username"));
|
|
+ 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));
|
|
+ } else {
|
|
+ g_variant_builder_add (options_builder, "{sv}", "kind", g_variant_new_string ("key"));
|
|
+ g_variant_builder_add (options_builder, "{sv}", "activation-key", g_variant_new_string (activation_keys));
|
|
+ }
|
|
+
|
|
+ g_dbus_proxy_call (self->proxy,
|
|
+ "Register",
|
|
+ g_variant_new ("(a{sv})", options_builder),
|
|
+ G_DBUS_CALL_FLAGS_NONE,
|
|
+ DBUS_TIMEOUT,
|
|
+ self->cancellable,
|
|
+ registration_done_cb,
|
|
+ self);
|
|
+}
|
|
+
|
|
+static void
|
|
+update_for_status (CcSubmanPage *self,
|
|
+ GsdSubmanSubscriptionStatus status)
|
|
+{
|
|
+ switch (status) {
|
|
+ case GSD_SUBMAN_SUBSCRIPTION_STATUS_NOT_READ:
|
|
+ g_object_set (G_OBJECT (self), "is-registered", FALSE, NULL);
|
|
+ break;// Why return?;
|
|
+ case GSD_SUBMAN_SUBSCRIPTION_STATUS_UNKNOWN:
|
|
+ g_object_set (G_OBJECT (self), "is-registered", FALSE, NULL);
|
|
+ break;
|
|
+ case GSD_SUBMAN_SUBSCRIPTION_STATUS_DISABLED:
|
|
+ g_object_set (G_OBJECT (self), "is-registered", TRUE, NULL);
|
|
+ break;
|
|
+ case GSD_SUBMAN_SUBSCRIPTION_STATUS_VALID:
|
|
+ case GSD_SUBMAN_SUBSCRIPTION_STATUS_PARTIALLY_VALID:
|
|
+ g_object_set (G_OBJECT (self), "is-registered", TRUE, NULL);
|
|
+ break;
|
|
+ case GSD_SUBMAN_SUBSCRIPTION_STATUS_INVALID:
|
|
+ g_object_set (G_OBJECT (self), "is-registered", TRUE, NULL);
|
|
+ break;
|
|
+ case GSD_SUBMAN_SUBSCRIPTION_STATUS_NO_INSTALLED_PRODUCTS:
|
|
+ g_object_set (G_OBJECT (self), "is-registered", FALSE, NULL);
|
|
+ break;
|
|
+ default:
|
|
+ g_assert_not_reached ();
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+get_subscription_status (CcSubmanPage *self)
|
|
+{
|
|
+ g_autoptr(GVariant) status_variant = NULL;
|
|
+ g_autoptr(GError) error = NULL;
|
|
+ guint32 u;
|
|
+
|
|
+ if (self->proxy == NULL) {
|
|
+ self->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 proxy for org.gnome.SettingsDaemon.Subscription: %s", error->message);
|
|
+
|
|
+ self->is_available = FALSE;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ g_signal_connect_swapped (self->proxy, "g-properties-changed",
|
|
+ G_CALLBACK (get_subscription_status), self);
|
|
+ }
|
|
+
|
|
+ status_variant = g_dbus_proxy_get_cached_property (self->proxy, "SubscriptionStatus");
|
|
+ if (!status_variant) {
|
|
+ g_debug ("Unable to get SubscriptionStatus property");
|
|
+
|
|
+ self->is_available = FALSE;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ g_debug ("Subscription manager is available");
|
|
+ self->is_available = TRUE;
|
|
+
|
|
+ g_variant_get (status_variant, "u", &u);
|
|
+ update_for_status (self, u);
|
|
+}
|
|
+
|
|
+static void
|
|
+validate_registration_details (CcSubmanPage *self)
|
|
+{
|
|
+ gboolean username_auth = gtk_check_button_get_active (self->username_method);
|
|
+ const gchar *server_url = gtk_editable_get_text (GTK_EDITABLE (self->server_row));
|
|
+ gboolean can_register = FALSE;
|
|
+
|
|
+ can_register = (g_strcmp0 (server_url, "") != 0);
|
|
+ if (username_auth) {
|
|
+ const gchar *username = gtk_editable_get_text (GTK_EDITABLE (self->username_row));
|
|
+ const gchar *password = gtk_editable_get_text (GTK_EDITABLE (self->password_row));
|
|
+
|
|
+ can_register = can_register && (g_strcmp0 (username, "") != 0) && (g_strcmp0 (password, "") != 0);
|
|
+ } else {
|
|
+ const gchar *activation_keys = gtk_editable_get_text (GTK_EDITABLE (self->activation_keys_row));
|
|
+ const gchar *organization = gtk_editable_get_text (GTK_EDITABLE (self->organization_row));
|
|
+
|
|
+ can_register = can_register && (g_strcmp0 (activation_keys, "") != 0) && (g_strcmp0 (organization, "") != 0);
|
|
+ }
|
|
+
|
|
+ gtk_widget_set_sensitive (self->register_button, can_register);
|
|
+}
|
|
+
|
|
+static void
|
|
+on_is_registered_changed (CcSubmanPage *self)
|
|
+{
|
|
+ adw_navigation_view_replace (self->navigation, NULL, 0);
|
|
+ adw_navigation_view_push_by_tag (self->navigation,
|
|
+ self->is_registered ? "products-page" : "registration-page");
|
|
+}
|
|
+
|
|
+static void
|
|
+update_organization_info_text (CcSubmanPage *self)
|
|
+{
|
|
+ gboolean username_auth = gtk_check_button_get_active (self->username_method);
|
|
+
|
|
+ cc_list_row_info_button_set_text (self->organization_info_text,
|
|
+ username_auth ? _("If the user account belongs to multiple organizations, an organization must be specified.") :
|
|
+ _("Organization must be specified when registering with an activation key."));
|
|
+}
|
|
+
|
|
+static void
|
|
+reset_server_row_button_clicked_cb (CcSubmanPage *self)
|
|
+{
|
|
+ gtk_editable_set_text (GTK_EDITABLE (self->server_row), SERVER_URL);
|
|
+}
|
|
+
|
|
+static void
|
|
+on_register_system_button_clicked_cb (CcSubmanPage *self)
|
|
+{
|
|
+ gtk_stack_set_visible_child_name (self->registration_page_stack, "registration");
|
|
+}
|
|
+
|
|
+static void
|
|
+cc_subman_page_set_property (GObject *object,
|
|
+ guint prop_id,
|
|
+ const GValue *value,
|
|
+ GParamSpec *pspec)
|
|
+{
|
|
+ CcSubmanPage *self = CC_SUBMAN_PAGE (object);
|
|
+
|
|
+ switch (prop_id) {
|
|
+ case PROP_IS_REGISTERED:
|
|
+ self->is_registered = g_value_get_boolean (value);
|
|
+ break;
|
|
+ default:
|
|
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+cc_subman_page_get_property (GObject *object,
|
|
+ guint prop_id,
|
|
+ GValue *value,
|
|
+ GParamSpec *pspec)
|
|
+{
|
|
+ CcSubmanPage *self = CC_SUBMAN_PAGE (object);
|
|
+
|
|
+ switch (prop_id) {
|
|
+ case PROP_IS_AVAILABLE:
|
|
+ g_value_set_boolean (value, self->is_available);
|
|
+ break;
|
|
+ case PROP_IS_REGISTERED:
|
|
+ g_value_set_boolean (value, self->is_registered);
|
|
+ break;
|
|
+ default:
|
|
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+cc_subman_page_dispose (GObject *object)
|
|
+{
|
|
+ CcSubmanPage *self = CC_SUBMAN_PAGE (object);
|
|
+
|
|
+ g_cancellable_cancel (self->cancellable);
|
|
+ g_clear_object (&self->cancellable);
|
|
+
|
|
+ g_clear_object (&self->proxy);
|
|
+
|
|
+ G_OBJECT_CLASS (cc_subman_page_parent_class)->dispose (object);
|
|
+}
|
|
+
|
|
+static void
|
|
+cc_subman_page_init (CcSubmanPage *self)
|
|
+{
|
|
+ gtk_widget_init_template (GTK_WIDGET (self));
|
|
+
|
|
+ gtk_editable_set_text (GTK_EDITABLE (self->server_row), SERVER_URL);
|
|
+ update_organization_info_text (self);
|
|
+ self->products = g_ptr_array_new_with_free_func ((GDestroyNotify) product_data_free);
|
|
+
|
|
+ g_signal_connect_swapped (G_OBJECT (self),
|
|
+ "notify::is-registered",
|
|
+ G_CALLBACK (on_is_registered_changed),
|
|
+ self);
|
|
+
|
|
+ self->cancellable = g_cancellable_new ();
|
|
+}
|
|
+
|
|
+static void
|
|
+cc_subman_page_class_init (CcSubmanPageClass * klass)
|
|
+{
|
|
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
+
|
|
+ object_class->set_property = cc_subman_page_set_property;
|
|
+ object_class->get_property = cc_subman_page_get_property;
|
|
+ object_class->finalize = cc_subman_page_dispose;
|
|
+
|
|
+ properties[PROP_IS_AVAILABLE] =
|
|
+ g_param_spec_boolean ("is-available",
|
|
+ "Subscription Manager is available",
|
|
+ "Whether Subscription Manager service is available in the system.",
|
|
+ FALSE,
|
|
+ G_PARAM_READABLE);
|
|
+ properties[PROP_IS_REGISTERED] =
|
|
+ g_param_spec_boolean ("is-registered",
|
|
+ "System is registered",
|
|
+ "The system is registered with subscription manager",
|
|
+ FALSE,
|
|
+ G_PARAM_READWRITE);
|
|
+
|
|
+ g_object_class_install_properties (object_class, N_PROPERTIES, properties);
|
|
+
|
|
+ g_type_ensure (CC_TYPE_LIST_ROW);
|
|
+ g_type_ensure (CC_TYPE_LIST_ROW_INFO_BUTTON);
|
|
+
|
|
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/system/subman/cc-subman-page.ui");
|
|
+
|
|
+ gtk_widget_class_bind_template_child (widget_class, CcSubmanPage, navigation);
|
|
+
|
|
+ gtk_widget_class_bind_template_child (widget_class, CcSubmanPage, username_method);
|
|
+
|
|
+ gtk_widget_class_bind_template_child (widget_class, CcSubmanPage, server_row);
|
|
+ gtk_widget_class_bind_template_child (widget_class, CcSubmanPage, organization_row);
|
|
+ gtk_widget_class_bind_template_child (widget_class, CcSubmanPage, organization_info_text);
|
|
+
|
|
+ gtk_widget_class_bind_template_child (widget_class, CcSubmanPage, username_row);
|
|
+ gtk_widget_class_bind_template_child (widget_class, CcSubmanPage, password_row);
|
|
+ gtk_widget_class_bind_template_child (widget_class, CcSubmanPage, activation_keys_row);
|
|
+
|
|
+ gtk_widget_class_bind_template_child (widget_class, CcSubmanPage, register_button);
|
|
+
|
|
+ gtk_widget_class_bind_template_child (widget_class, CcSubmanPage, registration_page_stack);
|
|
+ gtk_widget_class_bind_template_child (widget_class, CcSubmanPage, products_group);
|
|
+
|
|
+ gtk_widget_class_bind_template_callback (widget_class, validate_registration_details);
|
|
+ gtk_widget_class_bind_template_callback (widget_class, register_subscription);
|
|
+ gtk_widget_class_bind_template_callback (widget_class, unregister);
|
|
+ gtk_widget_class_bind_template_callback (widget_class, reset_server_row_button_clicked_cb);
|
|
+ gtk_widget_class_bind_template_callback (widget_class, update_organization_info_text);
|
|
+ gtk_widget_class_bind_template_callback (widget_class, update_register_button);
|
|
+ gtk_widget_class_bind_template_callback (widget_class, on_register_system_button_clicked_cb);
|
|
+}
|
|
+
|
|
+gboolean
|
|
+cc_subman_page_is_available (CcSubmanPage *self)
|
|
+{
|
|
+ get_subscription_status (self);
|
|
+
|
|
+ fetch_installed_products (self);
|
|
+
|
|
+ return self->is_available;
|
|
+}
|
|
diff --git a/panels/system/subman/cc-subman-page.h b/panels/system/subman/cc-subman-page.h
|
|
new file mode 100644
|
|
index 000000000..875b1e57a
|
|
--- /dev/null
|
|
+++ b/panels/system/subman/cc-subman-page.h
|
|
@@ -0,0 +1,38 @@
|
|
+/*
|
|
+ * cc-subman-page.h
|
|
+ *
|
|
+ * Copyright 2025 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 3 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 <http://www.gnu.org/licenses/>.
|
|
+ *
|
|
+ * SPDX-License-Identifier: GPL-3.0-or-later
|
|
+ *
|
|
+ * Author(s):
|
|
+ * Felipe Borges <felipeborges@gnome.org>
|
|
+ */
|
|
+
|
|
+#pragma once
|
|
+
|
|
+#include <adwaita.h>
|
|
+
|
|
+G_BEGIN_DECLS
|
|
+
|
|
+#define CC_TYPE_SUBMAN_PAGE (cc_subman_page_get_type ())
|
|
+
|
|
+G_DECLARE_FINAL_TYPE (CcSubmanPage, cc_subman_page, CC, SUBMAN_PAGE, AdwNavigationPage)
|
|
+
|
|
+gboolean cc_subman_page_is_available (CcSubmanPage *self);
|
|
+
|
|
+G_END_DECLS
|
|
+
|
|
diff --git a/panels/system/subman/cc-subman-page.ui b/panels/system/subman/cc-subman-page.ui
|
|
new file mode 100644
|
|
index 000000000..025f23172
|
|
--- /dev/null
|
|
+++ b/panels/system/subman/cc-subman-page.ui
|
|
@@ -0,0 +1,254 @@
|
|
+<?xml version="1.0" encoding="UTF-8"?>
|
|
+<interface>
|
|
+ <template class="CcSubmanPage" parent="AdwNavigationPage">
|
|
+ <property name="tag">subman</property>
|
|
+ <signal name="notify::sensitive" handler="update_register_button" swapped="yes"/>
|
|
+ <property name="child">
|
|
+ <object class="AdwNavigationView" id="navigation">
|
|
+ <child>
|
|
+ <object class="AdwNavigationPage">
|
|
+ <property name="tag">loading-page</property>
|
|
+ <property name="child">
|
|
+ <object class="AdwToolbarView">
|
|
+ <child type="top">
|
|
+ <object class="AdwHeaderBar"/>
|
|
+ </child>
|
|
+ <property name="content">
|
|
+ <object class="AdwSpinner"/>
|
|
+ </property>
|
|
+ </object>
|
|
+ </property>
|
|
+ </object>
|
|
+ </child>
|
|
+ <child>
|
|
+ <object class="AdwNavigationPage">
|
|
+ <property name="tag">registration-page</property>
|
|
+ <property name="title" translatable="yes">Register System</property>
|
|
+ <property name="child">
|
|
+ <object class="AdwToolbarView">
|
|
+ <child type="top">
|
|
+ <object class="AdwHeaderBar"/>
|
|
+ </child>
|
|
+ <property name="content">
|
|
+ <object class="GtkStack" id="registration_page_stack">
|
|
+ <child>
|
|
+ <object class="GtkStackPage">
|
|
+ <property name="name">status-page</property>
|
|
+ <property name="child">
|
|
+ <object class="AdwStatusPage">
|
|
+ <property name="icon-name">key-symbolic</property>
|
|
+ <property name="title" translatable="yes">System Not Registered</property>
|
|
+ <property name="description" translatable="yes">Register to receive software updates, content, and services from Red Hat. No cost registrations are available as part of the Red Hat Developer program.</property>
|
|
+ <child>
|
|
+ <object class="GtkButton">
|
|
+ <property name="halign">center</property>
|
|
+ <property name="label" translatable="yes">Register System…</property>
|
|
+ <signal name="clicked" handler="on_register_system_button_clicked_cb" swapped="yes"/>
|
|
+ </object>
|
|
+ </child>
|
|
+ </object>
|
|
+ </property>
|
|
+ </object>
|
|
+ </child>
|
|
+
|
|
+ <child>
|
|
+ <object class="GtkStackPage">
|
|
+ <property name="name">registration</property>
|
|
+ <property name="child">
|
|
+ <object class="AdwPreferencesPage">
|
|
+
|
|
+ <child>
|
|
+ <object class="AdwPreferencesGroup">
|
|
+ <property name="title" translatable="yes">Registration Method</property>
|
|
+ <child>
|
|
+ <object class="AdwActionRow">
|
|
+ <property name="title" translatable="yes">Username and password</property>
|
|
+ <property name="activatable-widget">username_method</property>
|
|
+ <child type="prefix">
|
|
+ <object class="GtkCheckButton" id="username_method">
|
|
+ <property name="active">True</property>
|
|
+ <signal name="notify::active" handler="update_organization_info_text" swapped="yes"/>
|
|
+ <signal name="notify::active" handler="validate_registration_details" swapped="yes"/>
|
|
+ </object>
|
|
+ </child>
|
|
+ </object>
|
|
+ </child>
|
|
+ <child>
|
|
+ <object class="AdwActionRow">
|
|
+ <property name="title" translatable="yes">Activation Key(s)</property>
|
|
+ <property name="activatable-widget">activation_keys_method</property>
|
|
+ <child type="prefix">
|
|
+ <object class="GtkCheckButton" id="activation_keys_method">
|
|
+ <property name="group">username_method</property>
|
|
+ <signal name="notify::active" handler="update_organization_info_text" swapped="yes"/>
|
|
+ <signal name="notify::active" handler="validate_registration_details" swapped="yes"/>
|
|
+ </object>
|
|
+ </child>
|
|
+ </object>
|
|
+ </child>
|
|
+ </object>
|
|
+ </child>
|
|
+
|
|
+ <child>
|
|
+ <object class="AdwPreferencesGroup">
|
|
+ <property name="title" translatable="yes">Registration Details</property>
|
|
+ <child>
|
|
+ <object class="AdwEntryRow" id="server_row">
|
|
+ <property name="title" translatable="yes">Registration Server</property>
|
|
+ <signal name="notify::text" handler="validate_registration_details" swapped="yes"/>
|
|
+ <child type="suffix">
|
|
+ <object class="GtkButton">
|
|
+ <property name="icon-name">view-refresh-symbolic</property>
|
|
+ <property name="valign">center</property>
|
|
+ <signal name="clicked" handler="reset_server_row_button_clicked_cb" swapped="yes"/>
|
|
+ <style>
|
|
+ <class name="flat"/>
|
|
+ </style>
|
|
+ </object>
|
|
+ </child>
|
|
+ </object>
|
|
+ </child>
|
|
+ <child>
|
|
+ <object class="AdwEntryRow" id="organization_row">
|
|
+ <property name="title" translatable="yes">Organization</property>
|
|
+ <signal name="notify::text" handler="validate_registration_details" swapped="yes"/>
|
|
+ <child type="suffix">
|
|
+ <object class="CcListRowInfoButton" id="organization_info_text">
|
|
+ <property name="valign">center</property>
|
|
+ </object>
|
|
+ </child>
|
|
+ </object>
|
|
+ </child>
|
|
+ </object>
|
|
+ </child>
|
|
+
|
|
+ <child>
|
|
+ <object class="AdwPreferencesGroup">
|
|
+ <child>
|
|
+ <object class="AdwEntryRow" id="username_row">
|
|
+ <property name="title" translatable="yes">Username</property>
|
|
+ <property name="visible" bind-source="username_method" bind-property="active" bind-flags="default|sync-create"/>
|
|
+ <signal name="notify::text" handler="validate_registration_details" swapped="yes"/>
|
|
+ </object>
|
|
+ </child>
|
|
+ <child>
|
|
+ <object class="AdwPasswordEntryRow" id="password_row">
|
|
+ <property name="title" translatable="yes">Password</property>
|
|
+ <property name="visible" bind-source="username_method" bind-property="active" bind-flags="default|sync-create"/>
|
|
+ <signal name="notify::text" handler="validate_registration_details" swapped="yes"/>
|
|
+ </object>
|
|
+ </child>
|
|
+ <child>
|
|
+ <object class="AdwEntryRow" id="activation_keys_row">
|
|
+ <property name="title" translatable="yes">Activation Key(s)</property>
|
|
+ <property name="visible" bind-source="activation_keys_method" bind-property="active" bind-flags="default|sync-create"/>
|
|
+ <signal name="notify::text" handler="validate_registration_details" swapped="yes"/>
|
|
+ <child type="suffix">
|
|
+ <object class="CcListRowInfoButton">
|
|
+ <property name="text" translatable="yes">To provide multiple activation keys, separate them with commas. If keys conflict, the rightmost key takes precedence.</property>
|
|
+ <property name="valign">center</property>
|
|
+ </object>
|
|
+ </child>
|
|
+ </object>
|
|
+ </child>
|
|
+ </object>
|
|
+ </child>
|
|
+
|
|
+ <child>
|
|
+ <object class="AdwPreferencesGroup">
|
|
+ <child>
|
|
+ <object class="AdwButtonRow" id="register_button">
|
|
+ <property name="sensitive">False</property>
|
|
+ <property name="title" translatable="yes">Register</property>
|
|
+ <signal name="activated" handler="register_subscription" swapped="yes"/>
|
|
+ <style>
|
|
+ <class name="suggested-action"/>
|
|
+ </style>
|
|
+ </object>
|
|
+ </child>
|
|
+ <child>
|
|
+ <object class="AdwButtonRow" id="registering_button">
|
|
+ <property name="visible" bind-source="register_button" bind-property="visible" bind-flags="sync-create|invert-boolean"/>
|
|
+ <property name="sensitive">False</property>
|
|
+ <signal name="activated" handler="register_subscription" swapped="yes"/>
|
|
+ <child>
|
|
+ <object class="GtkBox">
|
|
+ <property name="halign">center</property>
|
|
+ <child>
|
|
+ <object class="AdwSpinner"/>
|
|
+ </child>
|
|
+ <child>
|
|
+ <object class="GtkLabel">
|
|
+ <property name="label" translatable="yes">Registering</property>
|
|
+ <style>
|
|
+ <class name="title"/>
|
|
+ </style>
|
|
+ </object>
|
|
+ </child>
|
|
+ </object>
|
|
+ </child>
|
|
+ </object>
|
|
+ </child>
|
|
+ </object>
|
|
+ </child>
|
|
+
|
|
+ </object>
|
|
+ </property>
|
|
+ </object>
|
|
+ </child>
|
|
+ </object>
|
|
+ </property>
|
|
+ </object>
|
|
+ </property>
|
|
+ </object>
|
|
+ </child>
|
|
+
|
|
+ <child>
|
|
+ <object class="AdwNavigationPage">
|
|
+ <property name="tag">products-page</property>
|
|
+ <property name="title" translatable="yes">Registration</property>
|
|
+ <property name="child">
|
|
+ <object class="AdwToolbarView">
|
|
+ <child type="top">
|
|
+ <object class="AdwHeaderBar"/>
|
|
+ </child>
|
|
+ <property name="content">
|
|
+ <object class="AdwPreferencesPage">
|
|
+ <child>
|
|
+ <object class="AdwPreferencesGroup">
|
|
+ <child>
|
|
+ <object class="AdwActionRow" id="registration_status_row">
|
|
+ <property name="title" translatable="yes">Registration Status</property>
|
|
+ <property name="subtitle" translatable="yes">Active</property>
|
|
+ </object>
|
|
+ </child>
|
|
+ </object>
|
|
+ </child>
|
|
+ <child>
|
|
+ <object class="AdwPreferencesGroup">
|
|
+ <child>
|
|
+ <object class="AdwButtonRow">
|
|
+ <property name="title" translatable="yes">Remove Registration…</property>
|
|
+ <signal name="activated" handler="unregister" swapped="yes"/>
|
|
+ <style>
|
|
+ <class name="destructive-action"/>
|
|
+ </style>
|
|
+ </object>
|
|
+ </child>
|
|
+ </object>
|
|
+ </child>
|
|
+ <child>
|
|
+ <object class="AdwPreferencesGroup" id="products_group">
|
|
+ <property name="title" translatable="yes">Products</property>
|
|
+ </object>
|
|
+ </child>
|
|
+ </object>
|
|
+ </property>
|
|
+ </object>
|
|
+ </property>
|
|
+ </object>
|
|
+ </child>
|
|
+ </object>
|
|
+ </property>
|
|
+ </template>
|
|
+</interface>
|
|
diff --git a/panels/system/subman/gnome-subman-panel.desktop.in b/panels/system/subman/gnome-subman-panel.desktop.in
|
|
new file mode 100644
|
|
index 000000000..ca67b33af
|
|
--- /dev/null
|
|
+++ b/panels/system/subman/gnome-subman-panel.desktop.in
|
|
@@ -0,0 +1,14 @@
|
|
+[Desktop Entry]
|
|
+Name=Registration
|
|
+Comment=Enable Red Hat updates, content, and services
|
|
+Exec=gnome-control-center system subman
|
|
+# Translators: Do NOT translate or transliterate this text (this is an icon file name)!
|
|
+Icon=key-symbolic
|
|
+Terminal=false
|
|
+Type=Application
|
|
+NoDisplay=true
|
|
+StartupNotify=true
|
|
+Categories=GNOME;GTK;Settings;X-GNOME-Settings-Panel;X-GNOME-SystemSettings;
|
|
+OnlyShowIn=GNOME;Unity;
|
|
+# Translators: Search terms to find the Users panel. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!
|
|
+Keywords=Registration;Subscription;Red Hat;Products;Updates;Registry;Account;Activation;Register;Subscribe;
|
|
diff --git a/panels/system/subman/meson.build b/panels/system/subman/meson.build
|
|
new file mode 100644
|
|
index 000000000..58a99ee49
|
|
--- /dev/null
|
|
+++ b/panels/system/subman/meson.build
|
|
@@ -0,0 +1,9 @@
|
|
+desktop = 'gnome-subman-panel.desktop'
|
|
+i18n.merge_file(
|
|
+ type: 'desktop',
|
|
+ input: desktop + '.in',
|
|
+ output: desktop,
|
|
+ po_dir: po_dir,
|
|
+ install: true,
|
|
+ install_dir: control_center_desktopdir
|
|
+)
|
|
diff --git a/panels/system/system.gresource.xml b/panels/system/system.gresource.xml
|
|
index acb4169b1..3d5c0684e 100644
|
|
--- a/panels/system/system.gresource.xml
|
|
+++ b/panels/system/system.gresource.xml
|
|
@@ -29,9 +29,12 @@
|
|
<file preprocess="xml-stripblanks">users/cc-user-page.ui</file>
|
|
<file alias="users/join-dialog.ui" preprocess="xml-stripblanks">users/data/join-dialog.ui</file>
|
|
<file>users/users.css</file>
|
|
+
|
|
+ <file preprocess="xml-stripblanks">subman/cc-subman-page.ui</file>
|
|
</gresource>
|
|
|
|
<gresource prefix="/org/gnome/Settings">
|
|
+ <file preprocess="xml-stripblanks">icons/scalable/actions/key-symbolic.svg</file>
|
|
<file preprocess="xml-stripblanks">icons/scalable/actions/system-update-symbolic.svg</file>
|
|
<file preprocess="xml-stripblanks">icons/scalable/status/fingerprint-detection-complete-symbolic.svg</file>
|
|
<file preprocess="xml-stripblanks">icons/scalable/status/fingerprint-detection-symbolic.svg</file>
|
|
--
|
|
2.47.0
|
|
|