IssuesWidget: Add button to retry 507 errors #5537
authorChristian Kamm <mail@ckamm.de>
Tue, 11 Jul 2017 13:54:01 +0000 (15:54 +0200)
committerckamm <mail@ckamm.de>
Wed, 12 Jul 2017 07:04:27 +0000 (09:04 +0200)
Since these errors are blacklisted, it can take up to 24h to retry items
that had a 507 error for a while. This way users can intervene and cause
an upload attempt immediately.

src/gui/folder.cpp
src/gui/folder.h
src/gui/issueswidget.cpp
src/gui/issueswidget.h
src/gui/issueswidget.ui
src/libsync/progressdispatcher.h
src/libsync/syncengine.cpp
src/libsync/syncengine.h
src/libsync/syncjournaldb.cpp
src/libsync/syncjournaldb.h

index cf91c6ac6d2bce09d4e59fd6a234eaf3ab9ee6a5..7282e431f659819de2631de5a124768312a329cf 100644 (file)
@@ -89,7 +89,6 @@ Folder::Folder(const FolderDefinition &definition,
 
     connect(_engine.data(), SIGNAL(started()), SLOT(slotSyncStarted()), Qt::QueuedConnection);
     connect(_engine.data(), SIGNAL(finished(bool)), SLOT(slotSyncFinished(bool)), Qt::QueuedConnection);
-    connect(_engine.data(), SIGNAL(csyncError(QString)), SLOT(slotSyncError(QString)), Qt::QueuedConnection);
     connect(_engine.data(), SIGNAL(csyncUnavailable()), SLOT(slotCsyncUnavailable()), Qt::QueuedConnection);
 
     //direct connection so the message box is blocking the sync.
@@ -105,7 +104,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)));
+    connect(_engine.data(), &SyncEngine::syncError, this, &Folder::slotSyncError);
 
     _scheduleSelfTimer.setSingleShot(true);
     _scheduleSelfTimer.setInterval(SyncEngine::minimumFileAgeForUpload);
@@ -721,10 +720,10 @@ void Folder::setDirtyNetworkLimits()
     _engine->setNetworkLimits(uploadLimit, downloadLimit);
 }
 
-void Folder::slotSyncError(const QString &message)
+void Folder::slotSyncError(const QString &message, ErrorCategory category)
 {
     _syncResult.appendErrorString(message);
-    emit ProgressDispatcher::instance()->syncError(alias(), message);
+    emit ProgressDispatcher::instance()->syncError(alias(), message, category);
 }
 
 void Folder::slotSyncStarted()
index 29c21c76ffd5c2580b2b162109dd948ea5ea5e91..1c67c0722641fd32f50d86a6200ac99cfb88189d 100644 (file)
@@ -282,7 +282,7 @@ private slots:
 
     /** Adds a error message that's not tied to a specific item.
      */
-    void slotSyncError(const QString &message);
+    void slotSyncError(const QString &message, ErrorCategory category = ErrorCategory::Normal);
 
     void slotCsyncUnavailable();
 
index bed1d02403a2396614bbdbf29c6f0f93bfdfac67..51b47835fa5ad91d56e36a46b76318585b25d07e 100644 (file)
@@ -32,6 +32,8 @@
 #include "accountstate.h"
 #include "account.h"
 #include "accountmanager.h"
+#include "syncjournalfilerecord.h"
+#include "elidedlabel.h"
 
 #include "ui_issueswidget.h"
 
@@ -49,8 +51,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(ProgressDispatcher::instance(), &ProgressDispatcher::syncError,
+        this, &IssuesWidget::addError);
 
     connect(_ui->_treeWidget, SIGNAL(itemActivated(QTreeWidgetItem *, int)), SLOT(slotOpenFile(QTreeWidgetItem *, int)));
     connect(_ui->copyIssuesButton, SIGNAL(clicked()), SIGNAL(copyToClipboard()));
@@ -138,7 +140,20 @@ void IssuesWidget::addItem(QTreeWidgetItem *item)
 {
     if (!item)
         return;
-    _ui->_treeWidget->insertTopLevelItem(0, item);
+
+    int insertLoc = 0;
+
+    // Insert item specific errors behind the others
+    if (!item->text(1).isEmpty()) {
+        for (int i = 0; i < _ui->_treeWidget->topLevelItemCount(); ++i) {
+            if (!_ui->_treeWidget->topLevelItem(i)->text(1).isEmpty()) {
+                insertLoc = i;
+                break;
+            }
+        }
+    }
+
+    _ui->_treeWidget->insertTopLevelItem(insertLoc, item);
     item->setHidden(!shouldBeVisible(item, currentAccountFilter(), currentFolderFilter()));
     emit issueCountUpdated(_ui->_treeWidget->topLevelItemCount());
 }
@@ -337,10 +352,9 @@ void IssuesWidget::showFolderErrors(const QString &folderAlias)
     _ui->showWarnings->setChecked(false);
 }
 
-void IssuesWidget::addLine(const QString &folderAlias, const QString &message)
+void IssuesWidget::addError(const QString &folderAlias, const QString &message,
+    ErrorCategory category)
 {
-    SyncFileItem::Status status = SyncFileItem::NormalError;
-
     auto folder = FolderMan::instance()->folder(folderAlias);
     if (!folder)
         return;
@@ -351,26 +365,58 @@ void IssuesWidget::addLine(const QString &folderAlias, const QString &message)
     const QString longTimeStr = ProtocolWidget::timeString(timestamp, QLocale::LongFormat);
 
     columns << timeStr;
-    columns << tr("<global error>");
+    columns << ""; // no "File" entry
     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);
-    }
+    QIcon icon = Theme::instance()->syncStateIcon(SyncResult::Error);
 
     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(0, Qt::UserRole, SyncFileItem::NormalError);
     twitem->setData(2, Qt::UserRole, folderAlias);
 
     addItem(twitem);
+    addErrorWidget(twitem, message, category);
+}
+
+void IssuesWidget::addErrorWidget(QTreeWidgetItem *item, const QString &message, ErrorCategory category)
+{
+    QWidget *widget = 0;
+    if (category == ErrorCategory::InsufficientRemoteStorage) {
+        widget = new QWidget;
+        auto layout = new QHBoxLayout;
+        widget->setLayout(layout);
+
+        auto label = new ElidedLabel(message, widget);
+        label->setElideMode(Qt::ElideMiddle);
+        layout->addWidget(label);
+
+        auto button = new QPushButton("Retry all uploads", widget);
+        button->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
+        auto folderAlias = item->data(2, Qt::UserRole).toString();
+        connect(button, &QPushButton::clicked,
+            this, [this, folderAlias]() { retryInsufficentRemoteStorageErrors(folderAlias); });
+        layout->addWidget(button);
+    }
+
+    if (widget) {
+        item->setText(3, QString());
+    }
+    _ui->_treeWidget->setItemWidget(item, 3, widget);
+}
+
+void IssuesWidget::retryInsufficentRemoteStorageErrors(const QString &folderAlias)
+{
+    auto folderman = FolderMan::instance();
+    auto folder = folderman->folder(folderAlias);
+    if (!folder)
+        return;
+
+    folder->journalDb()->wipeErrorBlacklistCategory(SyncJournalErrorBlacklistRecord::InsufficientRemoteStorage);
+    folderman->scheduleFolderNext(folder);
 }
 }
index e9a0ef52d733cba3891e1ad7395e4c449235c6ae..4accd89b220489ca5f480c281115a06f2e4508fa 100644 (file)
@@ -50,7 +50,7 @@ public:
     void showFolderErrors(const QString &folderAlias);
 
 public slots:
-    void addLine(const QString &folderAlias, const QString &message);
+    void addError(const QString &folderAlias, const QString &message, ErrorCategory category);
     void slotProgressInfo(const QString &folder, const ProgressInfo &progress);
     void slotItemCompleted(const QString &folder, const SyncFileItemPtr &item);
     void slotOpenFile(QTreeWidgetItem *item, int);
@@ -78,6 +78,12 @@ private:
     void cleanItems(const QString &folder);
     void addItem(QTreeWidgetItem *item);
 
+    /// Add the special error widget for the category, if any
+    void addErrorWidget(QTreeWidgetItem *item, const QString &message, ErrorCategory category);
+
+    /// Wipes all insufficient remote storgage blacklist entries
+    void retryInsufficentRemoteStorageErrors(const QString &folderAlias);
+
     Ui::IssuesWidget *_ui;
 };
 }
index 5a1e1fac676cfdd091be372a0a3e11344abe4cbb..27bfe111842c4d8756284c21a86f1af9a0a49172 100644 (file)
@@ -99,9 +99,6 @@
      <property name="rootIsDecorated">
       <bool>false</bool>
      </property>
-     <property name="uniformRowHeights">
-      <bool>true</bool>
-     </property>
      <property name="columnCount">
       <number>4</number>
      </property>
index e968216996868a273f665a18b7d41924cc30e76c..f7015f6c55afa4dc59e1dc2deb1c2dc30f4e5628 100644 (file)
@@ -248,6 +248,16 @@ namespace Progress {
     OWNCLOUDSYNC_EXPORT bool isIgnoredKind(SyncFileItem::Status);
 }
 
+/** Type of error
+ *
+ * Used for ProgressDispatcher::syncError. May trigger error interactivity
+ * in IssuesWidget.
+ */
+enum class ErrorCategory {
+    Normal,
+    InsufficientRemoteStorage,
+};
+
 /**
  * @file progressdispatcher.h
  * @brief A singleton class to provide sync progress information to other gui classes.
@@ -283,7 +293,7 @@ signals:
     /**
      * @brief A new folder-wide sync error was seen.
      */
-    void syncError(const QString &folder, const QString &message);
+    void syncError(const QString &folder, const QString &message, ErrorCategory category);
 
 protected:
     void setProgressInfo(const QString &folder, const ProgressInfo &progress);
index 65fdd61135bb66cea5a012466417d23dcdbd4090..96ffbcbb7951577b03c24b8217cd6a8f0a0973e0 100644 (file)
@@ -709,11 +709,17 @@ void SyncEngine::handleSyncError(CSYNC *ctx, const char *state)
     } else if (CSYNC_STATUS_IS_EQUAL(err, CSYNC_STATUS_SERVICE_UNAVAILABLE) || CSYNC_STATUS_IS_EQUAL(err, CSYNC_STATUS_CONNECT_ERROR)) {
         emit csyncUnavailable();
     } else {
-        emit csyncError(errStr);
+        csyncError(errStr);
     }
     finalize(false);
 }
 
+void SyncEngine::csyncError(const QString &message)
+{
+    emit syncError(message, ErrorCategory::Normal);
+}
+
+
 void SyncEngine::startSync()
 {
     if (_journal->exists()) {
@@ -744,7 +750,7 @@ void SyncEngine::startSync()
     if (!QDir(_localPath).exists()) {
         _anotherSyncNeeded = DelayedFollowUp;
         // No _tr, it should only occur in non-mirall
-        emit csyncError("Unable to find local sync folder.");
+        csyncError("Unable to find local sync folder.");
         finalize(false);
         return;
     }
@@ -757,11 +763,11 @@ void SyncEngine::startSync()
                          << "and at least" << minFree << "are required";
         if (freeBytes < minFree) {
             _anotherSyncNeeded = DelayedFollowUp;
-            emit csyncError(tr("Only %1 are available, need at least %2 to start",
+            csyncError(tr("Only %1 are available, need at least %2 to start",
                 "Placeholders are postfixed with file sizes using Utility::octetsToString()")
-                                .arg(
-                                    Utility::octetsToString(freeBytes),
-                                    Utility::octetsToString(minFree)));
+                           .arg(
+                               Utility::octetsToString(freeBytes),
+                               Utility::octetsToString(minFree)));
             finalize(false);
             return;
         }
@@ -794,7 +800,7 @@ void SyncEngine::startSync()
 
     if (fileRecordCount == -1) {
         qCWarning(lcEngine) << "No way to create a sync journal!";
-        emit csyncError(tr("Unable to open or create the local sync database. Make sure you have write access in the sync folder."));
+        csyncError(tr("Unable to open or create the local sync database. Make sure you have write access in the sync folder."));
         finalize(false);
         return;
         // database creation error!
@@ -813,7 +819,7 @@ void SyncEngine::startSync()
         qCInfo(lcEngine) << (usingSelectiveSync ? "Using Selective Sync" : "NOT Using Selective Sync");
     } else {
         qCWarning(lcEngine) << "Could not retrieve selective sync list from DB";
-        emit csyncError(tr("Unable to read the blacklist from the local database"));
+        csyncError(tr("Unable to read the blacklist from the local database"));
         finalize(false);
         return;
     }
@@ -854,7 +860,7 @@ void SyncEngine::startSync()
     if (!ok) {
         delete discoveryJob;
         qCWarning(lcEngine) << "Unable to read selective sync list, aborting.";
-        emit csyncError(tr("Unable to read from the sync journal."));
+        csyncError(tr("Unable to read from the sync journal."));
         finalize(false);
         return;
     }
@@ -903,7 +909,7 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
     // Sanity check
     if (!_journal->isConnected()) {
         qCWarning(lcEngine) << "Bailing out, DB failure";
-        emit csyncError(tr("Cannot open the sync journal"));
+        csyncError(tr("Cannot open the sync journal"));
         finalize(false);
         return;
     } else {
@@ -1101,7 +1107,7 @@ void SyncEngine::slotItemCompleted(const SyncFileItemPtr &item)
     _progressInfo->setProgressComplete(*item);
 
     if (item->_status == SyncFileItem::FatalError) {
-        emit csyncError(item->_errorString);
+        csyncError(item->_errorString);
     }
 
     emit transmissionProgress(*_progressInfo);
@@ -1551,7 +1557,7 @@ void SyncEngine::slotSummaryError(const QString &message)
         return;
 
     _uniqueErrors.insert(message);
-    emit summaryError(message);
+    emit syncError(message, ErrorCategory::Normal);
 }
 
 void SyncEngine::slotInsufficientLocalStorage()
@@ -1564,7 +1570,12 @@ void SyncEngine::slotInsufficientLocalStorage()
 
 void SyncEngine::slotInsufficientRemoteStorage()
 {
-    slotSummaryError(tr("There is insufficient space available on the server for some uploads."));
+    auto msg = tr("There is insufficient space available on the server for some uploads.");
+    if (_uniqueErrors.contains(msg))
+        return;
+
+    _uniqueErrors.insert(msg);
+    emit syncError(msg, ErrorCategory::InsufficientRemoteStorage);
 }
 
 } // namespace OCC
index e307a900b7c0bc9621b001eca938c0a7827c4ae2..4f96e404f00a724a65e8945bc261adf5e0638131 100644 (file)
@@ -104,7 +104,6 @@ public:
     static qint64 minimumFileAgeForUpload; // in ms
 
 signals:
-    void csyncError(const QString &);
     void csyncUnavailable();
 
     // During update, before reconcile
@@ -120,8 +119,8 @@ signals:
 
     void transmissionProgress(const ProgressInfo &progress);
 
-    /// We've produced a new summary error.
-    void summaryError(const QString &message);
+    /// We've produced a new sync error of a type.
+    void syncError(const QString &message, ErrorCategory category);
 
     void finished(bool success);
     void started();
@@ -171,6 +170,7 @@ private slots:
 
 private:
     void handleSyncError(CSYNC *ctx, const char *state);
+    void csyncError(const QString &message);
 
     QString journalDbFilePath() const;
 
index 5b43a3595e7a7dc160cc2ee810b794b4a65c9aad..0eb34e6097f07ccbd2e9705817e199f7c1f574d1 100644 (file)
@@ -1513,6 +1513,20 @@ void SyncJournalDb::wipeErrorBlacklistEntry(const QString &file)
     }
 }
 
+void SyncJournalDb::wipeErrorBlacklistCategory(SyncJournalErrorBlacklistRecord::Category category)
+{
+    QMutexLocker locker(&_mutex);
+    if (checkConnect()) {
+        SqlQuery query(_db);
+
+        query.prepare("DELETE FROM blacklist WHERE errorCategory=?1");
+        query.bindValue(1, category);
+        if (!query.exec()) {
+            sqlFail("Deletion of blacklist category failed.", query);
+        }
+    }
+}
+
 void SyncJournalDb::setErrorBlacklistEntry(const SyncJournalErrorBlacklistRecord &item)
 {
     QMutexLocker locker(&_mutex);
index 15f1d01cab122ce5426c46210b63f1bdca36bb61..62295f62e10816645ffe0604fa3bf6d13de89fe5 100644 (file)
 
 #include "utility.h"
 #include "ownsql.h"
+#include "syncjournalfilerecord.h"
 
 namespace OCC {
 class SyncJournalFileRecord;
-class SyncJournalErrorBlacklistRecord;
 
 /**
  * @brief Class that handles the sync database
@@ -73,6 +73,7 @@ public:
 
     void setErrorBlacklistEntry(const SyncJournalErrorBlacklistRecord &item);
     void wipeErrorBlacklistEntry(const QString &file);
+    void wipeErrorBlacklistCategory(SyncJournalErrorBlacklistRecord::Category category);
     int wipeErrorBlacklist();
     int errorBlackListEntryCount();