From 5ca743dd256bac83b92a3ede48c0368f95c8942b Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Wed, 28 Jun 2017 12:45:54 +0200 Subject: [PATCH] SyncEngine: Introduce overall errors that are not tied to a file #5746 For now we use them for: * csync errors: This allows them to appear in the sync issues tab * insufficient local disk space, as a summary of individual file errors Insufficient remote space will use them too, as might other issues that are bigger than a single sync item. --- src/gui/folder.cpp | 7 ++-- src/gui/folder.h | 9 +++-- src/gui/issueswidget.cpp | 53 +++++++++++++++++++++++++++--- src/gui/issueswidget.h | 2 ++ src/libsync/owncloudpropagator.cpp | 1 + src/libsync/owncloudpropagator.h | 3 +- src/libsync/progressdispatcher.h | 5 +++ src/libsync/propagatedownload.cpp | 9 +++-- src/libsync/syncengine.cpp | 20 +++++++++++ src/libsync/syncengine.h | 11 +++++++ 10 files changed, 107 insertions(+), 13 deletions(-) diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index e00166262..670e941dd 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -106,6 +106,7 @@ Folder::Folder(const FolderDefinition &definition, connect(_engine.data(), SIGNAL(seenLockedFile(QString)), FolderMan::instance(), SLOT(slotSyncOnceFileUnlocks(QString))); connect(_engine.data(), SIGNAL(aboutToPropagate(SyncFileItemVector &)), SLOT(slotLogPropagationStart())); + connect(_engine.data(), SIGNAL(summaryError(QString)), SLOT(slotSyncError(QString))); _scheduleSelfTimer.setSingleShot(true); _scheduleSelfTimer.setInterval(SyncEngine::minimumFileAgeForUpload); @@ -721,10 +722,10 @@ void Folder::setDirtyNetworkLimits() _engine->setNetworkLimits(uploadLimit, downloadLimit); } - -void Folder::slotSyncError(const QString &err) +void Folder::slotSyncError(const QString &message) { - _syncResult.appendErrorString(err); + _syncResult.appendErrorString(message); + emit ProgressDispatcher::instance()->syncError(alias(), message); } void Folder::slotSyncStarted() diff --git a/src/gui/folder.h b/src/gui/folder.h index ce1b3a7d9..602d95b75 100644 --- a/src/gui/folder.h +++ b/src/gui/folder.h @@ -278,11 +278,16 @@ public slots: private slots: void slotSyncStarted(); - void slotSyncError(const QString &); - void slotCsyncUnavailable(); void slotSyncFinished(bool); + /** Adds a error message that's not tied to a specific item. + */ + void slotSyncError(const QString &message); + + void slotCsyncUnavailable(); + void slotFolderDiscovered(bool local, QString folderName); + void slotTransmissionProgress(const ProgressInfo &pi); void slotItemCompleted(const SyncFileItemPtr &); diff --git a/src/gui/issueswidget.cpp b/src/gui/issueswidget.cpp index e32e89aac..ed1792a1c 100644 --- a/src/gui/issueswidget.cpp +++ b/src/gui/issueswidget.cpp @@ -49,6 +49,8 @@ IssuesWidget::IssuesWidget(QWidget *parent) this, SLOT(slotProgressInfo(QString, ProgressInfo))); connect(ProgressDispatcher::instance(), SIGNAL(itemCompleted(QString, SyncFileItemPtr)), this, SLOT(slotItemCompleted(QString, SyncFileItemPtr))); + connect(ProgressDispatcher::instance(), SIGNAL(syncError(QString, QString)), + this, SLOT(addLine(QString, QString))); connect(_ui->_treeWidget, SIGNAL(itemActivated(QTreeWidgetItem *, int)), SLOT(slotOpenFile(QTreeWidgetItem *, int))); connect(_ui->copyIssuesButton, SIGNAL(clicked()), SIGNAL(copyToClipboard())); @@ -132,6 +134,15 @@ void IssuesWidget::cleanItems(const QString &folder) emit(issueCountUpdated(_ui->_treeWidget->topLevelItemCount())); } +void IssuesWidget::addItem(QTreeWidgetItem *item) +{ + if (!item) + return; + _ui->_treeWidget->insertTopLevelItem(0, item); + item->setHidden(!shouldBeVisible(item, currentAccountFilter(), currentFolderFilter())); + emit issueCountUpdated(_ui->_treeWidget->topLevelItemCount()); +} + void IssuesWidget::slotOpenFile(QTreeWidgetItem *item, int) { QString folderName = item->data(2, Qt::UserRole).toString(); @@ -164,10 +175,7 @@ void IssuesWidget::slotItemCompleted(const QString &folder, const SyncFileItemPt QTreeWidgetItem *line = ProtocolWidget::createCompletedTreewidgetItem(folder, *item); if (!line) return; - - _ui->_treeWidget->insertTopLevelItem(0, line); - line->setHidden(!shouldBeVisible(line, currentAccountFilter(), currentFolderFilter())); - emit issueCountUpdated(_ui->_treeWidget->topLevelItemCount()); + addItem(line); } void IssuesWidget::slotRefreshIssues() @@ -328,4 +336,41 @@ void IssuesWidget::showFolderErrors(const QString &folderAlias) _ui->showIgnores->setChecked(false); _ui->showWarnings->setChecked(false); } + +void IssuesWidget::addLine(const QString &folderAlias, const QString &message) +{ + SyncFileItem::Status status = SyncFileItem::NormalError; + + auto folder = FolderMan::instance()->folder(folderAlias); + if (!folder) + return; + + QStringList columns; + QDateTime timestamp = QDateTime::currentDateTime(); + const QString timeStr = ProtocolWidget::timeString(timestamp); + const QString longTimeStr = ProtocolWidget::timeString(timestamp, QLocale::LongFormat); + + columns << timeStr; + columns << tr(""); + columns << folder->shortGuiLocalPath(); + columns << message; + + QIcon icon; + if (status == SyncFileItem::NormalError + || status == SyncFileItem::FatalError) { + icon = Theme::instance()->syncStateIcon(SyncResult::Error); + } else if (Progress::isWarningKind(status)) { + icon = Theme::instance()->syncStateIcon(SyncResult::Problem); + } + + QTreeWidgetItem *twitem = new QTreeWidgetItem(columns); + twitem->setData(0, Qt::SizeHintRole, QSize(0, ActivityItemDelegate::rowHeight())); + twitem->setIcon(0, icon); + twitem->setToolTip(0, longTimeStr); + twitem->setToolTip(3, message); + twitem->setData(0, Qt::UserRole, status); + twitem->setData(2, Qt::UserRole, folderAlias); + + addItem(twitem); +} } diff --git a/src/gui/issueswidget.h b/src/gui/issueswidget.h index 8ebe6451d..e9a0ef52d 100644 --- a/src/gui/issueswidget.h +++ b/src/gui/issueswidget.h @@ -50,6 +50,7 @@ public: void showFolderErrors(const QString &folderAlias); public slots: + void addLine(const QString &folderAlias, const QString &message); void slotProgressInfo(const QString &folder, const ProgressInfo &progress); void slotItemCompleted(const QString &folder, const SyncFileItemPtr &item); void slotOpenFile(QTreeWidgetItem *item, int); @@ -75,6 +76,7 @@ private: bool shouldBeVisible(QTreeWidgetItem *item, AccountState *filterAccount, const QString &filterFolderAlias) const; void cleanItems(const QString &folder); + void addItem(QTreeWidgetItem *item); Ui::IssuesWidget *_ui; }; diff --git a/src/libsync/owncloudpropagator.cpp b/src/libsync/owncloudpropagator.cpp index 48ba29c0a..96bb4ca21 100644 --- a/src/libsync/owncloudpropagator.cpp +++ b/src/libsync/owncloudpropagator.cpp @@ -238,6 +238,7 @@ void PropagateItemJob::done(SyncFileItem::Status statusArg, const QString &error _item->_status = SyncFileItem::SoftError; } + // Blacklist handling switch (_item->_status) { case SyncFileItem::SoftError: case SyncFileItem::FatalError: diff --git a/src/libsync/owncloudpropagator.h b/src/libsync/owncloudpropagator.h index 0e7dd573c..5b769d2f3 100644 --- a/src/libsync/owncloudpropagator.h +++ b/src/libsync/owncloudpropagator.h @@ -417,7 +417,6 @@ public: */ DiskSpaceResult diskSpaceCheck() const; - private slots: /** Emit the finished signal and make sure it is only emitted once */ @@ -445,6 +444,8 @@ signals: */ void touchedFile(const QString &fileName); + void insufficientLocalStorage(); + private: AccountPtr _account; QScopedPointer _rootJob; diff --git a/src/libsync/progressdispatcher.h b/src/libsync/progressdispatcher.h index 05aa06b4f..24764b2cb 100644 --- a/src/libsync/progressdispatcher.h +++ b/src/libsync/progressdispatcher.h @@ -249,6 +249,11 @@ signals: */ void itemCompleted(const QString &folder, const SyncFileItemPtr &item); + /** + * @brief A new folder-wide sync error was seen. + */ + void syncError(const QString &folder, const QString &message); + protected: void setProgressInfo(const QString &folder, const ProgressInfo &progress); diff --git a/src/libsync/propagatedownload.cpp b/src/libsync/propagatedownload.cpp index 8cc87ccac..b5d6ac004 100644 --- a/src/libsync/propagatedownload.cpp +++ b/src/libsync/propagatedownload.cpp @@ -427,9 +427,12 @@ void PropagateDownloadFile::startDownload() const auto diskSpaceResult = propagator()->diskSpaceCheck(); if (diskSpaceResult != OwncloudPropagator::DiskSpaceOk) { if (diskSpaceResult == OwncloudPropagator::DiskSpaceFailure) { - _item->_errorMayBeBlacklisted = true; - done(SyncFileItem::NormalError, - tr("The download would reduce free disk space below %1").arg(Utility::octetsToString(freeSpaceLimit()))); + // Using BlacklistedError here will make the error not pop up in the account + // tab: instead we'll generate a general "disk space low" message and show + // these detail errors only in the error view. + done(SyncFileItem::BlacklistedError, + tr("The download would reduce free local disk space below the limit")); + emit propagator()->insufficientLocalStorage(); } else if (diskSpaceResult == OwncloudPropagator::DiskSpaceCritical) { done(SyncFileItem::FatalError, tr("Free space on disk is less than %1").arg(Utility::octetsToString(criticalFreeSpaceLimit()))); diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index 5c1f1f10c..7389a0ce2 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -24,6 +24,7 @@ #include "csync_private.h" #include "filesystem.h" #include "propagateremotedelete.h" +#include "propagatedownload.h" #include "asserts.h" #ifdef Q_OS_WIN @@ -1025,6 +1026,7 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult) connect(_propagator.data(), SIGNAL(finished(bool)), this, SLOT(slotFinished(bool)), Qt::QueuedConnection); connect(_propagator.data(), SIGNAL(seenLockedFile(QString)), SIGNAL(seenLockedFile(QString))); connect(_propagator.data(), SIGNAL(touchedFile(QString)), SLOT(slotAddTouchedFile(QString))); + connect(_propagator.data(), SIGNAL(insufficientLocalStorage()), SLOT(slotInsufficientLocalStorage())); // apply the network limits to the propagator setNetworkLimits(_uploadLimit, _downloadLimit); @@ -1135,6 +1137,7 @@ void SyncEngine::finalize(bool success) _seenFiles.clear(); _temporarilyUnavailablePaths.clear(); _renamedFolders.clear(); + _uniqueErrors.clear(); _clearTouchedFilesTimer.start(); } @@ -1523,4 +1526,21 @@ void SyncEngine::abort() } } +void SyncEngine::slotSummaryError(const QString &message) +{ + if (_uniqueErrors.contains(message)) + return; + + _uniqueErrors.insert(message); + emit summaryError(message); +} + +void SyncEngine::slotInsufficientLocalStorage() +{ + slotSummaryError( + tr("Disk space is low: Downloads that would reduce free space " + "below %1 were skipped.") + .arg(Utility::octetsToString(freeSpaceLimit()))); +} + } // namespace OCC diff --git a/src/libsync/syncengine.h b/src/libsync/syncengine.h index 8652ef2cb..bc6b35bc6 100644 --- a/src/libsync/syncengine.h +++ b/src/libsync/syncengine.h @@ -121,6 +121,9 @@ signals: void transmissionProgress(const ProgressInfo &progress); + /// We've produced a new summary error. + void summaryError(const QString &message); + void finished(bool success); void started(); @@ -160,6 +163,11 @@ private slots: /** Wipes the _touchedFiles hash */ void slotClearTouchedFiles(); + /** Emit a summary error, unless it was seen before */ + void slotSummaryError(const QString &message); + + void slotInsufficientLocalStorage(); + private: void handleSyncError(CSYNC *ctx, const char *state); @@ -267,6 +275,9 @@ private: /** For clearing the _touchedFiles variable after sync finished */ QTimer _clearTouchedFilesTimer; + + /** List of unique errors that occurred in a sync run. */ + QSet _uniqueErrors; }; }