mirror of
https://github.com/nextcloud/desktop.git
synced 2024-10-24 13:25:52 +03:00
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.
This commit is contained in:
parent
a76670f3b8
commit
971abaea80
10 changed files with 126 additions and 42 deletions
|
@ -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()
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
Loading…
Reference in a new issue