diff --git a/0001-gnome-initial-setup-Bump-GLib-required-version-to-2..patch b/0001-gnome-initial-setup-Bump-GLib-required-version-to-2..patch new file mode 100644 index 0000000..611a7b7 --- /dev/null +++ b/0001-gnome-initial-setup-Bump-GLib-required-version-to-2..patch @@ -0,0 +1,150 @@ +From 58251548ef5835d1c1ce464fd4e8c29d99c1b343 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 15 Aug 2023 10:53:41 -0400 +Subject: [PATCH 1/7] gnome-initial-setup: Bump GLib required version to 2.70 + +This gives us GStrvBuilder +--- + gnome-initial-setup/meson.build | 2 +- + meson.build | 4 ++-- + 2 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/gnome-initial-setup/meson.build b/gnome-initial-setup/meson.build +index a6a014d1..d0ad5ff8 100644 +--- a/gnome-initial-setup/meson.build ++++ b/gnome-initial-setup/meson.build +@@ -25,61 +25,61 @@ sources += [ + 'gis-driver.h', + 'gis-keyring.h' + ] + + geocode_glib_2_dep = dependency( + 'geocode-glib-2.0', + fallback: ['geocode-glib', 'geocode_glib_dep'], + default_options: [ + 'enable-gtk-doc=false', + 'enable-installed-tests=false', + 'enable-introspection=false', + 'soup2=false', + ], + ) + + gweather_dep = dependency('gweather4') + + subdir('pages') + + dependencies = [ + dependency ('libnm', version: '>= 1.2'), + dependency ('libnma-gtk4', version: '>= 1.0'), + dependency ('polkit-gobject-1', version: '>= 0.103'), + dependency ('accountsservice'), + geocode_glib_2_dep, + dependency ('gnome-desktop-4'), + dependency ('gsettings-desktop-schemas', version: '>= 3.37.1'), + dependency ('fontconfig'), + dependency ('goa-1.0'), + dependency ('gtk4', version: '>= 4.6'), +- dependency ('glib-2.0', version: '>= 2.63.1'), ++ dependency ('glib-2.0', version: '>= 2.70.0'), + dependency ('gio-unix-2.0', version: '>= 2.53.0'), + dependency ('gdm', version: '>= 3.8.3'), + gweather_dep, + dependency ('libgeoclue-2.0', version: '>= 2.3.1'), + cc.find_library('m', required: false), + dependency ('pango', version: '>= 1.32.5'), + dependency ('json-glib-1.0'), + dependency ('krb5'), + dependency ('libsecret-1', version: '>= 0.18.8'), + dependency ('pwquality'), + dependency ('rest-1.0'), + ibus_dep, + libmalcontent_dep, + libmalcontent_ui_dep, + libadwaita_dep, + webkitgtk_dep + ] + + executable( + 'gnome-initial-setup', + sources, + include_directories: config_h_dir, + dependencies: dependencies, + install: true, + install_dir: get_option('libexecdir') + ) + + executable( + 'gnome-initial-setup-copy-worker', + ['gnome-initial-setup-copy-worker.c'], +diff --git a/meson.build b/meson.build +index a1057988..28cf7998 100644 +--- a/meson.build ++++ b/meson.build +@@ -3,62 +3,62 @@ project('gnome-initial-setup', + 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')) + 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()) + + 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('PKGSYSCONFDIR', pkgsysconf_dir) + conf.set_quoted('SYSCONFDIR', sysconf_dir) + conf.set_quoted('LIBEXECDIR', libexec_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_64') +-conf.set('GLIB_VERSION_MAX_ALLOWED', 'GLIB_VERSION_2_64') ++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 + # find an older version of libadwaita in the host system, cache it, and fail + # to fallback to a submodule. + libadwaita_dep = dependency( + 'libadwaita-1', + version: '>= 1.2.alpha', +-- +2.41.0.rc2 + diff --git a/0002-driver-Specify-mode-via-flags-instead-of-boolean.patch b/0002-driver-Specify-mode-via-flags-instead-of-boolean.patch new file mode 100644 index 0000000..3be92af --- /dev/null +++ b/0002-driver-Specify-mode-via-flags-instead-of-boolean.patch @@ -0,0 +1,633 @@ +From 73d90588a4ab47d46c7aca4fe2f1313a1f7b1353 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Sun, 13 Aug 2023 09:09:56 -0400 +Subject: [PATCH 2/7] driver: Specify mode via flags instead of boolean + +At the moment we just have system mode and new user mode, +but we're actually going to want other modes (such as +live user mode) as well. + +Currently the code distinguishes between its two available +modes using a boolean `is_new_user`. That isn't extensible beyond +two modes, so this commit changes it use bit flags instead. +--- + gnome-initial-setup/gis-driver.c | 17 ++- + gnome-initial-setup/gis-driver.h | 8 +- + gnome-initial-setup/gnome-initial-setup.c | 128 +++++++++++++--------- + 3 files changed, 93 insertions(+), 60 deletions(-) + +diff --git a/gnome-initial-setup/gis-driver.c b/gnome-initial-setup/gis-driver.c +index 98b4fc3e..d3013063 100644 +--- a/gnome-initial-setup/gis-driver.c ++++ b/gnome-initial-setup/gis-driver.c +@@ -9,75 +9,74 @@ + * + * 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: + * Jasper St. Pierre + */ + + #include "config.h" + + #include "gnome-initial-setup.h" + + #include + #include + #include + + #ifdef HAVE_WEBKITGTK_6_0 + #include + #else + #include + #endif + + #include "cc-common-language.h" + #include "gis-assistant.h" + +-#define GIS_TYPE_DRIVER_MODE (gis_driver_mode_get_type ()) +- + /* Statically include this for now. Maybe later + * we'll generate this from glib-mkenums. */ + GType + gis_driver_mode_get_type (void) { + static GType enum_type_id = 0; + if (G_UNLIKELY (!enum_type_id)) + { +- static const GEnumValue values[] = { ++ static const GFlagsValue values[] = { + { GIS_DRIVER_MODE_NEW_USER, "GIS_DRIVER_MODE_NEW_USER", "new_user" }, + { GIS_DRIVER_MODE_EXISTING_USER, "GIS_DRIVER_MODE_EXISTING_USER", "existing_user" }, ++ { GIS_DRIVER_MODE_ALL, "GIS_DRIVER_MODE_ALL", "all" }, + { 0, NULL, NULL } + }; +- enum_type_id = g_enum_register_static("GisDriverMode", values); ++ enum_type_id = g_flags_register_static("GisDriverMode", values); + } + return enum_type_id; + } + + enum { + REBUILD_PAGES, + LOCALE_CHANGED, + LAST_SIGNAL, + }; + + static guint signals[LAST_SIGNAL]; + + typedef enum { + PROP_MODE = 1, + PROP_USERNAME, + PROP_SMALL_SCREEN, + PROP_PARENTAL_CONTROLS_ENABLED, + PROP_FULL_NAME, + PROP_AVATAR, + } GisDriverProperty; + + static GParamSpec *obj_props[PROP_AVATAR + 1]; + + struct _GisDriver { + AdwApplication parent_instance; + + GtkWindow *main_window; + GisAssistant *assistant; + + GdmClient *client; +@@ -624,61 +623,61 @@ gis_driver_get_property (GObject *object, + break; + case PROP_SMALL_SCREEN: + g_value_set_boolean (value, driver->small_screen); + break; + case PROP_PARENTAL_CONTROLS_ENABLED: + g_value_set_boolean (value, driver->parental_controls_enabled); + break; + case PROP_FULL_NAME: + g_value_set_string (value, driver->full_name); + break; + case PROP_AVATAR: + g_value_set_object (value, driver->avatar); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + } + + static void + gis_driver_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) + { + GisDriver *driver = GIS_DRIVER (object); + + switch ((GisDriverProperty) prop_id) + { + case PROP_MODE: +- driver->mode = g_value_get_enum (value); ++ driver->mode = g_value_get_flags (value); + break; + case PROP_USERNAME: + g_free (driver->username); + driver->username = g_value_dup_string (value); + break; + case PROP_PARENTAL_CONTROLS_ENABLED: + gis_driver_set_parental_controls_enabled (driver, g_value_get_boolean (value)); + break; + case PROP_FULL_NAME: + gis_driver_set_full_name (driver, g_value_get_string (value)); + break; + case PROP_AVATAR: + gis_driver_set_avatar (driver, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + } + + static void + gis_driver_activate (GApplication *app) + { + GisDriver *driver = GIS_DRIVER (app); + + G_APPLICATION_CLASS (gis_driver_parent_class)->activate (app); + + gtk_window_present (GTK_WINDOW (driver->main_window)); + } + +@@ -826,64 +825,64 @@ gis_driver_init (GisDriver *driver) + static void + gis_driver_class_init (GisDriverClass *klass) + { + GApplicationClass *application_class = G_APPLICATION_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->get_property = gis_driver_get_property; + gobject_class->set_property = gis_driver_set_property; + gobject_class->dispose = gis_driver_dispose; + gobject_class->finalize = gis_driver_finalize; + application_class->startup = gis_driver_startup; + application_class->activate = gis_driver_activate; + + signals[REBUILD_PAGES] = + g_signal_new ("rebuild-pages", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); + + signals[LOCALE_CHANGED] = + g_signal_new ("locale-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); + + obj_props[PROP_MODE] = +- g_param_spec_enum ("mode", "", "", +- GIS_TYPE_DRIVER_MODE, +- GIS_DRIVER_MODE_EXISTING_USER, +- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); ++ g_param_spec_flags ("mode", "", "", ++ GIS_TYPE_DRIVER_MODE, ++ GIS_DRIVER_MODE_EXISTING_USER, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + obj_props[PROP_USERNAME] = + g_param_spec_string ("username", "", "", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + obj_props[PROP_SMALL_SCREEN] = + g_param_spec_boolean ("small-screen", "", "", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * GisDriver:parental-controls-enabled: + * + * Whether parental controls are enabled for the main user. If this is %TRUE, + * two user accounts will be created when this page is saved: one for the main + * user (a child) which will be a standard account; and one for the parent + * which will be an administrative account. + * + * Since: 3.36 + */ + obj_props[PROP_PARENTAL_CONTROLS_ENABLED] = + g_param_spec_boolean ("parental-controls-enabled", + "Parental Controls Enabled", + "Whether parental controls are enabled for the main user.", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + /** + * GisDriver:full-name: (nullable) +diff --git a/gnome-initial-setup/gis-driver.h b/gnome-initial-setup/gis-driver.h +index 9b935e24..b57db2e2 100644 +--- a/gnome-initial-setup/gis-driver.h ++++ b/gnome-initial-setup/gis-driver.h +@@ -4,74 +4,78 @@ + * + * 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: + * Jasper St. Pierre + */ + + #ifndef __GIS_DRIVER_H__ + #define __GIS_DRIVER_H__ + + #include "gis-assistant.h" + #include "gis-page.h" + #include + #include + #include + + G_BEGIN_DECLS + + #define GIS_TYPE_DRIVER (gis_driver_get_type ()) ++#define GIS_TYPE_DRIVER_MODE (gis_driver_mode_get_type ()) + + G_DECLARE_FINAL_TYPE (GisDriver, gis_driver, GIS, DRIVER, AdwApplication) + + typedef enum { + UM_LOCAL, + UM_ENTERPRISE, + NUM_MODES, + } UmAccountMode; + + typedef enum { +- GIS_DRIVER_MODE_NEW_USER, +- GIS_DRIVER_MODE_EXISTING_USER, ++ GIS_DRIVER_MODE_NEW_USER = 1 << 0, ++ GIS_DRIVER_MODE_EXISTING_USER = 1 << 1, ++ GIS_DRIVER_MODE_ALL = (GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER), + } GisDriverMode; + ++GType gis_driver_mode_get_type (void); ++ + GisAssistant *gis_driver_get_assistant (GisDriver *driver); + + void gis_driver_set_user_permissions (GisDriver *driver, + ActUser *user, + const gchar *password); + + void gis_driver_get_user_permissions (GisDriver *driver, + ActUser **user, + const gchar **password); + + void gis_driver_set_parent_permissions (GisDriver *driver, + ActUser *parent, + const gchar *password); + + void gis_driver_get_parent_permissions (GisDriver *driver, + ActUser **parent, + const gchar **password); + + void gis_driver_set_account_mode (GisDriver *driver, + UmAccountMode mode); + + UmAccountMode gis_driver_get_account_mode (GisDriver *driver); + + void gis_driver_set_parental_controls_enabled (GisDriver *driver, + gboolean parental_controls_enabled); + + gboolean gis_driver_get_parental_controls_enabled (GisDriver *driver); + + void gis_driver_set_user_language (GisDriver *driver, + const gchar *lang_id, +diff --git a/gnome-initial-setup/gnome-initial-setup.c b/gnome-initial-setup/gnome-initial-setup.c +index 113ded36..59955779 100644 +--- a/gnome-initial-setup/gnome-initial-setup.c ++++ b/gnome-initial-setup/gnome-initial-setup.c +@@ -29,228 +29,258 @@ + #include + #include + + #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" + + 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; +- gboolean new_user_only; ++ GisDriverMode modes; + } PageData; + +-#define PAGE(name, new_user_only) { #name, gis_prepare_ ## name ## _page, new_user_only } ++#define PAGE(name, modes) { #name, gis_prepare_ ## name ## _page, modes } + + static PageData page_table[] = { +- PAGE (welcome, FALSE), +- PAGE (language, FALSE), +- PAGE (keyboard, FALSE), +- PAGE (network, FALSE), +- PAGE (privacy, FALSE), +- PAGE (timezone, TRUE), +- PAGE (software, TRUE), +- PAGE (goa, FALSE), +- PAGE (account, TRUE), +- PAGE (password, TRUE), ++ 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, TRUE), +- PAGE (parent_password, TRUE), ++ 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, FALSE), ++ PAGE (summary, GIS_DRIVER_MODE_NEW_USER), + { NULL }, + }; + + #undef PAGE + + static gboolean + should_skip_page (const gchar *page_id, + gchar **skip_pages) + { + 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 ** +-strv_append (gchar **a, +- gchar **b) ++pages_to_skip_from_file (GisDriver *driver) + { +- guint n = g_strv_length (a); +- guint m = g_strv_length (b); +- +- a = g_renew (gchar *, a, n + m + 1); +- for (guint i = 0; i < m; i++) +- a[n + i] = g_strdup (b[i]); +- a[n + m] = NULL; +- +- return a; +-} +- +-static gchar ** +-pages_to_skip_from_file (GisDriver *driver, +- gboolean is_new_user) +-{ +- GStrv skip_pages = NULL; +- GStrv additional_skip_pages = NULL; ++ 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; + + /* 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); +- additional_skip_pages = +- gis_driver_conf_get_string_list (driver, VENDOR_PAGES_GROUP, +- is_new_user ? VENDOR_EXISTING_USER_ONLY_KEY : VENDOR_NEW_USER_ONLY_KEY, +- NULL); +- +- if (!skip_pages && additional_skip_pages) { +- skip_pages = additional_skip_pages; +- } else if (skip_pages && additional_skip_pages) { +- skip_pages = strv_append (skip_pages, additional_skip_pages); +- g_strfreev (additional_skip_pages); ++ if (skip_pages != NULL) ++ { ++ g_strv_builder_addv (builder, (const char **) skip_pages); ++ g_clear_pointer (&skip_pages, g_strfreev); ++ } ++ ++ driver_mode_flags_class = g_type_class_ref (GIS_TYPE_DRIVER_MODE); ++ ++ driver_mode = gis_driver_get_mode (driver); ++ driver_mode_flags = g_flags_get_first_value (driver_mode_flags_class, driver_mode); ++ ++ 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; ++ } + } + +- return skip_pages; ++ 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); + + if (assistant) + gis_assistant_remove_page (GIS_ASSISTANT (assistant), page); + } + + static void + rebuild_pages_cb (GisDriver *driver) + { + PageData *page_data; + GisPage *page; + GisAssistant *assistant; + GisPage *current_page; + gchar **skip_pages; +- gboolean is_new_user, skipped; ++ GisDriverMode driver_mode; ++ gboolean skipped; + + assistant = gis_driver_get_assistant (driver); + current_page = gis_assistant_get_current_page (assistant); + page_data = page_table; + + g_ptr_array_free (skipped_pages, TRUE); + skipped_pages = g_ptr_array_new_with_free_func (destroy_page); + + if (current_page != NULL) { + destroy_pages_after (assistant, current_page); + + for (page_data = page_table; page_data->page_id != NULL; ++page_data) + if (g_str_equal (page_data->page_id, GIS_PAGE_GET_CLASS (current_page)->page_id)) + break; + + ++page_data; + } + +- is_new_user = (gis_driver_get_mode (driver) == GIS_DRIVER_MODE_NEW_USER); +- skip_pages = pages_to_skip_from_file (driver, is_new_user); ++ driver_mode = gis_driver_get_mode (driver); ++ skip_pages = pages_to_skip_from_file (driver); + + for (; page_data->page_id != NULL; ++page_data) { + skipped = FALSE; + +- if ((page_data->new_user_only && !is_new_user) || ++ if (((page_data->modes & driver_mode) == 0) || + (should_skip_page (page_data->page_id, skip_pages))) + skipped = TRUE; + + page = page_data->prepare_page_func (driver); + if (!page) + continue; + + if (skipped) { + gis_page_skip (page); + g_ptr_array_add (skipped_pages, page); + } else { + gis_driver_add_page (driver, page); + } + } + + g_strfreev (skip_pages); + } + + static GisDriverMode + get_mode (void) + { + if (force_existing_user_mode) + return GIS_DRIVER_MODE_EXISTING_USER; + else + return GIS_DRIVER_MODE_NEW_USER; + } + + static gboolean + initial_setup_disabled_by_anaconda (void) + { +-- +2.41.0.rc2 + diff --git a/0003-gnome-initial-setup-Add-live-user-mode.patch b/0003-gnome-initial-setup-Add-live-user-mode.patch new file mode 100644 index 0000000..b33f9af --- /dev/null +++ b/0003-gnome-initial-setup-Add-live-user-mode.patch @@ -0,0 +1,1635 @@ +From bf02d8405e7192f736ecc7088f2bd265b87db4b7 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Sun, 13 Aug 2023 09:39:07 -0400 +Subject: [PATCH 3/7] gnome-initial-setup: Add live user mode + +This commit adds a new "live user" mode meant to be run in live image +environments. + +It asks questions the user should answer before the installer is +started, and provides a way for the user to initiate the installer +or just jump into the live session instead. +--- + data/20-gnome-initial-setup.rules.in | 3 +- + gnome-initial-setup/gis-driver.c | 4 +- + gnome-initial-setup/gis-driver.h | 4 +- + gnome-initial-setup/gis-util.c | 70 ++++ + gnome-initial-setup/gis-util.h | 2 + + gnome-initial-setup/gnome-initial-setup.c | 26 +- + .../pages/account/gis-account-pages.c | 21 + + .../pages/install/gis-install-page.c | 384 ++++++++++++++++++ + .../pages/install/gis-install-page.h | 52 +++ + .../pages/install/gis-install-page.ui | 49 +++ + .../pages/install/install.gresource.xml | 7 + + gnome-initial-setup/pages/install/meson.build | 9 + + .../pages/keyboard/gis-keyboard-page.c | 4 +- + .../pages/language/gis-language-page.c | 5 +- + gnome-initial-setup/pages/meson.build | 1 + + .../pages/password/gis-password-page.c | 6 + + 16 files changed, 632 insertions(+), 15 deletions(-) + create mode 100644 gnome-initial-setup/pages/install/gis-install-page.c + create mode 100644 gnome-initial-setup/pages/install/gis-install-page.h + create mode 100644 gnome-initial-setup/pages/install/gis-install-page.ui + create mode 100644 gnome-initial-setup/pages/install/install.gresource.xml + create mode 100644 gnome-initial-setup/pages/install/meson.build + +diff --git a/data/20-gnome-initial-setup.rules.in b/data/20-gnome-initial-setup.rules.in +index 02fd21d0..881efde9 100644 +--- a/data/20-gnome-initial-setup.rules.in ++++ b/data/20-gnome-initial-setup.rules.in +@@ -1,29 +1,30 @@ + // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + // + // DO NOT EDIT THIS FILE, it will be overwritten on update. + // + // Allow the gnome-initial-setup user to do certain actions without + // being interrupted by password dialogs + + polkit.addRule(function(action, subject) { + if (subject.user !== 'gnome-initial-setup') + return undefined; + + var actionMatches = (action.id.indexOf('org.freedesktop.hostname1.') === 0 || + action.id.indexOf('org.freedesktop.NetworkManager.') === 0 || + action.id.indexOf('org.freedesktop.locale1.') === 0 || + action.id.indexOf('org.freedesktop.accounts.') === 0 || + action.id.indexOf('org.freedesktop.timedate1.') === 0 || + action.id.indexOf('org.freedesktop.realmd.') === 0 || + action.id.indexOf('com.endlessm.ParentalControls.') === 0 || +- action.id.indexOf('org.fedoraproject.thirdparty.') === 0); ++ action.id.indexOf('org.fedoraproject.thirdparty.') === 0 || ++ action.id.indexOf('org.freedesktop.login1.reboot') === 0); + + if (actionMatches) { + if (subject.local) + return 'yes'; + else + return 'auth_admin'; + } + + return undefined; + }); +diff --git a/gnome-initial-setup/gis-driver.c b/gnome-initial-setup/gis-driver.c +index d3013063..b18a3808 100644 +--- a/gnome-initial-setup/gis-driver.c ++++ b/gnome-initial-setup/gis-driver.c +@@ -19,60 +19,62 @@ + * Jasper St. Pierre + */ + + #include "config.h" + + #include "gnome-initial-setup.h" + + #include + #include + #include + + #ifdef HAVE_WEBKITGTK_6_0 + #include + #else + #include + #endif + + #include "cc-common-language.h" + #include "gis-assistant.h" + + /* Statically include this for now. Maybe later + * we'll generate this from glib-mkenums. */ + GType + gis_driver_mode_get_type (void) { + static GType enum_type_id = 0; + if (G_UNLIKELY (!enum_type_id)) + { + static const GFlagsValue values[] = { + { GIS_DRIVER_MODE_NEW_USER, "GIS_DRIVER_MODE_NEW_USER", "new_user" }, + { GIS_DRIVER_MODE_EXISTING_USER, "GIS_DRIVER_MODE_EXISTING_USER", "existing_user" }, ++ { GIS_DRIVER_MODE_LIVE_USER, "GIS_DRIVER_MODE_LIVE_USER", "live_user" }, ++ { GIS_DRIVER_MODE_SYSTEM, "GIS_DRIVER_MODE_SYSTEM", "system" }, + { GIS_DRIVER_MODE_ALL, "GIS_DRIVER_MODE_ALL", "all" }, + { 0, NULL, NULL } + }; + enum_type_id = g_flags_register_static("GisDriverMode", values); + } + return enum_type_id; + } + + enum { + REBUILD_PAGES, + LOCALE_CHANGED, + LAST_SIGNAL, + }; + + static guint signals[LAST_SIGNAL]; + + typedef enum { + PROP_MODE = 1, + PROP_USERNAME, + PROP_SMALL_SCREEN, + PROP_PARENTAL_CONTROLS_ENABLED, + PROP_FULL_NAME, + PROP_AVATAR, + } GisDriverProperty; + + static GParamSpec *obj_props[PROP_AVATAR + 1]; + + struct _GisDriver { + AdwApplication parent_instance; + +@@ -764,61 +766,61 @@ static void + connect_to_gdm (GisDriver *driver) + { + g_autoptr(GError) error = NULL; + + driver->client = gdm_client_new (); + + driver->greeter = gdm_client_get_greeter_sync (driver->client, NULL, &error); + if (error == NULL) + driver->user_verifier = gdm_client_get_user_verifier_sync (driver->client, NULL, &error); + + if (error != NULL) { + g_warning ("Failed to open connection to GDM: %s", error->message); + g_clear_object (&driver->user_verifier); + g_clear_object (&driver->greeter); + g_clear_object (&driver->client); + } + } + + static void + gis_driver_startup (GApplication *app) + { + GisDriver *driver = GIS_DRIVER (app); + WebKitWebContext *context = webkit_web_context_get_default (); + + G_APPLICATION_CLASS (gis_driver_parent_class)->startup (app); + + #if !WEBKIT_CHECK_VERSION(2, 39, 5) + webkit_web_context_set_sandbox_enabled (context, TRUE); + #endif + +- if (driver->mode == GIS_DRIVER_MODE_NEW_USER) ++ if (driver->mode & GIS_DRIVER_MODE_SYSTEM) + connect_to_gdm (driver); + + driver->main_window = g_object_new (GTK_TYPE_APPLICATION_WINDOW, + "application", app, + "icon-name", "preferences-system", + "deletable", FALSE, + "title", _("Initial Setup"), + NULL); + + g_signal_connect (driver->main_window, + "realize", + G_CALLBACK (window_realize_cb), + (gpointer)app); + + driver->assistant = g_object_new (GIS_TYPE_ASSISTANT, NULL); + gtk_window_set_child (GTK_WINDOW (driver->main_window), + GTK_WIDGET (driver->assistant)); + + gis_driver_set_user_language (driver, setlocale (LC_MESSAGES, NULL), FALSE); + + prepare_main_window (driver); + rebuild_pages (driver); + } + + static void + gis_driver_init (GisDriver *driver) + { + load_vendor_conf_file (driver); + } + +diff --git a/gnome-initial-setup/gis-driver.h b/gnome-initial-setup/gis-driver.h +index b57db2e2..aedb9a73 100644 +--- a/gnome-initial-setup/gis-driver.h ++++ b/gnome-initial-setup/gis-driver.h +@@ -17,61 +17,63 @@ + * + * Written by: + * Jasper St. Pierre + */ + + #ifndef __GIS_DRIVER_H__ + #define __GIS_DRIVER_H__ + + #include "gis-assistant.h" + #include "gis-page.h" + #include + #include + #include + + G_BEGIN_DECLS + + #define GIS_TYPE_DRIVER (gis_driver_get_type ()) + #define GIS_TYPE_DRIVER_MODE (gis_driver_mode_get_type ()) + + G_DECLARE_FINAL_TYPE (GisDriver, gis_driver, GIS, DRIVER, AdwApplication) + + typedef enum { + UM_LOCAL, + UM_ENTERPRISE, + NUM_MODES, + } UmAccountMode; + + typedef enum { + GIS_DRIVER_MODE_NEW_USER = 1 << 0, + GIS_DRIVER_MODE_EXISTING_USER = 1 << 1, +- GIS_DRIVER_MODE_ALL = (GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER), ++ GIS_DRIVER_MODE_LIVE_USER = 1 << 2, ++ GIS_DRIVER_MODE_SYSTEM = (GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_LIVE_USER), ++ GIS_DRIVER_MODE_ALL = (GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER | GIS_DRIVER_MODE_LIVE_USER), + } GisDriverMode; + + GType gis_driver_mode_get_type (void); + + GisAssistant *gis_driver_get_assistant (GisDriver *driver); + + void gis_driver_set_user_permissions (GisDriver *driver, + ActUser *user, + const gchar *password); + + void gis_driver_get_user_permissions (GisDriver *driver, + ActUser **user, + const gchar **password); + + void gis_driver_set_parent_permissions (GisDriver *driver, + ActUser *parent, + const gchar *password); + + void gis_driver_get_parent_permissions (GisDriver *driver, + ActUser **parent, + const gchar **password); + + void gis_driver_set_account_mode (GisDriver *driver, + UmAccountMode mode); + + UmAccountMode gis_driver_get_account_mode (GisDriver *driver); + + void gis_driver_set_parental_controls_enabled (GisDriver *driver, + gboolean parental_controls_enabled); + +diff --git a/gnome-initial-setup/gis-util.c b/gnome-initial-setup/gis-util.c +index ac153fc1..315661b6 100644 +--- a/gnome-initial-setup/gis-util.c ++++ b/gnome-initial-setup/gis-util.c +@@ -4,30 +4,100 @@ + * 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 . + */ + + #include "config.h" + + #include + + #include "gis-util.h" + + void + gis_add_style_from_resource (const char *resource_path) + { + g_autoptr(GtkCssProvider) provider = gtk_css_provider_new (); + + gtk_css_provider_load_from_resource (provider, resource_path); + gtk_style_context_add_provider_for_display (gdk_display_get_default (), + GTK_STYLE_PROVIDER (provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + } ++ ++gboolean ++gis_kernel_command_line_has_argument (const char *arguments[]) ++{ ++ GError *error = NULL; ++ g_autofree char *contents = NULL; ++ g_autoptr (GString) pattern = NULL; ++ gboolean has_argument = FALSE; ++ size_t i; ++ ++ if (!g_file_get_contents ("/proc/cmdline", &contents, NULL, &error)) { ++ g_error_free (error); ++ return FALSE; ++ } ++ ++ /* Build up the pattern by iterating through the alternatives, ++ * escaping all dots so they don't match any character but period, ++ * and adding word boundary specifiers around the arguments so ++ * substrings don't get matched. ++ * ++ * Also, add a | between each alternative. ++ */ ++ pattern = g_string_new (NULL); ++ for (i = 0; arguments[i] != NULL; i++) { ++ g_autofree char *escaped_argument = g_regex_escape_string (arguments[i], -1); ++ ++ if (i > 0) { ++ g_string_append (pattern, "|"); ++ } ++ ++ g_string_append (pattern, "\\b"); ++ ++ g_string_append (pattern, escaped_argument); ++ ++ g_string_append (pattern, "\\b"); ++ } ++ ++ has_argument = g_regex_match_simple (pattern->str, contents, 0, 0); ++ ++ return has_argument; ++} ++ ++void ++gis_substitute_word_in_text (char **text, ++ const char *old_word, ++ const char *new_word) ++{ ++ g_autoptr (GError) error = NULL; ++ g_autofree char *pattern = g_strdup_printf ("\\b%s\\b", old_word); ++ g_autoptr (GRegex) regex = g_regex_new (pattern, 0, 0, &error); ++ g_autofree char *replacement_text = NULL; ++ ++ if (text == NULL || *text == NULL) { ++ return; ++ } ++ ++ if (error != NULL) { ++ g_debug ("Error creating regex to match %s: %s\n", old_word, error->message); ++ } ++ ++ replacement_text = g_regex_replace (regex, *text, -1, 0, new_word, 0, &error); ++ ++ if (error != NULL) { ++ g_debug ("Error replacing %s with %s in %s: %s\n", old_word, new_word, *text, error->message); ++ return; ++ } ++ ++ g_free (*text); ++ *text = g_steal_pointer (&replacement_text); ++} +diff --git a/gnome-initial-setup/gis-util.h b/gnome-initial-setup/gis-util.h +index 5041bddd..10dcd70c 100644 +--- a/gnome-initial-setup/gis-util.h ++++ b/gnome-initial-setup/gis-util.h +@@ -1,19 +1,21 @@ + /* + * Copyright 2023 Endless OS Foundation LLC + * + * 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 . + */ + #pragma once + + void gis_add_style_from_resource (const char *path); ++gboolean gis_kernel_command_line_has_argument (const char *arguments[]); ++void gis_substitute_word_in_text (char **text, const char *old_word, const char *new_word); +diff --git a/gnome-initial-setup/gnome-initial-setup.c b/gnome-initial-setup/gnome-initial-setup.c +index 59955779..bc7a4ee9 100644 +--- a/gnome-initial-setup/gnome-initial-setup.c ++++ b/gnome-initial-setup/gnome-initial-setup.c +@@ -14,141 +14,146 @@ + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Written by: + * Jasper St. Pierre + */ + + #include "config.h" + + #include "gnome-initial-setup.h" + + #include + #include + #include + #include + #include + + #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" ++#include "pages/install/gis-install-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 VENDOR_LIVE_USER_ONLY_KEY "live_user_only" + + static gboolean force_existing_user_mode; ++static gboolean force_live_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 (network, GIS_DRIVER_MODE_ALL), + 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 (timezone, GIS_DRIVER_MODE_ALL), + 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), ++ /* In live user mode, the account page isn't displayed, it just quietly creates the live user */ ++ PAGE (account, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_LIVE_USER), ++ PAGE (password, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_LIVE_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), ++ PAGE (install, GIS_DRIVER_MODE_LIVE_USER), + { NULL }, + }; + + #undef PAGE + + static gboolean + should_skip_page (const gchar *page_id, + gchar **skip_pages) + { + 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; + + /* 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 ++ * - new_user_only (optional): list of pages to be skipped for modes other than new_user ++ * - existing_user_only (optional): list of pages to be skipped for modes other than existing_user + * + * 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) + { + g_strv_builder_addv (builder, (const char **) skip_pages); + g_clear_pointer (&skip_pages, g_strfreev); + } + + driver_mode_flags_class = g_type_class_ref (GIS_TYPE_DRIVER_MODE); + +@@ -250,130 +255,137 @@ rebuild_pages_cb (GisDriver *driver) + driver_mode = gis_driver_get_mode (driver); + skip_pages = pages_to_skip_from_file (driver); + + for (; page_data->page_id != NULL; ++page_data) { + skipped = FALSE; + + if (((page_data->modes & driver_mode) == 0) || + (should_skip_page (page_data->page_id, skip_pages))) + skipped = TRUE; + + page = page_data->prepare_page_func (driver); + if (!page) + continue; + + if (skipped) { + gis_page_skip (page); + g_ptr_array_add (skipped_pages, page); + } else { + gis_driver_add_page (driver, page); + } + } + + g_strfreev (skip_pages); + } + + static GisDriverMode + get_mode (void) + { + if (force_existing_user_mode) + return GIS_DRIVER_MODE_EXISTING_USER; ++ else if (force_live_user_mode) ++ return GIS_DRIVER_MODE_LIVE_USER; + else + return GIS_DRIVER_MODE_NEW_USER; + } + + static gboolean + initial_setup_disabled_by_anaconda (void) + { + const gchar *file_name = SYSCONFDIR "/sysconfig/anaconda"; + g_autoptr(GError) error = NULL; + g_autoptr(GKeyFile) key_file = g_key_file_new (); + + if (!g_key_file_load_from_file (key_file, file_name, G_KEY_FILE_NONE, &error)) { + if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT) && + !g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)) { + g_warning ("Could not read %s: %s", file_name, error->message); + } + return FALSE; + } + + return g_key_file_get_boolean (key_file, "General", "post_install_tools_disabled", NULL); + } + + int + main (int argc, char *argv[]) + { + GisDriver *driver; + int status; + GOptionContext *context; + GisDriverMode mode; + + GOptionEntry entries[] = { + { "existing-user", 0, 0, G_OPTION_ARG_NONE, &force_existing_user_mode, + _("Force existing user mode"), NULL }, ++ { "live-user", 0, 0, G_OPTION_ARG_NONE, &force_live_user_mode, ++ _("Force live user mode"), NULL }, + { NULL } + }; + + g_unsetenv ("GIO_USE_VFS"); + + /* By default, libadwaita reads settings from the Settings portal, which causes + * the portal to be started, which causes gnome-keyring to be started. This + * interferes with our attempt below to manually start gnome-keyring and set + * the login keyring password to a well-known value, which we overwrite with + * the user's password once they choose one. + */ + g_setenv ("ADW_DISABLE_PORTAL", "1", TRUE); + + context = g_option_context_new (_("— GNOME initial setup")); + g_option_context_add_main_entries (context, entries, NULL); + + g_option_context_parse (context, &argc, &argv, NULL); + ++ if (gis_kernel_command_line_has_argument ((const char *[]) { "rd.live.image", "endless.live_boot", NULL })) ++ force_live_user_mode = TRUE; ++ + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + g_message ("Starting gnome-initial-setup"); + if (gis_get_mock_mode ()) + g_message ("Mock mode: changes will not be saved to disk"); + else + g_message ("Production mode: changes will be saved to disk"); + + skipped_pages = g_ptr_array_new_with_free_func (destroy_page); + mode = get_mode (); + + /* When we are running as the gnome-initial-setup user we + * dont have a normal user session and need to initialize + * the keyring manually so that we can pass the credentials + * along to the new user in the handoff. + */ +- if (mode == GIS_DRIVER_MODE_NEW_USER && !gis_get_mock_mode ()) ++ if ((mode & GIS_DRIVER_MODE_SYSTEM) && !gis_get_mock_mode ()) + gis_ensure_login_keyring (); + + driver = gis_driver_new (mode); + adw_style_manager_set_color_scheme (adw_style_manager_get_default (), + ADW_COLOR_SCHEME_PREFER_LIGHT); + + /* On first login, GNOME Shell offers to run a tour. If we also run Initial + * Setup, the two immovable, centred windows will sit atop one another. + * Until we have the ability to run Initial Setup in the "kiosk" mode, like + * 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); +diff --git a/gnome-initial-setup/pages/account/gis-account-pages.c b/gnome-initial-setup/pages/account/gis-account-pages.c +index d9cc8d9f..8b0d8e99 100644 +--- a/gnome-initial-setup/pages/account/gis-account-pages.c ++++ b/gnome-initial-setup/pages/account/gis-account-pages.c +@@ -1,32 +1,53 @@ + /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + /* + * Copyright (C) 2013 Red Hat + * + * 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: + * Jasper St. Pierre + */ + + #include "config.h" + #include "gis-account-pages.h" + #include "gis-account-page.h" + + GisPage * + gis_prepare_account_page (GisDriver *driver) + { ++ GisDriverMode driver_mode; ++ ++ driver_mode = gis_driver_get_mode (driver); ++ ++ if (driver_mode == GIS_DRIVER_MODE_LIVE_USER && !gis_kernel_command_line_has_argument ((const char *[]) { "rd.live.overlay", NULL })) { ++ ActUserManager *act_client = act_user_manager_get_default (); ++ const char *username = "liveuser"; ++ g_autoptr(ActUser) user = NULL; ++ g_autoptr(GError) error = NULL; ++ ++ user = act_user_manager_create_user (act_client, username, username, ACT_USER_ACCOUNT_TYPE_ADMINISTRATOR, &error); ++ ++ if (user != NULL) { ++ act_user_set_password_mode (user, ACT_USER_PASSWORD_MODE_NONE); ++ gis_driver_set_username (driver, username); ++ gis_driver_set_account_mode (driver, UM_LOCAL); ++ gis_driver_set_user_permissions (driver, user, NULL); ++ } ++ return NULL; ++ } ++ + return g_object_new (GIS_TYPE_ACCOUNT_PAGE, + "driver", driver, + NULL); + } +diff --git a/gnome-initial-setup/pages/install/gis-install-page.c b/gnome-initial-setup/pages/install/gis-install-page.c +new file mode 100644 +index 00000000..70e3f9ec +--- /dev/null ++++ b/gnome-initial-setup/pages/install/gis-install-page.c +@@ -0,0 +1,384 @@ ++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ ++/* ++ * Copyright (C) 2023 Red Hat ++ * ++ * 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 . ++ */ ++ ++/* Install page {{{1 */ ++ ++#define PAGE_ID "install" ++ ++#include "config.h" ++#include "cc-common-language.h" ++#include "gis-install-page.h" ++#include "gis-pkexec.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define SERVICE_NAME "gdm-password" ++#define VENDOR_INSTALLER_GROUP "install" ++#define VENDOR_APPLICATION_KEY "application" ++ ++struct _GisInstallPagePrivate { ++ GtkWidget *try_button; ++ GtkWidget *install_button; ++ AdwStatusPage *status_page; ++ GDesktopAppInfo *installer; ++ ++ ActUser *user_account; ++ const gchar *user_password; ++}; ++typedef struct _GisInstallPagePrivate GisInstallPagePrivate; ++ ++G_DEFINE_TYPE_WITH_PRIVATE (GisInstallPage, gis_install_page, GIS_TYPE_PAGE); ++ ++static void ++request_info_query (GisInstallPage *page, ++ GdmUserVerifier *user_verifier, ++ const char *question, ++ gboolean is_secret) ++{ ++ /* TODO: pop up modal dialog */ ++ g_debug ("user verifier asks%s question: %s", ++ is_secret ? " secret" : "", ++ question); ++} ++ ++static void ++on_info (GdmUserVerifier *user_verifier, ++ const char *service_name, ++ const char *info, ++ GisInstallPage *page) ++{ ++ g_debug ("PAM module info: %s", info); ++} ++ ++static void ++on_problem (GdmUserVerifier *user_verifier, ++ const char *service_name, ++ const char *problem, ++ GisInstallPage *page) ++{ ++ g_warning ("PAM module error: %s", problem); ++} ++ ++static void ++on_info_query (GdmUserVerifier *user_verifier, ++ const char *service_name, ++ const char *question, ++ GisInstallPage *page) ++{ ++ request_info_query (page, user_verifier, question, FALSE); ++} ++ ++static void ++on_secret_info_query (GdmUserVerifier *user_verifier, ++ const char *service_name, ++ const char *question, ++ GisInstallPage *page) ++{ ++ GisInstallPagePrivate *priv = gis_install_page_get_instance_private (page); ++ gboolean should_send_password = priv->user_password != NULL; ++ ++ g_debug ("PAM module secret info query: %s", question); ++ if (should_send_password) { ++ g_debug ("sending password\n"); ++ gdm_user_verifier_call_answer_query (user_verifier, ++ service_name, ++ priv->user_password, ++ NULL, NULL, NULL); ++ priv->user_password = NULL; ++ } else { ++ request_info_query (page, user_verifier, question, TRUE); ++ } ++} ++ ++static void ++on_session_opened (GdmGreeter *greeter, ++ const char *service_name, ++ GisInstallPage *page) ++{ ++ gdm_greeter_call_start_session_when_ready_sync (greeter, service_name, ++ TRUE, NULL, NULL); ++} ++ ++static void ++log_user_in (GisInstallPage *page) ++{ ++ GisInstallPagePrivate *priv = gis_install_page_get_instance_private (page); ++ g_autoptr(GError) error = NULL; ++ GdmGreeter *greeter = NULL; ++ GdmUserVerifier *user_verifier = NULL; ++ ++ if (!gis_driver_get_gdm_objects (GIS_PAGE (page)->driver, ++ &greeter, &user_verifier)) { ++ g_warning ("No GDM connection; not initiating login"); ++ return; ++ } ++ ++ g_signal_connect (user_verifier, "info", ++ G_CALLBACK (on_info), page); ++ g_signal_connect (user_verifier, "problem", ++ G_CALLBACK (on_problem), page); ++ g_signal_connect (user_verifier, "info-query", ++ G_CALLBACK (on_info_query), page); ++ g_signal_connect (user_verifier, "secret-info-query", ++ G_CALLBACK (on_secret_info_query), page); ++ ++ g_signal_connect (greeter, "session-opened", ++ G_CALLBACK (on_session_opened), page); ++ ++ gdm_user_verifier_call_begin_verification_for_user_sync (user_verifier, ++ SERVICE_NAME, ++ act_user_get_user_name (priv->user_account), ++ NULL, &error); ++ ++ if (error != NULL) ++ g_warning ("Could not begin verification: %s", error->message); ++} ++ ++static void ++on_try_button_clicked (GtkButton *button, ++ GisInstallPage *page) ++{ ++ gis_ensure_stamp_files (GIS_PAGE (page)->driver); ++ ++ gis_driver_hide_window (GIS_PAGE (page)->driver); ++ log_user_in (page); ++} ++ ++static void ++on_installer_exited (GPid pid, ++ int exit_status, ++ gpointer user_data) ++{ ++ g_autoptr (GError) error = NULL; ++ g_autoptr(GSubprocessLauncher) launcher = NULL; ++ g_autoptr(GSubprocess) subprocess = NULL; ++ gboolean started_to_reboot; ++ ++ launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_SEARCH_PATH_FROM_ENVP); ++ ++ g_subprocess_launcher_unsetenv (launcher, "SHELL"); ++ ++ subprocess = g_subprocess_launcher_spawn (launcher, &error, "systemctl", "reboot", NULL); ++ ++ if (subprocess == NULL) { ++ g_warning ("Failed to initiate reboot: %s\n", error->message); ++ return; ++ } ++ ++ started_to_reboot = g_subprocess_wait (subprocess, NULL, &error); ++ ++ if (!started_to_reboot) { ++ g_warning ("Failed to reboot: %s\n", error->message); ++ return; ++ } ++} ++ ++static void ++on_installer_started (GDesktopAppInfo *appinfo, ++ GPid pid, ++ gpointer user_data) ++{ ++ g_child_watch_add (pid, on_installer_exited, user_data); ++} ++ ++static void ++run_installer (GisInstallPage *page) ++{ ++ g_autoptr (GError) error = NULL; ++ GisInstallPagePrivate *priv = gis_install_page_get_instance_private (page); ++ gboolean installer_launched; ++ g_autoptr (GAppLaunchContext) launch_context = NULL; ++ g_autofree char *language = NULL; ++ ++ gis_ensure_stamp_files (GIS_PAGE (page)->driver); ++ ++ launch_context = g_app_launch_context_new (); ++ ++ g_app_launch_context_unsetenv (launch_context, "SHELL"); ++ ++ language = cc_common_language_get_current_language (); ++ ++ if (language != NULL) ++ g_app_launch_context_setenv (launch_context, "LANG", language); ++ ++ installer_launched = g_desktop_app_info_launch_uris_as_manager (priv->installer, ++ NULL, ++ launch_context, ++ G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_CHILD_INHERITS_STDERR | G_SPAWN_CHILD_INHERITS_STDOUT | G_SPAWN_SEARCH_PATH, ++ NULL, ++ NULL, ++ on_installer_started, ++ page, ++ &error); ++ ++ if (!installer_launched) ++ g_warning ("Could not launch installer: %s", error->message); ++} ++ ++static void ++on_install_button_clicked (GtkButton *button, ++ GisInstallPage *page) ++{ ++ gis_driver_hide_window (GIS_PAGE (page)->driver); ++ run_installer (page); ++} ++ ++static void ++gis_install_page_shown (GisPage *page) ++{ ++ GisInstallPage *install = GIS_INSTALL_PAGE (page); ++ GisInstallPagePrivate *priv = gis_install_page_get_instance_private (install); ++ g_autoptr(GError) local_error = NULL; ++ ++ if (!gis_driver_save_data (GIS_PAGE (page)->driver, &local_error)) ++ g_warning ("Error saving data: %s", local_error->message); ++ ++ gis_driver_get_user_permissions (GIS_PAGE (page)->driver, ++ &priv->user_account, ++ &priv->user_password); ++ ++ gtk_widget_grab_focus (priv->install_button); ++} ++ ++static void ++update_distro_name (GisInstallPage *page) ++{ ++ GisInstallPagePrivate *priv = gis_install_page_get_instance_private (page); ++ g_autofree char *name = g_get_os_info (G_OS_INFO_KEY_NAME); ++ g_autofree char *text = NULL; ++ ++ if (!name) ++ name = g_strdup ("GNOME"); ++ ++ text = g_strdup (adw_status_page_get_description (priv->status_page)); ++ gis_substitute_word_in_text (&text, "GNOME", name); ++ adw_status_page_set_description (priv->status_page, text); ++ g_clear_pointer (&text, g_free); ++} ++ ++static void ++update_distro_logo (GisInstallPage *page) ++{ ++ GisInstallPagePrivate *priv = gis_install_page_get_instance_private (page); ++ g_autoptr (GtkIconTheme) icon_theme = NULL; ++ g_autofree char *logo_name = g_get_os_info ("LOGO"); ++ g_autoptr(GtkIconPaintable) icon_paintable = NULL; ++ g_autoptr(GPtrArray) array = NULL; ++ g_autoptr(GIcon) icon = NULL; ++ ++ if (logo_name == NULL) ++ logo_name = g_strdup ("gnome-logo"); ++ ++ array = g_ptr_array_new_with_free_func (g_free); ++ g_ptr_array_add (array, (gpointer) g_strdup_printf ("%s-text", logo_name)); ++ g_ptr_array_add (array, (gpointer) g_strdup_printf ("%s", logo_name)); ++ ++ icon = g_themed_icon_new_from_names ((char **) array->pdata, array->len); ++ icon_theme = g_object_ref (gtk_icon_theme_get_for_display (gdk_display_get_default ())); ++ icon_paintable = gtk_icon_theme_lookup_by_gicon (icon_theme, icon, ++ 192, ++ gtk_widget_get_scale_factor (GTK_WIDGET (priv->status_page)), ++ gtk_widget_get_direction (GTK_WIDGET (priv->status_page)), ++ 0); ++ ++ adw_status_page_set_paintable (priv->status_page, GDK_PAINTABLE (icon_paintable)); ++} ++ ++static gboolean ++find_installer (GisInstallPage *page) ++{ ++ GisInstallPagePrivate *priv = gis_install_page_get_instance_private (page); ++ g_autofree char *desktop_file = NULL; ++ ++ desktop_file = gis_driver_conf_get_string (GIS_PAGE (page)->driver, ++ VENDOR_INSTALLER_GROUP, ++ VENDOR_APPLICATION_KEY); ++ ++ if (!desktop_file) ++ return FALSE; ++ ++ priv->installer = g_desktop_app_info_new (desktop_file); ++ ++ return priv->installer != NULL; ++} ++ ++static void ++gis_install_page_constructed (GObject *object) ++{ ++ GisInstallPage *page = GIS_INSTALL_PAGE (object); ++ GisInstallPagePrivate *priv = gis_install_page_get_instance_private (page); ++ ++ G_OBJECT_CLASS (gis_install_page_parent_class)->constructed (object); ++ ++ if (!find_installer (page)) ++ gtk_widget_set_sensitive (priv->install_button, FALSE); ++ ++ update_distro_name (page); ++ update_distro_logo (page); ++ g_signal_connect (priv->try_button, "clicked", G_CALLBACK (on_try_button_clicked), page); ++ g_signal_connect (priv->install_button, "clicked", G_CALLBACK (on_install_button_clicked), page); ++ ++ gis_page_set_complete (GIS_PAGE (page), TRUE); ++ ++ gtk_widget_set_visible (GTK_WIDGET (page), TRUE); ++} ++ ++static void ++gis_install_page_locale_changed (GisPage *page) ++{ ++ gis_page_set_title (page, _("Install or Try?")); ++} ++ ++static void ++gis_install_page_class_init (GisInstallPageClass *klass) ++{ ++ GisPageClass *page_class = GIS_PAGE_CLASS (klass); ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ ++ gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), "/org/gnome/initial-setup/gis-install-page.ui"); ++ ++ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisInstallPage, try_button); ++ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisInstallPage, install_button); ++ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisInstallPage, status_page); ++ ++ page_class->page_id = PAGE_ID; ++ page_class->locale_changed = gis_install_page_locale_changed; ++ page_class->shown = gis_install_page_shown; ++ object_class->constructed = gis_install_page_constructed; ++} ++ ++static void ++gis_install_page_init (GisInstallPage *page) ++{ ++ gtk_widget_init_template (GTK_WIDGET (page)); ++} ++ ++GisPage * ++gis_prepare_install_page (GisDriver *driver) ++{ ++ return g_object_new (GIS_TYPE_INSTALL_PAGE, ++ "driver", driver, ++ NULL); ++} +diff --git a/gnome-initial-setup/pages/install/gis-install-page.h b/gnome-initial-setup/pages/install/gis-install-page.h +new file mode 100644 +index 00000000..292427d8 +--- /dev/null ++++ b/gnome-initial-setup/pages/install/gis-install-page.h +@@ -0,0 +1,52 @@ ++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ ++/* ++ * Copyright (C) 2023 Red Hat ++ * ++ * 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 . ++ */ ++ ++#ifndef __GIS_INSTALL_PAGE_H__ ++#define __GIS_INSTALL_PAGE_H__ ++ ++#include "gnome-initial-setup.h" ++ ++G_BEGIN_DECLS ++ ++#define GIS_TYPE_INSTALL_PAGE (gis_install_page_get_type ()) ++#define GIS_INSTALL_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIS_TYPE_INSTALL_PAGE, GisInstallPage)) ++#define GIS_INSTALL_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIS_TYPE_INSTALL_PAGE, GisInstallPageClass)) ++#define GIS_IS_INSTALL_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIS_TYPE_INSTALL_PAGE)) ++#define GIS_IS_INSTALL_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIS_TYPE_INSTALL_PAGE)) ++#define GIS_INSTALL_PAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIS_TYPE_INSTALL_PAGE, GisInstallPageClass)) ++ ++typedef struct _GisInstallPage GisInstallPage; ++typedef struct _GisInstallPageClass GisInstallPageClass; ++ ++struct _GisInstallPage ++{ ++ GisPage parent; ++}; ++ ++struct _GisInstallPageClass ++{ ++ GisPageClass parent_class; ++}; ++ ++GType gis_install_page_get_type (void); ++ ++GisPage *gis_prepare_install_page (GisDriver *driver); ++ ++G_END_DECLS ++ ++#endif /* __GIS_INSTALL_PAGE_H__ */ +diff --git a/gnome-initial-setup/pages/install/gis-install-page.ui b/gnome-initial-setup/pages/install/gis-install-page.ui +new file mode 100644 +index 00000000..08cfbd6e +--- /dev/null ++++ b/gnome-initial-setup/pages/install/gis-install-page.ui +@@ -0,0 +1,49 @@ ++ ++ ++ ++ +diff --git a/gnome-initial-setup/pages/install/install.gresource.xml b/gnome-initial-setup/pages/install/install.gresource.xml +new file mode 100644 +index 00000000..f1802db5 +--- /dev/null ++++ b/gnome-initial-setup/pages/install/install.gresource.xml +@@ -0,0 +1,7 @@ ++ ++ ++ ++ gis-install-page.ui ++ ++ ++ +diff --git a/gnome-initial-setup/pages/install/meson.build b/gnome-initial-setup/pages/install/meson.build +new file mode 100644 +index 00000000..e5084e5e +--- /dev/null ++++ b/gnome-initial-setup/pages/install/meson.build +@@ -0,0 +1,9 @@ ++sources += gnome.compile_resources( ++ 'install-resources', ++ files('install.gresource.xml'), ++ c_name: 'install' ++) ++sources += files( ++ 'gis-install-page.c', ++ 'gis-install-page.h' ++) +diff --git a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c +index fa41230f..e5556483 100644 +--- a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c ++++ b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c +@@ -168,61 +168,61 @@ set_localed_input (GisKeyboardPage *self) + + static void + change_locale_permission_acquired (GObject *source, + GAsyncResult *res, + gpointer data) + { + GisKeyboardPage *page = GIS_KEYBOARD_PAGE (data); + GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (page); + GError *error = NULL; + gboolean allowed; + + allowed = g_permission_acquire_finish (priv->permission, res, &error); + if (error) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Failed to acquire permission: %s", error->message); + g_error_free (error); + return; + } + + if (allowed) + set_localed_input (GIS_KEYBOARD_PAGE (data)); + } + + static void + update_input (GisKeyboardPage *self) + { + GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self); + + set_input_settings (self); + +- if (gis_driver_get_mode (GIS_PAGE (self)->driver) == GIS_DRIVER_MODE_NEW_USER) { ++ if (gis_driver_get_mode (GIS_PAGE (self)->driver) & GIS_DRIVER_MODE_SYSTEM) { + if (g_permission_get_allowed (priv->permission)) { + set_localed_input (self); + } else if (g_permission_get_can_acquire (priv->permission)) { + g_permission_acquire_async (priv->permission, + NULL, + change_locale_permission_acquired, + self); + } + } + } + + static gboolean + gis_keyboard_page_apply (GisPage *page, + GCancellable *cancellable) + { + update_input (GIS_KEYBOARD_PAGE (page)); + return FALSE; + } + + static GSList * + get_localed_input (GDBusProxy *proxy) + { + GVariant *v; + const gchar *s; + gchar *id; + guint i, n; + gchar **layouts = NULL; + gchar **variants = NULL; + GSList *sources = NULL; + +@@ -461,61 +461,61 @@ input_changed (CcInputChooser *chooser, + + static void + gis_keyboard_page_constructed (GObject *object) + { + GisKeyboardPage *self = GIS_KEYBOARD_PAGE (object); + GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self); + + G_OBJECT_CLASS (gis_keyboard_page_parent_class)->constructed (object); + + g_signal_connect (priv->input_chooser, "confirm", + G_CALLBACK (input_confirmed), self); + g_signal_connect (priv->input_chooser, "changed", + G_CALLBACK (input_changed), self); + + priv->input_settings = g_settings_new (GNOME_DESKTOP_INPUT_SOURCES_DIR); + g_settings_delay (priv->input_settings); + + priv->cancellable = g_cancellable_new (); + + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES, + NULL, + "org.freedesktop.locale1", + "/org/freedesktop/locale1", + "org.freedesktop.locale1", + priv->cancellable, + (GAsyncReadyCallback) localed_proxy_ready, + self); + + /* If we're in new user mode then we're manipulating system settings */ +- if (gis_driver_get_mode (GIS_PAGE (self)->driver) == GIS_DRIVER_MODE_NEW_USER) ++ if (gis_driver_get_mode (GIS_PAGE (self)->driver) & GIS_DRIVER_MODE_SYSTEM) + priv->permission = polkit_permission_new_sync ("org.freedesktop.locale1.set-keyboard", NULL, NULL, NULL); + + update_page_complete (self); + + gtk_widget_set_visible (GTK_WIDGET (self), TRUE); + } + + static void + gis_keyboard_page_locale_changed (GisPage *page) + { + gis_page_set_title (GIS_PAGE (page), _("Typing")); + } + + static void + gis_keyboard_page_class_init (GisKeyboardPageClass * klass) + { + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GisPageClass * page_class = GIS_PAGE_CLASS (klass); + + gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), "/org/gnome/initial-setup/gis-keyboard-page.ui"); + + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisKeyboardPage, input_chooser); + + page_class->page_id = PAGE_ID; + page_class->apply = gis_keyboard_page_apply; + page_class->skip = gis_keyboard_page_skip; + page_class->locale_changed = gis_keyboard_page_locale_changed; + object_class->constructed = gis_keyboard_page_constructed; + object_class->finalize = gis_keyboard_page_finalize; + } +diff --git a/gnome-initial-setup/pages/language/gis-language-page.c b/gnome-initial-setup/pages/language/gis-language-page.c +index 87b9f2d8..f7931c46 100644 +--- a/gnome-initial-setup/pages/language/gis-language-page.c ++++ b/gnome-initial-setup/pages/language/gis-language-page.c +@@ -99,61 +99,61 @@ change_locale_permission_acquired (GObject *source, + } + + static void + user_loaded (GObject *object, + GParamSpec *pspec, + gpointer user_data) + { + gchar *new_locale_id = user_data; + + act_user_set_language (ACT_USER (object), new_locale_id); + + g_free (new_locale_id); + } + + static void + language_changed (CcLanguageChooser *chooser, + GParamSpec *pspec, + GisLanguagePage *page) + { + GisLanguagePagePrivate *priv = gis_language_page_get_instance_private (page); + GisDriver *driver; + GSettings *region_settings; + ActUser *user; + + priv->new_locale_id = cc_language_chooser_get_language (chooser); + driver = GIS_PAGE (page)->driver; + + gis_driver_set_user_language (driver, priv->new_locale_id, TRUE); + gtk_widget_set_default_direction (gtk_get_locale_direction ()); + +- if (gis_driver_get_mode (driver) == GIS_DRIVER_MODE_NEW_USER) { ++ if (gis_driver_get_mode (driver) & GIS_DRIVER_MODE_SYSTEM) { + if (g_permission_get_allowed (priv->permission)) { + set_localed_locale (page); + } + else if (g_permission_get_can_acquire (priv->permission)) { + g_permission_acquire_async (priv->permission, + NULL, + change_locale_permission_acquired, + page); + } + } + + /* Ensure we won't override the selected language for format strings */ + region_settings = g_settings_new (GNOME_SYSTEM_LOCALE_DIR); + g_settings_reset (region_settings, REGION_KEY); + g_object_unref (region_settings); + + user = act_user_manager_get_user (act_user_manager_get_default (), + g_get_user_name ()); + if (act_user_is_loaded (user)) + act_user_set_language (user, priv->new_locale_id); + else + g_signal_connect (user, + "notify::is-loaded", + G_CALLBACK (user_loaded), + g_strdup (priv->new_locale_id)); + + gis_welcome_widget_show_locale (GIS_WELCOME_WIDGET (priv->welcome_widget), + priv->new_locale_id); + } + +@@ -207,62 +207,61 @@ update_distro_logo (GisLanguagePage *page) + break; + } + } + } + + static void + language_confirmed (CcLanguageChooser *chooser, + GisLanguagePage *page) + { + gis_assistant_next_page (gis_driver_get_assistant (GIS_PAGE (page)->driver)); + } + + static void + gis_language_page_constructed (GObject *object) + { + GisLanguagePage *page = GIS_LANGUAGE_PAGE (object); + GisLanguagePagePrivate *priv = gis_language_page_get_instance_private (page); + GDBusConnection *bus; + + g_type_ensure (CC_TYPE_LANGUAGE_CHOOSER); + + G_OBJECT_CLASS (gis_language_page_parent_class)->constructed (object); + + update_distro_logo (page); + + g_signal_connect (priv->language_chooser, "notify::language", + G_CALLBACK (language_changed), page); + g_signal_connect (priv->language_chooser, "confirm", + G_CALLBACK (language_confirmed), page); + +- /* If we're in new user mode then we're manipulating system settings */ +- if (gis_driver_get_mode (GIS_PAGE (page)->driver) == GIS_DRIVER_MODE_NEW_USER) ++ if (gis_driver_get_mode (GIS_PAGE (page)->driver) & GIS_DRIVER_MODE_SYSTEM) + { + priv->permission = polkit_permission_new_sync ("org.freedesktop.locale1.set-locale", NULL, NULL, NULL); + + bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL); + g_dbus_proxy_new (bus, + G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES, + NULL, + "org.freedesktop.locale1", + "/org/freedesktop/locale1", + "org.freedesktop.locale1", + priv->cancellable, + (GAsyncReadyCallback) localed_proxy_ready, + object); + g_object_unref (bus); + } + + gis_page_set_complete (GIS_PAGE (page), TRUE); + gtk_widget_set_visible (GTK_WIDGET (page), TRUE); + } + + static void + gis_language_page_locale_changed (GisPage *page) + { + gis_page_set_title (GIS_PAGE (page), _("Welcome")); + } + + static void + gis_language_page_dispose (GObject *object) + { + GisLanguagePage *page = GIS_LANGUAGE_PAGE (object); +diff --git a/gnome-initial-setup/pages/meson.build b/gnome-initial-setup/pages/meson.build +index 32305018..5ac4a80d 100644 +--- a/gnome-initial-setup/pages/meson.build ++++ b/gnome-initial-setup/pages/meson.build +@@ -1,21 +1,22 @@ + pages = [ + 'account', ++ 'install', + 'language', + 'keyboard', + 'network', + 'timezone', + 'privacy', + 'goa', + 'password', + 'software', + 'summary', + 'welcome', + ] + + if libmalcontent_dep.found() and libmalcontent_ui_dep.found() + pages += 'parental-controls' + endif + + foreach page: pages + subdir (page) + endforeach +diff --git a/gnome-initial-setup/pages/password/gis-password-page.c b/gnome-initial-setup/pages/password/gis-password-page.c +index 6c12ca38..3d648c48 100644 +--- a/gnome-initial-setup/pages/password/gis-password-page.c ++++ b/gnome-initial-setup/pages/password/gis-password-page.c +@@ -464,47 +464,53 @@ gis_password_page_class_init (GisPasswordPageClass *klass) + * + * If %FALSE (the default), this page will collect a password for the main + * user account. If %TRUE, it will collect a password for controlling access + * to parental controls — this will affect where the password is stored, and + * the appearance of the page. + * + * Since: 3.36 + */ + obj_props[PROP_PARENT_MODE] = + g_param_spec_boolean ("parent-mode", "Parent Mode", + "Whether to collect a password for the main user account or a parent account.", + FALSE, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + + g_object_class_install_properties (object_class, G_N_ELEMENTS (obj_props), obj_props); + + gis_add_style_from_resource ("/org/gnome/initial-setup/gis-password-page.css"); + } + + static void + gis_password_page_init (GisPasswordPage *page) + { + g_type_ensure (GIS_TYPE_PAGE_HEADER); + + gtk_widget_init_template (GTK_WIDGET (page)); + } + + GisPage * + gis_prepare_password_page (GisDriver *driver) + { ++ GisDriverMode driver_mode; ++ ++ driver_mode = gis_driver_get_mode (driver); ++ if (driver_mode == GIS_DRIVER_MODE_LIVE_USER && !gis_kernel_command_line_has_argument ((const char *[]) { "rd.live.overlay", NULL })) ++ return NULL; ++ + return g_object_new (GIS_TYPE_PASSWORD_PAGE, + "driver", driver, + NULL); + } + + GisPage * + gis_prepare_parent_password_page (GisDriver *driver) + { + /* Skip prompting for the parent password if parental controls aren’t enabled. */ + if (!gis_driver_get_parental_controls_enabled (driver)) + return NULL; + + return g_object_new (GIS_TYPE_PASSWORD_PAGE, + "driver", driver, + "parent-mode", TRUE, + NULL); + } +-- +2.41.0.rc2 + diff --git a/0004-gnome-initial-setup-Add-OEM-mode.patch b/0004-gnome-initial-setup-Add-OEM-mode.patch new file mode 100644 index 0000000..a0ce494 --- /dev/null +++ b/0004-gnome-initial-setup-Add-OEM-mode.patch @@ -0,0 +1,350 @@ +From 17d62c9450b38fa492c245ec3da8ebd5f9a001dc Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Sun, 13 Aug 2023 10:30:11 -0400 +Subject: [PATCH 4/5] gnome-initial-setup: Add OEM mode + +OEM mode is designed to get run when a site has imaged a bunch +of machines, and handed them off to the user. + +In this scenario, the user never saw the installer, so we need to +ask the user questions that the installer would have normally +asked them already. + +For example, the installer likely asks language, but if the user +never saw the installer, then we need to ask language again. +--- + gnome-initial-setup/gis-driver.c | 1 + + gnome-initial-setup/gis-driver.h | 5 +++-- + gnome-initial-setup/gnome-initial-setup.c | 22 ++++++++++++++-------- + 3 files changed, 18 insertions(+), 10 deletions(-) + +diff --git a/gnome-initial-setup/gis-driver.c b/gnome-initial-setup/gis-driver.c +index 75ceac76..d7a69fe1 100644 +--- a/gnome-initial-setup/gis-driver.c ++++ b/gnome-initial-setup/gis-driver.c +@@ -20,60 +20,61 @@ + */ + + #include "config.h" + + #include "gnome-initial-setup.h" + + #include + #include + #include + + #ifdef HAVE_WEBKITGTK_6_0 + #include + #else + #include + #endif + + #include "cc-common-language.h" + #include "gis-assistant.h" + + /* Statically include this for now. Maybe later + * we'll generate this from glib-mkenums. */ + GType + gis_driver_mode_get_type (void) { + static GType enum_type_id = 0; + if (G_UNLIKELY (!enum_type_id)) + { + static const GFlagsValue values[] = { + { GIS_DRIVER_MODE_NEW_USER, "GIS_DRIVER_MODE_NEW_USER", "new_user" }, + { GIS_DRIVER_MODE_EXISTING_USER, "GIS_DRIVER_MODE_EXISTING_USER", "existing_user" }, + { GIS_DRIVER_MODE_LIVE_USER, "GIS_DRIVER_MODE_LIVE_USER", "live_user" }, ++ { GIS_DRIVER_MODE_OEM, "GIS_DRIVER_MODE_OEM", "oem" }, + { GIS_DRIVER_MODE_SYSTEM, "GIS_DRIVER_MODE_SYSTEM", "system" }, + { GIS_DRIVER_MODE_ALL, "GIS_DRIVER_MODE_ALL", "all" }, + { 0, NULL, NULL } + }; + enum_type_id = g_flags_register_static("GisDriverMode", values); + } + return enum_type_id; + } + + enum { + REBUILD_PAGES, + LOCALE_CHANGED, + LAST_SIGNAL, + }; + + static guint signals[LAST_SIGNAL]; + + typedef enum { + PROP_MODE = 1, + PROP_USERNAME, + PROP_SMALL_SCREEN, + PROP_PARENTAL_CONTROLS_ENABLED, + PROP_FULL_NAME, + PROP_AVATAR, + } GisDriverProperty; + + static GParamSpec *obj_props[PROP_AVATAR + 1]; + + struct _GisDriver { + AdwApplication parent_instance; +diff --git a/gnome-initial-setup/gis-driver.h b/gnome-initial-setup/gis-driver.h +index aedb9a73..49639bef 100644 +--- a/gnome-initial-setup/gis-driver.h ++++ b/gnome-initial-setup/gis-driver.h +@@ -18,62 +18,63 @@ + * Written by: + * Jasper St. Pierre + */ + + #ifndef __GIS_DRIVER_H__ + #define __GIS_DRIVER_H__ + + #include "gis-assistant.h" + #include "gis-page.h" + #include + #include + #include + + G_BEGIN_DECLS + + #define GIS_TYPE_DRIVER (gis_driver_get_type ()) + #define GIS_TYPE_DRIVER_MODE (gis_driver_mode_get_type ()) + + G_DECLARE_FINAL_TYPE (GisDriver, gis_driver, GIS, DRIVER, AdwApplication) + + typedef enum { + UM_LOCAL, + UM_ENTERPRISE, + NUM_MODES, + } UmAccountMode; + + typedef enum { + GIS_DRIVER_MODE_NEW_USER = 1 << 0, + GIS_DRIVER_MODE_EXISTING_USER = 1 << 1, + GIS_DRIVER_MODE_LIVE_USER = 1 << 2, +- GIS_DRIVER_MODE_SYSTEM = (GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_LIVE_USER), +- GIS_DRIVER_MODE_ALL = (GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER | GIS_DRIVER_MODE_LIVE_USER), ++ GIS_DRIVER_MODE_OEM = 1 << 3, ++ GIS_DRIVER_MODE_SYSTEM = (GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_LIVE_USER | GIS_DRIVER_MODE_OEM), ++ GIS_DRIVER_MODE_ALL = (GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER | GIS_DRIVER_MODE_LIVE_USER | GIS_DRIVER_MODE_OEM), + } GisDriverMode; + + GType gis_driver_mode_get_type (void); + + GisAssistant *gis_driver_get_assistant (GisDriver *driver); + + void gis_driver_set_user_permissions (GisDriver *driver, + ActUser *user, + const gchar *password); + + void gis_driver_get_user_permissions (GisDriver *driver, + ActUser **user, + const gchar **password); + + void gis_driver_set_parent_permissions (GisDriver *driver, + ActUser *parent, + const gchar *password); + + void gis_driver_get_parent_permissions (GisDriver *driver, + ActUser **parent, + const gchar **password); + + void gis_driver_set_account_mode (GisDriver *driver, + UmAccountMode mode); + + UmAccountMode gis_driver_get_account_mode (GisDriver *driver); + + void gis_driver_set_parental_controls_enabled (GisDriver *driver, + gboolean parental_controls_enabled); + +diff --git a/gnome-initial-setup/gnome-initial-setup.c b/gnome-initial-setup/gnome-initial-setup.c +index 68f00f47..3e4c2c03 100644 +--- a/gnome-initial-setup/gnome-initial-setup.c ++++ b/gnome-initial-setup/gnome-initial-setup.c +@@ -24,90 +24,91 @@ + #include "gnome-initial-setup.h" + + #include + #include + #include + #include + #include + + #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" + #include "pages/install/gis-install-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 VENDOR_LIVE_USER_ONLY_KEY "live_user_only" + + static gboolean force_existing_user_mode; + static gboolean force_live_user_mode; ++static gboolean force_oem_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_ALL), +- PAGE (privacy, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER), ++ PAGE (privacy, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER | GIS_DRIVER_MODE_OEM), + PAGE (timezone, GIS_DRIVER_MODE_ALL), +- 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 (software, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER | GIS_DRIVER_MODE_OEM), ++ PAGE (goa, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER | GIS_DRIVER_MODE_OEM), + /* In live user mode, the account page isn't displayed, it just quietly creates the live user */ +- PAGE (account, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_LIVE_USER), +- PAGE (password, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_LIVE_USER), ++ PAGE (account, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_LIVE_USER | GIS_DRIVER_MODE_OEM), ++ PAGE (password, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_LIVE_USER | GIS_DRIVER_MODE_OEM), + #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), ++ PAGE (parental_controls, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER | GIS_DRIVER_MODE_OEM), ++ PAGE (parent_password, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER | GIS_DRIVER_MODE_OEM), + #endif +- PAGE (summary, GIS_DRIVER_MODE_NEW_USER), ++ PAGE (summary, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_OEM), + PAGE (install, GIS_DRIVER_MODE_LIVE_USER), + { NULL }, + }; + + #undef PAGE + + static gboolean + should_skip_page (const gchar *page_id, + gchar **skip_pages) + { + 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; + } + +@@ -292,81 +293,86 @@ static gboolean + initial_setup_disabled_by_anaconda (void) + { + const gchar *file_name = SYSCONFDIR "/sysconfig/anaconda"; + g_autoptr(GError) error = NULL; + g_autoptr(GKeyFile) key_file = g_key_file_new (); + + if (!g_key_file_load_from_file (key_file, file_name, G_KEY_FILE_NONE, &error)) { + if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT) && + !g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)) { + g_warning ("Could not read %s: %s", file_name, error->message); + } + return FALSE; + } + + return g_key_file_get_boolean (key_file, "General", "post_install_tools_disabled", NULL); + } + + int + main (int argc, char *argv[]) + { + GisDriver *driver; + int status; + GOptionContext *context; + GisDriverMode mode; + + GOptionEntry entries[] = { + { "existing-user", 0, 0, G_OPTION_ARG_NONE, &force_existing_user_mode, + _("Force existing user mode"), NULL }, + { "live-user", 0, 0, G_OPTION_ARG_NONE, &force_live_user_mode, + _("Force live user mode"), NULL }, ++ { "oem", 0, 0, G_OPTION_ARG_NONE, &force_oem_mode, ++ _("Force OEM mode"), NULL }, + { NULL } + }; + + g_unsetenv ("GIO_USE_VFS"); + + /* By default, libadwaita reads settings from the Settings portal, which causes + * the portal to be started, which causes gnome-keyring to be started. This + * interferes with our attempt below to manually start gnome-keyring and set + * the login keyring password to a well-known value, which we overwrite with + * the user's password once they choose one. + */ + g_setenv ("ADW_DISABLE_PORTAL", "1", TRUE); + + context = g_option_context_new (_("— GNOME initial setup")); + g_option_context_add_main_entries (context, entries, NULL); + + g_option_context_parse (context, &argc, &argv, NULL); + + if (gis_kernel_command_line_has_argument ((const char *[]) { "rd.live.image", "endless.live_boot", NULL })) + force_live_user_mode = TRUE; + ++ if (gis_kernel_command_line_has_argument ((const char *[]) { "gnome.oem_mode", NULL })) ++ force_oem_mode = TRUE; ++ + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + g_message ("Starting gnome-initial-setup"); + if (gis_get_mock_mode ()) + g_message ("Mock mode: changes will not be saved to disk"); + else + g_message ("Production mode: changes will be saved to disk"); + + skipped_pages = g_ptr_array_new_with_free_func (destroy_page); + mode = get_mode (); + + /* When we are running as the gnome-initial-setup user we + * dont have a normal user session and need to initialize + * the keyring manually so that we can pass the credentials + * along to the new user in the handoff. + */ + if (mode == GIS_DRIVER_MODE_NEW_USER && !gis_get_mock_mode ()) + gis_ensure_login_keyring (); + + driver = gis_driver_new (mode); + adw_style_manager_set_color_scheme (adw_style_manager_get_default (), + ADW_COLOR_SCHEME_PREFER_LIGHT); + + /* On first login, GNOME Shell offers to run a tour. If we also run Initial + * Setup, the two immovable, centred windows will sit atop one another. + * Until we have the ability to run Initial Setup in the "kiosk" mode, like + * it does in new-user mode, disable Initial Setup for existing users. + * +-- +2.41.0 + diff --git a/0004-initial-setup-Don-t-show-duplicated-pages-between-mo.patch b/0004-initial-setup-Don-t-show-duplicated-pages-between-mo.patch new file mode 100644 index 0000000..310fc8a --- /dev/null +++ b/0004-initial-setup-Don-t-show-duplicated-pages-between-mo.patch @@ -0,0 +1,535 @@ +From c03f853333a5c5ddb4d3549b7aa34a474b1aed42 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 16 Aug 2023 10:47:13 -0400 +Subject: [PATCH 4/7] 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 b18a3808..41cd6e38 100644 +--- a/gnome-initial-setup/gis-driver.c ++++ b/gnome-initial-setup/gis-driver.c +@@ -80,90 +80,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 bc7a4ee9..78c62cd8 100644 +--- a/gnome-initial-setup/gnome-initial-setup.c ++++ b/gnome-initial-setup/gnome-initial-setup.c +@@ -22,60 +22,62 @@ + #include "config.h" + + #include "gnome-initial-setup.h" + + #include + #include + #include + #include + #include + + #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" + #include "pages/install/gis-install-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 VENDOR_LIVE_USER_ONLY_KEY "live_user_only" + ++#define STATE_FILE GIS_WORKING_DIR "/state" ++ + static gboolean force_existing_user_mode; + static gboolean force_live_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_ALL), + PAGE (privacy, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER), + PAGE (timezone, GIS_DRIVER_MODE_ALL), + PAGE (software, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER), + PAGE (goa, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER), + /* In live user mode, the account page isn't displayed, it just quietly creates the live user */ + PAGE (account, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_LIVE_USER), + PAGE (password, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_LIVE_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), +@@ -94,60 +96,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 for modes other than new_user + * - existing_user_only (optional): list of pages to be skipped for modes other than existing_user + * + * 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) + { +@@ -162,60 +165,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); +@@ -371,59 +394,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 + * shouldn’t 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 + diff --git a/0005-keyboard-Don-t-add-default-input-sources-if-input-so.patch b/0005-keyboard-Don-t-add-default-input-sources-if-input-so.patch new file mode 100644 index 0000000..ffc1a55 --- /dev/null +++ b/0005-keyboard-Don-t-add-default-input-sources-if-input-so.patch @@ -0,0 +1,96 @@ +From b06e6e46b11e8e3ad16212d05ae24c3ff3184a3c Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Sat, 19 Aug 2023 15:31:03 -0400 +Subject: [PATCH 5/7] keyboard: Don't add default input sources if input + sources already set + +If the keyboard page gets skipped, it adds default input sources derived +from the users locale. + +That is all fine and good, but may stomp on existiing site or distro configuration. + +This commit checks for that kind of thing first, and skips adding the +default in that case. +--- + gnome-initial-setup/pages/keyboard/gis-keyboard-page.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c +index e5556483..2448cf2f 100644 +--- a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c ++++ b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c +@@ -258,62 +258,71 @@ get_localed_input (GDBusProxy *proxy) + + g_strfreev (variants); + g_strfreev (layouts); + + return sources; + } + + static void + add_default_keyboard_layout (GDBusProxy *proxy, + GVariantBuilder *builder) + { + GSList *sources = get_localed_input (proxy); + sources = g_slist_reverse (sources); + + for (; sources; sources = sources->next) + g_variant_builder_add (builder, "(ss)", "xkb", + (const gchar *) sources->data); + + g_slist_free_full (sources, g_free); + } + + static void + add_default_input_sources (GisKeyboardPage *self, + GDBusProxy *proxy) + { + const gchar *type; + const gchar *id; + gchar *language; + GVariantBuilder builder; + GSettings *input_settings; ++ g_autoptr(GVariant) default_value = NULL; ++ g_autoptr(GVariant) value = NULL; + + input_settings = g_settings_new (GNOME_DESKTOP_INPUT_SOURCES_DIR); ++ ++ default_value = g_settings_get_default_value (input_settings, KEY_INPUT_SOURCES); ++ value = g_settings_get_value (input_settings, KEY_INPUT_SOURCES); ++ ++ if (!g_variant_equal (default_value, value)) ++ return; ++ + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ss)")); + + add_default_keyboard_layout (proxy, &builder); + + /* add other input sources */ + language = cc_common_language_get_current_language (); + if (gnome_get_input_source_from_locale (language, &type, &id)) { + if (!g_str_equal (type, "xkb")) + g_variant_builder_add (&builder, "(ss)", type, id); + } + g_free (language); + + g_settings_delay (input_settings); + g_settings_set_value (input_settings, KEY_INPUT_SOURCES, g_variant_builder_end (&builder)); + g_settings_set_uint (input_settings, KEY_CURRENT_INPUT_SOURCE, 0); + g_settings_apply (input_settings); + + g_object_unref (input_settings); + } + + static void + skip_proxy_ready (GObject *source, + GAsyncResult *res, + gpointer data) + { + GisKeyboardPage *self = data; + GDBusProxy *proxy; + GError *error = NULL; + + proxy = g_dbus_proxy_new_finish (res, &error); +-- +2.41.0.rc2 + diff --git a/0005-polkit-Add-fedora-specfic-rules.patch b/0005-polkit-Add-fedora-specfic-rules.patch new file mode 100644 index 0000000..dc6bf89 --- /dev/null +++ b/0005-polkit-Add-fedora-specfic-rules.patch @@ -0,0 +1,51 @@ +From fef99a9707d53c3c8f82260994793623f61246aa Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Sun, 13 Aug 2023 16:33:49 -0400 +Subject: [PATCH 5/5] polkit: Add fedora specfic rules + +We should probably add some way to check vendor.conf for the policy +updates instead of a hardcoded list. +--- + data/20-gnome-initial-setup.rules.in | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/data/20-gnome-initial-setup.rules.in b/data/20-gnome-initial-setup.rules.in +index 881efde9..f5b7d981 100644 +--- a/data/20-gnome-initial-setup.rules.in ++++ b/data/20-gnome-initial-setup.rules.in +@@ -1,30 +1,31 @@ + // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + // + // DO NOT EDIT THIS FILE, it will be overwritten on update. + // + // Allow the gnome-initial-setup user to do certain actions without + // being interrupted by password dialogs + + polkit.addRule(function(action, subject) { + if (subject.user !== 'gnome-initial-setup') + return undefined; + + var actionMatches = (action.id.indexOf('org.freedesktop.hostname1.') === 0 || + action.id.indexOf('org.freedesktop.NetworkManager.') === 0 || + action.id.indexOf('org.freedesktop.locale1.') === 0 || + action.id.indexOf('org.freedesktop.accounts.') === 0 || + action.id.indexOf('org.freedesktop.timedate1.') === 0 || + action.id.indexOf('org.freedesktop.realmd.') === 0 || + action.id.indexOf('com.endlessm.ParentalControls.') === 0 || + action.id.indexOf('org.fedoraproject.thirdparty.') === 0 || +- action.id.indexOf('org.freedesktop.login1.reboot') === 0); ++ action.id.indexOf('org.freedesktop.login1.reboot') === 0 || ++ action.id.indexOf('org.fedoraproject.pkexec.liveinst') === 0); + + if (actionMatches) { + if (subject.local) + return 'yes'; + else + return 'auth_admin'; + } + + return undefined; + }); +-- +2.41.0 + diff --git a/0006-gnome-initial-setup-Add-OEM-mode.patch b/0006-gnome-initial-setup-Add-OEM-mode.patch new file mode 100644 index 0000000..078bb3c --- /dev/null +++ b/0006-gnome-initial-setup-Add-OEM-mode.patch @@ -0,0 +1,350 @@ +From 4f2aa7291f77b75efd7ec3a1a36371dc1a2bd7e9 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Sun, 13 Aug 2023 10:30:11 -0400 +Subject: [PATCH 6/7] gnome-initial-setup: Add OEM mode + +OEM mode is designed to get run when a site has imaged a bunch +of machines, and handed them off to the user. + +In this scenario, the user never saw the installer, so we need to +ask the user questions that the installer would have normally +asked them already. + +For example, the installer likely asks language, but if the user +never saw the installer, then we need to ask language again. +--- + gnome-initial-setup/gis-driver.c | 1 + + gnome-initial-setup/gis-driver.h | 5 +++-- + gnome-initial-setup/gnome-initial-setup.c | 22 ++++++++++++++-------- + 3 files changed, 18 insertions(+), 10 deletions(-) + +diff --git a/gnome-initial-setup/gis-driver.c b/gnome-initial-setup/gis-driver.c +index 41cd6e38..55b2493c 100644 +--- a/gnome-initial-setup/gis-driver.c ++++ b/gnome-initial-setup/gis-driver.c +@@ -20,60 +20,61 @@ + */ + + #include "config.h" + + #include "gnome-initial-setup.h" + + #include + #include + #include + + #ifdef HAVE_WEBKITGTK_6_0 + #include + #else + #include + #endif + + #include "cc-common-language.h" + #include "gis-assistant.h" + + /* Statically include this for now. Maybe later + * we'll generate this from glib-mkenums. */ + GType + gis_driver_mode_get_type (void) { + static GType enum_type_id = 0; + if (G_UNLIKELY (!enum_type_id)) + { + static const GFlagsValue values[] = { + { GIS_DRIVER_MODE_NEW_USER, "GIS_DRIVER_MODE_NEW_USER", "new_user" }, + { GIS_DRIVER_MODE_EXISTING_USER, "GIS_DRIVER_MODE_EXISTING_USER", "existing_user" }, + { GIS_DRIVER_MODE_LIVE_USER, "GIS_DRIVER_MODE_LIVE_USER", "live_user" }, ++ { GIS_DRIVER_MODE_OEM, "GIS_DRIVER_MODE_OEM", "oem" }, + { GIS_DRIVER_MODE_SYSTEM, "GIS_DRIVER_MODE_SYSTEM", "system" }, + { GIS_DRIVER_MODE_ALL, "GIS_DRIVER_MODE_ALL", "all" }, + { 0, NULL, NULL } + }; + enum_type_id = g_flags_register_static("GisDriverMode", values); + } + return enum_type_id; + } + + enum { + REBUILD_PAGES, + LOCALE_CHANGED, + LAST_SIGNAL, + }; + + static guint signals[LAST_SIGNAL]; + + typedef enum { + PROP_MODE = 1, + PROP_USERNAME, + PROP_SMALL_SCREEN, + PROP_PARENTAL_CONTROLS_ENABLED, + PROP_FULL_NAME, + PROP_AVATAR, + } GisDriverProperty; + + static GParamSpec *obj_props[PROP_AVATAR + 1]; + + struct _GisDriver { + AdwApplication parent_instance; +diff --git a/gnome-initial-setup/gis-driver.h b/gnome-initial-setup/gis-driver.h +index aedb9a73..49639bef 100644 +--- a/gnome-initial-setup/gis-driver.h ++++ b/gnome-initial-setup/gis-driver.h +@@ -18,62 +18,63 @@ + * Written by: + * Jasper St. Pierre + */ + + #ifndef __GIS_DRIVER_H__ + #define __GIS_DRIVER_H__ + + #include "gis-assistant.h" + #include "gis-page.h" + #include + #include + #include + + G_BEGIN_DECLS + + #define GIS_TYPE_DRIVER (gis_driver_get_type ()) + #define GIS_TYPE_DRIVER_MODE (gis_driver_mode_get_type ()) + + G_DECLARE_FINAL_TYPE (GisDriver, gis_driver, GIS, DRIVER, AdwApplication) + + typedef enum { + UM_LOCAL, + UM_ENTERPRISE, + NUM_MODES, + } UmAccountMode; + + typedef enum { + GIS_DRIVER_MODE_NEW_USER = 1 << 0, + GIS_DRIVER_MODE_EXISTING_USER = 1 << 1, + GIS_DRIVER_MODE_LIVE_USER = 1 << 2, +- GIS_DRIVER_MODE_SYSTEM = (GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_LIVE_USER), +- GIS_DRIVER_MODE_ALL = (GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER | GIS_DRIVER_MODE_LIVE_USER), ++ GIS_DRIVER_MODE_OEM = 1 << 3, ++ GIS_DRIVER_MODE_SYSTEM = (GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_LIVE_USER | GIS_DRIVER_MODE_OEM), ++ GIS_DRIVER_MODE_ALL = (GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER | GIS_DRIVER_MODE_LIVE_USER | GIS_DRIVER_MODE_OEM), + } GisDriverMode; + + GType gis_driver_mode_get_type (void); + + GisAssistant *gis_driver_get_assistant (GisDriver *driver); + + void gis_driver_set_user_permissions (GisDriver *driver, + ActUser *user, + const gchar *password); + + void gis_driver_get_user_permissions (GisDriver *driver, + ActUser **user, + const gchar **password); + + void gis_driver_set_parent_permissions (GisDriver *driver, + ActUser *parent, + const gchar *password); + + void gis_driver_get_parent_permissions (GisDriver *driver, + ActUser **parent, + const gchar **password); + + void gis_driver_set_account_mode (GisDriver *driver, + UmAccountMode mode); + + UmAccountMode gis_driver_get_account_mode (GisDriver *driver); + + void gis_driver_set_parental_controls_enabled (GisDriver *driver, + gboolean parental_controls_enabled); + +diff --git a/gnome-initial-setup/gnome-initial-setup.c b/gnome-initial-setup/gnome-initial-setup.c +index 78c62cd8..d9e9ca6e 100644 +--- a/gnome-initial-setup/gnome-initial-setup.c ++++ b/gnome-initial-setup/gnome-initial-setup.c +@@ -26,90 +26,91 @@ + #include + #include + #include + #include + #include + + #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" + #include "pages/install/gis-install-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 VENDOR_LIVE_USER_ONLY_KEY "live_user_only" + + #define STATE_FILE GIS_WORKING_DIR "/state" + + static gboolean force_existing_user_mode; + static gboolean force_live_user_mode; ++static gboolean force_oem_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_ALL), +- PAGE (privacy, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER), ++ PAGE (privacy, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER | GIS_DRIVER_MODE_OEM), + PAGE (timezone, GIS_DRIVER_MODE_ALL), +- 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 (software, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER | GIS_DRIVER_MODE_OEM), ++ PAGE (goa, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER | GIS_DRIVER_MODE_OEM), + /* In live user mode, the account page isn't displayed, it just quietly creates the live user */ +- PAGE (account, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_LIVE_USER), +- PAGE (password, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_LIVE_USER), ++ PAGE (account, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_LIVE_USER | GIS_DRIVER_MODE_OEM), ++ PAGE (password, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_LIVE_USER | GIS_DRIVER_MODE_OEM), + #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), ++ PAGE (parental_controls, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER | GIS_DRIVER_MODE_OEM), ++ PAGE (parent_password, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_EXISTING_USER | GIS_DRIVER_MODE_OEM), + #endif +- PAGE (summary, GIS_DRIVER_MODE_NEW_USER), ++ PAGE (summary, GIS_DRIVER_MODE_NEW_USER | GIS_DRIVER_MODE_OEM), + PAGE (install, GIS_DRIVER_MODE_LIVE_USER), + { NULL }, + }; + + #undef PAGE + + static gboolean + should_skip_page (const gchar *page_id, + gchar **skip_pages) + { + 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; + } + +@@ -315,81 +316,86 @@ static gboolean + initial_setup_disabled_by_anaconda (void) + { + const gchar *file_name = SYSCONFDIR "/sysconfig/anaconda"; + g_autoptr(GError) error = NULL; + g_autoptr(GKeyFile) key_file = g_key_file_new (); + + if (!g_key_file_load_from_file (key_file, file_name, G_KEY_FILE_NONE, &error)) { + if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT) && + !g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)) { + g_warning ("Could not read %s: %s", file_name, error->message); + } + return FALSE; + } + + return g_key_file_get_boolean (key_file, "General", "post_install_tools_disabled", NULL); + } + + int + main (int argc, char *argv[]) + { + GisDriver *driver; + int status; + GOptionContext *context; + GisDriverMode mode; + + GOptionEntry entries[] = { + { "existing-user", 0, 0, G_OPTION_ARG_NONE, &force_existing_user_mode, + _("Force existing user mode"), NULL }, + { "live-user", 0, 0, G_OPTION_ARG_NONE, &force_live_user_mode, + _("Force live user mode"), NULL }, ++ { "oem", 0, 0, G_OPTION_ARG_NONE, &force_oem_mode, ++ _("Force OEM mode"), NULL }, + { NULL } + }; + + g_unsetenv ("GIO_USE_VFS"); + + /* By default, libadwaita reads settings from the Settings portal, which causes + * the portal to be started, which causes gnome-keyring to be started. This + * interferes with our attempt below to manually start gnome-keyring and set + * the login keyring password to a well-known value, which we overwrite with + * the user's password once they choose one. + */ + g_setenv ("ADW_DISABLE_PORTAL", "1", TRUE); + + context = g_option_context_new (_("— GNOME initial setup")); + g_option_context_add_main_entries (context, entries, NULL); + + g_option_context_parse (context, &argc, &argv, NULL); + + if (gis_kernel_command_line_has_argument ((const char *[]) { "rd.live.image", "endless.live_boot", NULL })) + force_live_user_mode = TRUE; + ++ if (gis_kernel_command_line_has_argument ((const char *[]) { "gnome.oem_mode", NULL })) ++ force_oem_mode = TRUE; ++ + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + g_message ("Starting gnome-initial-setup"); + if (gis_get_mock_mode ()) + g_message ("Mock mode: changes will not be saved to disk"); + else + g_message ("Production mode: changes will be saved to disk"); + + skipped_pages = g_ptr_array_new_with_free_func (destroy_page); + mode = get_mode (); + + /* When we are running as the gnome-initial-setup user we + * dont have a normal user session and need to initialize + * the keyring manually so that we can pass the credentials + * along to the new user in the handoff. + */ + if ((mode & GIS_DRIVER_MODE_SYSTEM) && !gis_get_mock_mode ()) + gis_ensure_login_keyring (); + + driver = gis_driver_new (mode); + adw_style_manager_set_color_scheme (adw_style_manager_get_default (), + ADW_COLOR_SCHEME_PREFER_LIGHT); + + /* On first login, GNOME Shell offers to run a tour. If we also run Initial + * Setup, the two immovable, centred windows will sit atop one another. + * Until we have the ability to run Initial Setup in the "kiosk" mode, like + * it does in new-user mode, disable Initial Setup for existing users. + * +-- +2.41.0.rc2 + diff --git a/0007-polkit-Add-fedora-specfic-rules.patch b/0007-polkit-Add-fedora-specfic-rules.patch new file mode 100644 index 0000000..60c1241 --- /dev/null +++ b/0007-polkit-Add-fedora-specfic-rules.patch @@ -0,0 +1,51 @@ +From 5551d61c2bef3b607b9cb2a3058d97ce5afc1c03 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Sun, 13 Aug 2023 16:33:49 -0400 +Subject: [PATCH 7/7] polkit: Add fedora specfic rules + +We should probably add some way to check vendor.conf for the policy +updates instead of a hardcoded list. +--- + data/20-gnome-initial-setup.rules.in | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/data/20-gnome-initial-setup.rules.in b/data/20-gnome-initial-setup.rules.in +index 881efde9..f5b7d981 100644 +--- a/data/20-gnome-initial-setup.rules.in ++++ b/data/20-gnome-initial-setup.rules.in +@@ -1,30 +1,31 @@ + // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + // + // DO NOT EDIT THIS FILE, it will be overwritten on update. + // + // Allow the gnome-initial-setup user to do certain actions without + // being interrupted by password dialogs + + polkit.addRule(function(action, subject) { + if (subject.user !== 'gnome-initial-setup') + return undefined; + + var actionMatches = (action.id.indexOf('org.freedesktop.hostname1.') === 0 || + action.id.indexOf('org.freedesktop.NetworkManager.') === 0 || + action.id.indexOf('org.freedesktop.locale1.') === 0 || + action.id.indexOf('org.freedesktop.accounts.') === 0 || + action.id.indexOf('org.freedesktop.timedate1.') === 0 || + action.id.indexOf('org.freedesktop.realmd.') === 0 || + action.id.indexOf('com.endlessm.ParentalControls.') === 0 || + action.id.indexOf('org.fedoraproject.thirdparty.') === 0 || +- action.id.indexOf('org.freedesktop.login1.reboot') === 0); ++ action.id.indexOf('org.freedesktop.login1.reboot') === 0 || ++ action.id.indexOf('org.fedoraproject.pkexec.liveinst') === 0); + + if (actionMatches) { + if (subject.local) + return 'yes'; + else + return 'auth_admin'; + } + + return undefined; + }); +-- +2.41.0.rc2 + diff --git a/gnome-initial-setup.spec b/gnome-initial-setup.spec index 3c4d31d..0726d30 100644 --- a/gnome-initial-setup.spec +++ b/gnome-initial-setup.spec @@ -16,6 +16,15 @@ URL: https://wiki.gnome.org/Design/OS/InitialSetup Source0: https://download.gnome.org/sources/%{name}/45/%{name}-%{tarball_version}.tar.xz Source1: vendor.conf +Patch: 0001-gnome-initial-setup-Bump-GLib-required-version-to-2..patch +Patch: 0002-driver-Specify-mode-via-flags-instead-of-boolean.patch +Patch: 0003-gnome-initial-setup-Add-live-user-mode.patch +Patch: 0004-initial-setup-Don-t-show-duplicated-pages-between-mo.patch +Patch: 0005-keyboard-Don-t-add-default-input-sources-if-input-so.patch +Patch: 0006-gnome-initial-setup-Add-OEM-mode.patch +Patch: 0007-polkit-Add-fedora-specfic-rules.patch + + BuildRequires: desktop-file-utils BuildRequires: gcc BuildRequires: meson @@ -85,11 +94,13 @@ cp %{SOURCE1} %{buildroot}%{_datadir}/gnome-initial-setup/ %find_lang %{name} %pre -useradd -rM -d /run/gnome-initial-setup/ -s /sbin/nologin %{name} &>/dev/null || : +useradd -rM -d %{_localstatedir}/lib/gnome-initial-setup/ -s /sbin/nologin %{name} &>/dev/null || : %files -f %{name}.lang %license COPYING %doc NEWS README.md +%attr(-, gnome-initial-setup, gnome-initial-setup) %dir %{_localstatedir}/lib/gnome-initial-setup +%ghost %{_localstatedir}/gnome-initial-setup/state %{_libexecdir}/gnome-initial-setup %{_libexecdir}/gnome-initial-setup-copy-worker %{_libexecdir}/gnome-initial-setup-goa-helper diff --git a/vendor.conf b/vendor.conf index dc010ef..bc78d41 100644 --- a/vendor.conf +++ b/vendor.conf @@ -1,3 +1,5 @@ -[pages] -skip=timezone -existing_user_only=language;keyboard +[live_user pages] +skip=privacy;timezone;software;goa;network; + +[install] +application=anaconda.desktop