[haiku-commits] haiku: hrev53515 - in src/apps/haikudepot: model ui server util .

  • From: Stephan Aßmus <superstippi@xxxxxx>
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Tue, 24 Sep 2019 06:33:48 -0400 (EDT)

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",
+                       &timestampAgreedMillis) == 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);


Other related posts: