I have made the following changes intended for : CE:MW:Shared / nemo-qml-plugins
Please review and accept or decline. BOSS has already run some checks on this request. See the "Messages from BOSS" section below. https://build.pub.meego.com//request/show/7582 Thank You, John Brooks [This message was auto-generated] --- Request # 7582: Messages from BOSS: State: review at 2012-12-28T01:41:22 by bossbot Reviews: accepted by bossbot : Prechecks succeeded. new for CE-maintainers : Please replace this text with a review and approve/reject the review (not the SR). BOSS will take care of the rest Changes: submit: home:special:branches:CE:MW:Shared / nemo-qml-plugins -> CE:MW:Shared / nemo-qml-plugins changes files: -------------- --- nemo-qml-plugins.changes +++ nemo-qml-plugins.changes @@ -0,0 +1,3 @@ +* Thu Dec 27 2012 John Brooks <[email protected]> - 0.1.10 +- Fix account configuration value saving, add ServiceAccountModel (by Chris Adams) + old: ---- nemo-qml-plugins-0.1.9.tar.bz2 new: ---- nemo-qml-plugins-0.1.10.tar.bz2 spec files: ----------- --- nemo-qml-plugins.spec +++ nemo-qml-plugins.spec @@ -9,7 +9,7 @@ # << macros Summary: Nemo QML plugins source package. -Version: 0.1.9 +Version: 0.1.10 Release: 1 Group: System/Libraries License: BSD other changes: -------------- ++++++ nemo-qml-plugins-0.1.9.tar.bz2 -> nemo-qml-plugins-0.1.10.tar.bz2 --- accounts/src/accountinterface.cpp +++ accounts/src/accountinterface.cpp @@ -68,6 +68,26 @@ { } +static QVariant configurationValueVariant(Accounts::Account *acc, const QString &key) +{ + QString strVal = acc->valueAsString(key); + if (!strVal.isNull()) + return QVariant(strVal); + + quint64 uintMax = 0xffffffffffffffffU; + quint64 uintVal = acc->valueAsUInt64(key, uintMax); + if (uintVal != uintMax) + return QVariant(uintVal); + + int intMax = 0xffffffff; + int intVal = acc->valueAsInt(key, intMax); + if (intVal != intMax) + return QVariant(intVal); + + bool boolVal = acc->valueAsBool(key); + return QVariant(boolVal); +} + void AccountInterfacePrivate::setAccount(Accounts::Account *acc) { if (!acc) { @@ -172,11 +192,8 @@ // enumerate the configuration values QVariantMap allValues; QStringList allKeys = account->allKeys(); - foreach (const QString &key, allKeys) { - QVariant currValue; - account->value(key, currValue); // pass by ref. - allValues.insert(key, currValue); - } + foreach (const QString &key, allKeys) + allValues.insert(key, configurationValueVariant(account, key)); if (configurationValues != allValues) { configurationValues = allValues; emit q->configurationValuesChanged(); @@ -298,11 +315,8 @@ // check to see if the configuration values were updated QVariantMap allValues; QStringList allKeys = account->allKeys(); - foreach (const QString &key, allKeys) { - QVariant currValue; - account->value(key, currValue); // pass by ref. - allValues.insert(key, currValue); - } + foreach (const QString &key, allKeys) + allValues.insert(key, configurationValueVariant(account, key)); if (configurationValues != allValues) { configurationValues = allValues; emit q->configurationValuesChanged(); @@ -506,9 +520,6 @@ d->enabledServiceNamesPendingInit = false; } - d->account->setEnabled(d->enabled); - d->account->setDisplayName(d->displayName); - // set the credentials / identity identifiers for each service. foreach (const QString &srvn, d->supportedServiceNames) { int currIdentId = d->identityIdentifiers.value(srvn).value<int>(); @@ -548,14 +559,32 @@ // set the configuration values. QStringList allKeys = d->account->allKeys(); QStringList setKeys = d->configurationValues.keys(); + QStringList doneKeys; foreach (const QString &key, allKeys) { + // overwrite existing keys if (setKeys.contains(key)) { + doneKeys.append(key); const QVariant &currValue = d->configurationValues.value(key); - d->account->setValue(key, currValue); + if (currValue.isValid()) { + d->account->setValue(key, currValue); + } else { + d->account->remove(key); + } } else { + // remove removed keys d->account->remove(key); } } + foreach (const QString &key, setKeys) { + // add new keys + if (!doneKeys.contains(key)) { + const QVariant &currValue = d->configurationValues.value(key); + d->account->setValue(key, currValue); + } + } + + d->account->setEnabled(d->enabled); + d->account->setDisplayName(d->displayName); d->setStatus(AccountInterface::SyncInProgress); d->account->sync(); @@ -579,13 +608,22 @@ /*! \qmlmethod void Account::setConfigurationValue(const QString &key, const QVariant &value) Sets the account's configuration settings value for the key \a key - to the value \a value. + to the value \a value. The only supported value types are int, quint64, bool and QString. */ void AccountInterface::setConfigurationValue(const QString &key, const QVariant &value) { if (d->status == AccountInterface::Invalid || d->status == AccountInterface::SyncInProgress) return; + if (value.type() != QVariant::Int + && value.type() != QVariant::LongLong + && value.type() != QVariant::ULongLong + && value.type() != QVariant::Bool + && value.type() != QVariant::String) { + qWarning() << Q_FUNC_INFO << "Unsupported configuration value type! Must be int, quint64, bool or string."; + return; // unsupported value type. + } + d->configurationValues.insert(key, value); if (d->status == AccountInterface::Initializing) d->configurationValuesPendingInit = true; @@ -916,6 +954,8 @@ Some default settings are usually specified in the \c{.service} file installed by the account provider plugin. Other settings may be specified directly for an account. + + The only supported value types are int, quint64, bool and QString. */ QVariantMap AccountInterface::configurationValues() const @@ -929,7 +969,24 @@ { if (d->status == AccountInterface::Invalid || d->status == AccountInterface::SyncInProgress) return; - d->configurationValues = values; + + QVariantMap validValues; + QStringList vkeys = values.keys(); + foreach (const QString &key, vkeys) { + QVariant currValue = values.value(key); + if (currValue.type() == QVariant::Bool + || currValue.type() == QVariant::Int + || currValue.type() == QVariant::LongLong + || currValue.type() == QVariant::ULongLong + || currValue.type() == QVariant::String) { + validValues.insert(key, currValue); + } + } + + if (d->configurationValues == validValues) + return; + + d->configurationValues = validValues; if (d->status == AccountInterface::Initializing) d->configurationValuesPendingInit = true; else --- accounts/src/plugin.cpp +++ accounts/src/plugin.cpp @@ -36,6 +36,7 @@ #include "account-model.h" #include "account-provider-model.h" +#include "service-account-model.h" #include "accountinterface.h" #include "accountmanagerinterface.h" @@ -65,6 +66,7 @@ // Types which should be exposed to "normal" applications: qmlRegisterType<AccountModel>(uri, 1, 0, "AccountModel"); + qmlRegisterType<ServiceAccountModel>(uri, 1, 0, "ServiceAccountModel"); qmlRegisterType<AccountProviderModel>(uri, 1, 0, "AccountProviderModel"); qmlRegisterType<ProviderInterface>(uri, 1, 0, "Provider"); qmlRegisterUncreatableType<ServiceAccountInterface>(uri, 1, 0, "ServiceAccount", m2); --- accounts/src/providerinterface.cpp +++ accounts/src/providerinterface.cpp @@ -31,10 +31,14 @@ #include "providerinterface.h" +//libaccounts-qt +#include <Accounts/Manager> + class ProviderInterfacePrivate { public: Accounts::Provider provider; + QStringList serviceNames; }; /*! @@ -58,6 +62,15 @@ : QObject(parent), d(new ProviderInterfacePrivate) { d->provider = provider; + + // first time fetch of service names. + Accounts::Manager m; + Accounts::ServiceList services = m.serviceList(); + foreach (const Accounts::Service &srv, services) { + if (srv.provider() == d->provider.name()) { + d->serviceNames.append(srv.name()); + } + } } ProviderInterface::~ProviderInterface() @@ -96,3 +109,13 @@ return d->provider.iconName(); } +/*! + \qmlproperty QStringList Provider::serviceNames + The names of services provided by this provider. +*/ + +QStringList ProviderInterface::serviceNames() const +{ + return d->serviceNames; +} + --- accounts/src/providerinterface.h +++ accounts/src/providerinterface.h @@ -53,6 +53,7 @@ Q_PROPERTY(QString name READ name CONSTANT) Q_PROPERTY(QString displayName READ displayName CONSTANT) Q_PROPERTY(QString iconName READ iconName CONSTANT) + Q_PROPERTY(QStringList serviceNames READ serviceNames CONSTANT) // XXX TODO: expose the QDomDocument? What about the translation catalogue for the display name? public: @@ -64,6 +65,8 @@ QString displayName() const; QString iconName() const; + QStringList serviceNames() const; + private: ProviderInterfacePrivate *d; }; --- accounts/src/service-account-model.cpp +++ accounts/src/service-account-model.cpp @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2012 Jolla Ltd. <[email protected]> + * + * You may use this file under the terms of the BSD license as follows: + * + * "Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Nemo Mobile nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + */ + +//project +#include "service-account-model.h" +#include "serviceaccountinterface.h" +#include "providerinterface.h" + +//Qt +#include <QtDebug> + +//libaccounts-qt +#include <Accounts/Manager> +#include <Accounts/Account> +#include <Accounts/Provider> + +struct DisplayData { + DisplayData(Accounts::AccountService *acct) : account(acct), enabledWithService(false) {} + ~DisplayData() { delete account; } + Accounts::AccountService *account; + QString providerName; + QString providerDisplayName; + QString accountIcon; + QString serviceName; + QString serviceDisplayName; + QString serviceIcon; + bool enabledWithService; + Q_DISABLE_COPY(DisplayData); +}; + +class ServiceAccountModel::ServiceAccountModelPrivate +{ +public: + ~ServiceAccountModelPrivate() + { + qDeleteAll(accountsList); + } + + QHash<int, QByteArray> headerData; + Accounts::Manager *manager; + QList<DisplayData *> accountsList; +}; + +ServiceAccountModel::ServiceAccountModel(QObject* parent) + : QAbstractListModel(parent) + , d_ptr(new ServiceAccountModelPrivate()) +{ + Q_D(ServiceAccountModel); + d->manager = new Accounts::Manager(); + d->headerData.insert(AccountIdRole, "accountId"); + d->headerData.insert(AccountDisplayNameRole, "accountDisplayName"); + d->headerData.insert(AccountIconRole, "accountIcon" ); + d->headerData.insert(ServiceNameRole, "serviceName"); + d->headerData.insert(ServiceDisplayNameRole, "serviceDisplayName"); + d->headerData.insert(ServiceIconRole, "serviceIcon" ); + d->headerData.insert(ProviderNameRole, "providerName"); + d->headerData.insert(ProviderDisplayNameRole, "providerDisplayName"); + d->headerData.insert(EnabledWithServiceRole, "enabledWithService"); + d->headerData.insert(ColumnCountRole, "columncount"); + QObject::connect(d->manager, SIGNAL(accountCreated(Accounts::AccountId)), + this, SLOT(accountCreated(Accounts::AccountId))); + setRoleNames(d->headerData); + Accounts::AccountIdList idList = d->manager->accountList(); + foreach (Accounts::AccountId id, idList) + { + Accounts::Account *account = d->manager->account(id); + Accounts::ServiceList accountServices = account->services(); + foreach (const Accounts::Service &srv, accountServices) { + Accounts::AccountService *srvAcc = new Accounts::AccountService(account, srv); + d->accountsList.append(new DisplayData(srvAcc)); + } + } +} + +ServiceAccountModel::~ServiceAccountModel() +{ +} + +int ServiceAccountModel::rowCount(const QModelIndex &parent) const +{ + Q_D(const ServiceAccountModel); + if (parent.isValid()) + return 0; + return d->accountsList.length(); +} + +int ServiceAccountModel::columnCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + return ColumnCount; +} + +QVariant ServiceAccountModel::data(const QModelIndex &index, int role) const +{ + Q_D(const ServiceAccountModel); + if (!index.isValid() || index.row() >= d->accountsList.length()) + return QVariant(); + + DisplayData *data = d->accountsList[index.row()]; + Accounts::AccountService *account = data->account; + + if (role == AccountIdRole || + (role == Qt::DisplayRole && index.column() == AccountIdColumn)) + return QVariant::fromValue(account->account()->id()); + + if (role == AccountDisplayNameRole || + (role == Qt::DisplayRole && index.column() == AccountDisplayNameColumn)) + return QVariant::fromValue(account->account()->displayName()); + + if (role == AccountIconRole || + (role == Qt::DisplayRole && index.column() == AccountIconColumn)) { + if (data->accountIcon.isNull()) { + Accounts::Provider provider = d->manager->provider(account->account()->providerName()); + data->accountIcon = provider.iconName(); + } + return QVariant::fromValue(data->accountIcon); + } + + if (role == ServiceNameRole || + (role == Qt::DisplayRole && index.column() == ServiceNameColumn)) { + data->serviceName = account->service().name(); + return QVariant::fromValue(data->serviceName); + } + + if (role == ServiceDisplayNameRole || + (role == Qt::DisplayRole && index.column() == ServiceDisplayNameColumn)) { + data->serviceDisplayName = account->service().displayName(); + return QVariant::fromValue(data->serviceDisplayName); + } + + if (role == ServiceIconRole || + (role == Qt::DisplayRole && index.column() == ServiceIconColumn)) { + if (data->serviceIcon.isNull()) + data->serviceIcon = account->service().iconName(); + return QVariant::fromValue(data->serviceIcon); + } + + if (role == ProviderNameRole || + (role == Qt::DisplayRole && index.column() == ProviderNameColumn)) { + Accounts::Provider provider = d->manager->provider(account->account()->providerName()); + data->providerName = provider.name(); + return QVariant::fromValue(data->providerName); + } + + if (role == ProviderDisplayNameRole || + (role == Qt::DisplayRole && index.column() == ProviderDisplayNameColumn)) { + Accounts::Provider provider = d->manager->provider(account->account()->providerName()); + data->providerDisplayName = provider.displayName(); + return QVariant::fromValue(data->providerDisplayName); + } + + if (role == EnabledWithServiceRole || + (role == Qt::DisplayRole && index.column() == EnabledWithServiceColumn)) { + data->enabledWithService = account->enabled(); + return QVariant::fromValue(data->enabledWithService); + } + + return QVariant(); +} + +QVariant ServiceAccountModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + Q_D(const ServiceAccountModel); + if (orientation != Qt::Horizontal) { + return QVariant(); + } + Columns column = static_cast<Columns>(section); + + if (role == Qt::DisplayRole) { + if (section < d->headerData.size()) { + return d->headerData.value(column); + } + } + return QVariant(); +} + +void ServiceAccountModel::accountCreated(Accounts::AccountId id) +{ + Q_D(ServiceAccountModel); + QModelIndex index; + Accounts::Account *account = d->manager->account(id); + + QObject::connect(account, SIGNAL(removed()), + this, SLOT(accountRemoved())); + QObject::connect(account, SIGNAL(enabledChanged(const QString, bool)), + this, SLOT(accountUpdated())); + + if (account != 0) { + Accounts::ServiceList srvList = account->services(); + int count = srvList.size(); + beginInsertRows(index, 0, count - 1); + for (int i = 0; i < count; ++i) { + Accounts::AccountService *accSrv = new Accounts::AccountService(account, srvList.at(i)); + d->accountsList.insert(0, new DisplayData(accSrv)); + } + endInsertRows(); + } +} + +void ServiceAccountModel::accountRemoved() +{ + Q_D(ServiceAccountModel); + Accounts::Account *account = qobject_cast<Accounts::Account *>(sender()); + + QList<int> indexes = getAccountIndexes(account); + if (!indexes.size()) { + qWarning() << Q_FUNC_INFO << "Account not present in the list:" << account->id(); + return; + } + + QModelIndex parent; + QList<DisplayData *> cleanupList; + + // remove them in contiguous chunks if possible. + qSort(indexes); + int maxIdx = indexes.last(); + int firstIdx = indexes.first(); + int lastIdx = firstIdx; + int which = 0; + while (lastIdx < maxIdx) { + int currIdx = indexes.at(++which); + if (currIdx == (lastIdx + 1)) { + // contiguous + lastIdx += 1; + } else { + // noncontiguous. Remove the chunk. + beginRemoveRows(parent, firstIdx, lastIdx); + for (int i = firstIdx; i < lastIdx; ++i) { + DisplayData *data = d->accountsList[i]; + d->accountsList.removeAt(i); + cleanupList.append(data); + } + endRemoveRows(); + qDeleteAll(cleanupList); + cleanupList.clear(); + + // prepare for next chunk. + firstIdx = currIdx; + lastIdx = currIdx; + } + } + + // remove the last (or possibly only) chunk + beginRemoveRows(parent, firstIdx, lastIdx); + for (int i = firstIdx; i < lastIdx; ++i) { + DisplayData *data = d->accountsList[i]; + d->accountsList.removeAt(i); + cleanupList.append(data); + } + endRemoveRows(); + qDeleteAll(cleanupList); +} + +void ServiceAccountModel::accountUpdated() +{ + Accounts::Account *account = qobject_cast<Accounts::Account *>(sender()); + QList<int> indexes = getAccountIndexes(account); + if (!indexes.size()) { + qWarning() << Q_FUNC_INFO << "Account not present in the list:" << account->id(); + return; + } + + // emit change signals for contiguous chunks + qSort(indexes); + int maxIdx = indexes.last(); + int firstIdx = indexes.first(); + int lastIdx = firstIdx; + int which = 0; + while (lastIdx < maxIdx) { + int currIdx = indexes.at(++which); + if (currIdx == (lastIdx + 1)) { + // contiguous + lastIdx += 1; + } else { + // noncontiguous. Emit change signal for the chunk. + emit dataChanged(index(firstIdx, 0), index(lastIdx, 0)); + + // prepare for next chunk. + firstIdx = currIdx; + lastIdx = currIdx; + } + } + + // emit change signal for the last (or possibly only) chunk + emit dataChanged(index(firstIdx, 0), index(lastIdx, 0)); +} + +// Each Account can be represented multiple times in the list (as it may support multiple services) +QList<int> ServiceAccountModel::getAccountIndexes(Accounts::Account *account) const +{ + Q_D(const ServiceAccountModel); + QList<int> indexes; + for (int i = 0; i < d->accountsList.count(); ++i) { + const DisplayData *data = d->accountsList[i]; + if (data->account->account() == account) + indexes.append(i); + } + return indexes; +} + +/*! + \qmltype ServiceAccountModel + \instantiates ServiceAccountModel + \inqmlmodule org.nemomobile.accounts 1 + \brief Provides a model of existing per-service accounts + + The ServiceAccountModel can be used to provide service account data to a view. + For each account in the database, it exposes: + \list + \li accountId + \li accountDisplayName + \li accountIcon + \li serviceName + \li serviceDisplayName + \li serviceIcon + \li providerName + \li providerDisplayName + \li enabledWithService + \endlist + + It also provides invokable methods to retrieve a Provider + associated with an account. +*/ + +/*! + \qmlmethod Provider* ServiceAccountModel::provider(const QString &providerName, QObject *parent) + Returns the Provider with the given \a providerName, or zero if no such + provider exists. The returned Provider will be owned by the specified + \a parent. + */ +ProviderInterface *ServiceAccountModel::provider(const QString &providerName, QObject *parent) +{ + Q_D(ServiceAccountModel); + + if (providerName.isEmpty()) + return 0; + + Accounts::Provider retnProv; + Accounts::ProviderList providers = d->manager->providerList(); + foreach (const Accounts::Provider &p, providers) { + if (p.name() == providerName) { + retnProv = p; + break; + } + } + + if (!retnProv.isValid()) + return 0; + + return new ProviderInterface(retnProv, parent); +} + +/*! + \qmlmethod Provider* ServiceAccountModel::provider(const QString &providerName, QObject *parent) + Returns the Provider which provides the account identified by the given + \a accountId, or zero if no such account exists. The returned Provider + will be owned by the specified \a parent. + */ +ProviderInterface *ServiceAccountModel::provider(int accountId, QObject *parent) +{ + Q_D(ServiceAccountModel); + + if (accountId == 0) + return 0; + + for (int i = 0; i < d->accountsList.size(); ++i) { + DisplayData *data = d->accountsList.at(i); + if (data->account->account()->id() == accountId) { + return provider(data->account->account()->providerName(), parent); + } + } + + return 0; +} + --- accounts/src/service-account-model.h +++ accounts/src/service-account-model.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2012 Jolla Ltd. <[email protected]> + * + * You may use this file under the terms of the BSD license as follows: + * + * "Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Nemo Mobile nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + */ + +#ifndef SERVICEACCOUNTMODEL_H +#define SERVICEACCOUNTMODEL_H + +//accounts-qt +#include <Accounts/Manager> + +//Qt +#include <QAbstractTableModel> +#include <QMap> +#include <QVariant> + +class ServiceAccountInterface; +class ProviderInterface; + +/*! + * The Service Account Model is the model for per-service accounts + */ + +class ServiceAccountModel : public QAbstractListModel +{ + Q_OBJECT + class ServiceAccountModelPrivate; + +public: + + enum Columns { + AccountIdColumn, + AccountDisplayNameColumn, + AccountIconColumn, + ServiceNameColumn, + ServiceDisplayNameColumn, + ServiceIconColumn, + ProviderNameColumn, + ProviderDisplayNameColumn, + EnabledWithServiceColumn, + ColumnCount + }; + + enum Roles{ + AccountIdRole = Qt::UserRole + 1, + AccountDisplayNameRole, + AccountIconRole, + ServiceNameRole, + ServiceDisplayNameRole, + ServiceIconRole, + ProviderNameRole, + ProviderDisplayNameRole, + EnabledWithServiceRole, + ColumnCountRole + }; + + + ServiceAccountModel(QObject *parent = 0); + virtual ~ServiceAccountModel(); + + int rowCount(const QModelIndex &index = QModelIndex()) const; + int columnCount(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role) const; + + Q_INVOKABLE ProviderInterface *provider(const QString &providerName, QObject *parent = 0); + Q_INVOKABLE ProviderInterface *provider(int accountId, QObject *parent = 0); + +private slots: + void accountCreated(Accounts::AccountId id); + void accountRemoved(); + void accountUpdated(); + +private: + QList<int> getAccountIndexes(Accounts::Account *account) const; + +private: + ServiceAccountModelPrivate* d_ptr; + Q_DISABLE_COPY(ServiceAccountModel) + Q_DECLARE_PRIVATE(ServiceAccountModel); +}; +Q_DECLARE_METATYPE(Accounts::AccountService *) + +#endif // SERVICEACCOUNTMODEL_H --- accounts/src/src.pro +++ accounts/src/src.pro @@ -16,7 +16,8 @@ $$PWD/providerinterface.cpp \ $$PWD/serviceaccountinterface.cpp \ $$PWD/serviceinterface.cpp \ - $$PWD/servicetypeinterface.cpp + $$PWD/servicetypeinterface.cpp \ + $$PWD/service-account-model.cpp HEADERS += \ $$PWD/accountinterface.h \ @@ -30,7 +31,8 @@ $$PWD/accountinterface_p.h \ $$PWD/accountmanagerinterface_p.h \ $$PWD/serviceaccountinterface_p.h \ - $$PWD/servicetypeinterface.h + $$PWD/servicetypeinterface.h \ + $$PWD/service-account-model.h OTHER_FILES += accounts.qdoc accounts.qdocconf --- accounts/tests/tst_accountinterface/tst_accountinterface.cpp +++ accounts/tests/tst_accountinterface/tst_accountinterface.cpp @@ -172,12 +172,21 @@ void tst_AccountInterface::displayName() { QScopedPointer<AccountInterface> account(new AccountInterface); + account->classBegin(); account->setProviderName("test-provider"); QCOMPARE(account->displayName(), QString(QLatin1String(""))); QSignalSpy spy(account.data(), SIGNAL(displayNameChanged())); account->setDisplayName(QString(QLatin1String("test-display-name"))); QCOMPARE(spy.count(), 1); QCOMPARE(account->displayName(), QString(QLatin1String("test-display-name"))); + account->sync(); // pending sync. + account->componentComplete(); // will construct new account. + QTRY_COMPARE(account->status(), AccountInterface::Synced); + account->setDisplayName(QString(QLatin1String("test-display-name-two"))); + account->sync(); + QTRY_COMPARE(account->status(), AccountInterface::Synced); + QCOMPARE(account->displayName(), QString(QLatin1String("test-display-name-two"))); + account->remove(); } void tst_AccountInterface::supportedServiceNames() @@ -219,25 +228,88 @@ { QVariantMap testData; QString testKey(QLatin1String("test-key")); - QVariant testValue(QString(QLatin1String("test-value"))); - testData.insert(testKey, testValue); + QVariant testStrValue(QString(QLatin1String("test-value"))); + QVariant testBoolValue(true); + QVariant testIntValue(-5); + QVariant testQuintValue(0xaaaaaaaaaaaa); + testData.insert(testKey, testStrValue); QVariantMap noValueTestData; noValueTestData.insert(testKey, QVariant()); QScopedPointer<AccountInterface> account(new AccountInterface); + account->classBegin(); account->setProviderName("test-provider"); QCOMPARE(account->configurationValues(), QVariantMap()); QSignalSpy spy(account.data(), SIGNAL(configurationValuesChanged())); account->setConfigurationValues(testData); QCOMPARE(spy.count(), 1); QCOMPARE(account->configurationValues(), testData); - account->setConfigurationValue(testKey, QVariant()); - QCOMPARE(spy.count(), 2); - QCOMPARE(account->configurationValues(), noValueTestData); account->removeConfigurationValue(testKey); - QCOMPARE(spy.count(), 3); + QCOMPARE(spy.count(), 2); QCOMPARE(account->configurationValues(), QVariantMap()); + + // invalid values + account->setConfigurationValue(testKey, QVariant(QColor(Qt::black))); + QCOMPARE(spy.count(), 2); // no change signal + QCOMPARE(account->configurationValues(), QVariantMap()); // not set. + account->setConfigurationValue(testKey, QVariant()); + QCOMPARE(spy.count(), 2); // no change signal + QCOMPARE(account->configurationValues(), QVariantMap()); // not set. + + // bool, int, quint64 and string should all work. + account->setConfigurationValue(testKey, testBoolValue); + QCOMPARE(spy.count(), 3); + QCOMPARE(account->configurationValues().value(testKey), testBoolValue); + account->setConfigurationValue(testKey, testIntValue); + QCOMPARE(spy.count(), 4); + QCOMPARE(account->configurationValues().value(testKey), testIntValue); + account->setConfigurationValue(testKey, testQuintValue); + QCOMPARE(spy.count(), 5); + QCOMPARE(account->configurationValues().value(testKey), testQuintValue); + account->setConfigurationValue(testKey, testStrValue); + QCOMPARE(spy.count(), 6); + QCOMPARE(account->configurationValues().value(testKey), testStrValue); + + // ensure that configuration values can be saved. + account->sync(); // pending sync. + account->componentComplete(); // will create new account. + QTRY_COMPARE(account->status(), AccountInterface::Synced); + QCOMPARE(account->configurationValues().value(testKey), testStrValue); + account->setConfigurationValue(testKey, testQuintValue); + account->sync(); + QTRY_COMPARE(account->status(), AccountInterface::Synced); + QCOMPARE(account->configurationValues().value(testKey), testQuintValue); + account->setConfigurationValue(testKey, testIntValue); + account->sync(); + QTRY_COMPARE(account->status(), AccountInterface::Synced); + QCOMPARE(account->configurationValues().value(testKey), testIntValue); + account->setConfigurationValue(testKey, testBoolValue); + account->sync(); + QTRY_COMPARE(account->status(), AccountInterface::Synced); + QCOMPARE(account->configurationValues().value(testKey), testBoolValue); + + // ensure that configuration values from subgroups are reported correctly. + QString testGroup = QLatin1String("test-group"); + Accounts::Manager m; + Accounts::Account *a = m.account(account->identifier()); + QVERIFY(a != 0); + a->beginGroup(testGroup); + a->setValue(testKey, testStrValue); + a->endGroup(); + a->sync(); + + // account doesn't emit signals on configuration values changed... + // we really need a "refresh" function, similar to the one in Identity. + QScopedPointer<AccountInterface> existingAccount(new AccountInterface); + existingAccount->classBegin(); + existingAccount->setIdentifier(account->identifier()); + existingAccount->componentComplete(); // will load existing account + QTRY_COMPARE(existingAccount->status(), AccountInterface::Initialized); + QCOMPARE(existingAccount->configurationValues().value(QString("%1/%2").arg(testGroup).arg(testKey)), testStrValue); + + // cleanup. + account->remove(); } void tst_AccountInterface::status() --- signon/src/authsessioninterface.cpp +++ signon/src/authsessioninterface.cpp @@ -36,6 +36,7 @@ //libsignon-qt #include <SignOn/AuthSession> +#include <SignOn/SessionData> AuthSessionInterfacePrivate::AuthSessionInterfacePrivate(SignOn::AuthSession *authSession, AuthSessionInterface *parent) @@ -130,18 +131,34 @@ */ void AuthSessionInterface::process(SessionDataInterface *sessionData, const QString &mechanism) { + if (!sessionData) + return; d->session->process(sessionData->sessionData(), mechanism); } +/*! + \qmlmethod void AuthSession::process(const QVariantMap &sessionData, const QString &mechanism) + Process an authentication request contained in the \a sessionData using the given \a mechanism. + The \a sessionData should include credentials including userName, secret, etc. +*/ +void AuthSessionInterface::process(const QVariantMap &sessionData, const QString &mechanism) +{ + d->session->process(SignOn::SessionData(sessionData), mechanism); +} + // TODO: remove request() as it is mostly unused void AuthSessionInterface::request(SessionDataInterface *sessionData, const QString &mechanism) { + if (!sessionData) + return; d->session->request(sessionData->sessionData(), mechanism); } // TODO: remove challenge() as it is mostly unused void AuthSessionInterface::challenge(SessionDataInterface *sessionData, const QString &mechanism) { + if (!sessionData) + return; d->session->challenge(sessionData->sessionData(), mechanism); } --- signon/src/authsessioninterface.h +++ signon/src/authsessioninterface.h @@ -113,6 +113,7 @@ ~AuthSessionInterface(); // invokable api + Q_INVOKABLE void process(const QVariantMap &sessionData, const QString &mechanism); Q_INVOKABLE void process(SessionDataInterface *sessionData, const QString &mechanism); Q_INVOKABLE void request(SessionDataInterface *sessionData, const QString &mechanism); Q_INVOKABLE void challenge(SessionDataInterface *sessionData, const QString &mechanism); ++++++ nemo-qml-plugins.yaml --- nemo-qml-plugins.yaml +++ nemo-qml-plugins.yaml @@ -2,7 +2,7 @@ Summary: Nemo QML plugins source package. Group: System/Libraries Description: Do not install this, install the subpackaged plugins. -Version: 0.1.9 +Version: 0.1.10 Release: 1 Sources: - "%{name}-%{version}.tar.bz2"
