~ubuntu-filemanager-dev/ubuntu-filemanager-app/nemo-qml-plugins-packaging

« back to all changes in this revision

Viewing changes to social/src/socialnetworkinterface.cpp

  • Committer: Timo Jyrinki
  • Date: 2013-03-20 15:03:31 UTC
  • Revision ID: timo.jyrinki@canonical.com-20130320150331-aggtqfb7cufdiuzc
ImportĀ upstreamĀ 0.3.4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2013 Jolla Ltd. <chris.adams@jollamobile.com>
 
3
 *
 
4
 * You may use this file under the terms of the BSD license as follows:
 
5
 *
 
6
 * "Redistribution and use in source and binary forms, with or without
 
7
 * modification, are permitted provided that the following conditions are
 
8
 * met:
 
9
 *   * Redistributions of source code must retain the above copyright
 
10
 *     notice, this list of conditions and the following disclaimer.
 
11
 *   * Redistributions in binary form must reproduce the above copyright
 
12
 *     notice, this list of conditions and the following disclaimer in
 
13
 *     the documentation and/or other materials provided with the
 
14
 *     distribution.
 
15
 *   * Neither the name of Nemo Mobile nor the names of its contributors
 
16
 *     may be used to endorse or promote products derived from this
 
17
 *     software without specific prior written permission.
 
18
 *
 
19
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 
20
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 
21
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 
22
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 
23
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
24
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 
25
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
26
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
27
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
28
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 
29
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 
30
 */
 
31
 
 
32
#include "socialnetworkinterface.h"
 
33
#include "socialnetworkinterface_p.h"
 
34
 
 
35
#include "contentiteminterface.h"
 
36
#include "identifiablecontentiteminterface.h"
 
37
 
 
38
#include <QtCore/QByteArray>
 
39
#include <QtCore/QUrl>
 
40
#include <QtNetwork/QNetworkAccessManager>
 
41
#include <QtNetwork/QNetworkReply>
 
42
#include <QtNetwork/QNetworkRequest>
 
43
 
 
44
#include <QtDebug>
 
45
 
 
46
/*
 
47
    CacheEntry:
 
48
 
 
49
    The data in the model is actually a list of cache entries.
 
50
    Each entry consists of the QVariantMap of data for the object,
 
51
    and a (lazily constructed) ContentItem ptr.
 
52
 
 
53
    Specific implementations of the SocialNetwork interface should
 
54
    use \c SocialNetworkInterfacePrivate::createUncachedEntry() to
 
55
    create cache entries for data associated with a particular node,
 
56
    within their implementation of the \c populateDataForNode()
 
57
    functions.
 
58
 
 
59
    Once they have constructed cache entries for the related data
 
60
    objects, the implementation should call
 
61
    \c SocialNetworkInterfacePrivate::populateCache().
 
62
*/
 
63
CacheEntry::CacheEntry(const QVariantMap &d, ContentItemInterface *i)
 
64
    : data(d), item(i), refcount(0)
 
65
{
 
66
}
 
67
 
 
68
CacheEntry::~CacheEntry()
 
69
{
 
70
    if (item)
 
71
        delete item;
 
72
}
 
73
 
 
74
ArbitraryRequestHandler::ArbitraryRequestHandler(SocialNetworkInterface *parent)
 
75
    : QObject(parent), q(parent), reply(0), isError(false)
 
76
{
 
77
}
 
78
 
 
79
ArbitraryRequestHandler::~ArbitraryRequestHandler()
 
80
{
 
81
    if (reply) {
 
82
        disconnect(reply);
 
83
        reply->deleteLater();
 
84
    }
 
85
}
 
86
 
 
87
bool ArbitraryRequestHandler::request(int requestType, const QString &requestUri, const QVariantMap &queryItems, const QString &postData)
 
88
{
 
89
    if (reply) {
 
90
        qWarning() << Q_FUNC_INFO << "Warning: cannot start arbitrary request: another arbitrary request is in progress";
 
91
        return false;
 
92
    }
 
93
 
 
94
    QList<QPair<QString, QString> > qil;
 
95
    QStringList queryItemKeys = queryItems.keys();
 
96
    foreach (const QString &qik, queryItemKeys)
 
97
        qil.append(qMakePair<QString, QString>(qik, queryItems.value(qik).toString()));
 
98
 
 
99
    QUrl url(requestUri);
 
100
    url.setQueryItems(qil);
 
101
 
 
102
    QNetworkReply *sniReply = 0;
 
103
    switch (requestType) {
 
104
        case SocialNetworkInterface::Get: sniReply = q->d->qnam->get(QNetworkRequest(url)); break;
 
105
        case SocialNetworkInterface::Post: sniReply = q->d->qnam->post(QNetworkRequest(url), QByteArray::fromBase64(postData.toLatin1())); break;
 
106
        default: sniReply = q->d->qnam->deleteResource(QNetworkRequest(url)); break;
 
107
    }
 
108
 
 
109
    if (sniReply) {
 
110
        reply = sniReply;
 
111
        connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(errorHandler(QNetworkReply::NetworkError)));
 
112
        connect(reply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(sslErrorsHandler(QList<QSslError>)));
 
113
        connect(reply, SIGNAL(finished()), this, SLOT(finishedHandler()));
 
114
        return true;
 
115
    }
 
116
 
 
117
    qWarning() << Q_FUNC_INFO << "Warning: cannot start arbitrary request: null reply";
 
118
    return false;
 
119
}
 
120
 
 
121
void ArbitraryRequestHandler::finishedHandler()
 
122
{
 
123
    QByteArray replyData;
 
124
    if (reply) {
 
125
        replyData = reply->readAll();
 
126
        disconnect(reply);
 
127
        reply->deleteLater();
 
128
        reply = 0;
 
129
    }
 
130
 
 
131
    QVariantMap responseData;
 
132
    bool errorOccurred = isError;
 
133
    if (isError) {
 
134
        // note that errors to arbitrary requests don't cause the SocialNetwork
 
135
        // to transition to the Error state.  They are unrelated to the model.
 
136
        responseData.insert(QLatin1String("error"), errorMessage);
 
137
        errorMessage = QString();
 
138
        isError = false;
 
139
    } else {
 
140
        bool ok = false;
 
141
        QVariantMap parsed = ContentItemInterface::parseReplyData(replyData, &ok);
 
142
        if (!ok) {
 
143
            responseData.insert(QLatin1String("response"), replyData);
 
144
        } else {
 
145
            responseData = parsed;
 
146
        }
 
147
    }
 
148
 
 
149
    emit q->arbitraryRequestResponseReceived(errorOccurred, responseData);
 
150
}
 
151
 
 
152
void ArbitraryRequestHandler::errorHandler(QNetworkReply::NetworkError err)
 
153
{
 
154
    switch (err) {
 
155
        case QNetworkReply::NoError: errorMessage = QLatin1String("QNetworkReply::NoError"); break;
 
156
        case QNetworkReply::ConnectionRefusedError: errorMessage = QLatin1String("QNetworkReply::ConnectionRefusedError"); break;
 
157
        case QNetworkReply::RemoteHostClosedError: errorMessage = QLatin1String("QNetworkReply::RemoteHostClosedError"); break;
 
158
        case QNetworkReply::HostNotFoundError: errorMessage = QLatin1String("QNetworkReply::HostNotFoundError"); break;
 
159
        case QNetworkReply::TimeoutError: errorMessage = QLatin1String("QNetworkReply::TimeoutError"); break;
 
160
        case QNetworkReply::OperationCanceledError: errorMessage = QLatin1String("QNetworkReply::OperationCanceledError"); break;
 
161
        case QNetworkReply::SslHandshakeFailedError: errorMessage = QLatin1String("QNetworkReply::SslHandshakeFailedError"); break;
 
162
        case QNetworkReply::TemporaryNetworkFailureError: errorMessage = QLatin1String("QNetworkReply::TemporaryNetworkFailureError"); break;
 
163
        case QNetworkReply::ProxyConnectionRefusedError: errorMessage = QLatin1String("QNetworkReply::ProxyConnectionRefusedError"); break;
 
164
        case QNetworkReply::ProxyConnectionClosedError: errorMessage = QLatin1String("QNetworkReply::ProxyConnectionClosedError"); break;
 
165
        case QNetworkReply::ProxyNotFoundError: errorMessage = QLatin1String("QNetworkReply::ProxyNotFoundError"); break;
 
166
        case QNetworkReply::ProxyTimeoutError: errorMessage = QLatin1String("QNetworkReply::ProxyTimeoutError"); break;
 
167
        case QNetworkReply::ProxyAuthenticationRequiredError: errorMessage = QLatin1String("QNetworkReply::ProxyAuthenticationRequiredError"); break;
 
168
        case QNetworkReply::ContentAccessDenied: errorMessage = QLatin1String("QNetworkReply::ContentAccessDenied"); break;
 
169
        case QNetworkReply::ContentOperationNotPermittedError: errorMessage = QLatin1String("QNetworkReply::ContentOperationNotPermittedError"); break;
 
170
        case QNetworkReply::ContentNotFoundError: errorMessage = QLatin1String("QNetworkReply::ContentNotFoundError"); break;
 
171
        case QNetworkReply::AuthenticationRequiredError: errorMessage = QLatin1String("QNetworkReply::AuthenticationRequiredError"); break;
 
172
        case QNetworkReply::ContentReSendError: errorMessage = QLatin1String("QNetworkReply::ContentReSendError"); break;
 
173
        case QNetworkReply::ProtocolUnknownError: errorMessage = QLatin1String("QNetworkReply::ProtocolUnknownError"); break;
 
174
        case QNetworkReply::ProtocolInvalidOperationError: errorMessage = QLatin1String("QNetworkReply::ProtocolInvalidOperationError"); break;
 
175
        case QNetworkReply::UnknownNetworkError: errorMessage = QLatin1String("QNetworkReply::UnknownNetworkError"); break;
 
176
        case QNetworkReply::UnknownProxyError: errorMessage = QLatin1String("QNetworkReply::UnknownProxyError"); break;
 
177
        case QNetworkReply::UnknownContentError: errorMessage = QLatin1String("QNetworkReply::UnknownContentError"); break;
 
178
        case QNetworkReply::ProtocolFailure: errorMessage = QLatin1String("QNetworkReply::ProtocolFailure"); break;
 
179
        default: errorMessage = QLatin1String("Unknown QNetworkReply::NetworkError"); break;
 
180
    }
 
181
 
 
182
    isError = true;
 
183
}
 
184
 
 
185
void ArbitraryRequestHandler::sslErrorsHandler(const QList<QSslError> &sslErrors)
 
186
{
 
187
    errorMessage = QLatin1String("SSL error: ");
 
188
    if (sslErrors.isEmpty()) {
 
189
        errorMessage += QLatin1String("unknown SSL error");
 
190
    } else {
 
191
        foreach (const QSslError &sslE, sslErrors)
 
192
            errorMessage += sslE.errorString() + QLatin1String("; ");
 
193
        errorMessage.chop(2);
 
194
    }
 
195
 
 
196
    isError = true;
 
197
}
 
198
 
 
199
SocialNetworkInterfacePrivate::SocialNetworkInterfacePrivate(SocialNetworkInterface *parent)
 
200
    : q(parent)
 
201
    , qnam(new QNetworkAccessManager(parent))
 
202
    , placeHolderNode(new IdentifiableContentItemInterface(parent))
 
203
    , initialized(false)
 
204
    , repopulatingCurrentNode(false)
 
205
    , error(SocialNetworkInterface::NoError)
 
206
    , status(SocialNetworkInterface::Initializing)
 
207
    , currentNodePosition(-1)
 
208
    , nodeStackSize(5)
 
209
    , arbitraryRequestHandler(0)
 
210
{
 
211
    // Construct the placeholder node.  This node is used as a placeholder
 
212
    // when the client sets a specific nodeIdentifier until the node can
 
213
    // be retrieved from the social network.
 
214
    placeHolderNode->classBegin();
 
215
    placeHolderNode->componentComplete();
 
216
}
 
217
 
 
218
SocialNetworkInterfacePrivate::~SocialNetworkInterfacePrivate()
 
219
{
 
220
    // remove all cache entries.
 
221
    QList<IdentifiableContentItemInterface*> cacheEntries = nodeContent.keys();
 
222
    foreach (IdentifiableContentItemInterface *nodePtr, cacheEntries)
 
223
        purgeDoomedNode(nodePtr);
 
224
}
 
225
 
 
226
/*! \internal */
 
227
void SocialNetworkInterfacePrivate::filters_append(QDeclarativeListProperty<FilterInterface> *list, FilterInterface *filter)
 
228
{
 
229
    SocialNetworkInterface *sni = qobject_cast<SocialNetworkInterface *>(list->object);
 
230
    if (sni && filter) {
 
231
        if (!filter->parent()) {
 
232
            filter->setParent(sni);
 
233
            filter->m_ownedBySni = true;
 
234
        }
 
235
        sni->d->filters.append(filter);
 
236
    }
 
237
}
 
238
 
 
239
/*! \internal */
 
240
FilterInterface *SocialNetworkInterfacePrivate::filters_at(QDeclarativeListProperty<FilterInterface> *list, int index)
 
241
{
 
242
    SocialNetworkInterface *sni = qobject_cast<SocialNetworkInterface *>(list->object);
 
243
    if (sni && sni->d->filters.count() > index && index >= 0)
 
244
        return sni->d->filters.at(index);
 
245
    return 0;
 
246
}
 
247
 
 
248
/*! \internal */
 
249
void SocialNetworkInterfacePrivate::filters_clear(QDeclarativeListProperty<FilterInterface> *list)
 
250
{
 
251
    SocialNetworkInterface *sni = qobject_cast<SocialNetworkInterface *>(list->object);
 
252
    if (sni) {
 
253
        foreach (FilterInterface *cf, sni->d->filters) {
 
254
            if (cf->m_ownedBySni) {
 
255
                cf->deleteLater();
 
256
            }
 
257
        }
 
258
        sni->d->filters.clear();
 
259
    }
 
260
}
 
261
 
 
262
/*! \internal */
 
263
int SocialNetworkInterfacePrivate::filters_count(QDeclarativeListProperty<FilterInterface> *list)
 
264
{
 
265
    SocialNetworkInterface *sni = qobject_cast<SocialNetworkInterface *>(list->object);
 
266
    if (sni)
 
267
        return sni->d->filters.count();
 
268
    return 0;
 
269
}
 
270
 
 
271
/*! \internal */
 
272
void SocialNetworkInterfacePrivate::sorters_append(QDeclarativeListProperty<SorterInterface> *list, SorterInterface *sorter)
 
273
{
 
274
    SocialNetworkInterface *sni = qobject_cast<SocialNetworkInterface *>(list->object);
 
275
    if (sni && sorter) {
 
276
        if (!sorter->parent()) {
 
277
            sorter->setParent(sni);
 
278
            sorter->m_ownedBySni = true;
 
279
        }
 
280
        sni->d->sorters.append(sorter);
 
281
    }
 
282
}
 
283
 
 
284
/*! \internal */
 
285
SorterInterface *SocialNetworkInterfacePrivate::sorters_at(QDeclarativeListProperty<SorterInterface> *list, int index)
 
286
{
 
287
    SocialNetworkInterface *sni = qobject_cast<SocialNetworkInterface *>(list->object);
 
288
    if (sni && sni->d->sorters.count() > index && index >= 0)
 
289
        return sni->d->sorters.at(index);
 
290
    return 0;
 
291
}
 
292
 
 
293
/*! \internal */
 
294
void SocialNetworkInterfacePrivate::sorters_clear(QDeclarativeListProperty<SorterInterface> *list)
 
295
{
 
296
    SocialNetworkInterface *sni = qobject_cast<SocialNetworkInterface *>(list->object);
 
297
    if (sni) {
 
298
        foreach (SorterInterface *cs, sni->d->sorters) {
 
299
            if (cs->m_ownedBySni) {
 
300
                cs->deleteLater();
 
301
            }
 
302
        }
 
303
        sni->d->sorters.clear();
 
304
    }
 
305
}
 
306
 
 
307
/*! \internal */
 
308
int SocialNetworkInterfacePrivate::sorters_count(QDeclarativeListProperty<SorterInterface> *list)
 
309
{
 
310
    SocialNetworkInterface *sni = qobject_cast<SocialNetworkInterface *>(list->object);
 
311
    if (sni)
 
312
        return sni->d->sorters.count();
 
313
    return 0;
 
314
}
 
315
 
 
316
/*
 
317
    Returns the current node which is the central content item.
 
318
    The model is populated with data related to the node.
 
319
    The current node is always owned by the SocialNetwork base class;
 
320
    it might be deleted at any time (e.g., if the cache entry gets purged).
 
321
*/
 
322
IdentifiableContentItemInterface *SocialNetworkInterfacePrivate::currentNode() const
 
323
{
 
324
    // caller does NOT take ownership.  We can (and will) delete it whenever we like.
 
325
 
 
326
    if (currentNodePosition >= 0 && currentNodePosition < nodeStack.size())
 
327
        return nodeStack.at(currentNodePosition);
 
328
    return 0;
 
329
}
 
330
 
 
331
/*! \internal */
 
332
QString SocialNetworkInterfacePrivate::currentNodeIdentifier() const
 
333
{
 
334
    if (currentNodePosition >= 0 && currentNodePosition < nodeStack.size())
 
335
        return nodeStack.at(currentNodePosition)->identifier();
 
336
    return QString();
 
337
}
 
338
 
 
339
/*! \internal */
 
340
void SocialNetworkInterfacePrivate::purgeDoomedNode(IdentifiableContentItemInterface *n)
 
341
{
 
342
    QList<CacheEntry*> cacheData = nodeContent.values(n);
 
343
    foreach (CacheEntry *currData, cacheData)
 
344
        removeEntryFromNodeContent(n, currData);
 
345
    CacheEntry *nodeCacheEntry = findCacheEntry(n, false);
 
346
    if (nodeCacheEntry)
 
347
        derefCacheEntry(nodeCacheEntry);
 
348
}
 
349
 
 
350
/*! \internal */
 
351
void SocialNetworkInterfacePrivate::maybePurgeDoomedNodes(int count, int direction)
 
352
{
 
353
    // Removes \a count nodes from the node stack.  The nodes
 
354
    // will be removed from the top (most recent) of the stack
 
355
    // if direction == 1, and from the bottom (least recent)
 
356
    // of the stack if direction == 0.
 
357
    // Also purges the associated cached content data, if they
 
358
    // don't also appear elsewhere in the stack.
 
359
 
 
360
    if (direction != 0 && direction != 1) {
 
361
        qWarning() << Q_FUNC_INFO << "Error: invalid direction specified!";
 
362
        return;
 
363
    }
 
364
 
 
365
    if (count > nodeStack.size()) {
 
366
        qWarning() << Q_FUNC_INFO << "Error: not that many nodes in the stack!";
 
367
        return;
 
368
    }
 
369
 
 
370
    // XXX TODO: this is a terrible algorithm, that iterates over the nodeStack way too many times.
 
371
    for (int i = count; i > 0; --i) {
 
372
        // remove the ToS (or BoS) node.
 
373
        IdentifiableContentItemInterface *doomedNode = direction > 0 ? nodeStack.takeLast() : nodeStack.takeFirst();
 
374
        if (direction == 0) {
 
375
            // the current node position needs to be reduced, as all nodes' positions shift down.
 
376
            currentNodePosition--;
 
377
        }
 
378
 
 
379
        // determine whether we need to purge the doomed node.
 
380
        if (!nodeStack.contains(doomedNode)) {
 
381
            // the node doesn't appear anywhere else in the navigation breadcrumb trail.
 
382
            // so we have to delete it and purge our cache of content items for the node.
 
383
            purgeDoomedNode(doomedNode);
 
384
        }
 
385
    }
 
386
 
 
387
}
 
388
 
 
389
/*! \internal */
 
390
void SocialNetworkInterfacePrivate::pushPlaceHolderNode()
 
391
{
 
392
    pushNode(placeHolderNode);
 
393
}
 
394
 
 
395
/*! \internal */
 
396
void SocialNetworkInterfacePrivate::pushNode(IdentifiableContentItemInterface *n)
 
397
{
 
398
    // the caller is responsible for emitting dataChanged() etc.
 
399
 
 
400
    if (n == 0) {
 
401
        qWarning() << Q_FUNC_INFO << "Attempted to push null node!";
 
402
        return;
 
403
    }
 
404
 
 
405
    if (currentNodePosition >= nodeStack.size()) {
 
406
        qWarning() << Q_FUNC_INFO << "Current node not on stack!";
 
407
        return;
 
408
    }
 
409
 
 
410
    IdentifiableContentItemInterface *currentNode = 0;
 
411
    if (currentNodePosition >= 0)
 
412
        currentNode = nodeStack.at(currentNodePosition);
 
413
 
 
414
    if (currentNode == n && currentNode != placeHolderNode)
 
415
        return; // nothing to do.
 
416
 
 
417
    // Check to see if we need to replace the placeholder or current node.
 
418
    if (currentNode == placeHolderNode || repopulatingCurrentNode) {
 
419
        // this will happen when the node data that the
 
420
        // derived type requested is received.
 
421
        repopulatingCurrentNode = false;
 
422
        if (currentNodePosition != (nodeStack.size() - 1)) {
 
423
            qWarning() << Q_FUNC_INFO << "Error: placeholder node not the ToS!";
 
424
        } else {
 
425
            nodeStack.removeLast();
 
426
            nodeStack.append(n);
 
427
        }
 
428
        return;
 
429
    }
 
430
 
 
431
    // Otherwise, we're pushing a new navigable node to the nodeStack.
 
432
    if (currentNodePosition != (nodeStack.size()-1)) {
 
433
        // current navigation position is not the top of stack
 
434
        // ie, they pushed a bunch of nodes, then they called
 
435
        // SNI::previousNode() one or more times, and now they
 
436
        // are pushing a node.  This node becomes the new top
 
437
        // of stack.  Purge any cache entries beyond the current
 
438
        // position if applicable.
 
439
        maybePurgeDoomedNodes(currentNodePosition+1, 1);
 
440
    } else if (nodeStack.size() == nodeStackSize) {
 
441
        // current node position is already top of stack, and
 
442
        // we've reached our max for cached navigation steps.
 
443
        maybePurgeDoomedNodes(1, 0); // purge the bottom (least recently used) node.
 
444
    }
 
445
 
 
446
    nodeStack.append(n);
 
447
    currentNodePosition = nodeStack.size() - 1; // end up pointing to ToS.
 
448
}
 
449
 
 
450
/*! \internal */
 
451
void SocialNetworkInterfacePrivate::nextNode()
 
452
{
 
453
    // the caller is responsible for emitting dataChanged() etc.
 
454
 
 
455
    if (currentNodePosition == -1 || nodeStack.size() == 0) {
 
456
        qWarning() << Q_FUNC_INFO << "No nodes in cache!";
 
457
        return;
 
458
    }
 
459
 
 
460
    if (currentNodePosition == (nodeStack.size() - 1)) {
 
461
        qWarning() << Q_FUNC_INFO << "Already at last node in cache!";
 
462
        return;
 
463
    }
 
464
 
 
465
    currentNodePosition++;
 
466
}
 
467
 
 
468
/*! \internal */
 
469
void SocialNetworkInterfacePrivate::prevNode()
 
470
{
 
471
    // the caller is responsible for emitting dataChanged() etc.
 
472
 
 
473
    if (currentNodePosition == -1 || nodeStack.size() == 0) {
 
474
        qWarning() << Q_FUNC_INFO << "No nodes in cache!";
 
475
        return;
 
476
    }
 
477
 
 
478
    if (currentNodePosition == 0) {
 
479
        qWarning() << Q_FUNC_INFO << "Already at first node in cache!";
 
480
        return;
 
481
    }
 
482
 
 
483
    currentNodePosition--;
 
484
}
 
485
 
 
486
/*! \internal */
 
487
IdentifiableContentItemInterface *SocialNetworkInterfacePrivate::findCachedNode(const QString &nodeIdentifier)
 
488
{
 
489
    for (int i = 0; i < nodeStack.size(); ++i) {
 
490
        if (nodeStack.at(i)->identifier() == nodeIdentifier) {
 
491
            return nodeStack.at(i);
 
492
        }
 
493
    }
 
494
 
 
495
    return 0;
 
496
}
 
497
 
 
498
/*! \internal */
 
499
QList<CacheEntry*> SocialNetworkInterfacePrivate::cachedContent(IdentifiableContentItemInterface *n, bool *ok) const
 
500
{
 
501
    // Types derived from SocialNetworkInterface should call this to retrieve cached content for the node
 
502
 
 
503
    if (!nodeContent.contains(n)) {
 
504
        *ok = false;
 
505
        return QList<CacheEntry*>();
 
506
    }
 
507
 
 
508
    *ok = true;
 
509
    return nodeContent.values(n);
 
510
}
 
511
 
 
512
/*! \internal */
 
513
CacheEntry *SocialNetworkInterfacePrivate::findCacheEntry(const QVariantMap &data, bool create)
 
514
{
 
515
    // have to do a slow search.  avoid this if possible.
 
516
    foreach (CacheEntry *e, cache) {
 
517
        if (e->data == data)
 
518
            return e;
 
519
    }
 
520
 
 
521
    if (!create)
 
522
        return 0;
 
523
 
 
524
    // no such cache entry.  create it, but DON'T append it to the cache.
 
525
    // we append it to the cache (and take ownership of it) when they call
 
526
    // addEntryToNodeContent().
 
527
    CacheEntry *newEntry = new CacheEntry(data);
 
528
    return newEntry;
 
529
}
 
530
 
 
531
/*! \internal */
 
532
CacheEntry *SocialNetworkInterfacePrivate::findCacheEntry(ContentItemInterface *item, bool create)
 
533
{
 
534
    if (cachedItems.contains(item))
 
535
        return cachedItems.value(item);
 
536
 
 
537
    if (!create)
 
538
        return 0;
 
539
 
 
540
    // no such cache entry.  create it, but DON'T append it to the cache.
 
541
    // we append it to the cache (and take ownership of it) when they call
 
542
    // addEntryToNodeContent().
 
543
    CacheEntry *newEntry = new CacheEntry(item->data(), item);
 
544
    return newEntry;
 
545
}
 
546
 
 
547
/*! \internal */
 
548
void SocialNetworkInterfacePrivate::addEntryToNodeContent(IdentifiableContentItemInterface *item, CacheEntry *entry)
 
549
{
 
550
    if (!nodeContent.contains(item) && currentNode() != item) {
 
551
        qWarning() << Q_FUNC_INFO << "No such node:" << item;
 
552
        return;
 
553
    }
 
554
 
 
555
    if (nodeContent.find(item, entry) != nodeContent.end()) {
 
556
        qWarning() << Q_FUNC_INFO << "Entry:" << entry << "already cached as content for node:" << item;
 
557
        return;
 
558
    }
 
559
 
 
560
    entry->refcount++;
 
561
    if (entry->refcount == 1) {
 
562
        // new cache entry.
 
563
        cache.append(entry);
 
564
    }
 
565
 
 
566
    nodeContent.insert(item, entry);
 
567
}
 
568
 
 
569
/*! \internal */
 
570
void SocialNetworkInterfacePrivate::removeEntryFromNodeContent(IdentifiableContentItemInterface *item, CacheEntry *entry)
 
571
{
 
572
    if (entry == 0)
 
573
        return;
 
574
 
 
575
    int removeCount = nodeContent.remove(item, entry);
 
576
    if (removeCount == 0) {
 
577
        qWarning() << Q_FUNC_INFO << "Entry:" << entry << "is not cached as content for node:" << item;
 
578
        return;
 
579
    } else if (removeCount > 1) {
 
580
        qWarning() << Q_FUNC_INFO << "Entry:" << entry << "was cached" << removeCount << "times as content for node:" << item;
 
581
    }
 
582
 
 
583
    derefCacheEntry(entry);
 
584
}
 
585
 
 
586
/*! \internal */
 
587
void SocialNetworkInterfacePrivate::updateCacheEntry(CacheEntry *entry, ContentItemInterface *item, const QVariantMap &data)
 
588
{
 
589
    if (item)
 
590
        entry->item = item;
 
591
    if (data != QVariantMap())
 
592
        entry->data = data;
 
593
}
 
594
 
 
595
/*! \internal */
 
596
void SocialNetworkInterfacePrivate::derefCacheEntry(CacheEntry *entry)
 
597
{
 
598
    if (entry->refcount == 0)
 
599
        qWarning() << Q_FUNC_INFO << "Entry:" << entry << "has not been referenced in the cache";
 
600
 
 
601
    entry->refcount--;
 
602
    if (entry->refcount <= 0) {
 
603
        cache.removeAll(entry);
 
604
        delete entry;
 
605
    }
 
606
}
 
607
 
 
608
/*
 
609
    This function should be called by specific implementations of the
 
610
    SocialNetwork interface, to create a cache entry for the current
 
611
    node whose data is the given \a data, as part of the implementation
 
612
    for the \c populateDataForNode() functions.
 
613
 
 
614
    After the cache entries for the current node have all been created,
 
615
    the implementation should then call
 
616
    \c SocialNetworkInterfacePrivate::populateCache().
 
617
*/
 
618
CacheEntry *SocialNetworkInterfacePrivate::createUncachedEntry(const QVariantMap &data)
 
619
{
 
620
    // this function should be called by SocialNetworkInterface derived-types when
 
621
    // they retrieve data from the service during populateDataForNode().
 
622
    // After creating an uncached entry for each related content data object they receive
 
623
    // from the service, they should call populateCache().
 
624
    CacheEntry *newEntry = new CacheEntry(data);
 
625
    return newEntry;
 
626
}
 
627
 
 
628
/*
 
629
    This function should be called by specific implementations of the
 
630
    SocialNetwork interface, to populate the cache for the current
 
631
    node \a n with the cache entries \a c, as part of the implementation
 
632
    for the \c populateDataForNode() functions.
 
633
 
 
634
    This function will set \a ok to true if the node \a n is the current
 
635
    node as expected, and the cache could be populated.
 
636
*/
 
637
void SocialNetworkInterfacePrivate::populateCache(IdentifiableContentItemInterface *n, const QList<CacheEntry*> c, bool *ok)
 
638
{
 
639
    // Types derived from SocialNetworkInterface should call this to populate the cache
 
640
    // NOTE: we don't have any limits on cache size.  XXX TODO: something sensible?
 
641
 
 
642
    if (currentNode() != n) {
 
643
        // the populated node is not the current node... this is an error.
 
644
        qWarning() << Q_FUNC_INFO << "Attempted to populate cache for non-current node!";
 
645
        *ok = false;
 
646
        return;
 
647
    }
 
648
 
 
649
    *ok = true;
 
650
 
 
651
    QList<CacheEntry*> existingGoodEntries;
 
652
    QList<CacheEntry*> newCacheEntries;
 
653
    if (nodeContent.contains(n)) {
 
654
        // updating existing cache entry.
 
655
        QList<CacheEntry*> oldData = nodeContent.values(n);
 
656
        QList<CacheEntry*> doomedData;
 
657
        foreach (CacheEntry *currData, oldData) {
 
658
            if (c.contains(currData)) {
 
659
                existingGoodEntries.append(currData);
 
660
            } else {
 
661
                doomedData.append(currData);
 
662
            }
 
663
        }
 
664
 
 
665
        // purge old entries from the cache
 
666
        foreach (CacheEntry *doomedContent, doomedData) {
 
667
            // not contained in the updated cache.
 
668
            removeEntryFromNodeContent(n, doomedContent);
 
669
        }
 
670
 
 
671
        // add new entries to the cache
 
672
        foreach (CacheEntry *newEntry, c) {
 
673
            if (!existingGoodEntries.contains(newEntry)) {
 
674
                newCacheEntries.append(newEntry);
 
675
            }
 
676
        }
 
677
    } else {
 
678
        // new cache entry.
 
679
        newCacheEntries = c;
 
680
    }
 
681
 
 
682
    // populate the cache for the node n from the content c.
 
683
    foreach (CacheEntry *currData, newCacheEntries) {
 
684
        addEntryToNodeContent(n, currData);
 
685
    }
 
686
}
 
687
 
 
688
//----------------------------------------------------
 
689
 
 
690
/*!
 
691
    \qmltype SocialNetwork
 
692
    \instantiates SocialNetworkInterface
 
693
    \inqmlmodule org.nemomobile.social 1
 
694
    \brief Provides an abstraction API for graph- or model-based social
 
695
    network APIs.
 
696
 
 
697
    The SocialNetwork type should never be used directly by clients.
 
698
    Instead, clients should use specific implementations of the SocialNetwork
 
699
    interface, such as the Facebook adapter.
 
700
 
 
701
    The SocialNetwork type provides a generic API which allows content
 
702
    to be retrieved from a social network and exposed via a model.
 
703
    The API consists of a central \c node which is an IdentifiableContentItem,
 
704
    which may be specified by the client via the \c nodeIdentifier property.
 
705
    The data in the model will be populated from the graph connections of
 
706
    the node.
 
707
 
 
708
    The model roles are as follows:
 
709
    \list
 
710
    \li contentItem - the instantiated ContentItem related to the node
 
711
    \li contentItemType - the type of the ContentItem related to the node
 
712
    \li contentItemData - the underlying QVariantMap data of the ContentItem related to the node
 
713
    \li contentItemIdentifier - the identifier of the ContentItem related to the node, or an empty string
 
714
    \endlist
 
715
 
 
716
    Please see the documentation of the Facebook adapter for an example
 
717
    of how clients can use the SocialNetwork model in an application.
 
718
*/  
 
719
 
 
720
SocialNetworkInterface::SocialNetworkInterface(QObject *parent)
 
721
    : QAbstractListModel(parent), d(new SocialNetworkInterfacePrivate(this))
 
722
{
 
723
    d->headerData.insert(ContentItemRole, "contentItem");
 
724
    d->headerData.insert(ContentItemTypeRole, "contentItemType");
 
725
    d->headerData.insert(ContentItemDataRole, "contentItemData" );
 
726
    d->headerData.insert(ContentItemIdentifierRole, "contentItemIdentifier");
 
727
    setRoleNames(d->headerData);
 
728
}
 
729
 
 
730
SocialNetworkInterface::~SocialNetworkInterface()
 
731
{
 
732
    delete d;
 
733
}
 
734
 
 
735
 
 
736
void SocialNetworkInterface::classBegin()
 
737
{
 
738
    d->initialized = false;
 
739
}
 
740
 
 
741
void SocialNetworkInterface::componentComplete()
 
742
{
 
743
    // If you override this implementation, you MUST set d->initialized=true.
 
744
    d->initialized = true;
 
745
}
 
746
 
 
747
/*!
 
748
    \qmlmethod void SocialNetwork::nextNode()
 
749
    Navigates to the next node in the node stack, if it exists.
 
750
    The data in the model will be populated from the cache, if cached
 
751
    data for the node exists.  If you want to repopulate the data from
 
752
    the social network, you must call \c setNodeIdentifier() manually.
 
753
 
 
754
    The node stack is built up from successive changes to the
 
755
    \c nodeIdentifier property.
 
756
*/
 
757
void SocialNetworkInterface::nextNode()
 
758
{
 
759
    IdentifiableContentItemInterface *oldNode = d->currentNode();
 
760
    d->nextNode();
 
761
    IdentifiableContentItemInterface *newNode = d->currentNode();
 
762
    if (oldNode != newNode) {
 
763
        bool hasCachedContent = false;
 
764
        QList<CacheEntry*> data = d->cachedContent(newNode, &hasCachedContent);
 
765
        if (hasCachedContent) {
 
766
            // call derived class data update:
 
767
            //   perform filtering/sorting based on the defined stuff.
 
768
            //   and then emit dataChanged() etc signals.
 
769
            updateInternalData(data);
 
770
        } else {
 
771
            // call derived class data populate:
 
772
            //   d->populateCache() etc once it's finished retrieving.
 
773
            //   and then updateInternalData() itself.
 
774
            populateDataForNode(newNode);
 
775
        }
 
776
    }
 
777
}
 
778
 
 
779
/*!
 
780
    \qmlmethod void SocialNetwork::previousNode()
 
781
    Navigates to the previous node in the node stack, if it exists.
 
782
    The data in the model will be populated from the cache, if cached
 
783
    data for the node exists.  If you want to repopulate the data from
 
784
    the social network, you must call \c setNodeIdentifier() manually.
 
785
 
 
786
    The node stack is built up from successive changes to the
 
787
    \c nodeIdentifier property.
 
788
*/
 
789
void SocialNetworkInterface::previousNode()
 
790
{
 
791
    IdentifiableContentItemInterface *oldNode = d->currentNode();
 
792
    d->prevNode();
 
793
    IdentifiableContentItemInterface *newNode = d->currentNode();
 
794
    if (oldNode != newNode) {
 
795
        bool hasCachedContent = false;
 
796
        QList<CacheEntry*> data = d->cachedContent(newNode, &hasCachedContent);
 
797
        if (hasCachedContent) {
 
798
            // call derived class data update:
 
799
            //   perform filtering/sorting based on the defined stuff.
 
800
            //   and then emit dataChanged() etc signals.
 
801
            updateInternalData(data);
 
802
        } else {
 
803
            // call derived class data populate:
 
804
            //   d->populateCache() etc once it's finished retrieving.
 
805
            //   and then updateInternalData() itself.
 
806
            populateDataForNode(newNode);
 
807
        }
 
808
    }
 
809
}
 
810
 
 
811
/*!
 
812
    \qmlmethod QObject *SocialNetwork::relatedItem(int index)
 
813
    Returns the ContentItem which is related to the node from the given
 
814
    \a index of the model data.  This is identical to calling
 
815
    \c data() for the given model index and specifying the \c contentItem
 
816
    role.
 
817
 
 
818
    \note Although this function will always return a pointer to a
 
819
    ContentItem, the return type of the function is QObject*, so that
 
820
    this function can be used via QMetaObject::invokeMethod().
 
821
*/
 
822
QObject *SocialNetworkInterface::relatedItem(int index) const
 
823
{
 
824
    QVariant cv = data(QAbstractListModel::index(index), SocialNetworkInterface::ContentItemRole);
 
825
    if (!cv.isValid())
 
826
        return 0;
 
827
    ContentItemInterface *ci = cv.value<ContentItemInterface*>();
 
828
    
 
829
    return ci;
 
830
}
 
831
 
 
832
/*!
 
833
    \qmlproperty SocialNetwork::Status SocialNetwork::status
 
834
    Holds the current status of the social network.
 
835
*/
 
836
SocialNetworkInterface::Status SocialNetworkInterface::status() const
 
837
{
 
838
    return d->status;
 
839
}
 
840
 
 
841
/*!
 
842
    \qmlproperty SocialNetwork::ErrorType SocialNetwork::error
 
843
    Holds the most recent error which occurred during initialization
 
844
    or a network request.  Note that it will not be reset if subsequent
 
845
    operations are successful.
 
846
*/
 
847
SocialNetworkInterface::ErrorType SocialNetworkInterface::error() const
 
848
{
 
849
    return d->error;
 
850
}
 
851
 
 
852
/*!
 
853
    \qmlproperty QString SocialNetwork::errorMessage
 
854
    Holds the message associated with the most recent error which occurred
 
855
    during initialization or a network request.  Note that it will not be
 
856
    reset if subsequent operations are successful.
 
857
*/
 
858
QString SocialNetworkInterface::errorMessage() const
 
859
{
 
860
    return d->errorMessage;
 
861
}
 
862
 
 
863
/*!
 
864
    \qmlproperty QString SocialNetwork::nodeIdentifier
 
865
    Holds the identifier of the "central" content item.  This content item
 
866
    is the \c node of the current view of the social network graph.
 
867
    The data in the model will be populated from the graph connections
 
868
    to the node.
 
869
 
 
870
    If this property is not set, the node will be initialized to the
 
871
    current user node by the specific social network implementation adapter.
 
872
 
 
873
    As the client changes the \c nodeIdentifier, the SocialNetwork will
 
874
    request the related data from the network, and build up a node stack
 
875
    of visited nodes.  For each visited node, a cache of related content
 
876
    items (model data) is stored.  The size of the cache is implementation
 
877
    specific.
 
878
 
 
879
    Clients can later navigate down and up the stack using the \l previousNode() and
 
880
    \l nextNode() functions respectively.  Those operations are very cheap
 
881
    as they do not trigger any network requests in the common case.
 
882
 
 
883
    If the \c nodeIdentifier is set to an identifier which isn't represented
 
884
    in the node stack, the \c node property will be set to an empty placeholder
 
885
    node until the network request completes and the node can be populated with
 
886
    the downloaded data.
 
887
 
 
888
    If the \c nodeIdentifier is set to the identifier of the current node,
 
889
    the cached data for the node will be cleared and the node and its related
 
890
    data will be reloaded from the network.
 
891
*/
 
892
QString SocialNetworkInterface::nodeIdentifier() const
 
893
{
 
894
    if (d->pendingCurrentNodeIdentifier.isEmpty())
 
895
        return d->currentNodeIdentifier();  // normal case.
 
896
    return d->pendingCurrentNodeIdentifier; // status == Fetching, not sure if it's real yet.
 
897
}
 
898
 
 
899
void SocialNetworkInterface::setNodeIdentifier(const QString &contentItemIdentifier)
 
900
{
 
901
    IdentifiableContentItemInterface *cachedNode = d->findCachedNode(contentItemIdentifier);
 
902
    if (d->currentNode() && contentItemIdentifier == d->currentNode()->identifier()) {
 
903
        // resetting the current node.  This tells us to reload the node, clear its cache and repopulate.
 
904
        d->repopulatingCurrentNode = true;
 
905
        d->pendingCurrentNodeIdentifier = contentItemIdentifier;
 
906
        populateDataForNode(contentItemIdentifier); // "unseen node" without pushing placeholder.
 
907
    } else if (!cachedNode) {
 
908
        // Unseen node.
 
909
        // call derived class data populate:
 
910
        //   d->populateCache() etc once it's finished retrieving.
 
911
        //   d->pushNode(newNodePtr).
 
912
        //   and then updateInternalData() itself.
 
913
        d->pendingCurrentNodeIdentifier = contentItemIdentifier;
 
914
        d->pushPlaceHolderNode();
 
915
        emit nodeChanged();
 
916
        populateDataForNode(contentItemIdentifier); // XXX TODO: do we really want to trigger populate?  or wait for user to call populate?
 
917
    } else {
 
918
        // We've seen this node before and have it cached.
 
919
        bool hasCachedContent = false;
 
920
        QList<CacheEntry*> data = d->cachedContent(cachedNode, &hasCachedContent);
 
921
        if (hasCachedContent) {
 
922
            // call derived class data update:
 
923
            //   perform filtering/sorting based on the defined stuff.
 
924
            //   and then emit dataChanged() etc signals.
 
925
            d->pushNode(cachedNode);
 
926
            updateInternalData(data);
 
927
        } else {
 
928
            qWarning() << Q_FUNC_INFO << "Error: cached node has no cached content!";
 
929
        }
 
930
    }
 
931
}
 
932
 
 
933
/*!
 
934
    \qmlproperty IdentifiableContentItem *SocialNetwork::node
 
935
    Holds the "central" content item, or node, which defines the
 
936
    current view of the social network graph.  The data exposed in the
 
937
    SocialNetwork model will reflect the connections to the node.
 
938
 
 
939
    The node must be an identifiable content item (that is, it must
 
940
    have a unique identifier in the social network graph).
 
941
    Clients cannot set the node property directly, but instead must
 
942
    set the \c nodeIdentifier property.
 
943
*/
 
944
IdentifiableContentItemInterface *SocialNetworkInterface::node() const
 
945
{
 
946
    return d->currentNode();
 
947
}
 
948
 
 
949
/*!
 
950
    \qmlproperty QVariantMap SocialNetwork::relevanceCriteria
 
951
    Holds the social-network-specific relevance criteria which will
 
952
    be used to calculate the relevance of related content items.
 
953
    This relevance can be important in filtering and sorting operations.
 
954
*/
 
955
QVariantMap SocialNetworkInterface::relevanceCriteria() const
 
956
{
 
957
    return d->relevanceCriteria;
 
958
}
 
959
 
 
960
void SocialNetworkInterface::setRelevanceCriteria(const QVariantMap &rc)
 
961
{
 
962
    d->relevanceCriteria = rc;
 
963
}
 
964
 
 
965
/*!
 
966
    \qmlproperty QDeclarativeListProperty<Filter> SocialNetwork::filters
 
967
    Holds the list of filters which will be applied to the related content
 
968
    of the node.  Only those related content items which match each of the
 
969
    filters will be exposed as data in the model.
 
970
 
 
971
    Specific implementations of the SocialNetwork interface may not support
 
972
    certain standard filter types, or they may not support filtering at all.
 
973
*/
 
974
QDeclarativeListProperty<FilterInterface> SocialNetworkInterface::filters()
 
975
{
 
976
    return QDeclarativeListProperty<FilterInterface>(this, 0,
 
977
            &SocialNetworkInterfacePrivate::filters_append,
 
978
            &SocialNetworkInterfacePrivate::filters_count,
 
979
            &SocialNetworkInterfacePrivate::filters_at,
 
980
            &SocialNetworkInterfacePrivate::filters_clear);
 
981
}
 
982
 
 
983
/*!
 
984
    \qmlproperty QDeclarativeListProperty<Sorter> SocialNetwork::sorters
 
985
    Holds the list of sorters which will be applied to the related content
 
986
    of the node.  The order of sorters in the list is important, as it
 
987
    defines which sorting is applied first.
 
988
 
 
989
    Specific implementations of the SocialNetwork interface may not support
 
990
    certain standard sorter types, or they may not support sorting at all.
 
991
*/
 
992
QDeclarativeListProperty<SorterInterface> SocialNetworkInterface::sorters()
 
993
{
 
994
    return QDeclarativeListProperty<SorterInterface>(this, 0,
 
995
            &SocialNetworkInterfacePrivate::sorters_append,
 
996
            &SocialNetworkInterfacePrivate::sorters_count,
 
997
            &SocialNetworkInterfacePrivate::sorters_at,
 
998
            &SocialNetworkInterfacePrivate::sorters_clear);
 
999
}
 
1000
 
 
1001
/*!
 
1002
    \qmlproperty int SocialNetwork::count
 
1003
    Returns the number of content items related to the \c node
 
1004
    are exposed in the model.  Only those content items which
 
1005
    match the specified \c filters will be exposed in the model,
 
1006
    if the specific implementation of the SocialNetwork interface
 
1007
    supports every filter in the \c filters list.
 
1008
*/
 
1009
int SocialNetworkInterface::count() const
 
1010
{
 
1011
    return d->internalData.count();
 
1012
}
 
1013
 
 
1014
int SocialNetworkInterface::rowCount(const QModelIndex &index) const
 
1015
{
 
1016
    // only allow non-valid (default) parent index.
 
1017
    if (index.isValid())
 
1018
        return 0;
 
1019
    return d->internalData.count();
 
1020
}
 
1021
 
 
1022
int SocialNetworkInterface::columnCount(const QModelIndex &index) const
 
1023
{
 
1024
    // only allow non-valid (default) parent index.
 
1025
    if (index.isValid())
 
1026
        return 0;
 
1027
    return 1;
 
1028
}
 
1029
 
 
1030
QVariant SocialNetworkInterface::data(const QModelIndex &index, int role) const
 
1031
{
 
1032
    if (!index.isValid() || index.row() >= d->internalData.count() || index.row() < 0)
 
1033
        return QVariant();
 
1034
 
 
1035
    CacheEntry *cacheEntry = d->internalData.at(index.row());
 
1036
 
 
1037
    switch (role) {
 
1038
        case ContentItemTypeRole: return QVariant::fromValue(cacheEntry->data.value(NEMOQMLPLUGINS_SOCIAL_CONTENTITEMTYPE).toInt());
 
1039
        case ContentItemDataRole: return QVariant::fromValue(cacheEntry->data);
 
1040
        case ContentItemIdentifierRole: return QVariant::fromValue(cacheEntry->data.value(NEMOQMLPLUGINS_SOCIAL_CONTENTITEMID).toString());
 
1041
        case ContentItemRole: {
 
1042
            if (cacheEntry->item)
 
1043
                return QVariant::fromValue(cacheEntry->item);
 
1044
            // instantiate the item.
 
1045
            ContentItemInterface *newItem = contentItemFromData(const_cast<SocialNetworkInterface*>(this), cacheEntry->data);
 
1046
            d->updateCacheEntry(cacheEntry, newItem); // update the cache.
 
1047
            return QVariant::fromValue(newItem);
 
1048
        }
 
1049
        break;
 
1050
        default: return QVariant();
 
1051
    }
 
1052
}
 
1053
 
 
1054
QVariant SocialNetworkInterface::headerData(int section, Qt::Orientation orientation, int role) const
 
1055
{
 
1056
    // Not a table model, so perhaps this is wrong.
 
1057
 
 
1058
    if (orientation != Qt::Horizontal)
 
1059
        return QVariant();
 
1060
 
 
1061
    if (role == Qt::DisplayRole) {
 
1062
        if (section < d->headerData.size()) {
 
1063
            return d->headerData.value(section);
 
1064
        }
 
1065
    }
 
1066
 
 
1067
    return QVariant();
 
1068
}
 
1069
 
 
1070
/*!
 
1071
    \qmlmethod bool SocialNetwork::arbitraryRequest(SocialNetwork::RequestType requestType, const QString &requestUri, const QVariantMap &queryItems = QVariantMap(), const QString &postData = QString())
 
1072
 
 
1073
    Performs the HTTP request of the specified \a requestType (\c Get, \c Post or \c Delete) with
 
1074
    the specified \a requestUri and \a queryItems.  If the request is a Post request, the given
 
1075
    \a postData will be converted to a QByteArray via \c{QByteArray::fromBase64(postData.toLatin1())}
 
1076
    and used as the \c Post data.
 
1077
 
 
1078
    When a successfully started request is completed, the \c arbitraryRequestResponseReceived()
 
1079
    signal will be emitted, with the response data included as the \c data parameter.
 
1080
 
 
1081
    The request will not be started successfully if another arbitrary request is in progress.
 
1082
    Returns true if the request could be started successfully, false otherwise. 
 
1083
*/
 
1084
bool SocialNetworkInterface::arbitraryRequest(int requestType, const QString &requestUri, const QVariantMap &queryItems, const QString &postData)
 
1085
{
 
1086
    if (!d->arbitraryRequestHandler)
 
1087
        d->arbitraryRequestHandler = new ArbitraryRequestHandler(this);
 
1088
    return d->arbitraryRequestHandler->request(requestType, requestUri, queryItems, postData);
 
1089
}
 
1090
 
 
1091
QVariantMap SocialNetworkInterface::contentItemData(ContentItemInterface *contentItem) const
 
1092
{
 
1093
    // Helper function for SocialNetworkInterface-derived types
 
1094
    return contentItem->dataPrivate();
 
1095
}
 
1096
 
 
1097
void SocialNetworkInterface::setContentItemData(ContentItemInterface *contentItem, const QVariantMap &data) const
 
1098
{
 
1099
    // Helper function for SocialNetworkInterface-derived types
 
1100
    contentItem->setDataPrivate(data);
 
1101
}
 
1102
 
 
1103
bool SocialNetworkInterface::isInitialized() const
 
1104
{
 
1105
    // Helper function for ContentItemInterface
 
1106
    return d->initialized;
 
1107
}
 
1108
 
 
1109
/*
 
1110
    Specific implementations of the SocialNetwork interface MUST implement this
 
1111
    function.  It will be called to populate the model data as a filtered and
 
1112
    sorted view of the content items related to the specified node in the
 
1113
    social network.
 
1114
*/
 
1115
void SocialNetworkInterface::populate()
 
1116
{
 
1117
    qWarning() << Q_FUNC_INFO << "Error: this function MUST be implemented by derived types!";
 
1118
}
 
1119
 
 
1120
/*
 
1121
    Specific implementations of the SocialNetwork interface MUST implement this
 
1122
    function.  It must be implemented such that it performs the appropriate
 
1123
    get request to retrieve the data for the specified \c objectIdentifier, or
 
1124
    data related to that object according to the given \c extraPath parameter.
 
1125
    If possible, only the data specified by the \c whichFields parameter should
 
1126
    be retrieved, to minimise network usage.  The \c extraData parameter is
 
1127
    implementation specific, and may be used to modify the behaviour of the request.
 
1128
*/
 
1129
QNetworkReply *SocialNetworkInterface::getRequest(const QString &, const QString &, const QStringList &, const QVariantMap &)
 
1130
{
 
1131
    qWarning() << Q_FUNC_INFO << "Error: this function MUST be implemented by derived types!";
 
1132
    return 0;
 
1133
}
 
1134
 
 
1135
/*
 
1136
    Specific implementations of the SocialNetwork interface MUST implement this
 
1137
    function.  It must be implemented such that it performs the appropriate
 
1138
    post request to upload the \c data for the specified \c objectIdentifier, or
 
1139
    data related to that object according to the given \c extraPath parameter.
 
1140
    The \c extraData parameter is implementation specific, and may be used to
 
1141
    modify the behaviour of the request.
 
1142
*/
 
1143
QNetworkReply *SocialNetworkInterface::postRequest(const QString &, const QString &, const QVariantMap &, const QVariantMap &)
 
1144
{
 
1145
    qWarning() << Q_FUNC_INFO << "Error: this function MUST be implemented by derived types!";
 
1146
    return 0;
 
1147
}
 
1148
 
 
1149
/*
 
1150
    Specific implementations of the SocialNetwork interface MUST implement this
 
1151
    function.  It must be implemented such that it performs the appropriate
 
1152
    delete request to delete the object identified by the specified
 
1153
    \c objectIdentifier, or data related to that object according to the given
 
1154
    \c extraPath parameter.  The \c extraData parameter is implementation specific,
 
1155
    and may be used to modify the behaviour of the request.
 
1156
*/
 
1157
QNetworkReply *SocialNetworkInterface::deleteRequest(const QString &, const QString &, const QVariantMap &)
 
1158
{
 
1159
    qWarning() << Q_FUNC_INFO << "Error: this function MUST be implemented by derived types!";
 
1160
    return 0;
 
1161
}
 
1162
 
 
1163
/*
 
1164
    Specific implementations of the SocialNetwork interface MUST implement this
 
1165
    function.  It must return an instance of the correct ContentItem-derived type
 
1166
    given the QVariantMap of data.  This function is called when the \c contentItem
 
1167
    role for a specific model index is requested via the model data() function, to
 
1168
    instantiate the content item from the content item data lazily.
 
1169
*/
 
1170
ContentItemInterface *SocialNetworkInterface::contentItemFromData(QObject *, const QVariantMap &) const
 
1171
{
 
1172
    qWarning() << Q_FUNC_INFO << "Error: this function MUST be implemented by derived types!";
 
1173
    return 0;
 
1174
}
 
1175
 
 
1176
/*
 
1177
    Specific implementations of the SocialNetwork interface MUST implement this
 
1178
    function.  It must be implemented so that:
 
1179
    1) the provided data should have non-filters-matching-entries removed
 
1180
    2) the filtered data should then be sorted according to the sorters
 
1181
    3) the d->internalData list should be set
 
1182
    4) finally, dataChanged() and any other model signals should be emitted
 
1183
*/
 
1184
void SocialNetworkInterface::updateInternalData(QList<CacheEntry*>)
 
1185
{
 
1186
    qWarning() << Q_FUNC_INFO << "Error: this function MUST be implemented by derived types!";
 
1187
}
 
1188
 
 
1189
/*
 
1190
    Specific implementations of the SocialNetwork interface MUST implement this
 
1191
    function.  It must be implemented so that:
 
1192
    0) the current model data should be set to empty
 
1193
    1) the related content data should be requested from the service, according to the filters
 
1194
    2) when received, the related content data should be used to populate the cache via d->populateCache()
 
1195
    3) finally, updateInternalData() should be called, passing in the new cache data.
 
1196
*/
 
1197
void SocialNetworkInterface::populateDataForNode(IdentifiableContentItemInterface *)
 
1198
{
 
1199
    qWarning() << Q_FUNC_INFO << "Error: this function MUST be implemented by derived types!";
 
1200
}
 
1201
 
 
1202
/*
 
1203
    Specific implementations of the SocialNetwork interface MUST implement this
 
1204
    function.  It must be implemented so that:
 
1205
    0) the current model data should be set to empty
 
1206
    1) the given node is requested from the service
 
1207
    2) when received, the node should be pushed to the nodeStack via d->pushNode(n)
 
1208
    3) the related content data should be requested from the service, according to the filters
 
1209
    4) when received, the related content data should be used to populate the cache via d->populateCache()
 
1210
    5) finally, updateInternalData() should be called, passing in the new cache data.
 
1211
*/
 
1212
void SocialNetworkInterface::populateDataForNode(const QString &)
 
1213
{
 
1214
    qWarning() << Q_FUNC_INFO << "Error: this function MUST be implemented by derived types!";
 
1215
}
 
1216