HaikuDepot: Restore logged in user across sessions.

* Store the username in the app settings. The password was already stored
   in the keyring. Restore the username upon next launch.
 * Display the logged in user in the main window.
 * Added "Log out" menu entry.
 * When the password could not be retrieved from the keyring (also because
   the user rejects the keyring dialog), unset the username.
 * Allow unsetting the username by passing an empty name.
This commit is contained in:
Stephan Aßmus 2014-09-28 00:33:20 +02:00
parent 3e3d0effdc
commit 8f03a0f9f2
7 changed files with 176 additions and 24 deletions

View File

@ -30,14 +30,19 @@
static const char* kHaikuDepotKeyring = "HaikuDepot";
// #pragma mark - PackageFilters
PackageFilter::~PackageFilter()
{
}
ModelListener::~ModelListener()
{
}
// #pragma mark - PackageFilters
class AnyFilter : public PackageFilter {
public:
virtual bool AcceptsPackage(const PackageInfoRef& package) const
@ -368,6 +373,13 @@ Model::~Model()
}
bool
Model::AddListener(const ModelListenerRef& listener)
{
return fListeners.Add(listener);
}
PackageList
Model::CreatePackageList() const
{
@ -661,14 +673,27 @@ Model::StopPopulatingAllPackages()
void
Model::SetUser(const BString& username)
Model::SetUsername(BString username)
{
BPasswordKey key;
BKeyStore keyStore;
if (keyStore.GetKey(kHaikuDepotKeyring, B_KEY_TYPE_PASSWORD, username,
key) == B_OK) {
SetAuthorization(username, key.Password(), false);
BString password;
if (username.Length() > 0) {
BPasswordKey key;
BKeyStore keyStore;
if (keyStore.GetKey(kHaikuDepotKeyring, B_KEY_TYPE_PASSWORD, username,
key) == B_OK) {
password = key.Password();
} else {
username = "";
}
}
SetAuthorization(username, password, false);
}
const BString&
Model::Username() const
{
return fWebAppInterface.Username();
}
@ -676,7 +701,7 @@ void
Model::SetAuthorization(const BString& username, const BString& password,
bool storePassword)
{
if (storePassword) {
if (storePassword && username.Length() > 0 && password.Length() > 0) {
BPasswordKey key(password, B_KEY_PURPOSE_WEB, username);
BKeyStore keyStore;
keyStore.AddKeyring(kHaikuDepotKeyring);
@ -685,6 +710,8 @@ Model::SetAuthorization(const BString& username, const BString& password,
BAutolock locker(&fLock);
fWebAppInterface.SetAuthorization(username, password);
_NotifyAuthorizationChanged();
}
@ -1146,3 +1173,18 @@ Model::_HasNativeIcon(const BMessage& message) const
}
return false;
}
// #pragma mark - listener notification methods
void
Model::_NotifyAuthorizationChanged()
{
for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) {
const ModelListenerRef& listener = fListeners.ItemAtFast(i);
if (listener.Get() != NULL)
listener->AuthorizationChanged();
}
}

View File

@ -25,6 +25,17 @@ public:
typedef BReference<PackageFilter> PackageFilterRef;
class ModelListener : public BReferenceable {
public:
virtual ~ModelListener();
virtual void AuthorizationChanged() = 0;
};
typedef BReference<ModelListener> ModelListenerRef;
typedef List<ModelListenerRef, false> ModelListenerList;
class Model {
public:
Model();
@ -33,6 +44,8 @@ public:
BLocker* Lock()
{ return &fLock; }
bool AddListener(const ModelListenerRef& listener);
// !Returns new PackageInfoList from current parameters
PackageList CreatePackageList() const;
@ -110,7 +123,8 @@ public:
const BString& PreferredLanguage() const
{ return fPreferredLanguage; }
void SetUser(const BString& username);
void SetUsername(BString username);
const BString& Username() const;
void SetAuthorization(const BString& username,
const BString& password,
bool storePassword);
@ -139,6 +153,8 @@ private:
int32 scaledWidth,
bool fromCacheOnly);
void _NotifyAuthorizationChanged();
private:
BLocker fLock;
@ -182,6 +198,8 @@ private:
BString fPreferredLanguage;
WebAppInterface fWebAppInterface;
ModelListenerList fListeners;
};

View File

@ -28,6 +28,9 @@ public:
void SetAuthorization(const BString& username,
const BString& password);
const BString& Username() const
{ return fUsername; }
void SetPreferredLanguage(const BString& language);
void SetArchitecture(const BString& architecture);

View File

@ -15,6 +15,7 @@
#include <MenuItem.h>
#include <Messenger.h>
#include <PopUpMenu.h>
#include <StringView.h>
#include <TextControl.h>
#include "Model.h"
@ -37,16 +38,23 @@ add_categories_to_menu(const CategoryList& categories, BMenu* menu)
}
static void
set_small_font(BView* view)
{
BFont font;
view->GetFont(&font);
font.SetSize(ceilf(font.Size() * 0.8));
view->SetFont(&font);
}
static BCheckBox*
create_check_box(const char* label, const char* name)
{
BMessage* message = new BMessage(MSG_FILTER_SELECTED);
message->AddString("name", name);
BCheckBox* checkBox = new BCheckBox(label, message);
BFont font;
checkBox->GetFont(&font);
font.SetSize(ceilf(font.Size() * 0.75));
checkBox->SetFont(&font);
set_small_font(checkBox);
return checkBox;
}
@ -90,6 +98,11 @@ FilterView::FilterView()
fSourceCodeCheckBox = create_check_box(
B_TRANSLATE("Source code"), "source code");
// Logged in user label
fUsername = new BStringView("logged in user", "");
set_small_font(fUsername);
fUsername->SetHighColor(80, 80, 80);
// Build layout
BLayoutBuilder::Group<>(this)
.AddGroup(B_HORIZONTAL)
@ -107,6 +120,7 @@ FilterView::FilterView()
.Add(fDevelopmentCheckBox)
.Add(fSourceCodeCheckBox)
.AddGlue(0.5f)
.Add(fUsername)
.End()
.SetInsets(B_USE_DEFAULT_SPACING)
@ -200,3 +214,17 @@ FilterView::AdoptCheckmarks(const Model& model)
fSourceCodeCheckBox->SetValue(model.ShowSourcePackages());
}
void
FilterView::SetUsername(const BString& username)
{
BString label;
if (username.Length() == 0) {
label = B_TRANSLATE("Not logged in");
} else {
label = B_TRANSLATE("Logged in as %User%");
label.ReplaceAll("%User%", username);
}
fUsername->SetText(label);
}

View File

@ -10,6 +10,7 @@
class BCheckBox;
class BMenuField;
class BStringView;
class BTextControl;
class Model;
@ -30,8 +31,10 @@ public:
virtual void AttachedToWindow();
virtual void MessageReceived(BMessage* message);
virtual void AdoptModel(const Model& model);
virtual void AdoptCheckmarks(const Model& model);
void AdoptModel(const Model& model);
void AdoptCheckmarks(const Model& model);
void SetUsername(const BString& username);
private:
BMenuField* fShowField;
@ -42,6 +45,8 @@ private:
BCheckBox* fInstalledCheckBox;
BCheckBox* fDevelopmentCheckBox;
BCheckBox* fSourceCodeCheckBox;
BStringView* fUsername;
};
#endif // FILTER_VIEW_H

View File

@ -53,12 +53,14 @@
enum {
MSG_MODEL_WORKER_DONE = 'mmwd',
MSG_REFRESH_DEPOTS = 'mrdp',
MSG_LOG_IN = 'lgin',
MSG_PACKAGE_STATE_CHANGED = 'mpsc',
MSG_SHOW_SOURCE_PACKAGES = 'ssrc',
MSG_SHOW_DEVELOP_PACKAGES = 'sdvl'
MSG_MODEL_WORKER_DONE = 'mmwd',
MSG_REFRESH_DEPOTS = 'mrdp',
MSG_LOG_IN = 'lgin',
MSG_LOG_OUT = 'lgot',
MSG_AUTHORIZATION_CHANGED = 'athc',
MSG_PACKAGE_STATE_CHANGED = 'mpsc',
MSG_SHOW_SOURCE_PACKAGES = 'ssrc',
MSG_SHOW_DEVELOP_PACKAGES = 'sdvl'
};
@ -83,11 +85,31 @@ struct RefreshWorkerParameters {
};
class MessageModelListener : public ModelListener {
public:
MessageModelListener(const BMessenger& messenger)
:
fMessenger(messenger)
{
}
virtual void AuthorizationChanged()
{
if (fMessenger.IsValid())
fMessenger.SendMessage(MSG_AUTHORIZATION_CHANGED);
}
private:
BMessenger fMessenger;
};
MainWindow::MainWindow(BRect frame, const BMessage& settings)
:
BWindow(frame, B_TRANSLATE_SYSTEM_NAME("HaikuDepot"),
B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
fModelListener(new MessageModelListener(BMessenger(this)), true),
fTerminating(false),
fModelWorker(B_BAD_THREAD_ID)
{
@ -117,6 +139,8 @@ MainWindow::MainWindow(BRect frame, const BMessage& settings)
fSplitView->SetCollapsible(0, false);
fSplitView->SetCollapsible(1, false);
fModel.AddListener(fModelListener);
// Restore settings
BMessage columnSettings;
if (settings.FindMessage("column settings", &columnSettings) == B_OK)
@ -128,6 +152,13 @@ MainWindow::MainWindow(BRect frame, const BMessage& settings)
if (settings.FindBool("show source packages", &showOption) == B_OK)
fModel.SetShowSourcePackages(showOption);
BString username;
if (settings.FindString("username", &username) == B_OK
&& username.Length() > 0) {
fModel.SetUsername(username);
}
// start worker threads
BPackageRoster().StartWatching(this,
B_WATCH_PACKAGE_INSTALLATION_LOCATIONS);
@ -212,6 +243,14 @@ MainWindow::MessageReceived(BMessage* message)
_OpenLoginWindow();
break;
case MSG_LOG_OUT:
fModel.SetUsername("");
break;
case MSG_AUTHORIZATION_CHANGED:
_UpdateAuthorization();
break;
case MSG_SHOW_SOURCE_PACKAGES:
{
BAutolock locker(fModel.Lock());
@ -358,6 +397,8 @@ MainWindow::StoreSettings(BMessage& settings) const
settings.AddBool("show develop packages", fModel.ShowDevelopPackages());
settings.AddBool("show source packages", fModel.ShowSourcePackages());
settings.AddString("username", fModel.Username());
}
@ -401,8 +442,12 @@ MainWindow::_BuildMenu(BMenuBar* menuBar)
BMenu* menu = new BMenu(B_TRANSLATE("Tools"));
menu->AddItem(new BMenuItem(B_TRANSLATE("Refresh depots"),
new BMessage(MSG_REFRESH_DEPOTS)));
menu->AddItem(new BMenuItem(B_TRANSLATE("Log in"),
menu->AddSeparatorItem();
menu->AddItem(new BMenuItem(B_TRANSLATE("Log in" B_UTF8_ELLIPSIS),
new BMessage(MSG_LOG_IN)));
fLogOutItem = new BMenuItem(B_TRANSLATE("Log out"),
new BMessage(MSG_LOG_OUT));
menu->AddItem(fLogOutItem);
menuBar->AddItem(menu);
// menu = new BMenu(B_TRANSLATE("Options"));
@ -858,3 +903,11 @@ MainWindow::_OpenLoginWindow()
window->Show();
}
void
MainWindow::_UpdateAuthorization()
{
BString username(fModel.Username());
fLogOutItem->SetEnabled(username.Length() > 0);
fFilterView->SetUsername(username);
}

View File

@ -72,16 +72,19 @@ private:
const char* message);
void _OpenLoginWindow();
void _UpdateAuthorization();
private:
FilterView* fFilterView;
PackageListView* fPackageListView;
PackageInfoView* fPackageInfoView;
BSplitView* fSplitView;
BMenuItem* fLogOutItem;
BMenuItem* fShowDevelopPackagesItem;
BMenuItem* fShowSourcePackagesItem;
Model fModel;
ModelListenerRef fModelListener;
PackageList fVisiblePackages;
bool fTerminating;