Commit 8518e1ec authored by Tomi Leppänen's avatar Tomi Leppänen

Merge branch 'jb47825_user_switching' into 'master'

[nemo-systemsettings] Add setCurrentUser method to UserModel. Contributes to JB#47825

See merge request !137
parents 66e08cc6 e797c4db
...@@ -12,7 +12,7 @@ Requires: connman ...@@ -12,7 +12,7 @@ Requires: connman
Requires: mce >= 1.83.0 Requires: mce >= 1.83.0
Requires: libsailfishkeyprovider >= 0.0.14 Requires: libsailfishkeyprovider >= 0.0.14
Requires: connman-qt5 >= 1.2.21 Requires: connman-qt5 >= 1.2.21
Requires: user-managerd Requires: user-managerd >= 0.3.0
Requires(post): coreutils Requires(post): coreutils
BuildRequires: pkgconfig(Qt5Qml) BuildRequires: pkgconfig(Qt5Qml)
BuildRequires: pkgconfig(Qt5SystemInfo) BuildRequires: pkgconfig(Qt5SystemInfo)
......
...@@ -68,10 +68,10 @@ UserInfoPrivate::UserInfoPrivate(struct passwd *pwd) ...@@ -68,10 +68,10 @@ UserInfoPrivate::UserInfoPrivate(struct passwd *pwd)
: m_uid(pwd->pw_uid) : m_uid(pwd->pw_uid)
, m_username(QString::fromUtf8(pwd->pw_name)) , m_username(QString::fromUtf8(pwd->pw_name))
, m_name(nameFromGecos(pwd->pw_gecos)) , m_name(nameFromGecos(pwd->pw_gecos))
// require_active == false -> both online and active are logged in. // require_active == true -> only active user is logged in.
// Specifying seat should make sure that remote users are not // Specifying seat should make sure that remote users are not
// counted as they don't have seats. // counted as they don't have seats.
, m_loggedIn(sd_uid_is_on_seat(m_uid, 0, "seat0") > 0) , m_loggedIn(sd_uid_is_on_seat(m_uid, 1, "seat0") > 0)
{ {
} }
...@@ -113,9 +113,7 @@ void UserInfoPrivate::set(struct passwd *pwd) ...@@ -113,9 +113,7 @@ void UserInfoPrivate::set(struct passwd *pwd)
*/ */
UserInfo::UserInfo() UserInfo::UserInfo()
{ {
if (!UserInfoPrivate::s_current.isNull()) { d_ptr = UserInfoPrivate::s_current.toStrongRef();
d_ptr = QSharedPointer<UserInfoPrivate>(UserInfoPrivate::s_current);
}
if (d_ptr.isNull()) { if (d_ptr.isNull()) {
uid_t uid = InvalidId; uid_t uid = InvalidId;
struct passwd *pwd; struct passwd *pwd;
...@@ -251,10 +249,27 @@ bool UserInfo::current() const ...@@ -251,10 +249,27 @@ bool UserInfo::current() const
return d->m_loggedIn; return d->m_loggedIn;
} }
bool UserInfo::updateCurrent()
{
Q_D(UserInfo);
bool previous = d->m_loggedIn;
d->m_loggedIn = sd_uid_is_on_seat(d->m_uid, 1, "seat0") > 0;
if (d->m_loggedIn != previous) {
if (d->m_loggedIn)
UserInfoPrivate::s_current = d_ptr;
else if (UserInfoPrivate::s_current == d_ptr)
UserInfoPrivate::s_current.clear();
emit d_ptr->currentChanged();
return true;
}
return false;
}
void UserInfo::reset() void UserInfo::reset()
{ {
Q_D(UserInfo); Q_D(UserInfo);
d->set((isValid()) ? getpwuid(d->m_uid) : nullptr); d->set((isValid()) ? getpwuid(d->m_uid) : nullptr);
updateCurrent();
} }
UserInfo &UserInfo::operator=(const UserInfo &other) UserInfo &UserInfo::operator=(const UserInfo &other)
...@@ -286,4 +301,5 @@ void UserInfo::connectSignals() ...@@ -286,4 +301,5 @@ void UserInfo::connectSignals()
connect(d_ptr.data(), &UserInfoPrivate::usernameChanged, this, &UserInfo::usernameChanged); connect(d_ptr.data(), &UserInfoPrivate::usernameChanged, this, &UserInfo::usernameChanged);
connect(d_ptr.data(), &UserInfoPrivate::nameChanged, this, &UserInfo::nameChanged); connect(d_ptr.data(), &UserInfoPrivate::nameChanged, this, &UserInfo::nameChanged);
connect(d_ptr.data(), &UserInfoPrivate::uidChanged, this, &UserInfo::uidChanged); connect(d_ptr.data(), &UserInfoPrivate::uidChanged, this, &UserInfo::uidChanged);
connect(d_ptr.data(), &UserInfoPrivate::currentChanged, this, &UserInfo::currentChanged);
} }
...@@ -50,7 +50,7 @@ class SYSTEMSETTINGS_EXPORT UserInfo: public QObject ...@@ -50,7 +50,7 @@ class SYSTEMSETTINGS_EXPORT UserInfo: public QObject
Q_PROPERTY(QString name READ name NOTIFY nameChanged) Q_PROPERTY(QString name READ name NOTIFY nameChanged)
Q_PROPERTY(UserType type READ type CONSTANT) Q_PROPERTY(UserType type READ type CONSTANT)
Q_PROPERTY(int uid READ uid NOTIFY uidChanged) Q_PROPERTY(int uid READ uid NOTIFY uidChanged)
Q_PROPERTY(bool current READ current CONSTANT) Q_PROPERTY(bool current READ current NOTIFY currentChanged)
friend class UserModel; friend class UserModel;
...@@ -85,6 +85,7 @@ signals: ...@@ -85,6 +85,7 @@ signals:
void usernameChanged(); void usernameChanged();
void nameChanged(); void nameChanged();
void uidChanged(); void uidChanged();
void currentChanged();
private: private:
explicit UserInfo(int uid); explicit UserInfo(int uid);
...@@ -92,6 +93,7 @@ private: ...@@ -92,6 +93,7 @@ private:
void setUsername(QString username); void setUsername(QString username);
void setName(QString name); void setName(QString name);
bool updateCurrent();
void connectSignals(); void connectSignals();
......
...@@ -59,6 +59,7 @@ signals: ...@@ -59,6 +59,7 @@ signals:
void usernameChanged(); void usernameChanged();
void nameChanged(); void nameChanged();
void uidChanged(); void uidChanged();
void currentChanged();
}; };
#endif /* USERINFOPRIVATE_H */ #endif /* USERINFOPRIVATE_H */
...@@ -42,13 +42,32 @@ ...@@ -42,13 +42,32 @@
#include <QString> #include <QString>
#include <functional> #include <functional>
#include <sailfishusermanagerinterface.h> #include <sailfishusermanagerinterface.h>
#include <grp.h>
#include <sys/types.h> #include <sys/types.h>
#include <grp.h>
namespace { namespace {
const auto UserManagerService = QStringLiteral(SAILFISH_USERMANAGER_DBUS_INTERFACE); const auto UserManagerService = QStringLiteral(SAILFISH_USERMANAGER_DBUS_INTERFACE);
const auto UserManagerPath = QStringLiteral(SAILFISH_USERMANAGER_DBUS_OBJECT_PATH); const auto UserManagerPath = QStringLiteral(SAILFISH_USERMANAGER_DBUS_OBJECT_PATH);
const auto UserManagerInterface = QStringLiteral(SAILFISH_USERMANAGER_DBUS_INTERFACE); const auto UserManagerInterface = QStringLiteral(SAILFISH_USERMANAGER_DBUS_INTERFACE);
const QHash<const QString, int> errorTypeMap = {
{ QStringLiteral(SailfishUserManagerErrorBusy), UserModel::Busy },
{ QStringLiteral(SailfishUserManagerErrorHomeCreateFailed), UserModel::HomeCreateFailed },
{ QStringLiteral(SailfishUserManagerErrorHomeRemoveFailed), UserModel::HomeRemoveFailed },
{ QStringLiteral(SailfishUserManagerErrorGroupCreateFailed), UserModel::GroupCreateFailed },
{ QStringLiteral(SailfishUserManagerErrorUserAddFailed), UserModel::UserAddFailed },
{ QStringLiteral(SailfishUserManagerErrorUserModifyFailed), UserModel::UserModifyFailed },
{ QStringLiteral(SailfishUserManagerErrorUserRemoveFailed), UserModel::UserRemoveFailed },
{ QStringLiteral(SailfishUserManagerErrorGetUidFailed), UserModel::GetUidFailed },
};
int getErrorType(QDBusError &error)
{
if (error.type() != QDBusError::Other)
return error.type();
return errorTypeMap.value(error.name(), UserModel::OtherError);
}
} }
UserModel::UserModel(QObject *parent) UserModel::UserModel(QObject *parent)
...@@ -93,12 +112,12 @@ void UserModel::setPlaceholder(bool value) ...@@ -93,12 +112,12 @@ void UserModel::setPlaceholder(bool value)
return; return;
if (value) { if (value) {
auto row = m_users.count(); int row = m_users.count();
beginInsertRows(QModelIndex(), row, row); beginInsertRows(QModelIndex(), row, row);
m_users.append(UserInfo::placeholder()); m_users.append(UserInfo::placeholder());
endInsertRows(); endInsertRows();
} else { } else {
auto row = m_users.count()-1; int row = m_users.count()-1;
beginRemoveRows(QModelIndex(), row, row); beginRemoveRows(QModelIndex(), row, row);
m_users.remove(row); m_users.remove(row);
endRemoveRows(); endRemoveRows();
...@@ -213,7 +232,7 @@ void UserModel::createUser() ...@@ -213,7 +232,7 @@ void UserModel::createUser()
auto call = m_dBusInterface->asyncCall(QStringLiteral("addUser"), user.name()); auto call = m_dBusInterface->asyncCall(QStringLiteral("addUser"), user.name());
auto *watcher = new QDBusPendingCallWatcher(call, this); auto *watcher = new QDBusPendingCallWatcher(call, this);
connect(watcher, &QDBusPendingCallWatcher::finished, connect(watcher, &QDBusPendingCallWatcher::finished,
this, std::bind(&UserModel::userAddFinished, this, std::placeholders::_1, m_users.count()-1)); this, &UserModel::userAddFinished);
} }
void UserModel::removeUser(int row) void UserModel::removeUser(int row)
...@@ -232,6 +251,22 @@ void UserModel::removeUser(int row) ...@@ -232,6 +251,22 @@ void UserModel::removeUser(int row)
this, std::bind(&UserModel::userRemoveFinished, this, std::placeholders::_1, row)); this, std::bind(&UserModel::userRemoveFinished, this, std::placeholders::_1, row));
} }
void UserModel::setCurrentUser(int row)
{
if (row < 0 || row >= m_users.count())
return;
auto user = m_users.at(row);
if (!user.isValid())
return;
createInterface();
auto call = m_dBusInterface->asyncCall(QStringLiteral("setCurrentUser"), (uint)user.uid());
auto *watcher = new QDBusPendingCallWatcher(call, this);
connect(watcher, &QDBusPendingCallWatcher::finished,
this, std::bind(&UserModel::setCurrentUserFinished, this, std::placeholders::_1, row));
}
void UserModel::reset(int row) void UserModel::reset(int row)
{ {
if (row < 0 || row >= m_users.count()) if (row < 0 || row >= m_users.count())
...@@ -247,7 +282,7 @@ UserInfo * UserModel::getCurrentUser() const ...@@ -247,7 +282,7 @@ UserInfo * UserModel::getCurrentUser() const
return new UserInfo(); return new UserInfo();
} }
void UserModel::userAdded(const SailfishUserManagerEntry &entry) void UserModel::onUserAdded(const SailfishUserManagerEntry &entry)
{ {
if (m_uidsToRows.contains(entry.uid)) if (m_uidsToRows.contains(entry.uid))
return; return;
...@@ -263,7 +298,7 @@ void UserModel::userAdded(const SailfishUserManagerEntry &entry) ...@@ -263,7 +298,7 @@ void UserModel::userAdded(const SailfishUserManagerEntry &entry)
} }
} }
void UserModel::userModified(uint uid, const QString &newName) void UserModel::onUserModified(uint uid, const QString &newName)
{ {
if (!m_uidsToRows.contains(uid)) if (!m_uidsToRows.contains(uid))
return; return;
...@@ -277,7 +312,7 @@ void UserModel::userModified(uint uid, const QString &newName) ...@@ -277,7 +312,7 @@ void UserModel::userModified(uint uid, const QString &newName)
} }
} }
void UserModel::userRemoved(uint uid) void UserModel::onUserRemoved(uint uid)
{ {
if (!m_uidsToRows.contains(uid)) if (!m_uidsToRows.contains(uid))
return; return;
...@@ -285,20 +320,52 @@ void UserModel::userRemoved(uint uid) ...@@ -285,20 +320,52 @@ void UserModel::userRemoved(uint uid)
int row = m_uidsToRows.value(uid); int row = m_uidsToRows.value(uid);
beginRemoveRows(QModelIndex(), row, row); beginRemoveRows(QModelIndex(), row, row);
m_users.remove(row); m_users.remove(row);
// It is slightly costly to remove users since some row numbers may need to be updated
m_uidsToRows.remove(uid); m_uidsToRows.remove(uid);
for (auto iter = m_uidsToRows.begin(); iter != m_uidsToRows.end(); ++iter) {
if (iter.value() > row)
iter.value() -= 1;
}
endRemoveRows(); endRemoveRows();
} }
void UserModel::userAddFinished(QDBusPendingCallWatcher *call, int row) void UserModel::onCurrentUserChanged(uint uid)
{
UserInfo *previous = getCurrentUser();
if (previous) {
if (previous->updateCurrent()) {
auto idx = index(m_uidsToRows.value(previous->uid()), 0);
emit dataChanged(idx, idx, QVector<int>() << CurrentRole);
}
delete previous;
}
if (m_uidsToRows.contains(uid) && m_users[m_uidsToRows.value(uid)].updateCurrent()) {
auto idx = index(m_uidsToRows.value(uid), 0);
emit dataChanged(idx, idx, QVector<int>() << CurrentRole);
}
}
void UserModel::onCurrentUserChangeFailed(uint uid)
{
if (m_uidsToRows.contains(uid)) {
int row = m_uidsToRows.value(uid);
emit setCurrentUserFailed(row, Failure);
}
}
void UserModel::userAddFinished(QDBusPendingCallWatcher *call)
{ {
QDBusPendingReply<uint> reply = *call; QDBusPendingReply<uint> reply = *call;
if (reply.isError()) { if (reply.isError()) {
emit userAddFailed(); auto error = reply.error();
qWarning() << "Adding user with usermanager failed:" << reply.error(); emit userAddFailed(getErrorType(error));
qCWarning(lcUsersLog) << "Adding user with usermanager failed:" << error;
} else { } else {
uint uid = reply.value(); uint uid = reply.value();
// Check that this was not just added to the list by userAdded // Check that this was not just added to the list by onUserAdded
if (!m_uidsToRows.contains(uid)) { if (!m_uidsToRows.contains(uid)) {
// Add to the end
int row = m_users.count()-1;
beginInsertRows(QModelIndex(), row, row); beginInsertRows(QModelIndex(), row, row);
m_users.insert(row, UserInfo(uid)); m_users.insert(row, UserInfo(uid));
m_uidsToRows.insert(uid, row); m_uidsToRows.insert(uid, row);
...@@ -314,8 +381,9 @@ void UserModel::userModifyFinished(QDBusPendingCallWatcher *call, int row) ...@@ -314,8 +381,9 @@ void UserModel::userModifyFinished(QDBusPendingCallWatcher *call, int row)
{ {
QDBusPendingReply<void> reply = *call; QDBusPendingReply<void> reply = *call;
if (reply.isError()) { if (reply.isError()) {
emit userModifyFailed(row, m_users.at(row).name()); auto error = reply.error();
qWarning() << "Modifying user with usermanager failed:" << reply.error(); emit userModifyFailed(row, getErrorType(error));
qCWarning(lcUsersLog) << "Modifying user with usermanager failed:" << error;
reset(row); reset(row);
} // else awesome! (data was changed already) } // else awesome! (data was changed already)
call->deleteLater(); call->deleteLater();
...@@ -325,12 +393,24 @@ void UserModel::userRemoveFinished(QDBusPendingCallWatcher *call, int row) ...@@ -325,12 +393,24 @@ void UserModel::userRemoveFinished(QDBusPendingCallWatcher *call, int row)
{ {
QDBusPendingReply<void> reply = *call; QDBusPendingReply<void> reply = *call;
if (reply.isError()) { if (reply.isError()) {
emit userRemoveFailed(row, m_users.at(row).name()); auto error = reply.error();
qWarning() << "Removing user with usermanager failed:" << reply.error(); emit userRemoveFailed(row, getErrorType(error));
qCWarning(lcUsersLog) << "Removing user with usermanager failed:" << error;
} // else awesome! (waiting for signal to alter data) } // else awesome! (waiting for signal to alter data)
call->deleteLater(); call->deleteLater();
} }
void UserModel::setCurrentUserFinished(QDBusPendingCallWatcher *call, int row)
{
QDBusPendingReply<void> reply = *call;
if (reply.isError()) {
auto error = reply.error();
emit setCurrentUserFailed(row, getErrorType(error));
qCWarning(lcUsersLog) << "Switching user with usermanager failed:" << error;
} // else user switching was initiated successfully
call->deleteLater();
}
void UserModel::createInterface() void UserModel::createInterface()
{ {
if (!m_dBusInterface) { if (!m_dBusInterface) {
...@@ -338,11 +418,15 @@ void UserModel::createInterface() ...@@ -338,11 +418,15 @@ void UserModel::createInterface()
m_dBusInterface = new QDBusInterface(UserManagerService, UserManagerPath, UserManagerInterface, m_dBusInterface = new QDBusInterface(UserManagerService, UserManagerPath, UserManagerInterface,
QDBusConnection::systemBus(), this); QDBusConnection::systemBus(), this);
connect(m_dBusInterface, SIGNAL(userAdded(const SailfishUserManagerEntry &)), connect(m_dBusInterface, SIGNAL(userAdded(const SailfishUserManagerEntry &)),
this, SLOT(userAdded(const SailfishUserManagerEntry &)), Qt::QueuedConnection); this, SLOT(onUserAdded(const SailfishUserManagerEntry &)), Qt::QueuedConnection);
connect(m_dBusInterface, SIGNAL(userModified(uint, const QString &)), connect(m_dBusInterface, SIGNAL(userModified(uint, const QString &)),
this, SLOT(userModified(uint, const QString &))); this, SLOT(onUserModified(uint, const QString &)));
connect(m_dBusInterface, SIGNAL(userRemoved(uint)), connect(m_dBusInterface, SIGNAL(userRemoved(uint)),
this, SLOT(userRemoved(uint))); this, SLOT(onUserRemoved(uint)));
connect(m_dBusInterface, SIGNAL(currentUserChanged(uint)),
this, SLOT(onCurrentUserChanged(uint)));
connect(m_dBusInterface, SIGNAL(currentUserChangeFailed(uint)),
this, SLOT(onCurrentUserChangeFailed(uint)));
} }
} }
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#define USERMODEL_H #define USERMODEL_H
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QDBusError>
#include <QHash> #include <QHash>
#include <QVector> #include <QVector>
...@@ -66,6 +67,21 @@ public: ...@@ -66,6 +67,21 @@ public:
}; };
Q_ENUM(UserType) Q_ENUM(UserType)
enum ErrorType {
Failure = QDBusError::Failed,
OtherError = QDBusError::Other,
InvalidArgs = QDBusError::InvalidArgs,
Busy = 100,
HomeCreateFailed,
HomeRemoveFailed,
GroupCreateFailed,
UserAddFailed,
UserModifyFailed,
UserRemoveFailed,
GetUidFailed,
};
Q_ENUM(ErrorType)
explicit UserModel(QObject *parent = 0); explicit UserModel(QObject *parent = 0);
~UserModel(); ~UserModel();
...@@ -81,21 +97,28 @@ public: ...@@ -81,21 +97,28 @@ public:
Q_INVOKABLE void createUser(); Q_INVOKABLE void createUser();
Q_INVOKABLE void removeUser(int row); Q_INVOKABLE void removeUser(int row);
Q_INVOKABLE void reset(int row); Q_INVOKABLE void reset(int row);
Q_INVOKABLE void setCurrentUser(int row);
Q_INVOKABLE UserInfo * getCurrentUser() const; Q_INVOKABLE UserInfo * getCurrentUser() const;
signals: signals:
void placeholderChanged(); void placeholderChanged();
void userAddFailed(); void userAddFailed(int error);
void userModifyFailed(int row, const QString &name); void userModifyFailed(int row, int error);
void userRemoveFailed(int row, const QString &name); void userRemoveFailed(int row, int error);
void setCurrentUserFailed(int row, int error);
private slots: private slots:
void userAdded(const SailfishUserManagerEntry &entry); void onUserAdded(const SailfishUserManagerEntry &entry);
void userModified(uint uid, const QString &newName); void onUserModified(uint uid, const QString &newName);
void userRemoved(uint uid); void onUserRemoved(uint uid);
void userAddFinished(QDBusPendingCallWatcher *call, int row); void onCurrentUserChanged(uint uid);
void onCurrentUserChangeFailed(uint uid);
void userAddFinished(QDBusPendingCallWatcher *call);
void userModifyFinished(QDBusPendingCallWatcher *call, int row); void userModifyFinished(QDBusPendingCallWatcher *call, int row);
void userRemoveFinished(QDBusPendingCallWatcher *call, int row); void userRemoveFinished(QDBusPendingCallWatcher *call, int row);
void setCurrentUserFinished(QDBusPendingCallWatcher *call, int row);
void createInterface(); void createInterface();
void destroyInterface(); void destroyInterface();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment