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/8210 Thank You, John Brooks [This message was auto-generated] --- Request # 8210: Messages from BOSS: State: review at 2013-03-01T07:40:55 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,12 @@ +* Fri Mar 01 2013 John Brooks <[email protected]> - 0.3.2 +- email: Fix bug in folderUnreadCount binding (from Valerio Valerio) +- accounts: Allow changing the identifier of an identity after initialization (from Chris Adams) +- accounts: Make Account and ServiceAccount more robust (from Chris Adams) +- accounts: Force reload of service/provider files on account model creation (from Chris Adams) +- accounts: Sign out of active authentication sessions on Identity removal (from Chris Adams) +- alarms: Set the bootFlag for alarms (from Petri M. Gerdt) +- contacts: Split search strings more intelligently (from Andrew den Exter) +- contacts: Add vCard() method to Person (from Martin Jones) +- social: Only take ownership of filters/sorters if they have no parent (from Chris Adams) +- social: Fix cached node handling (from Chris Adams) + old: ---- nemo-qml-plugins-0.3.1.tar.bz2 new: ---- nemo-qml-plugins-0.3.2.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.3.1 +Version: 0.3.2 Release: 1 Group: System/Libraries License: BSD other changes: -------------- ++++++ nemo-qml-plugins-0.3.1.tar.bz2 -> nemo-qml-plugins-0.3.2.tar.bz2 --- README +++ README @@ -7,10 +7,8 @@ the current maintainer is: Robin Burchell ([email protected]) current plugins: - - thumbnailer: offers an image provider for fast thumbnailing/sharing of - thumbnail cache between multiple applications - - to use, access image://nemoThumbnail/path/to/file.jpg in an Image element. +- thumbnailer: offers an image provider for fast thumbnailing/sharing of + thumbnail cache between multiple applications - contacts: offers an API similar to QtMobility.contacts, except without the fail @@ -24,4 +22,7 @@ - email: offers a QML-friendly email API using Qt Messaging Framework as a backend. - time: provides WallClock element useful for clock displays, including keeping - time in sync with system time changes. +time in sync with system time changes. + +- alarms: provides QML APIs for creating, editing, and fetching information + about alarms --- accounts/src/account-model.cpp +++ accounts/src/account-model.cpp @@ -71,6 +71,7 @@ { Q_D(AccountModel); d->manager = new Accounts::Manager(); + Accounts::ServiceList allServices = d->manager->serviceList(); // force reload of service files. d->headerData.insert(AccountIdRole, "accountId"); d->headerData.insert(AccountDisplayNameRole, "accountDisplayName"); d->headerData.insert(AccountIconRole, "accountIcon" ); --- accounts/src/account-provider-model.cpp +++ accounts/src/account-provider-model.cpp @@ -77,6 +77,7 @@ setRoleNames(d->headerData); Accounts::Manager m; + Accounts::ServiceList allServices = m.serviceList(); // force reload of service files. Accounts::ProviderList providers = m.providerList(); for (int i = 0; i < providers.size(); i++) --- accounts/src/accountinterface.cpp +++ accounts/src/accountinterface.cpp @@ -289,6 +289,11 @@ void AccountInterfacePrivate::handleSynced() { if (status == AccountInterface::SyncInProgress) { + if (!account) { + qWarning() << Q_FUNC_INFO << "Account not valid"; + return; + } + // check to see if the id was updated int newIdentifier = account->id(); if (identifier != newIdentifier) { --- accounts/src/service-account-model.cpp +++ accounts/src/service-account-model.cpp @@ -75,6 +75,7 @@ { Q_D(ServiceAccountModel); d->manager = new Accounts::Manager(); + Accounts::ServiceList allServices = d->manager->serviceList(); // force reload of service files. d->headerData.insert(AccountIdRole, "accountId"); d->headerData.insert(AccountDisplayNameRole, "accountDisplayName"); d->headerData.insert(AccountIconRole, "accountIcon" ); --- accounts/src/serviceaccountinterface.cpp +++ accounts/src/serviceaccountinterface.cpp @@ -113,17 +113,21 @@ bool ServiceAccountInterface::enabled() const { - return d->serviceAccount->enabled(); + if (d->serviceAccount) + return d->serviceAccount->enabled(); + return false; } /*! \qmlproperty int ServiceAccount::identifier - Returns the identifier of the account + Returns the identifier of the account, or zero if not initialized */ int ServiceAccountInterface::identifier() const { - return d->serviceAccount->account()->id(); + if (d->serviceAccount && d->serviceAccount->account()) + return d->serviceAccount->account()->id(); + return 0; } /*! @@ -171,8 +175,12 @@ return; } - d->serviceAccount->setValue(key, value); - d->serviceAccount->account()->sync(); + if (d->serviceAccount) { + d->serviceAccount->setValue(key, value); + if (d->serviceAccount->account()) { + d->serviceAccount->account()->sync(); + } + } } /*! @@ -184,8 +192,12 @@ */ void ServiceAccountInterface::removeConfigurationValue(const QString &key) { - d->serviceAccount->remove(key); - d->serviceAccount->account()->sync(); + if (d->serviceAccount) { + d->serviceAccount->remove(key); + if (d->serviceAccount->account()) { + d->serviceAccount->account()->sync(); + } + } } /*! @@ -217,6 +229,8 @@ // doesn't emit a changed() signal the way Accounts::AccountService does. // Since we can't notify on changes, let's not pretend it's a property. Accounts::Account *acct = d->serviceAccount->account(); + if (!acct) + return QVariantMap(); Accounts::Service srv = acct->selectedService(); acct->selectService(Accounts::Service()); QStringList allKeys = acct->allKeys(); --- alarms/src/alarmobject.cpp +++ alarms/src/alarmobject.cpp @@ -137,6 +137,7 @@ ev.setAlarmFlag(); // Trigger the voland alarm/reminder dialog ev.setReminderFlag(); + ev.setBootFlag(); ev.setAttribute(QLatin1String("APPLICATION"), QLatin1String("nemoalarms")); ev.setAttribute(QLatin1String("createdDate"), m_createdDate.toString()); --- contacts/src/seasidefilteredmodel.cpp +++ contacts/src/seasidefilteredmodel.cpp @@ -40,6 +40,7 @@ #include <QContactOnlineAccount> #include <QContactOrganization> #include <QContactPhoneNumber> +#include <QTextBoundaryFinder> #include <QtDebug> @@ -52,6 +53,29 @@ destination->insert(to + i, source.at(i)); } +// Splits a string at word boundaries identified by QTextBoundaryFinder and returns a list of +// of the fragments that occur between StartWord and EndWord boundaries. +static QStringList splitWords(const QString &string) +{ + QStringList words; + QTextBoundaryFinder finder(QTextBoundaryFinder::Word, string); + + for (int start = 0; finder.position() != -1 && finder.position() < string.length();) { + if (!(finder.boundaryReasons() & QTextBoundaryFinder::StartWord)) { + finder.toNextBoundary(); + start = finder.position(); + } + + finder.toNextBoundary(); + + if (finder.position() > start && finder.boundaryReasons() & QTextBoundaryFinder::EndWord) { + words.append(string.mid(start, finder.position() - start)); + start = finder.position(); + } + } + return words; +} + SeasideFilteredModel::SeasideFilteredModel(QObject *parent) : QAbstractListModel(parent) , m_filterIndex(0) @@ -149,7 +173,7 @@ const bool subFilter = !wasEmpty && pattern.startsWith(m_filterPattern, Qt::CaseInsensitive); m_filterPattern = pattern; - m_filterParts = m_filterPattern.split(QLatin1Char(' ')); + m_filterParts = splitWords(m_filterPattern); m_referenceIndex = 0; m_filterIndex = 0; @@ -206,17 +230,17 @@ // other locales, see MBreakIterator if (item->filterKey.isEmpty()) { - item->filterKey = item->contact.detail<QContactName>().customLabel().split(QLatin1Char(' ')); + item->filterKey = splitWords(item->contact.detail<QContactName>().customLabel()); foreach (const QContactPhoneNumber &detail, item->contact.details<QContactPhoneNumber>()) - item->filterKey.append(detail.number()); + item->filterKey.append(splitWords(detail.number())); foreach (const QContactEmailAddress &detail, item->contact.details<QContactEmailAddress>()) - item->filterKey.append(detail.emailAddress()); + item->filterKey.append(splitWords(detail.emailAddress())); foreach (const QContactOrganization &detail, item->contact.details<QContactOrganization>()) - item->filterKey.append(detail.name().split(QLatin1Char(' '))); + item->filterKey.append(splitWords(detail.name())); foreach (const QContactOnlineAccount &detail, item->contact.details<QContactOnlineAccount>()) { - item->filterKey.append(detail.accountUri()); - item->filterKey.append(detail.serviceProvider()); + item->filterKey.append(splitWords(detail.accountUri())); + item->filterKey.append(splitWords(detail.serviceProvider())); } } @@ -317,7 +341,6 @@ if (filterId(m_referenceContactIds->at(i))) m_filteredContactIds.append(m_referenceContactIds->at(i)); } - if (!m_filteredContactIds.isEmpty()) beginInsertRows(QModelIndex(), 0, m_filteredContactIds.count() - 1); --- contacts/src/seasideperson.cpp +++ contacts/src/seasideperson.cpp @@ -44,6 +44,9 @@ #include <QContactOrganization> #include <QContactUrl> +#include <QVersitWriter> +#include <QVersitContactExporter> + #include "seasideperson.h" SeasidePerson::SeasidePerson(QObject *parent) @@ -772,3 +775,21 @@ recalculateDisplayLabel(); } +QString SeasidePerson::vCard() const +{ + QVersitContactExporter exporter; + if (!exporter.exportContacts(QList<QContact>() << mContact, QVersitDocument::VCard21Type)) { + qWarning() << Q_FUNC_INFO << "Failed to create vCard: " << exporter.errorMap(); + return QString(); + } + + QByteArray vcard; + QVersitWriter writer(&vcard); + if (!writer.startWriting(exporter.documents())) { + qWarning() << Q_FUNC_INFO << "Can't start writing vcard " << writer.error(); + return QString(); + } + writer.waitForFinished(); + + return QString::fromUtf8(vcard); +} --- contacts/src/seasideperson.h +++ contacts/src/seasideperson.h @@ -210,6 +210,8 @@ Q_INVOKABLE QVariant contactData() const { return QVariant::fromValue(contact()); } Q_INVOKABLE void setContactData(const QVariant &data) { setContact(data.value<QContact>()); } + Q_INVOKABLE QString vCard() const; + static QString generateDisplayLabel( const QContact &mContact, SeasideProxyModel::DisplayLabelOrder order = SeasideProxyModel::FirstNameFirst); --- contacts/tests/tst_seasidefilteredmodel/seasidecache.cpp +++ contacts/tests/tst_seasidefilteredmodel/seasidecache.cpp @@ -34,6 +34,7 @@ #include <QContactName> #include <QContactAvatar> +#include <QContactEmailAddress> #include <QtDebug> @@ -42,19 +43,21 @@ const char *firstName; const char *lastName; const char *fullName; - const char *avatar; const bool isFavorite; + const char *email; + const char *avatar; + }; static const Contact contactsData[] = { - { "Aaron", "Aaronson", "Aaron Aaronson", 0, false }, // 0 - { "Aaron", "Arthur", "Aaron Arthur", 0, false }, // 1 - { "Aaron", "Johns", "Aaron Johns", 0, true }, // 2 - { "Arthur", "Johns", "Arthur Johns", 0, false }, // 3 - { "Jason", "Aaronson", "Jason Aaronson", 0, false }, // 4 - { "Joe", "Johns", "Joe Johns", "file:///cache/joe.jpg", true }, // 5 - { "Robin", "Burchell", "Robin Burchell", 0, true } // 6 +/*0*/ { "Aaron", "Aaronson", "Aaron Aaronson", false, "[email protected]", 0 }, +/*1*/ { "Aaron", "Arthur", "Aaron Arthur", false, "[email protected]", 0 }, +/*2*/ { "Aaron", "Johns", "Aaron Johns", true, "[email protected]", 0 }, +/*3*/ { "Arthur", "Johns", "Arthur Johns", false, "[email protected]", 0 }, +/*4*/ { "Jason", "Aaronson", "Jason Aaronson", false, "[email protected]", 0 }, +/*5*/ { "Joe", "Johns", "Joe Johns", true, "[email protected]", "file:///cache/joe.jpg" }, +/*6*/ { "Robin", "Burchell", "Robin Burchell", true, 0, 0 } }; SeasideCache *SeasideCache::instance = 0; @@ -96,6 +99,13 @@ avatar.setImageUrl(QUrl(QLatin1String(contactsData[i].avatar))); contact.saveDetail(&avatar); } + + if (contactsData[i].email) { + QContactEmailAddress email; + email.setEmailAddress(QLatin1String(contactsData[i].email)); + contact.saveDetail(&email); + } + m_cache.append(SeasideCacheItem(contact)); } } --- contacts/tests/tst_seasidefilteredmodel/tst_seasidefilteredmodel.cpp +++ contacts/tests/tst_seasidefilteredmodel/tst_seasidefilteredmodel.cpp @@ -48,6 +48,7 @@ void populated(); void filterType(); void filterPattern(); + void filterEmail(); void rowsInserted(); void rowsRemoved(); void dataChanged(); @@ -212,7 +213,7 @@ patternSpy.clear(); removedSpy.clear(); - // + // empty model.setFilterType(SeasideFilteredModel::FilterFavorites); QCOMPARE(model.filterPattern(), QString("aaronso")); QCOMPARE(model.rowCount(), 0); @@ -296,6 +297,7 @@ insertedSpy.clear(); removedSpy.clear(); + // empty model.setFilterPattern(QString()); QCOMPARE(model.filterPattern(), QString()); QCOMPARE(model.rowCount(), 0); @@ -303,6 +305,81 @@ QCOMPARE(insertedSpy.count(), 0); QCOMPARE(removedSpy.count(), 1); QCOMPARE(removedSpy.at(0).at(1).value<int>(), 0); QCOMPARE(removedSpy.at(0).at(2).value<int>(), 3); + + patternSpy.clear(); + removedSpy.clear(); +} + +void tst_SeasideFilteredModel::filterEmail() +{ + cache.insert(SeasideFilteredModel::FilterAll, 0, QVector<QContactLocalId>() + << 0 << 1 << 2 << 3 << 4 << 5); + cache.insert(SeasideFilteredModel::FilterFavorites, 0, QVector<QContactLocalId>() + << 2 << 5); + + SeasideFilteredModel model; + QSignalSpy insertedSpy(&model, SIGNAL(rowsInserted(QModelIndex,int,int))); + QSignalSpy removedSpy(&model, SIGNAL(rowsRemoved(QModelIndex,int,int))); + + QCOMPARE(model.filterType(), SeasideFilteredModel::FilterAll); + QCOMPARE(model.filterPattern(), QString()); + QCOMPARE(model.rowCount(), 6); + + // 0 1 2 3 4 5 + model.setFilterPattern("example"); + QCOMPARE(model.rowCount(), 6); + QCOMPARE(insertedSpy.count(), 0); + QCOMPARE(removedSpy.count(), 0); + + // 0 1 2 3 4 5 + model.setFilterPattern("example."); + QCOMPARE(model.rowCount(), 6); + QCOMPARE(insertedSpy.count(), 0); + QCOMPARE(removedSpy.count(), 0); + + // 0 1 2 + model.setFilterPattern("example.com"); + QCOMPARE(model.rowCount(), 3); + QCOMPARE(insertedSpy.count(), 0); + QCOMPARE(removedSpy.count(), 1); + QCOMPARE(removedSpy.at(0).at(1).value<int>(), 3); QCOMPARE(removedSpy.at(0).at(2).value<int>(), 5); + + removedSpy.clear(); + + // 3 4 5 + model.setFilterPattern("example.org"); + QCOMPARE(model.rowCount(), 3); + QCOMPARE(insertedSpy.count(), 1); + QCOMPARE(insertedSpy.at(0).at(1).value<int>(), 0); QCOMPARE(insertedSpy.at(0).at(2).value<int>(), 2); + QCOMPARE(removedSpy.count(), 1); + QCOMPARE(removedSpy.at(0).at(1).value<int>(), 0); QCOMPARE(removedSpy.at(0).at(2).value<int>(), 2); + + insertedSpy.clear(); + removedSpy.clear(); + + // 4 5 + model.setFilterPattern("examplez.org"); + QCOMPARE(model.rowCount(), 2); + QCOMPARE(insertedSpy.count(), 0); + QCOMPARE(removedSpy.count(), 1); + QCOMPARE(removedSpy.at(0).at(1).value<int>(), 0); QCOMPARE(removedSpy.at(0).at(2).value<int>(), 0); + + removedSpy.clear(); + + // 4 + model.setFilterPattern("[email protected]"); + QCOMPARE(model.rowCount(), 1); + QCOMPARE(insertedSpy.count(), 0); + QCOMPARE(removedSpy.count(), 1); + QCOMPARE(removedSpy.at(0).at(1).value<int>(), 1); QCOMPARE(removedSpy.at(0).at(2).value<int>(), 1); + + removedSpy.clear(); + + // 4 + model.setFilterPattern("jay"); + QCOMPARE(model.rowCount(), 1); + QCOMPARE(insertedSpy.count(), 0); + QCOMPARE(removedSpy.count(), 0); } void tst_SeasideFilteredModel::rowsInserted() --- contacts/tests/tst_seasidefilteredmodel/tst_seasidefilteredmodel.pro +++ contacts/tests/tst_seasidefilteredmodel/tst_seasidefilteredmodel.pro @@ -5,7 +5,7 @@ SRCDIR = $$PWD/../../src CONFIG += mobility -MOBILITY += contacts +MOBILITY += contacts versit QT += declarative --- contacts/tests/tst_seasideperson/tst_seasideperson.cpp +++ contacts/tests/tst_seasideperson/tst_seasideperson.cpp @@ -76,6 +76,7 @@ void presenceState(); void marshalling(); void setContact(); + void vcard(); }; void tst_SeasidePerson::firstName() @@ -485,6 +486,20 @@ } } +void tst_SeasidePerson::vcard() +{ + QScopedPointer<SeasidePerson> person(new SeasidePerson); + person->setFirstName("Star"); + person->setLastName("Fish"); + person->setPhoneNumbers(QStringList() << "12345678"); + + QString vcard = person->vCard(); + + QVERIFY(vcard.indexOf("N:Fish;Star;") > 0); + QVERIFY(vcard.indexOf("FN:Star Fish") > 0); + QVERIFY(vcard.indexOf("TEL:12345678") > 0); +} + // TODO: // - account URIs/paths (or let contactsd do that?) --- email/src/folderlistmodel.cpp +++ email/src/folderlistmodel.cpp @@ -161,5 +161,9 @@ QVariant FolderListModel::folderUnreadCount(QVariant folderId) { int folderIndex = indexFromFolderId(folderId); + + if (folderIndex < 0) + return 0; + return data(index(folderIndex), FolderUnreadCount); } --- signon/src/identityinterface.cpp +++ signon/src/identityinterface.cpp @@ -77,6 +77,18 @@ identity = ident; identifier = identity->id(); + if (status != IdentityInterface::Initializing) { + status = IdentityInterface::Initializing; + bool needsEmit = false; + if (!statusMessage.isEmpty()) { + statusMessage = QString(); + needsEmit = true; + } + emit q->statusChanged(); + if (needsEmit) { + emit q->statusMessageChanged(); + } + } connect(identity, SIGNAL(info(SignOn::IdentityInfo)), this, SLOT(handleInfo(SignOn::IdentityInfo))); connect(identity, SIGNAL(removed()), this, SLOT(handleRemoved())); @@ -439,6 +451,7 @@ IdentityInterface::~IdentityInterface() { + signOut(); // sign out of the currently active authsession } // QDeclarativeParserStatus @@ -474,9 +487,6 @@ If the \c identifier is set, the identity in the database with the specified identifier will be loaded. - - You cannot change identifier of the Identity after initialization, - unless you initialize the identifierPending property to true. */ int IdentityInterface::identifier() const @@ -496,15 +506,29 @@ emit identifierChanged(); componentComplete(); // now initialize. } + } else if (id != 0 && id != d->identifier) { + // changing identifier after initialisation. + if (d->identity && d->identity->parent() == this) { + disconnect(d->identity); + d->identity->deleteLater(); + } + + d->identity = 0; + d->identifier = id; + componentComplete(); } } /*! \qmlproperty int Identity::identifierPending + This property should be set to true if the Identity is an existing identity, but the identifier is not available at initialization time. When this property is set, the Identity will remain in the Initializing state until the identifier is set. + + If this property is not set, and no identifier is set for the Identity, + a new signon identity will be instantiated for the Identity. */ bool IdentityInterface::identifierPending() const { @@ -904,6 +928,13 @@ if (!d->identity) return; + // since we are removing the SignOn::Identity itself, we not only sign + // out of the active auth session via IdentityInterface::signOut() but + // we actually sign out of all active auth sessions via + // SignOn::Identity::signOut(). + d->identity->signOut(); + + // remove the identity d->setStatus(IdentityInterface::SyncInProgress); d->identity->remove(); d->identity = 0; // seems like libsignon-qt doesn't propagate the signal correctly. assume it worked... --- signon/src/identityinterface_p.h +++ signon/src/identityinterface_p.h @@ -40,6 +40,8 @@ #include <SignOn/Identity> #include <SignOn/identityinfo.h> +using namespace SignOn; + class IdentityInterfacePrivate : public QObject { Q_OBJECT @@ -98,7 +100,7 @@ // auth session related void handleResponse(const SignOn::SessionData &sd); - void handleStateChanged(SignOn::AuthSession::AuthSessionState newState, const QString &message); + void handleStateChanged(AuthSession::AuthSessionState newState, const QString &message); public: void setUpSessionSignals(); --- signon/src/serviceaccountidentityinterface.cpp +++ signon/src/serviceaccountidentityinterface.cpp @@ -267,6 +267,7 @@ ServiceAccountIdentityInterface::~ServiceAccountIdentityInterface() { + signOut(); } /*! @@ -477,20 +478,25 @@ The ServiceAccountIdentity will have a status of Invalid until its identifier is set to the identifier of a valid Identity in the database. - - Once set, you may not change the identifier. */ void ServiceAccountIdentityInterface::setIdentifier(int identityId) { // XXX TODO: make this more robust using QDeclarativeParserStatus / Initializing. - if (identityId == 0) + if (identityId == 0 || identityId == identifier()) return; if (!d->startedInitialization) { d->startedInitialization = true; SignOn::Identity *ident = SignOn::Identity::existingIdentity(identityId, d); d->setIdentity(ident, true, true); + emit identifierChanged(); + } else { + d->deleteLater(); + d = new ServiceAccountIdentityInterfacePrivate(0, this); + d->startedInitialization = true; + SignOn::Identity *ident = SignOn::Identity::existingIdentity(identityId, d); + d->setIdentity(ident, true, true); emit identifierChanged(); } } --- signon/tests/tst_identityinterface/tst_identityinterface.cpp +++ signon/tests/tst_identityinterface/tst_identityinterface.cpp @@ -108,8 +108,35 @@ QTRY_COMPARE(spy.count(), 1); QVERIFY(identity->identifier() > 0); + QScopedPointer<IdentityInterface> identityTwo(new IdentityInterface); + identityTwo->classBegin(); + identityTwo->componentComplete(); + QTRY_COMPARE(identityTwo->status(), IdentityInterface::Initialized); + QCOMPARE(identityTwo->identifier(), 0); + QSignalSpy spyTwo(identityTwo.data(), SIGNAL(identifierChanged())); + identityTwo->setUserName(QString(QLatin1String("test-username-two"))); + identityTwo->setSecret(QString(QLatin1String("test-secret"))); + identityTwo->setCaption(QString(QLatin1String("test-caption"))); + identityTwo->setMethodMechanisms(QString(QLatin1String("password")), QStringList() << QString(QLatin1String("ClientLogin"))); + identityTwo->sync(); + QTRY_COMPARE(spyTwo.count(), 1); + QVERIFY(identityTwo->identifier() > 0); + + // this one doesn't create a new identity, but references an existing identity + QScopedPointer<IdentityInterface> identityThree(new IdentityInterface); + identityThree->classBegin(); + identityThree->setIdentifier(identity->identifier()); + identityThree->componentComplete(); + QTRY_COMPARE(identityThree->status(), IdentityInterface::Initialized); + QCOMPARE(identityThree->userName(), QLatin1String("test-username")); + identityThree->setIdentifier(identityTwo->identifier()); // test that you can set it after initialization. + QCOMPARE(identityThree->status(), IdentityInterface::Initializing); + QTRY_COMPARE(identityThree->status(), IdentityInterface::Synced); + QCOMPARE(identityThree->userName(), QLatin1String("test-username-two")); + // cleanup identity->remove(); + identityTwo->remove(); } void tst_IdentityInterface::identifierPending() @@ -307,22 +334,54 @@ void tst_IdentityInterface::signInOut() { - // first, create a new Identity so that we can use it for authentication. + // test that signIn / signOut work, and that remove() signs out if necessary + { + // first, create a new Identity so that we can use it for authentication. + QScopedPointer<IdentityInterface> identity(new IdentityInterface); + identity->classBegin(); + identity->componentComplete(); + QTRY_COMPARE(identity->status(), IdentityInterface::Initialized); + identity->setUserName(QString(QLatin1String("test-username"))); + identity->setMethodMechanisms(QString(QLatin1String("password")), QStringList() << QString(QLatin1String("ClientLogin"))); + identity->sync(); // begin sync. + QTRY_COMPARE(identity->status(), IdentityInterface::Synced); + QVERIFY(identity->signIn("password", "ClientLogin", QVariantMap())); // start new auth session + // not expecting a response, as the client login signon plugin may not exist. + QVERIFY(!identity->signIn("password", "ClientLogin", QVariantMap())); // already in progress + identity->process(QVariantMap()); // shouldn't crash + identity->signOut(); // shouldn't crash + QVERIFY(identity->signIn("password", "ClientLogin", QVariantMap())); // start new auth session + QSignalSpy spy(identity.data(), SIGNAL(statusChanged())); + identity->remove(); // remove should also signOut. + QCOMPARE(spy.count(), 3); // (some signIn() state) -> NotStarted -> SyncInProgress -> Invalid + } + + // test that dtor signs out if necessary + int identityToRemove = 0; + { + // first, create a new Identity so that we can use it for authentication. + IdentityInterface *identity = new IdentityInterface; + identity->classBegin(); + identity->componentComplete(); + QTRY_COMPARE(identity->status(), IdentityInterface::Initialized); + identity->setUserName(QString(QLatin1String("test-username"))); + identity->setMethodMechanisms(QString(QLatin1String("password")), QStringList() << QString(QLatin1String("ClientLogin"))); + identity->sync(); // begin sync. + QTRY_COMPARE(identity->status(), IdentityInterface::Synced); + identityToRemove = identity->identifier(); + QVERIFY(identity->signIn("password", "ClientLogin", QVariantMap())); // start new auth session + // not expecting a response, as the client login signon plugin may not exist. + QSignalSpy spy(identity, SIGNAL(statusChanged())); + delete identity; // delete should also sign out + QCOMPARE(spy.count(), 1); // (some signIn() state) -> NotStarted + } + + // clean up QScopedPointer<IdentityInterface> identity(new IdentityInterface); identity->classBegin(); + identity->setIdentifier(identityToRemove); identity->componentComplete(); - QTRY_COMPARE(identity->status(), IdentityInterface::Initialized); - identity->setUserName(QString(QLatin1String("test-username"))); - identity->setMethodMechanisms(QString(QLatin1String("password")), QStringList() << QString(QLatin1String("ClientLogin"))); - identity->sync(); // begin sync. - QTRY_COMPARE(identity->status(), IdentityInterface::Synced); - QVERIFY(identity->signIn("password", "ClientLogin", QVariantMap())); // start new auth session - // not expecting a response, as the client login signon plugin may not exist. - QVERIFY(!identity->signIn("password", "ClientLogin", QVariantMap())); // already in progress - identity->process(QVariantMap()); // shouldn't crash - identity->signOut(); // shouldn't crash - - // cleanup + QTRY_VERIFY(identity->status() == IdentityInterface::Initialized || identity->status() == IdentityInterface::Synced); identity->remove(); } --- signon/tests/tst_serviceaccountidentityinterface/tst_serviceaccountidentityinterface.cpp +++ signon/tests/tst_serviceaccountidentityinterface/tst_serviceaccountidentityinterface.cpp @@ -101,19 +101,33 @@ int identityIdentifier = identity->identifier(); QVERIFY(identityIdentifier > 0); // ensure save succeeded. + // second, create another new Identity so that we can try post-initialization identifier change + QScopedPointer<IdentityInterface> identityTwo(new IdentityInterface); + identityTwo->classBegin(); + identityTwo->componentComplete(); + QTRY_COMPARE(identityTwo->status(), IdentityInterface::Initialized); + identityTwo->setUserName(QString(QLatin1String("test-username-two"))); + identityTwo->sync(); // begin sync. + QTRY_COMPARE(identityTwo->status(), IdentityInterface::Synced); + int identityIdentifierTwo = identityTwo->identifier(); + QVERIFY(identityIdentifierTwo > 0); // ensure save succeeded. + QScopedPointer<ServiceAccountIdentityInterface> saidentity(new ServiceAccountIdentityInterface); QCOMPARE(saidentity->identifier(), 0); QSignalSpy spy(saidentity.data(), SIGNAL(identifierChanged())); saidentity->setIdentifier(identityIdentifier); QTRY_COMPARE(spy.count(), 1); QCOMPARE(saidentity->identifier(), identityIdentifier); - saidentity->setIdentifier(identityIdentifier + 1); - QTest::qWait(500); - QCOMPARE(saidentity->identifier(), identityIdentifier); // once set, cannot be set to anything else. - QCOMPARE(spy.count(), 1); + saidentity->setIdentifier(identityIdentifier + 999999); // some invalid identifier + QCOMPARE(saidentity->status(), ServiceAccountIdentityInterface::Initializing); + QTRY_COMPARE(saidentity->status(), ServiceAccountIdentityInterface::Error); // invalid identifier + saidentity->setIdentifier(identityIdentifierTwo); + QCOMPARE(saidentity->status(), ServiceAccountIdentityInterface::Initializing); + QTRY_COMPARE(saidentity->status(), ServiceAccountIdentityInterface::Initialized); // cleanup identity->remove(); + identityTwo->remove(); } void tst_ServiceAccountIdentityInterface::status() @@ -202,13 +216,16 @@ QTRY_COMPARE(identity->status(), IdentityInterface::Synced); int identityIdentifier = identity->identifier(); - QScopedPointer<ServiceAccountIdentityInterface> saidentity(new ServiceAccountIdentityInterface); + ServiceAccountIdentityInterface *saidentity = new ServiceAccountIdentityInterface; saidentity->setIdentifier(identityIdentifier); QTRY_COMPARE(saidentity->status(), ServiceAccountIdentityInterface::Initialized); QCOMPARE(saidentity->methods(), QStringList() << QString(QLatin1String("password"))); QCOMPARE(saidentity->methodMechanisms("password"), QStringList() << QString(QLatin1String("ClientLogin"))); QVERIFY(saidentity->signIn("password", "ClientLogin", QVariantMap())); // not expecting a response, as the client login signon plugin may not exist. + QSignalSpy spy(saidentity, SIGNAL(statusChanged())); + delete saidentity; // delete should also sign out + QCOMPARE(spy.count(), 1); // (some signIn() state) -> NotStarted // cleanup identity->remove(); --- social/src/facebook/facebookinterface.cpp +++ social/src/facebook/facebookinterface.cpp @@ -563,8 +563,20 @@ { qWarning() << Q_FUNC_INFO << "filtering/sorting not implemented. TODO!"; - // XXX TODO: filter the data + // XXX TODO: filter the data in a better manner than linear searches... QList<CacheEntry*> filteredData = data; + for (int i = 0; i < d->filters.size(); ++i) { + QList<CacheEntry*> temp = filteredData; + filteredData.clear(); + FilterInterface *currFilter = d->filters.at(i); + for (int j = 0; j < data.size(); ++j) { + CacheEntry *currEntry = data.at(j); + if ((!currEntry->item && currFilter->matches(currEntry->data)) + || (currEntry->item && currFilter->matches(currEntry->item))) { + filteredData.append(currEntry); + } + } + } // XXX TODO: sort the filtered data QList<CacheEntry*> sortedData = filteredData; --- social/src/filterinterface.h +++ social/src/filterinterface.h @@ -35,6 +35,7 @@ #include <QtCore/QObject> #include <QtCore/QVariantMap> +class SocialNetworkInterfacePrivate; class ContentItemInterface; class FilterInterface : public QObject @@ -46,6 +47,10 @@ virtual ~FilterInterface(); virtual Q_INVOKABLE bool matches(ContentItemInterface *content) const; virtual Q_INVOKABLE bool matches(const QVariantMap &contentData) const; + +private: + bool m_ownedBySni; + friend class SocialNetworkInterfacePrivate; }; #endif // FILTERINTERFACE_H --- social/src/filterinterfaces.cpp +++ social/src/filterinterfaces.cpp @@ -38,7 +38,7 @@ // ------------------------------ FilterInterface -FilterInterface::FilterInterface(QObject *parent) : QObject(parent) +FilterInterface::FilterInterface(QObject *parent) : QObject(parent), m_ownedBySni(false) { } --- social/src/socialnetworkinterface.cpp +++ social/src/socialnetworkinterface.cpp @@ -227,8 +227,11 @@ void SocialNetworkInterfacePrivate::filters_append(QDeclarativeListProperty<FilterInterface> *list, FilterInterface *filter) { SocialNetworkInterface *sni = qobject_cast<SocialNetworkInterface *>(list->object); - if (sni) { - filter->setParent(sni); + if (sni && filter) { + if (!filter->parent()) { + filter->setParent(sni); + filter->m_ownedBySni = true; + } sni->d->filters.append(filter); } } @@ -247,8 +250,11 @@ { SocialNetworkInterface *sni = qobject_cast<SocialNetworkInterface *>(list->object); if (sni) { - foreach (FilterInterface *cf, sni->d->filters) - cf->deleteLater(); + foreach (FilterInterface *cf, sni->d->filters) { + if (cf->m_ownedBySni) { + cf->deleteLater(); + } + } sni->d->filters.clear(); } } @@ -266,8 +272,11 @@ void SocialNetworkInterfacePrivate::sorters_append(QDeclarativeListProperty<SorterInterface> *list, SorterInterface *sorter) { SocialNetworkInterface *sni = qobject_cast<SocialNetworkInterface *>(list->object); - if (sni) { - sorter->setParent(sni); + if (sni && sorter) { + if (!sorter->parent()) { + sorter->setParent(sni); + sorter->m_ownedBySni = true; + } sni->d->sorters.append(sorter); } } @@ -286,8 +295,11 @@ { SocialNetworkInterface *sni = qobject_cast<SocialNetworkInterface *>(list->object); if (sni) { - foreach (SorterInterface *cs, sni->d->sorters) - cs->deleteLater(); + foreach (SorterInterface *cs, sni->d->sorters) { + if (cs->m_ownedBySni) { + cs->deleteLater(); + } + } sni->d->sorters.clear(); } } @@ -910,6 +922,7 @@ // call derived class data update: // perform filtering/sorting based on the defined stuff. // and then emit dataChanged() etc signals. + d->pushNode(cachedNode); updateInternalData(data); } else { qWarning() << Q_FUNC_INFO << "Error: cached node has no cached content!"; --- social/src/sorterinterface.h +++ social/src/sorterinterface.h @@ -34,6 +34,7 @@ #include <QtCore/QObject> +class SocialNetworkInterfacePrivate; class ContentItemInterface; class SorterInterface : public QObject @@ -44,6 +45,10 @@ SorterInterface(QObject *parent = 0); virtual ~SorterInterface(); virtual Q_INVOKABLE bool firstLessThanSecond(ContentItemInterface *first, ContentItemInterface *second) const; + +private: + bool m_ownedBySni; + friend class SocialNetworkInterfacePrivate; }; #endif // SORTERINTERFACE_H --- social/src/sorterinterfaces.cpp +++ social/src/sorterinterfaces.cpp @@ -36,7 +36,7 @@ // ------------------------------ SorterInterface SorterInterface::SorterInterface(QObject *parent) - : QObject(parent) + : QObject(parent), m_ownedBySni(false) { } ++++++ 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.3.1 +Version: 0.3.2 Release: 1 Sources: - "%{name}-%{version}.tar.bz2"
