From f66203a2674e483bf257db94cb78305754ccdf81 Mon Sep 17 00:00:00 2001 Message-Id: From: Mark Wooding Date: Sat, 14 Jun 2008 13:52:34 +0100 Subject: [PATCH] Better visual feedback in users window: the apply button is desensitized if it will not work an an error message is constantly displayed. If there are multiple errors you only get one, but they go in vertical order of text entry fields. Organization: Straylight/Edgeware From: Richard Kettlewell --- cgi/actions.c | 4 ++- disobedience/users.c | 66 +++++++++++++++++++++++++++++++++++++++++++- lib/Makefile.am | 1 + lib/email.c | 61 ++++++++++++++++++++++++++++++++++++++++ lib/sendmail.h | 1 + server/server.c | 1 + 6 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 lib/email.c diff --git a/cgi/actions.c b/cgi/actions.c index 449c5dc..8bbce87 100644 --- a/cgi/actions.c +++ b/cgi/actions.c @@ -434,7 +434,8 @@ static void act_register(void) { return; } /* We could well do better address validation but for now we'll just do the - * minimum */ + * minimum + /* TODO use email_valid() */ if(!strchr(email, '@')) { login_error("bademail"); return; @@ -524,6 +525,7 @@ static void act_edituser(void) { } } else password = password2 = 0; + /* TODO use email_valid() */ if(email && !strchr(email, '@')) { login_error("bademail"); return; diff --git a/disobedience/users.c b/disobedience/users.c index af42858..bb099f2 100644 --- a/disobedience/users.c +++ b/disobedience/users.c @@ -41,6 +41,10 @@ #include "disobedience.h" #include "bits.h" +#include "sendmail.h" + +static void users_details_sensitize_all(void); +static void users_set_report(const char *msg); static GtkWidget *users_window; static GtkListStore *users_list; @@ -54,6 +58,7 @@ static GtkWidget *users_details_email; static GtkWidget *users_details_password; static GtkWidget *users_details_password2; static GtkWidget *users_details_rights[32]; +static GtkWidget *users_reporter; static int users_details_row; static const char *users_selected; static const char *users_deferred_select; @@ -165,6 +170,11 @@ static void users_detail_generic(const char *title, 1, 1); /* x/ypadding */ } +static void users_entry_changed(GtkEditable attribute((unused)) *editable, + gpointer attribute((unused)) user_data) { + users_details_sensitize_all(); +} + /** @brief Add a row to the user details table * @param entryp Where to put GtkEntry * @param title Label for this row @@ -179,6 +189,8 @@ static void users_add_detail(GtkWidget **entryp, if(!(entry = *entryp)) { *entryp = entry = gtk_entry_new(); + g_signal_connect(entry, "changed", + G_CALLBACK(users_entry_changed), 0); users_detail_generic(title, entry); } gtk_entry_set_visibility(GTK_ENTRY(entry), @@ -220,6 +232,7 @@ static void users_details_sensitize(rights_type r) { /** @brief Set sensitivity of everything in sight */ static void users_details_sensitize_all(void) { int n; + const char *report = 0; for(n = 0; n < 32; ++n) if(users_details_rights[n]) @@ -231,8 +244,42 @@ static void users_details_sensitize_all(void) { users_details_sensitize(RIGHT_MOVE_ANY); users_details_sensitize(RIGHT_REMOVE_ANY); users_details_sensitize(RIGHT_SCRATCH_ANY); - gtk_widget_set_sensitive(users_apply_button, users_mode != MODE_NONE); + int apply_sensitive = 1; + if(users_mode == MODE_NONE) + apply_sensitive = 0; + else { + const char *name = gtk_entry_get_text(GTK_ENTRY(users_details_name)); + const char *email = gtk_entry_get_text(GTK_ENTRY(users_details_email)); + const char *pw = gtk_entry_get_text(GTK_ENTRY(users_details_password)); + const char *pw2 = gtk_entry_get_text(GTK_ENTRY(users_details_password2)); + /* Username must be filled in */ + if(!*name) { + apply_sensitive = 0; + if(!report) + report = "Must fill in username"; + } + /* Passwords must be nontrivial and match */ + if(!*pw) { + apply_sensitive = 0; + if(!report) + report = "Must fill in password"; + } + if(strcmp(pw, pw2)) { + apply_sensitive = 0; + if(!report) + report = "Passwords must match"; + } + /* Email address must be somewhat valid */ + if(*email) { + if(!email_valid(email)) { + apply_sensitive = 0; + report = "Invalid email address"; + } + } + } + gtk_widget_set_sensitive(users_apply_button, apply_sensitive); gtk_widget_set_sensitive(users_delete_button, !!users_selected); + users_set_report(report); } /** @brief Called when an _ALL widget is toggled @@ -593,6 +640,19 @@ static void users_selection_changed(GtkTreeSelection attribute((unused)) *treese mode(NONE); /* not editing *yet* */ } +static GtkWidget *users_make_reporter() { + if(!users_reporter) { + users_reporter = gtk_label_new(""); + gtk_label_set_ellipsize(GTK_LABEL(users_reporter), PANGO_ELLIPSIZE_END); + gtk_misc_set_alignment(GTK_MISC(users_reporter), 0.99, 0); + } + return users_reporter; +} + +static void users_set_report(const char *msg) { + gtk_label_set_text(GTK_LABEL(users_make_reporter()), msg ? msg : ""); +} + /** @brief Table of buttons below the user list */ static struct button users_buttons[] = { { @@ -674,6 +734,10 @@ void manage_users(void) { vbox2 = gtk_vbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox2), users_details_table, TRUE/*expand*/, TRUE/*fill*/, 0); + gtk_box_pack_start(GTK_BOX(vbox2), gtk_hseparator_new(), + FALSE/*expand*/, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox2), users_make_reporter(), + FALSE/*expand*/, FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox2), hbox2, FALSE/*expand*/, FALSE, 0); diff --git a/lib/Makefile.am b/lib/Makefile.am index 86886eb..f2943cc 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -44,6 +44,7 @@ libdisorder_a_SOURCES=charset.c charset.h \ dateparse.c dateparse.h xgetdate.c \ defs.c defs.h \ eclient.c eclient.h \ + email.c \ eventdist.c eventdist.h \ event.c event.h \ eventlog.c eventlog.h \ diff --git a/lib/email.c b/lib/email.c new file mode 100644 index 0000000..fb2f744 --- /dev/null +++ b/lib/email.c @@ -0,0 +1,61 @@ +/* + * This file is part of DisOrder + * Copyright (C) 2008 Richard Kettlewell + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +/** @file lib/email.c + * @brief Email addresses + */ + +#include "common.h" + +#include "sendmail.h" + +/** @brief Test email address validity + * @param address to verify + * @return 1 if it might be valid, 0 if it is definitely not + * + * This function doesn't promise to tell you whether an address is deliverable, + * it just does basic syntax checks. + */ +int email_valid(const char *address) { + /* There must be an '@' sign */ + const char *at = strchr(address, '@'); + if(!at) + return 0; + /* There must be only one of them */ + if(strchr(at + 1, '@')) + return 0; + /* It mustn't be the first or last character */ + if(at == address || !at[1]) + return 0; + /* Local part must be valid */ + /* TODO */ + /* Domain part must be valid */ + /* TODO */ + return 1; +} + +/* +Local Variables: +c-basic-offset:2 +comment-column:40 +fill-column:79 +indent-tabs-mode:nil +End: +*/ diff --git a/lib/sendmail.h b/lib/sendmail.h index e8d87a0..958cd79 100644 --- a/lib/sendmail.h +++ b/lib/sendmail.h @@ -35,6 +35,7 @@ pid_t sendmail_subprocess(const char *sender, const char *encoding, const char *content_type, const char *body); +int email_valid(const char *address); #endif /* SENDMAIL_H */ diff --git a/server/server.c b/server/server.c index 09f5098..6cfed04 100644 --- a/server/server.c +++ b/server/server.c @@ -1404,6 +1404,7 @@ static int c_reminder(struct conn *c, sink_writes(ev_writer_sink(c->w), "550 Cannot send a reminder email\n"); return 1; } + /* TODO use email_valid() */ if(!(email = kvp_get(k, "email")) || !strchr(email, '@')) { error(0, "user '%s' has no valid email address", vec[0]); -- [mdw]