Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/owncloud/client.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/common/syncjournaldb.cpp16
-rw-r--r--src/common/syncjournaldb.h7
-rw-r--r--src/common/utility.cpp2
-rw-r--r--src/common/utility.h8
-rw-r--r--src/gui/socketapi.cpp116
-rw-r--r--src/gui/socketapi.h3
-rw-r--r--src/libsync/filesystem.h1
-rw-r--r--src/libsync/syncengine.cpp2
-rw-r--r--test/testsyncconflict.cpp4
9 files changed, 150 insertions, 9 deletions
diff --git a/src/common/syncjournaldb.cpp b/src/common/syncjournaldb.cpp
index e052b8502..a8578effa 100644
--- a/src/common/syncjournaldb.cpp
+++ b/src/common/syncjournaldb.cpp
@@ -1954,6 +1954,22 @@ QByteArrayList SyncJournalDb::conflictRecordPaths()
return paths;
}
+QByteArray SyncJournalDb::conflictFileBaseName(const QByteArray &conflictName)
+{
+ auto conflict = conflictRecord(conflictName);
+ QByteArray result;
+ if (conflict.isValid()) {
+ getFileRecordsByFileId(conflict.baseFileId, [&result](const SyncJournalFileRecord &record) {
+ if (!record._path.isEmpty())
+ result = record._path;
+ });
+ }
+
+ if (result.isEmpty())
+ result = Utility::conflictFileBaseNameFromPattern(conflictName);
+ return result;
+}
+
void SyncJournalDb::clearFileTable()
{
QMutexLocker lock(&_mutex);
diff --git a/src/common/syncjournaldb.h b/src/common/syncjournaldb.h
index 4db859bbe..6f174b46d 100644
--- a/src/common/syncjournaldb.h
+++ b/src/common/syncjournaldb.h
@@ -237,6 +237,13 @@ public:
/// Return all paths of files with a conflict tag in the name and records in the db
QByteArrayList conflictRecordPaths();
+ /** Find the base name for a conflict file name, using journal or name pattern
+ *
+ * The path must by sync-folder relative.
+ *
+ * Will return an empty string if it's not even a conflict file by pattern.
+ */
+ QByteArray conflictFileBaseName(const QByteArray &conflictName);
/**
* Delete any file entry. This will force the next sync to re-sync everything as if it was new,
diff --git a/src/common/utility.cpp b/src/common/utility.cpp
index d2a46bb7c..28d2ea3e5 100644
--- a/src/common/utility.cpp
+++ b/src/common/utility.cpp
@@ -603,7 +603,7 @@ bool Utility::isConflictFile(const QString &name)
return false;
}
-QByteArray Utility::conflictFileBaseName(const QByteArray &conflictName)
+QByteArray Utility::conflictFileBaseNameFromPattern(const QByteArray &conflictName)
{
// This function must be able to deal with conflict files for conflict files.
// To do this, we scan backwards, for the outermost conflict marker and
diff --git a/src/common/utility.h b/src/common/utility.h
index 5e1ff930e..f6628c3e4 100644
--- a/src/common/utility.h
+++ b/src/common/utility.h
@@ -40,6 +40,8 @@ class QSettings;
namespace OCC {
+class SyncJournal;
+
Q_DECLARE_LOGGING_CATEGORY(lcUtility)
/** \addtogroup libsync
@@ -206,14 +208,14 @@ namespace Utility {
OCSYNC_EXPORT bool isConflictFile(const char *name);
OCSYNC_EXPORT bool isConflictFile(const QString &name);
- /** Find the base name for a conflict file name
+ /** Find the base name for a conflict file name, using name pattern only
*
* Will return an empty string if it's not a conflict file.
*
* Prefer to use the data from the conflicts table in the journal to determine
- * a conflict's base file.
+ * a conflict's base file, see SyncJournal::conflictFileBaseName()
*/
- OCSYNC_EXPORT QByteArray conflictFileBaseName(const QByteArray &conflictName);
+ OCSYNC_EXPORT QByteArray conflictFileBaseNameFromPattern(const QByteArray &conflictName);
#ifdef Q_OS_WIN
OCSYNC_EXPORT QVariant registryGetKeyValue(HKEY hRootKey, const QString &subKey, const QString &valueName);
diff --git a/src/gui/socketapi.cpp b/src/gui/socketapi.cpp
index e4ebd4ffc..c1c77f354 100644
--- a/src/gui/socketapi.cpp
+++ b/src/gui/socketapi.cpp
@@ -49,7 +49,7 @@
#include <QLocalSocket>
#include <QStringBuilder>
#include <QMessageBox>
-
+#include <QFileDialog>
#include <QClipboard>
#include <QStandardPaths>
@@ -621,6 +621,68 @@ void SocketApi::command_DOWNLOAD_VIRTUAL_FILE(const QString &filesArg, SocketLis
}
}
+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();
+ }
+}
+
+void SocketApi::command_MOVE_ITEM(const QString &localFile, SocketListener *)
+{
+ auto fileData = FileData::get(localFile);
+ auto parentDir = fileData.parentFolder();
+ if (!fileData.folder)
+ return; // should not have shown menu item
+
+ QString defaultDirAndName = fileData.folderRelativePath;
+
+ // If it's a conflict, we want to save it under the base name by default
+ if (Utility::isConflictFile(defaultDirAndName)) {
+ defaultDirAndName = fileData.folder->journalDb()->conflictFileBaseName(fileData.folderRelativePath.toUtf8());
+ }
+
+ // If the parent doesn't accept new files, go to the root of the sync folder
+ QFileInfo fileInfo(localFile);
+ auto parentRecord = parentDir.journalRecord();
+ if ((fileInfo.isFile() && !parentRecord._remotePerm.hasPermission(RemotePermissions::CanAddFile))
+ || (fileInfo.isDir() && !parentRecord._remotePerm.hasPermission(RemotePermissions::CanAddSubDirectories))) {
+ defaultDirAndName = QFileInfo(defaultDirAndName).fileName();
+ }
+
+ // Add back the folder path
+ defaultDirAndName = QDir(fileData.folder->path()).filePath(defaultDirAndName);
+
+ auto target = QFileDialog::getSaveFileName(
+ nullptr,
+ tr("Select new location..."),
+ defaultDirAndName,
+ QString(), nullptr, QFileDialog::HideNameFilterDetails);
+ 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));
+ }
+}
+
void SocketApi::emailPrivateLink(const QString &link)
{
Utility::openEmailComposer(
@@ -724,6 +786,11 @@ SyncJournalFileRecord SocketApi::FileData::journalRecord() const
return record;
}
+SocketApi::FileData SocketApi::FileData::parentFolder() const
+{
+ return FileData::get(QFileInfo(localPath).dir().path().toUtf8());
+}
+
void SocketApi::command_GET_MENU_ITEMS(const QString &argument, OCC::SocketListener *listener)
{
listener->sendMessage(QString("GET_MENU_ITEMS:BEGIN"));
@@ -747,12 +814,57 @@ void SocketApi::command_GET_MENU_ITEMS(const QString &argument, OCC::SocketListe
// Some options only show for single files
if (files.size() == 1) {
FileData fileData = FileData::get(files.first());
- bool isOnTheServer = fileData.journalRecord().isValid();
+ auto record = fileData.journalRecord();
+ bool isOnTheServer = record.isValid();
auto flagString = isOnTheServer ? QLatin1String("::") : QLatin1String(":d:");
if (fileData.folder && fileData.folder->accountState()->isConnected()) {
sendSharingContextMenuOptions(fileData, listener);
listener->sendMessage(QLatin1String("MENU_ITEM:OPEN_PRIVATE_LINK") + flagString + tr("Open in browser"));
+
+ // Conflict files get conflict resolution actions
+ bool isConflict = Utility::isConflictFile(fileData.folderRelativePath);
+ if (isConflict || !isOnTheServer) {
+ // Check whether this new file is in a read-only directory
+ QFileInfo fileInfo(fileData.localPath);
+ auto parentDir = fileData.parentFolder();
+ auto parentRecord = parentDir.journalRecord();
+ bool canAddToDir =
+ (fileInfo.isFile() && !parentRecord._remotePerm.hasPermission(RemotePermissions::CanAddFile))
+ || (fileInfo.isDir() && !parentRecord._remotePerm.hasPermission(RemotePermissions::CanAddSubDirectories));
+ bool canChangeFile =
+ !isOnTheServer
+ || (record._remotePerm.hasPermission(RemotePermissions::CanDelete)
+ && record._remotePerm.hasPermission(RemotePermissions::CanMove)
+ && record._remotePerm.hasPermission(RemotePermissions::CanRename));
+
+ if (isConflict && canChangeFile) {
+ if (canAddToDir) {
+ if (isOnTheServer) {
+ // Conflict file that is already uploaded
+ listener->sendMessage(QLatin1String("MENU_ITEM:MOVE_ITEM::") + tr("Rename..."));
+ } else {
+ // Local-only conflict file
+ listener->sendMessage(QLatin1String("MENU_ITEM:MOVE_ITEM::") + tr("Rename and upload..."));
+ }
+ } else {
+ if (isOnTheServer) {
+ // Uploaded conflict file in read-only directory
+ listener->sendMessage(QLatin1String("MENU_ITEM:MOVE_ITEM::") + tr("Move and rename..."));
+ } else {
+ // Local-only conflict file in a read-only dir
+ listener->sendMessage(QLatin1String("MENU_ITEM:MOVE_ITEM::") + tr("Move, rename and upload..."));
+ }
+ }
+ listener->sendMessage(QLatin1String("MENU_ITEM:DELETE_ITEM::") + tr("Delete local changes"));
+ }
+
+ // File in a read-only directory?
+ if (!isConflict && !isOnTheServer && !canAddToDir) {
+ listener->sendMessage(QLatin1String("MENU_ITEM:MOVE_ITEM::") + tr("Move and upload..."));
+ listener->sendMessage(QLatin1String("MENU_ITEM:DELETE_ITEM::") + tr("Delete"));
+ }
+ }
}
}
diff --git a/src/gui/socketapi.h b/src/gui/socketapi.h
index 5666c48b9..4ab576d4e 100644
--- a/src/gui/socketapi.h
+++ b/src/gui/socketapi.h
@@ -78,6 +78,7 @@ private:
static FileData get(const QString &localFile);
SyncFileStatus syncFileStatus() const;
SyncJournalFileRecord journalRecord() const;
+ FileData parentFolder() const;
Folder *folder;
QString localPath;
@@ -105,6 +106,8 @@ private:
Q_INVOKABLE void command_EMAIL_PRIVATE_LINK(const QString &localFile, SocketListener *listener);
Q_INVOKABLE void command_OPEN_PRIVATE_LINK(const QString &localFile, SocketListener *listener);
Q_INVOKABLE void command_DOWNLOAD_VIRTUAL_FILE(const QString &filesArg, SocketListener *listener);
+ Q_INVOKABLE void command_DELETE_ITEM(const QString &localFile, SocketListener *listener);
+ Q_INVOKABLE void command_MOVE_ITEM(const QString &localFile, SocketListener *listener);
// Fetch the private link and call targetFun
void fetchPrivateLinkUrlHelper(const QString &localFile, const std::function<void(const QString &url)> &targetFun);
diff --git a/src/libsync/filesystem.h b/src/libsync/filesystem.h
index 2dbbdb5c8..70dffef42 100644
--- a/src/libsync/filesystem.h
+++ b/src/libsync/filesystem.h
@@ -18,6 +18,7 @@
#include <QString>
#include <ctime>
+#include <functional>
#include <owncloudlib.h>
// Chain in the base include and extend the namespace
diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp
index e3cb90e66..5227a8873 100644
--- a/src/libsync/syncengine.cpp
+++ b/src/libsync/syncengine.cpp
@@ -352,7 +352,7 @@ void SyncEngine::conflictRecordMaintenance()
record.path = bapath;
// Determine fileid of target file
- auto basePath = Utility::conflictFileBaseName(bapath);
+ auto basePath = Utility::conflictFileBaseNameFromPattern(bapath);
SyncJournalFileRecord baseRecord;
if (_journal->getFileRecord(basePath, &baseRecord) && baseRecord.isValid()) {
record.baseFileId = baseRecord._fileId;
diff --git a/test/testsyncconflict.cpp b/test/testsyncconflict.cpp
index 8703ccc41..92d330ac1 100644
--- a/test/testsyncconflict.cpp
+++ b/test/testsyncconflict.cpp
@@ -125,7 +125,7 @@ private slots:
QVERIFY(conflictMap.contains(a1FileId));
QVERIFY(conflictMap.contains(a2FileId));
QCOMPARE(conflictMap.size(), 2);
- QCOMPARE(Utility::conflictFileBaseName(conflictMap[a1FileId].toUtf8()), QByteArray("A/a1"));
+ QCOMPARE(Utility::conflictFileBaseNameFromPattern(conflictMap[a1FileId].toUtf8()), QByteArray("A/a1"));
// Check that the conflict file contains the username
QVERIFY(conflictMap[a1FileId].contains(QString("(conflicted copy %1 ").arg(fakeFolder.syncEngine().account()->davDisplayName())));
@@ -384,7 +384,7 @@ private slots:
{
QFETCH(QString, input);
QFETCH(QString, output);
- QCOMPARE(Utility::conflictFileBaseName(input.toUtf8()), output.toUtf8());
+ QCOMPARE(Utility::conflictFileBaseNameFromPattern(input.toUtf8()), output.toUtf8());
}
void testLocalDirRemoteFileConflict()