diff --git a/resources/bell.png b/resources/bell.png index 1947cde8cd2ef..d96a82bc3d25d 100644 Binary files a/resources/bell.png and b/resources/bell.png differ diff --git a/src/common/utility.cpp b/src/common/utility.cpp index 29308ea844d81..30d17c8bab73f 100644 --- a/src/common/utility.cpp +++ b/src/common/utility.cpp @@ -431,27 +431,38 @@ QString Utility::timeAgoInWords(const QDateTime &dt, const QDateTime &from) now = from; } - if (dt.daysTo(now) > 0) { - int dtn = dt.daysTo(now); - return QObject::tr("%n day(s) ago", "", dtn); + if (dt.daysTo(now) == 1) { + return QObject::tr("%n day ago", "", dt.daysTo(now)); + } else if (dt.daysTo(now) > 1) { + return QObject::tr("%n days ago", "", dt.daysTo(now)); } else { qint64 secs = dt.secsTo(now); if (secs < 0) { return QObject::tr("in the future"); } + if (floor(secs / 3600.0) > 0) { int hours = floor(secs / 3600.0); - return (QObject::tr("%n hour(s) ago", "", hours)); + if(hours == 1){ + return (QObject::tr("%n hour ago", "", hours)); + } else { + return (QObject::tr("%n hours ago", "", hours)); + } } else { int minutes = qRound(secs / 60.0); + if (minutes == 0) { if (secs < 5) { return QObject::tr("now"); } else { return QObject::tr("Less than a minute ago"); } + + } else if(minutes == 1){ + return (QObject::tr("%n minute ago", "", minutes)); + } else { + return (QObject::tr("%n minutes ago", "", minutes)); } - return (QObject::tr("%n minute(s) ago", "", minutes)); } } return QObject::tr("Some time ago"); diff --git a/src/gui/accountstate.cpp b/src/gui/accountstate.cpp index a4b44b6a74537..64b603e859bab 100644 --- a/src/gui/accountstate.cpp +++ b/src/gui/accountstate.cpp @@ -35,6 +35,7 @@ AccountState::AccountState(AccountPtr account) , _connectionStatus(ConnectionValidator::Undefined) , _waitingForNewCredentials(false) , _maintenanceToConnectedDelay(60000 + (qrand() % (4 * 60000))) // 1-5min delay + , _notificationsEtagResponseHeader("*") { qRegisterMetaType("AccountState*"); @@ -176,6 +177,16 @@ void AccountState::tagLastSuccessfullETagRequest() _timeSinceLastETagCheck.start(); } +QByteArray AccountState::notificationsEtagResponseHeader() const +{ + return _notificationsEtagResponseHeader; +} + +void AccountState::setNotificationsEtagResponseHeader(const QByteArray &value) +{ + _notificationsEtagResponseHeader = value; +} + void AccountState::checkConnectivity() { if (isSignedOut() || _waitingForNewCredentials) { diff --git a/src/gui/accountstate.h b/src/gui/accountstate.h index 0ee6ec96a0bb5..0aca22ce2d433 100644 --- a/src/gui/accountstate.h +++ b/src/gui/accountstate.h @@ -131,6 +131,16 @@ class AccountState : public QObject, public QSharedData */ void tagLastSuccessfullETagRequest(); + /** Saves the ETag Response header from the last Notifications api + * request with statusCode 200. + */ + QByteArray notificationsEtagResponseHeader() const; + + /** Returns the ETag Response header from the last Notifications api + * request with statusCode 200. + */ + void setNotificationsEtagResponseHeader(const QByteArray &value); + public slots: /// Triggers a ping to the server to update state and /// connection status and errors. @@ -157,6 +167,7 @@ protected Q_SLOTS: bool _waitingForNewCredentials; QElapsedTimer _timeSinceLastETagCheck; QPointer _connectionValidator; + QByteArray _notificationsEtagResponseHeader; /** * Starts counting when the server starts being back up after 503 or diff --git a/src/gui/activitylistmodel.cpp b/src/gui/activitylistmodel.cpp index 33e57690d06b0..6684fed2aab96 100644 --- a/src/gui/activitylistmodel.cpp +++ b/src/gui/activitylistmodel.cpp @@ -53,14 +53,16 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const switch (role) { case ActivityItemDelegate::PathRole: - list = FolderMan::instance()->findFileInLocalFolders(a._file, ast->account()); - if (list.count() > 0) { - return QVariant(list.at(0)); - } - // File does not exist anymore? Let's try to open its path - list = FolderMan::instance()->findFileInLocalFolders(QFileInfo(a._file).path(), ast->account()); - if (list.count() > 0) { - return QVariant(list.at(0)); + if(!a._file.isEmpty()){ + list = FolderMan::instance()->findFileInLocalFolders(a._file, ast->account()); + if (list.count() > 0) { + return QVariant(list.at(0)); + } + // File does not exist anymore? Let's try to open its path + list = FolderMan::instance()->findFileInLocalFolders(QFileInfo(a._file).path(), ast->account()); + if (list.count() > 0) { + return QVariant(list.at(0)); + } } return QVariant(); break; diff --git a/src/gui/activitywidget.cpp b/src/gui/activitywidget.cpp index 8a29d9152fd40..c654072360bd5 100644 --- a/src/gui/activitywidget.cpp +++ b/src/gui/activitywidget.cpp @@ -37,6 +37,7 @@ #include "servernotificationhandler.h" #include "theme.h" #include "ocsjob.h" +#include "configfile.h" #include "ui_activitywidget.h" @@ -303,6 +304,16 @@ void ActivityWidget::slotBuildNotificationDisplay(const ActivityList &list) } } _guiLoggedNotifications.insert(activity._id); + + // Assemble a tray notification for the NEW notification + ConfigFile cfg; + if(cfg.optionalServerNotifications()){ + if(AccountManager::instance()->accounts().count() == 1){ + emit guiLog(activity._subject, ""); + } else { + emit guiLog(activity._subject, activity._accName); + } + } } } @@ -339,31 +350,9 @@ void ActivityWidget::slotBuildNotificationDisplay(const ActivityList &list) checkActivityTabVisibility(); - int newGuiLogCount = accNotified.count(); - - if (newGuiLogCount > 0) { + if (newNotificationShown) { // restart the gui log timer now that we show a notification _guiLogTimer.start(); - - // Assemble a tray notification - QString msg = tr("You received %n new notification(s) from %2.", "", accNotified[accNotified.keys().at(0)]).arg(accNotified.keys().at(0)); - - if (newGuiLogCount >= 2) { - QString acc1 = accNotified.keys().at(0); - QString acc2 = accNotified.keys().at(1); - if (newGuiLogCount == 2) { - int notiCount = accNotified[acc1] + accNotified[acc2]; - msg = tr("You received %n new notification(s) from %1 and %2.", "", notiCount).arg(acc1, acc2); - } else { - msg = tr("You received new notifications from %1, %2 and other accounts.").arg(acc1, acc2); - } - } - - const QString log = tr("%1 Notifications - Action Required").arg(Theme::instance()->appNameGUI()); - emit guiLog(log, msg); - } - - if (newNotificationShown) { emit newNotification(); } } @@ -615,6 +604,7 @@ void ActivitySettings::slotCopyToClipboard() } QApplication::clipboard()->setText(text); + emit guiLog(tr("Copied to clipboard"), message); } diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index f0664d2185935..6d3d35a813cc4 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -409,7 +409,9 @@ void Folder::createGuiLog(const QString &filename, LogStatus status, int count, } if (!text.isEmpty()) { - logger->postOptionalGuiLog(tr("Sync Activity"), text); + // Ignores the settings in case of an error or conflict + if(status == LogStatusError || status == LogStatusConflict) + logger->postOptionalGuiLog(tr("Sync Activity"), text); } } } diff --git a/src/gui/generalsettings.cpp b/src/gui/generalsettings.cpp index 69baaa84be4f4..419b5214b1ec0 100644 --- a/src/gui/generalsettings.cpp +++ b/src/gui/generalsettings.cpp @@ -42,8 +42,10 @@ GeneralSettings::GeneralSettings(QWidget *parent) { _ui->setupUi(this); - connect(_ui->desktopNotificationsCheckBox, &QAbstractButton::toggled, - this, &GeneralSettings::slotToggleOptionalDesktopNotifications); + connect(_ui->serverNotificationsCheckBox, &QAbstractButton::toggled, + this, &GeneralSettings::slotToggleOptionalServerNotifications); + _ui->serverNotificationsCheckBox->setToolTip(tr("Server notifications that require attention.")); + connect(_ui->showInExplorerNavigationPaneCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::slotShowInExplorerNavigationPane); _ui->autostartCheckBox->setChecked(Utility::hasLaunchOnStartup(Theme::instance()->appName())); @@ -114,7 +116,7 @@ void GeneralSettings::loadMiscSettings() QScopedValueRollback scope(_currentlyLoading, true); ConfigFile cfgFile; _ui->monoIconsCheckBox->setChecked(cfgFile.monoIcons()); - _ui->desktopNotificationsCheckBox->setChecked(cfgFile.optionalDesktopNotifications()); + _ui->serverNotificationsCheckBox->setChecked(cfgFile.optionalServerNotifications()); _ui->showInExplorerNavigationPaneCheckBox->setChecked(cfgFile.showInExplorerNavigationPane()); _ui->crashreporterCheckBox->setChecked(cfgFile.crashReporter()); auto newFolderLimit = cfgFile.newBigFolderSizeLimit(); @@ -165,10 +167,10 @@ void GeneralSettings::slotToggleLaunchOnStartup(bool enable) Utility::setLaunchOnStartup(theme->appName(), theme->appNameGUI(), enable); } -void GeneralSettings::slotToggleOptionalDesktopNotifications(bool enable) +void GeneralSettings::slotToggleOptionalServerNotifications(bool enable) { ConfigFile cfgFile; - cfgFile.setOptionalDesktopNotifications(enable); + cfgFile.setOptionalServerNotifications(enable); } void GeneralSettings::slotShowInExplorerNavigationPane(bool checked) diff --git a/src/gui/generalsettings.h b/src/gui/generalsettings.h index 4e92337e632b2..74c6520f2960b 100644 --- a/src/gui/generalsettings.h +++ b/src/gui/generalsettings.h @@ -42,7 +42,7 @@ class GeneralSettings : public QWidget private slots: void saveMiscSettings(); void slotToggleLaunchOnStartup(bool); - void slotToggleOptionalDesktopNotifications(bool); + void slotToggleOptionalServerNotifications(bool); void slotShowInExplorerNavigationPane(bool); void slotUpdateInfo(); void slotIgnoreFilesEditor(); diff --git a/src/gui/generalsettings.ui b/src/gui/generalsettings.ui index aacce23c2a317..abfdb92a91c1a 100644 --- a/src/gui/generalsettings.ui +++ b/src/gui/generalsettings.ui @@ -20,27 +20,27 @@ General Settings - - + + + + For System Tray + - &Launch on System Startup + Use &Monochrome Icons - - + + - Show &Desktop Notifications + &Launch on System Startup - - - - For System Tray - + + - Use &Monochrome Icons + Show Server &Notifications @@ -253,7 +253,7 @@ autostartCheckBox - desktopNotificationsCheckBox + serverNotificationsCheckBox monoIconsCheckBox ignoredFilesButton newFolderLimitCheckBox diff --git a/src/gui/notificationwidget.cpp b/src/gui/notificationwidget.cpp index cd879451e0ad3..e84ad557f7c47 100644 --- a/src/gui/notificationwidget.cpp +++ b/src/gui/notificationwidget.cpp @@ -16,6 +16,7 @@ #include "QProgressIndicator.h" #include "common/utility.h" #include "common/asserts.h" +#include "guiutility.h" #include @@ -40,7 +41,6 @@ void NotificationWidget::setActivity(const Activity &activity) _accountName = activity._accName; ASSERT(!_accountName.isEmpty()); - // _ui._headerLabel->setText( ); _ui._subjectLabel->setVisible(!activity._subject.isEmpty()); _ui._messageLabel->setVisible(!activity._message.isEmpty()); @@ -48,11 +48,11 @@ void NotificationWidget::setActivity(const Activity &activity) _ui._messageLabel->setText(activity._message); _ui._notifIcon->setPixmap(QPixmap(":/client/resources/bell.png")); - _ui._notifIcon->setMinimumWidth(64); - _ui._notifIcon->setMinimumHeight(64); + _ui._notifIcon->setMinimumWidth(22); + _ui._notifIcon->setMinimumHeight(22); _ui._notifIcon->show(); - QString tText = tr("Created at %1").arg(Utility::timeAgoInWords(activity._dateTime)); + QString tText = tr("%1").arg(Utility::timeAgoInWords(activity._dateTime)); _ui._timeLabel->setText(tText); // always remove the buttons @@ -61,8 +61,18 @@ void NotificationWidget::setActivity(const Activity &activity) } _buttons.clear(); + // open the notification in the browser if there is a link + if(!_myActivity._link.isEmpty()){ + QString buttonText(tr("More information")); + QPushButton *openBrowser = _ui._buttonBox->addButton(buttonText, QDialogButtonBox::AcceptRole); + openBrowser->setDefault(true); + connect(openBrowser, &QAbstractButton::clicked, this, &NotificationWidget::slotOpenBrowserButtonClicked); + _buttons.prepend(openBrowser); + } + // display buttons for the links if (activity._links.isEmpty()) { + // is there any case where this code is executed? // in case there is no action defined, do a close button. QPushButton *b = _ui._buttonBox->addButton(QDialogButtonBox::Close); b->setDefault(true); @@ -83,6 +93,11 @@ Activity NotificationWidget::activity() const return _myActivity; } +void NotificationWidget::slotOpenBrowserButtonClicked(){ + QUrl url(_myActivity._link); + Utility::openBrowser(url, this); +} + void NotificationWidget::slotButtonClicked() { QObject *buttonWidget = QObject::sender(); @@ -97,6 +112,10 @@ void NotificationWidget::slotButtonClicked() _buttons.at(i)->setEnabled(false); } + // there is an extra button: 'Open' + if(!_myActivity._link.isEmpty()) + index--; + // if the button was found, the link must be called if (index > -1 && _myActivity._links.count() == 0) { // no links, that means it was the close button diff --git a/src/gui/notificationwidget.h b/src/gui/notificationwidget.h index ce0e95f2b586d..04c5665c94951 100644 --- a/src/gui/notificationwidget.h +++ b/src/gui/notificationwidget.h @@ -46,6 +46,7 @@ public slots: private slots: void slotButtonClicked(); + void slotOpenBrowserButtonClicked(); private: Ui_NotificationWidget _ui; diff --git a/src/gui/notificationwidget.ui b/src/gui/notificationwidget.ui index 7f34baab5eccb..fc71d30611138 100644 --- a/src/gui/notificationwidget.ui +++ b/src/gui/notificationwidget.ui @@ -20,42 +20,88 @@ Form + + QLayout::SetMaximumSize + + + 24 + 0 + + 26 + 0 - - - - - 0 - 0 - + + 0 + + + 2 + + + + + 6 - - + + 5 - - ../../../../resources/bell.png + + 5 - - - - - - - - 0 - 0 - + + + 6 - - Lorem ipsum dolor sit amet + + 6 - + + + + + 0 + 0 + + + + 0 + + + + + + ../../../../resources/bell.png + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + 0 + + + + + + + + 0 + 0 + + + + Lorem ipsum dolor sit amet + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + @@ -71,13 +117,41 @@ false + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + true + + 0 + + + 28 + + + 6 + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 28 + 20 + + + + @@ -89,7 +163,7 @@ TextLabel - Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter @@ -104,19 +178,6 @@ - - - - QFrame::HLine - - - QFrame::Raised - - - 4 - - - diff --git a/src/gui/owncloudgui.cpp b/src/gui/owncloudgui.cpp index 10be7f430e302..135f93c78f788 100644 --- a/src/gui/owncloudgui.cpp +++ b/src/gui/owncloudgui.cpp @@ -16,7 +16,6 @@ #include "owncloudgui.h" #include "theme.h" #include "folderman.h" -#include "configfile.h" #include "progressdispatcher.h" #include "owncloudsetupwizard.h" #include "sharedialog.h" @@ -682,10 +681,7 @@ void ownCloudGui::slotShowTrayMessage(const QString &title, const QString &msg) void ownCloudGui::slotShowOptionalTrayMessage(const QString &title, const QString &msg) { - ConfigFile cfg; - if (cfg.optionalDesktopNotifications()) { - slotShowTrayMessage(title, msg); - } + slotShowTrayMessage(title, msg); } diff --git a/src/gui/servernotificationhandler.cpp b/src/gui/servernotificationhandler.cpp index fdf7b4dbf73c1..4368db6127bcb 100644 --- a/src/gui/servernotificationhandler.cpp +++ b/src/gui/servernotificationhandler.cpp @@ -24,7 +24,10 @@ namespace OCC { Q_LOGGING_CATEGORY(lcServerNotification, "gui.servernotification", QtInfoMsg) -const QString notificationsPath = QLatin1String("ocs/v2.php/apps/notifications/api/v1/notifications"); +const QString notificationsPath = QLatin1String("ocs/v2.php/apps/notifications/api/v2/notifications"); +const char propertyAccountStateC[] = "oc_account_state"; +const int successStatusCode = 200; +const int notModifiedStatusCode = 304; ServerNotificationHandler::ServerNotificationHandler(QObject *parent) : QObject(parent) @@ -52,22 +55,38 @@ void ServerNotificationHandler::slotFetchNotifications(AccountState *ptr) _notificationJob = new JsonApiJob(ptr->account(), notificationsPath, this); QObject::connect(_notificationJob.data(), &JsonApiJob::jsonReceived, this, &ServerNotificationHandler::slotNotificationsReceived); - _notificationJob->setProperty("AccountStatePtr", QVariant::fromValue(ptr)); - + QObject::connect(_notificationJob.data(), &JsonApiJob::etagResponseHeaderReceived, + this, &ServerNotificationHandler::slotEtagResponseHeaderReceived); + _notificationJob->setProperty(propertyAccountStateC, QVariant::fromValue(ptr)); + _notificationJob->addRawHeader("If-None-Match", ptr->notificationsEtagResponseHeader()); _notificationJob->start(); } +void ServerNotificationHandler::slotEtagResponseHeaderReceived(const QByteArray &value, int statusCode){ + if(statusCode == successStatusCode){ + qCWarning(lcServerNotification) << "New Notification ETag Response Header received " << value; + AccountState *account = qvariant_cast(sender()->property(propertyAccountStateC)); + account->setNotificationsEtagResponseHeader(value); + } +} + void ServerNotificationHandler::slotNotificationsReceived(const QJsonDocument &json, int statusCode) { - if (statusCode != 200) { + if (statusCode != successStatusCode && statusCode != notModifiedStatusCode) { qCWarning(lcServerNotification) << "Notifications failed with status code " << statusCode; deleteLater(); return; } + if (statusCode == notModifiedStatusCode) { + qCWarning(lcServerNotification) << "Status code " << statusCode << " Not Modified - No new notifications."; + deleteLater(); + return; + } + auto notifies = json.object().value("ocs").toObject().value("data").toArray(); - AccountState *ai = qvariant_cast(sender()->property("AccountStatePtr")); + AccountState *ai = qvariant_cast(sender()->property(propertyAccountStateC)); ActivityList list; @@ -79,9 +98,15 @@ void ServerNotificationHandler::slotNotificationsReceived(const QJsonDocument &j a._id = json.value("notification_id").toInt(); a._subject = json.value("subject").toString(); a._message = json.value("message").toString(); + QString s = json.value("link").toString(); if (!s.isEmpty()) { - a._link = QUrl(s); + QUrl link(s); + if(link.host().isEmpty()){ + link.setScheme(ai->account()->url().scheme()); + link.setHost(ai->account()->url().host()); + } + a._link = link; } a._dateTime = QDateTime::fromString(json.value("datetime").toString(), Qt::ISODate); diff --git a/src/gui/servernotificationhandler.h b/src/gui/servernotificationhandler.h index 875c3189ef9bb..60f530c076f0c 100644 --- a/src/gui/servernotificationhandler.h +++ b/src/gui/servernotificationhandler.h @@ -37,6 +37,7 @@ public slots: private slots: void slotNotificationsReceived(const QJsonDocument &json, int statusCode); + void slotEtagResponseHeaderReceived(const QByteArray &value, int statusCode); private: QPointer _notificationJob; diff --git a/src/libsync/configfile.cpp b/src/libsync/configfile.cpp index 295fafc17154b..25248bbdf6a02 100644 --- a/src/libsync/configfile.cpp +++ b/src/libsync/configfile.cpp @@ -53,7 +53,7 @@ static const char notificationRefreshIntervalC[] = "notificationRefreshInterval" static const char monoIconsC[] = "monoIcons"; static const char promptDeleteC[] = "promptDeleteAllFiles"; static const char crashReporterC[] = "crashReporter"; -static const char optionalDesktopNoficationsC[] = "optionalDesktopNotifications"; +static const char optionalServerNotificationsC[] = "optionalServerNotifications"; static const char showInExplorerNavigationPaneC[] = "showInExplorerNavigationPane"; static const char skipUpdateCheckC[] = "skipUpdateCheck"; static const char updateCheckIntervalC[] = "updateCheckInterval"; @@ -121,10 +121,10 @@ bool ConfigFile::setConfDir(const QString &value) return false; } -bool ConfigFile::optionalDesktopNotifications() const +bool ConfigFile::optionalServerNotifications() const { QSettings settings(configFile(), QSettings::IniFormat); - return settings.value(QLatin1String(optionalDesktopNoficationsC), true).toBool(); + return settings.value(QLatin1String(optionalServerNotificationsC), true).toBool(); } bool ConfigFile::showInExplorerNavigationPane() const @@ -177,10 +177,10 @@ quint64 ConfigFile::targetChunkUploadDuration() const return settings.value(QLatin1String(targetChunkUploadDurationC), 60 * 1000).toLongLong(); // default to 1 minute } -void ConfigFile::setOptionalDesktopNotifications(bool show) +void ConfigFile::setOptionalServerNotifications(bool show) { QSettings settings(configFile(), QSettings::IniFormat); - settings.setValue(QLatin1String(optionalDesktopNoficationsC), show); + settings.setValue(QLatin1String(optionalServerNotificationsC), show); settings.sync(); } diff --git a/src/libsync/configfile.h b/src/libsync/configfile.h index c4c66ac1a84ee..f414322a9ac2d 100644 --- a/src/libsync/configfile.h +++ b/src/libsync/configfile.h @@ -120,8 +120,8 @@ class OWNCLOUDSYNC_EXPORT ConfigFile static bool setConfDir(const QString &value); - bool optionalDesktopNotifications() const; - void setOptionalDesktopNotifications(bool show); + bool optionalServerNotifications() const; + void setOptionalServerNotifications(bool show); bool showInExplorerNavigationPane() const; void setShowInExplorerNavigationPane(bool show); diff --git a/src/libsync/networkjobs.cpp b/src/libsync/networkjobs.cpp index 193002a7471c9..00e10bc0e2b94 100644 --- a/src/libsync/networkjobs.cpp +++ b/src/libsync/networkjobs.cpp @@ -49,6 +49,7 @@ Q_LOGGING_CATEGORY(lcMkColJob, "sync.networkjob.mkcol", QtInfoMsg) Q_LOGGING_CATEGORY(lcProppatchJob, "sync.networkjob.proppatch", QtInfoMsg) Q_LOGGING_CATEGORY(lcJsonApiJob, "sync.networkjob.jsonapi", QtInfoMsg) Q_LOGGING_CATEGORY(lcDetermineAuthTypeJob, "sync.networkjob.determineauthtype", QtInfoMsg) +const int notModifiedStatusCode = 304; RequestEtagJob::RequestEtagJob(AccountPtr account, const QString &path, QObject *parent) : AbstractNetworkJob(account, path, parent) @@ -789,14 +790,18 @@ void JsonApiJob::addQueryParams(const QUrlQuery ¶ms) _additionalParams = params; } +void JsonApiJob::addRawHeader(const QByteArray &headerName, const QByteArray &value) +{ + _request.setRawHeader(headerName, value); +} + void JsonApiJob::start() { - QNetworkRequest req; - req.setRawHeader("OCS-APIREQUEST", "true"); + addRawHeader("OCS-APIREQUEST", "true"); auto query = _additionalParams; query.addQueryItem(QLatin1String("format"), QLatin1String("json")); QUrl url = Utility::concatUrlPath(account()->url(), path(), query); - sendRequest("GET", url, req); + sendRequest("GET", url, _request); AbstractNetworkJob::start(); } @@ -807,9 +812,9 @@ bool JsonApiJob::finished() << (reply()->error() == QNetworkReply::NoError ? QLatin1String("") : errorString()); int statusCode = 0; - + int httpStatusCode = reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (reply()->error() != QNetworkReply::NoError) { - qCWarning(lcJsonApiJob) << "Network error: " << path() << errorString() << reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute); + qCWarning(lcJsonApiJob) << "Network error: " << path() << errorString() << httpStatusCode; emit jsonReceived(QJsonDocument(), statusCode); return true; } @@ -821,7 +826,9 @@ bool JsonApiJob::finished() // this is a error message coming back from ocs. statusCode = rex.cap(1).toInt(); } - + } else if(jsonStr.isEmpty() && httpStatusCode == notModifiedStatusCode){ + qCWarning(lcJsonApiJob) << "Nothing changed so nothing to retrieve - status code: " << httpStatusCode; + statusCode = httpStatusCode; } else { QRegExp rex("\"statuscode\":(\\d+),"); // example: "{"ocs":{"meta":{"status":"ok","statuscode":100,"message":null},"data":{"version":{"major":8,"minor":"... (504) @@ -830,10 +837,14 @@ bool JsonApiJob::finished() } } + // save new ETag value + if(reply()->rawHeaderList().contains("ETag")) + emit etagResponseHeaderReceived(reply()->rawHeader("ETag"), statusCode); + QJsonParseError error; auto json = QJsonDocument::fromJson(jsonStr.toUtf8(), &error); - // empty or invalid response - if (error.error != QJsonParseError::NoError || json.isNull()) { + // empty or invalid response and status code is != 304 because jsonStr is expected to be empty + if ((error.error != QJsonParseError::NoError || json.isNull()) && httpStatusCode != notModifiedStatusCode) { qCWarning(lcJsonApiJob) << "invalid JSON!" << jsonStr << error.errorString(); emit jsonReceived(json, statusCode); return true; diff --git a/src/libsync/networkjobs.h b/src/libsync/networkjobs.h index 994f2046c606d..853c1db98ebe8 100644 --- a/src/libsync/networkjobs.h +++ b/src/libsync/networkjobs.h @@ -341,6 +341,7 @@ class OWNCLOUDSYNC_EXPORT JsonApiJob : public AbstractNetworkJob * This function needs to be called before start() obviously. */ void addQueryParams(const QUrlQuery ¶ms); + void addRawHeader(const QByteArray &headerName, const QByteArray &value); public slots: void start() Q_DECL_OVERRIDE; @@ -356,8 +357,17 @@ public slots: */ void jsonReceived(const QJsonDocument &json, int statusCode); + /** + * @brief etagResponseHeaderReceived - signal to report the ETag response header value + * from ocs api v2 + * @param value - the ETag response header value + * @param statusCode - the OCS status code: 100 (!) for success + */ + void etagResponseHeaderReceived(const QByteArray &value, int statusCode); + private: QUrlQuery _additionalParams; + QNetworkRequest _request; }; /**