Virtual Files: Allow to download a folder recursively from the socket API
authorOlivier Goffart <ogoffart@woboq.com>
Mon, 28 May 2018 12:49:02 +0000 (14:49 +0200)
committerKevin Ottens <kevin.ottens@nextcloud.com>
Tue, 15 Dec 2020 09:57:54 +0000 (10:57 +0100)
Issue: #6466

src/common/syncjournaldb.cpp
src/common/syncjournaldb.h
src/gui/folder.cpp
src/gui/folder.h
src/gui/socketapi.cpp
test/testsyncvirtualfiles.cpp

index 41cf3e0bfcc8a6cd374034d562aa86fe226249d9..e61ef0970b67faa4ad5efeea7a4ff9079819b5fc 100644 (file)
@@ -2062,6 +2062,25 @@ void SyncJournalDb::clearFileTable()
     query.exec();
 }
 
+void SyncJournalDb::markVirtualFileForDownloadRecursively(const QByteArray &path)
+{
+    QMutexLocker lock(&_mutex);
+    if (!checkConnect())
+        return;
+
+    static_assert(ItemTypeVirtualFile == 4 && ItemTypeVirtualFileDownload == 5, "");
+    SqlQuery query("UPDATE metadata SET type=5 WHERE " IS_PREFIX_PATH_OF("?1", "path") " AND type=4;", _db);
+    query.bindValue(1, path);
+    query.exec();
+
+    // We also must make sure we do not read the files from the database (same logic as in avoidReadFromDbOnNextSync)
+    // This includes all the parents up to the root, but also all the directory within the selected dir.
+    static_assert(ItemTypeDirectory == 2, "");
+    query.prepare("UPDATE metadata SET md5='_invalid_' WHERE (" IS_PREFIX_PATH_OF("?1", "path") " OR " IS_PREFIX_PATH_OR_EQUAL("path", "?1") ") AND type == 2;");
+    query.bindValue(1, path);
+    query.exec();
+}
+
 void SyncJournalDb::commit(const QString &context, bool startTrans)
 {
     QMutexLocker lock(&_mutex);
index de93bdc5e3d170fbf35ded9058588608b1689d1c..f9992a71e2721d2d25ff322f8afbad4a3ae8898f 100644 (file)
@@ -241,6 +241,12 @@ public:
      */
     void clearFileTable();
 
+    /**
+     * Set the 'ItemTypeVirtualFileDownload' to all the files that have the ItemTypeVirtualFile flag
+     * within the directory specified path path
+     */
+    void markVirtualFileForDownloadRecursively(const QByteArray &path);
+
 private:
     int getFileRecordCount();
     bool updateDatabaseStructure();
index 0e24fffdc2fd6b4f8b474869a67d1805ebdae989..56fa5640128439ab984c4163083f3ffd869ebdc7 100644 (file)
@@ -532,11 +532,16 @@ void Folder::downloadVirtualFile(const QString &_relativepath)
     _journal.getFileRecord(relativepath, &record);
     if (!record.isValid())
         return;
-    record._type = ItemTypeVirtualFileDownload;
-    _journal.setFileRecord(record);
-
-    // Make sure we go over that file during the discovery
-    _journal.avoidReadFromDbOnNextSync(relativepath);
+    if (record._type == ItemTypeVirtualFile) {
+        record._type = ItemTypeVirtualFileDownload;
+        _journal.setFileRecord(record);
+        // Make sure we go over that file during the discovery
+        _journal.avoidReadFromDbOnNextSync(relativepath);
+    } else if (record._type == ItemTypeDirectory) {
+        _journal.markVirtualFileForDownloadRecursively(relativepath);
+    } else {
+        qCWarning(lcFolder) << "Invalid existing record " << record._type << " for file " << _relativepath;
+    }
 
     // Schedule a sync (Folder man will start the sync in a few ms)
     slotScheduleThisFolder();
index e1d642f3e3fd031f45c292a72020821b2a7d2ddb..46d3499e3c9d8d8581d69f8bbe1d34fda8e28734 100644 (file)
@@ -236,6 +236,9 @@ public:
      */
     void registerFolderWatcher();
 
+    /** new files are downloaded as virtual files */
+    bool useVirtualFiles() { return _definition.useVirtualFiles; }
+
 signals:
     void syncStateChange();
     void syncStarted();
index c1ccdc2ea33dd14402db7a48f26e2c7be0575479..3f222e949eaf1dda9043bffddde8b95336ac6726 100644 (file)
@@ -692,7 +692,7 @@ void SocketApi::command_DOWNLOAD_VIRTUAL_FILE(const QString &filesArg, SocketLis
     auto suffix = QStringLiteral(APPLICATION_DOTVIRTUALFILE_SUFFIX);
 
     for (const auto &file : files) {
-        if (!file.endsWith(suffix))
+        if (!file.endsWith(suffix) && !QFileInfo(file).isDir())
             continue;
         auto folder = FolderMan::instance()->folderForPath(file);
         if (folder) {
@@ -980,7 +980,7 @@ void SocketApi::command_GET_MENU_ITEMS(const QString &argument, OCC::SocketListe
         auto virtualFileSuffix = QStringLiteral(APPLICATION_DOTVIRTUALFILE_SUFFIX);
         bool hasVirtualFile = false;
         for (const auto &file : files) {
-            if (file.endsWith(virtualFileSuffix))
+            if (file.endsWith(virtualFileSuffix) || (syncFolder->useVirtualFiles() && QFileInfo(file).isDir()))
                 hasVirtualFile = true;
         }
         if (hasVirtualFile)
index 0bcd33c51da72f77a8f482bb8eb2b1154dbf0c35..db677a3064571e3b7ee0c865cff595e39253a75a 100644 (file)
@@ -485,6 +485,105 @@ private slots:
         QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
         QVERIFY(!dbRecord(fakeFolder, "A/a1.owncloud").isValid());
     }
+
+    void testDownloadRecursive()
+    {
+        FakeFolder fakeFolder{ FileInfo() };
+        SyncOptions syncOptions;
+        syncOptions._newFilesAreVirtual = true;
+        fakeFolder.syncEngine().setSyncOptions(syncOptions);
+        QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
+
+        // Create a virtual file for remote files
+        fakeFolder.remoteModifier().mkdir("A");
+        fakeFolder.remoteModifier().mkdir("A/Sub");
+        fakeFolder.remoteModifier().mkdir("A/Sub/SubSub");
+        fakeFolder.remoteModifier().mkdir("A/Sub2");
+        fakeFolder.remoteModifier().mkdir("B");
+        fakeFolder.remoteModifier().mkdir("B/Sub");
+        fakeFolder.remoteModifier().insert("A/a1");
+        fakeFolder.remoteModifier().insert("A/a2");
+        fakeFolder.remoteModifier().insert("A/Sub/a3");
+        fakeFolder.remoteModifier().insert("A/Sub/a4");
+        fakeFolder.remoteModifier().insert("A/Sub/SubSub/a5");
+        fakeFolder.remoteModifier().insert("A/Sub2/a6");
+        fakeFolder.remoteModifier().insert("B/b1");
+        fakeFolder.remoteModifier().insert("B/Sub/b2");
+        QVERIFY(fakeFolder.syncOnce());
+        QVERIFY(fakeFolder.currentLocalState().find("A/a1.owncloud"));
+        QVERIFY(fakeFolder.currentLocalState().find("A/a2.owncloud"));
+        QVERIFY(fakeFolder.currentLocalState().find("A/Sub/a3.owncloud"));
+        QVERIFY(fakeFolder.currentLocalState().find("A/Sub/a4.owncloud"));
+        QVERIFY(fakeFolder.currentLocalState().find("A/Sub/SubSub/a5.owncloud"));
+        QVERIFY(fakeFolder.currentLocalState().find("A/Sub2/a6.owncloud"));
+        QVERIFY(fakeFolder.currentLocalState().find("B/b1.owncloud"));
+        QVERIFY(fakeFolder.currentLocalState().find("B/Sub/b2.owncloud"));
+        QVERIFY(!fakeFolder.currentLocalState().find("A/a1"));
+        QVERIFY(!fakeFolder.currentLocalState().find("A/a2"));
+        QVERIFY(!fakeFolder.currentLocalState().find("A/Sub/a3"));
+        QVERIFY(!fakeFolder.currentLocalState().find("A/Sub/a4"));
+        QVERIFY(!fakeFolder.currentLocalState().find("A/Sub/SubSub/a5"));
+        QVERIFY(!fakeFolder.currentLocalState().find("A/Sub2/a6"));
+        QVERIFY(!fakeFolder.currentLocalState().find("B/b1"));
+        QVERIFY(!fakeFolder.currentLocalState().find("B/Sub/b2"));
+
+
+        // Download All file in the directory A/Sub
+        // (as in Folder::downloadVirtualFile)
+        fakeFolder.syncJournal().markVirtualFileForDownloadRecursively("A/Sub");
+
+        QVERIFY(fakeFolder.syncOnce());
+        QVERIFY(fakeFolder.currentLocalState().find("A/a1.owncloud"));
+        QVERIFY(fakeFolder.currentLocalState().find("A/a2.owncloud"));
+        QVERIFY(!fakeFolder.currentLocalState().find("A/Sub/a3.owncloud"));
+        QVERIFY(!fakeFolder.currentLocalState().find("A/Sub/a4.owncloud"));
+        QVERIFY(!fakeFolder.currentLocalState().find("A/Sub/SubSub/a5.owncloud"));
+        QVERIFY(fakeFolder.currentLocalState().find("A/Sub2/a6.owncloud"));
+        QVERIFY(fakeFolder.currentLocalState().find("B/b1.owncloud"));
+        QVERIFY(fakeFolder.currentLocalState().find("B/Sub/b2.owncloud"));
+        QVERIFY(!fakeFolder.currentLocalState().find("A/a1"));
+        QVERIFY(!fakeFolder.currentLocalState().find("A/a2"));
+        QVERIFY(fakeFolder.currentLocalState().find("A/Sub/a3"));
+        QVERIFY(fakeFolder.currentLocalState().find("A/Sub/a4"));
+        QVERIFY(fakeFolder.currentLocalState().find("A/Sub/SubSub/a5"));
+        QVERIFY(!fakeFolder.currentLocalState().find("A/Sub2/a6"));
+        QVERIFY(!fakeFolder.currentLocalState().find("B/b1"));
+        QVERIFY(!fakeFolder.currentLocalState().find("B/Sub/b2"));
+
+        // Add a file in a subfolder that was downloaded
+        // Currently, this continue to add it as a virtual file.
+        fakeFolder.remoteModifier().insert("A/Sub/SubSub/a7");
+        QVERIFY(fakeFolder.syncOnce());
+        QVERIFY(fakeFolder.currentLocalState().find("A/Sub/SubSub/a7.owncloud"));
+        QVERIFY(!fakeFolder.currentLocalState().find("A/Sub/SubSub/a7"));
+
+        // Now download all files in "A"
+        fakeFolder.syncJournal().markVirtualFileForDownloadRecursively("A");
+        QVERIFY(fakeFolder.syncOnce());
+        QVERIFY(!fakeFolder.currentLocalState().find("A/a1.owncloud"));
+        QVERIFY(!fakeFolder.currentLocalState().find("A/a2.owncloud"));
+        QVERIFY(!fakeFolder.currentLocalState().find("A/Sub/a3.owncloud"));
+        QVERIFY(!fakeFolder.currentLocalState().find("A/Sub/a4.owncloud"));
+        QVERIFY(!fakeFolder.currentLocalState().find("A/Sub/SubSub/a5.owncloud"));
+        QVERIFY(!fakeFolder.currentLocalState().find("A/Sub2/a6.owncloud"));
+        QVERIFY(!fakeFolder.currentLocalState().find("A/Sub/SubSub/a7.owncloud"));
+        QVERIFY(fakeFolder.currentLocalState().find("B/b1.owncloud"));
+        QVERIFY(fakeFolder.currentLocalState().find("B/Sub/b2.owncloud"));
+        QVERIFY(fakeFolder.currentLocalState().find("A/a1"));
+        QVERIFY(fakeFolder.currentLocalState().find("A/a2"));
+        QVERIFY(fakeFolder.currentLocalState().find("A/Sub/a3"));
+        QVERIFY(fakeFolder.currentLocalState().find("A/Sub/a4"));
+        QVERIFY(fakeFolder.currentLocalState().find("A/Sub/SubSub/a5"));
+        QVERIFY(fakeFolder.currentLocalState().find("A/Sub2/a6"));
+        QVERIFY(fakeFolder.currentLocalState().find("A/Sub/SubSub/a7"));
+        QVERIFY(!fakeFolder.currentLocalState().find("B/b1"));
+        QVERIFY(!fakeFolder.currentLocalState().find("B/Sub/b2"));
+
+        // Now download remaining files in "B"
+        fakeFolder.syncJournal().markVirtualFileForDownloadRecursively("B");
+        QVERIFY(fakeFolder.syncOnce());
+        QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
+    }
 };
 
 QTEST_GUILESS_MAIN(TestSyncVirtualFiles)