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.
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);
_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()
/** 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();
#include "accountstate.h"
#include "account.h"
#include "accountmanager.h"
+#include "syncjournalfilerecord.h"
+#include "elidedlabel.h"
#include "ui_issueswidget.h"
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()));
{
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());
}
_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;
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);
}
}
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);
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;
};
}
<property name="rootIsDecorated">
<bool>false</bool>
</property>
- <property name="uniformRowHeights">
- <bool>true</bool>
- </property>
<property name="columnCount">
<number>4</number>
</property>
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.
/**
* @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);
} 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()) {
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;
}
<< "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;
}
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!
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;
}
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;
}
// 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 {
_progressInfo->setProgressComplete(*item);
if (item->_status == SyncFileItem::FatalError) {
- emit csyncError(item->_errorString);
+ csyncError(item->_errorString);
}
emit transmissionProgress(*_progressInfo);
return;
_uniqueErrors.insert(message);
- emit summaryError(message);
+ emit syncError(message, ErrorCategory::Normal);
}
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
static qint64 minimumFileAgeForUpload; // in ms
signals:
- void csyncError(const QString &);
void csyncUnavailable();
// During update, before reconcile
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();
private:
void handleSyncError(CSYNC *ctx, const char *state);
+ void csyncError(const QString &message);
QString journalDbFilePath() const;
}
}
+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);
#include "utility.h"
#include "ownsql.h"
+#include "syncjournalfilerecord.h"
namespace OCC {
class SyncJournalFileRecord;
-class SyncJournalErrorBlacklistRecord;
/**
* @brief Class that handles the sync database
void setErrorBlacklistEntry(const SyncJournalErrorBlacklistRecord &item);
void wipeErrorBlacklistEntry(const QString &file);
+ void wipeErrorBlacklistCategory(SyncJournalErrorBlacklistRecord::Category category);
int wipeErrorBlacklist();
int errorBlackListEntryCount();