Move some of the SocketApi conflict handling in a ConflictSolver class
authorKevin Ottens <kevin.ottens@nextcloud.com>
Wed, 7 Oct 2020 14:30:43 +0000 (16:30 +0200)
committerKevin Ottens <kevin.ottens@nextcloud.com>
Thu, 22 Oct 2020 14:40:46 +0000 (16:40 +0200)
The socket api move and delete commands are not strictly about conflicts
since they also deal with files which couldn't be uploaded for some
other reason. Still the new ConflictSolver could be used in those cases.

This opens the door at reusing that logic in other places.

Signed-off-by: Kevin Ottens <kevin.ottens@nextcloud.com>
src/gui/CMakeLists.txt
src/gui/conflictsolver.cpp [new file with mode: 0644]
src/gui/conflictsolver.h [new file with mode: 0644]
src/gui/socketapi.cpp
test/CMakeLists.txt

index 97a3c19af4ac2c03ebcad1a1599c90f8a4f2c0f8..7ae36a983c646ecd59453879c9612e0b390ccd77 100644 (file)
@@ -54,6 +54,7 @@ set(client_SRCS
     accountmanager.cpp
     accountsettings.cpp
     application.cpp
+    conflictsolver.cpp
     connectionvalidator.cpp
     folder.cpp
     folderman.cpp
diff --git a/src/gui/conflictsolver.cpp b/src/gui/conflictsolver.cpp
new file mode 100644 (file)
index 0000000..578d15a
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) by Kevin Ottens <kevin.ottens@nextcloud.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "conflictsolver.h"
+
+#include <QFileDialog>
+#include <QMessageBox>
+
+#include "filesystem.h"
+
+namespace OCC {
+
+Q_LOGGING_CATEGORY(lcConflict, "nextcloud.gui.conflictsolver", QtInfoMsg)
+
+ConflictSolver::ConflictSolver(QWidget *parent)
+    : QObject(parent)
+    , _parentWidget(parent)
+{
+}
+
+QString ConflictSolver::localVersionFilename() const
+{
+    return _localVersionFilename;
+}
+
+QString ConflictSolver::remoteVersionFilename() const
+{
+    return _remoteVersionFilename;
+}
+
+bool ConflictSolver::exec(ConflictSolver::Solution solution)
+{
+    switch (solution) {
+    case KeepLocalVersion:
+        return overwriteRemoteVersion();
+    case KeepRemoteVersion:
+        return deleteLocalVersion();
+    }
+    Q_UNREACHABLE();
+    return false;
+}
+
+void ConflictSolver::setLocalVersionFilename(const QString &localVersionFilename)
+{
+    if (_localVersionFilename == localVersionFilename) {
+        return;
+    }
+
+    _localVersionFilename = localVersionFilename;
+    emit localVersionFilenameChanged();
+}
+
+void ConflictSolver::setRemoteVersionFilename(const QString &remoteVersionFilename)
+{
+    if (_remoteVersionFilename == remoteVersionFilename) {
+        return;
+    }
+
+    _remoteVersionFilename = remoteVersionFilename;
+    emit remoteVersionFilenameChanged();
+}
+
+bool ConflictSolver::deleteLocalVersion()
+{
+    if (_localVersionFilename.isEmpty()) {
+        return false;
+    }
+
+    QFileInfo info(_localVersionFilename);
+    if (!info.exists()) {
+        return false;
+    }
+
+    const auto message = info.isDir() ? tr("Do you want to delete the directory <i>%1</i> and all its contents permanently?").arg(info.dir().dirName())
+                                      : tr("Do you want to delete the file <i>%1</i> permanently?").arg(info.fileName());
+    const auto result = QMessageBox::question(_parentWidget, tr("Confirm deletion"), message, QMessageBox::Yes, QMessageBox::No);
+    if (result != QMessageBox::Yes)
+        return false;
+
+    if (info.isDir()) {
+        return FileSystem::removeRecursively(_localVersionFilename);
+    } else {
+        return QFile(_localVersionFilename).remove();
+    }
+}
+
+bool ConflictSolver::overwriteRemoteVersion()
+{
+    if (_localVersionFilename.isEmpty()) {
+        return false;
+    }
+
+    if (_remoteVersionFilename.isEmpty()) {
+        return false;
+    }
+
+    QFileInfo info(_localVersionFilename);
+    if (!info.exists()) {
+        return false;
+    }
+
+    QString error;
+    if (FileSystem::uncheckedRenameReplace(_localVersionFilename, _remoteVersionFilename, &error)) {
+        return true;
+    } else {
+        qCWarning(lcConflict) << "Rename error:" << error;
+        QMessageBox::warning(_parentWidget, tr("Error"), tr("Moving file failed:\n\n%1").arg(error));
+        return false;
+    }
+}
+
+} // namespace OCC
diff --git a/src/gui/conflictsolver.h b/src/gui/conflictsolver.h
new file mode 100644 (file)
index 0000000..e31e6eb
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) by Kevin Ottens <kevin.ottens@nextcloud.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef CONFLICTSOLVER_H
+#define CONFLICTSOLVER_H
+
+#include <QObject>
+
+class QWidget;
+
+namespace OCC {
+
+class ConflictSolver : public QObject
+{
+    Q_OBJECT
+    Q_PROPERTY(QString localVersionFilename READ localVersionFilename WRITE setLocalVersionFilename NOTIFY localVersionFilenameChanged)
+    Q_PROPERTY(QString remoteVersionFilename READ remoteVersionFilename WRITE setRemoteVersionFilename NOTIFY remoteVersionFilenameChanged)
+public:
+    enum Solution {
+        KeepLocalVersion,
+        KeepRemoteVersion
+    };
+
+    explicit ConflictSolver(QWidget *parent = nullptr);
+
+    QString localVersionFilename() const;
+    QString remoteVersionFilename() const;
+
+    bool exec(Solution solution);
+
+public slots:
+    void setLocalVersionFilename(const QString &localVersionFilename);
+    void setRemoteVersionFilename(const QString &remoteVersionFilename);
+
+signals:
+    void localVersionFilenameChanged();
+    void remoteVersionFilenameChanged();
+
+private:
+    bool deleteLocalVersion();
+    bool overwriteRemoteVersion();
+
+    QWidget *_parentWidget;
+    QString _localVersionFilename;
+    QString _remoteVersionFilename;
+};
+
+} // namespace OCC
+
+#endif // CONFLICTSOLVER_H
index e7bb0fa476dcc702a3d4e96cb73c1a2c7eabc717..3fa216919d08ae66e5d2d7ac261976c15615fa82 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "socketapi.h"
 
+#include "conflictsolver.h"
 #include "config.h"
 #include "configfile.h"
 #include "folderman.h"
@@ -691,22 +692,9 @@ void SocketApi::copyUrlToClipboard(const QString &link)
 
 void SocketApi::command_DELETE_ITEM(const QString &localFile, SocketListener *)
 {
-    QFileInfo info(localFile);
-
-    auto result = QMessageBox::question(
-        nullptr, tr("Confirm deletion"),
-        info.isDir()
-            ? tr("Do you want to delete the directory <i>%1</i> and all its contents permanently?").arg(info.dir().dirName())
-            : tr("Do you want to delete the file <i>%1</i> permanently?").arg(info.fileName()),
-        QMessageBox::Yes, QMessageBox::No);
-    if (result != QMessageBox::Yes)
-        return;
-
-    if (info.isDir()) {
-        FileSystem::removeRecursively(localFile);
-    } else {
-        QFile(localFile).remove();
-    }
+    ConflictSolver solver;
+    solver.setLocalVersionFilename(localFile);
+    solver.exec(ConflictSolver::KeepRemoteVersion);
 }
 
 void SocketApi::command_MOVE_ITEM(const QString &localFile, SocketListener *)
@@ -742,13 +730,9 @@ void SocketApi::command_MOVE_ITEM(const QString &localFile, SocketListener *)
     if (target.isEmpty())
         return;
 
-    QString error;
-    if (!FileSystem::uncheckedRenameReplace(localFile, target, &error)) {
-        qCWarning(lcSocketApi) << "Rename error:" << error;
-        QMessageBox::warning(
-            nullptr, tr("Error"),
-            tr("Moving file failed:\n\n%1").arg(error));
-    }
+    ConflictSolver solver;
+    solver.setLocalVersionFilename(localFile);
+    solver.setRemoteVersionFilename(target);
 }
 
 void SocketApi::emailPrivateLink(const QString &link)
index 40a22090c8ee5656350c6396734184f75838d482..864a99bb48a08b39d9d2048800b1fc1d530b4432 100644 (file)
@@ -63,6 +63,7 @@ nextcloud_add_benchmark(LargeSync "syncenginetestutils.h")
 
 SET(FolderMan_SRC ../src/gui/folderman.cpp)
 list(APPEND FolderMan_SRC ../src/gui/folder.cpp )
+list(APPEND FolderMan_SRC ../src/gui/conflictsolver.cpp )
 list(APPEND FolderMan_SRC ../src/gui/socketapi.cpp )
 list(APPEND FolderMan_SRC ../src/gui/syncrunfilelog.cpp )
 list(APPEND FolderMan_SRC ../src/gui/lockwatcher.cpp )
@@ -85,6 +86,7 @@ list(APPEND RemoteWipe_SRC ../src/gui/connectionvalidator.cpp )
 list(APPEND RemoteWipe_SRC ../src/gui/ocsjob.cpp )
 list(APPEND RemoteWipe_SRC ../src/gui/ocsnavigationappsjob.cpp )
 list(APPEND RemoteWipe_SRC ../src/gui/accountstate.cpp )
+list(APPEND RemoteWipe_SRC ../src/gui/conflictsolver.cpp )
 list(APPEND RemoteWipe_SRC ../src/gui/socketapi.cpp )
 list(APPEND RemoteWipe_SRC ../src/gui/folder.cpp )
 list(APPEND RemoteWipe_SRC ../src/gui/syncrunfilelog.cpp )