diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index 5c7620c6a7fa0..52baa342f13b6 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -101,6 +101,8 @@ Folder::Folder(const FolderDefinition &definition, connect(_engine.data(), &SyncEngine::aboutToRemoveAllFiles, this, &Folder::slotAboutToRemoveAllFiles); + connect(_engine.data(), &SyncEngine::aboutToRemoveRemnantsReadOnlyFolders, + this, &Folder::slotNeedToRemoveRemnantsReadOnlyFolders); connect(_engine.data(), &SyncEngine::transmissionProgress, this, &Folder::slotTransmissionProgress); connect(_engine.data(), &SyncEngine::itemCompleted, this, &Folder::slotItemCompleted); @@ -1664,6 +1666,34 @@ void Folder::slotAboutToRemoveAllFiles(SyncFileItem::Direction dir, std::functio msgBox->open(); } +void Folder::slotNeedToRemoveRemnantsReadOnlyFolders(const QList &folders, + const QString &localPath, + std::function callback) +{ + const auto msg = tr("Do you want to clean up remnant read-only folders left over from previous failed synchronization attempts."); + auto msgBox = new QMessageBox(QMessageBox::Question, tr("Remove remnant invalid folders?"), + msg, QMessageBox::NoButton); + msgBox->setAttribute(Qt::WA_DeleteOnClose); + msgBox->setWindowFlags(msgBox->windowFlags() | Qt::WindowStaysOnTopHint); + msgBox->addButton(tr("Proceed to remove remnant folders"), QMessageBox::AcceptRole); + const auto keepBtn = msgBox->addButton(tr("Do nothing"), QMessageBox::RejectRole); + setSyncPaused(true); + connect(msgBox, &QMessageBox::finished, this, [msgBox, keepBtn, callback, folders, localPath, this] { + const bool cancel = msgBox->clickedButton() == keepBtn; + if (!cancel) { + for(const auto &oneFolder : folders) { + FileSystem::removeRecursively(localPath + oneFolder->_file); + } + } + callback(cancel); + if (cancel) { + setSyncPaused(true); + } + }); + connect(this, &Folder::destroyed, msgBox, &QMessageBox::deleteLater); + msgBox->open(); +} + void Folder::removeLocalE2eFiles() { qCDebug(lcFolder) << "Removing local E2EE files"; diff --git a/src/gui/folder.h b/src/gui/folder.h index e3bed9f7f92e8..c79153a86758f 100644 --- a/src/gui/folder.h +++ b/src/gui/folder.h @@ -335,6 +335,10 @@ public slots: // connected to the corresponding signals in the SyncEngine void slotAboutToRemoveAllFiles(OCC::SyncFileItem::Direction, std::function callback); + void slotNeedToRemoveRemnantsReadOnlyFolders(const QList &folders, + const QString &localPath, + std::function callback); + /** * Starts a sync operation * diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index 297c732f90890..400e93e3261b2 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -811,22 +811,16 @@ void SyncEngine::slotDiscoveryFinished() } } - QPointer guard = new QObject(); - QPointer self = this; - auto callback = [this, self, guard](bool cancel) -> void { - // use a guard to ensure its only called once... - // qpointer to self to ensure we still exist - if (!guard || !self) { - return; - } - guard->deleteLater(); + promptUserBeforePropagation([this, side](auto &&callback){ + emit aboutToRemoveAllFiles(side >= 0 ? SyncFileItem::Down : SyncFileItem::Up, callback); + }); + } - cancelSyncOrContinue(cancel); - }; - emit aboutToRemoveAllFiles(side >= 0 ? SyncFileItem::Down : SyncFileItem::Up, callback); - return; + if (!_remnantReadOnlyFolders.isEmpty()) { + handleRemnantReadOnlyFolders(); } + finishSync(); } @@ -1026,17 +1020,17 @@ void SyncEngine::finishSync() restoreOldFiles(_syncItems); } - if (_discoveryPhase->_anotherSyncNeeded && !_discoveryPhase->_filesNeedingScheduledSync.empty()) { + if (_discoveryPhase && _discoveryPhase->_anotherSyncNeeded && !_discoveryPhase->_filesNeedingScheduledSync.empty()) { slotScheduleFilesDelayedSync(); - } else if (_discoveryPhase->_anotherSyncNeeded && _anotherSyncNeeded == NoFollowUpSync) { + } else if (_discoveryPhase && _discoveryPhase->_anotherSyncNeeded && _anotherSyncNeeded == NoFollowUpSync) { _anotherSyncNeeded = ImmediateFollowUp; } - if (!_discoveryPhase->_filesUnscheduleSync.empty()) { + if (_discoveryPhase && !_discoveryPhase->_filesUnscheduleSync.empty()) { slotUnscheduleFilesDelayedSync(); } - if (_discoveryPhase->_hasDownloadRemovedItems && _discoveryPhase->_hasUploadErrorItems) { + if (_discoveryPhase && _discoveryPhase->_hasDownloadRemovedItems && _discoveryPhase->_hasUploadErrorItems) { for (const auto &item : qAsConst(_syncItems)) { if (item->_instruction == CSYNC_INSTRUCTION_ERROR && item->_direction == SyncFileItem::Up) { // item->_instruction = CSYNC_INSTRUCTION_IGNORE; @@ -1111,6 +1105,32 @@ void SyncEngine::finishSync() qCInfo(lcEngine) << "#### Post-Reconcile end #################################################### " << _stopWatch.addLapTime(QStringLiteral("Post-Reconcile Finished")) << "ms"; } +void SyncEngine::handleRemnantReadOnlyFolders() +{ + promptUserBeforePropagation([this](auto &&callback) { + emit aboutToRemoveRemnantsReadOnlyFolders(_remnantReadOnlyFolders, _localPath, callback); + }); +} + +template +void SyncEngine::promptUserBeforePropagation(T &&lambda) +{ + QPointer guard = new QObject(); + QPointer self = this; + auto callback = [this, self, guard](bool cancel) -> void { + // use a guard to ensure its only called once... + // qpointer to self to ensure we still exist + if (!guard || !self) { + return; + } + guard->deleteLater(); + + cancelSyncOrContinue(cancel); + }; + + lambda(callback); +} + void SyncEngine::slotAddTouchedFile(const QString &fn) { QElapsedTimer now; diff --git a/src/libsync/syncengine.h b/src/libsync/syncengine.h index 2290a718a8fed..b0b26c5804bf2 100644 --- a/src/libsync/syncengine.h +++ b/src/libsync/syncengine.h @@ -184,6 +184,10 @@ public slots: */ void aboutToRemoveAllFiles(OCC::SyncFileItem::Direction direction, std::function f); + void aboutToRemoveRemnantsReadOnlyFolders(const QList &folders, + const QString &localPath, + std::function f); + // A new folder was discovered and was not synced because of the confirmation feature void newBigFolder(const QString &folder, bool isExternal); @@ -360,6 +364,11 @@ private slots: void finishSync(); + void handleRemnantReadOnlyFolders(); + + template + void promptUserBeforePropagation(T &&lambda); + // true if there is at least one file which was not changed on the server bool _hasNoneFiles = false;