HaikuDepot: Implemented creating web-app user accounts
The accounts are created for real, but the information is not yet stored. Nothing which would need it is currently implemented, like rating packages. It is recommended to create accounts with a valid email address. Otherwise the password cannot be reset, and it will need to be soon, since there is a small change to the password storage in the pipe. The error response from the service is parsed and presented to the user, when the account could not be created. However, other checks before contacting the server are not performed, like if the two passphrases actually match. The UserLoginWindow now has the concept of a running worker thread, and while it runs, the UI controls (except Cancel) are disabled.
This commit is contained in:
parent
dfee849828
commit
9be707603f
@ -65,8 +65,9 @@ UserLoginWindow::UserLoginWindow(BWindow* parent, BRect frame, Model& model)
|
||||
B_FLOATING_WINDOW_LOOK, B_FLOATING_SUBSET_WINDOW_FEEL,
|
||||
B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS
|
||||
| B_NOT_RESIZABLE | B_NOT_ZOOMABLE),
|
||||
fModel(model),
|
||||
fMode(NONE),
|
||||
fRequestCaptchaThread(-1)
|
||||
fWorkerThread(-1)
|
||||
{
|
||||
AddToSubset(parent);
|
||||
|
||||
@ -140,8 +141,8 @@ UserLoginWindow::~UserLoginWindow()
|
||||
{
|
||||
BAutolock locker(&fLock);
|
||||
|
||||
if (fRequestCaptchaThread >= 0)
|
||||
wait_for_thread(fRequestCaptchaThread, NULL);
|
||||
if (fWorkerThread >= 0)
|
||||
wait_for_thread(fWorkerThread, NULL);
|
||||
}
|
||||
|
||||
|
||||
@ -184,6 +185,8 @@ UserLoginWindow::MessageReceived(BMessage* message)
|
||||
if (fCaptchaImage.Get() != NULL) {
|
||||
fCaptchaView->SetBitmap(
|
||||
fCaptchaImage->Bitmap(SharedBitmap::SIZE_ANY));
|
||||
} else {
|
||||
fCaptchaView->SetBitmap(NULL);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -237,31 +240,66 @@ UserLoginWindow::_Login()
|
||||
void
|
||||
UserLoginWindow::_CreateAccount()
|
||||
{
|
||||
// TODO: Implement...
|
||||
BAlert* alert = new BAlert(B_TRANSLATE("Not implemented"),
|
||||
B_TRANSLATE("Sorry, while the web application would already support "
|
||||
"creating accounts remotely, HaikuDepot was not yet updated to use "
|
||||
"this functionality."),
|
||||
B_TRANSLATE("Bummer"));
|
||||
alert->Go(NULL);
|
||||
BAutolock locker(&fLock);
|
||||
|
||||
if (fWorkerThread >= 0)
|
||||
return;
|
||||
|
||||
PostMessage(B_QUIT_REQUESTED);
|
||||
thread_id thread = spawn_thread(&_CreateAccountThreadEntry,
|
||||
"Account creator", B_NORMAL_PRIORITY, this);
|
||||
if (thread >= 0)
|
||||
_SetWorkerThread(thread);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UserLoginWindow::_RequestCaptcha()
|
||||
{
|
||||
if (Lock()) {
|
||||
fCaptchaToken = "";
|
||||
fCaptchaView->SetBitmap(NULL);
|
||||
fCaptchaImage.Unset();
|
||||
Unlock();
|
||||
}
|
||||
|
||||
BAutolock locker(&fLock);
|
||||
|
||||
if (fRequestCaptchaThread >= 0)
|
||||
if (fWorkerThread >= 0)
|
||||
return;
|
||||
|
||||
fRequestCaptchaThread = spawn_thread(&_RequestCaptchaThreadEntry,
|
||||
thread_id thread = spawn_thread(&_RequestCaptchaThreadEntry,
|
||||
"Captcha requester", B_NORMAL_PRIORITY, this);
|
||||
if (fRequestCaptchaThread >= 0)
|
||||
resume_thread(fRequestCaptchaThread);
|
||||
if (thread >= 0)
|
||||
_SetWorkerThread(thread);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UserLoginWindow::_SetWorkerThread(thread_id thread)
|
||||
{
|
||||
if (!Lock())
|
||||
return;
|
||||
|
||||
bool enabled = thread < 0;
|
||||
|
||||
fUsernameField->SetEnabled(enabled);
|
||||
fPasswordField->SetEnabled(enabled);
|
||||
fNewUsernameField->SetEnabled(enabled);
|
||||
fNewPasswordField->SetEnabled(enabled);
|
||||
fRepeatPasswordField->SetEnabled(enabled);
|
||||
fEmailField->SetEnabled(enabled);
|
||||
fLanguageCodeField->SetEnabled(enabled);
|
||||
fCaptchaResultField->SetEnabled(enabled);
|
||||
fSendButton->SetEnabled(enabled);
|
||||
|
||||
if (thread >= 0) {
|
||||
fWorkerThread = thread;
|
||||
resume_thread(fWorkerThread);
|
||||
} else {
|
||||
fWorkerThread = -1;
|
||||
}
|
||||
|
||||
Unlock();
|
||||
}
|
||||
|
||||
|
||||
@ -311,7 +349,152 @@ UserLoginWindow::_RequestCaptchaThread()
|
||||
fprintf(stderr, "Failed to obtain captcha: %s\n", strerror(status));
|
||||
}
|
||||
|
||||
fRequestCaptchaThread = -1;
|
||||
_SetWorkerThread(-1);
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
UserLoginWindow::_CreateAccountThreadEntry(void* data)
|
||||
{
|
||||
UserLoginWindow* window = reinterpret_cast<UserLoginWindow*>(data);
|
||||
window->_CreateAccountThread();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UserLoginWindow::_CreateAccountThread()
|
||||
{
|
||||
if (!Lock())
|
||||
return;
|
||||
|
||||
BString nickName(fNewUsernameField->Text());
|
||||
BString passwordClear(fNewPasswordField->Text());
|
||||
BString email(fEmailField->Text());
|
||||
BString captchaToken(fCaptchaToken);
|
||||
BString captchaResponse(fCaptchaResultField->Text());
|
||||
BString languageCode(fLanguageCodeField->Text());
|
||||
|
||||
Unlock();
|
||||
|
||||
WebAppInterface interface;
|
||||
BMessage info;
|
||||
|
||||
status_t status = interface.CreateUser(
|
||||
nickName, passwordClear, email, captchaToken, captchaResponse,
|
||||
languageCode, info);
|
||||
|
||||
BAutolock locker(&fLock);
|
||||
|
||||
BString error = B_TRANSLATE(
|
||||
"There was a puzzling response from the web service.");
|
||||
|
||||
BMessage result;
|
||||
if (status == B_OK) {
|
||||
if (info.FindMessage("result", &result) == B_OK) {
|
||||
error = "";
|
||||
} else if (info.FindMessage("error", &result) == B_OK) {
|
||||
result.PrintToStream();
|
||||
BString message;
|
||||
if (result.FindString("message", &message) == B_OK) {
|
||||
if (message == "captchabadresponse") {
|
||||
error = B_TRANSLATE("You have not solved the captcha "
|
||||
"puzzle correctly.");
|
||||
} else if (message == "validationerror") {
|
||||
_CollectValidationFailures(result, error);
|
||||
} else {
|
||||
error << B_TRANSLATE("The web service responded with: ");
|
||||
error << message;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error = B_TRANSLATE(
|
||||
"It was not possible to contact the web service.");
|
||||
}
|
||||
|
||||
locker.Unlock();
|
||||
|
||||
if (!error.IsEmpty()) {
|
||||
BAlert* alert = new(std::nothrow) BAlert(
|
||||
B_TRANSLATE("Failed to create account"),
|
||||
error,
|
||||
B_TRANSLATE("Close"));
|
||||
|
||||
if (alert != NULL)
|
||||
alert->Go();
|
||||
|
||||
fprintf(stderr,
|
||||
B_TRANSLATE("Failed to create account: %s\n"), error.String());
|
||||
|
||||
_SetWorkerThread(-1);
|
||||
|
||||
// We need a new captcha, it can be used only once
|
||||
fCaptchaToken = "";
|
||||
_RequestCaptcha();
|
||||
} else {
|
||||
_SetWorkerThread(-1);
|
||||
BMessenger(this).SendMessage(B_QUIT_REQUESTED);
|
||||
|
||||
BAlert* alert = new(std::nothrow) BAlert(
|
||||
B_TRANSLATE("Success"),
|
||||
B_TRANSLATE("Account created successfully. "
|
||||
"You can now rate packages and do other useful things."),
|
||||
B_TRANSLATE("Close"));
|
||||
|
||||
if (alert != NULL)
|
||||
alert->Go();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UserLoginWindow::_CollectValidationFailures(const BMessage& result,
|
||||
BString& error) const
|
||||
{
|
||||
error = B_TRANSLATE("There are problems with the data you entered:\n\n");
|
||||
|
||||
bool found = false;
|
||||
|
||||
BMessage data;
|
||||
BMessage failures;
|
||||
if (result.FindMessage("data", &data) == B_OK
|
||||
&& data.FindMessage("validationfailures", &failures) == B_OK) {
|
||||
int32 index = 0;
|
||||
while (true) {
|
||||
BString name;
|
||||
name << index++;
|
||||
BMessage failure;
|
||||
if (failures.FindMessage(name, &failure) != B_OK)
|
||||
break;
|
||||
|
||||
BString property;
|
||||
BString message;
|
||||
if (failure.FindString("property", &property) == B_OK
|
||||
&& failure.FindString("message", &message) == B_OK) {
|
||||
found = true;
|
||||
if (property == "nickname" && message == "notunique") {
|
||||
error << B_TRANSLATE(
|
||||
"The username is already taken. "
|
||||
"Please choose another.");
|
||||
} else if (property == "passwordClear"
|
||||
&& message == "invalid") {
|
||||
error << 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.");
|
||||
} else if (property == "email" && message == "malformed") {
|
||||
error << B_TRANSLATE(
|
||||
"The email address appears to be malformed.");
|
||||
} else {
|
||||
error << property << ": " << message;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
error << B_TRANSLATE("But none could be listed here, sorry.");
|
||||
}
|
||||
}
|
||||
|
@ -38,9 +38,18 @@ private:
|
||||
void _CreateAccount();
|
||||
void _RequestCaptcha();
|
||||
|
||||
void _SetWorkerThread(thread_id thread);
|
||||
|
||||
static int32 _RequestCaptchaThreadEntry(void* data);
|
||||
void _RequestCaptchaThread();
|
||||
|
||||
static int32 _CreateAccountThreadEntry(void* data);
|
||||
void _CreateAccountThread();
|
||||
|
||||
void _CollectValidationFailures(
|
||||
const BMessage& result,
|
||||
BString& error) const;
|
||||
|
||||
private:
|
||||
BTabView* fTabView;
|
||||
|
||||
@ -61,10 +70,12 @@ private:
|
||||
BString fCaptchaToken;
|
||||
BitmapRef fCaptchaImage;
|
||||
|
||||
Model& fModel;
|
||||
|
||||
Mode fMode;
|
||||
|
||||
BLocker fLock;
|
||||
thread_id fRequestCaptchaThread;
|
||||
thread_id fWorkerThread;
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user