hrev53515 adds 1 changeset to branch 'master'
old head: 23d6916acd20e0d8f9c902678c8f8698be1378aa
new head: d2d4866dd448fdcd8a336a904562a044060100d9
overview:
https://git.haiku-os.org/haiku/log/?qt=range&q=d2d4866dd448+%5E23d6916acd20
----------------------------------------------------------------------------
d2d4866dd448: HaikuDepot: Display User's Usage Conditions
A HaikuDepotServer may have previously agreed to
user usage conditions. If this is the case then
they are now able to view those conditions. There
were some problems with date formatting in this
change and so the date formatting logic has moved
into the HaikuDepot source code temporarily until
issues with the BDateTime class can be resolved;
most likely as a separate piece of work.
Relates to 15209
Change-Id: Ic3e5413d9139f410d7f7e8b566d4c56352dd2778
Reviewed-on: https://review.haiku-os.org/c/haiku/+/1870
Reviewed-by: Stephan Aßmus <superstippi@xxxxxx>
[ Andrew Lindesay <apl@xxxxxxxxxxxxxx> ]
----------------------------------------------------------------------------
Revision: hrev53515
Commit: d2d4866dd448fdcd8a336a904562a044060100d9
URL: https://git.haiku-os.org/haiku/commit/?id=d2d4866dd448
Author: Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date: Thu Sep 19 07:59:29 2019 UTC
Committer: Stephan Aßmus <superstippi@xxxxxx>
Commit-Date: Tue Sep 24 10:33:43 2019 UTC
----------------------------------------------------------------------------
24 files changed, 935 insertions(+), 220 deletions(-)
src/apps/haikudepot/HaikuDepot.rdef | 2 +-
src/apps/haikudepot/HaikuDepotConstants.h | 8 +
src/apps/haikudepot/Jamfile | 34 ++--
src/apps/haikudepot/model/Model.cpp | 38 ++--
src/apps/haikudepot/model/Model.h | 8 +-
src/apps/haikudepot/model/PackageInfo.cpp | 38 +---
src/apps/haikudepot/model/PackageInfo.h | 21 +--
src/apps/haikudepot/model/UserCredentials.cpp | 119 ++++++++++++
src/apps/haikudepot/model/UserCredentials.h | 45 +++++
src/apps/haikudepot/model/UserDetail.cpp | 183 ++++++++++++++++++
src/apps/haikudepot/model/UserDetail.h | 71 +++++++
.../haikudepot/model/UserUsageConditions.cpp | 1 +
src/apps/haikudepot/model/UserUsageConditions.h | 8 +-
src/apps/haikudepot/server/WebAppInterface.cpp | 150 +++++++++++++--
src/apps/haikudepot/server/WebAppInterface.h | 37 +++-
src/apps/haikudepot/ui/MainWindow.cpp | 54 ++++--
src/apps/haikudepot/ui/MainWindow.h | 14 +-
src/apps/haikudepot/ui/PackageInfoView.cpp | 10 +-
src/apps/haikudepot/ui/RatePackageWindow.cpp | 4 +-
src/apps/haikudepot/ui/UserLoginWindow.cpp | 62 ++++---
.../haikudepot/ui/UserUsageConditionsWindow.cpp | 185 +++++++++++++++----
.../haikudepot/ui/UserUsageConditionsWindow.h | 21 ++-
src/apps/haikudepot/util/LocaleUtils.cpp | 38 ++++
src/apps/haikudepot/util/LocaleUtils.h | 4 +
----------------------------------------------------------------------------
diff --git a/src/apps/haikudepot/HaikuDepot.rdef
b/src/apps/haikudepot/HaikuDepot.rdef
index 35897fec87..b2a05c5404 100644
--- a/src/apps/haikudepot/HaikuDepot.rdef
+++ b/src/apps/haikudepot/HaikuDepot.rdef
@@ -8,7 +8,7 @@ resource app_flags B_SINGLE_LAUNCH;
resource app_version {
major = 0,
middle = 0,
- minor = 2,
+ minor = 3,
variety = B_APPV_ALPHA,
internal = 1,
diff --git a/src/apps/haikudepot/HaikuDepotConstants.h
b/src/apps/haikudepot/HaikuDepotConstants.h
index 9dc8c062f5..4e79b348ca 100644
--- a/src/apps/haikudepot/HaikuDepotConstants.h
+++ b/src/apps/haikudepot/HaikuDepotConstants.h
@@ -21,6 +21,7 @@ enum {
MSG_DID_UPDATE_USER_RATING = 'upur',
MSG_LANGUAGE_SELECTED = 'lngs',
MSG_VIEW_LATEST_USER_USAGE_CONDITIONS = 'vluc',
+ MSG_VIEW_USERS_USER_USAGE_CONDITIONS = 'vuuc',
MSG_USER_USAGE_CONDITIONS_DATA = 'uucd',
MSG_USER_USAGE_CONDITIONS_ERROR = 'uuce'
};
@@ -62,6 +63,13 @@ enum {
};
+enum UserUsageConditionsSelectionMode {
+ LATEST = 1,
+ USER = 2,
+ FIXED = 3
+ // means that the user usage conditions are supplied to the
window.
+};
+
#define LANGUAGE_DEFAULT_CODE "en"
#define LANGUAGE_DEFAULT Language(LANGUAGE_DEFAULT_CODE, "English", true)
diff --git a/src/apps/haikudepot/Jamfile b/src/apps/haikudepot/Jamfile
index f6912f45a9..74ec3d990a 100644
--- a/src/apps/haikudepot/Jamfile
+++ b/src/apps/haikudepot/Jamfile
@@ -6,7 +6,9 @@ SubDirC++Flags -Wno-error ;
include [ FDirName $(HAIKU_TOP) src apps haikudepot build jam
HdsSchemaGenRules ] ;
-UsePrivateHeaders interface kernel net package shared storage support ;
+UseBuildFeatureHeaders icu ;
+
+UsePrivateHeaders interface kernel net package shared storage support locale ;
HAIKUDEPOT_GENERATED_SOURCES_DIRECTORY = $(TARGET_COMMON_DEBUG_LOCATE_TARGET) ;
@@ -117,8 +119,8 @@ local applicationSources =
DecisionProvider.cpp
FeaturedPackagesView.cpp
FilterView.cpp
- LocalIconStore.cpp
JobStateListener.cpp
+ LocalIconStore.cpp
LanguageModel.cpp
LinkView.cpp
LinkedBitmapView.cpp
@@ -142,6 +144,8 @@ local applicationSources =
ScreenshotWindow.cpp
ScrollableGroupView.cpp
SharedBitmap.cpp
+ UserCredentials.cpp
+ UserDetail.cpp
UserLoginWindow.cpp
UserUsageConditions.cpp
UserUsageConditionsWindow.cpp
@@ -218,17 +222,21 @@ local generatedReferenceParserSourceFiles =
;
Application HaikuDepot
- : $(applicationSources)
- $(textDocumentSources)
- $(generatedPkgModelSourceFiles)
- $(generatedRepositoryModelSourceFiles)
- $(generatedReferenceModelSourceFiles)
- $(generatedPkgParserSourceFiles)
- $(generatedRepositoryParserSourceFiles)
- $(generatedReferenceParserSourceFiles)
- : be package bnetapi translation libcolumnlistview.a shared
- [ TargetLibstdc++ ] [ TargetLibsupc++ ] localestub
- : HaikuDepot.rdef
+ :
+ $(applicationSources)
+ $(textDocumentSources)
+ $(generatedPkgModelSourceFiles)
+ $(generatedRepositoryModelSourceFiles)
+ $(generatedReferenceModelSourceFiles)
+ $(generatedPkgParserSourceFiles)
+ $(generatedRepositoryParserSourceFiles)
+ $(generatedReferenceParserSourceFiles)
+ :
+ be package bnetapi translation libcolumnlistview.a shared
+ [ TargetLibstdc++ ] [ TargetLibsupc++ ] localestub
+ [ BuildFeatureAttribute icu : libraries ]
+ :
+ HaikuDepot.rdef
;
DoCatalogs HaikuDepot :
diff --git a/src/apps/haikudepot/model/Model.cpp
b/src/apps/haikudepot/model/Model.cpp
index 4a2bb36f9f..2031168c1e 100644
--- a/src/apps/haikudepot/model/Model.cpp
+++ b/src/apps/haikudepot/model/Model.cpp
@@ -720,21 +720,13 @@ Model::PopulatePackage(const PackageInfoRef& package,
uint32 flags)
versionString <<
architectureCode;
}
- BDateTime createTimestamp;
- double createTimestampMillisF;
- if (item.FindDouble("createTimestamp",
- &createTimestampMillisF) ==
B_OK) {
- double createTimestampSecsF =
- createTimestampMillisF
/ 1000.0;
- time_t createTimestampSecs =
- (time_t)
createTimestampSecsF;
-
createTimestamp.SetTime_t(createTimestampSecs);
- }
+ double createTimestamp;
+ item.FindDouble("createTimestamp",
&createTimestamp);
// Add the rating to the PackageInfo
UserRating userRating =
UserRating(UserInfo(user), rating,
- comment, languageCode,
versionString, 0, 0,
- createTimestamp);
+ comment, languageCode,
versionString,
+ (uint64) createTimestamp);
package->AddUserRating(userRating);
if (Logger::IsDebugEnabled()) {
@@ -813,43 +805,43 @@ Model::_PopulatePackageChangelog(const PackageInfoRef&
package)
void
-Model::SetUsername(BString username)
+Model::SetNickname(BString nickname)
{
BString password;
- if (username.Length() > 0) {
+ if (nickname.Length() > 0) {
BPasswordKey key;
BKeyStore keyStore;
- if (keyStore.GetKey(kHaikuDepotKeyring, B_KEY_TYPE_PASSWORD,
username,
+ if (keyStore.GetKey(kHaikuDepotKeyring, B_KEY_TYPE_PASSWORD,
nickname,
key) == B_OK) {
password = key.Password();
} else {
- username = "";
+ nickname = "";
}
}
- SetAuthorization(username, password, false);
+ SetAuthorization(nickname, password, false);
}
const BString&
-Model::Username() const
+Model::Nickname() const
{
- return fWebAppInterface.Username();
+ return fWebAppInterface.Nickname();
}
void
-Model::SetAuthorization(const BString& username, const BString& password,
+Model::SetAuthorization(const BString& nickname, const BString& passwordClear,
bool storePassword)
{
- if (storePassword && username.Length() > 0 && password.Length() > 0) {
- BPasswordKey key(password, B_KEY_PURPOSE_WEB, username);
+ if (storePassword && nickname.Length() > 0 && passwordClear.Length() >
0) {
+ BPasswordKey key(passwordClear, B_KEY_PURPOSE_WEB, nickname);
BKeyStore keyStore;
keyStore.AddKeyring(kHaikuDepotKeyring);
keyStore.AddKey(kHaikuDepotKeyring, key);
}
BAutolock locker(&fLock);
- fWebAppInterface.SetAuthorization(username, password);
+ fWebAppInterface.SetAuthorization(UserCredentials(nickname,
passwordClear));
_NotifyAuthorizationChanged();
}
diff --git a/src/apps/haikudepot/model/Model.h
b/src/apps/haikudepot/model/Model.h
index 0e23af33bb..6925b6aef8 100644
--- a/src/apps/haikudepot/model/Model.h
+++ b/src/apps/haikudepot/model/Model.h
@@ -131,10 +131,10 @@ public:
void PopulatePackage(const
PackageInfoRef& package,
uint32
flags);
- void SetUsername(BString
username);
- const BString& Username() const;
- void SetAuthorization(const
BString& username,
- const
BString& password,
+ void SetNickname(BString
nickname);
+ const BString& Nickname() const;
+ void SetAuthorization(const
BString& nickname,
+ const
BString& passwordClear,
bool
storePassword);
const WebAppInterface& GetWebAppInterface() const
diff --git a/src/apps/haikudepot/model/PackageInfo.cpp
b/src/apps/haikudepot/model/PackageInfo.cpp
index 4006bbf27f..237ce9250a 100644
--- a/src/apps/haikudepot/model/PackageInfo.cpp
+++ b/src/apps/haikudepot/model/PackageInfo.cpp
@@ -55,7 +55,6 @@ Language::GetName(BString& name,
UserInfo::UserInfo()
:
- fAvatar(),
fNickName()
{
}
@@ -63,15 +62,6 @@ UserInfo::UserInfo()
UserInfo::UserInfo(const BString& nickName)
:
- fAvatar(),
- fNickName(nickName)
-{
-}
-
-
-UserInfo::UserInfo(const BitmapRef& avatar, const BString& nickName)
- :
- fAvatar(avatar),
fNickName(nickName)
{
}
@@ -79,7 +69,6 @@ UserInfo::UserInfo(const BitmapRef& avatar, const BString&
nickName)
UserInfo::UserInfo(const UserInfo& other)
:
- fAvatar(other.fAvatar),
fNickName(other.fNickName)
{
}
@@ -88,7 +77,6 @@ UserInfo::UserInfo(const UserInfo& other)
UserInfo&
UserInfo::operator=(const UserInfo& other)
{
- fAvatar = other.fAvatar;
fNickName = other.fNickName;
return *this;
}
@@ -97,8 +85,7 @@ UserInfo::operator=(const UserInfo& other)
bool
UserInfo::operator==(const UserInfo& other) const
{
- return fAvatar == other.fAvatar
- && fNickName == other.fNickName;
+ return fNickName == other.fNickName;
}
@@ -119,28 +106,22 @@ UserRating::UserRating()
fComment(),
fLanguage(),
fPackageVersion(),
- fUpVotes(0),
- fDownVotes(0),
- fCreateTimestamp()
+ fCreateTimestamp(0)
{
}
UserRating::UserRating(const UserInfo& userInfo, float rating,
const BString& comment, const BString& language,
- const BString& packageVersion, int32 upVotes, int32 downVotes,
- const BDateTime& createTimestamp)
+ const BString& packageVersion, uint64 createTimestamp)
:
fUserInfo(userInfo),
fRating(rating),
fComment(comment),
fLanguage(language),
fPackageVersion(packageVersion),
- fUpVotes(upVotes),
- fDownVotes(downVotes),
- fCreateTimestamp()
+ fCreateTimestamp(createTimestamp)
{
- fCreateTimestamp.SetTime_t(createTimestamp.Time_t());
}
@@ -151,11 +132,8 @@ UserRating::UserRating(const UserRating& other)
fComment(other.fComment),
fLanguage(other.fLanguage),
fPackageVersion(other.fPackageVersion),
- fUpVotes(other.fUpVotes),
- fDownVotes(other.fDownVotes),
- fCreateTimestamp()
+ fCreateTimestamp(other.fCreateTimestamp)
{
- fCreateTimestamp.SetTime_t(other.CreateTimestamp().Time_t());
}
@@ -167,9 +145,7 @@ UserRating::operator=(const UserRating& other)
fComment = other.fComment;
fLanguage = other.fLanguage;
fPackageVersion = other.fPackageVersion;
- fUpVotes = other.fUpVotes;
- fDownVotes = other.fDownVotes;
- fCreateTimestamp.SetTime_t(other.fCreateTimestamp.Time_t());
+ fCreateTimestamp = other.fCreateTimestamp;
return *this;
}
@@ -182,8 +158,6 @@ UserRating::operator==(const UserRating& other) const
&& fComment == other.fComment
&& fLanguage == other.fLanguage
&& fPackageVersion == other.fPackageVersion
- && fUpVotes == other.fUpVotes
- && fDownVotes == other.fDownVotes
&& fCreateTimestamp == other.fCreateTimestamp;
}
diff --git a/src/apps/haikudepot/model/PackageInfo.h
b/src/apps/haikudepot/model/PackageInfo.h
index 09aabd5a19..9ccaeb443a 100644
--- a/src/apps/haikudepot/model/PackageInfo.h
+++ b/src/apps/haikudepot/model/PackageInfo.h
@@ -13,7 +13,6 @@
#include <Referenceable.h>
#include <package/PackageInfo.h>
-#include "DateTime.h"
#include "List.h"
#include "PackageInfoListener.h"
#include "SharedBitmap.h"
@@ -50,21 +49,16 @@ class UserInfo {
public:
UserInfo();
UserInfo(const
BString& nickName);
- UserInfo(const
BitmapRef& avatar,
- const
BString& nickName);
UserInfo(const
UserInfo& other);
UserInfo& operator=(const
UserInfo& other);
bool operator==(const
UserInfo& other) const;
bool operator!=(const
UserInfo& other) const;
- const BitmapRef& Avatar() const
- {
return fAvatar; }
const BString& NickName() const
{
return fNickName; }
private:
- BitmapRef fAvatar;
BString fNickName;
};
@@ -77,8 +71,7 @@ public:
const
BString& comment,
const
BString& language,
const
BString& packageVersion,
- int32
upVotes, int32 downVotes,
- const
BDateTime& createTimestamp);
+ uint64
createTimestamp);
UserRating(const UserRating& other);
UserRating& operator=(const
UserRating& other);
@@ -95,12 +88,7 @@ public:
{
return fRating; }
const BString& PackageVersion() const
{
return fPackageVersion; }
-
- int32 UpVotes() const
- {
return fUpVotes; }
- int32 DownVotes() const
- {
return fDownVotes; }
- const BDateTime& CreateTimestamp() const
+ const uint64 CreateTimestamp() const
{
return fCreateTimestamp; }
private:
UserInfo fUserInfo;
@@ -108,9 +96,8 @@ private:
BString fComment;
BString fLanguage;
BString fPackageVersion;
- int32 fUpVotes;
- int32 fDownVotes;
- BDateTime fCreateTimestamp;
+ uint64 fCreateTimestamp;
+ // milliseconds since epoc
};
diff --git a/src/apps/haikudepot/model/UserCredentials.cpp
b/src/apps/haikudepot/model/UserCredentials.cpp
new file mode 100644
index 0000000000..d62d3ca0fa
--- /dev/null
+++ b/src/apps/haikudepot/model/UserCredentials.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2019, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ *
+ * All rights reserved. Distributed under the terms of the MIT License.
+ */
+#include "UserCredentials.h"
+
+
+// These are keys that are used to store this object's data into a BMessage
+// instance.
+
+#define KEY_NICKNAME "nickname"
+#define KEY_PASSWORD_CLEAR "passwordClear"
+#define KEY_IS_SUCCESSFUL "isSuccessful"
+
+
+UserCredentials::UserCredentials(BMessage* from)
+{
+ from->FindString(KEY_NICKNAME, &fNickname);
+ from->FindString(KEY_PASSWORD_CLEAR, &fPasswordClear);
+ from->FindBool(KEY_IS_SUCCESSFUL, &fIsSuccessful);
+}
+
+
+UserCredentials::UserCredentials(const BString& nickname,
+ const BString& passwordClear)
+ :
+ fNickname(nickname),
+ fPasswordClear(passwordClear),
+ fIsSuccessful(false)
+{
+}
+
+
+UserCredentials::UserCredentials()
+ :
+ fNickname(),
+ fPasswordClear(),
+ fIsSuccessful(false)
+{
+}
+
+
+UserCredentials::~UserCredentials()
+{
+}
+
+
+const BString&
+UserCredentials::Nickname() const
+{
+ return fNickname;
+}
+
+
+const BString&
+UserCredentials::PasswordClear() const
+{
+ return fPasswordClear;
+}
+
+
+const bool
+UserCredentials::IsSuccessful() const
+{
+ return fIsSuccessful;
+}
+
+
+const bool
+UserCredentials::IsValid() const
+{
+ return !fNickname.IsEmpty() && !fPasswordClear.IsEmpty();
+}
+
+
+void
+UserCredentials::SetNickname(const BString& value)
+{
+ fNickname = value;
+}
+
+
+void
+UserCredentials::SetPasswordClear(const BString& value)
+{
+ fPasswordClear = value;
+}
+
+
+void
+UserCredentials::SetIsSuccessful(bool value)
+{
+ fIsSuccessful = value;
+}
+
+
+UserCredentials&
+UserCredentials::operator=(const UserCredentials& other)
+{
+ fNickname = other.fNickname;
+ fPasswordClear = other.fPasswordClear;
+ fIsSuccessful = other.fIsSuccessful;
+ return *this;
+}
+
+
+status_t
+UserCredentials::Archive(BMessage* into, bool deep) const
+{
+ status_t result = B_OK;
+ if (result == B_OK)
+ result = into->AddString(KEY_NICKNAME, fNickname);
+ if (result == B_OK)
+ result = into->AddString(KEY_PASSWORD_CLEAR, fPasswordClear);
+ if (result == B_OK)
+ result = into->AddBool(KEY_IS_SUCCESSFUL, fIsSuccessful);
+ return result;
+}
\ No newline at end of file
diff --git a/src/apps/haikudepot/model/UserCredentials.h
b/src/apps/haikudepot/model/UserCredentials.h
new file mode 100644
index 0000000000..01a5cc902f
--- /dev/null
+++ b/src/apps/haikudepot/model/UserCredentials.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ *
+ * All rights reserved. Distributed under the terms of the MIT License.
+ */
+#ifndef USER_CREDENTIALS_H
+#define USER_CREDENTIALS_H
+
+
+#include <Archivable.h>
+#include <String.h>
+
+
+/*! This object represents the tuple of the user's nickname (username) and
+ password. It also carries a boolean that indicates if an authentication
+ with these credentials was successful or failed.
+*/
+
+class UserCredentials : public BArchivable {
+public:
+
UserCredentials(BMessage* from);
+
UserCredentials(const BString& nickname,
+ const
BString& passwordClear);
+
UserCredentials();
+ virtual ~UserCredentials();
+
+ const BString& Nickname() const;
+ const BString& PasswordClear() const;
+ const bool IsSuccessful() const;
+ const bool IsValid() const;
+
+ void SetNickname(const
BString& value);
+ void SetPasswordClear(const
BString& value);
+ void SetIsSuccessful(bool
value);
+
+ UserCredentials& operator=(const
UserCredentials& other);
+
+ status_t Archive(BMessage* into,
bool deep = true) const;
+private:
+ BString fNickname;
+ BString fPasswordClear;
+ bool fIsSuccessful;
+};
+
+#endif // USER_CREDENTIALS_H
diff --git a/src/apps/haikudepot/model/UserDetail.cpp
b/src/apps/haikudepot/model/UserDetail.cpp
new file mode 100644
index 0000000000..8b0788a13c
--- /dev/null
+++ b/src/apps/haikudepot/model/UserDetail.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2019, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ *
+ * All rights reserved. Distributed under the terms of the MIT License.
+ */
+#include "UserDetail.h"
+
+
+// These are keys that are used to store this object's data into a BMessage
+// instance.
+
+#define KEY_NICKNAME
"nickname"
+#define KEY_AGREEMENT
"agreement"
+#define KEY_IS_LATEST
"isLatest"
+#define KEY_CODE
"code"
+#define KEY_TIMESTAMP_AGREED
"timestampAgreed"
+
+
+UserUsageConditionsAgreement::UserUsageConditionsAgreement(BMessage* from)
+{
+ from->FindUInt64(KEY_TIMESTAMP_AGREED, &fTimestampAgreed);
+ from->FindString(KEY_CODE, &fCode);
+ from->FindBool(KEY_IS_LATEST, &fIsLatest);
+}
+
+
+UserUsageConditionsAgreement::UserUsageConditionsAgreement()
+ :
+ fCode(),
+ fTimestampAgreed(0),
+ fIsLatest(false)
+{
+}
+
+
+UserUsageConditionsAgreement::~UserUsageConditionsAgreement()
+{
+}
+
+
+const BString&
+UserUsageConditionsAgreement::Code() const
+{
+ return fCode;
+}
+
+
+const uint64
+UserUsageConditionsAgreement::TimestampAgreed() const
+{
+ return fTimestampAgreed;
+}
+
+
+const bool
+UserUsageConditionsAgreement::IsLatest() const
+{
+ return fIsLatest;
+}
+
+
+void
+UserUsageConditionsAgreement::SetCode(const BString& value)
+{
+ fCode = value;
+}
+
+
+void
+UserUsageConditionsAgreement::SetTimestampAgreed(uint64 value)
+{
+ fTimestampAgreed = value;
+}
+
+
+void
+UserUsageConditionsAgreement::SetIsLatest(const bool value)
+{
+ fIsLatest = value;
+}
+
+
+UserUsageConditionsAgreement&
+UserUsageConditionsAgreement::operator=(
+ const UserUsageConditionsAgreement& other)
+{
+ fCode = other.fCode;
+ fTimestampAgreed = other.fTimestampAgreed;
+ fIsLatest = other.fIsLatest;
+ return *this;
+}
+
+
+status_t
+UserUsageConditionsAgreement::Archive(BMessage* into, bool deep) const
+{
+ status_t result = B_OK;
+ if (result == B_OK)
+ result = into->AddUInt64(KEY_TIMESTAMP_AGREED,
fTimestampAgreed);
+ if (result == B_OK)
+ result = into->AddString(KEY_CODE, fCode);
+ if (result == B_OK)
+ result = into->AddBool(KEY_IS_LATEST, fIsLatest);
+ return result;
+}
+
+
+UserDetail::UserDetail(BMessage* from)
+{
+ BMessage agreementMessage;
+ if (from->FindMessage(KEY_AGREEMENT,
+ &agreementMessage) == B_OK) {
+ fAgreement = UserUsageConditionsAgreement(&agreementMessage);
+ }
+ from->FindString(KEY_NICKNAME, &fNickname);
+}
+
+
+UserDetail::UserDetail()
+ :
+ fNickname(),
+ fAgreement()
+{
+}
+
+
+UserDetail::~UserDetail()
+{
+}
+
+
+const BString&
+UserDetail::Nickname() const
+{
+ return fNickname;
+}
+
+
+const UserUsageConditionsAgreement&
+UserDetail::Agreement() const
+{
+ return fAgreement;
+}
+
+
+void
+UserDetail::SetNickname(const BString& value)
+{
+ fNickname = value;
+}
+
+
+void
+UserDetail::SetAgreement(
+ const UserUsageConditionsAgreement& value)
+{
+ fAgreement = value;
+}
+
+
+UserDetail&
+UserDetail::operator=(const UserDetail& other)
+{
+ fNickname = other.fNickname;
+ fAgreement = other.fAgreement;
+ return *this;
+}
+
+
+status_t
+UserDetail::Archive(BMessage* into, bool deep) const
+{
+ status_t result = B_OK;
+ if (result == B_OK) {
+ BMessage agreementMessage;
+ result = fAgreement.Archive(&agreementMessage, true);
+ if (result == B_OK)
+ result = into->AddMessage(KEY_AGREEMENT,
&agreementMessage);
+ }
+ if (result == B_OK)
+ result = into->AddString(KEY_NICKNAME, fNickname);
+ return result;
+}
diff --git a/src/apps/haikudepot/model/UserDetail.h
b/src/apps/haikudepot/model/UserDetail.h
new file mode 100644
index 0000000000..694cbd75cb
--- /dev/null
+++ b/src/apps/haikudepot/model/UserDetail.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2019, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ *
+ * All rights reserved. Distributed under the terms of the MIT License.
+ */
+#ifndef USER_DETAIL_H
+#define USER_DETAIL_H
+
+#include <stdio.h>
+
+#include <Archivable.h>
+#include <String.h>
+
+#include "DateTime.h"
+
+
+class UserUsageConditionsAgreement : public BArchivable {
+public:
+
UserUsageConditionsAgreement(BMessage* from);
+
UserUsageConditionsAgreement();
+ virtual
~UserUsageConditionsAgreement();
+
+ const BString& Code() const;
+ const uint64 TimestampAgreed() const;
+ const bool IsLatest() const;
+
+ void SetCode(const BString&
value);
+ void
SetTimestampAgreed(uint64 value);
+ void SetIsLatest(const bool
value);
+
+ UserUsageConditionsAgreement&
+ operator=(
+ const
UserUsageConditionsAgreement& other);
+
+ status_t Archive(BMessage* into,
bool deep = true) const;
+private:
+ BString fCode;
+ uint64 fTimestampAgreed;
+ // milliseconds since epoc
+ bool fIsLatest;
+};
+
+
+/*! This objects represents a user in the HaikuDepotServer system.
+ */
+
+class UserDetail : public BArchivable {
+public:
+
UserDetail(BMessage* from);
+ UserDetail();
+ virtual ~UserDetail();
+
+ const BString& Nickname() const;
+ const UserUsageConditionsAgreement&
+ Agreement()
const;
+
+ void SetNickname(const
BString& value);
+ void SetAgreement(
+ const
UserUsageConditionsAgreement& value);
+
+ UserDetail& operator=(const
UserDetail& other);
+
+ status_t Archive(BMessage* into,
bool deep = true) const;
+private:
+ BString fNickname;
+ UserUsageConditionsAgreement
+ fAgreement;
+};
+
+
+#endif // USER_DETAIL_H
diff --git a/src/apps/haikudepot/model/UserUsageConditions.cpp
b/src/apps/haikudepot/model/UserUsageConditions.cpp
index 24fc6fdf54..d3008fd13d 100644
--- a/src/apps/haikudepot/model/UserUsageConditions.cpp
+++ b/src/apps/haikudepot/model/UserUsageConditions.cpp
@@ -5,6 +5,7 @@
*/
#include "UserUsageConditions.h"
+#include <stdio.h>
// These are keys that are used to store this object's data into a BMessage
// instance.
diff --git a/src/apps/haikudepot/model/UserUsageConditions.h
b/src/apps/haikudepot/model/UserUsageConditions.h
index 5dffca35e4..3d48c443c9 100644
--- a/src/apps/haikudepot/model/UserUsageConditions.h
+++ b/src/apps/haikudepot/model/UserUsageConditions.h
@@ -6,12 +6,18 @@
#ifndef USER_USAGE_CONDITIONS_H
#define USER_USAGE_CONDITIONS_H
-#include <stdio.h>
#include <Archivable.h>
#include <String.h>
+/*! A user in the HDS system should have agreed to user usage conditions when
+ they created their user on the server. This object represents the user
+ usage conditions that either they have agreed to or that they could agree
+ to. Each set of user usage conditions has a code that uniquely identifies
+ a given set of conditions.
+*/
+
class UserUsageConditions : public BArchivable {
public:
UserUsageConditions(BMessage* from);
diff --git a/src/apps/haikudepot/server/WebAppInterface.cpp
b/src/apps/haikudepot/server/WebAppInterface.cpp
index 326ddd9bd1..4cf368d49d 100644
--- a/src/apps/haikudepot/server/WebAppInterface.cpp
+++ b/src/apps/haikudepot/server/WebAppInterface.cpp
@@ -277,8 +277,7 @@ WebAppInterface::WebAppInterface()
WebAppInterface::WebAppInterface(const WebAppInterface& other)
:
- fUsername(other.fUsername),
- fPassword(other.fPassword)
+ fCredentials(other.fCredentials)
{
}
@@ -293,18 +292,22 @@ WebAppInterface::operator=(const WebAppInterface& other)
{
if (this == &other)
return *this;
- fUsername = other.fUsername;
- fPassword = other.fPassword;
+ fCredentials = other.fCredentials;
return *this;
}
void
-WebAppInterface::SetAuthorization(const BString& username,
- const BString& password)
+WebAppInterface::SetAuthorization(const UserCredentials& value)
{
- fUsername = username;
- fPassword = password;
+ fCredentials = value;
+}
+
+
+const BString&
+WebAppInterface::Nickname() const
+{
+ return fCredentials.Nickname();
}
@@ -368,7 +371,7 @@ status_t
WebAppInterface::RetreiveUserRatingForPackageAndVersionByUser(
const BString& packageName, const BPackageVersion& version,
const BString& architecture, const BString &repositoryCode,
- const BString& username, BMessage& message)
+ const BString& userNickname, BMessage& message)
{
// BHttpRequest later takes ownership of this.
BMallocIO* requestEnvelopeData = new BMallocIO();
@@ -383,7 +386,7 @@
WebAppInterface::RetreiveUserRatingForPackageAndVersionByUser(
requestEnvelopeWriter.WriteObjectStart();
requestEnvelopeWriter.WriteObjectName("userNickname");
- requestEnvelopeWriter.WriteString(username.String());
+ requestEnvelopeWriter.WriteString(userNickname.String());
requestEnvelopeWriter.WriteObjectName("pkgName");
requestEnvelopeWriter.WriteString(packageName.String());
requestEnvelopeWriter.WriteObjectName("pkgVersionArchitectureCode");
@@ -426,6 +429,114 @@
WebAppInterface::RetreiveUserRatingForPackageAndVersionByUser(
}
+/*! This method will fill out the supplied UserDetail object with information
+ about the user that is supplied in the credentials. Importantly it will
+ also authenticate the request with the details of the credentials and will
+ not use the credentials that are configured in 'fCredentials'.
+*/
+
+status_t
+WebAppInterface::RetrieveUserDetailForCredentials(
+ const UserCredentials& credentials, UserDetail& userDetail)
+{
+ if (!credentials.IsValid()) {
+ debugger("the credentials supplied are invalid so it is not
possible "
+ "to obtain the user detail");
+ }
+
+ // BHttpRequest later takes ownership of this.
+ BMallocIO* requestEnvelopeData = new BMallocIO();
+ BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
+
+ requestEnvelopeWriter.WriteObjectStart();
+ _WriteStandardJsonRpcEnvelopeValues(requestEnvelopeWriter, "getUser");
+ requestEnvelopeWriter.WriteObjectName("params");
+ requestEnvelopeWriter.WriteArrayStart();
+ requestEnvelopeWriter.WriteObjectStart();
+ requestEnvelopeWriter.WriteObjectName("nickname");
+ requestEnvelopeWriter.WriteString(credentials.Nickname().String());
+ requestEnvelopeWriter.WriteObjectEnd();
+ requestEnvelopeWriter.WriteArrayEnd();
+ requestEnvelopeWriter.WriteObjectEnd();
+
+ BMessage responseEnvelopeMessage;
+ status_t result = _SendJsonRequest("user", credentials,
requestEnvelopeData,
+ _LengthAndSeekToZero(requestEnvelopeData), NEEDS_AUTHORIZATION,
+ responseEnvelopeMessage);
+ // note that the credentials used here are passed in as args.
+
+ if (result == B_OK)
+ result = _UnpackUserDetails(responseEnvelopeMessage,
userDetail);
+
+ return result;
+}
+
+
+/*! This method will return the credentials for the currently authenticated
+ user.
+*/
+
+status_t
+WebAppInterface::RetrieveCurrentUserDetail(UserDetail& userDetail)
+{
+ return RetrieveUserDetailForCredentials(fCredentials, userDetail);
+}
+
+
+/*! When the user requests user detail, the server sends back an envelope of
+ response data. This method will unpack the data into a model object.
+ \return Not B_OK if something went wrong.
+*/
+
+/*static*/ status_t
+WebAppInterface::_UnpackUserDetails(BMessage& responseEnvelopeMessage,
+ UserDetail& userDetail)
+{
+ BMessage resultMessage;
+ status_t result = responseEnvelopeMessage.FindMessage(
+ "result", &resultMessage);
+
+ if (result != B_OK) {
+ fprintf(stderr, "bad response envelope missing 'result'
entry\n");
+ return result;
+ }
+
+ BString nickname;
+ result = resultMessage.FindString("nickname", &nickname);
+ userDetail.SetNickname(nickname);
+
+ BMessage agreementMessage;
+ if (resultMessage.FindMessage("userUsageConditionsAgreement",
+ &agreementMessage) == B_OK) {
+ BString code;
+ BDateTime agreedToTimestamp;
+ BString userUsageConditionsCode;
+ UserUsageConditionsAgreement agreement = userDetail.Agreement();
+ bool isLatest;
+
+ if (agreementMessage.FindString("userUsageConditionsCode",
+ &userUsageConditionsCode) == B_OK) {
+ agreement.SetCode(userUsageConditionsCode);
+ }
+
+ double timestampAgreedMillis;
+ if (agreementMessage.FindDouble("timestampAgreed",
+ ×tampAgreedMillis) == B_OK) {
+ agreement.SetTimestampAgreed((uint64)
timestampAgreedMillis);
+ }
+
+ if (agreementMessage.FindBool("isLatest", &isLatest)
+ == B_OK) {
+ agreement.SetIsLatest(isLatest);
+ }
+
+ userDetail.SetAgreement(agreement);
+ }
+
+ return result;
+}
+
+
/*! \brief Returns data relating to the user usage conditions
\param code defines the version of the data to return or if empty then
the
@@ -551,7 +662,7 @@ WebAppInterface::CreateUserRating(const BString&
packageName,
requestEnvelopeWriter.WriteObjectName("pkgVersionType");
requestEnvelopeWriter.WriteString("SPECIFIC");
requestEnvelopeWriter.WriteObjectName("userNickname");
- requestEnvelopeWriter.WriteString(fUsername.String());
+ requestEnvelopeWriter.WriteString(fCredentials.Nickname());
if (!version.Major().IsEmpty()) {
requestEnvelopeWriter.WriteObjectName("pkgVersionMajor");
@@ -796,6 +907,16 @@
WebAppInterface::_WriteStandardJsonRpcEnvelopeValues(BJsonWriter& writer,
status_t
WebAppInterface::_SendJsonRequest(const char* domain, BPositionIO* requestData,
size_t requestDataSize, uint32 flags, BMessage& reply) const
+{
+ return _SendJsonRequest(domain, fCredentials, requestData,
requestDataSize,
+ flags, reply);
+}
+
+
+status_t
+WebAppInterface::_SendJsonRequest(const char* domain,
+ UserCredentials credentials, BPositionIO* requestData,
+ size_t requestDataSize, uint32 flags, BMessage& reply) const
{
if (requestDataSize == 0) {
if (Logger::IsInfoEnabled())
@@ -853,14 +974,13 @@ WebAppInterface::_SendJsonRequest(const char* domain,
BPositionIO* requestData,
// Authentication via Basic Authentication
// The other way would be to obtain a token and then use the Token
Bearer
// header.
- if ((flags & NEEDS_AUTHORIZATION) != 0
- && !fUsername.IsEmpty() && !fPassword.IsEmpty()) {
- BHttpAuthentication authentication(fUsername, fPassword);
+ if (((flags & NEEDS_AUTHORIZATION) != 0) && credentials.IsValid()) {
+ BHttpAuthentication authentication(credentials.Nickname(),
+ credentials.PasswordClear());
authentication.SetMethod(B_HTTP_AUTHENTICATION_BASIC);
context.AddAuthentication(url, authentication);
}
-
request.AdoptInputData(requestData, requestDataSize);
BMallocIO replyData;
diff --git a/src/apps/haikudepot/server/WebAppInterface.h
b/src/apps/haikudepot/server/WebAppInterface.h
index 0feaf496f1..aa2a99c85e 100644
--- a/src/apps/haikudepot/server/WebAppInterface.h
+++ b/src/apps/haikudepot/server/WebAppInterface.h
@@ -13,6 +13,8 @@
#include <package/PackageVersion.h>
#include "List.h"
+#include "UserCredentials.h"
+#include "UserDetail.h"
#include "UserUsageConditions.h"
@@ -47,10 +49,8 @@ public:
WebAppInterface& operator=(const
WebAppInterface& other);
- void SetAuthorization(const
BString& username,
- const
BString& password);
- const BString& Username() const
- {
return fUsername; }
+ void SetAuthorization(const
UserCredentials& value);
+ const BString& Nickname() const;
status_t GetChangelog(
const
BString& packageName,
@@ -67,7 +67,7 @@ public:
const
BPackageVersion& version,
const
BString& architecture,
const
BString& repositoryCode,
- const
BString& username,
+ const
BString& userNickname,
BMessage& message);
status_t CreateUserRating(
@@ -89,6 +89,13 @@ public:
int
rating, bool active,
BMessage& message);
+ status_t
RetrieveUserDetailForCredentials(
+ const
UserCredentials& credentials,
+
UserDetail& userDetail);
+
+ status_t
RetrieveCurrentUserDetail(
+
UserDetail& userDetail);
+
status_t
RetrieveUserUsageConditions(
const
BString& code,
UserUsageConditions& conditions);
@@ -116,13 +123,15 @@ public:
static int32 ErrorCodeFromResponse(BMessage&
response);
private:
- status_t _SendRawGetRequest(
- const
BString urlPathComponents,
-
BDataIO* stream);
+ static status_t _UnpackUserDetails(
+
BMessage& responseEnvelopeMessage,
+
UserDetail& userDetail);
+
status_t
_RetrieveUserUsageConditionsMeta(
const
BString& code, BMessage& message);
status_t
_RetrieveUserUsageConditionsCopy(
const
BString& code, BDataIO* stream);
+
void
_WriteStandardJsonRpcEnvelopeValues(
BJsonWriter& writer,
const
char* methodName);
@@ -130,16 +139,24 @@ private:
const
BString& jsonString, uint32 flags,
BMessage& reply) const;
status_t _SendJsonRequest(const
char* domain,
+
UserCredentials credentials,
BPositionIO* requestData,
size_t
requestDataSize, uint32 flags,
BMessage& reply) const;
+ status_t _SendJsonRequest(const
char* domain,
+
BPositionIO* requestData,
+ size_t
requestDataSize, uint32 flags,
+
BMessage& reply) const;
+
+ status_t _SendRawGetRequest(
+ const
BString urlPathComponents,
+
BDataIO* stream);
static void _LogPayload(BPositionIO*
requestData,
size_t
size);
static off_t
_LengthAndSeekToZero(BPositionIO* data);
private:
- BString fUsername;
- BString fPassword;
+ UserCredentials fCredentials;
static int fRequestIndex;
};
diff --git a/src/apps/haikudepot/ui/MainWindow.cpp
b/src/apps/haikudepot/ui/MainWindow.cpp
index caa23af602..a2135d98e3 100644
--- a/src/apps/haikudepot/ui/MainWindow.cpp
+++ b/src/apps/haikudepot/ui/MainWindow.cpp
@@ -143,6 +143,7 @@ MainWindow::MainWindow(const BMessage& settings)
set_small_font(userMenuBar);
userMenuBar->SetExplicitMaxSize(BSize(B_SIZE_UNSET,
menuBar->MaxSize().height));
+ _UpdateAuthorization();
fFilterView = new FilterView();
fFeaturedPackagesView = new FeaturedPackagesView();
@@ -204,7 +205,7 @@ MainWindow::MainWindow(const BMessage& settings)
else
fListTabs->Select(1);
- _RestoreUserName(settings);
+ _RestoreNickname(settings);
_RestoreWindowFrame(settings);
atomic_set(&fPackagesToShowListID, 0);
@@ -245,7 +246,7 @@ MainWindow::MainWindow(const BMessage& settings, const
PackageInfoRef& package)
fModel.AddListener(fModelListener);
// Restore settings
- _RestoreUserName(settings);
+ _RestoreNickname(settings);
_RestoreWindowFrame(settings);
fPackageInfoView->SetPackage(package);
@@ -332,11 +333,15 @@ MainWindow::MessageReceived(BMessage* message)
break;
case MSG_LOG_OUT:
- fModel.SetUsername("");
+ fModel.SetNickname("");
break;
case MSG_VIEW_LATEST_USER_USAGE_CONDITIONS:
- _ViewLatestUserUsageConditions();
+ _ViewUserUsageConditions(LATEST);
+ break;
+
+ case MSG_VIEW_USERS_USER_USAGE_CONDITIONS:
+ _ViewUserUsageConditions(USER);
break;
case MSG_AUTHORIZATION_CHANGED:
@@ -641,7 +646,7 @@ MainWindow::StoreSettings(BMessage& settings) const
settings.AddBool("show source packages",
fModel.ShowSourcePackages());
}
- settings.AddString("username", fModel.Username());
+ settings.AddString("username", fModel.Nickname());
}
@@ -745,17 +750,23 @@ MainWindow::_BuildUserMenu(BMenuBar* menuBar)
new BMessage(MSG_VIEW_LATEST_USER_USAGE_CONDITIONS));
fUserMenu->AddItem(latestUserUsageConditionsMenuItem);
+ fUsersUserUsageConditionsMenuItem =
+ new BMenuItem(B_TRANSLATE("View agreed usage conditions"
+ B_UTF8_ELLIPSIS),
+ new BMessage(MSG_VIEW_USERS_USER_USAGE_CONDITIONS));
+ fUserMenu->AddItem(fUsersUserUsageConditionsMenuItem);
+
menuBar->AddItem(fUserMenu);
}
void
-MainWindow::_RestoreUserName(const BMessage& settings)
+MainWindow::_RestoreNickname(const BMessage& settings)
{
- BString username;
- if (settings.FindString("username", &username) == B_OK
- && username.Length() > 0) {
- fModel.SetUsername(username);
+ BString nickname;
+ if (settings.FindString("username", &nickname) == B_OK
+ && nickname.Length() > 0) {
+ fModel.SetNickname(nickname);
}
}
@@ -1193,11 +1204,13 @@ MainWindow::_OpenLoginWindow(const BMessage&
onSuccessMessage)
void
MainWindow::_UpdateAuthorization()
{
- BString username(fModel.Username());
- bool hasUser = !username.IsEmpty();
+ BString nickname(fModel.Nickname());
+ bool hasUser = !nickname.IsEmpty();
if (fLogOutItem != NULL)
fLogOutItem->SetEnabled(hasUser);
+ if (fUsersUserUsageConditionsMenuItem != NULL)
+ fUsersUserUsageConditionsMenuItem->SetEnabled(hasUser);
if (fLogInItem != NULL) {
if (hasUser)
fLogInItem->SetLabel(B_TRANSLATE("Switch account"
B_UTF8_ELLIPSIS));
@@ -1207,11 +1220,11 @@ MainWindow::_UpdateAuthorization()
if (fUserMenu != NULL) {
BString label;
- if (username.Length() == 0) {
- label = B_TRANSLATE("Not logged in");
- } else {
+ if (hasUser) {
label = B_TRANSLATE("Logged in as %User%");
- label.ReplaceAll("%User%", username);
+ label.ReplaceAll("%User%", nickname);
+ } else {
+ label = B_TRANSLATE("Not logged in");
}
fUserMenu->Superitem()->SetLabel(label);
}
@@ -1298,7 +1311,7 @@ MainWindow::_RatePackage()
return;
}
- if (fModel.Username().IsEmpty()) {
+ if (fModel.Nickname().IsEmpty()) {
BAlert* alert = new(std::nothrow) BAlert(
B_TRANSLATE("Not logged in"),
B_TRANSLATE("You need to be logged into an account
before you "
@@ -1346,9 +1359,10 @@ MainWindow::_ShowScreenshot()
void
-MainWindow::_ViewLatestUserUsageConditions()
+MainWindow::_ViewUserUsageConditions(
+ UserUsageConditionsSelectionMode mode)
{
UserUsageConditionsWindow* window = new UserUsageConditionsWindow(
- fModel, LATEST);
+ fModel, mode);
window->Show();
-}
+}
\ No newline at end of file
diff --git a/src/apps/haikudepot/ui/MainWindow.h
b/src/apps/haikudepot/ui/MainWindow.h
index 841b2800ff..2c403d36ae 100644
--- a/src/apps/haikudepot/ui/MainWindow.h
+++ b/src/apps/haikudepot/ui/MainWindow.h
@@ -2,7 +2,7 @@
* Copyright 2013-2014, Stephan Aßmus <superstippi@xxxxxx>.
* Copyright 2013, Rene Gollent <rene@xxxxxxxxxxx>.
* Copyright 2017, Julian Harnath <julian.harnath@xxxxxxxxxxxxxx>.
- * Copyright 2017-2018, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ * Copyright 2017-2019, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef MAIN_WINDOW_H
@@ -10,13 +10,13 @@
#include <Window.h>
-#include "TabView.h"
+#include "HaikuDepotConstants.h"
#include "Model.h"
#include "PackageAction.h"
#include "PackageActionHandler.h"
-#include "PackageInfoListener.h"
#include "ProcessCoordinator.h"
-#include "HaikuDepotConstants.h"
+#include "PackageInfoListener.h"
+#include "TabView.h"
class BCardLayout;
@@ -69,7 +69,7 @@ private:
void _BuildMenu(BMenuBar*
menuBar);
void
_BuildUserMenu(BMenuBar* menuBar);
- void _RestoreUserName(const
BMessage& settings);
+ void _RestoreNickname(const
BMessage& settings);
const char* _WindowFrameName()
const;
void
_RestoreWindowFrame(const BMessage& settings);
@@ -101,7 +101,8 @@ private:
void _RatePackage();
void _ShowScreenshot();
- void
_ViewLatestUserUsageConditions();
+ void
_ViewUserUsageConditions(
+
UserUsageConditionsSelectionMode mode);
private:
FilterView* fFilterView;
@@ -118,6 +119,7 @@ private:
BMenu* fRepositoryMenu;
BMenuItem* fLogInItem;
BMenuItem* fLogOutItem;
+ BMenuItem*
fUsersUserUsageConditionsMenuItem;
BMenuItem*
fShowAvailablePackagesItem;
BMenuItem*
fShowInstalledPackagesItem;
diff --git a/src/apps/haikudepot/ui/PackageInfoView.cpp
b/src/apps/haikudepot/ui/PackageInfoView.cpp
index 03e95f1e80..74499a14ef 100644
--- a/src/apps/haikudepot/ui/PackageInfoView.cpp
+++ b/src/apps/haikudepot/ui/PackageInfoView.cpp
@@ -16,7 +16,6 @@
#include <CardLayout.h>
#include <Catalog.h>
#include <ColumnListView.h>
-#include <DateFormat.h>
#include <Font.h>
#include <GridView.h>
#include <LayoutBuilder.h>
@@ -39,6 +38,7 @@
#include "BitmapView.h"
#include "LinkView.h"
#include "LinkedBitmapView.h"
+#include "LocaleUtils.h"
#include "MarkupTextView.h"
#include "MessagePackageListener.h"
#include "PackageActionHandler.h"
@@ -899,11 +899,9 @@ public:
}
{
- BDateFormat dateFormat;
- BString createTimestampPresentation;
-
- dateFormat.Format(createTimestampPresentation,
- rating.CreateTimestamp().Date(),
B_MEDIUM_DATE_FORMAT);
+ BString createTimestampPresentation =
+ LocaleUtils::TimestampToDateTimeString(
+ rating.CreateTimestamp());
BString ratingContextDescription(
B_TRANSLATE("%hd.timestamp% (version
%hd.version%)"));
diff --git a/src/apps/haikudepot/ui/RatePackageWindow.cpp
b/src/apps/haikudepot/ui/RatePackageWindow.cpp
index 97ce8c2899..8b0e534d4e 100644
--- a/src/apps/haikudepot/ui/RatePackageWindow.cpp
+++ b/src/apps/haikudepot/ui/RatePackageWindow.cpp
@@ -548,7 +548,7 @@ RatePackageWindow::_QueryRatingThread()
Unlock();
BAutolock locker(fModel.Lock());
- BString username = fModel.Username();
+ BString nickname = fModel.Nickname();
locker.Unlock();
if (package.Get() == NULL) {
@@ -573,7 +573,7 @@ RatePackageWindow::_QueryRatingThread()
status_t status = interface
.RetreiveUserRatingForPackageAndVersionByUser(package->Name(),
package->Version(), package->Architecture(),
repositoryCode,
- username, info);
+ nickname, info);
if (status == B_OK) {
// could be an error or could be a valid
response envelope
diff --git a/src/apps/haikudepot/ui/UserLoginWindow.cpp
b/src/apps/haikudepot/ui/UserLoginWindow.cpp
index 13164f3856..a0dba6e66f 100644
--- a/src/apps/haikudepot/ui/UserLoginWindow.cpp
+++ b/src/apps/haikudepot/ui/UserLoginWindow.cpp
@@ -7,6 +7,7 @@
#include "UserLoginWindow.h"
#include <algorithm>
+#include <ctype.h>
#include <stdio.h>
#include <mail_encoding.h>
@@ -341,6 +342,24 @@ count_upper_case_letters(const BString& string)
}
+static bool
+contains_any_whitespace(const BString& string)
+{
+ const char* c = string.String();
+ for (int32 i = 0; i < string.CountChars(); i++) {
+ if (isspace(c[i]))
+ return true;
+ }
+
+ return false;
+}
+
+
+/*! This method will check that the inputs in the user interface are, as far as
+ the desktop application can be sure, correct.
+ \return true if the data in the form was valid.
+*/
+
bool
UserLoginWindow::_ValidateCreateAccountFields(bool alertProblems)
{
@@ -349,11 +368,17 @@ UserLoginWindow::_ValidateCreateAccountFields(bool
alertProblems)
BString password2(fRepeatPasswordField->Text());
BString email(fEmailField->Text());
BString captcha(fCaptchaResultField->Text());
- bool minimumAgeConfirmed = fConfirmMinimumAgeCheckBox->Value() != 0;
+ bool minimumAgeConfirmed = fConfirmMinimumAgeCheckBox->Value()
+ == B_CONTROL_ON;
bool userUsageConditionsConfirmed =
- fConfirmUserUsageConditionsCheckBox->Value() != 0;
+ fConfirmUserUsageConditionsCheckBox->Value() == B_CONTROL_ON;
// TODO: Use the same validation as the web-serivce
+
+ bool validEmail = email.IsEmpty() ||
+ (B_ERROR != email.FindFirst("@") &&
!contains_any_whitespace(email));
+ fEmailField->MarkAsInvalid(!validEmail);
+
bool validUserName = nickName.Length() >= 3;
fNewUsernameField->MarkAsInvalid(!validUserName);
@@ -367,23 +392,11 @@ UserLoginWindow::_ValidateCreateAccountFields(bool
alertProblems)
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");
- }
+ && validCaptcha && minimumAgeConfirmed &&
userUsageConditionsConfirmed
+ && validEmail;
+
+ if (!valid && alertProblems) {
+ BString message = B_TRANSLATE("There are problems in the
form:\n\n");
if (!validUserName) {
message << B_TRANSLATE(
@@ -425,14 +438,11 @@ UserLoginWindow::_ValidateCreateAccountFields(bool
alertProblems)
BAlert* alert = new(std::nothrow) BAlert(
B_TRANSLATE("Input validation"),
message,
- okLabel, cancelLabel, NULL,
- B_WIDTH_AS_USUAL, alertType);
+ B_TRANSLATE("OK"), NULL, NULL,
+ B_WIDTH_AS_USUAL, B_WARNING_ALERT);
- if (alert != NULL) {
- int32 choice = alert->Go();
- if (choice == 1)
- return false;
- }
+ if (alert != NULL)
+ alert->Go();
}
return valid;
diff --git a/src/apps/haikudepot/ui/UserUsageConditionsWindow.cpp
b/src/apps/haikudepot/ui/UserUsageConditionsWindow.cpp
index ced074ed01..b567acfdbe 100644
--- a/src/apps/haikudepot/ui/UserUsageConditionsWindow.cpp
+++ b/src/apps/haikudepot/ui/UserUsageConditionsWindow.cpp
@@ -16,6 +16,7 @@
#include "AppUtils.h"
#include "BarberPole.h"
#include "HaikuDepotConstants.h"
+#include "LocaleUtils.h"
#include "Logger.h"
#include "MarkupTextView.h"
#include "Model.h"
@@ -29,13 +30,16 @@
#define PLACEHOLDER_TEXT "..."
#define INTRODUCTION_TEXT_LATEST "HaikuDepot communicates with a " \
- "sever component called HaikuDepotServer. These are the latest " \
+ "sever component called HaikuDepotServer. These are the latest " \
"usage conditions for use of the HaikuDepotServer service."
#define INTRODUCTION_TEXT_USER "HaikuDepot communicates with a " \
- "sever component called HaikuDepotServer. These are the usage " \
- "conditions that the user has agreed to in relation to the use of the "
\
- "HaikuDepotServer service."
+ "sever component called HaikuDepotServer. These are the usage " \
+ "conditions that the user '%Nickname%' agreed to at %AgreedToTimestamp%
"\
+ "in relation to the use of the HaikuDepotServer service."
+
+#define KEY_USER_USAGE_CONDITIONS "userUsageConditions"
+#define KEY_USER_DETAIL "userDetail"
/*! This is the anticipated number of lines of test that appear in the
introduction.
@@ -55,6 +59,7 @@ UserUsageConditionsWindow::UserUsageConditionsWindow(Model&
model,
| B_NOT_RESIZABLE | B_NOT_ZOOMABLE),
fMode(FIXED),
fModel(model),
+ fIntroductionTextView(NULL),
fWorkerThread(-1)
{
_InitUiControls();
@@ -76,7 +81,10 @@ UserUsageConditionsWindow::UserUsageConditionsWindow(Model&
model,
.End();
CenterOnScreen();
- _DisplayData(userUsageConditions);
+
+ UserDetail userDetail;
+ // invalid user detail
+ _DisplayData(userDetail, userUsageConditions);
}
UserUsageConditionsWindow::UserUsageConditionsWindow(
@@ -90,9 +98,6 @@ UserUsageConditionsWindow::UserUsageConditionsWindow(
fModel(model),
fWorkerThread(-1)
{
- if (mode != LATEST)
- debugger("only the LATEST usage conditions are handled for
now");
-
_InitUiControls();
fWorkerIndicator = new BarberPole("fetch data worker indicator");
@@ -100,16 +105,17 @@ UserUsageConditionsWindow::UserUsageConditionsWindow(
workerIndicatorSize.SetHeight(20);
fWorkerIndicator->SetExplicitMinSize(workerIndicatorSize);
- BTextView* introductionTextView = new BTextView("introduction text
view");
- introductionTextView->AdoptSystemColors();
- introductionTextView->MakeEditable(false);
- introductionTextView->MakeSelectable(false);
-
introductionTextView->SetText(B_TRANSLATE(_IntroductionTextForMode(mode)));
+ fIntroductionTextView = new BTextView("introduction text view");
+ fIntroductionTextView->AdoptSystemColors();
+ fIntroductionTextView->MakeEditable(false);
+ fIntroductionTextView->MakeSelectable(false);
+ UserDetail userDetail;
+ fIntroductionTextView->SetText(_IntroductionTextForMode(mode,
userDetail));
BSize introductionSize;
introductionSize.SetHeight(
- _ExpectedIntroductionTextHeight(introductionTextView));
- introductionTextView->SetExplicitPreferredSize(introductionSize);
+ _ExpectedIntroductionTextHeight(fIntroductionTextView));
+ fIntroductionTextView->SetExplicitPreferredSize(introductionSize);
BScrollView* scrollView = new BScrollView("copy scroll view", fCopyView,
0, false, true, B_PLAIN_BORDER);
@@ -118,7 +124,7 @@ UserUsageConditionsWindow::UserUsageConditionsWindow(
BLayoutBuilder::Group<>(this, B_VERTICAL)
.SetInsets(B_USE_WINDOW_INSETS)
- .Add(introductionTextView, 1)
+ .Add(fIntroductionTextView, 1)
.AddGlue()
.Add(fVersionStringView, 1)
.Add(scrollView, 95)
@@ -180,8 +186,14 @@ UserUsageConditionsWindow::MessageReceived(BMessage*
message)
switch (message->what) {
case MSG_USER_USAGE_CONDITIONS_DATA:
{
- UserUsageConditions data(message);
- _DisplayData(data);
+ BMessage userDetailMessage;
+ BMessage userUsageConditionsMessage;
+ message->FindMessage(KEY_USER_DETAIL,
&userDetailMessage);
+ message->FindMessage(KEY_USER_USAGE_CONDITIONS,
+ &userUsageConditionsMessage);
+ UserDetail userDetail(&userDetailMessage);
+ UserUsageConditions
userUsageConditions(&userUsageConditionsMessage);
+ _DisplayData(userDetail, userUsageConditions);
fWorkerIndicator->Stop();
break;
}
@@ -256,25 +268,103 @@ UserUsageConditionsWindow::_FetchDataThreadEntry(void*
data)
void
UserUsageConditionsWindow::_FetchDataPerform()
{
+ UserDetail userDetail;
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);
+ BString code;
+ status_t status = _FetchUserUsageConditionsCodePerform(userDetail,
code);
+
+ if (status == B_OK) {
+ if (fMode == USER && code.IsEmpty()) {
+ BString message = B_TRANSLATE(
+ "The user '%Nickname%' has not agreed to any
usage "
+ "conditions.");
+ message.ReplaceAll("%Nickname%", userDetail.Nickname());
+ AppUtils::NotifySimpleError(B_TRANSLATE("No usage
conditions"),
+ message);
+ BMessenger(this).SendMessage(B_QUIT_REQUESTED);
+ status = B_BAD_DATA;
+ }
} else {
- AppUtils::NotifySimpleError(
- B_TRANSLATE("Usage conditions download problem"),
- B_TRANSLATE("An error has arisen downloading the usage "
- "conditions. Check the log for details and try
again."));
+ _NotifyFetchProblem();
BMessenger(this).SendMessage(B_QUIT_REQUESTED);
}
+ if (status == B_OK) {
+ if (interface.RetrieveUserUsageConditions(code, conditions) ==
B_OK) {
+ BMessage userUsageConditionsMessage;
+ BMessage userDetailMessage;
+ conditions.Archive(&userUsageConditionsMessage, true);
+ userDetail.Archive(&userDetailMessage, true);
+ BMessage dataMessage(MSG_USER_USAGE_CONDITIONS_DATA);
+ dataMessage.AddMessage(KEY_USER_USAGE_CONDITIONS,
+ &userUsageConditionsMessage);
+ dataMessage.AddMessage(KEY_USER_DETAIL,
&userDetailMessage);
+ BMessenger(this).SendMessage(&dataMessage);
+ } else {
+ _NotifyFetchProblem();
+ BMessenger(this).SendMessage(B_QUIT_REQUESTED);
+ }
+ }
+
_SetWorkerThread(-1);
}
+status_t
+UserUsageConditionsWindow::_FetchUserUsageConditionsCodePerform(
+ UserDetail& userDetail, BString& code)
+{
+ switch (fMode) {
+ case LATEST:
+ code.SetTo("");
+ // no code for the latest
+ return B_OK;
+ case USER:
+ {
+ WebAppInterface interface = fModel.GetWebAppInterface();
+
+ if (interface.Nickname().IsEmpty())
+ debugger("attempt to get user details for the
current user, but"
+ " there is no current user");
+
+ status_t result =
interface.RetrieveCurrentUserDetail(userDetail);
+
+ if (result == B_OK) {
+ BString userUsageConditionsCode =
userDetail.Agreement().Code();
+ if (Logger::IsDebugEnabled()) {
+ printf("the user [%s] has agreed to uuc
[%s]\n",
+ interface.Nickname().String(),
+
userUsageConditionsCode.String());
+ }
+ code.SetTo(userUsageConditionsCode);
+ } else {
+ if (Logger::IsDebugEnabled()) {
+ printf("unable to get details of the
user [%s]\n",
+ interface.Nickname().String());
+ }
+ }
+
+ return result;
+ break;
+ }
+ default:
+ debugger("unhanded mode");
+ return B_ERROR;
+ }
+}
+
+
+void
+UserUsageConditionsWindow::_NotifyFetchProblem()
+{
+ AppUtils::NotifySimpleError(
+ B_TRANSLATE("Usage conditions download problem"),
+ B_TRANSLATE("An error has arisen downloading the usage "
+ "conditions. Check the log for details and try
again."));
+}
+
+
void
UserUsageConditionsWindow::_SetWorkerThread(thread_id thread)
{
@@ -289,11 +379,18 @@ UserUsageConditionsWindow::_SetWorkerThread(thread_id
thread)
void
-UserUsageConditionsWindow::_DisplayData(const UserUsageConditions& data)
+UserUsageConditionsWindow::_DisplayData(
+ const UserDetail& userDetail,
+ const UserUsageConditions& userUsageConditions)
{
- fCopyView->SetText(data.CopyMarkdown());
- fAgeNoteStringView->SetText(_MinimumAgeText(data.MinimumAge()));
- fVersionStringView->SetText(_VersionText(data.Code()));
+ fCopyView->SetText(userUsageConditions.CopyMarkdown());
+ fAgeNoteStringView->SetText(_MinimumAgeText(
+ userUsageConditions.MinimumAge()));
+ fVersionStringView->SetText(_VersionText(userUsageConditions.Code()));
+ if (fIntroductionTextView != NULL) {
+ fIntroductionTextView->SetText(
+ _IntroductionTextForMode(fMode, userDetail));
+ }
}
@@ -322,13 +419,33 @@ UserUsageConditionsWindow::_MinimumAgeText(uint8
minimumAge)
/*static*/ const BString
UserUsageConditionsWindow::_IntroductionTextForMode(
- UserUsageConditionsSelectionMode mode)
+ UserUsageConditionsSelectionMode mode,
+ const UserDetail& userDetail)
{
switch (mode) {
case LATEST:
- return INTRODUCTION_TEXT_LATEST;
+ return B_TRANSLATE(INTRODUCTION_TEXT_LATEST);
case USER:
- return INTRODUCTION_TEXT_USER;
+ {
+ BString nicknamePresentation = PLACEHOLDER_TEXT;
+ BString agreedToTimestampPresentation =
PLACEHOLDER_TEXT;
+
+ if (!userDetail.Nickname().IsEmpty())
+ nicknamePresentation = userDetail.Nickname();
+
+ uint64 timestampAgreed =
userDetail.Agreement().TimestampAgreed();
+
+ if (timestampAgreed > 0) {
+ agreedToTimestampPresentation =
+
LocaleUtils::TimestampToDateTimeString(timestampAgreed);
+ }
+
+ BString text = B_TRANSLATE(INTRODUCTION_TEXT_USER);
+ text.ReplaceAll("%Nickname%", nicknamePresentation);
+ text.ReplaceAll("%AgreedToTimestamp%",
+ agreedToTimestampPresentation);
+ return text;
+ }
default:
return "???";
}
diff --git a/src/apps/haikudepot/ui/UserUsageConditionsWindow.h
b/src/apps/haikudepot/ui/UserUsageConditionsWindow.h
index 6e8facc0e9..823c50fb2e 100644
--- a/src/apps/haikudepot/ui/UserUsageConditionsWindow.h
+++ b/src/apps/haikudepot/ui/UserUsageConditionsWindow.h
@@ -9,7 +9,9 @@
#include <Messenger.h>
#include <Window.h>
+#include "HaikuDepotConstants.h"
#include "PackageInfo.h"
+#include "UserDetail.h"
#include "UserUsageConditions.h"
@@ -20,14 +22,6 @@ class MarkupTextView;
class Model;
-enum UserUsageConditionsSelectionMode {
- LATEST = 1,
- USER = 2,
- FIXED = 3
- // means that the user usage conditions are supplied to the
window.
-};
-
-
class UserUsageConditionsWindow : public BWindow {
public:
UserUsageConditionsWindow(Model& model,
@@ -45,16 +39,22 @@ private:
static const BString _VersionText(const BString& code);
static const BString _MinimumAgeText(uint8 minimumAge);
static const BString _IntroductionTextForMode(
-
UserUsageConditionsSelectionMode mode);
+
UserUsageConditionsSelectionMode mode,
+ const
UserDetail& userDetail);
static float _ExpectedIntroductionTextHeight(
BTextView* introductionTextView);
- void _DisplayData(const
UserUsageConditions& data);
+ void _DisplayData(const
UserDetail& userDetail,
+ const
UserUsageConditions&
+
userUsageConditions);
void _FetchData();
void
_SetWorkerThread(thread_id thread);
static int32 _FetchDataThreadEntry(void*
data);
void _FetchDataPerform();
+ status_t
_FetchUserUsageConditionsCodePerform(
+
UserDetail& userDetail, BString& code);
+ void _NotifyFetchProblem();
private:
UserUsageConditionsSelectionMode
@@ -63,6 +63,7 @@ private:
Model& fModel;
BStringView* fAgeNoteStringView;
BStringView* fVersionStringView;
+ BTextView* fIntroductionTextView;
BarberPole* fWorkerIndicator;
thread_id fWorkerThread;
};
diff --git a/src/apps/haikudepot/util/LocaleUtils.cpp
b/src/apps/haikudepot/util/LocaleUtils.cpp
index ce594ba34f..3cb31963f5 100644
--- a/src/apps/haikudepot/util/LocaleUtils.cpp
+++ b/src/apps/haikudepot/util/LocaleUtils.cpp
@@ -5,8 +5,12 @@
#include "LocaleUtils.h"
#include <stdlib.h>
+#include <unicode/datefmt.h>
+#include <unicode/dtptngen.h>
+#include <unicode/smpdtfmt.h>
#include <Collator.h>
+#include <ICUWrapper.h>
#include <Locale.h>
#include <LocaleRoster.h>
@@ -36,3 +40,37 @@ LocaleUtils::GetCollator(BCollator* collator)
exit(EXIT_FAILURE);
}
}
+
+
+/*! There was some difficulty in getting BDateTime and friends to
+ work for the purposes of this application. Data comes in as millis since
+ the epoc relative to GMT0. These need to be displayed in the local time
+ zone, but the timezone aspect never seems to be quite right with BDateTime!
+ For now, to avoid this work over-spilling into a debug of the date-time
+ classes in Haiku, I am adding this method that uses ICU directly in order
+ to get something basic working for now. Later this should be migrated to
+ use the BDateTime etc... classes from Haiku once these problems have been
+ ironed out.
+*/
+
+/*static*/ BString
+LocaleUtils::TimestampToDateTimeString(uint64 millis)
+{
+ if (millis == 0)
+ return "?";
+
+ UnicodeString pattern("yyyy-MM-dd HH:mm:ss");
+ // later use variants of DateFormat::createInstance()
+ UErrorCode success = U_ZERO_ERROR;
+ SimpleDateFormat sdf(pattern, success);
+
+ if (U_FAILURE(success))
+ return "!";
+
+ UnicodeString icuResult;
+ sdf.format((UDate) millis, icuResult);
+ BString result;
+ BStringByteSink converter(&result);
+ icuResult.toUTF8(converter);
+ return result;
+}
diff --git a/src/apps/haikudepot/util/LocaleUtils.h
b/src/apps/haikudepot/util/LocaleUtils.h
index 9887012239..caf718a066 100644
--- a/src/apps/haikudepot/util/LocaleUtils.h
+++ b/src/apps/haikudepot/util/LocaleUtils.h
@@ -6,6 +6,9 @@
#define LOCALE_UTILS_H
+#include <String.h>
+
+
class BCollator;
@@ -13,6 +16,7 @@ class LocaleUtils {
public:
static BCollator* GetSharedCollator();
+ static BString TimestampToDateTimeString(uint64
millis);
private:
static void GetCollator(BCollator* collator);