SyncEngine: Introduce overall errors that are not tied to a file #5746
authorChristian Kamm <mail@ckamm.de>
Wed, 28 Jun 2017 10:45:54 +0000 (12:45 +0200)
committerckamm <mail@ckamm.de>
Wed, 12 Jul 2017 07:04:27 +0000 (09:04 +0200)
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
src/gui/folder.h
src/gui/issueswidget.cpp
src/gui/issueswidget.h
src/libsync/owncloudpropagator.cpp
src/libsync/owncloudpropagator.h
src/libsync/progressdispatcher.h
src/libsync/propagatedownload.cpp
src/libsync/syncengine.cpp
src/libsync/syncengine.h

index e00166262e03c7924bbaea04b2d9fb0c043f3373..670e941dd0650e95076714677895eeef9d0a0ed4 100644 (file)
@@ -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()
index ce1b3a7d991e5924a89df7ce9edea6df03be12d0..602d95b750c630fbb6382eaedc7c1015b38b5a98 100644 (file)
@@ -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 &);
 
index e32e89aac7b491421766370ee4121fb420e9fbf8..ed1792a1ceca01aa3444af3eadc1624a5dea037d 100644 (file)
@@ -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("<global error>");
+    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);
+}
 }
index 8ebe6451d7f46fc3d37d88b268ed3e799e2e8142..e9a0ef52d733cba3891e1ad7395e4c449235c6ae 100644 (file)
@@ -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;
 };
index 48ba29c0a17164763bc63649ce6ba12310b9e10c..96bb4ca2100986d5a9487aed209be96582709e18 100644 (file)
@@ -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:
index 0e7dd573cc69dd7b335d4dbb2a99f091a238cbc6..5b769d2f3cd8232e322d2b43b2821026102575d8 100644 (file)
@@ -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<PropagateDirectory> _rootJob;
index 05aa06b4ff274a3762b7c64525f198497c21d2e6..24764b2cb91edcbba7aed2fcea7e7bd8dba3a9c2 100644 (file)
@@ -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);
 
index 8cc87ccac9be8b7fc8cbd548208eb9e7e106e1cd..b5d6ac00406a3fbbea47ff8c05d411cd07d1df8c 100644 (file)
@@ -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())));
index 5c1f1f10c74a6b26f779e6404b82e21ec732972b..7389a0ce229c638c5ff10bf214366d3ea34bc577 100644 (file)
@@ -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
index 8652ef2cb0c6b04290735828ceaba5e8c615c790..bc6b35bc6728264413fc55539fc5d5f161d7ff1d 100644 (file)
@@ -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<QString> _uniqueErrors;
 };
 }