diff options
Diffstat (limited to 'panels/user-accounts/cc-user-panel.c')
-rw-r--r-- | panels/user-accounts/cc-user-panel.c | 1678 |
1 files changed, 1678 insertions, 0 deletions
diff --git a/panels/user-accounts/cc-user-panel.c b/panels/user-accounts/cc-user-panel.c new file mode 100644 index 0000000..5a9b5c2 --- /dev/null +++ b/panels/user-accounts/cc-user-panel.c @@ -0,0 +1,1678 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright 2009-2010 Red Hat, Inc, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Written by: Matthias Clasen <mclasen@redhat.com> + */ + +#include "config.h" + +#include "cc-user-panel.h" + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <locale.h> +#include <errno.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <polkit/polkit.h> +#include <act/act.h> +#include <cairo-gobject.h> + +#define GNOME_DESKTOP_USE_UNSTABLE_API +#include <libgnome-desktop/gnome-languages.h> + +#ifdef HAVE_MALCONTENT +#include <libmalcontent/malcontent.h> +#endif + +#include "cc-add-user-dialog.h" +#include "cc-avatar-chooser.h" +#include "cc-carousel.h" +#include "cc-language-chooser.h" +#include "cc-login-history-dialog.h" +#include "cc-password-dialog.h" +#include "cc-realm-manager.h" +#include "cc-user-accounts-resources.h" +#include "cc-user-image.h" +#include "cc-fingerprint-manager.h" +#include "cc-fingerprint-dialog.h" +#include "user-utils.h" + +#include "cc-common-language.h" +#include "cc-permission-infobar.h" +#include "cc-util.h" +#include "list-box-helper.h" + +#define USER_ACCOUNTS_PERMISSION "org.gnome.controlcenter.user-accounts.administration" + +struct _CcUserPanel { + CcPanel parent_instance; + + ActUserManager *um; + GSettings *login_screen_settings; + + GtkBox *accounts_box; + GtkBox *account_settings_box; + GtkListBox *account_settings_listbox; + GtkListBox *authentication_and_login_listbox; + GtkListBoxRow *account_type_row; + GtkSwitch *account_type_switch; + GtkButton *add_user_button; + GtkListBoxRow *autologin_row; + GtkSwitch *autologin_switch; + CcCarousel *carousel; + GtkLabel *fingerprint_state_label; + GtkListBoxRow *fingerprint_row; + GtkStack *full_name_stack; + GtkLabel *full_name_label; + GtkToggleButton *full_name_edit_button; + GtkEntry *full_name_entry; + GtkLabel *language_button_label; + GtkListBoxRow *language_row; + GtkLabel *last_login_button_label; + GtkListBoxRow *last_login_row; + GtkBox *no_users_box; + GtkRevealer *notification_revealer; + GtkLabel *password_button_label; +#ifdef HAVE_MALCONTENT + GtkLabel *parental_controls_button_label; + GtkImage *parental_control_go_next; + GtkListBoxRow *parental_controls_row; +#endif + GtkListBoxRow *password_row; + CcPermissionInfobar *permission_infobar; + GtkButton *remove_user_button; + GtkStack *stack; + GtkToggleButton *user_icon_button; + CcUserImage *user_icon_image; + CcUserImage *user_icon_image2; + GtkStack *user_icon_stack; + GtkOverlay *users_overlay; + + ActUser *selected_user; + GPermission *permission; + CcLanguageChooser *language_chooser; + + CcAvatarChooser *avatar_chooser; + + CcFingerprintManager *fingerprint_manager; + + gint other_accounts; +}; + +CC_PANEL_REGISTER (CcUserPanel, cc_user_panel) + +static void show_restart_notification (CcUserPanel *self, const gchar *locale); +static gint user_compare (gconstpointer i, gconstpointer u); + +typedef struct { + CcUserPanel *self; + GCancellable *cancellable; + gchar *login; +} AsyncDeleteData; + +static void +async_delete_data_free (AsyncDeleteData *data) +{ + g_object_unref (data->self); + g_object_unref (data->cancellable); + g_free (data->login); + g_slice_free (AsyncDeleteData, data); +} + +static void +show_error_dialog (CcUserPanel *self, + const gchar *message, + GError *error) +{ + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_USE_HEADER_BAR, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "%s", message); + + if (error != NULL) { + g_dbus_error_strip_remote_error (error); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + "%s", error->message); + } + + g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL); + gtk_window_present (GTK_WINDOW (dialog)); +} + +static ActUser * +get_selected_user (CcUserPanel *self) +{ + return self->selected_user; +} + +static const gchar * +get_real_or_user_name (ActUser *user) +{ + const gchar *name; + + name = act_user_get_real_name (user); + if (name == NULL) + name = act_user_get_user_name (user); + + return name; +} + +static void show_user (ActUser *user, CcUserPanel *self); + +static void +set_selected_user (CcUserPanel *self, CcCarouselItem *item) +{ + uid_t uid; + + uid = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "uid")); + g_set_object (&self->selected_user, + act_user_manager_get_user_by_id (self->um, uid)); + + if (self->selected_user != NULL) { + show_user (self->selected_user, self); + } +} + +static GtkWidget * +create_carousel_entry (CcUserPanel *self, ActUser *user) +{ + GtkWidget *box, *widget; + gchar *label; + + box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + + widget = cc_user_image_new (); + cc_user_image_set_user (CC_USER_IMAGE (widget), user); + gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 0); + + label = g_markup_printf_escaped ("<b>%s</b>", + get_real_or_user_name (user)); + widget = gtk_label_new (label); + gtk_label_set_use_markup (GTK_LABEL (widget), TRUE); + gtk_label_set_ellipsize (GTK_LABEL (widget), PANGO_ELLIPSIZE_END); + gtk_widget_set_margin_top (widget, 5); + gtk_box_pack_start (GTK_BOX (box), widget, FALSE, TRUE, 0); + g_free (label); + + if (act_user_get_uid (user) == getuid ()) + label = g_strdup_printf ("<small>%s</small>", _("Your account")); + else + label = g_strdup (" "); + + widget = gtk_label_new (label); + gtk_label_set_use_markup (GTK_LABEL (widget), TRUE); + g_free (label); + + gtk_box_pack_start (GTK_BOX (box), widget, FALSE, TRUE, 0); + gtk_style_context_add_class (gtk_widget_get_style_context (widget), + "dim-label"); + + return box; +} + +static void +user_added (CcUserPanel *self, ActUser *user) +{ + GtkWidget *item, *widget; + gboolean show_carousel; + + if (act_user_is_system_account (user)) { + return; + } + + g_debug ("user added: %d %s\n", act_user_get_uid (user), get_real_or_user_name (user)); + + widget = create_carousel_entry (self, user); + item = cc_carousel_item_new (); + gtk_container_add (GTK_CONTAINER (item), widget); + + g_object_set_data (G_OBJECT (item), "uid", GINT_TO_POINTER (act_user_get_uid (user))); + gtk_container_add (GTK_CONTAINER (self->carousel), item); + + if (act_user_get_uid (user) != getuid ()) { + self->other_accounts++; + } + + /* Show heading for other accounts if new one have been added. */ + show_carousel = (self->other_accounts > 0); + gtk_revealer_set_reveal_child (GTK_REVEALER (self->carousel), show_carousel); + + gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->users_overlay)); +} + +static gint +sort_users (gconstpointer a, gconstpointer b) +{ + ActUser *ua, *ub; + gchar *name1, *name2; + gint result; + + ua = ACT_USER (a); + ub = ACT_USER (b); + + /* Make sure the current user is shown first */ + if (act_user_get_uid (ua) == getuid ()) { + result = -G_MAXINT32; + } + else if (act_user_get_uid (ub) == getuid ()) { + result = G_MAXINT32; + } + else { + name1 = g_utf8_collate_key (get_real_or_user_name (ua), -1); + name2 = g_utf8_collate_key (get_real_or_user_name (ub), -1); + + result = strcmp (name1, name2); + + g_free (name1); + g_free (name2); + } + + return result; +} + +static void +reload_users (CcUserPanel *self, ActUser *selected_user) +{ + ActUser *user; + GSList *list, *l; + CcCarouselItem *item = NULL; + GtkSettings *settings; + gboolean animations; + guint users_count; + + settings = gtk_widget_get_settings (GTK_WIDGET (self->carousel)); + + g_object_get (settings, "gtk-enable-animations", &animations, NULL); + g_object_set (settings, "gtk-enable-animations", FALSE, NULL); + + cc_carousel_purge_items (self->carousel); + self->other_accounts = 0; + + list = act_user_manager_list_users (self->um); + users_count = g_slist_length (list); + g_debug ("Got %d users\n", users_count); + + list = g_slist_sort (list, (GCompareFunc) sort_users); + for (l = list; l; l = l->next) { + user = l->data; + g_debug ("adding user %s\n", get_real_or_user_name (user)); + user_added (self, user); + } + g_slist_free (list); + + if (cc_carousel_get_item_count (self->carousel) == 0) + gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->no_users_box)); + if (self->other_accounts == 0) + gtk_revealer_set_reveal_child (GTK_REVEALER (self->carousel), FALSE); + + if (selected_user) + item = cc_carousel_find_item (self->carousel, selected_user, user_compare); + cc_carousel_select_item (self->carousel, item); + + g_object_set (settings, "gtk-enable-animations", animations, NULL); +#ifdef HAVE_MALCONTENT + /* Parental Controls row not to be shown for single user setups. */ + gtk_widget_set_visible (GTK_WIDGET (self->parental_controls_row), users_count > 1); +#endif +} + +static gint +user_compare (gconstpointer i, + gconstpointer u) +{ + CcCarouselItem *item; + ActUser *user; + gint uid_a, uid_b; + gint result; + + item = (CcCarouselItem *) i; + user = ACT_USER (u); + + uid_a = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "uid")); + uid_b = act_user_get_uid (user); + + result = uid_a - uid_b; + + return result; +} + +static void +user_changed (CcUserPanel *self, ActUser *user) +{ + reload_users (self, self->selected_user); +} + +static void +add_user (CcUserPanel *self) +{ + CcAddUserDialog *dialog; + g_autoptr(GdkPixbuf) pixbuf = NULL; + GtkWindow *toplevel; + ActUser *user; + + dialog = cc_add_user_dialog_new (self->permission); + toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))); + gtk_window_set_transient_for (GTK_WINDOW (dialog), toplevel); + + gtk_dialog_run (GTK_DIALOG (dialog)); + + user = cc_add_user_dialog_get_user (dialog); + if (user != NULL) { + set_default_avatar (user); + reload_users (self, user); + } + + gtk_widget_destroy (GTK_WIDGET (dialog)); +} + +static void +delete_user_done (ActUserManager *manager, + GAsyncResult *res, + CcUserPanel *self) +{ + GError *error; + + error = NULL; + if (!act_user_manager_delete_user_finish (manager, res, &error)) { + if (!g_error_matches (error, ACT_USER_MANAGER_ERROR, + ACT_USER_MANAGER_ERROR_PERMISSION_DENIED)) + show_error_dialog (self, _("Failed to delete user"), error); + + g_error_free (error); + } +} + +static void +delete_user_response (CcUserPanel *self, + gint response_id, + GtkWidget *dialog) +{ + ActUser *user; + gboolean remove_files; + + gtk_widget_destroy (dialog); + + if (response_id == GTK_RESPONSE_CANCEL) { + return; + } + else if (response_id == GTK_RESPONSE_NO) { + remove_files = TRUE; + } + else { + remove_files = FALSE; + } + + user = get_selected_user (self); + + /* remove autologin */ + if (act_user_get_automatic_login (user)) { + act_user_set_automatic_login (user, FALSE); + } + + act_user_manager_delete_user_async (self->um, + user, + remove_files, + NULL, + (GAsyncReadyCallback)delete_user_done, + self); +} + +static void +enterprise_user_revoked (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + AsyncDeleteData *data = user_data; + CcUserPanel *self = data->self; + CcRealmCommon *common = CC_REALM_COMMON (source); + GError *error = NULL; + + if (g_cancellable_is_cancelled (data->cancellable)) { + async_delete_data_free (data); + return; + } + + cc_realm_common_call_change_login_policy_finish (common, result, &error); + if (error != NULL) { + show_error_dialog (self, _("Failed to revoke remotely managed user"), error); + g_error_free (error); + } + + async_delete_data_free (data); +} + +static CcRealmCommon * +find_matching_realm (CcRealmManager *realm_manager, const gchar *login) +{ + CcRealmCommon *common = NULL; + GList *realms, *l; + + realms = cc_realm_manager_get_realms (realm_manager); + for (l = realms; l != NULL; l = g_list_next (l)) { + const gchar * const *permitted_logins; + gint i; + + common = cc_realm_object_get_common (l->data); + if (common == NULL) + continue; + + permitted_logins = cc_realm_common_get_permitted_logins (common); + for (i = 0; permitted_logins[i] != NULL; i++) { + if (g_strcmp0 (permitted_logins[i], login) == 0) + break; + } + + if (permitted_logins[i] != NULL) + break; + + g_clear_object (&common); + } + g_list_free_full (realms, g_object_unref); + + return common; +} + +static void +realm_manager_found (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + AsyncDeleteData *data = user_data; + CcUserPanel *self = data->self; + CcRealmCommon *common; + CcRealmManager *realm_manager; + const gchar *add[1]; + const gchar *remove[2]; + GVariant *options; + GError *error = NULL; + + if (g_cancellable_is_cancelled (data->cancellable)) { + async_delete_data_free (data); + return; + } + + realm_manager = cc_realm_manager_new_finish (result, &error); + if (error != NULL) { + show_error_dialog (self, _("Failed to revoke remotely managed user"), error); + g_error_free (error); + async_delete_data_free (data); + return; + } + + /* Find matching realm */ + common = find_matching_realm (realm_manager, data->login); + if (common == NULL) { + /* The realm was probably left */ + async_delete_data_free (data); + return; + } + + /* Remove the user from permitted logins */ + g_debug ("Denying future login for: %s", data->login); + + add[0] = NULL; + remove[0] = data->login; + remove[1] = NULL; + + options = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0); + cc_realm_common_call_change_login_policy (common, "", + add, remove, options, + data->cancellable, + enterprise_user_revoked, + data); + + g_object_unref (common); +} + +static void +enterprise_user_uncached (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + AsyncDeleteData *data = user_data; + CcUserPanel *self = data->self; + ActUserManager *manager = ACT_USER_MANAGER (source); + GError *error = NULL; + + if (g_cancellable_is_cancelled (data->cancellable)) { + async_delete_data_free (data); + return; + } + + act_user_manager_uncache_user_finish (manager, res, &error); + if (error == NULL) { + /* Find realm manager */ + cc_realm_manager_new (cc_panel_get_cancellable (CC_PANEL (self)), realm_manager_found, data); + } + else { + show_error_dialog (self, _("Failed to revoke remotely managed user"), error); + g_error_free (error); + async_delete_data_free (data); + } +} + +static void +delete_enterprise_user_response (CcUserPanel *self, + gint response_id, + GtkWidget *dialog) +{ + AsyncDeleteData *data; + ActUser *user; + + gtk_widget_destroy (dialog); + + if (response_id != GTK_RESPONSE_ACCEPT) { + return; + } + + user = get_selected_user (self); + + data = g_slice_new (AsyncDeleteData); + data->self = g_object_ref (self); + data->cancellable = g_object_ref (cc_panel_get_cancellable (CC_PANEL (self))); + data->login = g_strdup (act_user_get_user_name (user)); + + /* Uncache the user account from the accountsservice */ + g_debug ("Uncaching remote user: %s", data->login); + + act_user_manager_uncache_user_async (self->um, data->login, + data->cancellable, + enterprise_user_uncached, + data); +} + +static void +delete_user (CcUserPanel *self) +{ + ActUser *user; + GtkWidget *dialog; + + user = get_selected_user (self); + if (user == NULL) { + return; + } + else if (act_user_get_uid (user) == getuid ()) { + dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))), + 0, + GTK_MESSAGE_INFO, + GTK_BUTTONS_CLOSE, + _("You cannot delete your own account.")); + g_signal_connect (dialog, "response", + G_CALLBACK (gtk_widget_destroy), NULL); + } + else if (act_user_is_logged_in_anywhere (user)) { + dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))), + 0, + GTK_MESSAGE_INFO, + GTK_BUTTONS_CLOSE, + _("%s is still logged in"), + get_real_or_user_name (user)); + + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("Deleting a user while they are logged in can leave the system in an inconsistent state.")); + g_signal_connect (dialog, "response", + G_CALLBACK (gtk_widget_destroy), NULL); + } + else if (act_user_is_local_account (user)) { + dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))), + 0, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_NONE, + _("Do you want to keep %s’s files?"), + get_real_or_user_name (user)); + + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("It is possible to keep the home directory, mail spool and temporary files around when deleting a user account.")); + + gtk_dialog_add_buttons (GTK_DIALOG (dialog), + _("_Delete Files"), GTK_RESPONSE_NO, + _("_Keep Files"), GTK_RESPONSE_YES, + _("_Cancel"), GTK_RESPONSE_CANCEL, + NULL); + + gtk_window_set_icon_name (GTK_WINDOW (dialog), "system-users"); + + g_signal_connect_object (dialog, "response", + G_CALLBACK (delete_user_response), self, G_CONNECT_SWAPPED); + } + else { + dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))), + 0, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_NONE, + _("Are you sure you want to revoke remotely managed %s’s account?"), + get_real_or_user_name (user)); + + gtk_dialog_add_buttons (GTK_DIALOG (dialog), + _("_Delete"), GTK_RESPONSE_ACCEPT, + _("_Cancel"), GTK_RESPONSE_CANCEL, + NULL); + + gtk_window_set_icon_name (GTK_WINDOW (dialog), "system-users"); + + g_signal_connect_object (dialog, "response", + G_CALLBACK (delete_enterprise_user_response), self, G_CONNECT_SWAPPED); + } + + g_signal_connect (dialog, "close", + G_CALLBACK (gtk_widget_destroy), NULL); + + gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); + + gtk_window_present (GTK_WINDOW (dialog)); +} + +static const gchar * +get_invisible_text (void) +{ + GtkWidget *entry; + gunichar invisible_char; + static gchar invisible_text[40]; + gchar *p; + gint i; + + entry = gtk_entry_new (); + invisible_char = gtk_entry_get_invisible_char (GTK_ENTRY (entry)); + if (invisible_char == 0) + invisible_char = 0x2022; + + g_object_ref_sink (entry); + g_object_unref (entry); + + /* five bullets */ + p = invisible_text; + for (i = 0; i < 5; i++) + p += g_unichar_to_utf8 (invisible_char, p); + *p = 0; + + return invisible_text; +} + +static const gchar * +get_password_mode_text (ActUser *user) +{ + const gchar *text; + + if (act_user_get_locked (user)) { + text = C_("Password mode", "Account disabled"); + } + else { + switch (act_user_get_password_mode (user)) { + case ACT_USER_PASSWORD_MODE_REGULAR: + text = get_invisible_text (); + break; + case ACT_USER_PASSWORD_MODE_SET_AT_LOGIN: + text = C_("Password mode", "To be set at next login"); + break; + case ACT_USER_PASSWORD_MODE_NONE: + text = C_("Password mode", "None"); + break; + default: + g_assert_not_reached (); + } + } + + return text; +} + +static void +autologin_changed (CcUserPanel *self) +{ + gboolean active; + ActUser *user; + + active = gtk_switch_get_active (self->autologin_switch); + user = get_selected_user (self); + + if (active != act_user_get_automatic_login (user)) { + act_user_set_automatic_login (user, active); + if (act_user_get_automatic_login (user)) { + GSList *list; + GSList *l; + list = act_user_manager_list_users (self->um); + for (l = list; l != NULL; l = l->next) { + ActUser *u = l->data; + if (act_user_get_uid (u) != act_user_get_uid (user)) { + act_user_set_automatic_login (user, FALSE); + } + } + g_slist_free (list); + } + } +} + +static gchar * +get_login_time_text (ActUser *user) +{ + gchar *text, *date_str, *time_str; + GDateTime *date_time; + gint64 time; + + time = act_user_get_login_time (user); + if (act_user_is_logged_in (user)) { + text = g_strdup (_("Logged in")); + } + else if (time > 0) { + date_time = g_date_time_new_from_unix_local (time); + date_str = cc_util_get_smart_date (date_time); + /* Translators: This is a time format string in the style of "22:58". + It indicates a login time which follows a date. */ + time_str = g_date_time_format (date_time, C_("login date-time", "%k:%M")); + + /* Translators: This indicates a login date-time. + The first %s is a date, and the second %s a time. */ + text = g_strdup_printf(C_("login date-time", "%s, %s"), date_str, time_str); + + g_date_time_unref (date_time); + g_free (date_str); + g_free (time_str); + } + else { + text = g_strdup ("—"); + } + + return text; +} + +static gboolean +get_autologin_possible (ActUser *user) +{ + gboolean locked; + gboolean set_password_at_login; + + locked = act_user_get_locked (user); + set_password_at_login = (act_user_get_password_mode (user) == ACT_USER_PASSWORD_MODE_SET_AT_LOGIN); + + return !(locked || set_password_at_login); +} + +static void on_permission_changed (CcUserPanel *self); +static void full_name_edit_button_toggled (CcUserPanel *self); + +#ifdef HAVE_MALCONTENT +static gboolean +is_parental_controls_enabled_for_user (ActUser *user) +{ + g_autoptr(MctManager) manager = NULL; + g_autoptr(MctAppFilter) app_filter = NULL; + g_autoptr(GDBusConnection) system_bus = NULL; + g_autoptr(GError) error = NULL; + + /* FIXME: should become asynchronous */ + system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); + if (system_bus == NULL) { + g_warning ("Error getting system bus while trying to show user details: %s", error->message); + return FALSE; + } + + manager = mct_manager_new (system_bus); + app_filter = mct_manager_get_app_filter (manager, + act_user_get_uid (user), + MCT_GET_APP_FILTER_FLAGS_NONE, + NULL, + &error); + if (error) { + if (!g_error_matches (error, MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_DISABLED)) + g_warning ("Error retrieving app filter for user %s: %s", + act_user_get_user_name (user), + error->message); + + return FALSE; + } + + return mct_app_filter_is_enabled (app_filter); +} +#endif + +static void +update_fingerprint_row_state (CcUserPanel *self, GParamSpec *spec, CcFingerprintManager *fingerprint_manager) +{ + CcFingerprintState state = cc_fingerprint_manager_get_state (fingerprint_manager); + + if (state != CC_FINGERPRINT_STATE_UPDATING) { + gtk_widget_set_visible (GTK_WIDGET (self->fingerprint_row), + state != CC_FINGERPRINT_STATE_NONE); + } + + gtk_widget_set_sensitive (GTK_WIDGET (self->fingerprint_row), + state != CC_FINGERPRINT_STATE_UPDATING); + + if (state == CC_FINGERPRINT_STATE_ENABLED) + gtk_label_set_text (self->fingerprint_state_label, _("Enabled")); + else if (state == CC_FINGERPRINT_STATE_DISABLED) + gtk_label_set_text (self->fingerprint_state_label, _("Disabled")); +} + +static void +show_user (ActUser *user, CcUserPanel *self) +{ + gchar *lang, *text, *name; + gboolean show, enable; + ActUser *current; + + self->selected_user = user; + + cc_user_image_set_user (self->user_icon_image, user); + cc_user_image_set_user (self->user_icon_image2, user); + + cc_avatar_chooser_set_user (self->avatar_chooser, user); + + gtk_label_set_label (self->full_name_label, act_user_get_real_name (user)); + gtk_entry_set_text (self->full_name_entry, act_user_get_real_name (user)); + gtk_widget_set_tooltip_text (GTK_WIDGET (self->full_name_label), act_user_get_user_name (user)); + + g_signal_handlers_block_by_func (self->full_name_edit_button, full_name_edit_button_toggled, self); + gtk_stack_set_visible_child (self->full_name_stack, GTK_WIDGET (self->full_name_label)); + gtk_toggle_button_set_active (self->full_name_edit_button, FALSE); + g_signal_handlers_unblock_by_func (self->full_name_edit_button, full_name_edit_button_toggled, self); + + enable = (act_user_get_account_type (user) == ACT_USER_ACCOUNT_TYPE_ADMINISTRATOR); + gtk_switch_set_active (self->account_type_switch, enable); + + /* Do not show the "Account Type" option when there's a single user account. */ + show = (self->other_accounts != 0); + gtk_widget_set_visible (GTK_WIDGET (self->account_settings_box), show); + + gtk_label_set_label (self->password_button_label, get_password_mode_text (user)); + enable = act_user_is_local_account (user); + gtk_widget_set_sensitive (GTK_WIDGET (self->password_button_label), enable); + + g_signal_handlers_block_by_func (self->autologin_switch, autologin_changed, self); + gtk_switch_set_active (self->autologin_switch, act_user_get_automatic_login (user)); + g_signal_handlers_unblock_by_func (self->autologin_switch, autologin_changed, self); + gtk_widget_set_sensitive (GTK_WIDGET (self->autologin_switch), get_autologin_possible (user)); + + name = NULL; + lang = g_strdup (act_user_get_language (user)); + + if (lang && *lang != '\0') { + name = gnome_get_language_from_locale (lang, NULL); + } else { + name = g_strdup ("—"); + } + + gtk_label_set_label (self->language_button_label, name); + g_free (lang); + g_free (name); + + /* Fingerprint: show when self, local, enabled, and possible */ + show = (act_user_get_uid (user) == getuid() && + act_user_is_local_account (user) && + (self->login_screen_settings && + g_settings_get_boolean (self->login_screen_settings, + "enable-fingerprint-authentication"))); + + if (show) { + if (!self->fingerprint_manager) { + self->fingerprint_manager = cc_fingerprint_manager_new (user); + g_signal_connect_object (self->fingerprint_manager, + "notify::state", + G_CALLBACK (update_fingerprint_row_state), + self, G_CONNECT_SWAPPED); + } + + update_fingerprint_row_state (self, NULL, self->fingerprint_manager); + } else { + gtk_widget_set_visible (GTK_WIDGET (self->fingerprint_row), FALSE); + } + + /* Autologin: show when local account */ + show = act_user_is_local_account (user); + gtk_widget_set_visible (GTK_WIDGET (self->autologin_row), show); + +#ifdef HAVE_MALCONTENT + /* Parental Controls: Unavailable if user is admin */ + if (act_user_get_account_type (user) == ACT_USER_ACCOUNT_TYPE_ADMINISTRATOR) { + gtk_widget_hide (GTK_WIDGET (self->parental_control_go_next)); + /* TRANSLATORS: Status of Parental Controls setup */ + gtk_label_set_text (self->parental_controls_button_label, _("Unavailable")); + } else { + if (is_parental_controls_enabled_for_user (user)) + /* TRANSLATORS: Status of Parental Controls setup */ + gtk_label_set_text (self->parental_controls_button_label, _("Enabled")); + else + /* TRANSLATORS: Status of Parental Controls setup */ + gtk_label_set_text (self->parental_controls_button_label, _("Disabled")); + + gtk_widget_show (GTK_WIDGET (self->parental_control_go_next)); + } +#endif + + /* Language: do not show for current user */ + show = act_user_get_uid (user) != getuid(); + gtk_widget_set_visible (GTK_WIDGET (self->language_row), show); + + /* Last login: show when administrator or current user */ + current = act_user_manager_get_user_by_id (self->um, getuid ()); + show = act_user_get_uid (user) == getuid () || + act_user_get_account_type (current) == ACT_USER_ACCOUNT_TYPE_ADMINISTRATOR; + if (show) { + text = get_login_time_text (user); + gtk_label_set_label (self->last_login_button_label, text); + g_free (text); + } + gtk_widget_set_visible (GTK_WIDGET (self->last_login_row), show); + + enable = act_user_get_login_history (user) != NULL; + gtk_widget_set_sensitive (GTK_WIDGET (self->last_login_row), enable); + + if (self->permission != NULL) + on_permission_changed (self); +} + +static void +full_name_entry_activate (CcUserPanel *self) +{ + const gchar *text; + ActUser *user; + + user = get_selected_user (self); + text = gtk_entry_get_text (self->full_name_entry); + if (g_strcmp0 (text, act_user_get_real_name (user)) != 0 && + is_valid_name (text)) { + act_user_set_real_name (user, text); + } + + gtk_toggle_button_set_active (self->full_name_edit_button, FALSE); +} + +static void +full_name_edit_button_toggled (CcUserPanel *self) +{ + if (gtk_stack_get_visible_child (self->full_name_stack) == GTK_WIDGET (self->full_name_label)) { + gtk_stack_set_visible_child (self->full_name_stack, GTK_WIDGET (self->full_name_entry)); + + gtk_widget_grab_focus (GTK_WIDGET (self->full_name_entry)); + } else { + gtk_stack_set_visible_child (self->full_name_stack, GTK_WIDGET (self->full_name_label)); + + full_name_entry_activate (self); + } +} + +static gboolean +full_name_entry_key_press_cb (CcUserPanel *self, + GdkEvent *event) +{ + GdkEventKey *key = (GdkEventKey *)event; + + if (key->keyval == GDK_KEY_Escape) { + gtk_entry_set_text (self->full_name_entry, act_user_get_real_name (self->selected_user)); + + full_name_entry_activate (self); + + return TRUE; + } + + return FALSE; +} + +static void +account_type_changed (CcUserPanel *self) +{ + ActUser *user; + gboolean self_selected; + gboolean is_admin; + ActUserAccountType account_type; + + user = get_selected_user (self); + self_selected = act_user_get_uid (user) == geteuid (); + is_admin = gtk_switch_get_active (self->account_type_switch); + + account_type = is_admin ? ACT_USER_ACCOUNT_TYPE_ADMINISTRATOR : ACT_USER_ACCOUNT_TYPE_STANDARD; + if (account_type != act_user_get_account_type (user)) { + act_user_set_account_type (user, account_type); + + if (self_selected) + show_restart_notification (self, NULL); + } +} + +static void +dismiss_notification (CcUserPanel *self) +{ + gtk_revealer_set_reveal_child (self->notification_revealer, FALSE); +} + +static void +restart_now (CcUserPanel *self) +{ + GDBusConnection *bus; + + gtk_revealer_set_reveal_child (self->notification_revealer, FALSE); + + bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); + g_dbus_connection_call (bus, + "org.gnome.SessionManager", + "/org/gnome/SessionManager", + "org.gnome.SessionManager", + "Logout", + g_variant_new ("(u)", 0), + NULL, 0, G_MAXINT, + NULL, NULL, NULL); + g_object_unref (bus); +} + +static void +show_restart_notification (CcUserPanel *self, const gchar *locale) +{ + locale_t current_locale; + locale_t new_locale; + + if (locale) { + new_locale = newlocale (LC_MESSAGES_MASK, locale, (locale_t) 0); + if (new_locale == (locale_t) 0) + g_warning ("Failed to create locale %s: %s", locale, g_strerror (errno)); + else + current_locale = uselocale (new_locale); + } + + gtk_revealer_set_reveal_child (self->notification_revealer, TRUE); + + if (locale && new_locale != (locale_t) 0) { + uselocale (current_locale); + freelocale (new_locale); + } +} + +static void +language_response (CcUserPanel *self, + gint response_id, + GtkDialog *dialog) +{ + ActUser *user; + const gchar *lang, *account_language; + + if (response_id != GTK_RESPONSE_OK) { + gtk_widget_hide (GTK_WIDGET (dialog)); + return; + } + + user = get_selected_user (self); + account_language = act_user_get_language (user); + + lang = cc_language_chooser_get_language (CC_LANGUAGE_CHOOSER (dialog)); + if (lang) { + g_autofree gchar *name = NULL; + if (g_strcmp0 (lang, account_language) != 0) { + act_user_set_language (user, lang); + } + + name = gnome_get_language_from_locale (lang, NULL); + gtk_label_set_label (self->language_button_label, name); + } + + gtk_widget_hide (GTK_WIDGET (dialog)); +} + +static void +change_language (CcUserPanel *self) +{ + const gchar *current_language; + ActUser *user; + + user = get_selected_user (self); + current_language = act_user_get_language (user); + + if (self->language_chooser) { + cc_language_chooser_clear_filter (self->language_chooser); + cc_language_chooser_set_language (self->language_chooser, NULL); + } + else { + self->language_chooser = cc_language_chooser_new (); + gtk_window_set_transient_for (GTK_WINDOW (self->language_chooser), + GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self)))); + + g_signal_connect_object (self->language_chooser, "response", + G_CALLBACK (language_response), self, G_CONNECT_SWAPPED); + g_signal_connect (self->language_chooser, "delete-event", + G_CALLBACK (gtk_widget_hide_on_delete), NULL); + + gdk_window_set_cursor (gtk_widget_get_window (gtk_widget_get_toplevel (GTK_WIDGET (self))), NULL); + } + + if (current_language && *current_language != '\0') + cc_language_chooser_set_language (self->language_chooser, current_language); + gtk_window_present (GTK_WINDOW (self->language_chooser)); +} + +static void +change_password (CcUserPanel *self) +{ + ActUser *user; + CcPasswordDialog *dialog; + GtkWindow *parent; + + user = get_selected_user (self); + dialog = cc_password_dialog_new (user); + + parent = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))); + gtk_window_set_transient_for (GTK_WINDOW (dialog), parent); + + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (GTK_WIDGET (dialog)); +} + +static void +change_fingerprint (CcUserPanel *self) +{ + ActUser *user; + GtkWindow *top_level; + CcFingerprintDialog *dialog; + + user = get_selected_user (self); + top_level = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))); + + g_assert (g_strcmp0 (g_get_user_name (), act_user_get_user_name (user)) == 0); + + dialog = cc_fingerprint_dialog_new (self->fingerprint_manager); + gtk_window_set_transient_for (GTK_WINDOW (dialog), top_level); + gtk_widget_show (GTK_WIDGET (dialog)); +} + +static void +show_history (CcUserPanel *self) +{ + CcLoginHistoryDialog *dialog; + ActUser *user; + GtkWindow *parent; + gint parent_width; + + user = get_selected_user (self); + dialog = cc_login_history_dialog_new (user); + + parent = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))); + gtk_window_get_size (parent, &parent_width, NULL); + gtk_window_set_default_size (GTK_WINDOW (dialog), parent_width * 0.6, -1); + gtk_window_set_transient_for (GTK_WINDOW (dialog), parent); + + gtk_dialog_run (GTK_DIALOG (dialog)); + + gtk_widget_destroy (GTK_WIDGET (dialog)); +} + +#ifdef HAVE_MALCONTENT +static void +spawn_malcontent_control (CcUserPanel *self) +{ + ActUser *user; + + user = get_selected_user (self); + + /* no-op if the user is administrator */ + if (act_user_get_account_type (user) != ACT_USER_ACCOUNT_TYPE_ADMINISTRATOR) { + const gchar *argv[] = { "malcontent-control", NULL }; + g_spawn_async (NULL, (char **)argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL); + } +} +#endif + +static void +activate_row (GtkListBox *box, GtkListBoxRow *row, CcUserPanel *self) +{ + if (!gtk_widget_get_sensitive (GTK_WIDGET (row))) + return; + + if (row == self->language_row) { + change_language (self); + } else if (row == self->password_row) { + change_password (self); + } else if (row == self->fingerprint_row) { + change_fingerprint (self); + } else if (row == self->last_login_row) { + show_history (self); + } + +#ifdef HAVE_MALCONTENT + if (row == self->parental_controls_row) { + spawn_malcontent_control (self); + } +#endif +} + +static void +users_loaded (CcUserPanel *self) +{ + GtkWidget *dialog; + + if (act_user_manager_no_service (self->um)) { + dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))), + GTK_DIALOG_MODAL, + GTK_MESSAGE_OTHER, + GTK_BUTTONS_CLOSE, + _("Failed to contact the accounts service")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("Please make sure that the AccountService is installed and enabled.")); + g_signal_connect (dialog, "response", + G_CALLBACK (gtk_widget_destroy), + NULL); + gtk_widget_show (dialog); + + gtk_widget_set_sensitive (GTK_WIDGET (self->accounts_box), FALSE); + } + + g_signal_connect_object (self->um, "user-changed", G_CALLBACK (user_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (self->um, "user-is-logged-in-changed", G_CALLBACK (user_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (self->um, "user-added", G_CALLBACK (user_added), self, G_CONNECT_SWAPPED); + g_signal_connect_object (self->um, "user-removed", G_CALLBACK (user_changed), self, G_CONNECT_SWAPPED); + + reload_users (self, NULL); +} + +static void +add_unlock_tooltip (GtkWidget *widget) +{ + gchar *names[3]; + GIcon *icon; + + names[0] = "changes-allow-symbolic"; + names[1] = "changes-allow"; + names[2] = NULL; + icon = (GIcon *)g_themed_icon_new_from_names (names, -1); + setup_tooltip_with_embedded_icon (widget, + /* Translator comments: + * We split the line in 2 here to "make it look good", as there's + * no good way to do this in GTK+ for tooltips. See: + * https://bugzilla.gnome.org/show_bug.cgi?id=657168 */ + _("To make changes,\nclick the * icon first"), + "*", + icon); + g_object_unref (icon); + g_signal_connect (widget, "button-release-event", + G_CALLBACK (show_tooltip_now), NULL); +} + +static void +remove_unlock_tooltip (GtkWidget *widget) +{ + setup_tooltip_with_embedded_icon (widget, NULL, NULL, NULL); + g_signal_handlers_disconnect_by_func (widget, + G_CALLBACK (show_tooltip_now), NULL); +} + +static guint +get_num_active_admin (ActUserManager *um) +{ + GSList *list; + GSList *l; + guint num_admin = 0; + + list = act_user_manager_list_users (um); + for (l = list; l != NULL; l = l->next) { + ActUser *u = l->data; + if (act_user_get_account_type (u) == ACT_USER_ACCOUNT_TYPE_ADMINISTRATOR && !act_user_get_locked (u)) { + num_admin++; + } + } + g_slist_free (list); + + return num_admin; +} + +static gboolean +would_demote_only_admin (ActUser *user) +{ + ActUserManager *um = act_user_manager_get_default (); + + /* Prevent the user from demoting the only admin account. + * Returns TRUE when user is an administrator and there is only + * one enabled administrator. */ + + if (act_user_get_account_type (user) == ACT_USER_ACCOUNT_TYPE_STANDARD || + act_user_get_locked (user)) + return FALSE; + + if (get_num_active_admin (um) > 1) + return FALSE; + + return TRUE; +} + +static void +on_permission_changed (CcUserPanel *self) +{ + gboolean is_authorized; + gboolean self_selected; + ActUser *user; + + is_authorized = g_permission_get_allowed (G_PERMISSION (self->permission)); + + gtk_widget_set_visible (GTK_WIDGET (self->add_user_button), is_authorized); + + user = get_selected_user (self); + if (!user) { + return; + } + + self_selected = act_user_get_uid (user) == geteuid (); + gtk_widget_set_sensitive (GTK_WIDGET (self->remove_user_button), is_authorized && !self_selected + && !would_demote_only_admin (user)); + if (is_authorized) { + setup_tooltip_with_embedded_icon (GTK_WIDGET (self->remove_user_button), _("Delete the selected user account"), NULL, NULL); + } + else { + gchar *names[3]; + GIcon *icon; + + names[0] = "changes-allow-symbolic"; + names[1] = "changes-allow"; + names[2] = NULL; + icon = (GIcon *)g_themed_icon_new_from_names (names, -1); + + setup_tooltip_with_embedded_icon (GTK_WIDGET (self->remove_user_button), + _("To delete the selected user account,\nclick the * icon first"), + "*", + icon); + g_object_unref (icon); + } + + if (!act_user_is_local_account (user)) { + gtk_widget_set_sensitive (GTK_WIDGET (self->account_type_row), FALSE); + remove_unlock_tooltip (GTK_WIDGET (self->account_type_row)); + gtk_widget_set_sensitive (GTK_WIDGET (self->autologin_row), FALSE); + remove_unlock_tooltip (GTK_WIDGET (self->autologin_row)); + + } else if (is_authorized && act_user_is_local_account (user)) { + if (would_demote_only_admin (user)) { + gtk_widget_set_sensitive (GTK_WIDGET (self->account_type_row), FALSE); + } else { + gtk_widget_set_sensitive (GTK_WIDGET (self->account_type_row), TRUE); + } + remove_unlock_tooltip (GTK_WIDGET (self->account_type_row)); + + gtk_widget_set_sensitive (GTK_WIDGET (self->autologin_row), get_autologin_possible (user)); + remove_unlock_tooltip (GTK_WIDGET (self->autologin_row)); + } + else { + gtk_widget_set_sensitive (GTK_WIDGET (self->account_type_row), FALSE); + if (would_demote_only_admin (user)) { + remove_unlock_tooltip (GTK_WIDGET (self->account_type_row)); + } else { + add_unlock_tooltip (GTK_WIDGET (self->account_type_row)); + } + gtk_widget_set_sensitive (GTK_WIDGET (self->autologin_row), FALSE); + add_unlock_tooltip (GTK_WIDGET (self->autologin_row)); + } + + /* The full name entry: insensitive if remote or not authorized and not self */ + if (!act_user_is_local_account (user)) { + gtk_widget_set_sensitive (GTK_WIDGET (self->full_name_edit_button), FALSE); + remove_unlock_tooltip (GTK_WIDGET (self->full_name_stack)); + + } else if (is_authorized || self_selected) { + gtk_widget_set_sensitive (GTK_WIDGET (self->full_name_edit_button), TRUE); + remove_unlock_tooltip (GTK_WIDGET (self->full_name_stack)); + + } else { + gtk_widget_set_sensitive (GTK_WIDGET (self->full_name_edit_button), FALSE); + add_unlock_tooltip (GTK_WIDGET (self->full_name_stack)); + } + + if (is_authorized || self_selected) { + CcFingerprintState fingerprint_state = CC_FINGERPRINT_STATE_NONE; + + if (self->fingerprint_manager) + fingerprint_state = cc_fingerprint_manager_get_state (self->fingerprint_manager); + + gtk_stack_set_visible_child (self->user_icon_stack, GTK_WIDGET (self->user_icon_button)); + + gtk_widget_set_sensitive (GTK_WIDGET (self->language_row), TRUE); + remove_unlock_tooltip (GTK_WIDGET (self->language_row)); + + gtk_widget_set_sensitive (GTK_WIDGET (self->password_row), TRUE); + remove_unlock_tooltip (GTK_WIDGET (self->password_row)); + + gtk_widget_set_sensitive (GTK_WIDGET (self->fingerprint_row), + fingerprint_state != CC_FINGERPRINT_STATE_UPDATING); + remove_unlock_tooltip (GTK_WIDGET (self->fingerprint_row)); + + gtk_widget_set_sensitive (GTK_WIDGET (self->last_login_row), TRUE); + remove_unlock_tooltip (GTK_WIDGET (self->last_login_row)); +#ifdef HAVE_MALCONTENT + gtk_widget_set_sensitive (GTK_WIDGET (self->parental_controls_row), TRUE); + remove_unlock_tooltip (GTK_WIDGET (self->parental_controls_row)); +#endif + } + else { + gtk_stack_set_visible_child (self->user_icon_stack, GTK_WIDGET (self->user_icon_image)); + + gtk_widget_set_sensitive (GTK_WIDGET (self->language_row), FALSE); + add_unlock_tooltip (GTK_WIDGET (self->language_row)); + + gtk_widget_set_sensitive (GTK_WIDGET (self->password_row), FALSE); + add_unlock_tooltip (GTK_WIDGET (self->password_row)); + + gtk_widget_set_sensitive (GTK_WIDGET (self->fingerprint_row), FALSE); + add_unlock_tooltip (GTK_WIDGET (self->fingerprint_row)); + + gtk_widget_set_sensitive (GTK_WIDGET (self->last_login_row), FALSE); + add_unlock_tooltip (GTK_WIDGET (self->last_login_row)); +#ifdef HAVE_MALCONTENT + gtk_widget_set_sensitive (GTK_WIDGET (self->parental_controls_row), FALSE); + add_unlock_tooltip (GTK_WIDGET (self->parental_controls_row)); +#endif + } +} + +static void +setup_main_window (CcUserPanel *self) +{ + GIcon *icon; + GError *error = NULL; + gchar *names[3]; + gboolean loaded; + + self->other_accounts = 0; + + add_unlock_tooltip (GTK_WIDGET (self->user_icon_image)); + + self->permission = (GPermission *)polkit_permission_new_sync (USER_ACCOUNTS_PERMISSION, NULL, NULL, &error); + if (self->permission != NULL) { + g_signal_connect_object (self->permission, "notify", + G_CALLBACK (on_permission_changed), self, G_CONNECT_SWAPPED); + on_permission_changed (self); + } else { + g_warning ("Cannot create '%s' permission: %s", USER_ACCOUNTS_PERMISSION, error->message); + g_error_free (error); + } + + names[0] = "changes-allow-symbolic"; + names[1] = "changes-allow"; + names[2] = NULL; + icon = (GIcon *)g_themed_icon_new_from_names (names, -1); + setup_tooltip_with_embedded_icon (GTK_WIDGET (self->remove_user_button), + _("To delete the selected user account,\nclick the * icon first"), + "*", + icon); + g_object_unref (icon); + + g_object_get (self->um, "is-loaded", &loaded, NULL); + if (loaded) + users_loaded (self); + else + g_signal_connect_object (self->um, "notify::is-loaded", G_CALLBACK (users_loaded), self, G_CONNECT_SWAPPED); + + gtk_list_box_set_header_func (self->account_settings_listbox, + cc_list_box_update_header_func, + NULL, NULL); + gtk_list_box_set_header_func (self->authentication_and_login_listbox, + cc_list_box_update_header_func, + NULL, NULL); +} + +static GSettings * +settings_or_null (const gchar *schema) +{ + GSettingsSchemaSource *source = NULL; + gchar **non_relocatable = NULL; + gchar **relocatable = NULL; + GSettings *settings = NULL; + + source = g_settings_schema_source_get_default (); + if (!source) + return NULL; + + g_settings_schema_source_list_schemas (source, TRUE, &non_relocatable, &relocatable); + + if (g_strv_contains ((const gchar * const *)non_relocatable, schema) || + g_strv_contains ((const gchar * const *)relocatable, schema)) + settings = g_settings_new (schema); + + g_strfreev (non_relocatable); + g_strfreev (relocatable); + return settings; +} + +static void +cc_user_panel_constructed (GObject *object) +{ + CcUserPanel *self = CC_USER_PANEL (object); + CcShell *shell; + + G_OBJECT_CLASS (cc_user_panel_parent_class)->constructed (object); + + shell = cc_panel_get_shell (CC_PANEL (self)); + cc_shell_embed_widget_in_header (shell, GTK_WIDGET (self->add_user_button), GTK_POS_RIGHT); + + cc_permission_infobar_set_permission (self->permission_infobar, self->permission); +} + +static void +cc_user_panel_init (CcUserPanel *self) +{ + volatile GType type G_GNUC_UNUSED; + GtkCssProvider *provider; + + g_resources_register (cc_user_accounts_get_resource ()); + + /* register types that the builder might need */ + type = cc_user_image_get_type (); + type = cc_carousel_get_type (); + type = cc_permission_infobar_get_type (); + + gtk_widget_init_template (GTK_WIDGET (self)); + + self->um = act_user_manager_get_default (); + + provider = gtk_css_provider_new (); + gtk_css_provider_load_from_resource (provider, "/org/gnome/control-center/user-accounts/user-accounts-dialog.css"); + gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), + GTK_STYLE_PROVIDER (provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + g_object_unref (provider); + + self->login_screen_settings = settings_or_null ("org.gnome.login-screen"); + + self->avatar_chooser = cc_avatar_chooser_new (GTK_WIDGET (self->user_icon_button)); + setup_main_window (self); +} + +static void +cc_user_panel_dispose (GObject *object) +{ + CcUserPanel *self = CC_USER_PANEL (object); + + g_clear_object (&self->selected_user); + + g_clear_object (&self->login_screen_settings); + + g_clear_pointer ((GtkWidget **)&self->language_chooser, gtk_widget_destroy); + g_clear_object (&self->permission); + G_OBJECT_CLASS (cc_user_panel_parent_class)->dispose (object); +} + +static const char * +cc_user_panel_get_help_uri (CcPanel *panel) +{ + return "help:gnome-help/user-accounts"; +} + +static void +cc_user_panel_class_init (CcUserPanelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + CcPanelClass *panel_class = CC_PANEL_CLASS (klass); + + object_class->dispose = cc_user_panel_dispose; + object_class->constructed = cc_user_panel_constructed; + + panel_class->get_help_uri = cc_user_panel_get_help_uri; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/user-accounts/cc-user-panel.ui"); + + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, accounts_box); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, account_settings_box); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, account_settings_listbox); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, authentication_and_login_listbox); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, account_type_row); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, account_type_switch); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, add_user_button); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, autologin_row); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, autologin_switch); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, carousel); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, fingerprint_state_label); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, fingerprint_row); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, full_name_stack); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, full_name_label); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, full_name_edit_button); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, full_name_entry); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, language_button_label); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, language_row); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, last_login_button_label); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, last_login_row); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, no_users_box); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, notification_revealer); +#ifdef HAVE_MALCONTENT + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, parental_controls_button_label); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, parental_control_go_next); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, parental_controls_row); +#endif + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, password_button_label); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, password_row); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, permission_infobar); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, remove_user_button); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, stack); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, user_icon_button); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, user_icon_image); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, user_icon_image2); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, user_icon_stack); + gtk_widget_class_bind_template_child (widget_class, CcUserPanel, users_overlay); + + gtk_widget_class_bind_template_callback (widget_class, account_type_changed); + gtk_widget_class_bind_template_callback (widget_class, activate_row); + gtk_widget_class_bind_template_callback (widget_class, add_user); + gtk_widget_class_bind_template_callback (widget_class, autologin_changed); + gtk_widget_class_bind_template_callback (widget_class, change_fingerprint); + gtk_widget_class_bind_template_callback (widget_class, change_language); + gtk_widget_class_bind_template_callback (widget_class, full_name_edit_button_toggled); + gtk_widget_class_bind_template_callback (widget_class, full_name_entry_activate); + gtk_widget_class_bind_template_callback (widget_class, full_name_entry_key_press_cb); + gtk_widget_class_bind_template_callback (widget_class, change_password); + gtk_widget_class_bind_template_callback (widget_class, delete_user); + gtk_widget_class_bind_template_callback (widget_class, dismiss_notification); + gtk_widget_class_bind_template_callback (widget_class, restart_now); + gtk_widget_class_bind_template_callback (widget_class, set_selected_user); +} |