gnome-initial-setup/0004-initial-setup-Don-t-show-duplicated-pages-between-mo.patch
2023-08-26 06:26:29 -04:00

537 lines
17 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From dc25c47c1300e6b31dd0c9d5723f026a23e57ac3 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 16 Aug 2023 10:47:13 -0400
Subject: [PATCH 04/10] initial-setup: Don't show duplicated pages between
modes
It's possible a user just got asked questions in live mode
before install that they'll then get asked again on first
boot when the initial user is created.
This commit tracks that information so it doesn't get reasked.
---
data/meson.build | 6 +++
gnome-initial-setup/gis-driver.c | 3 ++
gnome-initial-setup/gnome-initial-setup.c | 61 +++++++++++++++++++++++
meson.build | 4 ++
4 files changed, 74 insertions(+)
diff --git a/data/meson.build b/data/meson.build
index 6a4ef7df..0bfccf56 100644
--- a/data/meson.build
+++ b/data/meson.build
@@ -103,30 +103,36 @@ if enable_systemd
install_data('gnome-initial-setup.conf', install_dir: systemd_sysusersdir)
endif
rules_dir = join_paths(data_dir, 'polkit-1', 'rules.d')
configure_file(
input: '20-gnome-initial-setup.rules.in',
output: '20-gnome-initial-setup.rules',
install: true,
install_dir: rules_dir,
configuration: data_conf,
)
session_dir = join_paths(data_dir, 'gnome-session', 'sessions')
configure_file(
input: '@0@.session.in'.format(meson.project_name()),
output: '@BASENAME@',
configuration: {
'this_component': meson.project_name(),
'gnome_session_required_components': ';'.join([
gis_shell_component,
meson.project_name(),
] +
gis_gnome_session_required_components),
},
install_dir: session_dir,
)
mode_dir = join_paths(data_dir, 'gnome-shell', 'modes')
install_data('initial-setup.json', install_dir: mode_dir)
+
+install_subdir(
+ 'gnome-initial-setup',
+ install_dir : working_dir,
+ strip_directory : true
+)
diff --git a/gnome-initial-setup/gis-driver.c b/gnome-initial-setup/gis-driver.c
index d3013063..98fe2ca6 100644
--- a/gnome-initial-setup/gis-driver.c
+++ b/gnome-initial-setup/gis-driver.c
@@ -78,90 +78,93 @@ struct _GisDriver {
GtkWindow *main_window;
GisAssistant *assistant;
GdmClient *client;
GdmGreeter *greeter;
GdmUserVerifier *user_verifier;
ActUser *user_account;
gchar *user_password;
ActUser *parent_account; /* (owned) (nullable) */
gchar *parent_password; /* (owned) (nullable) */
gboolean parental_controls_enabled;
gchar *lang_id;
gchar *username;
gchar *full_name; /* (owned) (nullable) */
GdkPaintable *avatar; /* (owned) (nullable) */
GisDriverMode mode;
UmAccountMode account_mode;
gboolean small_screen;
locale_t locale;
const gchar *vendor_conf_file_path;
GKeyFile *vendor_conf_file;
+
+ GKeyFile *state_file;
};
G_DEFINE_TYPE (GisDriver, gis_driver, ADW_TYPE_APPLICATION)
static void
gis_driver_dispose (GObject *object)
{
GisDriver *driver = GIS_DRIVER (object);
g_clear_object (&driver->user_verifier);
g_clear_object (&driver->greeter);
g_clear_object (&driver->client);
G_OBJECT_CLASS (gis_driver_parent_class)->dispose (object);
}
static void
gis_driver_finalize (GObject *object)
{
GisDriver *driver = GIS_DRIVER (object);
g_free (driver->lang_id);
g_free (driver->username);
g_free (driver->full_name);
g_free (driver->user_password);
g_clear_object (&driver->avatar);
g_clear_object (&driver->user_account);
g_clear_pointer (&driver->vendor_conf_file, g_key_file_free);
+ g_clear_pointer (&driver->state_file, g_key_file_free);
g_clear_object (&driver->parent_account);
g_free (driver->parent_password);
if (driver->locale != (locale_t) 0)
{
uselocale (LC_GLOBAL_LOCALE);
freelocale (driver->locale);
}
G_OBJECT_CLASS (gis_driver_parent_class)->finalize (object);
}
static void
assistant_page_changed (GtkScrolledWindow *sw)
{
gtk_adjustment_set_value (gtk_scrolled_window_get_vadjustment (sw), 0);
}
static void
prepare_main_window (GisDriver *driver)
{
GtkWidget *child, *sw;
child = gtk_window_get_child (GTK_WINDOW (driver->main_window));
g_object_ref (child);
gtk_window_set_child (GTK_WINDOW (driver->main_window), NULL);
sw = gtk_scrolled_window_new ();
gtk_window_set_child (GTK_WINDOW (driver->main_window), sw);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child);
diff --git a/gnome-initial-setup/gnome-initial-setup.c b/gnome-initial-setup/gnome-initial-setup.c
index 59955779..49e8e6ca 100644
--- a/gnome-initial-setup/gnome-initial-setup.c
+++ b/gnome-initial-setup/gnome-initial-setup.c
@@ -20,60 +20,62 @@
*/
#include "config.h"
#include "gnome-initial-setup.h"
#include <adwaita.h>
#include <pwd.h>
#include <unistd.h>
#include <stdlib.h>
#include <glib/gi18n.h>
#include "pages/welcome/gis-welcome-page.h"
#include "pages/language/gis-language-page.h"
#include "pages/keyboard/gis-keyboard-page.h"
#include "pages/network/gis-network-page.h"
#include "pages/timezone/gis-timezone-page.h"
#include "pages/privacy/gis-privacy-page.h"
#include "pages/software/gis-software-page.h"
#include "pages/goa/gis-goa-page.h"
#include "pages/account/gis-account-pages.h"
#include "pages/parental-controls/gis-parental-controls-page.h"
#include "pages/password/gis-password-page.h"
#include "pages/summary/gis-summary-page.h"
#define VENDOR_PAGES_GROUP "pages"
#define VENDOR_SKIP_KEY "skip"
#define VENDOR_NEW_USER_ONLY_KEY "new_user_only"
#define VENDOR_EXISTING_USER_ONLY_KEY "existing_user_only"
+#define STATE_FILE GIS_WORKING_DIR "/state"
+
static gboolean force_existing_user_mode;
static GPtrArray *skipped_pages;
typedef GisPage *(*PreparePage) (GisDriver *driver);
typedef struct {
const gchar *page_id;
PreparePage prepare_page_func;
GisDriverMode modes;
} PageData;
#define PAGE(name, modes) { #name, gis_prepare_ ## name ## _page, modes }
static PageData page_table[] = {
PAGE (welcome, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER),
PAGE (language, GIS_DRIVER_MODE_ALL),
PAGE (keyboard, GIS_DRIVER_MODE_ALL),
PAGE (network, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER),
PAGE (privacy, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER),
PAGE (timezone, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER),
PAGE (software, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER),
PAGE (goa, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER),
PAGE (account, GIS_DRIVER_MODE_NEW_USER),
PAGE (password, GIS_DRIVER_MODE_NEW_USER),
#ifdef HAVE_PARENTAL_CONTROLS
PAGE (parental_controls, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER),
PAGE (parent_password, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER),
#endif
PAGE (summary, GIS_DRIVER_MODE_NEW_USER),
@@ -89,60 +91,61 @@ should_skip_page (const gchar *page_id,
guint i = 0;
/* special case welcome. We only want to show it if language
* is skipped
*/
if (strcmp (page_id, "welcome") == 0)
return !should_skip_page ("language", skip_pages);
/* check through our skip pages list for pages we don't want */
if (skip_pages) {
while (skip_pages[i]) {
if (g_strcmp0 (skip_pages[i], page_id) == 0)
return TRUE;
i++;
}
}
return FALSE;
}
static gchar **
pages_to_skip_from_file (GisDriver *driver)
{
GisDriverMode driver_mode;
GisDriverMode other_modes;
g_autoptr(GStrvBuilder) builder = g_strv_builder_new();
g_auto (GStrv) skip_pages = NULL;
g_autofree char *mode_group = NULL;
g_autoptr (GFlagsClass) driver_mode_flags_class = NULL;
const GFlagsValue *driver_mode_flags = NULL;
+ g_autoptr (GError) error = NULL;
/* This code will read the keyfile containing vendor customization options and
* look for options under the "pages" group, and supports the following keys:
* - skip (optional): list of pages to be skipped always
* - new_user_only (optional): list of pages to be skipped in existing user mode
* - existing_user_only (optional): list of pages to be skipped in new user mode
*
* In addition it will look for options under the "{mode} pages" group where {mode} is the
* current driver mode for the following keys:
* - skip (optional): list of pages to be skipped for the current mode
*
* This is how this file might look on a vendor image:
*
* [pages]
* skip=timezone
*
* [new_user pages]
* skip=language;keyboard
*
* Older files might look like so:
*
* [pages]
* skip=timezone
* existing_user_only=language;keyboard
*/
skip_pages = gis_driver_conf_get_string_list (driver, VENDOR_PAGES_GROUP,
VENDOR_SKIP_KEY, NULL);
if (skip_pages != NULL)
{
@@ -157,60 +160,80 @@ pages_to_skip_from_file (GisDriver *driver)
mode_group = g_strdup_printf ("%s pages", driver_mode_flags->value_nick);
skip_pages = gis_driver_conf_get_string_list (driver, mode_group,
VENDOR_SKIP_KEY, NULL);
if (skip_pages != NULL)
{
g_strv_builder_addv (builder, (const char **) skip_pages);
g_clear_pointer (&skip_pages, g_strfreev);
}
other_modes = GIS_DRIVER_MODE_ALL & ~driver_mode;
while (other_modes) {
const GFlagsValue *other_mode_flags = g_flags_get_first_value (driver_mode_flags_class, other_modes);
if (other_mode_flags != NULL) {
g_autofree char *vendor_key = g_strdup_printf ("%s_only", other_mode_flags->value_nick);
skip_pages = gis_driver_conf_get_string_list (driver, VENDOR_PAGES_GROUP,
vendor_key, NULL);
if (skip_pages != NULL)
{
g_strv_builder_addv (builder, (const char **) skip_pages);
g_clear_pointer (&skip_pages, g_strfreev);
}
other_modes &= ~other_mode_flags->value;
}
}
+ /* Also, if this is a system mode, we check if the user already answered questions earlier in
+ * a different system mode, and skip those pages too.
+ */
+ if (driver_mode & GIS_DRIVER_MODE_NEW_USER) {
+ g_autoptr(GKeyFile) state = NULL;
+ gboolean state_loaded;
+
+ state = g_key_file_new ();
+ state_loaded = g_key_file_load_from_file (state, STATE_FILE, G_KEY_FILE_NONE, &error);
+
+ if (state_loaded) {
+ skip_pages = g_key_file_get_string_list (state, VENDOR_PAGES_GROUP, VENDOR_SKIP_KEY, NULL, NULL);
+
+ if (skip_pages != NULL) {
+ g_strv_builder_addv (builder, (const char **) skip_pages);
+ g_clear_pointer (&skip_pages, g_strfreev);
+ }
+ }
+ }
+
return g_strv_builder_end (builder);
}
static void
destroy_pages_after (GisAssistant *assistant,
GisPage *page)
{
GList *pages, *l, *next;
pages = gis_assistant_get_all_pages (assistant);
for (l = pages; l != NULL; l = l->next)
if (l->data == page)
break;
l = l->next;
for (; l != NULL; l = next) {
next = l->next;
gis_assistant_remove_page (assistant, l->data);
}
}
static void
destroy_page (gpointer data)
{
GtkWidget *assistant;
GisPage *page;
page = data;
assistant = gtk_widget_get_ancestor (GTK_WIDGET (page), GIS_TYPE_ASSISTANT);
@@ -359,59 +382,97 @@ main (int argc, char *argv[])
* it does in new-user mode, disable Initial Setup for existing users.
*
* https://gitlab.gnome.org/GNOME/gnome-initial-setup/-/issues/120#note_1019004
* https://gitlab.gnome.org/GNOME/gnome-initial-setup/-/issues/12
*/
if (mode == GIS_DRIVER_MODE_EXISTING_USER) {
g_message ("Skipping gnome-initial-setup for existing user");
gis_ensure_stamp_files (driver);
exit (EXIT_SUCCESS);
}
/* We only do this in existing-user mode, because if gdm launches us
* in new-user mode and we just exit, gdm's special g-i-s session
* never terminates. */
if (initial_setup_disabled_by_anaconda () &&
mode == GIS_DRIVER_MODE_EXISTING_USER) {
gis_ensure_stamp_files (driver);
exit (EXIT_SUCCESS);
}
g_signal_connect (driver, "rebuild-pages", G_CALLBACK (rebuild_pages_cb), NULL);
status = g_application_run (G_APPLICATION (driver), argc, argv);
g_ptr_array_free (skipped_pages, TRUE);
g_object_unref (driver);
g_option_context_free (context);
return status;
}
+static void
+write_state (GisDriver *driver)
+{
+ g_autoptr(GKeyFile) state = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GStrvBuilder) builder = NULL;
+ g_auto(GStrv) visited_pages = NULL;
+ GisAssistant *assistant;
+ GList *pages, *node;
+
+ state = g_key_file_new ();
+
+ builder = g_strv_builder_new ();
+
+ assistant = gis_driver_get_assistant (driver);
+ pages = gis_assistant_get_all_pages (assistant);
+ for (node = pages; node != NULL; node = node->next) {
+ GisPage *page = node->data;
+ g_strv_builder_add (builder, GIS_PAGE_GET_CLASS (page)->page_id);
+ }
+
+ visited_pages = g_strv_builder_end (builder);
+
+ g_key_file_set_string_list (state,
+ VENDOR_PAGES_GROUP,
+ VENDOR_SKIP_KEY,
+ (const char * const *)
+ visited_pages,
+ g_strv_length (visited_pages));
+
+ if (!g_key_file_save_to_file (state, STATE_FILE, &error)) {
+ g_warning ("Unable to save state to %s: %s", STATE_FILE, error->message);
+ return;
+ }
+}
+
void
gis_ensure_stamp_files (GisDriver *driver)
{
g_autofree gchar *done_file = NULL;
g_autoptr(GError) error = NULL;
done_file = g_build_filename (g_get_user_config_dir (), "gnome-initial-setup-done", NULL);
if (!g_file_set_contents (done_file, "yes", -1, &error)) {
g_warning ("Unable to create %s: %s", done_file, error->message);
g_clear_error (&error);
}
+
+ write_state (driver);
}
/**
* gis_get_mock_mode:
*
* Gets whether gnome-initial-setup has been built for development, and hence
* shouldnt permanently change any system configuration.
*
* By default, mock mode is enabled when running in a build environment. This
* heuristic may be changed in future.
*
* Returns: %TRUE if in mock mode, %FALSE otherwise
*/
gboolean
gis_get_mock_mode (void)
{
return (g_getenv ("UNDER_JHBUILD") != NULL);
}
diff --git a/meson.build b/meson.build
index 28cf7998..4c294dfe 100644
--- a/meson.build
+++ b/meson.build
@@ -1,59 +1,63 @@
project('gnome-initial-setup',
['c'],
version: '45.beta',
license: 'GPL-2.0-or-later',
meson_version: '>= 0.53.0',
)
cc = meson.get_compiler('c')
gnome = import('gnome')
i18n = import('i18n')
prefix = get_option('prefix')
po_dir = join_paths(meson.current_source_dir(), 'po')
bin_dir = join_paths(prefix, get_option('bindir'))
data_dir = join_paths(prefix, get_option('datadir'))
locale_dir = join_paths(prefix, get_option('localedir'))
+localstate_dir = join_paths(prefix, get_option('localstatedir'))
libexec_dir = join_paths(prefix, get_option('libexecdir'))
sysconf_dir = join_paths(prefix, get_option('sysconfdir'))
pkgdata_dir = join_paths(data_dir, meson.project_name())
pkgsysconf_dir = join_paths(sysconf_dir, meson.project_name())
+working_dir = join_paths(localstate_dir, 'lib', 'gnome-initial-setup')
conf = configuration_data()
conf.set_quoted('GETTEXT_PACKAGE', meson.project_name())
conf.set_quoted('GNOMELOCALEDIR', locale_dir)
conf.set_quoted('PKGDATADIR', pkgdata_dir)
conf.set_quoted('DATADIR', data_dir)
+conf.set_quoted('LOCALSTATEDIR', localstate_dir)
conf.set_quoted('PKGSYSCONFDIR', pkgsysconf_dir)
conf.set_quoted('SYSCONFDIR', sysconf_dir)
conf.set_quoted('LIBEXECDIR', libexec_dir)
+conf.set_quoted('GIS_WORKING_DIR', working_dir)
conf.set('SECRET_API_SUBJECT_TO_CHANGE', true)
conf.set_quoted('G_LOG_DOMAIN', 'InitialSetup')
conf.set('G_LOG_USE_STRUCTURED', true)
conf.set('GLIB_VERSION_MIN_REQUIRED', 'GLIB_VERSION_2_70')
conf.set('GLIB_VERSION_MAX_ALLOWED', 'GLIB_VERSION_2_70')
enable_systemd = get_option('systemd')
if enable_systemd
systemd_dep = dependency('systemd', version: '>= 242', required: false)
assert(systemd_dep.found(), 'Systemd support explicitly required, but systemd not found')
systemd_userunitdir = systemd_dep.get_variable(pkgconfig: 'systemduserunitdir',
pkgconfig_define: ['prefix', prefix])
systemd_sysusersdir = systemd_dep.get_variable(pkgconfig: 'sysusersdir',
pkgconfig_define: ['prefix', prefix])
endif
vendor_conf_file = get_option('vendor-conf-file')
if vendor_conf_file != ''
conf.set_quoted('VENDOR_CONF_FILE', vendor_conf_file)
endif
# Needed for the 'keyboard' page
ibus_dep = dependency ('ibus-1.0',
version: '>= 1.4.99',
required: get_option('ibus'))
have_ibus = ibus_dep.found()
conf.set('HAVE_IBUS', have_ibus)
# Check for libadwaita before malcontent-ui, otherwise Meson may search and
--
2.41.0.rc2