From: Christian Kamm Date: Tue, 11 Jul 2017 13:54:01 +0000 (+0200) Subject: IssuesWidget: Add button to retry 507 errors #5537 X-Git-Tag: archive/raspbian/3.16.7-1_deb13u1+rpi1~1^2~704^2^2~44 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=971abaea801a413921421a5887463c4234c0c116;p=nextcloud-desktop.git IssuesWidget: Add button to retry 507 errors #5537 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. --- diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index cf91c6ac6..7282e431f 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -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() diff --git a/src/gui/folder.h b/src/gui/folder.h index 29c21c76f..1c67c0722 100644 --- a/src/gui/folder.h +++ b/src/gui/folder.h @@ -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(); diff --git a/src/gui/issueswidget.cpp b/src/gui/issueswidget.cpp index bed1d0240..51b47835f 100644 --- a/src/gui/issueswidget.cpp +++ b/src/gui/issueswidget.cpp @@ -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(""); + 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); } } diff --git a/src/gui/issueswidget.h b/src/gui/issueswidget.h index e9a0ef52d..4accd89b2 100644 --- a/src/gui/issueswidget.h +++ b/src/gui/issueswidget.h @@ -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; }; } diff --git a/src/gui/issueswidget.ui b/src/gui/issueswidget.ui index 5a1e1fac6..27bfe1118 100644 --- a/src/gui/issueswidget.ui +++ b/src/gui/issueswidget.ui @@ -99,9 +99,6 @@ false - - true - 4 diff --git a/src/libsync/progressdispatcher.h b/src/libsync/progressdispatcher.h index e96821699..f7015f6c5 100644 --- a/src/libsync/progressdispatcher.h +++ b/src/libsync/progressdispatcher.h @@ -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); diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index 65fdd6113..96ffbcbb7 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -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 diff --git a/src/libsync/syncengine.h b/src/libsync/syncengine.h index e307a900b..4f96e404f 100644 --- a/src/libsync/syncengine.h +++ b/src/libsync/syncengine.h @@ -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; diff --git a/src/libsync/syncjournaldb.cpp b/src/libsync/syncjournaldb.cpp index 5b43a3595..0eb34e609 100644 --- a/src/libsync/syncjournaldb.cpp +++ b/src/libsync/syncjournaldb.cpp @@ -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); diff --git a/src/libsync/syncjournaldb.h b/src/libsync/syncjournaldb.h index 15f1d01ca..62295f62e 100644 --- a/src/libsync/syncjournaldb.h +++ b/src/libsync/syncjournaldb.h @@ -22,10 +22,10 @@ #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();