HaikuDepot: Conditions on Create User

When the user chooses to create a new user they
are able to view the current usage conditions for
users.  They are also required to agree to the
conditions and they are required to confirm that
they meet the minimum age requirement.

Relates to 15209

Change-Id: I83cdaabe1b3da31a4cd21139b72341f4b93cab85
Reviewed-on: https://review.haiku-os.org/c/haiku/+/1842
Reviewed-by: Jérôme Duval <[email protected]>
This commit is contained in:
Andrew Lindesay
2019-09-10 06:10:17 +00:00
parent a310e5e52f
commit 0c82f64bf6
8 changed files with 368 additions and 123 deletions
+17 -14
View File
@@ -6,20 +6,23 @@
#define HAIKU_DEPOT_CONSTANTS_H #define HAIKU_DEPOT_CONSTANTS_H
enum { enum {
MSG_MAIN_WINDOW_CLOSED = 'mwcl', MSG_MAIN_WINDOW_CLOSED = 'mwcl',
MSG_PACKAGE_SELECTED = 'pkgs', MSG_PACKAGE_SELECTED = 'pkgs',
MSG_PACKAGE_WORKER_BUSY = 'pkwb', MSG_PACKAGE_WORKER_BUSY = 'pkwb',
MSG_PACKAGE_WORKER_IDLE = 'pkwi', MSG_PACKAGE_WORKER_IDLE = 'pkwi',
MSG_ADD_VISIBLE_PACKAGES = 'avpk', MSG_ADD_VISIBLE_PACKAGES = 'avpk',
MSG_UPDATE_SELECTED_PACKAGE = 'uspk', MSG_UPDATE_SELECTED_PACKAGE = 'uspk',
MSG_CLIENT_TOO_OLD = 'oldc', MSG_CLIENT_TOO_OLD = 'oldc',
MSG_NETWORK_TRANSPORT_ERROR = 'nett', MSG_NETWORK_TRANSPORT_ERROR = 'nett',
MSG_SERVER_ERROR = 'svre', MSG_SERVER_ERROR = 'svre',
MSG_SERVER_DATA_CHANGED = 'svdc', MSG_SERVER_DATA_CHANGED = 'svdc',
MSG_ALERT_SIMPLE_ERROR = 'nser', MSG_ALERT_SIMPLE_ERROR = 'nser',
MSG_DID_ADD_USER_RATING = 'adur', MSG_DID_ADD_USER_RATING = 'adur',
MSG_DID_UPDATE_USER_RATING = 'upur', MSG_DID_UPDATE_USER_RATING = 'upur',
MSG_LANGUAGE_SELECTED = 'lngs' MSG_LANGUAGE_SELECTED = 'lngs',
MSG_VIEW_LATEST_USER_USAGE_CONDITIONS = 'vluc',
MSG_USER_USAGE_CONDITIONS_DATA = 'uucd',
MSG_USER_USAGE_CONDITIONS_ERROR = 'uuce'
}; };
+33 -20
View File
@@ -693,31 +693,44 @@ status_t
WebAppInterface::CreateUser(const BString& nickName, WebAppInterface::CreateUser(const BString& nickName,
const BString& passwordClear, const BString& email, const BString& passwordClear, const BString& email,
const BString& captchaToken, const BString& captchaResponse, const BString& captchaToken, const BString& captchaResponse,
const BString& languageCode, BMessage& message) const BString& languageCode, const BString& userUsageConditionsCode,
BMessage& message)
{ {
JsonBuilder builder; // BHttpRequest later takes ownership of this.
builder BMallocIO* requestEnvelopeData = new BMallocIO();
.AddValue("jsonrpc", "2.0") BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
.AddValue("id", ++fRequestIndex)
.AddValue("method", "createUser")
.AddArray("params")
.AddObject()
.AddValue("nickname", nickName)
.AddValue("passwordClear", passwordClear);
if (!email.IsEmpty()) requestEnvelopeWriter.WriteObjectStart();
builder.AddValue("email", email); _WriteStandardJsonRpcEnvelopeValues(requestEnvelopeWriter, "createUser");
requestEnvelopeWriter.WriteObjectName("params");
requestEnvelopeWriter.WriteArrayStart();
builder.AddValue("captchaToken", captchaToken) requestEnvelopeWriter.WriteObjectStart();
.AddValue("captchaResponse", captchaResponse)
.AddValue("naturalLanguageCode", languageCode)
.EndObject()
.EndArray()
;
BString jsonString = builder.End(); requestEnvelopeWriter.WriteObjectName("nickname");
requestEnvelopeWriter.WriteString(nickName.String());
requestEnvelopeWriter.WriteObjectName("passwordClear");
requestEnvelopeWriter.WriteString(passwordClear.String());
requestEnvelopeWriter.WriteObjectName("captchaToken");
requestEnvelopeWriter.WriteString(captchaToken.String());
requestEnvelopeWriter.WriteObjectName("captchaResponse");
requestEnvelopeWriter.WriteString(captchaResponse.String());
requestEnvelopeWriter.WriteObjectName("naturalLanguageCode");
requestEnvelopeWriter.WriteString(languageCode.String());
requestEnvelopeWriter.WriteObjectName("userUsageConditionsCode");
requestEnvelopeWriter.WriteString(userUsageConditionsCode.String());
return _SendJsonRequest("user", jsonString, 0, message); if (!email.IsEmpty()) {
requestEnvelopeWriter.WriteObjectName("email");
requestEnvelopeWriter.WriteString(email.String());
}
requestEnvelopeWriter.WriteObjectEnd();
requestEnvelopeWriter.WriteArrayEnd();
requestEnvelopeWriter.WriteObjectEnd();
return _SendJsonRequest("user", requestEnvelopeData,
_LengthAndSeekToZero(requestEnvelopeData), 0, message);
} }
@@ -106,6 +106,7 @@ public:
const BString& captchaToken, const BString& captchaToken,
const BString& captchaResponse, const BString& captchaResponse,
const BString& languageCode, const BString& languageCode,
const BString& userUsageConditionsCode,
BMessage& message); BMessage& message);
status_t AuthenticateUser(const BString& nickName, status_t AuthenticateUser(const BString& nickName,
+7 -7
View File
@@ -68,7 +68,6 @@ enum {
MSG_PACKAGE_CHANGED = 'pchd', MSG_PACKAGE_CHANGED = 'pchd',
MSG_WORK_STATUS_CHANGE = 'wsch', MSG_WORK_STATUS_CHANGE = 'wsch',
MSG_WORK_STATUS_CLEAR = 'wscl', MSG_WORK_STATUS_CLEAR = 'wscl',
MSG_VIEW_LATEST_USER_USAGE_CONDITIONS = 'vluc',
MSG_SHOW_FEATURED_PACKAGES = 'sofp', MSG_SHOW_FEATURED_PACKAGES = 'sofp',
MSG_SHOW_AVAILABLE_PACKAGES = 'savl', MSG_SHOW_AVAILABLE_PACKAGES = 'savl',
@@ -740,10 +739,11 @@ MainWindow::_BuildUserMenu(BMenuBar* menuBar)
new BMessage(MSG_LOG_OUT)); new BMessage(MSG_LOG_OUT));
fUserMenu->AddItem(fLogOutItem); fUserMenu->AddItem(fLogOutItem);
fLogOutItem = new BMenuItem(B_TRANSLATE("View latest user usage conditions" BMenuItem *latestUserUsageConditionsMenuItem =
B_UTF8_ELLIPSIS), new BMenuItem(B_TRANSLATE("View latest user usage conditions"
new BMessage(MSG_VIEW_LATEST_USER_USAGE_CONDITIONS)); B_UTF8_ELLIPSIS),
fUserMenu->AddItem(fLogOutItem); new BMessage(MSG_VIEW_LATEST_USER_USAGE_CONDITIONS));
fUserMenu->AddItem(latestUserUsageConditionsMenuItem);
menuBar->AddItem(fUserMenu); menuBar->AddItem(fUserMenu);
} }
@@ -1349,6 +1349,6 @@ void
MainWindow::_ViewLatestUserUsageConditions() MainWindow::_ViewLatestUserUsageConditions()
{ {
UserUsageConditionsWindow* window = new UserUsageConditionsWindow( UserUsageConditionsWindow* window = new UserUsageConditionsWindow(
this, BRect(0, 0, 500, 400), fModel, LATEST); fModel, LATEST);
window->Show(); window->Show();
} }
+194 -25
View File
@@ -15,6 +15,7 @@
#include <Autolock.h> #include <Autolock.h>
#include <AutoLocker.h> #include <AutoLocker.h>
#include <Catalog.h> #include <Catalog.h>
#include <CheckBox.h>
#include <Button.h> #include <Button.h>
#include <LayoutBuilder.h> #include <LayoutBuilder.h>
#include <MenuField.h> #include <MenuField.h>
@@ -22,17 +23,27 @@
#include <TextControl.h> #include <TextControl.h>
#include <UnicodeChar.h> #include <UnicodeChar.h>
#include "AppUtils.h"
#include "BitmapView.h" #include "BitmapView.h"
#include "HaikuDepotConstants.h" #include "HaikuDepotConstants.h"
#include "LanguageMenuUtils.h" #include "LanguageMenuUtils.h"
#include "LinkView.h"
#include "Model.h" #include "Model.h"
#include "TabView.h" #include "TabView.h"
#include "UserUsageConditions.h"
#include "UserUsageConditionsWindow.h"
#include "WebAppInterface.h" #include "WebAppInterface.h"
#undef B_TRANSLATION_CONTEXT #undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "UserLoginWindow" #define B_TRANSLATION_CONTEXT "UserLoginWindow"
#define PLACEHOLDER_TEXT B_UTF8_ELLIPSIS
enum ActionTabs {
TAB_LOGIN = 0,
TAB_CREATE_ACCOUNT = 1
};
enum { enum {
MSG_SEND = 'send', MSG_SEND = 'send',
@@ -41,6 +52,26 @@ enum {
MSG_VALIDATE_FIELDS = 'vldt' MSG_VALIDATE_FIELDS = 'vldt'
}; };
/*! The creation of an account requires that some prerequisite data is first
loaded in or may later need to be refreshed. This enum controls what
elements of the setup should be performed.
*/
enum CreateAccountSetupMask {
CREATE_CAPTCHA = 1 << 1,
FETCH_USER_USAGE_CONDITIONS = 1 << 2
};
/*! A background thread runs to gather data to use in the interface for creating
a new user. This structure is passed to the background thread.
*/
struct CreateAccountSetupThreadData {
UserLoginWindow* window;
uint32 mask;
// defines what setup steps are required
};
UserLoginWindow::UserLoginWindow(BWindow* parent, BRect frame, Model& model) UserLoginWindow::UserLoginWindow(BWindow* parent, BRect frame, Model& model)
: :
@@ -51,6 +82,7 @@ UserLoginWindow::UserLoginWindow(BWindow* parent, BRect frame, Model& model)
fPreferredLanguageCode(LANGUAGE_DEFAULT_CODE), fPreferredLanguageCode(LANGUAGE_DEFAULT_CODE),
fModel(model), fModel(model),
fMode(NONE), fMode(NONE),
fUserUsageConditions(NULL),
fWorkerThread(-1) fWorkerThread(-1)
{ {
AddToSubset(parent); AddToSubset(parent);
@@ -90,6 +122,19 @@ UserLoginWindow::UserLoginWindow(BWindow* parent, BRect frame, Model& model)
fEmailField = new BTextControl(B_TRANSLATE("Email address:"), "", NULL); fEmailField = new BTextControl(B_TRANSLATE("Email address:"), "", NULL);
fCaptchaView = new BitmapView("captcha view"); fCaptchaView = new BitmapView("captcha view");
fCaptchaResultField = new BTextControl("", "", NULL); fCaptchaResultField = new BTextControl("", "", NULL);
fConfirmMinimumAgeCheckBox = new BCheckBox("confirm minimum age",
PLACEHOLDER_TEXT,
// is filled in when the user usage conditions data is available
NULL);
fConfirmMinimumAgeCheckBox->SetEnabled(false);
fConfirmUserUsageConditionsCheckBox = new BCheckBox(
"confirm user usage conditions",
B_TRANSLATE("I agree to the usage conditions for users"),
NULL);
fUserUsageConditionsLink = new LinkView("user usage conditions view",
B_TRANSLATE("View the usage conditions for users"),
new BMessage(MSG_VIEW_LATEST_USER_USAGE_CONDITIONS));
fUserUsageConditionsLink->SetTarget(this);
// Setup modification messages on all text fields to trigger validation // Setup modification messages on all text fields to trigger validation
// of input // of input
@@ -103,7 +148,6 @@ UserLoginWindow::UserLoginWindow(BWindow* parent, BRect frame, Model& model)
new BMessage(MSG_VALIDATE_FIELDS)); new BMessage(MSG_VALIDATE_FIELDS));
fCaptchaResultField->SetModificationMessage( fCaptchaResultField->SetModificationMessage(
new BMessage(MSG_VALIDATE_FIELDS)); new BMessage(MSG_VALIDATE_FIELDS));
fTabView = new TabView(BMessenger(this), fTabView = new TabView(BMessenger(this),
BMessage(MSG_TAB_SELECTED)); BMessage(MSG_TAB_SELECTED));
@@ -126,7 +170,9 @@ UserLoginWindow::UserLoginWindow(BWindow* parent, BRect frame, Model& model)
.AddMenuField(fLanguageCodeField, 0, 4) .AddMenuField(fLanguageCodeField, 0, 4)
.Add(fCaptchaView, 0, 5) .Add(fCaptchaView, 0, 5)
.Add(fCaptchaResultField, 1, 5) .Add(fCaptchaResultField, 1, 5)
.Add(fConfirmMinimumAgeCheckBox, 1, 6)
.Add(fConfirmUserUsageConditionsCheckBox, 1, 7)
.Add(fUserUsageConditionsLink, 1, 8)
.SetInsets(B_USE_DEFAULT_SPACING) .SetInsets(B_USE_DEFAULT_SPACING)
; ;
fTabView->AddTab(createAccountCard); fTabView->AddTab(createAccountCard);
@@ -172,6 +218,10 @@ UserLoginWindow::MessageReceived(BMessage* message)
_ValidateCreateAccountFields(); _ValidateCreateAccountFields();
break; break;
case MSG_VIEW_LATEST_USER_USAGE_CONDITIONS:
_ViewUserUsageConditions();
break;
case MSG_SEND: case MSG_SEND:
switch (fMode) { switch (fMode) {
case LOGIN: case LOGIN:
@@ -190,10 +240,10 @@ UserLoginWindow::MessageReceived(BMessage* message)
int32 tabIndex; int32 tabIndex;
if (message->FindInt32("tab index", &tabIndex) == B_OK) { if (message->FindInt32("tab index", &tabIndex) == B_OK) {
switch (tabIndex) { switch (tabIndex) {
case 0: case TAB_LOGIN:
_SetMode(LOGIN); _SetMode(LOGIN);
break; break;
case 1: case TAB_CREATE_ACCOUNT:
_SetMode(CREATE_ACCOUNT); _SetMode(CREATE_ACCOUNT);
break; break;
default: default:
@@ -212,6 +262,10 @@ UserLoginWindow::MessageReceived(BMessage* message)
fCaptchaResultField->SetText(""); fCaptchaResultField->SetText("");
break; break;
case MSG_USER_USAGE_CONDITIONS_DATA:
_SetUserUsageConditions(new UserUsageConditions(message));
break;
case MSG_LANGUAGE_SELECTED: case MSG_LANGUAGE_SELECTED:
message->FindString("code", &fPreferredLanguageCode); message->FindString("code", &fPreferredLanguageCode);
break; break;
@@ -242,15 +296,14 @@ UserLoginWindow::_SetMode(Mode mode)
switch (fMode) { switch (fMode) {
case LOGIN: case LOGIN:
fTabView->Select((int32)0); fTabView->Select(TAB_LOGIN);
fSendButton->SetLabel(B_TRANSLATE("Log in")); fSendButton->SetLabel(B_TRANSLATE("Log in"));
fUsernameField->MakeFocus(); fUsernameField->MakeFocus();
break; break;
case CREATE_ACCOUNT: case CREATE_ACCOUNT:
fTabView->Select(1); fTabView->Select(TAB_CREATE_ACCOUNT);
fSendButton->SetLabel(B_TRANSLATE("Create account")); fSendButton->SetLabel(B_TRANSLATE("Create account"));
if (fCaptchaToken.IsEmpty()) _CreateAccountSetupIfNecessary();
_RequestCaptcha();
fNewUsernameField->MakeFocus(); fNewUsernameField->MakeFocus();
_ValidateCreateAccountFields(); _ValidateCreateAccountFields();
break; break;
@@ -296,6 +349,9 @@ UserLoginWindow::_ValidateCreateAccountFields(bool alertProblems)
BString password2(fRepeatPasswordField->Text()); BString password2(fRepeatPasswordField->Text());
BString email(fEmailField->Text()); BString email(fEmailField->Text());
BString captcha(fCaptchaResultField->Text()); BString captcha(fCaptchaResultField->Text());
bool minimumAgeConfirmed = fConfirmMinimumAgeCheckBox->Value() != 0;
bool userUsageConditionsConfirmed =
fConfirmUserUsageConditionsCheckBox->Value() != 0;
// TODO: Use the same validation as the web-serivce // TODO: Use the same validation as the web-serivce
bool validUserName = nickName.Length() >= 3; bool validUserName = nickName.Length() >= 3;
@@ -356,6 +412,16 @@ UserLoginWindow::_ValidateCreateAccountFields(bool alertProblems)
"The captcha puzzle needs to be solved.") << "\n\n"; "The captcha puzzle needs to be solved.") << "\n\n";
} }
if (!minimumAgeConfirmed) {
message << B_TRANSLATE(
"The minimum age requirements must be met.") << "\n\n";
}
if (!userUsageConditionsConfirmed) {
message << B_TRANSLATE(
"The usage conditions for users must be agreed to.") << "\n\n";
}
BAlert* alert = new(std::nothrow) BAlert( BAlert* alert = new(std::nothrow) BAlert(
B_TRANSLATE("Input validation"), B_TRANSLATE("Input validation"),
message, message,
@@ -407,24 +473,57 @@ UserLoginWindow::_CreateAccount()
void void
UserLoginWindow::_RequestCaptcha() UserLoginWindow::_CreateAccountSetupIfNecessary()
{ {
if (Lock()) { uint32 setupMask = 0;
fCaptchaToken = ""; if (fCaptchaToken.IsEmpty())
fCaptchaView->UnsetBitmap(); setupMask |= CREATE_CAPTCHA;
fCaptchaImage.Unset(); if (fUserUsageConditions == NULL)
Unlock(); setupMask |= FETCH_USER_USAGE_CONDITIONS;
} _CreateAccountSetup(setupMask);
}
void
UserLoginWindow::_CreateAccountSetup(uint32 mask)
{
if (mask == 0)
return;
BAutolock locker(&fLock); BAutolock locker(&fLock);
if (fWorkerThread >= 0) if (fWorkerThread >= 0)
return; return;
thread_id thread = spawn_thread(&_RequestCaptchaThreadEntry, if (!Lock())
"Captcha requester", B_NORMAL_PRIORITY, this); debugger("unable to lock the user login window");
if ((mask & CREATE_CAPTCHA) != 0) {
fCaptchaToken = "";
fCaptchaView->UnsetBitmap();
fCaptchaImage.Unset();
}
if ((mask & FETCH_USER_USAGE_CONDITIONS) != 0) {
fConfirmMinimumAgeCheckBox->SetLabel(PLACEHOLDER_TEXT);
fConfirmMinimumAgeCheckBox->SetValue(0);
fConfirmUserUsageConditionsCheckBox->SetValue(0);
}
Unlock();
CreateAccountSetupThreadData* threadData = new CreateAccountSetupThreadData;
threadData->window = this;
threadData->mask = mask;
thread_id thread = spawn_thread(&_CreateAccountSetupThreadEntry,
"Create account setup", B_NORMAL_PRIORITY, threadData);
if (thread >= 0) if (thread >= 0)
_SetWorkerThread(thread); _SetWorkerThread(thread);
else {
debugger("unable to start a thread to gather data for creating an "
"account");
}
} }
@@ -469,6 +568,9 @@ UserLoginWindow::_SetWorkerThread(thread_id thread)
fEmailField->SetEnabled(enabled); fEmailField->SetEnabled(enabled);
fLanguageCodeField->SetEnabled(enabled); fLanguageCodeField->SetEnabled(enabled);
fCaptchaResultField->SetEnabled(enabled); fCaptchaResultField->SetEnabled(enabled);
fConfirmMinimumAgeCheckBox->SetEnabled(enabled);
fConfirmUserUsageConditionsCheckBox->SetEnabled(enabled);
fUserUsageConditionsLink->SetEnabled(enabled);
fSendButton->SetEnabled(enabled); fSendButton->SetEnabled(enabled);
if (thread >= 0) { if (thread >= 0) {
@@ -482,6 +584,45 @@ UserLoginWindow::_SetWorkerThread(thread_id thread)
} }
void
UserLoginWindow::_CreateAccountUserUsageConditionsSetupThread()
{
UserUsageConditions conditions;
WebAppInterface interface = fModel.GetWebAppInterface();
if (interface.RetrieveUserUsageConditions(NULL, conditions) == B_OK) {
BMessage dataMessage(MSG_USER_USAGE_CONDITIONS_DATA);
conditions.Archive(&dataMessage, true);
BMessenger(this).SendMessage(&dataMessage);
} else {
AppUtils::NotifySimpleError(
B_TRANSLATE("User Usage Conditions Download Problem"),
B_TRANSLATE("An error has arisen downloading the user usage "
"conditions. Check the log for details and try again."));
BMessenger(this).SendMessage(B_QUIT_REQUESTED);
}
}
/*! This method is hit when the user usage conditions data arrives back from the
server. At this point some of the UI elements may need to be updated.
*/
void
UserLoginWindow::_SetUserUsageConditions(
UserUsageConditions* userUsageConditions)
{
fUserUsageConditions = userUsageConditions;
BString minimumAgeString;
minimumAgeString.SetToFormat("%" B_PRId8,
fUserUsageConditions->MinimumAge());
BString label = B_TRANSLATE(
"I am %MinimumAgeYears% years of age or older");
label.ReplaceAll("%MinimumAgeYears%", minimumAgeString);
fConfirmMinimumAgeCheckBox->SetLabel(label);
}
int32 int32
UserLoginWindow::_AuthenticateThreadEntry(void* data) UserLoginWindow::_AuthenticateThreadEntry(void* data)
{ {
@@ -549,16 +690,22 @@ UserLoginWindow::_AuthenticateThread()
int32 int32
UserLoginWindow::_RequestCaptchaThreadEntry(void* data) UserLoginWindow::_CreateAccountSetupThreadEntry(void* data)
{ {
UserLoginWindow* window = reinterpret_cast<UserLoginWindow*>(data); CreateAccountSetupThreadData* threadData =
window->_RequestCaptchaThread(); reinterpret_cast<CreateAccountSetupThreadData*>(data);
if ((threadData->mask & CREATE_CAPTCHA) != 0)
threadData->window->_CreateAccountCaptchaSetupThread();
if ((threadData->mask & FETCH_USER_USAGE_CONDITIONS) != 0)
threadData->window->_CreateAccountUserUsageConditionsSetupThread();
threadData->window->_SetWorkerThread(-1);
delete threadData;
return 0; return 0;
} }
void void
UserLoginWindow::_RequestCaptchaThread() UserLoginWindow::_CreateAccountCaptchaSetupThread()
{ {
WebAppInterface interface; WebAppInterface interface;
BMessage info; BMessage info;
@@ -593,8 +740,6 @@ UserLoginWindow::_RequestCaptchaThread()
} else { } else {
fprintf(stderr, "Failed to obtain captcha: %s\n", strerror(status)); fprintf(stderr, "Failed to obtain captcha: %s\n", strerror(status));
} }
_SetWorkerThread(-1);
} }
@@ -613,12 +758,22 @@ UserLoginWindow::_CreateAccountThread()
if (!Lock()) if (!Lock())
return; return;
if (fUserUsageConditions == NULL)
debugger("missing user usage conditions when creating an account");
if (fConfirmMinimumAgeCheckBox->Value() == 0
|| fConfirmUserUsageConditionsCheckBox->Value() == 0) {
debugger("expected that the minimum age and user usage conditions are"
"agreed to at this point");
}
BString nickName(fNewUsernameField->Text()); BString nickName(fNewUsernameField->Text());
BString passwordClear(fNewPasswordField->Text()); BString passwordClear(fNewPasswordField->Text());
BString email(fEmailField->Text()); BString email(fEmailField->Text());
BString captchaToken(fCaptchaToken); BString captchaToken(fCaptchaToken);
BString captchaResponse(fCaptchaResultField->Text()); BString captchaResponse(fCaptchaResultField->Text());
BString languageCode(fPreferredLanguageCode); BString languageCode(fPreferredLanguageCode);
BString userUsageConditionsCode(fUserUsageConditions->Code());
Unlock(); Unlock();
@@ -627,7 +782,7 @@ UserLoginWindow::_CreateAccountThread()
status_t status = interface.CreateUser( status_t status = interface.CreateUser(
nickName, passwordClear, email, captchaToken, captchaResponse, nickName, passwordClear, email, captchaToken, captchaResponse,
languageCode, info); languageCode, userUsageConditionsCode, info);
BAutolock locker(&fLock); BAutolock locker(&fLock);
@@ -678,7 +833,7 @@ UserLoginWindow::_CreateAccountThread()
// We need a new captcha, it can be used only once // We need a new captcha, it can be used only once
fCaptchaToken = ""; fCaptchaToken = "";
_RequestCaptcha(); _CreateAccountSetup(CREATE_CAPTCHA);
} else { } else {
fModel.SetAuthorization(nickName, passwordClear, true); fModel.SetAuthorization(nickName, passwordClear, true);
@@ -739,3 +894,17 @@ UserLoginWindow::_CollectValidationFailures(const BMessage& result,
error << B_TRANSLATE("But none could be listed here, sorry."); error << B_TRANSLATE("But none could be listed here, sorry.");
} }
} }
/*! Opens a new window that shows the already downloaded user usage conditions.
*/
void
UserLoginWindow::_ViewUserUsageConditions()
{
if (fUserUsageConditions == NULL)
debugger("the user usage conditions should be set");
UserUsageConditionsWindow* window = new UserUsageConditionsWindow(
fModel, *fUserUsageConditions);
window->Show();
}
+20 -3
View File
@@ -14,11 +14,14 @@
class BButton; class BButton;
class BCheckBox;
class BMenuField; class BMenuField;
class BTabView; class BTabView;
class BTextControl; class BTextControl;
class BitmapView; class BitmapView;
class LinkView;
class Model; class Model;
class UserUsageConditions;
class UserLoginWindow : public BWindow { class UserLoginWindow : public BWindow {
@@ -34,6 +37,7 @@ public:
const BMessage& message); const BMessage& message);
private: private:
enum Mode { enum Mode {
NONE = 0, NONE = 0,
LOGIN, LOGIN,
@@ -45,7 +49,8 @@ private:
bool alertProblems = false); bool alertProblems = false);
void _Login(); void _Login();
void _CreateAccount(); void _CreateAccount();
void _RequestCaptcha(); void _CreateAccountSetup(uint32 mask);
void _CreateAccountSetupIfNecessary();
void _LoginSuccessful(const BString& message); void _LoginSuccessful(const BString& message);
void _SetWorkerThread(thread_id thread); void _SetWorkerThread(thread_id thread);
@@ -53,8 +58,12 @@ private:
static int32 _AuthenticateThreadEntry(void* data); static int32 _AuthenticateThreadEntry(void* data);
void _AuthenticateThread(); void _AuthenticateThread();
static int32 _RequestCaptchaThreadEntry(void* data); static int32 _CreateAccountSetupThreadEntry(void* data);
void _RequestCaptchaThread(); void _CreateAccountCaptchaSetupThread();
void _CreateAccountUserUsageConditionsSetupThread();
void _SetUserUsageConditions(
UserUsageConditions* userUsageConditions);
static int32 _CreateAccountThreadEntry(void* data); static int32 _CreateAccountThreadEntry(void* data);
void _CreateAccountThread(); void _CreateAccountThread();
@@ -63,6 +72,8 @@ private:
const BMessage& result, const BMessage& result,
BString& error) const; BString& error) const;
void _ViewUserUsageConditions();
private: private:
BMessenger fOnSuccessTarget; BMessenger fOnSuccessTarget;
BMessage fOnSuccessMessage; BMessage fOnSuccessMessage;
@@ -79,6 +90,9 @@ private:
BMenuField* fLanguageCodeField; BMenuField* fLanguageCodeField;
BitmapView* fCaptchaView; BitmapView* fCaptchaView;
BTextControl* fCaptchaResultField; BTextControl* fCaptchaResultField;
BCheckBox* fConfirmMinimumAgeCheckBox;
BCheckBox* fConfirmUserUsageConditionsCheckBox;
LinkView* fUserUsageConditionsLink;
BButton* fSendButton; BButton* fSendButton;
BButton* fCancelButton; BButton* fCancelButton;
@@ -91,6 +105,9 @@ private:
Mode fMode; Mode fMode;
UserUsageConditions*
fUserUsageConditions;
BLocker fLock; BLocker fLock;
thread_id fWorkerThread; thread_id fWorkerThread;
}; };
@@ -43,48 +43,117 @@
#define LINES_INTRODUCTION_TEXT 2 #define LINES_INTRODUCTION_TEXT 2
#define WINDOW_FRAME BRect(0, 0, 500, 400)
enum {
MSG_USER_USAGE_CONDITIONS_DATA = 'uucd',
MSG_USER_USAGE_CONDITIONS_ERROR = 'uuce'
};
UserUsageConditionsWindow::UserUsageConditionsWindow(BWindow* parent, UserUsageConditionsWindow::UserUsageConditionsWindow(Model& model,
BRect frame, Model& model, UserUsageConditionsSelectionMode mode) UserUsageConditions& userUsageConditions)
: :
BWindow(frame, B_TRANSLATE("User Usage Conditions"), BWindow(WINDOW_FRAME, B_TRANSLATE("User Usage Conditions"),
B_FLOATING_WINDOW_LOOK, B_FLOATING_SUBSET_WINDOW_FEEL, B_FLOATING_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS
| B_NOT_RESIZABLE | B_NOT_ZOOMABLE),
fMode(FIXED),
fModel(model),
fWorkerThread(-1)
{
_InitUiControls();
BScrollView* scrollView = new BScrollView("copy scroll view", fCopyView,
0, false, true, B_PLAIN_BORDER);
BButton* okButton = new BButton("ok", B_TRANSLATE("OK"),
new BMessage(B_QUIT_REQUESTED));
BLayoutBuilder::Group<>(this, B_VERTICAL)
.SetInsets(B_USE_WINDOW_INSETS)
.Add(fVersionStringView, 1)
.Add(scrollView, 97)
.Add(fAgeNoteStringView, 1)
.AddGroup(B_HORIZONTAL, 1)
.AddGlue()
.Add(okButton)
.End()
.End();
CenterOnScreen();
_DisplayData(userUsageConditions);
}
UserUsageConditionsWindow::UserUsageConditionsWindow(
Model& model, UserUsageConditionsSelectionMode mode)
:
BWindow(WINDOW_FRAME, B_TRANSLATE("User Usage Conditions"),
B_FLOATING_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS
| B_NOT_RESIZABLE | B_NOT_ZOOMABLE), | B_NOT_RESIZABLE | B_NOT_ZOOMABLE),
fMode(mode), fMode(mode),
fModel(model), fModel(model),
fWorkerThread(-1) fWorkerThread(-1)
{ {
AddToSubset(parent);
if (mode != LATEST) if (mode != LATEST)
debugger("only the LATEST user usage conditions are handled for now"); debugger("only the LATEST user usage conditions are handled for now");
_InitUiControls();
fWorkerIndicator = new BarberPole("fetch data worker indicator"); fWorkerIndicator = new BarberPole("fetch data worker indicator");
BSize workerIndicatorSize; BSize workerIndicatorSize;
workerIndicatorSize.SetHeight(20); workerIndicatorSize.SetHeight(20);
fWorkerIndicator->SetExplicitMinSize(workerIndicatorSize); fWorkerIndicator->SetExplicitMinSize(workerIndicatorSize);
fCopyView = new MarkupTextView("copy view");
fCopyView->SetViewUIColor(B_NO_COLOR);
fCopyView->SetLowColor(RGB_COLOR_WHITE);
fCopyView->SetInsets(8.0f);
BScrollView* scrollView = new BScrollView("copy scroll view", fCopyView,
0, false, true, B_PLAIN_BORDER);
BTextView* introductionTextView = new BTextView("introduction text view"); BTextView* introductionTextView = new BTextView("introduction text view");
introductionTextView->AdoptSystemColors(); introductionTextView->AdoptSystemColors();
introductionTextView->MakeEditable(false); introductionTextView->MakeEditable(false);
introductionTextView->MakeSelectable(false); introductionTextView->MakeSelectable(false);
introductionTextView->SetText(B_TRANSLATE(_IntroductionTextForMode(mode))); introductionTextView->SetText(B_TRANSLATE(_IntroductionTextForMode(mode)));
BSize introductionSize;
introductionSize.SetHeight(
_ExpectedIntroductionTextHeight(introductionTextView));
introductionTextView->SetExplicitPreferredSize(introductionSize);
BScrollView* scrollView = new BScrollView("copy scroll view", fCopyView,
0, false, true, B_PLAIN_BORDER);
BButton* okButton = new BButton("ok", B_TRANSLATE("OK"),
new BMessage(B_QUIT_REQUESTED));
BLayoutBuilder::Group<>(this, B_VERTICAL)
.SetInsets(B_USE_WINDOW_INSETS)
.Add(introductionTextView, 1)
.AddGlue()
.Add(fVersionStringView, 1)
.Add(scrollView, 95)
.Add(fAgeNoteStringView, 1)
.AddGroup(B_HORIZONTAL, 1)
.AddGlue()
.Add(okButton)
.End()
.Add(fWorkerIndicator, 1)
.End();
CenterOnScreen();
_FetchData();
// start a new thread to pull down the user usage conditions data.
}
UserUsageConditionsWindow::~UserUsageConditionsWindow()
{
}
/*! This sets up the UI controls / interface elements that are not specific to
a given mode of viewing.
*/
void
UserUsageConditionsWindow::_InitUiControls()
{
fCopyView = new MarkupTextView("copy view");
fCopyView->SetViewUIColor(B_NO_COLOR);
fCopyView->SetLowColor(RGB_COLOR_WHITE);
fCopyView->SetInsets(8.0f);
fAgeNoteStringView = new BStringView("age note string view", fAgeNoteStringView = new BStringView("age note string view",
PLACEHOLDER_TEXT); PLACEHOLDER_TEXT);
fAgeNoteStringView->AdoptSystemColors(); fAgeNoteStringView->AdoptSystemColors();
@@ -102,38 +171,6 @@ UserUsageConditionsWindow::UserUsageConditionsWindow(BWindow* parent,
fVersionStringView->SetHighUIColor(B_PANEL_TEXT_COLOR, B_DARKEN_3_TINT); fVersionStringView->SetHighUIColor(B_PANEL_TEXT_COLOR, B_DARKEN_3_TINT);
fVersionStringView->SetExplicitMaxSize( fVersionStringView->SetExplicitMaxSize(
BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
BSize introductionSize;
introductionSize.SetHeight(
_ExpectedIntroductionTextHeight(introductionTextView));
introductionTextView->SetExplicitPreferredSize(introductionSize);
BButton* okButton = new BButton("ok", B_TRANSLATE("OK"),
new BMessage(B_QUIT_REQUESTED));
BLayoutBuilder::Group<>(this, B_VERTICAL)
.SetInsets(B_USE_WINDOW_INSETS)
.Add(introductionTextView, 1)
.AddGlue()
.Add(fVersionStringView, 1)
.Add(scrollView, 96)
.Add(fAgeNoteStringView, 1)
.AddGroup(B_HORIZONTAL, 1)
.AddGlue()
.Add(okButton)
.End()
.Add(fWorkerIndicator, 1)
.End();
CenterIn(parent->Frame());
_FetchData();
// start a new thread to pull down the user usage conditions data.
}
UserUsageConditionsWindow::~UserUsageConditionsWindow()
{
} }
@@ -22,14 +22,17 @@ class Model;
enum UserUsageConditionsSelectionMode { enum UserUsageConditionsSelectionMode {
LATEST = 1, LATEST = 1,
USER = 2 USER = 2,
FIXED = 3
// means that the user usage conditions are supplied to the window.
}; };
class UserUsageConditionsWindow : public BWindow { class UserUsageConditionsWindow : public BWindow {
public: public:
UserUsageConditionsWindow(BWindow* parent, UserUsageConditionsWindow(Model& model,
BRect frame, Model& model, UserUsageConditions& userUsageConditions);
UserUsageConditionsWindow(Model& model,
UserUsageConditionsSelectionMode mode); UserUsageConditionsSelectionMode mode);
virtual ~UserUsageConditionsWindow(); virtual ~UserUsageConditionsWindow();
@@ -37,6 +40,8 @@ public:
virtual bool QuitRequested(); virtual bool QuitRequested();
private: private:
void _InitUiControls();
static const BString _VersionText(const BString& code); static const BString _VersionText(const BString& code);
static const BString _MinimumAgeText(uint8 minimumAge); static const BString _MinimumAgeText(uint8 minimumAge);
static const BString _IntroductionTextForMode( static const BString _IntroductionTextForMode(