Merge pull request #197 from nextcloud/fix-notifications

Improves notifications:
- Display the actual response text from the notification API in the popups.
- Do not display sync activities messages like how many files were downloaded, only errors.
- Make the notifications section above the activity feed also clickable. e.g open the call, the file that was shared, open the calendar event etc
- Changes "Show desktop notifications" setting to "Show server notifications"
- Show notifications on startup and then show only NEW notifications: uses If-None-Match check to only retrieve notifications once there are new notifications.
This commit is contained in:
Camila Ayres 2018-03-13 13:30:44 +01:00 committed by GitHub
commit 08b33c6f4b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 278 additions and 125 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 900 B

After

Width:  |  Height:  |  Size: 495 B

View file

@ -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");

View file

@ -35,6 +35,7 @@ AccountState::AccountState(AccountPtr account)
, _connectionStatus(ConnectionValidator::Undefined)
, _waitingForNewCredentials(false)
, _maintenanceToConnectedDelay(60000 + (qrand() % (4 * 60000))) // 1-5min delay
, _notificationsEtagResponseHeader("*")
{
qRegisterMetaType<AccountState *>("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) {

View file

@ -131,6 +131,16 @@ public:
*/
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 @@ private:
bool _waitingForNewCredentials;
QElapsedTimer _timeSinceLastETagCheck;
QPointer<ConnectionValidator> _connectionValidator;
QByteArray _notificationsEtagResponseHeader;
/**
* Starts counting when the server starts being back up after 503 or

View file

@ -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;

View file

@ -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);
}

View file

@ -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);
}
}
}

View file

@ -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<bool> 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)

View file

@ -42,7 +42,7 @@ public:
private slots:
void saveMiscSettings();
void slotToggleLaunchOnStartup(bool);
void slotToggleOptionalDesktopNotifications(bool);
void slotToggleOptionalServerNotifications(bool);
void slotShowInExplorerNavigationPane(bool);
void slotUpdateInfo();
void slotIgnoreFilesEditor();

View file

@ -20,6 +20,16 @@
<string>General Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="0">
<widget class="QCheckBox" name="monoIconsCheckBox">
<property name="toolTip">
<string>For System Tray</string>
</property>
<property name="text">
<string>Use &amp;Monochrome Icons</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="autostartCheckBox">
<property name="text">
@ -28,19 +38,9 @@
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="desktopNotificationsCheckBox">
<widget class="QCheckBox" name="serverNotificationsCheckBox">
<property name="text">
<string>Show &amp;Desktop Notifications</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="monoIconsCheckBox">
<property name="toolTip">
<string>For System Tray</string>
</property>
<property name="text">
<string>Use &amp;Monochrome Icons</string>
<string>Show Server &amp;Notifications</string>
</property>
</widget>
</item>
@ -253,7 +253,7 @@
</widget>
<tabstops>
<tabstop>autostartCheckBox</tabstop>
<tabstop>desktopNotificationsCheckBox</tabstop>
<tabstop>serverNotificationsCheckBox</tabstop>
<tabstop>monoIconsCheckBox</tabstop>
<tabstop>ignoredFilesButton</tabstop>
<tabstop>newFolderLimitCheckBox</tabstop>

View file

@ -16,6 +16,7 @@
#include "QProgressIndicator.h"
#include "common/utility.h"
#include "common/asserts.h"
#include "guiutility.h"
#include <QPushButton>
@ -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

View file

@ -46,6 +46,7 @@ public slots:
private slots:
void slotButtonClicked();
void slotOpenBrowserButtonClicked();
private:
Ui_NotificationWidget _ui;

View file

@ -20,42 +20,88 @@
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMaximumSize</enum>
</property>
<property name="leftMargin">
<number>24</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>26</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="_notifIcon">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap>../../../../resources/bell.png</pixmap>
</property>
</widget>
</item>
<property name="horizontalSpacing">
<number>0</number>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="_subjectLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<layout class="QHBoxLayout" name="labelsHorizontalLayout" stretch="1,1">
<property name="spacing">
<number>6</number>
</property>
<property name="text">
<string>Lorem ipsum dolor sit amet</string>
<property name="topMargin">
<number>6</number>
</property>
</widget>
<item>
<widget class="QLabel" name="_notifIcon">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap>../../../../resources/bell.png</pixmap>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
<property name="margin">
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="_subjectLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Lorem ipsum dolor sit amet</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="_messageLabel">
@ -71,13 +117,41 @@
<property name="scaledContents">
<bool>false</bool>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="margin">
<number>0</number>
</property>
<property name="indent">
<number>28</number>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>6</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>28</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="_timeLabel">
<property name="font">
@ -89,7 +163,7 @@
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
@ -104,19 +178,6 @@
</item>
</layout>
</item>
<item row="1" column="0" colspan="2">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::HLine</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<property name="lineWidth">
<number>4</number>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>

View file

@ -17,7 +17,6 @@
#include "ocsnavigationappsjob.h"
#include "theme.h"
#include "folderman.h"
#include "configfile.h"
#include "progressdispatcher.h"
#include "owncloudsetupwizard.h"
#include "sharedialog.h"
@ -689,10 +688,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);
}

View file

@ -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<AccountState *>(ptr));
QObject::connect(_notificationJob.data(), &JsonApiJob::etagResponseHeaderReceived,
this, &ServerNotificationHandler::slotEtagResponseHeaderReceived);
_notificationJob->setProperty(propertyAccountStateC, QVariant::fromValue<AccountState *>(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<AccountState *>(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<AccountState *>(sender()->property("AccountStatePtr"));
AccountState *ai = qvariant_cast<AccountState *>(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);

View file

@ -37,6 +37,7 @@ public slots:
private slots:
void slotNotificationsReceived(const QJsonDocument &json, int statusCode);
void slotEtagResponseHeaderReceived(const QByteArray &value, int statusCode);
private:
QPointer<JsonApiJob> _notificationJob;

View file

@ -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();
}

View file

@ -120,8 +120,8 @@ public:
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);

View file

@ -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 &params)
_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;

View file

@ -341,6 +341,7 @@ public:
* This function needs to be called before start() obviously.
*/
void addQueryParams(const QUrlQuery &params);
void addRawHeader(const QByteArray &headerName, const QByteArray &value);
public slots:
void start() Q_DECL_OVERRIDE;
@ -356,8 +357,17 @@ signals:
*/
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;
};
/**