From 6f9eb8d1bb1fd9d9a93b3afda0be7578ca322caa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Sat, 4 Oct 2014 13:31:58 +0200 Subject: [PATCH] HaikuDepot: Validate input before creating account Provide error or info alert when fields are invalid, suggest providing an email, but allow to ignore the warning. --- src/apps/haikudepot/ui/UserLoginWindow.cpp | 144 ++++++++++++++++++++- src/apps/haikudepot/ui/UserLoginWindow.h | 2 + src/kits/interface/TextControl.cpp | 5 + 3 files changed, 148 insertions(+), 3 deletions(-) diff --git a/src/apps/haikudepot/ui/UserLoginWindow.cpp b/src/apps/haikudepot/ui/UserLoginWindow.cpp index f2598e5120..25624b811b 100644 --- a/src/apps/haikudepot/ui/UserLoginWindow.cpp +++ b/src/apps/haikudepot/ui/UserLoginWindow.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include "BitmapView.h" #include "Model.h" @@ -33,6 +34,7 @@ enum { MSG_SEND = 'send', MSG_TAB_SELECTED = 'tbsl', MSG_CAPTCHA_OBTAINED = 'cpob', + MSG_VALIDATE_FIELDS = 'vldt', MSG_LANGUAGE_SELECTED = 'lngs', }; @@ -92,12 +94,13 @@ UserLoginWindow::UserLoginWindow(BWindow* parent, BRect frame, Model& model) fPasswordField = new BTextControl(B_TRANSLATE("Pass phrase:"), "", NULL); fPasswordField->TextView()->HideTyping(true); - fNewUsernameField = new BTextControl(B_TRANSLATE("User name:"), "", NULL); - fNewPasswordField = new BTextControl(B_TRANSLATE("Pass phrase:"), "", + fNewUsernameField = new BTextControl(B_TRANSLATE("User name:"), "", NULL); + fNewPasswordField = new BTextControl(B_TRANSLATE("Pass phrase:"), "", + new BMessage(MSG_VALIDATE_FIELDS)); fNewPasswordField->TextView()->HideTyping(true); fRepeatPasswordField = new BTextControl(B_TRANSLATE("Repeat pass phrase:"), - "", NULL); + "", new BMessage(MSG_VALIDATE_FIELDS)); fRepeatPasswordField->TextView()->HideTyping(true); // Construct languages popup @@ -118,6 +121,19 @@ UserLoginWindow::UserLoginWindow(BWindow* parent, BRect frame, Model& model) fCaptchaView = new BitmapView("captcha view"); fCaptchaResultField = new BTextControl("", "", NULL); + // Setup modification messages on all text fields to trigger validation + // of input + fNewUsernameField->SetModificationMessage( + new BMessage(MSG_VALIDATE_FIELDS)); + fNewPasswordField->SetModificationMessage( + new BMessage(MSG_VALIDATE_FIELDS)); + fRepeatPasswordField->SetModificationMessage( + new BMessage(MSG_VALIDATE_FIELDS)); + fEmailField->SetModificationMessage( + new BMessage(MSG_VALIDATE_FIELDS)); + fCaptchaResultField->SetModificationMessage( + new BMessage(MSG_VALIDATE_FIELDS)); + fTabView = new TabView(BMessenger(this), BMessage(MSG_TAB_SELECTED)); @@ -182,6 +198,10 @@ void UserLoginWindow::MessageReceived(BMessage* message) { switch (message->what) { + case MSG_VALIDATE_FIELDS: + _ValidateCreateAccountFields(); + break; + case MSG_SEND: switch (fMode) { case LOGIN: @@ -220,6 +240,7 @@ UserLoginWindow::MessageReceived(BMessage* message) } else { fCaptchaView->SetBitmap(NULL); } + fCaptchaResultField->SetText(""); break; case MSG_LANGUAGE_SELECTED: @@ -262,6 +283,7 @@ UserLoginWindow::_SetMode(Mode mode) if (fCaptchaToken.IsEmpty()) _RequestCaptcha(); fNewUsernameField->MakeFocus(); + _ValidateCreateAccountFields(); break; default: break; @@ -269,6 +291,119 @@ UserLoginWindow::_SetMode(Mode mode) } +static int32 +count_digits(const BString& string) +{ + int32 digits = 0; + const char* c = string.String(); + for (int32 i = 0; i < string.CountChars(); i++) { + uint32 unicodeChar = BUnicodeChar::FromUTF8(&c); + if (BUnicodeChar::IsDigit(unicodeChar)) + digits++; + } + return digits; +} + + +static int32 +count_upper_case_letters(const BString& string) +{ + int32 upperCaseLetters = 0; + const char* c = string.String(); + for (int32 i = 0; i < string.CountChars(); i++) { + uint32 unicodeChar = BUnicodeChar::FromUTF8(&c); + if (BUnicodeChar::IsUpper(unicodeChar)) + upperCaseLetters++; + } + return upperCaseLetters; +} + + +bool +UserLoginWindow::_ValidateCreateAccountFields(bool alertProblems) +{ + BString nickName(fNewUsernameField->Text()); + BString password1(fNewPasswordField->Text()); + BString password2(fRepeatPasswordField->Text()); + BString email(fEmailField->Text()); + BString captcha(fCaptchaResultField->Text()); + + // TODO: Use the same validation as the web-serivce + bool validUserName = nickName.Length() >= 3; + fNewUsernameField->MarkAsInvalid(validUserName); + + bool validPassword = password1.Length() >= 8 + && count_digits(password1) >= 2 + && count_upper_case_letters(password1) >= 2; + fNewPasswordField->MarkAsInvalid(!validPassword); + fRepeatPasswordField->MarkAsInvalid(password1 != password2); + + bool validCaptcha = captcha.Length() > 0; + fCaptchaResultField->MarkAsInvalid(!validCaptcha); + + bool valid = validUserName && validPassword && password1 == password2 + && validCaptcha; + if (valid && email.Length() > 0) + return true; + + if (alertProblems) { + BString message; + alert_type alertType; + const char* okLabel = B_TRANSLATE("OK"); + const char* cancelLabel = NULL; + if (!valid) { + message = B_TRANSLATE("There are problems in the form:\n\n"); + alertType = B_WARNING_ALERT; + } else { + alertType = B_IDEA_ALERT; + okLabel = B_TRANSLATE("Ignore"); + cancelLabel = B_TRANSLATE("Cancel"); + } + + if (!validUserName) { + message << B_TRANSLATE( + "The user name needs to be at least " + "3 letters long.") << "\n\n"; + } + if (!validPassword) { + message << B_TRANSLATE( + "The password is too weak or invalid. " + "Please use at least 8 characters with " + "at least 2 numbers and 2 upper-case " + "letters.") << "\n\n"; + } + if (password1 != password2) { + message << B_TRANSLATE( + "The passwords do not match.") << "\n\n"; + } + if (email.Length() == 0) { + message << B_TRANSLATE( + "If you do not provide an email address, " + "you will not be able to reset your password " + "if you forget it.") << "\n\n"; + } + if (!validCaptcha) { + message << B_TRANSLATE( + "The captcha puzzle needs to be solved.") << "\n\n"; + } + + BAlert* alert = new(std::nothrow) BAlert( + B_TRANSLATE("Input validation"), + message, + okLabel, cancelLabel, NULL, + B_WIDTH_AS_USUAL, alertType); + + if (alert != NULL) { + int32 choice = alert->Go(); + if (choice == 1) + return false; + } + } + + return valid; +} + + void UserLoginWindow::_Login() { @@ -287,6 +422,9 @@ UserLoginWindow::_Login() void UserLoginWindow::_CreateAccount() { + if (!_ValidateCreateAccountFields(true)) + return; + BAutolock locker(&fLock); if (fWorkerThread >= 0) diff --git a/src/apps/haikudepot/ui/UserLoginWindow.h b/src/apps/haikudepot/ui/UserLoginWindow.h index 6d1540ba04..6d529e717e 100644 --- a/src/apps/haikudepot/ui/UserLoginWindow.h +++ b/src/apps/haikudepot/ui/UserLoginWindow.h @@ -40,6 +40,8 @@ private: }; void _SetMode(Mode mode); + bool _ValidateCreateAccountFields( + bool alertProblems = false); void _Login(); void _CreateAccount(); void _RequestCaptcha(); diff --git a/src/kits/interface/TextControl.cpp b/src/kits/interface/TextControl.cpp index caabbbed37..fac0166aea 100644 --- a/src/kits/interface/TextControl.cpp +++ b/src/kits/interface/TextControl.cpp @@ -336,10 +336,15 @@ BTextControl::Text() const void BTextControl::MarkAsInvalid(bool invalid) { + uint32 look = fLook; + if (invalid) fLook |= BControlLook::B_INVALID; else fLook &= ~BControlLook::B_INVALID; + + if (look != fLook) + Invalidate(); }