Skip to content

Commit

Permalink
Merge pull request #6857 from nextcloud/backport/6773/stable-3.13
Browse files Browse the repository at this point in the history
[stable-3.13] Bugfix nextcloudcmd when syncing results in deleting all files
  • Loading branch information
mgallien authored Jun 28, 2024
2 parents 07317da + 25c7a73 commit 17fcf14
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 26 deletions.
9 changes: 5 additions & 4 deletions src/cmd/cmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -529,10 +529,11 @@ int main(int argc, char **argv)
selectiveSyncFixup(&db, selectiveSyncList);
}

SyncOptions opt;
opt.fillFromEnvironmentVariables();
opt.verifyChunkSizes();
SyncEngine engine(account, options.source_dir, opt, folder, &db);
SyncOptions syncOptions;
syncOptions.fillFromEnvironmentVariables();
syncOptions.verifyChunkSizes();
syncOptions.setIsCmd(true);
SyncEngine engine(account, options.source_dir, syncOptions, folder, &db);
engine.setIgnoreHiddenFiles(options.ignoreHiddenFiles);
engine.setNetworkLimits(options.uplimit, options.downlimit);
QObject::connect(&engine, &SyncEngine::finished,
Expand Down
25 changes: 6 additions & 19 deletions src/gui/folder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1628,27 +1628,14 @@ bool Folder::virtualFilesEnabled() const

void Folder::slotAboutToRemoveAllFiles(SyncFileItem::Direction dir, std::function<void(bool)> callback)
{
ConfigFile cfgFile;
if (!cfgFile.promptDeleteFiles()) {
callback(false);
return;
}

const QString msg = dir == SyncFileItem::Down ? tr("All files in the sync folder \"%1\" folder were deleted on the server.\n"
"These deletes will be synchronized to your local sync folder, making such files "
"unavailable unless you have a right to restore. \n"
"If you decide to restore the files, they will be re-synced with the server if you have rights to do so.\n"
"If you decide to delete the files, they will be unavailable to you, unless you are the owner.")
: tr("All the files in your local sync folder \"%1\" were deleted. These deletes will be "
"synchronized with your server, making such files unavailable unless restored.\n"
"Are you sure you want to sync those actions with the server?\n"
"If this was an accident and you decide to keep your files, they will be re-synced from the server.");
auto msgBox = new QMessageBox(QMessageBox::Warning, tr("Remove All Files?"),
msg.arg(shortGuiLocalPath()), QMessageBox::NoButton);
const QString msg = dir == SyncFileItem::Down ? tr("All files in the server folder \"%1\" were deleted.\n\nIf you restore the files, they will be uploaded again to the server.")
: tr("All files in the local folder \"%1\" were deleted.\n\nIf you restore the files, they will be downloaded again from the server.");
auto msgBox = new QMessageBox(QMessageBox::Warning, tr("Remove all files?"),
msg.arg(shortGuiLocalPath()), QMessageBox::NoButton);
msgBox->setAttribute(Qt::WA_DeleteOnClose);
msgBox->setWindowFlags(msgBox->windowFlags() | Qt::WindowStaysOnTopHint);
msgBox->addButton(tr("Remove all files"), QMessageBox::DestructiveRole);
QPushButton *keepBtn = msgBox->addButton(tr("Keep files"), QMessageBox::AcceptRole);
msgBox->addButton(tr("Proceed to remove all files"), QMessageBox::DestructiveRole);
QPushButton *keepBtn = msgBox->addButton(tr("Restore files"), QMessageBox::AcceptRole);
bool oldPaused = syncPaused();
setSyncPaused(true);
connect(msgBox, &QMessageBox::finished, this, [msgBox, keepBtn, callback, oldPaused, this] {
Expand Down
5 changes: 3 additions & 2 deletions src/libsync/syncengine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -898,10 +898,11 @@ void SyncEngine::slotDiscoveryFinished()
qCInfo(lcEngine) << "#### Post-Reconcile end #################################################### " << _stopWatch.addLapTime(QStringLiteral("Post-Reconcile Finished")) << "ms";
};

if (!_hasNoneFiles && _hasRemoveFile) {
const auto displayDialog = ConfigFile().promptDeleteFiles() && !_syncOptions.isCmd();
if (!_hasNoneFiles && _hasRemoveFile && displayDialog) {
qCInfo(lcEngine) << "All the files are going to be changed, asking the user";
int side = 0; // > 0 means more deleted on the server. < 0 means more deleted on the client
foreach (const auto &it, _syncItems) {
for (const auto &it : _syncItems) {
if (it->_instruction == CSYNC_INSTRUCTION_REMOVE) {
side += it->_direction == SyncFileItem::Down ? 1 : -1;
}
Expand Down
11 changes: 11 additions & 0 deletions src/libsync/syncoptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ using namespace OCC;

SyncOptions::SyncOptions()
: _vfs(new VfsOff)
, _isCmd(false)
{
}

Expand Down Expand Up @@ -91,3 +92,13 @@ void SyncOptions::setPathPattern(const QString &pattern)
_fileRegex.setPatternOptions(Utility::fsCasePreserving() ? QRegularExpression::CaseInsensitiveOption : QRegularExpression::NoPatternOption);
_fileRegex.setPattern(pattern);
}

void SyncOptions::setIsCmd(const bool isCmd)
{
_isCmd = isCmd;
}

bool SyncOptions::isCmd() const
{
return _isCmd;
}
7 changes: 6 additions & 1 deletion src/libsync/syncoptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ class OWNCLOUDSYNC_EXPORT SyncOptions
*/
void verifyChunkSizes();


/** A regular expression to match file names
* If no pattern is provided the default is an invalid regular expression.
*/
Expand All @@ -109,6 +108,10 @@ class OWNCLOUDSYNC_EXPORT SyncOptions
*/
void setPathPattern(const QString &pattern);

/** sync had been started via nextcloudcmd command line */
[[nodiscard]] bool isCmd() const;
void setIsCmd(const bool isCmd);

private:
/**
* Only sync files that match the expression
Expand All @@ -118,6 +121,8 @@ class OWNCLOUDSYNC_EXPORT SyncOptions

qint64 _minChunkSize = chunkV2MinChunkSize;
qint64 _maxChunkSize = chunkV2MaxChunkSize;

bool _isCmd = false;
};

}
69 changes: 69 additions & 0 deletions test/testsyncengine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1906,6 +1906,75 @@ private slots:
QVERIFY(fakeFolder.syncOnce());
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
}

void testRemoveAllFilesWithNextcloudCmd()
{
FakeFolder fakeFolder{FileInfo{}};
auto nextcloudCmdSyncOptions = fakeFolder.syncEngine().syncOptions();
nextcloudCmdSyncOptions.setIsCmd(true);
fakeFolder.syncEngine().setSyncOptions(nextcloudCmdSyncOptions);
ConfigFile().setPromptDeleteFiles(true);
QSignalSpy displayDialogSignal(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveAllFiles);

fakeFolder.remoteModifier().mkdir("folder");
fakeFolder.remoteModifier().insert("folder/file1");
fakeFolder.remoteModifier().insert("folder/file2");
fakeFolder.remoteModifier().insert("folder/file3");
fakeFolder.remoteModifier().mkdir("folder2");
fakeFolder.remoteModifier().insert("file1");
fakeFolder.remoteModifier().insert("file2");
fakeFolder.remoteModifier().insert("file3");

QVERIFY(fakeFolder.syncOnce());

fakeFolder.remoteModifier().remove("folder");
fakeFolder.remoteModifier().remove("folder2");
fakeFolder.remoteModifier().remove("file1");
fakeFolder.remoteModifier().remove("file2");
fakeFolder.remoteModifier().remove("file3");

QVERIFY(fakeFolder.syncOnce());
// the signal to display the dialog should not be emitted
QCOMPARE(displayDialogSignal.count(), 0);
QCOMPARE(fakeFolder.remoteModifier().find("folder"), nullptr);
QCOMPARE(fakeFolder.remoteModifier().find("folder2"), nullptr);
QCOMPARE(fakeFolder.remoteModifier().find("file1"), nullptr);
}

void testRemoveAllFilesWithoutNextcloudCmd()
{
FakeFolder fakeFolder{FileInfo{}};
auto nextcloudCmdSyncOptions = fakeFolder.syncEngine().syncOptions();
nextcloudCmdSyncOptions.setIsCmd(false);
fakeFolder.syncEngine().setSyncOptions(nextcloudCmdSyncOptions);
ConfigFile().setPromptDeleteFiles(true);
QSignalSpy displayDialogSignal(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveAllFiles);

fakeFolder.remoteModifier().mkdir("folder");
fakeFolder.remoteModifier().insert("folder/file1");
fakeFolder.remoteModifier().insert("folder/file2");
fakeFolder.remoteModifier().insert("folder/file3");
fakeFolder.remoteModifier().mkdir("folder2");
fakeFolder.remoteModifier().insert("file1");
fakeFolder.remoteModifier().insert("file2");
fakeFolder.remoteModifier().insert("file3");

QVERIFY(fakeFolder.syncOnce());
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());

fakeFolder.remoteModifier().remove("folder");
fakeFolder.remoteModifier().remove("folder2");
fakeFolder.remoteModifier().remove("file1");
fakeFolder.remoteModifier().remove("file2");
fakeFolder.remoteModifier().remove("file3");

QVERIFY(fakeFolder.syncOnce());
// the signal to show the dialog should be emitted
QCOMPARE(displayDialogSignal.count(), 1);
QCOMPARE(fakeFolder.remoteModifier().find("folder"), nullptr);
QCOMPARE(fakeFolder.remoteModifier().find("folder2"), nullptr);
QCOMPARE(fakeFolder.remoteModifier().find("file1"), nullptr);
}
};

QTEST_GUILESS_MAIN(TestSyncEngine)
Expand Down

0 comments on commit 17fcf14

Please sign in to comment.