Browse Source

Move some of the SocketApi conflict handling in a ConflictSolver class

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>
Kevin Ottens 5 years ago
parent
commit
4fdb203cdb
5 changed files with 194 additions and 23 deletions
  1. 1 0
      src/gui/CMakeLists.txt
  2. 123 0
      src/gui/conflictsolver.cpp
  3. 61 0
      src/gui/conflictsolver.h
  4. 7 23
      src/gui/socketapi.cpp
  5. 2 0
      test/CMakeLists.txt

+ 1 - 0
src/gui/CMakeLists.txt

@@ -54,6 +54,7 @@ set(client_SRCS
     accountmanager.cpp
     accountsettings.cpp
     application.cpp
+    conflictsolver.cpp
     connectionvalidator.cpp
     folder.cpp
     folderman.cpp

+ 123 - 0
src/gui/conflictsolver.cpp

@@ -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

+ 61 - 0
src/gui/conflictsolver.h

@@ -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

+ 7 - 23
src/gui/socketapi.cpp

@@ -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)

+ 2 - 0
test/CMakeLists.txt

@@ -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 )