[accounts-dialog] Improve async handling of user creating/deletion
- From: Matthias Clasen <matthiasc src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [accounts-dialog] Improve async handling of user creating/deletion
- Date: Sun, 24 Jan 2010 20:51:42 +0000 (UTC)
commit a22e79bd22d80b84e413e38cd8d632208fc56521
Author: Matthias Clasen <mclasen redhat com>
Date: Sun Jan 24 15:48:15 2010 -0500
Improve async handling of user creating/deletion
Use the GAsyncResult pattern, translate errors that are returned,
don't show an error dialog if authentication failed/was canceled,
select newly created users, etc, etc.
TODO | 1 -
src/main.c | 106 ++++++++++++++++-----
src/um-account-dialog.c | 69 ++++++++++++--
src/um-account-dialog.h | 10 ++-
src/um-user-manager.c | 238 +++++++++++++++++++++++++++++++++++++++++-----
src/um-user-manager.h | 40 +++++++--
6 files changed, 394 insertions(+), 70 deletions(-)
---
diff --git a/TODO b/TODO
index e4dfb25..514725d 100644
--- a/TODO
+++ b/TODO
@@ -3,7 +3,6 @@ Dialog
- add supervisor user to supervised accounts
- move 'disable account' to restrictions
- more async ?
-- select new user after creating
- find a place to point to for safe password tips, or include them
- review text in acount type dialog
- hide unimplemented parts
diff --git a/src/main.c b/src/main.c
index 42bd368..6f5f0e1 100644
--- a/src/main.c
+++ b/src/main.c
@@ -259,10 +259,67 @@ user_changed (UmUserManager *um, UmUser *user, UserAccountDialog *d)
}
static void
+select_created_user (UmUser *user, UserAccountDialog *d)
+{
+ GtkTreeView *tv;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ UmUser *current;
+ GtkTreePath *path;
+
+ tv = (GtkTreeView *)get_widget (d, "list-treeview");
+ model = gtk_tree_view_get_model (tv);
+ selection = gtk_tree_view_get_selection (tv);
+
+ gtk_tree_model_get_iter_first (model, &iter);
+ do {
+ gtk_tree_model_get (model, &iter, USER_COL, ¤t, -1);
+ if (user == current) {
+ path = gtk_tree_model_get_path (model, &iter);
+ gtk_tree_view_scroll_to_cell (tv, path, NULL, FALSE, 0.0, 0.0);
+ gtk_tree_selection_select_path (selection, path);
+ gtk_tree_path_free (path);
+ break;
+ }
+ } while (gtk_tree_model_iter_next (model, &iter));
+}
+
+static void
add_user (GtkButton *button, UserAccountDialog *d)
{
um_account_dialog_show (d->account_dialog,
- GTK_WINDOW (get_widget (d, "user-account-window")));
+ GTK_WINDOW (get_widget (d, "user-account-window")),
+ (UserCreatedCallback)select_created_user, d);
+}
+
+static void
+delete_user_done (UmUserManager *manager,
+ GAsyncResult *res,
+ UserAccountDialog *d)
+{
+ GError *error;
+
+ error = NULL;
+ if (!um_user_manager_delete_user_finish (manager, res, &error)) {
+ if (!g_error_matches (error, UM_USER_MANAGER_ERROR, UM_USER_MANAGER_ERROR_PERMISSION_DENIED)) {
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new (gtk_window_get_transient_for (GTK_WINDOW (d->main_window)),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ _("Failed to delete user"));
+
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ "%s", error->message);
+
+ g_signal_connect (G_OBJECT (dialog), "response",
+ G_CALLBACK (gtk_widget_destroy), NULL);
+ gtk_window_present (GTK_WINDOW (dialog));
+ }
+ g_error_free (error);
+ }
}
static void
@@ -271,24 +328,25 @@ delete_user_response (GtkWidget *dialog,
UserAccountDialog *d)
{
UmUser *user;
+ gboolean remove_files;
gtk_widget_destroy (dialog);
user = get_selected_user (d);
- switch (response_id) {
- case GTK_RESPONSE_NO:
- g_debug ("Goodbye, %s. Your files are toast.\n",
- um_user_get_real_name (user));
- um_user_manager_delete_user (d->um, user, TRUE);
- break;
- case GTK_RESPONSE_YES:
- g_debug ("Goodbye, %s. Your files are safe.\n",
- um_user_get_real_name (user));
- um_user_manager_delete_user (d->um, user, FALSE);
- break;
- default: ;
+ if (response_id == GTK_RESPONSE_NO) {
+ remove_files = TRUE;
}
+ else {
+ remove_files = FALSE;
+ }
+
+ um_user_manager_delete_user (d->um,
+ user,
+ remove_files,
+ (GAsyncReadyCallback)delete_user_done,
+ d,
+ NULL);
g_object_unref (user);
}
@@ -469,7 +527,7 @@ update_data_change_buttons (GObject *source,
gboolean is_authorized;
error = NULL;
- is_authorized = FALSE;
+ is_authorized = FALSE;
result = polkit_authority_check_authorization_finish (d->authority,
res,
&error);
@@ -515,10 +573,10 @@ update_account_type_change_buttons (GObject *source,
PolkitAuthorizationResult *result;
gboolean is_authorized;
gboolean can_change_password;
- UmUser *user;
+ UmUser *user;
error = NULL;
- is_authorized = FALSE;
+ is_authorized = FALSE;
result = polkit_authority_check_authorization_finish (d->authority,
res,
&error);
@@ -544,11 +602,11 @@ update_account_type_change_buttons (GObject *source,
user = get_selected_user (d);
if (um_user_get_uid (user) == geteuid ()) {
- can_change_password = TRUE;
- }
- else {
- can_change_password = is_authorized;
- }
+ can_change_password = TRUE;
+ }
+ else {
+ can_change_password = is_authorized;
+ }
if (can_change_password) {
gtk_widget_show (get_widget (d, "change-password-button"));
@@ -603,7 +661,7 @@ update_create_buttons_cb (GObject *source,
UmUser *user;
error = NULL;
- is_authorized = FALSE;
+ is_authorized = FALSE;
result = polkit_authority_check_authorization_finish (d->authority,
res,
&error);
@@ -629,9 +687,9 @@ update_create_buttons_cb (GObject *source,
if (user && um_user_get_uid (user) == getuid ()) {
gtk_widget_set_sensitive (get_widget (d, "delete-user-button"), FALSE);
}
- else {
+ else {
gtk_widget_set_sensitive (get_widget (d, "delete-user-button"), TRUE);
- }
+ }
}
else {
gtk_widget_hide (get_widget (d, "add-delete-buttonbox"));
diff --git a/src/um-account-dialog.c b/src/um-account-dialog.c
index 8edc623..c2b545b 100644
--- a/src/um-account-dialog.c
+++ b/src/um-account-dialog.c
@@ -22,6 +22,8 @@
#include "config.h"
#include <stdlib.h>
+#include <sys/types.h>
+#include <pwd.h>
#include <glib.h>
#include <glib/gi18n.h>
@@ -40,6 +42,9 @@ struct _UmAccountDialog {
gboolean valid_name;
gboolean valid_shortname;
+
+ UserCreatedCallback user_created_callback;
+ gpointer user_created_data;
};
static void
@@ -50,6 +55,40 @@ cancel_account_dialog (GtkButton *button,
}
static void
+create_user_done (UmUserManager *manager,
+ GAsyncResult *res,
+ UmAccountDialog *um)
+{
+ UmUser *user;
+ GError *error;
+
+ error = NULL;
+ if (!um_user_manager_create_user_finish (manager, res, &user, &error)) {
+
+ if (!g_error_matches (error, UM_USER_MANAGER_ERROR, UM_USER_MANAGER_ERROR_PERMISSION_DENIED)) {
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new (gtk_window_get_transient_for (GTK_WINDOW (um->dialog)),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ _("Failed to create user"));
+
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ "%s", error->message);
+
+ g_signal_connect (G_OBJECT (dialog), "response",
+ G_CALLBACK (gtk_widget_destroy), NULL);
+ gtk_window_present (GTK_WINDOW (dialog));
+ }
+ g_error_free (error);
+ }
+ else {
+ um->user_created_callback (user, um->user_created_data);
+ }
+}
+
+static void
accept_account_dialog (GtkButton *button,
UmAccountDialog *um)
{
@@ -67,7 +106,13 @@ accept_account_dialog (GtkButton *button,
gtk_tree_model_get (model, &iter, 1, &account_type, -1);
manager = um_user_manager_ref_default ();
- um_user_manager_create_user (manager, shortname, name, account_type);
+ um_user_manager_create_user (manager,
+ shortname,
+ name,
+ account_type,
+ (GAsyncReadyCallback)create_user_done,
+ um,
+ NULL);
g_object_unref (manager);
gtk_widget_hide (um->dialog);
@@ -76,14 +121,11 @@ accept_account_dialog (GtkButton *button,
static gboolean
is_shortname_used (const gchar *shortname)
{
- UmUserManager *manager;
- UmUser *user;
+ struct passwd *pwent;
- manager = um_user_manager_ref_default ();
- user = um_user_manager_get_user (manager, shortname);
- g_object_unref (manager);
+ pwent = getpwnam (shortname);
- return user != NULL;
+ return pwent != NULL;
}
static void
@@ -422,11 +464,17 @@ um_account_dialog_free (UmAccountDialog *um)
}
void
-um_account_dialog_show (UmAccountDialog *um,
- GtkWindow *parent)
+um_account_dialog_show (UmAccountDialog *um,
+ GtkWindow *parent,
+ UserCreatedCallback user_created_callback,
+ gpointer user_created_data)
{
+ GtkTreeModel *model;
+
gtk_entry_set_text (GTK_ENTRY (um->name_entry), "");
gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (um->shortname_combo))), "");
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (um->shortname_combo));
+ gtk_list_store_clear (GTK_LIST_STORE (model));
gtk_combo_box_set_active (GTK_COMBO_BOX (um->account_type_combo), 0);
gtk_window_set_transient_for (GTK_WINDOW (um->dialog), parent);
@@ -434,6 +482,9 @@ um_account_dialog_show (UmAccountDialog *um,
gtk_widget_grab_focus (um->name_entry);
um->valid_name = um->valid_shortname = TRUE;
+
+ um->user_created_callback = user_created_callback;
+ um->user_created_data = user_created_data;
}
diff --git a/src/um-account-dialog.h b/src/um-account-dialog.h
index 3d4a8f2..e3b2ebc 100644
--- a/src/um-account-dialog.h
+++ b/src/um-account-dialog.h
@@ -29,10 +29,14 @@ G_BEGIN_DECLS
typedef struct _UmAccountDialog UmAccountDialog;
+typedef void (*UserCreatedCallback) (UmUser *user, gpointer data);
+
UmAccountDialog *um_account_dialog_new (void);
-void um_account_dialog_free (UmAccountDialog *dialog);
-void um_account_dialog_show (UmAccountDialog *dialog,
- GtkWindow *parent);
+void um_account_dialog_free (UmAccountDialog *dialog);
+void um_account_dialog_show (UmAccountDialog *dialog,
+ GtkWindow *parent,
+ UserCreatedCallback user_created,
+ gpointer data);
G_END_DECLS
diff --git a/src/um-user-manager.c b/src/um-user-manager.c
index 7330e91..f611051 100644
--- a/src/um-user-manager.c
+++ b/src/um-user-manager.c
@@ -117,7 +117,7 @@ remove_user_from_dupe_ring (UmUserManager *manager,
um_user_show_short_display_name (user);
- dupes = g_object_get_data (user, "dupes");
+ dupes = g_object_get_data (G_OBJECT (user), "dupes");
if (dupes == NULL) {
return;
@@ -129,7 +129,7 @@ remove_user_from_dupe_ring (UmUserManager *manager,
g_signal_emit (manager, signals[USER_CHANGED], 0, dup);
g_list_free_1 (dupes->next);
- g_object_set_data (dup, "dupes", NULL);
+ g_object_set_data (G_OBJECT (dup), "dupes", NULL);
}
else {
dupes->next->prev = dupes->prev;
@@ -137,7 +137,7 @@ remove_user_from_dupe_ring (UmUserManager *manager,
}
g_list_free_1 (dupes);
- g_object_set_data (user, "dupes", NULL);
+ g_object_set_data (G_OBJECT (user), "dupes", NULL);
}
static gboolean
@@ -165,17 +165,17 @@ add_user_to_dupe_ring (UmUserManager *manager,
um_user_show_full_display_name (user);
- dupes = g_object_get_data (dup, "dupes");
+ dupes = g_object_get_data (G_OBJECT (dup), "dupes");
if (!dupes) {
um_user_show_full_display_name (dup);
g_signal_emit (manager, signals[USER_CHANGED], 0, dup);
dupes = g_list_append (NULL, dup);
- g_object_set_data (dup, "dupes", dupes);
+ g_object_set_data (G_OBJECT (dup), "dupes", dupes);
dupes->next = dupes->prev = dupes;
}
l = g_list_append (NULL, user);
- g_object_set_data (user, "dupes", l);
+ g_object_set_data (G_OBJECT (user), "dupes", l);
l->prev = dupes->prev;
dupes->prev->next = l;
l->next = dupes;
@@ -333,11 +333,11 @@ clear_dup (gpointer key,
/* don't bother maintaining the ring, we're destroying the
* entire hash table anyway
*/
- dupes = g_object_get_data (value, "dupes");
+ dupes = g_object_get_data (G_OBJECT (value), "dupes");
if (dupes) {
g_list_free_1 (dupes);
- g_object_set_data (value, "dupes", NULL);
+ g_object_set_data (G_OBJECT (value), "dupes", NULL);
}
}
@@ -369,30 +369,210 @@ um_user_manager_ref_default (void)
return UM_USER_MANAGER (user_manager_object);
}
+typedef struct {
+ UmUserManager *manager;
+ gchar *user_name;
+ GAsyncReadyCallback callback;
+ gpointer data;
+ GDestroyNotify destroy;
+} AsyncUserOpData;
+
+static void
+async_user_op_data_free (gpointer d)
+{
+ AsyncUserOpData *data = d;
+
+ g_object_unref (data->manager);
+
+ g_free (data->user_name);
+
+ if (data->destroy)
+ data->destroy (data->data);
+
+ g_free (data);
+}
+
+static void
+create_user_done (DBusGProxy *proxy,
+ DBusGProxyCall *call_id,
+ gpointer user_data)
+{
+ AsyncUserOpData *data = user_data;
+ gchar *path;
+ GError *error;
+ GSimpleAsyncResult *res;
+
+ res = g_simple_async_result_new (G_OBJECT (data->manager),
+ data->callback,
+ data->data,
+ um_user_manager_create_user);
+ error = NULL;
+ if (!dbus_g_proxy_end_call (proxy,
+ call_id,
+ &error,
+ DBUS_TYPE_G_OBJECT_PATH, &path,
+ G_TYPE_INVALID)) {
+ /* dbus-glib fail:
+ * We have to translate the errors manually here, since
+ * calling dbus_g_error_has_name on the error returned in
+ * um_user_manager_create_user_finish doesn't work.
+ */
+ if (dbus_g_error_has_name (error, "org.freedesktop.Accounts.Error.PermissionDenied")) {
+ g_simple_async_result_set_error (res,
+ UM_USER_MANAGER_ERROR,
+ UM_USER_MANAGER_ERROR_PERMISSION_DENIED,
+ "Not authorized");
+ }
+ else if (dbus_g_error_has_name (error, "org.freedesktop.Accounts.Error.UserExists")) {
+ g_simple_async_result_set_error (res,
+ UM_USER_MANAGER_ERROR,
+ UM_USER_MANAGER_ERROR_USER_EXISTS,
+ _("A user with name '%s' already exists."),
+ data->user_name);
+ }
+ else {
+ g_simple_async_result_set_from_error (res, error);
+ }
+ g_error_free (error);
+ }
+ else {
+ g_simple_async_result_set_op_res_gpointer (res, path, g_free);
+ }
+
+ data->callback (G_OBJECT (data->manager), G_ASYNC_RESULT (res), data->data);
+}
+
+gboolean
+um_user_manager_create_user_finish (UmUserManager *manager,
+ GAsyncResult *result,
+ UmUser **user,
+ GError **error)
+{
+ gchar *path;
+ GSimpleAsyncResult *res;
+
+ res = G_SIMPLE_ASYNC_RESULT (result);
+
+ *user = NULL;
+
+ if (g_simple_async_result_propagate_error (res, error)) {
+ return FALSE;
+ }
+
+ path = g_simple_async_result_get_op_res_gpointer (res);
+ *user = g_hash_table_lookup (manager->user_by_object_path, path);
+
+ return TRUE;
+}
+
void
-um_user_manager_create_user (UmUserManager *manager,
- const char *user_name,
- const char *real_name,
- gint account_type)
+um_user_manager_create_user (UmUserManager *manager,
+ const char *user_name,
+ const char *real_name,
+ gint account_type,
+ GAsyncReadyCallback done,
+ gpointer done_data,
+ GDestroyNotify destroy)
+{
+ AsyncUserOpData *data;
+
+ data = g_new0 (AsyncUserOpData, 1);
+ data->manager = g_object_ref (manager);
+ data->user_name = g_strdup (user_name);
+ data->callback = done;
+ data->data = done_data;
+ data->destroy = destroy;
+
+ dbus_g_proxy_begin_call (manager->proxy,
+ "CreateUser",
+ create_user_done,
+ data,
+ async_user_op_data_free,
+ G_TYPE_STRING, user_name,
+ G_TYPE_STRING, real_name,
+ G_TYPE_INT, account_type,
+ G_TYPE_INVALID);
+}
+
+static void
+delete_user_done (DBusGProxy *proxy,
+ DBusGProxyCall *call_id,
+ gpointer user_data)
+{
+ AsyncUserOpData *data = user_data;
+ GError *error;
+ GSimpleAsyncResult *res;
+
+ res = g_simple_async_result_new (G_OBJECT (data->manager),
+ data->callback,
+ data->data,
+ um_user_manager_delete_user);
+ error = NULL;
+ if (!dbus_g_proxy_end_call (proxy,
+ call_id,
+ &error,
+ G_TYPE_INVALID)) {
+ if (dbus_g_error_has_name (error, "org.freedesktop.Accounts.Error.PermissionDenied")) {
+ g_simple_async_result_set_error (res,
+ UM_USER_MANAGER_ERROR,
+ UM_USER_MANAGER_ERROR_PERMISSION_DENIED,
+ "Not authorized");
+ }
+ else if (dbus_g_error_has_name (error, "org.freedesktop.Accounts.Error.UserDoesntExists")) {
+ g_simple_async_result_set_error (res,
+ UM_USER_MANAGER_ERROR,
+ UM_USER_MANAGER_ERROR_USER_DOES_NOT_EXIST,
+ _("This user does not exist."));
+ }
+ else {
+ g_simple_async_result_set_from_error (res, error);
+ g_error_free (error);
+ }
+ }
+
+ data->callback (G_OBJECT (data->manager), G_ASYNC_RESULT (res), data->data);
+}
+
+gboolean
+um_user_manager_delete_user_finish (UmUserManager *manager,
+ GAsyncResult *result,
+ GError **error)
{
- dbus_g_proxy_call_no_reply (manager->proxy,
- "CreateUser",
- G_TYPE_STRING, user_name,
- G_TYPE_STRING, real_name,
- G_TYPE_INT, account_type,
- G_TYPE_INVALID);
+ GSimpleAsyncResult *res;
+
+ res = G_SIMPLE_ASYNC_RESULT (result);
+
+ if (g_simple_async_result_propagate_error (res, error)) {
+ return FALSE;
+ }
+
+ return TRUE;
}
void
-um_user_manager_delete_user (UmUserManager *manager,
- UmUser *user,
- gboolean remove_files)
+um_user_manager_delete_user (UmUserManager *manager,
+ UmUser *user,
+ gboolean remove_files,
+ GAsyncReadyCallback done,
+ gpointer done_data,
+ GDestroyNotify destroy)
{
- dbus_g_proxy_call_no_reply (manager->proxy,
- "DeleteUser",
- G_TYPE_INT64, um_user_get_uid (user),
- G_TYPE_BOOLEAN, remove_files,
- G_TYPE_INVALID);
+ AsyncUserOpData *data;
+
+ data = g_new0 (AsyncUserOpData, 1);
+ data->manager = g_object_ref (manager);
+ data->callback = done;
+ data->data = done_data;
+ data->destroy = destroy;
+
+ dbus_g_proxy_begin_call (manager->proxy,
+ "DeleteUser",
+ delete_user_done,
+ data,
+ async_user_op_data_free,
+ G_TYPE_INT64, um_user_get_uid (user),
+ G_TYPE_BOOLEAN, remove_files,
+ G_TYPE_INVALID);
}
GSList *
@@ -436,3 +616,9 @@ um_user_manager_no_service (UmUserManager *manager)
{
return manager->no_service;
}
+
+GQuark
+um_user_manager_error_quark (void)
+{
+ return g_quark_from_static_string ("um-user-manager-error-quark");
+}
diff --git a/src/um-user-manager.h b/src/um-user-manager.h
index a25b36a..b051a88 100644
--- a/src/um-user-manager.h
+++ b/src/um-user-manager.h
@@ -22,6 +22,7 @@
#define __UM_USER_MANAGER__
#include <glib-object.h>
+#include <gio/gio.h>
#include <dbus/dbus-glib.h>
#include "um-user.h"
@@ -62,6 +63,17 @@ typedef struct
} UmUserManagerClass;
+typedef enum {
+ UM_USER_MANAGER_ERROR_FAILED,
+ UM_USER_MANAGER_ERROR_USER_EXISTS,
+ UM_USER_MANAGER_ERROR_USER_DOES_NOT_EXIST,
+ UM_USER_MANAGER_ERROR_PERMISSION_DENIED
+} UmUserManagerError;
+
+#define UM_USER_MANAGER_ERROR um_user_manager_error_quark ()
+
+GQuark um_user_manager_error_quark (void);
+
GType um_user_manager_get_type (void);
UmUserManager * um_user_manager_ref_default (void);
@@ -73,13 +85,27 @@ UmUser * um_user_manager_get_user (UmUserManager *manager
const char *user_name);
UmUser * um_user_manager_get_user_by_id (UmUserManager *manager,
uid_t uid);
-void um_user_manager_create_user (UmUserManager *manager,
- const char *user_name,
- const char *real_name,
- gint account_type);
-void um_user_manager_delete_user (UmUserManager *manager,
- UmUser *user,
- gboolean remove_files);
+
+void um_user_manager_create_user (UmUserManager *manager,
+ const char *user_name,
+ const char *real_name,
+ gint account_type,
+ GAsyncReadyCallback done,
+ gpointer user_data,
+ GDestroyNotify destroy);
+gboolean um_user_manager_create_user_finish (UmUserManager *manager,
+ GAsyncResult *result,
+ UmUser **user,
+ GError **error);
+void um_user_manager_delete_user (UmUserManager *manager,
+ UmUser *user,
+ gboolean remove_files,
+ GAsyncReadyCallback done,
+ gpointer user_data,
+ GDestroyNotify destroy);
+gboolean um_user_manager_delete_user_finish (UmUserManager *manager,
+ GAsyncResult *result,
+ GError **error);
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]