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/8139

Thank You,
John Brooks

[This message was auto-generated]

---

Request # 8139:

Messages from BOSS:

State: review at 2013-02-20T10:55:27 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,9 @@
+* Wed Feb 20 2013 John Brooks <[email protected]> - 0.3.1
+- time: Ensure time is updated when WallClock is enabled (from Martin Jones)
+- email: Add folderUnreadCount binding (from Valerio Valerio)
+- contacts: Expose contact presence state (from Matt Vogt)
+- contacts: Check for valid ID before returning a person instance (from Matt 
Vogt)
+- contacts: Match filter against more properties than display name (from 
Adnrew den Exter)
+- contacts: Update the SeasidePerson test to allow for caching of display 
label (from Andrew den Exter)
+- social: Support request paging in Facebook adapter
+

old:
----
  nemo-qml-plugins-0.3.0.tar.bz2

new:
----
  nemo-qml-plugins-0.3.1.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.0
+Version:    0.3.1
 Release:    1
 Group:      System/Libraries
 License:    BSD

other changes:
--------------

++++++ nemo-qml-plugins-0.3.0.tar.bz2 -> nemo-qml-plugins-0.3.1.tar.bz2
--- contacts/src/seasidecache.cpp
+++ contacts/src/seasidecache.cpp
@@ -42,8 +42,11 @@
 
 #include <QContactAvatar>
 #include <QContactDetailFilter>
+#include <QContactEmailAddress>
 #include <QContactFavorite>
 #include <QContactName>
+#include <QContactOnlineAccount>
+#include <QContactOrganization>
 #include <QContactPhoneNumber>
 
 #include <QVersitContactExporter>
@@ -131,7 +134,10 @@
     fetchHint.setDetailDefinitionsHint(QStringList()
             << QContactName::DefinitionName
             << QContactAvatar::DefinitionName
-            << QContactPhoneNumber::DefinitionName);
+            << QContactPhoneNumber::DefinitionName
+            << QContactEmailAddress::DefinitionName
+            << QContactOrganization::DefinitionName
+            << QContactOnlineAccount::DefinitionName);
 
     m_fetchRequest.setFetchHint(fetchHint);
     m_fetchRequest.setFilter(QContactFavorite::match());
@@ -199,6 +205,9 @@
 
 SeasidePerson *SeasideCache::personById(QContactLocalId id)
 {
+    if (id == 0)
+        return 0;
+
     QHash<QContactLocalId, SeasideCacheItem>::iterator it = 
instance->m_people.find(id);
     if (it != instance->m_people.end()) {
         return person(&(*it));
--- contacts/src/seasidefilteredmodel.cpp
+++ contacts/src/seasidefilteredmodel.cpp
@@ -35,7 +35,11 @@
 #include "synchronizelists_p.h"
 
 #include <QContactAvatar>
+#include <QContactEmailAddress>
 #include <QContactName>
+#include <QContactOnlineAccount>
+#include <QContactOrganization>
+#include <QContactPhoneNumber>
 
 #include <QtDebug>
 
@@ -201,9 +205,21 @@
     // TODO: i18n will require different splitting for thai and possibly
     // other locales, see MBreakIterator
 
-    if (item->filterKey.isEmpty())
+    if (item->filterKey.isEmpty()) {
         item->filterKey = 
item->contact.detail<QContactName>().customLabel().split(QLatin1Char(' '));
 
+        foreach (const QContactPhoneNumber &detail, 
item->contact.details<QContactPhoneNumber>())
+            item->filterKey.append(detail.number());
+        foreach (const QContactEmailAddress &detail, 
item->contact.details<QContactEmailAddress>())
+            item->filterKey.append(detail.emailAddress());
+        foreach (const QContactOrganization &detail, 
item->contact.details<QContactOrganization>())
+            item->filterKey.append(detail.name().split(QLatin1Char(' ')));
+        foreach (const QContactOnlineAccount &detail, 
item->contact.details<QContactOnlineAccount>()) {
+            item->filterKey.append(detail.accountUri());
+            item->filterKey.append(detail.serviceProvider());
+        }
+    }
+
     // search forwards over the label components for each filter word, making
     // sure to find all filter words before considering it a match.
     int j = 0;
--- contacts/src/seasidepeoplemodel.cpp
+++ contacts/src/seasidepeoplemodel.cpp
@@ -147,9 +147,7 @@
 
 SeasidePerson *SeasidePeopleModel::personById(int id) const
 {
-    // TODO: can this crash? probably
-    SeasidePerson *person = priv->idToContact.value(id);
-    return person;
+    return priv->idToContact.value(id);
 }
 
 SeasidePerson *SeasidePeopleModel::personByPhoneNumber(const QString &msisdn) 
const
@@ -242,12 +240,13 @@
     contacts.reserve(priv->contactIds.size());
 
     foreach (const QContactLocalId &contactId, priv->contactIds) {
-        SeasidePerson *p = personById(contactId);
-
-        if (p->id() == manager()->selfContactId())
-            continue;
-
-        contacts.append(p->contact());
+        if ((contactId != 0) && (contactId != manager()->selfContactId())) {
+            if (SeasidePerson *p = personById(contactId)) {
+                contacts.append(p->contact());
+            } else {
+                qWarning() << Q_FUNC_INFO << "Failed to retrieve contact for 
export: " << contactId;
+            }
+        }
     }
 
     if (!exporter.exportContacts(contacts)) {
--- contacts/src/seasideperson.cpp
+++ contacts/src/seasideperson.cpp
@@ -684,6 +684,19 @@
     emit anniversaryChanged();
 }
 
+SeasidePerson::PresenceState SeasidePerson::presenceState() const
+{
+    return 
static_cast<SeasidePerson::PresenceState>(mContact.detail<QContactPresence>().presenceState());
+}
+
+void SeasidePerson::setPresenceState(PresenceState state)
+{
+    QContactPresence presence = mContact.detail<QContactPresence>();
+    
presence.setPresenceState(static_cast<QContactPresence::PresenceState>(state));
+    mContact.saveDetail(&presence);
+    emit presenceStateChanged();
+}
+
 // TODO: merge with LIST_PROPERTY_FROM_DETAIL_FIELD
 #define LIST_PROPERTY_FROM_FIELD_NAME(detailType, fieldName) \
     QStringList list; \
@@ -744,6 +757,12 @@
     if (oldAvatar.imageUrl() != newAvatar.imageUrl())
         emit avatarPathChanged();
 
+    QContactPresence oldPresence = oldContact.detail<QContactPresence>();
+    QContactPresence newPresence = mContact.detail<QContactPresence>();
+
+    if (oldPresence.presenceState() != newPresence.presenceState())
+        emit presenceStateChanged();
+
     // TODO: differencing of list type details
     emit phoneNumbersChanged();
     emit emailAddressesChanged();
--- contacts/src/seasideperson.h
+++ contacts/src/seasideperson.h
@@ -38,6 +38,7 @@
 
 // Mobility
 #include <QContact>
+#include <QContactPresence>
 
 // Seaside
 #include "seasideproxymodel.h"
@@ -50,6 +51,7 @@
 {
     Q_OBJECT
     Q_ENUMS(DetailType)
+    Q_ENUMS(PresenceState)
 
 public:
     /**
@@ -89,7 +91,19 @@
         WebsiteOtherType,
         // Dates
         BirthdayType,
-        AnniversaryType
+        AnniversaryType,
+        // Presence information
+        PresenceStateType
+    };
+
+    enum PresenceState {
+        PresenceUnknown = QContactPresence::PresenceUnknown,
+        PresenceAvailable = QContactPresence::PresenceAvailable,
+        PresenceHidden = QContactPresence::PresenceHidden,
+        PresenceBusy = QContactPresence::PresenceBusy,
+        PresenceAway = QContactPresence::PresenceAway,
+        PresenceExtendedAway = QContactPresence::PresenceExtendedAway,
+        PresenceOffline = QContactPresence::PresenceOffline
     };
 
     explicit SeasidePerson(QObject *parent = 0);
@@ -180,6 +194,10 @@
     QDateTime anniversary() const;
     void setAnniversary(const QDateTime &av);
 
+    Q_PROPERTY(PresenceState presenceState READ presenceState WRITE 
setPresenceState NOTIFY presenceStateChanged)
+    PresenceState presenceState() const;
+    void setPresenceState(PresenceState state);
+
     Q_PROPERTY(QStringList accountUris READ accountUris NOTIFY 
accountUrisChanged)
     QStringList accountUris() const;
 
@@ -219,6 +237,7 @@
     void websiteTypesChanged();
     void birthdayChanged();
     void anniversaryChanged();
+    void presenceStateChanged();
     void accountUrisChanged();
     void accountPathsChanged();
 
--- contacts/src/sparqlfetchrequest.cpp
+++ contacts/src/sparqlfetchrequest.cpp
@@ -42,7 +42,10 @@
 #include <QSparqlResult>
 
 #include <QContactAvatar>
+#include <QContactEmailAddress>
 #include <QContactName>
+#include <QContactOnlineAccount>
+#include <QContactOrganization>
 #include <QContactPhoneNumber>
 
 #include <QElapsedTimer>
@@ -271,7 +274,11 @@
                 "\n tracker:coalesce("
                 "\n  nie:url(nco:photo(?x)),"
                 "\n  nco:imAvatar(?imAccount))"
-                "\n GROUP_CONCAT(nco:phoneNumber(?phoneNumber), ';')");
+                "\n GROUP_CONCAT(nco:phoneNumber(?phoneNumber), ';')"
+                "\n GROUP_CONCAT(nco:emailAddress(?emailAddress), ';')"
+                "\n GROUP_CONCAT(nco:fullname(?organization), ';')"
+                "\n GROUP_CONCAT(nco:imID(?imAccount), ';')"
+                "\n GROUP_CONCAT(nco:imAccountType(?imAccount), ';')");
     }
 
     queryString += QLatin1String(
@@ -356,6 +363,28 @@
                 contact.saveDetail(&number);
             }
 
+            foreach (const QString &string, 
results->value(6).toString().split(QLatin1Char(';'))) {
+                QContactEmailAddress address;
+                address.setEmailAddress(string);
+                contact.saveDetail(&address);
+            }
+
+            foreach (const QString &string, 
results->value(7).toString().split(QLatin1Char(';'))) {
+                QContactOrganization organization;
+                organization.setName(string);
+                contact.saveDetail(&organization);
+            }
+
+            const QStringList imIds = 
results->value(8).toString().split(QLatin1Char(';'));
+            const QStringList imTypes = 
results->value(9).toString().split(QLatin1Char(';'));
+
+            for (int i = 0; i < imIds.count() || i < imTypes.count(); ++i) {
+                QContactOnlineAccount account;
+                account.setAccountUri(imIds.value(i));
+                account.setServiceProvider(imTypes.value(i));
+                contact.saveDetail(&account);
+            }
+
             contacts.append(contact);
         }
 
--- contacts/tests/tst_seasideperson/tst_seasideperson.cpp
+++ contacts/tests/tst_seasideperson/tst_seasideperson.cpp
@@ -73,6 +73,7 @@
     void birthday();
     void anniversary();
     void address();
+    void presenceState();
     void marshalling();
     void setContact();
 };
@@ -399,6 +400,19 @@
     QCOMPARE(person->addressTypes().at(1), 
(int)SeasidePerson::AddressWorkType);
 }
 
+void tst_SeasidePerson::presenceState()
+{
+    QScopedPointer<SeasidePerson> person(new SeasidePerson);
+    QCOMPARE(person->presenceState(), SeasidePerson::PresenceUnknown);
+    QSignalSpy spy(person.data(), SIGNAL(presenceStateChanged()));
+    person->setPresenceState(SeasidePerson::PresenceAvailable);
+    QCOMPARE(spy.count(), 1);
+    QCOMPARE(person->presenceState(), SeasidePerson::PresenceAvailable);
+    person->setPresenceState(SeasidePerson::PresenceBusy);
+    QCOMPARE(spy.count(), 2);
+    QCOMPARE(person->presenceState(), SeasidePerson::PresenceBusy);
+}
+
 
 void tst_SeasidePerson::marshalling()
 {
@@ -423,6 +437,12 @@
     QCOMPARE(person->displayLabel(), QString::fromLatin1("Hello World"));
     QVERIFY(person->favorite());
 
+    {   // Seaside person saves the display label as the custom label.
+        QContactName nameDetail = contact.detail<QContactName>();
+        nameDetail.setCustomLabel("Hello World");
+        contact.saveDetail(&nameDetail);
+    }
+
     QCOMPARE(person->contact(), contact);
 }
 
@@ -432,6 +452,12 @@
 
     QContact contact;
 
+    {   // ### contactChanged is only emitted if the id differs, not any of 
the members.
+        QContactId contactId;
+        contactId.setLocalId(5);
+        contact.setId(contactId);
+    }
+
     {
         QContactName nameDetail;
         nameDetail.setFirstName("Hello");
--- email/src/folderlistmodel.cpp
+++ email/src/folderlistmodel.cpp
@@ -157,3 +157,9 @@
 {
     return m_mailFolderIds.count();
 }
+
+QVariant FolderListModel::folderUnreadCount(QVariant folderId)
+{
+    int folderIndex = indexFromFolderId(folderId);
+    return data(index(folderIndex), FolderUnreadCount);
+}
--- email/src/folderlistmodel.h
+++ email/src/folderlistmodel.h
@@ -41,6 +41,7 @@
     Q_INVOKABLE QVariant inboxFolderId ();
     Q_INVOKABLE QVariant inboxFolderName();
     Q_INVOKABLE int totalNumberOfFolders();
+    Q_INVOKABLE QVariant folderUnreadCount(QVariant folderId);
 
 private:
     QMailFolderIdList m_mailFolderIds;
--- social/src/facebook/facebookinterface.cpp
+++ social/src/facebook/facebookinterface.cpp
@@ -76,6 +76,8 @@
     , q(parent)
     , d(parentData)
     , populatePending(false)
+    , populateDataForUnseenPending(false)
+    , continuationRequestActive(false)
     , internalStatus(FacebookInterfacePrivate::Idle)
     , currentReply(0)
 {
@@ -236,6 +238,7 @@
     }
 
     QByteArray replyData = currentReply->readAll();
+    QUrl requestUrl = currentReply->request().url();
     deleteReply();
     bool ok = false;
     QVariantMap responseData = ContentItemInterface::parseReplyData(replyData, 
&ok);
@@ -252,7 +255,7 @@
         q->continuePopulateDataForUnseenNode(responseData);
     } else if (internalStatus == FacebookInterfacePrivate::PopulatingSeenNode) 
{
         // This one should be simpler because each of the requested 
fields/connections is a property.
-        q->continuePopulateDataForSeenNode(responseData);
+        q->continuePopulateDataForSeenNode(responseData, requestUrl);
     } else {
         qWarning() << Q_FUNC_INFO << "Error: network reply finished while in 
unexpectant state!  Received:" << responseData;
     }
@@ -262,9 +265,6 @@
 void FacebookInterfacePrivate::errorHandler(QNetworkReply::NetworkError err)
 {
     qWarning() << Q_FUNC_INFO << "Error: network error occurred:" << err;
-    //deleteReply(); // XXX TODO: we seem to get "UnknownContentError" all the 
time.
-                     // So, for now, don't delete reply on error, as we still 
get the
-                     // finished signal, and need to handle the "unknown" data.
 
     switch (err) {
         case QNetworkReply::NoError: d->errorMessage = 
QLatin1String("QNetworkReply::NoError"); break;
@@ -297,6 +297,19 @@
     d->error = SocialNetworkInterface::RequestError;
     d->status = SocialNetworkInterface::Error;
 
+    if ((internalStatus == FacebookInterfacePrivate::PopulatingUnseenNode
+            || internalStatus == FacebookInterfacePrivate::PopulatingSeenNode)
+            && d->repopulatingCurrentNode) {
+        // failed repopulating, either at "get node" step, or at "get related 
data" step.
+        d->repopulatingCurrentNode = false;
+    }
+
+    if (continuationRequestActive) {
+        // failed during a continuation request.  This shouldn't be a huge 
deal,
+        // since we have been populating the cache as we received more data 
anyway
+        continuationRequestActive = false;
+    }
+
     emit q->statusChanged();
     emit q->errorChanged();
     emit q->errorMessageChanged();
@@ -305,8 +318,6 @@
 /*! \internal */
 void FacebookInterfacePrivate::sslErrorsHandler(const QList<QSslError> &errs)
 {
-    deleteReply();
-
     d->errorMessage = QLatin1String("SSL error: ");
     if (errs.isEmpty()) {
         d->errorMessage += QLatin1String("unknown SSL error");
@@ -667,54 +678,72 @@
     // eg: with currentNode = Photo; connectionTypes == comments,likes,tags; 
whichFields = id,name; limit = 10:
     // GET 
https://graph.facebook.com/<photo_id>/fields=comments.limit(10).fields(id,name),likes.limit(10).fields(id,name),tags.fields(id,name).limit(10)
 
-    QString totalFieldsQuery;
-    for (int i = 0; i < connectionTypes.size(); ++i) {
-        bool addExtra = false;
-        bool append = false;
-        FacebookInterface::ContentItemType cit = 
static_cast<FacebookInterface::ContentItemType>(connectionTypes.at(i));
-        switch (cit) {
-            case FacebookInterface::NotInitialized:  qWarning() << Q_FUNC_INFO 
<< "Invalid content item type specified in filter: NotInitialized";  break;
-            case FacebookInterface::Unknown:         qWarning() << Q_FUNC_INFO 
<< "Invalid content item type specified in filter: NotInitialized";  break;
-            case FacebookInterface::ObjectReference: qWarning() << Q_FUNC_INFO 
<< "Invalid content item type specified in filter: ObjectReference"; break;
-            case FacebookInterface::Like:     addExtra = true;  append = true; 
totalFieldsQuery.append(QLatin1String("likes"));    break;
-            case FacebookInterface::Tag:      addExtra = true;  append = true; 
totalFieldsQuery.append(QLatin1String("tags"));     break;
-            case FacebookInterface::Picture:  addExtra = false; append = true; 
totalFieldsQuery.append(QLatin1String("picture"));  break;
-            case FacebookInterface::Location: addExtra = true; append = false; 
totalFieldsQuery.append(QLatin1String("location")); break; // not supported?
-            case FacebookInterface::Comment:  addExtra = true; append = true; 
totalFieldsQuery.append(QLatin1String("comments")); break;
-            case FacebookInterface::User:     addExtra = true; append = true; 
totalFieldsQuery.append(QLatin1String("friends"));  break; // subscriptions etc?
-            case FacebookInterface::Album:    addExtra = true; append = true; 
totalFieldsQuery.append(QLatin1String("albums"));   break;
-            case FacebookInterface::Photo:    addExtra = true; append = true; 
totalFieldsQuery.append(QLatin1String("photos"));   break;
-            case FacebookInterface::Event:    addExtra = true; append = true; 
totalFieldsQuery.append(QLatin1String("events"));   break;
-            default: break;
-        }
-
-        QString whichFieldsString;
-        QStringList whichFields = connectionWhichFields.at(i);
-        if (!whichFields.isEmpty())
-            whichFieldsString = QLatin1String(".fields(") + 
whichFields.join(",") + QLatin1String(")");
-
-        QString limitString;
-        int limit = connectionLimits.at(i);
-        if (limit > 0)
-            limitString = QLatin1String(".limit(") + QString::number(limit) + 
QLatin1String(")");
-
-        if (append && addExtra && !limitString.isEmpty())
-            totalFieldsQuery.append(limitString);
-        if (append && addExtra && !whichFieldsString.isEmpty())
-            totalFieldsQuery.append(whichFieldsString);
-        if (append)
-            totalFieldsQuery.append(QLatin1String(","));
-    }
-
-    totalFieldsQuery.chop(1); // remove trailing comma.
-    QVariantMap extraData;
-    extraData.insert("fields", totalFieldsQuery);
-
-    // now start the request.
-    f->currentReply = getRequest(whichNode->identifier(), QString(), 
QStringList(), extraData);
-    connect(f->currentReply, SIGNAL(finished()), f, SLOT(finishedHandler()));
-    connect(f->currentReply, SIGNAL(error(QNetworkReply::NetworkError)), f, 
SLOT(errorHandler(QNetworkReply::NetworkError)));
-    connect(f->currentReply, SIGNAL(sslErrors(QList<QSslError>)), f, 
SLOT(sslErrorsHandler(QList<QSslError>)));
+    // XXX TODO: in the future, each of the Facebook-specific 
IdentifiableContentItemType classes should
+    // provide private helper functions for the FacebookInterface to build the 
appropriate query.
+    // e.g: QString FacebookAlbumInterface::relatedDataQuery(types, limits, 
whichfields);
+    // That way we can provide "special case" code for every type in a neat, 
modular fashion.
+    if (connectionTypes.size() == 1 && connectionTypes.at(0) == 
FacebookInterface::Photo
+            && connectionLimits.at(0) == -1 && whichNode->type() == 
FacebookInterface::Album) {
+        // special case code for FacebookAlbum "populate all photos" request
+        QVariantMap extraData;
+        extraData.insert(QLatin1String("limit"), QLatin1String("25"));
+        f->currentReply = getRequest(whichNode->identifier(), 
FACEBOOK_ONTOLOGY_CONNECTIONS_PHOTOS, QStringList(), extraData);
+        connect(f->currentReply, SIGNAL(finished()), f, 
SLOT(finishedHandler()));
+        connect(f->currentReply, SIGNAL(error(QNetworkReply::NetworkError)), 
f, SLOT(errorHandler(QNetworkReply::NetworkError)));
+        connect(f->currentReply, SIGNAL(sslErrors(QList<QSslError>)), f, 
SLOT(sslErrorsHandler(QList<QSslError>)));
+    } else {
+        // generic query
+        QString totalFieldsQuery;
+        for (int i = 0; i < connectionTypes.size(); ++i) {
+            bool addExtra = false;
+            bool append = false;
+            FacebookInterface::ContentItemType cit = 
static_cast<FacebookInterface::ContentItemType>(connectionTypes.at(i));
+            switch (cit) {
+                case FacebookInterface::NotInitialized:  qWarning() << 
Q_FUNC_INFO << "Invalid content item type specified in filter: NotInitialized"; 
 break;
+                case FacebookInterface::Unknown:         qWarning() << 
Q_FUNC_INFO << "Invalid content item type specified in filter: NotInitialized"; 
 break;
+                case FacebookInterface::ObjectReference: qWarning() << 
Q_FUNC_INFO << "Invalid content item type specified in filter: 
ObjectReference"; break;
+                case FacebookInterface::Like:     addExtra = true;  append = 
true; totalFieldsQuery.append(QLatin1String("likes"));    break;
+                case FacebookInterface::Tag:      addExtra = true;  append = 
true; totalFieldsQuery.append(QLatin1String("tags"));     break;
+                case FacebookInterface::Picture:  addExtra = false; append = 
true; totalFieldsQuery.append(QLatin1String("picture"));  break;
+                case FacebookInterface::Location: addExtra = true; append = 
false; totalFieldsQuery.append(QLatin1String("location")); break; // not 
supported?
+                case FacebookInterface::Comment:  addExtra = true; append = 
true; totalFieldsQuery.append(QLatin1String("comments")); break;
+                case FacebookInterface::User:     addExtra = true; append = 
true; totalFieldsQuery.append(QLatin1String("friends"));  break; // 
subscriptions etc?
+                case FacebookInterface::Album:    addExtra = true; append = 
true; totalFieldsQuery.append(QLatin1String("albums"));   break;
+                case FacebookInterface::Photo:    addExtra = true; append = 
true; totalFieldsQuery.append(QLatin1String("photos"));   break;
+                case FacebookInterface::Event:    addExtra = true; append = 
true; totalFieldsQuery.append(QLatin1String("events"));   break;
+                default: break;
+            }
+
+            QString whichFieldsString;
+            QStringList whichFields = connectionWhichFields.at(i);
+            if (!whichFields.isEmpty())
+                whichFieldsString = QLatin1String(".fields(") + 
whichFields.join(",") + QLatin1String(")");
+
+            QString limitString;
+            int limit = connectionLimits.at(i);
+            if (limit == -1)
+                limitString = QLatin1String(".limit(0)");
+            else if (limit > 0)
+                limitString = QLatin1String(".limit(") + 
QString::number(limit) + QLatin1String(")");
+
+            if (append && addExtra && !limitString.isEmpty())
+                totalFieldsQuery.append(limitString);
+            if (append && addExtra && !whichFieldsString.isEmpty())
+                totalFieldsQuery.append(whichFieldsString);
+            if (append)
+                totalFieldsQuery.append(QLatin1String(","));
+        }
+
+        totalFieldsQuery.chop(1); // remove trailing comma.
+        QVariantMap extraData;
+        extraData.insert("fields", totalFieldsQuery);
+
+        // now start the request.
+        f->currentReply = getRequest(whichNode->identifier(), QString(), 
QStringList(), extraData);
+        connect(f->currentReply, SIGNAL(finished()), f, 
SLOT(finishedHandler()));
+        connect(f->currentReply, SIGNAL(error(QNetworkReply::NetworkError)), 
f, SLOT(errorHandler(QNetworkReply::NetworkError)));
+        connect(f->currentReply, SIGNAL(sslErrors(QList<QSslError>)), f, 
SLOT(sslErrorsHandler(QList<QSslError>)));
+    }
 }
 
 /*! \reimp */
@@ -744,72 +773,82 @@
     // continued in continuePopulateDataForSeenNode().
 }
 
+#define FACEBOOK_CREATE_UNCACHED_ENTRY_FROM_DATA(data, type)                   
             \
+    do {                                                                       
             \
+        QVariantList itemsList = 
data.value(FACEBOOK_ONTOLOGY_CONNECTIONS_DATA).toList();   \
+        foreach (const QVariant &currVar, itemsList) {                         
             \
+            QVariantMap currMap = currVar.toMap();                             
             \
+            currMap.insert(NEMOQMLPLUGINS_SOCIAL_CONTENTITEMTYPE, type);       
             \
+            currMap.insert(NEMOQMLPLUGINS_SOCIAL_CONTENTITEMID,                
             \
+                    currMap.value(FACEBOOK_ONTOLOGY_METADATA_ID).toString());  
             \
+            relatedContent.append(d->createUncachedEntry(currMap));            
             \
+        }                                                                      
             \
+    } while (0)
+
 /*! \internal */
-void FacebookInterface::continuePopulateDataForSeenNode(const QVariantMap 
&relatedData)
+void FacebookInterface::continuePopulateDataForSeenNode(const QVariantMap 
&relatedData, const QUrl &requestUrl)
 {
     // We receive the related data and transform it into ContentItems.
     // Finally, we populate the cache for the node and update the internal 
model data.
 
+    QString continuationRequestUri;
     QList<CacheEntry *> relatedContent;
+    if (f->continuationRequestActive) {
+        // we are continuing a request, and thus don't overwrite the existing
+        // cache entries, but instead append to them.
+        bool ok = true;
+        relatedContent = d->cachedContent(d->currentNode(), &ok);
+        if (!ok) {
+            qWarning() << Q_FUNC_INFO << "Clobbering cached content in 
continuation request for node:" << d->currentNode()->identifier();
+        }
+    }
+
+    // construct related content items from the request results.
     QStringList keys = relatedData.keys();
     foreach (const QString &key, keys) {
 #if 0
 qWarning() << "        " << key << " = " << 
FACEBOOK_DEBUG_VALUE_STRING_FROM_DATA(key, relatedData);
 #endif
-        if (key == FACEBOOK_ONTOLOGY_CONNECTIONS_LIKES) {
-            QVariantMap likesObject = relatedData.value(key).toMap();
-            QVariantList likesData = 
likesObject.value(FACEBOOK_ONTOLOGY_CONNECTIONS_DATA).toList();
-            foreach (const QVariant &currLike, likesData) {
-                QVariantMap clm = currLike.toMap();
-                clm.insert(NEMOQMLPLUGINS_SOCIAL_CONTENTITEMTYPE, 
FacebookInterface::Like);
-                clm.insert(NEMOQMLPLUGINS_SOCIAL_CONTENTITEMID, 
clm.value(FACEBOOK_ONTOLOGY_METADATA_ID).toString());
-                relatedContent.append(d->createUncachedEntry(clm));
+        if (key == FACEBOOK_ONTOLOGY_CONNECTIONS_DATA) {
+            // contains a list of objects, whose type should be described by 
the request uri
+            QString reqPath = requestUrl.path();
+            if (reqPath.endsWith(FACEBOOK_ONTOLOGY_CONNECTIONS_LIKES)) {
+                FACEBOOK_CREATE_UNCACHED_ENTRY_FROM_DATA(relatedData, 
FacebookInterface::Like);
+            } else if 
(reqPath.endsWith(FACEBOOK_ONTOLOGY_CONNECTIONS_COMMENTS)) {
+                FACEBOOK_CREATE_UNCACHED_ENTRY_FROM_DATA(relatedData, 
FacebookInterface::Comment);
+            } else if (reqPath.endsWith(FACEBOOK_ONTOLOGY_CONNECTIONS_TAGS)) {
+                FACEBOOK_CREATE_UNCACHED_ENTRY_FROM_DATA(relatedData, 
FacebookInterface::Tag);
+            } else if (reqPath.endsWith(FACEBOOK_ONTOLOGY_CONNECTIONS_PHOTOS)) 
{
+                FACEBOOK_CREATE_UNCACHED_ENTRY_FROM_DATA(relatedData, 
FacebookInterface::Photo);
+            } else if (reqPath.endsWith(FACEBOOK_ONTOLOGY_CONNECTIONS_ALBUMS)) 
{
+                FACEBOOK_CREATE_UNCACHED_ENTRY_FROM_DATA(relatedData, 
FacebookInterface::Album);
+            } else if 
(reqPath.endsWith(FACEBOOK_ONTOLOGY_CONNECTIONS_FRIENDS)) {
+                FACEBOOK_CREATE_UNCACHED_ENTRY_FROM_DATA(relatedData, 
FacebookInterface::User);
+            } else {
+                qWarning() << Q_FUNC_INFO << "Informative: Unsupported data 
retrieved via edge:" << reqPath;
             }
+        } else if (key == FACEBOOK_ONTOLOGY_CONNECTIONS_LIKES) {
+            QVariantMap likesObject = relatedData.value(key).toMap();
+            FACEBOOK_CREATE_UNCACHED_ENTRY_FROM_DATA(likesObject, 
FacebookInterface::Like);
         } else if (key == FACEBOOK_ONTOLOGY_CONNECTIONS_COMMENTS) {
             QVariantMap commentsObject = relatedData.value(key).toMap();
-            QVariantList commentsData = 
commentsObject.value(FACEBOOK_ONTOLOGY_CONNECTIONS_DATA).toList();
-            foreach (const QVariant &currComm, commentsData) {
-                QVariantMap ccm = currComm.toMap();
-                ccm.insert(NEMOQMLPLUGINS_SOCIAL_CONTENTITEMTYPE, 
FacebookInterface::Comment);
-                ccm.insert(NEMOQMLPLUGINS_SOCIAL_CONTENTITEMID, 
ccm.value(FACEBOOK_ONTOLOGY_METADATA_ID).toString());
-                relatedContent.append(d->createUncachedEntry(ccm));
-            }
+            FACEBOOK_CREATE_UNCACHED_ENTRY_FROM_DATA(commentsObject, 
FacebookInterface::Comment);
         } else if (key == FACEBOOK_ONTOLOGY_CONNECTIONS_TAGS) {
             QVariantMap tagsObject = relatedData.value(key).toMap();
-            QVariantList tagsData = 
tagsObject.value(FACEBOOK_ONTOLOGY_CONNECTIONS_DATA).toList();
-            foreach (const QVariant &currTag, tagsData) {
-                QVariantMap ctm = currTag.toMap();
-                ctm.insert(NEMOQMLPLUGINS_SOCIAL_CONTENTITEMTYPE, 
FacebookInterface::Tag);
-                ctm.insert(NEMOQMLPLUGINS_SOCIAL_CONTENTITEMID, 
ctm.value(FACEBOOK_ONTOLOGY_METADATA_ID).toString());
-                relatedContent.append(d->createUncachedEntry(ctm));
-            }
+            FACEBOOK_CREATE_UNCACHED_ENTRY_FROM_DATA(tagsObject, 
FacebookInterface::Tag);
         } else if (key == FACEBOOK_ONTOLOGY_CONNECTIONS_PHOTOS) {
             QVariantMap photosObject = relatedData.value(key).toMap();
-            QVariantList photosData = 
photosObject.value(FACEBOOK_ONTOLOGY_CONNECTIONS_DATA).toList();
-            foreach (const QVariant &currPhoto, photosData) {
-                QVariantMap cpm = currPhoto.toMap();
-                cpm.insert(NEMOQMLPLUGINS_SOCIAL_CONTENTITEMTYPE, 
FacebookInterface::Photo);
-                cpm.insert(NEMOQMLPLUGINS_SOCIAL_CONTENTITEMID, 
cpm.value(FACEBOOK_ONTOLOGY_METADATA_ID).toString());
-                relatedContent.append(d->createUncachedEntry(cpm));
-            }
+            FACEBOOK_CREATE_UNCACHED_ENTRY_FROM_DATA(photosObject, 
FacebookInterface::Photo);
         } else if (key == FACEBOOK_ONTOLOGY_CONNECTIONS_ALBUMS) {
             QVariantMap albumsObject = relatedData.value(key).toMap();
             QVariantList albumsData = 
albumsObject.value(FACEBOOK_ONTOLOGY_CONNECTIONS_DATA).toList();
-            foreach (const QVariant &currAlbum, albumsData) {
-                QVariantMap cam = currAlbum.toMap();
-                cam.insert(NEMOQMLPLUGINS_SOCIAL_CONTENTITEMTYPE, 
FacebookInterface::Album);
-                cam.insert(NEMOQMLPLUGINS_SOCIAL_CONTENTITEMID, 
cam.value(FACEBOOK_ONTOLOGY_METADATA_ID).toString());
-                relatedContent.append(d->createUncachedEntry(cam));
-            }
+            FACEBOOK_CREATE_UNCACHED_ENTRY_FROM_DATA(albumsObject, 
FacebookInterface::Album);
         } else if (key == FACEBOOK_ONTOLOGY_CONNECTIONS_FRIENDS) {
             QVariantMap friendsObject = relatedData.value(key).toMap();
-            QVariantList friendsData = 
friendsObject.value(FACEBOOK_ONTOLOGY_CONNECTIONS_DATA).toList();
-            foreach (const QVariant &currFriend, friendsData) {
-                QVariantMap cfm = currFriend.toMap();
-                cfm.insert(NEMOQMLPLUGINS_SOCIAL_CONTENTITEMTYPE, 
FacebookInterface::User);
-                cfm.insert(NEMOQMLPLUGINS_SOCIAL_CONTENTITEMID, 
cfm.value(FACEBOOK_ONTOLOGY_METADATA_ID).toString());
-                relatedContent.append(d->createUncachedEntry(cfm));
-            }
+            FACEBOOK_CREATE_UNCACHED_ENTRY_FROM_DATA(friendsObject, 
FacebookInterface::User);
+        } else if (key == FACEBOOK_ONTOLOGY_METADATA_PAGING) {
+            QVariantMap pagingObject = relatedData.value(key).toMap();
+            continuationRequestUri = 
pagingObject.value(FACEBOOK_ONTOLOGY_METADATA_PAGING_NEXT).toString();
         } else if (key == FACEBOOK_ONTOLOGY_OBJECTREFERENCE_OBJECTIDENTIFIER
                 || key == FACEBOOK_ONTOLOGY_OBJECTREFERENCE_OBJECTPICTURE) {
             // can ignore this data - it's for the current node, which we 
already know.
@@ -822,13 +861,34 @@
     // XXX TODO: make the entire filter/sort codepath more efficient, by 
guaranteeing that whatever
     // comes out of the cache must be sorted/filtered already?  Requires 
invalidating the entire
     // cache on filter/sorter change, however... hrm...
+
     bool ok = false;
     d->populateCache(d->currentNode(), relatedContent, &ok);
-    if (!ok)
+    if (!ok) {
         qWarning() << Q_FUNC_INFO << "Error: Unable to populate the cache for 
the current node:" << d->currentNode()->identifier();
+    }
 
-    // Finally, update the model data.
+    // Update the model data.
     updateInternalData(relatedContent);
+
+    // If we need to request more (paged) data, do so.
+    if (continuationRequestUri.isEmpty()) {
+        // there are no more results / result pages to retrieve.
+        f->continuationRequestActive = false;
+        d->status = SocialNetworkInterface::Idle;
+        emit statusChanged();
+    } else {
+        // there are more results to retrieve.  Start a continuation request.
+        f->continuationRequestActive = true;
+        // grab the relevant parts of the continuation uri to create a new 
request.
+        QUrl continuationUrl(continuationRequestUri);
+        if 
(continuationUrl.queryItemValue(QLatin1String("access_token")).isEmpty())
+            continuationUrl.addQueryItem(QLatin1String("access_token"), 
f->accessToken);
+        f->currentReply = d->qnam->get(QNetworkRequest(continuationUrl));
+        connect(f->currentReply, SIGNAL(finished()), f, 
SLOT(finishedHandler()));
+        connect(f->currentReply, SIGNAL(error(QNetworkReply::NetworkError)), 
f, SLOT(errorHandler(QNetworkReply::NetworkError)));
+        connect(f->currentReply, SIGNAL(sslErrors(QList<QSslError>)), f, 
SLOT(sslErrorsHandler(QList<QSslError>)));
+    }
 }
 
 /*! \reimp */
--- social/src/facebook/facebookinterface.h
+++ social/src/facebook/facebookinterface.h
@@ -124,7 +124,7 @@
 private:
     void retrieveRelatedContent(IdentifiableContentItemInterface *whichNode);
     void continuePopulateDataForUnseenNode(const QVariantMap &nodeData);
-    void continuePopulateDataForSeenNode(const QVariantMap &nodeData);
+    void continuePopulateDataForSeenNode(const QVariantMap &nodeData, const 
QUrl &requestUrl);
 
     // private data.
 private:
--- social/src/facebook/facebookinterface_p.h
+++ social/src/facebook/facebookinterface_p.h
@@ -32,6 +32,8 @@
 #ifndef FACEBOOKINTERFACE_P_H
 #define FACEBOOKINTERFACE_P_H
 
+#include "socialnetworkinterface_p.h"
+
 #include <QtCore/QObject>
 #include <QtCore/QVariantMap>
 #include <QtCore/QStringList>
@@ -60,6 +62,7 @@
 
     bool populatePending;
     bool populateDataForUnseenPending;
+    bool continuationRequestActive;
 
     enum InternalStatus {
         Idle = 0,
--- social/src/facebook/facebookontology_p.h
+++ social/src/facebook/facebookontology_p.h
@@ -55,6 +55,8 @@
 #define FACEBOOK_ONTOLOGY_METADATA_FIELDS                  
QLatin1String("fields")
 #define FACEBOOK_ONTOLOGY_METADATA_TYPE                    
QLatin1String("type")
 #define FACEBOOK_ONTOLOGY_METADATA_ID                      QLatin1String("id")
+#define FACEBOOK_ONTOLOGY_METADATA_PAGING                  
QLatin1String("paging")
+#define FACEBOOK_ONTOLOGY_METADATA_PAGING_NEXT             
QLatin1String("next")
 
 #define FACEBOOK_ONTOLOGY_CONNECTIONS_LIKES                
QLatin1String("likes")
 #define FACEBOOK_ONTOLOGY_CONNECTIONS_COMMENTS             
QLatin1String("comments")
--- social/src/socialnetworkinterface.cpp
+++ social/src/socialnetworkinterface.cpp
@@ -201,6 +201,7 @@
     , qnam(new QNetworkAccessManager(parent))
     , placeHolderNode(new IdentifiableContentItemInterface(parent))
     , initialized(false)
+    , repopulatingCurrentNode(false)
     , error(SocialNetworkInterface::NoError)
     , status(SocialNetworkInterface::Initializing)
     , currentNodePosition(-1)
@@ -399,12 +400,13 @@
         currentNode = nodeStack.at(currentNodePosition);
 
     if (currentNode == n && currentNode != placeHolderNode)
-        return; // XXX TODO MAYBE: trigger reload of cache data for this node?
+        return; // nothing to do.
 
-    // Check to see if we need to replace the placeholder node.
-    if (currentNode == placeHolderNode) {
+    // Check to see if we need to replace the placeholder or current node.
+    if (currentNode == placeHolderNode || repopulatingCurrentNode) {
         // this will happen when the node data that the
         // derived type requested is received.
+        repopulatingCurrentNode = false;
         if (currentNodePosition != (nodeStack.size() - 1)) {
             qWarning() << Q_FUNC_INFO << "Error: placeholder node not the 
ToS!";
         } else {
@@ -555,10 +557,15 @@
 /*! \internal */
 void 
SocialNetworkInterfacePrivate::removeEntryFromNodeContent(IdentifiableContentItemInterface
 *item, CacheEntry *entry)
 {
+    if (entry == 0)
+        return;
+
     int removeCount = nodeContent.remove(item, entry);
     if (removeCount == 0) {
         qWarning() << Q_FUNC_INFO << "Entry:" << entry << "is not cached as 
content for node:" << item;
         return;
+    } else if (removeCount > 1) {
+        qWarning() << Q_FUNC_INFO << "Entry:" << entry << "was cached" << 
removeCount << "times as content for node:" << item;
     }
 
     derefCacheEntry(entry);
@@ -629,16 +636,17 @@
 
     *ok = true;
 
-    QList<CacheEntry*> newData;
+    QList<CacheEntry*> existingGoodEntries;
+    QList<CacheEntry*> newCacheEntries;
     if (nodeContent.contains(n)) {
         // updating existing cache entry.
         QList<CacheEntry*> oldData = nodeContent.values(n);
         QList<CacheEntry*> doomedData;
         foreach (CacheEntry *currData, oldData) {
-            if (!c.contains(currData)) {
-                doomedData.append(currData);
+            if (c.contains(currData)) {
+                existingGoodEntries.append(currData);
             } else {
-                newData.append(currData);
+                doomedData.append(currData);
             }
         }
 
@@ -647,13 +655,20 @@
             // not contained in the updated cache.
             removeEntryFromNodeContent(n, doomedContent);
         }
+
+        // add new entries to the cache
+        foreach (CacheEntry *newEntry, c) {
+            if (!existingGoodEntries.contains(newEntry)) {
+                newCacheEntries.append(newEntry);
+            }
+        }
     } else {
         // new cache entry.
-        newData = c;
+        newCacheEntries = c;
     }
 
     // populate the cache for the node n from the content c.
-    foreach (CacheEntry *currData, newData) {
+    foreach (CacheEntry *currData, newCacheEntries) {
         addEntryToNodeContent(n, currData);
     }
 }
@@ -857,6 +872,10 @@
     in the node stack, the \c node property will be set to an empty placeholder
     node until the network request completes and the node can be populated with
     the downloaded data.
+
+    If the \c nodeIdentifier is set to the identifier of the current node,
+    the cached data for the node will be cleared and the node and its related
+    data will be reloaded from the network.
 */
 QString SocialNetworkInterface::nodeIdentifier() const
 {
@@ -868,7 +887,13 @@
 void SocialNetworkInterface::setNodeIdentifier(const QString 
&contentItemIdentifier)
 {
     IdentifiableContentItemInterface *cachedNode = 
d->findCachedNode(contentItemIdentifier);
-    if (!cachedNode) {
+    if (d->currentNode() && contentItemIdentifier == 
d->currentNode()->identifier()) {
+        // resetting the current node.  This tells us to reload the node, 
clear its cache and repopulate.
+        d->repopulatingCurrentNode = true;
+        d->pendingCurrentNodeIdentifier = contentItemIdentifier;
+        populateDataForNode(contentItemIdentifier); // "unseen node" without 
pushing placeholder.
+    } else if (!cachedNode) {
+        // Unseen node.
         // call derived class data populate:
         //   d->populateCache() etc once it's finished retrieving.
         //   d->pushNode(newNodePtr).
@@ -878,6 +903,7 @@
         emit nodeChanged();
         populateDataForNode(contentItemIdentifier); // XXX TODO: do we really 
want to trigger populate?  or wait for user to call populate?
     } else {
+        // We've seen this node before and have it cached.
         bool hasCachedContent = false;
         QList<CacheEntry*> data = d->cachedContent(cachedNode, 
&hasCachedContent);
         if (hasCachedContent) {
--- social/src/socialnetworkinterface_p.h
+++ social/src/socialnetworkinterface_p.h
@@ -96,6 +96,7 @@
     IdentifiableContentItemInterface *placeHolderNode;
 
     bool initialized;
+    bool repopulatingCurrentNode;
 
     QString errorMessage;
     SocialNetworkInterface::ErrorType error;
--- time/src/nemowallclock.cpp
+++ time/src/nemowallclock.cpp
@@ -67,6 +67,8 @@
     m_enabled = e;
     update();
     emit q->enabledChanged();
+    if (m_enabled)
+        emit q->timeChanged();
 }
 
 QDateTime WallClockPrivate::time() const
--- time/tests/wallclock/tst_wallclock.cpp
+++ time/tests/wallclock/tst_wallclock.cpp
@@ -106,6 +106,16 @@
     upCount = updateSpy.count();
     QTest::qWait(1100);
     QVERIFY(updateSpy.count() == upCount);
+
+    // enable and ensure updates happen.
+    obj->setProperty("enabled", QVariant(true));
+
+    // should get an update immediately.
+    QVERIFY(updateSpy.count() == upCount + 1);
+
+    // and another within a second
+    QTest::qWait(1100);
+    QVERIFY(updateSpy.count() > upCount + 1);
 }
 
 

++++++ 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.0
+Version: 0.3.1
 Release: 1
 Sources:
     - "%{name}-%{version}.tar.bz2"



Reply via email to