Skip to content

Commit

Permalink
Merge branch 'jb35571' into 'master'
Browse files Browse the repository at this point in the history
[buteo-sync-plugins-social] Update Facebook API calls to v2.3 or v2.6. Contributes to JB#35571

This commit updates most Facebook API calls to v2.6, including ensuring
that all fields which we need are explicitly listed in the query, and
reading the photo source url by parsing the images list.

In one case (notifications) we still use v2.3 because after that
version the API was removed.

Contributes to JB#35571

See merge request !17
  • Loading branch information
chriadam committed Jul 11, 2016
2 parents 21e115e + a1b56b5 commit 25ca4f9
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 61 deletions.
85 changes: 42 additions & 43 deletions src/facebook/facebook-calendars/facebookcalendarsyncadaptor.cpp
Expand Up @@ -28,6 +28,8 @@
#include <QtCore/QJsonArray>
#include <QtSql/QSqlQuery>
#include <QtSql/QSqlError>
#include <QtNetwork/QHttpMultiPart>
#include <QtNetwork/QHttpPart>

#include <extendedcalendar.h>
#include <extendedstorage.h>
Expand Down Expand Up @@ -199,23 +201,21 @@ void FacebookCalendarSyncAdaptor::requestEvents(int accountId,
const QString &accessToken,
const QString &batchRequest)
{
QList<QPair<QString, QString> > queryItems;
queryItems.append(QPair<QString, QString>(QString(QLatin1String("access_token")), accessToken));

QString batch = batchRequest;
if (batch.isEmpty()) {
// Create batch query of following format:
// [{ "method":"GET","relative_url":"me/events/created?include_headers=false&limit=200"},
// { "method":"GET","relative_url":"me/events/attending?include_headers=false&limit=200"},
// { "method":"GET","relative_url":"me/events/maybe?include_headers=false&limit=200"},
// { "method":"GET","relative_url":"me/events/not_replied?include_headers=false&limit=200"}]
// [{ "method":"GET","relative_url":"me/events?type=created&include_headers=false&limit=200&fields=..."},
// { "method":"GET","relative_url":"me/events?type=attending&include_headers=false&limit=200&fields=..."},
// { "method":"GET","relative_url":"me/events?type=maybe&include_headers=false&limit=200&fields=..."},
// { "method":"GET","relative_url":"me/events?type=not_replied&include_headers=false&limit=200&fields=..."}]

int sinceSpan = m_accountSyncProfile
? m_accountSyncProfile->key(Buteo::KEY_SYNC_SINCE_DAYS_PAST, QStringLiteral("30")).toInt()
: 30;
uint startTime = QDateTime::currentDateTimeUtc().addDays(sinceSpan * -1).toTime_t();
QString since = QStringLiteral("since=") + QString::number(startTime);
QString calendarQuery = QStringLiteral("{\"method\":\"GET\",\"relative_url\":\"me/events/%1?fields=id,name,start_time,end_time,is_date_only,description,location&include_headers=false&limit=200&")
QString calendarQuery = QStringLiteral("{\"method\":\"GET\",\"relative_url\":\"me/events?type=%1&include_headers=false&limit=200&fields=id,name,start_time,end_time,description,place&")
+ since
+ QStringLiteral("\"}");

Expand All @@ -226,17 +226,27 @@ void FacebookCalendarSyncAdaptor::requestEvents(int accountId,
+ calendarQuery.arg(QStringLiteral("not_replied"))
+ QStringLiteral("]");
}
queryItems.append(QPair<QString, QString>(QString(QLatin1String("batch")), batch));

QUrl url(graphAPI());
QUrlQuery query(url);
query.setQueryItems(queryItems);
url.setQuery(query);
QNetworkRequest request(url);

SOCIALD_LOG_DEBUG("performing request:" << url.toString());
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
multiPart->setBoundary("-------Sska2129ifcalksmqq3");

QNetworkReply *reply = m_networkAccessManager->post(QNetworkRequest(url), QByteArray());
QHttpPart accessTokenPart;
accessTokenPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"access_token\""));
accessTokenPart.setBody(accessToken.toUtf8());

QHttpPart batchPart;
batchPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"batch\""));
batchPart.setBody(batch.toUtf8());

multiPart->append(accessTokenPart);
multiPart->append(batchPart);
request.setRawHeader("Content-Type", "multipart/form-data; boundary="+multiPart->boundary());
QNetworkReply *reply = m_networkAccessManager->post(request, multiPart);
if (reply) {
multiPart->setParent(reply);
reply->setProperty("accountId", accountId);
reply->setProperty("accessToken", accessToken);
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
Expand All @@ -251,6 +261,7 @@ void FacebookCalendarSyncAdaptor::requestEvents(int accountId,
}
setupReplyTimeout(accountId, reply);
} else {
delete multiPart;
SOCIALD_LOG_ERROR("unable to request events from Facebook account" << accountId);
}
}
Expand Down Expand Up @@ -343,39 +354,27 @@ void FacebookCalendarSyncAdaptor::finishedHandler()
// workaround for empty ET events
endTimeString = startTimeString;
}
parsedEvent.m_isDateOnly = dataMap.value(QLatin1String("is_date_only")).toBool();
parsedEvent.m_endExists = true;
if (!parsedEvent.m_isDateOnly) {
KDateTime parsedStartTime = KDateTime::fromString(startTimeString);
KDateTime parsedEndTime = KDateTime::fromString(endTimeString);

// Sometimes KDateTime cannot parse the timezone
// even if it should support it
// We are then doing it manually
if (parsedStartTime.isNull()) {
parsedStartTime = KDateTime::fromString(startTimeString,
QLatin1String("%Y-%m-%dT%H:%M:%S%z"));
}
if (parsedEndTime.isNull()) {
parsedEndTime = KDateTime::fromString(endTimeString,
QLatin1String("%Y-%m-%dT%H:%M:%S%z"));
}
parsedEvent.m_startTime = parsedStartTime.toLocalZone();
parsedEvent.m_endTime = parsedEndTime.toLocalZone();
} else {
// mkcal date-only event semantics:
// if a date-only event lasts only one day, set isAllDay to true, but don't set an end date.
// if a date-only event lasts multiple days, set isAllDay to true, and set an end date.
// Use ClockTime format, so that it doesn't get offset according to timezone.
parsedEvent.m_startTime = KDateTime(QLocale::c().toDate(startTimeString, QLatin1String("yyyy-MM-dd")), QTime(), KDateTime::ClockTime);
parsedEvent.m_endTime = KDateTime(QLocale::c().toDate(endTimeString, QLatin1String("yyyy-MM-dd")), QTime(), KDateTime::ClockTime);
if (parsedEvent.m_endTime == parsedEvent.m_startTime) {
parsedEvent.m_endExists = false; // single-day all day event; don't set endDt.
}

KDateTime parsedStartTime = KDateTime::fromString(startTimeString);
KDateTime parsedEndTime = KDateTime::fromString(endTimeString);

// Sometimes KDateTime cannot parse the timezone
// even if it should support it
// We are then doing it manually
if (parsedStartTime.isNull()) {
parsedStartTime = KDateTime::fromString(startTimeString,
QLatin1String("%Y-%m-%dT%H:%M:%S%z"));
}
if (parsedEndTime.isNull()) {
parsedEndTime = KDateTime::fromString(endTimeString,
QLatin1String("%Y-%m-%dT%H:%M:%S%z"));
}

parsedEvent.m_startTime = parsedStartTime.toLocalZone();
parsedEvent.m_endTime = parsedEndTime.toLocalZone();
parsedEvent.m_summary = dataMap.value(QLatin1String("name")).toString();
parsedEvent.m_description = dataMap.value(QLatin1String("description")).toString();
parsedEvent.m_location = dataMap.value(QLatin1String("location")).toString();
parsedEvent.m_location = dataMap.value(QLatin1String("place")).toObject().toVariantMap().value("name").toString();
m_parsedEvents[eventId] = parsedEvent;
}
}
Expand Down
37 changes: 26 additions & 11 deletions src/facebook/facebook-images/facebookimagesyncadaptor.cpp
Expand Up @@ -150,6 +150,13 @@ void FacebookImageSyncAdaptor::requestData(int accountId,
QUrlQuery query(url);
queryItems.append(QPair<QString, QString>(QString(QLatin1String("access_token")), accessToken));
queryItems.append(QPair<QString, QString>(QString(QLatin1String("limit")), QString(QLatin1String("2000"))));
if (fbAlbumId.isEmpty()) {
queryItems.append(QPair<QString, QString>(QString(QLatin1String("fields")),
QString(QLatin1String("id,from,name,created_time,updated_time,count"))));
} else {
queryItems.append(QPair<QString, QString>(QString(QLatin1String("fields")),
QString(QLatin1String("id,picture,source,images,width,height,created_time,updated_time,name"))));
}
query.setQueryItems(queryItems);
url.setQuery(query);
}
Expand Down Expand Up @@ -231,7 +238,7 @@ void FacebookImageSyncAdaptor::albumsFinishedHandler()
QString createdTimeStr = albumObject.value(QLatin1String("created_time")).toString();
QString updatedTimeStr = albumObject.value(QLatin1String("updated_time")).toString();
int imageCount = static_cast<int>(albumObject.value(QLatin1String("count")).toDouble());

SOCIALD_LOG_DEBUG("Got album information:" << userId << albumId << albumName << createdTimeStr << updatedTimeStr << imageCount);

// check to see whether we need to sync (any changes since last sync)
// Note that we also check if the image count is the same, since, when
Expand All @@ -257,7 +264,6 @@ void FacebookImageSyncAdaptor::albumsFinishedHandler()
// TODO: After successfully added an album, we should begin a new query to get the image
// information (based on cover image id).
requestData(accountId, accessToken, QString(), fbUserId, fbAlbumId);

}

// Perform a continuation request if required.
Expand Down Expand Up @@ -291,6 +297,7 @@ void FacebookImageSyncAdaptor::imagesFinishedHandler()
QJsonObject parsed = parseJsonObjectReplyData(replyData, &ok);
if (isError || !ok || !parsed.contains(QLatin1String("data"))) {
SOCIALD_LOG_ERROR("unable to read photos response for Facebook account with id" << accountId);
SOCIALD_LOG_DEBUG(replyData);
clearRemovalDetectionLists(); // don't perform server-side removal detection during this sync run.
decrementSemaphore(accountId);
return;
Expand All @@ -305,8 +312,8 @@ void FacebookImageSyncAdaptor::imagesFinishedHandler()
}

// read the photos information
foreach (const QJsonValue imageValue, data) {
QJsonObject imageObject = imageValue.toObject();
foreach (const QJsonValue &photoValue, data) {
QJsonObject imageObject = photoValue.toObject();
if (imageObject.isEmpty()) {
continue;
}
Expand All @@ -319,6 +326,10 @@ void FacebookImageSyncAdaptor::imagesFinishedHandler()
QString photoName = imageObject.value(QLatin1String("name")).toString();
int imageWidth = 0;
int imageHeight = 0;
if (photoId.isEmpty()) {
SOCIALD_LOG_ERROR("Unable to parse photo id from data:" << photoValue.toObject().toVariantMap());
continue;
}

// Find optimal thumbnail and image source urls based on dimensions.
QList<ImageSource> imageSources;
Expand All @@ -344,11 +355,11 @@ void FacebookImageSyncAdaptor::imagesFinishedHandler()
imageSrcUrl = img.sourceUrl;
}
}
if (!foundOptimalThumbnail) {
if (!foundOptimalThumbnail && imageSources.size()) {
// just choose the largest one.
thumbnailUrl = imageSources.last().sourceUrl;
}
if (!foundOptimalImage) {
if (!foundOptimalImage && imageSources.size()) {
// just choose the largest one.
imageSrcUrl = imageSources.last().sourceUrl;
imageWidth = imageSources.last().width;
Expand All @@ -362,12 +373,16 @@ void FacebookImageSyncAdaptor::imagesFinishedHandler()
}

// check if we need to sync, and write to the database.
if (haveAlreadyCachedImage(photoId, imageSrcUrl)) {
SOCIALD_LOG_DEBUG("have previously cached photo" << photoId << ":" << imageSrcUrl);
if (!imageSrcUrl.isEmpty()) {
if (haveAlreadyCachedImage(photoId, imageSrcUrl)) {
SOCIALD_LOG_DEBUG("have previously cached photo" << photoId << ":" << imageSrcUrl);
} else {
SOCIALD_LOG_DEBUG("caching new photo" << photoId << ":" << imageSrcUrl << "->" << imageWidth << "x" << imageHeight);
m_db.addImage(photoId, fbAlbumId, fbUserId, createdTime, updatedTime,
photoName, imageWidth, imageHeight, thumbnailUrl, imageSrcUrl);
}
} else {
SOCIALD_LOG_DEBUG("caching new photo" << photoId << ":" << imageSrcUrl << "->" << imageWidth << "x" << imageHeight);
m_db.addImage(photoId, fbAlbumId, fbUserId, createdTime, updatedTime,
photoName, imageWidth, imageHeight, thumbnailUrl, imageSrcUrl);
SOCIALD_LOG_ERROR("Cannot add photo to database:" << photoId << "- empty image source url!");
}
}
// perform a continuation request if required.
Expand Down
@@ -1,4 +1,4 @@
CONFIG += link_pkgconfig meegotouchevents-qt5
CONFIG += link_pkgconfig
PKGCONFIG += nemonotifications-qt5
SOURCES += $$PWD/facebooknotificationsyncadaptor.cpp
HEADERS += $$PWD/facebooknotificationsyncadaptor.h
Expand Down
Expand Up @@ -86,7 +86,7 @@ void FacebookNotificationSyncAdaptor::requestNotifications(int accountId, const
queryItems.append(QPair<QString, QString>(QString(QLatin1String("include_read")), QString(QLatin1String("true"))));
queryItems.append(QPair<QString, QString>(QString(QLatin1String("access_token")), accessToken));
queryItems.append(QPair<QString, QString>(QString(QLatin1String("locale")), QLocale::system().name()));
QUrl url(graphAPI(QLatin1String("/me/notifications")));
QUrl url(QLatin1String("https://graph.facebook.com/v2.3/me/notifications")); // removed after v2.3, cannot use in v2.6!
if (pagingToken.isEmpty()) {
QDateTime since = lastSuccessfulSyncTime(accountId);
if (!since.isValid()) {
Expand All @@ -103,6 +103,8 @@ void FacebookNotificationSyncAdaptor::requestNotifications(int accountId, const
queryItems.append(QPair<QString, QString>(QString(QLatin1String("until")), until));
queryItems.append(QPair<QString, QString>(QString(QLatin1String("__paging_token")), pagingToken));
}
queryItems.append(QPair<QString, QString>(QString(QLatin1String("fields")),
QString(QLatin1String("id,from,to,application,object,created_time,updated_time,title,link"))));

QUrlQuery query(url);
query.setQueryItems(queryItems);
Expand Down
2 changes: 1 addition & 1 deletion src/facebook/facebook-signon/facebooksignonsyncadaptor.cpp
Expand Up @@ -210,7 +210,7 @@ void FacebookSignonSyncAdaptor::lowerCredentialsNeedUpdateFlag(int accountId)
{
Accounts::Account *acc = loadAccount(accountId);
if (acc) {
SOCIALD_LOG_ERROR("FBSSA: lowering CredentialsNeedUpdate flag");
SOCIALD_LOG_INFO("FBSSA: lowering CredentialsNeedUpdate flag");
Accounts::Service srv = m_accountManager.service(syncServiceName());
acc->selectService(srv);
acc->setValue(QStringLiteral("CredentialsNeedUpdate"), QVariant::fromValue<bool>(false));
Expand Down
2 changes: 1 addition & 1 deletion src/google/google-signon/googlesignonsyncadaptor.cpp
Expand Up @@ -113,7 +113,7 @@ void GoogleSignonSyncAdaptor::lowerCredentialsNeedUpdateFlag(int accountId)
{
Accounts::Account *acc = loadAccount(accountId);
if (acc) {
SOCIALD_LOG_ERROR("GSSA: lowering CredentialsNeedUpdate flag");
SOCIALD_LOG_INFO("GSSA: lowering CredentialsNeedUpdate flag");
Accounts::Service srv = m_accountManager.service(syncServiceName());
acc->selectService(srv);
acc->setValue(QStringLiteral("CredentialsNeedUpdate"), QVariant::fromValue<bool>(false));
Expand Down
@@ -1,4 +1,4 @@
CONFIG += link_pkgconfig meegotouchevents-qt5
CONFIG += link_pkgconfig
PKGCONFIG += nemonotifications-qt5
SOURCES += $$PWD/twitternotificationsyncadaptor.cpp
HEADERS += $$PWD/twitternotificationsyncadaptor.h
Expand Down
2 changes: 1 addition & 1 deletion src/twitter/twitter-posts/twitter-posts.pri
@@ -1,4 +1,4 @@
CONFIG += link_pkgconfig meegotouchevents-qt5
CONFIG += link_pkgconfig
PKGCONFIG += nemonotifications-qt5
SOURCES += $$PWD/twitterhometimelinesyncadaptor.cpp
HEADERS += $$PWD/twitterhometimelinesyncadaptor.h
Expand Down
2 changes: 1 addition & 1 deletion src/vk/vk-notifications/vk-notifications.pri
@@ -1,4 +1,4 @@
CONFIG += link_pkgconfig meegotouchevents-qt5
CONFIG += link_pkgconfig
PKGCONFIG += nemonotifications-qt5
SOURCES += $$PWD/vknotificationsyncadaptor.cpp
HEADERS += $$PWD/vknotificationsyncadaptor.h
Expand Down

0 comments on commit 25ca4f9

Please sign in to comment.