From 75f0e53ce6ecaeac841bf52c7f184f21c51843a8 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 27 Jun 2023 15:28:53 +0800 Subject: [PATCH 01/57] Const autofy variables in ProcessDirectoryJob::processFileAnalyzeRemoteInfo Signed-off-by: Claudio Cambra --- src/libsync/discovery.cpp | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp index dcabd45e11c7..d818359afb71 100644 --- a/src/libsync/discovery.cpp +++ b/src/libsync/discovery.cpp @@ -593,7 +593,15 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo( item->_lockEditorApp = serverEntry.lockEditorApp; item->_lockTime = serverEntry.lockTime; item->_lockTimeout = serverEntry.lockTimeout; - qCDebug(lcDisco()) << item->_locked << item->_lockOwnerDisplayName << item->_lockOwnerId << item->_lockOwnerType << item->_lockEditorApp << item->_lockTime << item->_lockTimeout; + + qCDebug(lcDisco()) << "item lock for:" << item->_file + << item->_locked + << item->_lockOwnerDisplayName + << item->_lockOwnerId + << item->_lockOwnerType + << item->_lockEditorApp + << item->_lockTime + << item->_lockTimeout; // Check for missing server data { @@ -640,11 +648,11 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo( // The file is known in the db already if (dbEntry.isValid()) { - const bool isDbEntryAnE2EePlaceholder = dbEntry.isVirtualFile() && !dbEntry.e2eMangledName().isEmpty(); + const auto isDbEntryAnE2EePlaceholder = dbEntry.isVirtualFile() && !dbEntry.e2eMangledName().isEmpty(); Q_ASSERT(!isDbEntryAnE2EePlaceholder || serverEntry.size >= Constants::e2EeTagSize); - const bool isVirtualE2EePlaceholder = isDbEntryAnE2EePlaceholder && serverEntry.size >= Constants::e2EeTagSize; - const qint64 sizeOnServer = isVirtualE2EePlaceholder ? serverEntry.size - Constants::e2EeTagSize : serverEntry.size; - const bool metaDataSizeNeedsUpdateForE2EeFilePlaceholder = isVirtualE2EePlaceholder && dbEntry._fileSize == serverEntry.size; + const auto isVirtualE2EePlaceholder = isDbEntryAnE2EePlaceholder && serverEntry.size >= Constants::e2EeTagSize; + const auto sizeOnServer = isVirtualE2EePlaceholder ? serverEntry.size - Constants::e2EeTagSize : serverEntry.size; + const auto metaDataSizeNeedsUpdateForE2EeFilePlaceholder = isVirtualE2EePlaceholder && dbEntry._fileSize == serverEntry.size; if (serverEntry.isDirectory != dbEntry.isDirectory()) { // If the type of the entity changed, it's like NEW, but @@ -696,7 +704,7 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo( // to update a placeholder with corrected size (-16 Bytes) // or, maybe, add a flag to the database - vfsE2eeSizeCorrected? if it is not set - subtract it from the placeholder's size and re-create/update a placeholder? const QueryMode serverQueryMode = [this, &dbEntry, &serverEntry]() { - const bool isVfsModeOn = _discoveryData && _discoveryData->_syncOptions._vfs && _discoveryData->_syncOptions._vfs->mode() != Vfs::Off; + const auto isVfsModeOn = _discoveryData && _discoveryData->_syncOptions._vfs && _discoveryData->_syncOptions._vfs->mode() != Vfs::Off; if (isVfsModeOn && dbEntry.isDirectory() && dbEntry.isE2eEncrypted()) { qint64 localFolderSize = 0; const auto listFilesCallback = [&localFolderSize](const OCC::SyncJournalFileRecord &record) { @@ -709,7 +717,7 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo( } }; - const bool listFilesSucceeded = _discoveryData->_statedb->listFilesInPath(dbEntry.path().toUtf8(), listFilesCallback); + const auto listFilesSucceeded = _discoveryData->_statedb->listFilesInPath(dbEntry.path().toUtf8(), listFilesCallback); if (listFilesSucceeded && localFolderSize != 0 && localFolderSize == serverEntry.sizeOfFolder) { qCInfo(lcDisco) << "Migration of E2EE folder " << dbEntry.path() << " from older version to the one, supporting the implicit VFS hydration."; @@ -735,7 +743,7 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo( item->_modtime = serverEntry.modtime; item->_size = serverEntry.size; - auto conflictRecord = _discoveryData->_statedb->caseConflictRecordByBasePath(item->_file); + const auto conflictRecord = _discoveryData->_statedb->caseConflictRecordByBasePath(item->_file); if (conflictRecord.isValid() && QString::fromUtf8(conflictRecord.path).contains(QStringLiteral("(case clash from"))) { qCInfo(lcDisco) << "should ignore" << item->_file << "has already a case clash conflict record" << conflictRecord.path; @@ -758,7 +766,7 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo( return; } // Turn new remote files into virtual files if the option is enabled. - auto &opts = _discoveryData->_syncOptions; + const auto opts = _discoveryData->_syncOptions; if (!localEntry.isValid() && item->_type == ItemTypeFile && opts._vfs->mode() != Vfs::Off @@ -831,7 +839,7 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo( } // Now we know there is a sane rename candidate. - QString originalPath = base.path(); + const auto originalPath = base.path(); if (_discoveryData->isRenamed(originalPath)) { qCInfo(lcDisco, "folder already has a rename entry, skipping"); @@ -847,7 +855,7 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo( return; } - QString originalPathAdjusted = _discoveryData->adjustRenamedPath(originalPath, SyncFileItem::Up); + const auto originalPathAdjusted = _discoveryData->adjustRenamedPath(originalPath, SyncFileItem::Up); if (!base.isDirectory()) { csync_file_stat_t buf; @@ -873,10 +881,10 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo( item->_type = ItemTypeVirtualFile; } - bool wasDeletedOnServer = _discoveryData->findAndCancelDeletedJob(originalPath).first; + const auto wasDeletedOnServer = _discoveryData->findAndCancelDeletedJob(originalPath).first; auto postProcessRename = [this, item, base, originalPath](PathTuple &path) { - auto adjustedOriginalPath = _discoveryData->adjustRenamedPath(originalPath, SyncFileItem::Up); + const auto adjustedOriginalPath = _discoveryData->adjustRenamedPath(originalPath, SyncFileItem::Up); _discoveryData->_renamedItemsRemote.insert(originalPath, path._target); item->_modtime = base._modtime; item->_inode = base._inode; @@ -896,7 +904,7 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo( } else { // we need to make a request to the server to know that the original file is deleted on the server _pendingAsyncJobs++; - auto job = new RequestEtagJob(_discoveryData->_account, _discoveryData->_remoteFolder + originalPath, this); + const auto job = new RequestEtagJob(_discoveryData->_account, _discoveryData->_remoteFolder + originalPath, this); connect(job, &RequestEtagJob::finishedWithResult, this, [=](const HttpResult &etag) mutable { _pendingAsyncJobs--; QTimer::singleShot(0, _discoveryData, &DiscoveryPhase::scheduleMoreJobs); From 17de46cec2bef59016985ccbb71c69356bf2b755 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 27 Jun 2023 15:56:30 +0800 Subject: [PATCH 02/57] Clean up declaration of DiscoveryPhase::checkSelectiveSyncNewFolder by using const auto, renaming single letter variables, more Signed-off-by: Claudio Cambra --- src/libsync/discoveryphase.cpp | 76 +++++++++++++++++++--------------- src/libsync/discoveryphase.h | 8 +++- 2 files changed, 49 insertions(+), 35 deletions(-) diff --git a/src/libsync/discoveryphase.cpp b/src/libsync/discoveryphase.cpp index 3142a2968a74..3fef366c5634 100644 --- a/src/libsync/discoveryphase.cpp +++ b/src/libsync/discoveryphase.cpp @@ -82,8 +82,48 @@ bool DiscoveryPhase::isInSelectiveSyncBlackList(const QString &path) const return false; } -void DiscoveryPhase::checkSelectiveSyncNewFolder(const QString &path, RemotePermissions remotePerm, - std::function callback) +void DiscoveryPhase::checkFolderSizeLimit(const QString &path, + const std::function callback) +{ + const auto limit = _syncOptions._newBigFolderSizeLimit; + if (limit < 0 || _syncOptions._vfs->mode() != Vfs::Off) { + // no limit, everything is allowed; + return callback(false); + } + + // do a PROPFIND to know the size of this folder + const auto propfindJob = new PropfindJob(_account, _remoteFolder + path, this); + propfindJob->setProperties(QList() << "resourcetype" + << "http://owncloud.org/ns:size"); + + connect(propfindJob, &PropfindJob::finishedWithError, this, [=] { return callback(false); }); + connect(propfindJob, &PropfindJob::result, this, [=](const QVariantMap &values) { + auto result = values.value(QLatin1String("size")).toLongLong(); + if (result >= limit) { + // we tell the UI there is a new folder + emit newBigFolder(path, false); + return callback(true); + } else { + // it is not too big, put it in the white list (so we will not do more query for the children) + // and and do not block. + auto sanitisedPath = path; + if (!sanitisedPath.endsWith(QLatin1Char('/'))) { + sanitisedPath += QLatin1Char('/'); + } + + _selectiveSyncWhiteList.insert(std::upper_bound(_selectiveSyncWhiteList.begin(), + _selectiveSyncWhiteList.end(), + sanitisedPath), + sanitisedPath); + return callback(false); + } + }); + propfindJob->start(); +} + +void DiscoveryPhase::checkSelectiveSyncNewFolder(const QString &path, + const RemotePermissions remotePerm, + const std::function callback) { if (_syncOptions._confirmExternalStorage && _syncOptions._vfs->mode() == Vfs::Off && remotePerm.hasPermission(RemotePermissions::IsMounted)) { @@ -107,37 +147,7 @@ void DiscoveryPhase::checkSelectiveSyncNewFolder(const QString &path, RemotePerm return callback(false); } - auto limit = _syncOptions._newBigFolderSizeLimit; - if (limit < 0 || _syncOptions._vfs->mode() != Vfs::Off) { - // no limit, everything is allowed; - return callback(false); - } - - // do a PROPFIND to know the size of this folder - auto propfindJob = new PropfindJob(_account, _remoteFolder + path, this); - propfindJob->setProperties(QList() << "resourcetype" - << "http://owncloud.org/ns:size"); - QObject::connect(propfindJob, &PropfindJob::finishedWithError, - this, [=] { return callback(false); }); - QObject::connect(propfindJob, &PropfindJob::result, this, [=](const QVariantMap &values) { - auto result = values.value(QLatin1String("size")).toLongLong(); - if (result >= limit) { - // we tell the UI there is a new folder - emit newBigFolder(path, false); - return callback(true); - } else { - // it is not too big, put it in the white list (so we will not do more query for the children) - // and and do not block. - auto p = path; - if (!p.endsWith(QLatin1Char('/'))) - p += QLatin1Char('/'); - _selectiveSyncWhiteList.insert( - std::upper_bound(_selectiveSyncWhiteList.begin(), _selectiveSyncWhiteList.end(), p), - p); - return callback(false); - } - }); - propfindJob->start(); + checkFolderSizeLimit(path, callback); } /* Given a path on the remote, give the path as it is when the rename is done */ diff --git a/src/libsync/discoveryphase.h b/src/libsync/discoveryphase.h index 36b07b874daf..948500371117 100644 --- a/src/libsync/discoveryphase.h +++ b/src/libsync/discoveryphase.h @@ -255,10 +255,14 @@ class DiscoveryPhase : public QObject [[nodiscard]] bool isInSelectiveSyncBlackList(const QString &path) const; + void checkFolderSizeLimit(const QString &path, + const std::function callback); + // Check if the new folder should be deselected or not. // May be async. "Return" via the callback, true if the item is blacklisted - void checkSelectiveSyncNewFolder(const QString &path, RemotePermissions rp, - std::function callback); + void checkSelectiveSyncNewFolder(const QString &path, + const RemotePermissions rp, + const std::function callback); /** Given an original path, return the target path obtained when renaming is done. * From e5f399ff51fa101939220b7fa88bfa8c67d106cd Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 27 Jun 2023 22:09:16 +0800 Subject: [PATCH 03/57] Move active folder size limit check to independent convenience method Signed-off-by: Claudio Cambra --- src/libsync/discoveryphase.cpp | 15 +++++++++++---- src/libsync/discoveryphase.h | 2 ++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/libsync/discoveryphase.cpp b/src/libsync/discoveryphase.cpp index 3fef366c5634..05ab702663ef 100644 --- a/src/libsync/discoveryphase.cpp +++ b/src/libsync/discoveryphase.cpp @@ -82,11 +82,15 @@ bool DiscoveryPhase::isInSelectiveSyncBlackList(const QString &path) const return false; } +bool DiscoveryPhase::activeFolderSizeLimit() const +{ + return _syncOptions._newBigFolderSizeLimit > 0 && _syncOptions._vfs->mode() == Vfs::Off; +} + void DiscoveryPhase::checkFolderSizeLimit(const QString &path, const std::function callback) { - const auto limit = _syncOptions._newBigFolderSizeLimit; - if (limit < 0 || _syncOptions._vfs->mode() != Vfs::Off) { + if (activeFolderSizeLimit()) { // no limit, everything is allowed; return callback(false); } @@ -98,8 +102,11 @@ void DiscoveryPhase::checkFolderSizeLimit(const QString &path, connect(propfindJob, &PropfindJob::finishedWithError, this, [=] { return callback(false); }); connect(propfindJob, &PropfindJob::result, this, [=](const QVariantMap &values) { - auto result = values.value(QLatin1String("size")).toLongLong(); - if (result >= limit) { + + if (const auto result = values.value(QLatin1String("size")).toLongLong(), + limit = _syncOptions._newBigFolderSizeLimit; + result >= limit) { + // we tell the UI there is a new folder emit newBigFolder(path, false); return callback(true); diff --git a/src/libsync/discoveryphase.h b/src/libsync/discoveryphase.h index 948500371117..93f3989229ac 100644 --- a/src/libsync/discoveryphase.h +++ b/src/libsync/discoveryphase.h @@ -255,6 +255,8 @@ class DiscoveryPhase : public QObject [[nodiscard]] bool isInSelectiveSyncBlackList(const QString &path) const; + [[nodiscard]] bool activeFolderSizeLimit() const; + void checkFolderSizeLimit(const QString &path, const std::function callback); From e3dc6be3710ad104a63c75d0c4d3883aca8c133a Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 27 Jun 2023 23:27:28 +0800 Subject: [PATCH 04/57] Extract processServerNew into a proper method instead of large lambda Signed-off-by: Claudio Cambra --- src/libsync/discovery.cpp | 100 +++++++++++++++++++++----------------- src/libsync/discovery.h | 6 +++ 2 files changed, 62 insertions(+), 44 deletions(-) diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp index d818359afb71..a17b86e5ab6e 100644 --- a/src/libsync/discovery.cpp +++ b/src/libsync/discovery.cpp @@ -559,9 +559,58 @@ static bool computeLocalChecksum(const QByteArray &header, const QString &path, return false; } -void ProcessDirectoryJob::processFileAnalyzeRemoteInfo( - const SyncFileItemPtr &item, PathTuple path, const LocalInfo &localEntry, - const RemoteInfo &serverEntry, const SyncJournalFileRecord &dbEntry) +void ProcessDirectoryJob::postProcessServerNew(const SyncFileItemPtr &item, + PathTuple &path, + const LocalInfo &localEntry, + const RemoteInfo &serverEntry, + const SyncJournalFileRecord &dbEntry) +{ + if (item->isDirectory()) { + _pendingAsyncJobs++; + _discoveryData->checkSelectiveSyncNewFolder(path._server, + serverEntry.remotePerm, + [=](bool result) { + --_pendingAsyncJobs; + if (!result) { + processFileAnalyzeLocalInfo(item, path, localEntry, serverEntry, dbEntry, _queryServer); + } + QTimer::singleShot(0, _discoveryData, &DiscoveryPhase::scheduleMoreJobs); + }); + return; + } + + // Turn new remote files into virtual files if the option is enabled. + const auto opts = _discoveryData->_syncOptions; + if (!localEntry.isValid() && + item->_type == ItemTypeFile && + opts._vfs->mode() != Vfs::Off && + !FileSystem::isLnkFile(item->_file) && + _pinState != PinState::AlwaysLocal && + !FileSystem::isExcludeFile(item->_file)) { + + item->_type = ItemTypeVirtualFile; + if (isVfsWithSuffix()) { + addVirtualFileSuffix(path._original); + } + } + + if (opts._vfs->mode() != Vfs::Off && !item->_encryptedFileName.isEmpty()) { + // We are syncing a file for the first time (local entry is invalid) and it is encrypted file that will be virtual once synced + // to avoid having error of "file has changed during sync" when trying to hydrate it explicitly - we must remove Constants::e2EeTagSize bytes from the end + // as explicit hydration does not care if these bytes are present in the placeholder or not, but, the size must not change in the middle of the sync + // this way it works for both implicit and explicit hydration by making a placeholder size that does not includes encryption tag Constants::e2EeTagSize bytes + // another scenario - we are syncing a file which is on disk but not in the database (database was removed or file was not written there yet) + item->_size = serverEntry.size - Constants::e2EeTagSize; + } + + processFileAnalyzeLocalInfo(item, path, localEntry, serverEntry, dbEntry, _queryServer); +} + +void ProcessDirectoryJob::processFileAnalyzeRemoteInfo(const SyncFileItemPtr &item, + PathTuple path, + const LocalInfo &localEntry, + const RemoteInfo &serverEntry, + const SyncJournalFileRecord &dbEntry) { item->_checksumHeader = serverEntry.checksumHeader; item->_fileId = serverEntry.fileId; @@ -593,7 +642,7 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo( item->_lockEditorApp = serverEntry.lockEditorApp; item->_lockTime = serverEntry.lockTime; item->_lockTimeout = serverEntry.lockTimeout; - + qCDebug(lcDisco()) << "item lock for:" << item->_file << item->_locked << item->_lockOwnerDisplayName @@ -752,46 +801,9 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo( return; } - auto postProcessServerNew = [=]() mutable { - if (item->isDirectory()) { - _pendingAsyncJobs++; - _discoveryData->checkSelectiveSyncNewFolder(path._server, serverEntry.remotePerm, - [=](bool result) { - --_pendingAsyncJobs; - if (!result) { - processFileAnalyzeLocalInfo(item, path, localEntry, serverEntry, dbEntry, _queryServer); - } - QTimer::singleShot(0, _discoveryData, &DiscoveryPhase::scheduleMoreJobs); - }); - return; - } - // Turn new remote files into virtual files if the option is enabled. - const auto opts = _discoveryData->_syncOptions; - if (!localEntry.isValid() - && item->_type == ItemTypeFile - && opts._vfs->mode() != Vfs::Off - && !FileSystem::isLnkFile(item->_file) - && _pinState != PinState::AlwaysLocal - && !FileSystem::isExcludeFile(item->_file)) { - item->_type = ItemTypeVirtualFile; - if (isVfsWithSuffix()) - addVirtualFileSuffix(path._original); - } - - if (opts._vfs->mode() != Vfs::Off && !item->_encryptedFileName.isEmpty()) { - // We are syncing a file for the first time (local entry is invalid) and it is encrypted file that will be virtual once synced - // to avoid having error of "file has changed during sync" when trying to hydrate it explicitly - we must remove Constants::e2EeTagSize bytes from the end - // as explicit hydration does not care if these bytes are present in the placeholder or not, but, the size must not change in the middle of the sync - // this way it works for both implicit and explicit hydration by making a placeholder size that does not includes encryption tag Constants::e2EeTagSize bytes - // another scenario - we are syncing a file which is on disk but not in the database (database was removed or file was not written there yet) - item->_size = serverEntry.size - Constants::e2EeTagSize; - } - processFileAnalyzeLocalInfo(item, path, localEntry, serverEntry, dbEntry, _queryServer); - }; - // Potential NEW/NEW conflict is handled in AnalyzeLocal if (localEntry.isValid()) { - postProcessServerNew(); + postProcessServerNew(item, path, localEntry, serverEntry, dbEntry); return; } @@ -912,7 +924,7 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo( // Somehow another item claimed this original path, consider as if it existed _discoveryData->isRenamed(originalPath)) { // If the file exist or if there is another error, consider it is a new file. - postProcessServerNew(); + postProcessServerNew(item, path, localEntry, serverEntry, dbEntry); return; } @@ -938,7 +950,7 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo( } if (item->_instruction == CSYNC_INSTRUCTION_NEW) { - postProcessServerNew(); + postProcessServerNew(item, path, localEntry, serverEntry, dbEntry); return; } processFileAnalyzeLocalInfo(item, path, localEntry, serverEntry, dbEntry, _queryServer); diff --git a/src/libsync/discovery.h b/src/libsync/discovery.h index debb4572de40..eaa2657697a9 100644 --- a/src/libsync/discovery.h +++ b/src/libsync/discovery.h @@ -162,6 +162,12 @@ class ProcessDirectoryJob : public QObject */ void processFile(PathTuple, const LocalInfo &, const RemoteInfo &, const SyncJournalFileRecord &); + void postProcessServerNew(const SyncFileItemPtr &item, + PathTuple &path, + const LocalInfo &localEntry, + const RemoteInfo &serverEntry, + const SyncJournalFileRecord &dbEntry); + /// processFile helper for when remote information is available, typically flows into AnalyzeLocalInfo when done void processFileAnalyzeRemoteInfo(const SyncFileItemPtr &item, PathTuple, const LocalInfo &, const RemoteInfo &, const SyncJournalFileRecord &); From 7145d73f15b5aa12327a9bbb8f2efa9ae82823b8 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 28 Jun 2023 18:21:40 +0800 Subject: [PATCH 05/57] Add DiscoveryPhase::checkSelectiveSyncExistingFolder Signed-off-by: Claudio Cambra --- src/libsync/discoveryphase.cpp | 13 +++++++++++++ src/libsync/discoveryphase.h | 2 ++ 2 files changed, 15 insertions(+) diff --git a/src/libsync/discoveryphase.cpp b/src/libsync/discoveryphase.cpp index 05ab702663ef..446570e53870 100644 --- a/src/libsync/discoveryphase.cpp +++ b/src/libsync/discoveryphase.cpp @@ -157,6 +157,19 @@ void DiscoveryPhase::checkSelectiveSyncNewFolder(const QString &path, checkFolderSizeLimit(path, callback); } +void DiscoveryPhase::checkSelectiveSyncExistingFolder(const QString &path, const RemotePermissions &rp, const qint64 folderSize) +{ + // TODO: Check for setting to obey big folder sync + // If no size limit is enforced, or if is in whitelist (explicitly allowed) or in blacklist (explicitly disallowed), do nothing. + if (!activeFolderSizeLimit() || findPathInList(_selectiveSyncWhiteList, path) || findPathInList(_selectiveSyncBlackList, path)) { + return; + } else if (folderSize >= _syncOptions._newBigFolderSizeLimit) { // If the folder is too big, notify the user and prompt for response. + const auto isExternalStorage = + _syncOptions._confirmExternalStorage && _syncOptions._vfs->mode() == Vfs::Off && rp.hasPermission(RemotePermissions::IsMounted); + emit newBigFolder(path, isExternalStorage); + } +} + /* Given a path on the remote, give the path as it is when the rename is done */ QString DiscoveryPhase::adjustRenamedPath(const QString &original, SyncFileItem::Direction d) const { diff --git a/src/libsync/discoveryphase.h b/src/libsync/discoveryphase.h index 93f3989229ac..38c54832eef8 100644 --- a/src/libsync/discoveryphase.h +++ b/src/libsync/discoveryphase.h @@ -266,6 +266,8 @@ class DiscoveryPhase : public QObject const RemotePermissions rp, const std::function callback); + void checkSelectiveSyncExistingFolder(const QString &path, const RemotePermissions &rp, const qint64 folderSize); + /** Given an original path, return the target path obtained when renaming is done. * * Note that it only considers parent directory renames. So if A/B got renamed to C/D, From 5844b2763d03c3b29c6e074a3e6dbe0dc627ace9 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 28 Jun 2023 18:33:21 +0800 Subject: [PATCH 06/57] Check selective sync state directories that have been updated and now have differing size Signed-off-by: Claudio Cambra --- src/libsync/discovery.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp index a17b86e5ab6e..16b2b12c5a24 100644 --- a/src/libsync/discovery.cpp +++ b/src/libsync/discovery.cpp @@ -718,11 +718,19 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo(const SyncFileItemPtr &it item->_instruction = CSYNC_INSTRUCTION_SYNC; item->_type = ItemTypeVirtualFileDownload; } else if (dbEntry._etag != serverEntry.etag) { + const auto differingSize = sizeOnServer != item->_size; + item->_direction = SyncFileItem::Down; item->_modtime = serverEntry.modtime; item->_size = sizeOnServer; + if (serverEntry.isDirectory) { ENFORCE(dbEntry.isDirectory()); + + if (differingSize) { + _discoveryData->checkSelectiveSyncExistingFolder(path._server, serverEntry.remotePerm, sizeOnServer); + } + item->_instruction = CSYNC_INSTRUCTION_UPDATE_METADATA; } else if (!localEntry.isValid() && _queryLocal != ParentNotChanged) { // Deleted locally, changed on server From ac218b000ebf1eae617a58920b7a5819bc9e5f42 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 28 Jun 2023 19:00:28 +0800 Subject: [PATCH 07/57] Use single convenience method to handle trailing slash in paths for Folder Signed-off-by: Claudio Cambra --- src/gui/folder.cpp | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index 8d15e8da3021..ddb2fb9c5813 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -51,7 +51,13 @@ namespace { #define VERSION_C constexpr auto versionC = "version"; #endif + +QString trailingSlashPath(const QString &path) +{ + return path.endsWith('/') ? path : QString(path + QStringLiteral("/")); +} } + namespace OCC { Q_LOGGING_CATEGORY(lcFolder, "nextcloud.gui.folder", QtInfoMsg) @@ -167,10 +173,10 @@ void Folder::checkLocalPath() if (_canonicalLocalPath.isEmpty()) { qCWarning(lcFolder) << "Broken symlink:" << _definition.localPath; _canonicalLocalPath = _definition.localPath; - } else if (!_canonicalLocalPath.endsWith('/')) { - _canonicalLocalPath.append('/'); } + _canonicalLocalPath = trailingSlashPath(_canonicalLocalPath); + if (fi.isDir() && fi.isReadable()) { qCDebug(lcFolder) << "Checked local path ok"; } else { @@ -214,10 +220,8 @@ QString Folder::path() const QString Folder::shortGuiLocalPath() const { QString p = _definition.localPath; - QString home = QDir::homePath(); - if (!home.endsWith('/')) { - home.append('/'); - } + const auto home = trailingSlashPath(QDir::homePath()); + if (p.startsWith(home)) { p = p.mid(home.length()); } @@ -266,10 +270,7 @@ QString Folder::remotePath() const QString Folder::remotePathTrailingSlash() const { - QString result = remotePath(); - if (!result.endsWith('/')) - result.append('/'); - return result; + return trailingSlashPath(remotePath()); } QUrl Folder::remoteUrl() const @@ -1183,10 +1184,7 @@ void Folder::slotItemCompleted(const SyncFileItemPtr &item, ErrorCategory errorC void Folder::slotNewBigFolderDiscovered(const QString &newF, bool isExternal) { - auto newFolder = newF; - if (!newFolder.endsWith(QLatin1Char('/'))) { - newFolder += QLatin1Char('/'); - } + const auto newFolder = trailingSlashPath(newF); auto journal = journalDb(); // Add the entry to the blacklist if it is neither in the blacklist or whitelist already @@ -1451,7 +1449,7 @@ void Folder::removeLocalE2eFiles() } if (!parentPathEncrypted) { - const auto pathAdjusted = rec._path.endsWith('/') ? rec._path : QString(rec._path + QStringLiteral("/")); + const auto pathAdjusted = trailingSlashPath(rec._path); e2eFoldersToBlacklist.append(pathAdjusted); } } @@ -1566,11 +1564,8 @@ bool FolderDefinition::load(QSettings &settings, const QString &alias, QString FolderDefinition::prepareLocalPath(const QString &path) { - QString p = QDir::fromNativeSeparators(path); - if (!p.endsWith(QLatin1Char('/'))) { - p.append(QLatin1Char('/')); - } - return p; + const auto normalisedPath = QDir::fromNativeSeparators(path); + return trailingSlashPath(normalisedPath); } QString FolderDefinition::prepareTargetPath(const QString &path) From 10dd03f50a4d4baad2e23c632218d301ca8af8e4 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 28 Jun 2023 19:33:22 +0800 Subject: [PATCH 08/57] Add slot for existing folders becoming big in Folder Signed-off-by: Claudio Cambra --- src/gui/folder.cpp | 21 +++++++++++++++++++++ src/gui/folder.h | 1 + 2 files changed, 22 insertions(+) diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index ddb2fb9c5813..9a503853e31a 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -1216,6 +1216,27 @@ void Folder::slotNewBigFolderDiscovered(const QString &newF, bool isExternal) } } +void Folder::slotExistingFolderNowBig(const QString &folderPath) +{ + const auto trailSlashFolderPath = trailingSlashPath(folderPath); + const auto journal = journalDb(); + + auto undecidedListQueryOk = false; + auto undecidedList = journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncUndecidedList, &undecidedListQueryOk); + if (undecidedListQueryOk) { + if (!undecidedList.contains(trailSlashFolderPath)) { + undecidedList.append(trailSlashFolderPath); + journal->setSelectiveSyncList(SyncJournalDb::SelectiveSyncUndecidedList, undecidedList); + emit newBigFolderDiscovered(trailSlashFolderPath); + } + + const auto message = tr("A folder has surpassed the set folder size limit of %1MB: %2.\n" + "Please go into the settings and disable it if you wish to stop synchronising it.") + .arg(QString::number(ConfigFile().newBigFolderSizeLimit().second), folderPath); + Logger::instance()->postOptionalGuiLog(Theme::instance()->appNameGUI(), message); + } +} + void Folder::slotLogPropagationStart() { _fileLog->logLap("Propagation starts"); diff --git a/src/gui/folder.h b/src/gui/folder.h index cac47809ba11..78f4023f07eb 100644 --- a/src/gui/folder.h +++ b/src/gui/folder.h @@ -405,6 +405,7 @@ private slots: void slotEmitFinishedDelayed(); void slotNewBigFolderDiscovered(const QString &, bool isExternal); + void slotExistingFolderNowBig(const QString &folderPath); void slotLogPropagationStart(); From 6f2e63d52f59574c12b0f6bfd2687c43deb0e4e5 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 28 Jun 2023 19:39:09 +0800 Subject: [PATCH 09/57] Create and connect specific signal for existing folder now being discovered to be big Signed-off-by: Claudio Cambra --- src/gui/folder.cpp | 1 + src/libsync/discovery.cpp | 2 +- src/libsync/discoveryphase.cpp | 6 ++---- src/libsync/discoveryphase.h | 3 ++- src/libsync/syncengine.cpp | 1 + src/libsync/syncengine.h | 2 ++ 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index 9a503853e31a..9879ed30284f 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -108,6 +108,7 @@ Folder::Folder(const FolderDefinition &definition, this, &Folder::slotItemCompleted); connect(_engine.data(), &SyncEngine::newBigFolder, this, &Folder::slotNewBigFolderDiscovered); + connect(_engine.data(), &SyncEngine::existingFolderNowBig, this, &Folder::slotExistingFolderNowBig); connect(_engine.data(), &SyncEngine::seenLockedFile, FolderMan::instance(), &FolderMan::slotSyncOnceFileUnlocks); connect(_engine.data(), &SyncEngine::aboutToPropagate, this, &Folder::slotLogPropagationStart); diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp index 16b2b12c5a24..e9bbe46b6bfb 100644 --- a/src/libsync/discovery.cpp +++ b/src/libsync/discovery.cpp @@ -728,7 +728,7 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo(const SyncFileItemPtr &it ENFORCE(dbEntry.isDirectory()); if (differingSize) { - _discoveryData->checkSelectiveSyncExistingFolder(path._server, serverEntry.remotePerm, sizeOnServer); + _discoveryData->checkSelectiveSyncExistingFolder(path._server, sizeOnServer); } item->_instruction = CSYNC_INSTRUCTION_UPDATE_METADATA; diff --git a/src/libsync/discoveryphase.cpp b/src/libsync/discoveryphase.cpp index 446570e53870..b1efc31572a8 100644 --- a/src/libsync/discoveryphase.cpp +++ b/src/libsync/discoveryphase.cpp @@ -157,16 +157,14 @@ void DiscoveryPhase::checkSelectiveSyncNewFolder(const QString &path, checkFolderSizeLimit(path, callback); } -void DiscoveryPhase::checkSelectiveSyncExistingFolder(const QString &path, const RemotePermissions &rp, const qint64 folderSize) +void DiscoveryPhase::checkSelectiveSyncExistingFolder(const QString &path, const qint64 folderSize) { // TODO: Check for setting to obey big folder sync // If no size limit is enforced, or if is in whitelist (explicitly allowed) or in blacklist (explicitly disallowed), do nothing. if (!activeFolderSizeLimit() || findPathInList(_selectiveSyncWhiteList, path) || findPathInList(_selectiveSyncBlackList, path)) { return; } else if (folderSize >= _syncOptions._newBigFolderSizeLimit) { // If the folder is too big, notify the user and prompt for response. - const auto isExternalStorage = - _syncOptions._confirmExternalStorage && _syncOptions._vfs->mode() == Vfs::Off && rp.hasPermission(RemotePermissions::IsMounted); - emit newBigFolder(path, isExternalStorage); + emit existingFolderNowBig(path); } } diff --git a/src/libsync/discoveryphase.h b/src/libsync/discoveryphase.h index 38c54832eef8..1775325a61fc 100644 --- a/src/libsync/discoveryphase.h +++ b/src/libsync/discoveryphase.h @@ -266,7 +266,7 @@ class DiscoveryPhase : public QObject const RemotePermissions rp, const std::function callback); - void checkSelectiveSyncExistingFolder(const QString &path, const RemotePermissions &rp, const qint64 folderSize); + void checkSelectiveSyncExistingFolder(const QString &path, const qint64 folderSize); /** Given an original path, return the target path obtained when renaming is done. * @@ -324,6 +324,7 @@ class DiscoveryPhase : public QObject // A new folder was discovered and was not synced because of the confirmation feature void newBigFolder(const QString &folder, bool isExternal); + void existingFolderNowBig(const QString &folder); /** For excluded items that don't show up in itemDiscovered() * diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index 7a13f7133f30..b4fadc45399d 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -656,6 +656,7 @@ void SyncEngine::startSync() connect(_discoveryPhase.data(), &DiscoveryPhase::itemDiscovered, this, &SyncEngine::slotItemDiscovered); connect(_discoveryPhase.data(), &DiscoveryPhase::newBigFolder, this, &SyncEngine::newBigFolder); + connect(_discoveryPhase.data(), &DiscoveryPhase::existingFolderNowBig, this, &SyncEngine::existingFolderNowBig); connect(_discoveryPhase.data(), &DiscoveryPhase::fatalError, this, [this](const QString &errorString, ErrorCategory errorCategory) { Q_EMIT syncError(errorString, errorCategory); finalize(false); diff --git a/src/libsync/syncengine.h b/src/libsync/syncengine.h index fce3a96b6945..d1c4ab9d5169 100644 --- a/src/libsync/syncengine.h +++ b/src/libsync/syncengine.h @@ -187,6 +187,8 @@ public slots: // A new folder was discovered and was not synced because of the confirmation feature void newBigFolder(const QString &folder, bool isExternal); + void existingFolderNowBig(const QString &folder); + /** Emitted when propagation has problems with a locked file. * * Forwarded from OwncloudPropagator::seenLockedFile. From 5acacdd4d81b7301944a889e9cde125b212b6891 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 4 Jul 2023 01:15:34 +0800 Subject: [PATCH 10/57] Make checkFolderSizeLimit more task agnostic Signed-off-by: Claudio Cambra --- src/libsync/discoveryphase.cpp | 49 +++++++++++++++------------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/src/libsync/discoveryphase.cpp b/src/libsync/discoveryphase.cpp index b1efc31572a8..68aaf6c0c57d 100644 --- a/src/libsync/discoveryphase.cpp +++ b/src/libsync/discoveryphase.cpp @@ -87,12 +87,11 @@ bool DiscoveryPhase::activeFolderSizeLimit() const return _syncOptions._newBigFolderSizeLimit > 0 && _syncOptions._vfs->mode() == Vfs::Off; } -void DiscoveryPhase::checkFolderSizeLimit(const QString &path, - const std::function callback) +void DiscoveryPhase::checkFolderSizeLimit(const QString &path, const std::function completionCallback) { - if (activeFolderSizeLimit()) { + if (!activeFolderSizeLimit()) { // no limit, everything is allowed; - return callback(false); + return completionCallback(false); } // do a PROPFIND to know the size of this folder @@ -100,30 +99,13 @@ void DiscoveryPhase::checkFolderSizeLimit(const QString &path, propfindJob->setProperties(QList() << "resourcetype" << "http://owncloud.org/ns:size"); - connect(propfindJob, &PropfindJob::finishedWithError, this, [=] { return callback(false); }); + connect(propfindJob, &PropfindJob::finishedWithError, this, [=] { + return completionCallback(false); + }); connect(propfindJob, &PropfindJob::result, this, [=](const QVariantMap &values) { - - if (const auto result = values.value(QLatin1String("size")).toLongLong(), - limit = _syncOptions._newBigFolderSizeLimit; - result >= limit) { - - // we tell the UI there is a new folder - emit newBigFolder(path, false); - return callback(true); - } else { - // it is not too big, put it in the white list (so we will not do more query for the children) - // and and do not block. - auto sanitisedPath = path; - if (!sanitisedPath.endsWith(QLatin1Char('/'))) { - sanitisedPath += QLatin1Char('/'); - } - - _selectiveSyncWhiteList.insert(std::upper_bound(_selectiveSyncWhiteList.begin(), - _selectiveSyncWhiteList.end(), - sanitisedPath), - sanitisedPath); - return callback(false); - } + const auto result = values.value(QLatin1String("size")).toLongLong(); + const auto limit = _syncOptions._newBigFolderSizeLimit; + return completionCallback(result >= limit); }); propfindJob->start(); } @@ -154,7 +136,18 @@ void DiscoveryPhase::checkSelectiveSyncNewFolder(const QString &path, return callback(false); } - checkFolderSizeLimit(path, callback); + checkFolderSizeLimit(path, [this, path, callback](const bool bigFolder) { + if (bigFolder) { + // we tell the UI there is a new folder + emit newBigFolder(path, false); + return callback(true); + } + + // it is not too big, put it in the white list (so we will not do more query for the children) and and do not block. + const auto sanitisedPath = path.endsWith(QLatin1Char('/')) ? path + QLatin1Char('/') : path; + _selectiveSyncWhiteList.insert(std::upper_bound(_selectiveSyncWhiteList.begin(), _selectiveSyncWhiteList.end(), sanitisedPath), sanitisedPath); + return callback(false); + }); } void DiscoveryPhase::checkSelectiveSyncExistingFolder(const QString &path, const qint64 folderSize) From aa20c102b65a3cef40186dd5be86d2502cf687be Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 4 Jul 2023 01:21:03 +0800 Subject: [PATCH 11/57] Properly check folder size on server Signed-off-by: Claudio Cambra --- src/libsync/discovery.cpp | 15 +++++++-------- src/libsync/discoveryphase.cpp | 11 ++++++++--- src/libsync/discoveryphase.h | 2 +- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp index e9bbe46b6bfb..3205580eeb3e 100644 --- a/src/libsync/discovery.cpp +++ b/src/libsync/discovery.cpp @@ -703,6 +703,11 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo(const SyncFileItemPtr &it const auto sizeOnServer = isVirtualE2EePlaceholder ? serverEntry.size - Constants::e2EeTagSize : serverEntry.size; const auto metaDataSizeNeedsUpdateForE2EeFilePlaceholder = isVirtualE2EePlaceholder && dbEntry._fileSize == serverEntry.size; + if (serverEntry.isDirectory) { + // Even if over quota, continue syncing as normal for now + _discoveryData->checkSelectiveSyncExistingFolder(path._server); + } + if (serverEntry.isDirectory != dbEntry.isDirectory()) { // If the type of the entity changed, it's like NEW, but // needs to delete the other entity first. @@ -718,19 +723,12 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo(const SyncFileItemPtr &it item->_instruction = CSYNC_INSTRUCTION_SYNC; item->_type = ItemTypeVirtualFileDownload; } else if (dbEntry._etag != serverEntry.etag) { - const auto differingSize = sizeOnServer != item->_size; - item->_direction = SyncFileItem::Down; item->_modtime = serverEntry.modtime; item->_size = sizeOnServer; if (serverEntry.isDirectory) { ENFORCE(dbEntry.isDirectory()); - - if (differingSize) { - _discoveryData->checkSelectiveSyncExistingFolder(path._server, sizeOnServer); - } - item->_instruction = CSYNC_INSTRUCTION_UPDATE_METADATA; } else if (!localEntry.isValid() && _queryLocal != ParentNotChanged) { // Deleted locally, changed on server @@ -743,7 +741,8 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo(const SyncFileItemPtr &it << "serverEntry.isDirectory:" << serverEntry.isDirectory << "dbEntry.isDirectory:" << dbEntry.isDirectory(); } - } else if (dbEntry._modtime != serverEntry.modtime && localEntry.size == serverEntry.size && dbEntry._fileSize == serverEntry.size && dbEntry._etag == serverEntry.etag) { + } else if (dbEntry._modtime != serverEntry.modtime && localEntry.size == serverEntry.size && dbEntry._fileSize == serverEntry.size + && dbEntry._etag == serverEntry.etag) { item->_direction = SyncFileItem::Down; item->_modtime = serverEntry.modtime; item->_size = sizeOnServer; diff --git a/src/libsync/discoveryphase.cpp b/src/libsync/discoveryphase.cpp index 68aaf6c0c57d..8f2f1e7c88a8 100644 --- a/src/libsync/discoveryphase.cpp +++ b/src/libsync/discoveryphase.cpp @@ -150,15 +150,20 @@ void DiscoveryPhase::checkSelectiveSyncNewFolder(const QString &path, }); } -void DiscoveryPhase::checkSelectiveSyncExistingFolder(const QString &path, const qint64 folderSize) +void DiscoveryPhase::checkSelectiveSyncExistingFolder(const QString &path) { // TODO: Check for setting to obey big folder sync // If no size limit is enforced, or if is in whitelist (explicitly allowed) or in blacklist (explicitly disallowed), do nothing. if (!activeFolderSizeLimit() || findPathInList(_selectiveSyncWhiteList, path) || findPathInList(_selectiveSyncBlackList, path)) { return; - } else if (folderSize >= _syncOptions._newBigFolderSizeLimit) { // If the folder is too big, notify the user and prompt for response. - emit existingFolderNowBig(path); } + + checkFolderSizeLimit(path, [this, path](const bool bigFolder) { + if (bigFolder) { + // Notify the user and prompt for response. + emit existingFolderNowBig(path); + } + }); } /* Given a path on the remote, give the path as it is when the rename is done */ diff --git a/src/libsync/discoveryphase.h b/src/libsync/discoveryphase.h index 1775325a61fc..dce4261e8202 100644 --- a/src/libsync/discoveryphase.h +++ b/src/libsync/discoveryphase.h @@ -266,7 +266,7 @@ class DiscoveryPhase : public QObject const RemotePermissions rp, const std::function callback); - void checkSelectiveSyncExistingFolder(const QString &path, const qint64 folderSize); + void checkSelectiveSyncExistingFolder(const QString &path); /** Given an original path, return the target path obtained when renaming is done. * From e8b82115e23e0e12cc91ed898cbb4ec2529febb1 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 4 Jul 2023 15:36:57 +0800 Subject: [PATCH 12/57] Add a checkbox in UI to obey folder size limit for existing folders Signed-off-by: Claudio Cambra --- src/gui/generalsettings.ui | 105 ++++++++++++++++++++++++------------- 1 file changed, 68 insertions(+), 37 deletions(-) diff --git a/src/gui/generalsettings.ui b/src/gui/generalsettings.ui index ee82031c911e..ee037123133c 100644 --- a/src/gui/generalsettings.ui +++ b/src/gui/generalsettings.ui @@ -222,46 +222,77 @@ - + - - - Ask for confirmation before synchronizing folders larger than - - - true - - - - - - - 999999 - - - 99 - - - - - - - MB - - + + + + + Ask for confirmation before synchronizing new folders larger than + + + true + + + + + + + 999999 + + + 99 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + MB + + + + - - - Qt::Horizontal - - - - 40 - 20 - - - + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Notify when synchronised folders grow larger than specified limit + + + + From b06cbf4dce65a4b1527af2074c33e4540691c190 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 4 Jul 2023 16:12:11 +0800 Subject: [PATCH 13/57] Add config settings for notifying existing folders going over limit Signed-off-by: Claudio Cambra --- src/libsync/configfile.cpp | 12 ++++++++++++ src/libsync/configfile.h | 2 ++ 2 files changed, 14 insertions(+) diff --git a/src/libsync/configfile.cpp b/src/libsync/configfile.cpp index fd8d938d643f..01ed91f0a3d8 100644 --- a/src/libsync/configfile.cpp +++ b/src/libsync/configfile.cpp @@ -99,6 +99,7 @@ static constexpr char downloadLimitC[] = "BWLimit/downloadLimit"; static constexpr char newBigFolderSizeLimitC[] = "newBigFolderSizeLimit"; static constexpr char useNewBigFolderSizeLimitC[] = "useNewBigFolderSizeLimit"; +static constexpr char notifyExistingFoldersOverLimitC[] = "notifyExistingFoldersOverLimit"; static constexpr char confirmExternalStorageC[] = "confirmExternalStorage"; static constexpr char moveToTrashC[] = "moveToTrash"; @@ -959,6 +960,17 @@ bool ConfigFile::useNewBigFolderSizeLimit() const return getPolicySetting(QLatin1String(useNewBigFolderSizeLimitC), fallback).toBool(); } +bool ConfigFile::notifyExistingFoldersOverLimit() const +{ + const auto fallback = getValue(notifyExistingFoldersOverLimitC, {}, true); + return getPolicySetting(QString(notifyExistingFoldersOverLimitC), fallback).toBool(); +} + +void ConfigFile::setNotifyExistingFoldersOverLimit(const bool notify) +{ + setValue(notifyExistingFoldersOverLimitC, notify); +} + void ConfigFile::setConfirmExternalStorage(bool isChecked) { setValue(confirmExternalStorageC, isChecked); diff --git a/src/libsync/configfile.h b/src/libsync/configfile.h index 5d0ee5460fce..a947fec2bd6e 100644 --- a/src/libsync/configfile.h +++ b/src/libsync/configfile.h @@ -141,6 +141,8 @@ class OWNCLOUDSYNC_EXPORT ConfigFile /** [checked, size in MB] **/ [[nodiscard]] QPair newBigFolderSizeLimit() const; void setNewBigFolderSizeLimit(bool isChecked, qint64 mbytes); + [[nodiscard]] bool notifyExistingFoldersOverLimit() const; + void setNotifyExistingFoldersOverLimit(const bool notify); [[nodiscard]] bool useNewBigFolderSizeLimit() const; [[nodiscard]] bool confirmExternalStorage() const; void setConfirmExternalStorage(bool); From e6f5fb63826d18c8edf27ba3ebc88ff4f53e1cd0 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 4 Jul 2023 16:24:58 +0800 Subject: [PATCH 14/57] Hook up existing folder notification size checkbox in general settings Signed-off-by: Claudio Cambra --- src/gui/generalsettings.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/gui/generalsettings.cpp b/src/gui/generalsettings.cpp index 0296c7083280..3348bf1a8d3f 100644 --- a/src/gui/generalsettings.cpp +++ b/src/gui/generalsettings.cpp @@ -186,6 +186,7 @@ GeneralSettings::GeneralSettings(QWidget *parent) connect(_ui->crashreporterCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::saveMiscSettings); connect(_ui->newFolderLimitCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::saveMiscSettings); connect(_ui->newFolderLimitSpinBox, static_cast(&QSpinBox::valueChanged), this, &GeneralSettings::saveMiscSettings); + connect(_ui->existingFolderLimitCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::saveMiscSettings); connect(_ui->newExternalStorage, &QAbstractButton::toggled, this, &GeneralSettings::saveMiscSettings); connect(_ui->moveFilesToTrashCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::saveMiscSettings); @@ -261,6 +262,10 @@ void GeneralSettings::loadMiscSettings() auto newFolderLimit = cfgFile.newBigFolderSizeLimit(); _ui->newFolderLimitCheckBox->setChecked(newFolderLimit.first); _ui->newFolderLimitSpinBox->setValue(newFolderLimit.second); + _ui->existingFolderLimitCheckBox->setEnabled(newFolderLimit.first); + _ui->existingFolderLimitCheckBox->setChecked(newFolderLimit.first && cfgFile.notifyExistingFoldersOverLimit()); + _ui->newExternalStorage->setChecked(cfgFile.confirmExternalStorage()); + _ui->monoIconsCheckBox->setChecked(cfgFile.monoIcons()); } #if defined(BUILD_UPDATER) @@ -274,8 +279,12 @@ void GeneralSettings::slotUpdateInfo() } if (updater) { - connect(_ui->updateButton, &QAbstractButton::clicked, this, - &GeneralSettings::slotUpdateCheckNow, Qt::UniqueConnection); + connect(_ui->updateButton, + &QAbstractButton::clicked, + this, + + &GeneralSettings::slotUpdateCheckNow, + Qt::UniqueConnection); connect(_ui->autoCheckForUpdatesCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::slotToggleAutoUpdateCheck, Qt::UniqueConnection); _ui->autoCheckForUpdatesCheckBox->setChecked(ConfigFile().autoUpdateCheck()); @@ -422,14 +431,15 @@ void GeneralSettings::saveMiscSettings() ConfigFile cfgFile; - const auto monoIconsChecked = _ui->monoIconsCheckBox->isChecked(); - cfgFile.setMonoIcons(monoIconsChecked); - Theme::instance()->setSystrayUseMonoIcons(monoIconsChecked); + const auto useMonoIcons = _ui->monoIconsCheckBox->isChecked(); + Theme::instance()->setSystrayUseMonoIcons(useMonoIcons); + cfgFile.setMonoIcons(useMonoIcons); cfgFile.setCrashReporter(_ui->crashreporterCheckBox->isChecked()); + cfgFile.setMoveToTrash(_ui->moveFilesToTrashCheckBox->isChecked()); cfgFile.setNewBigFolderSizeLimit(_ui->newFolderLimitCheckBox->isChecked(), _ui->newFolderLimitSpinBox->value()); cfgFile.setConfirmExternalStorage(_ui->newExternalStorage->isChecked()); - cfgFile.setMoveToTrash(_ui->moveFilesToTrashCheckBox->isChecked()); + cfgFile.setNotifyExistingFoldersOverLimit(_ui->existingFolderLimitCheckBox->isChecked()); } void GeneralSettings::slotToggleLaunchOnStartup(bool enable) From 61057334f6bf01c2e2865c4cc9f68c968bc69b2c Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 4 Jul 2023 22:03:50 +0800 Subject: [PATCH 15/57] Do not notify if setting is not enabled for folders going over limit Signed-off-by: Claudio Cambra --- src/libsync/discoveryphase.cpp | 9 +++++++-- src/libsync/discoveryphase.h | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/libsync/discoveryphase.cpp b/src/libsync/discoveryphase.cpp index 8f2f1e7c88a8..14ff897947f8 100644 --- a/src/libsync/discoveryphase.cpp +++ b/src/libsync/discoveryphase.cpp @@ -13,6 +13,7 @@ */ #include "discoveryphase.h" +#include "configfile.h" #include "discovery.h" #include "helpers.h" #include "progressdispatcher.h" @@ -87,6 +88,11 @@ bool DiscoveryPhase::activeFolderSizeLimit() const return _syncOptions._newBigFolderSizeLimit > 0 && _syncOptions._vfs->mode() == Vfs::Off; } +bool DiscoveryPhase::notifyExistingFolderOverLimit() const +{ + return activeFolderSizeLimit() && ConfigFile().notifyExistingFoldersOverLimit(); +} + void DiscoveryPhase::checkFolderSizeLimit(const QString &path, const std::function completionCallback) { if (!activeFolderSizeLimit()) { @@ -152,9 +158,8 @@ void DiscoveryPhase::checkSelectiveSyncNewFolder(const QString &path, void DiscoveryPhase::checkSelectiveSyncExistingFolder(const QString &path) { - // TODO: Check for setting to obey big folder sync // If no size limit is enforced, or if is in whitelist (explicitly allowed) or in blacklist (explicitly disallowed), do nothing. - if (!activeFolderSizeLimit() || findPathInList(_selectiveSyncWhiteList, path) || findPathInList(_selectiveSyncBlackList, path)) { + if (!notifyExistingFolderOverLimit() || findPathInList(_selectiveSyncWhiteList, path) || findPathInList(_selectiveSyncBlackList, path)) { return; } diff --git a/src/libsync/discoveryphase.h b/src/libsync/discoveryphase.h index dce4261e8202..900584066cd0 100644 --- a/src/libsync/discoveryphase.h +++ b/src/libsync/discoveryphase.h @@ -256,7 +256,8 @@ class DiscoveryPhase : public QObject [[nodiscard]] bool isInSelectiveSyncBlackList(const QString &path) const; [[nodiscard]] bool activeFolderSizeLimit() const; - + [[nodiscard]] bool notifyExistingFolderOverLimit() const; + void checkFolderSizeLimit(const QString &path, const std::function callback); From 83b69032ff2ba12f44b59a92469ffce2aa6368ec Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 4 Jul 2023 22:26:11 +0800 Subject: [PATCH 16/57] Ensure notify big folders checkbox state is changed when parent checkbox changes Signed-off-by: Claudio Cambra --- src/gui/generalsettings.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/gui/generalsettings.cpp b/src/gui/generalsettings.cpp index 3348bf1a8d3f..82931e5fc708 100644 --- a/src/gui/generalsettings.cpp +++ b/src/gui/generalsettings.cpp @@ -432,14 +432,17 @@ void GeneralSettings::saveMiscSettings() ConfigFile cfgFile; const auto useMonoIcons = _ui->monoIconsCheckBox->isChecked(); + const auto newFolderLimitEnabled = _ui->newFolderLimitCheckBox->isChecked(); Theme::instance()->setSystrayUseMonoIcons(useMonoIcons); cfgFile.setMonoIcons(useMonoIcons); cfgFile.setCrashReporter(_ui->crashreporterCheckBox->isChecked()); cfgFile.setMoveToTrash(_ui->moveFilesToTrashCheckBox->isChecked()); - cfgFile.setNewBigFolderSizeLimit(_ui->newFolderLimitCheckBox->isChecked(), _ui->newFolderLimitSpinBox->value()); + cfgFile.setNewBigFolderSizeLimit(newFolderLimitEnabled, _ui->newFolderLimitSpinBox->value()); cfgFile.setConfirmExternalStorage(_ui->newExternalStorage->isChecked()); cfgFile.setNotifyExistingFoldersOverLimit(_ui->existingFolderLimitCheckBox->isChecked()); + + _ui->existingFolderLimitCheckBox->setEnabled(newFolderLimitEnabled); } void GeneralSettings::slotToggleLaunchOnStartup(bool enable) From 2a4ba398e21d53d69c53918cfe4ba2041a993c49 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 4 Jul 2023 22:32:58 +0800 Subject: [PATCH 17/57] Do not notify about folders growing beyond threshold by default Signed-off-by: Claudio Cambra --- src/libsync/configfile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsync/configfile.cpp b/src/libsync/configfile.cpp index 01ed91f0a3d8..86e39d956172 100644 --- a/src/libsync/configfile.cpp +++ b/src/libsync/configfile.cpp @@ -962,7 +962,7 @@ bool ConfigFile::useNewBigFolderSizeLimit() const bool ConfigFile::notifyExistingFoldersOverLimit() const { - const auto fallback = getValue(notifyExistingFoldersOverLimitC, {}, true); + const auto fallback = getValue(notifyExistingFoldersOverLimitC, {}, false); return getPolicySetting(QString(notifyExistingFoldersOverLimitC), fallback).toBool(); } From a97bdb95eef8bfec377995dfc98538a7c92c65b4 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 4 Jul 2023 23:33:00 +0800 Subject: [PATCH 18/57] Clean up AccountSettings::refreshSelectiveSyncStatus Signed-off-by: Claudio Cambra --- src/gui/accountsettings.cpp | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/gui/accountsettings.cpp b/src/gui/accountsettings.cpp index b4ca6b3b7808..623d8e8e18ed 100644 --- a/src/gui/accountsettings.cpp +++ b/src/gui/accountsettings.cpp @@ -1480,38 +1480,44 @@ void AccountSettings::folderTerminateSyncAndUpdateBlackList(const QStringList &b void AccountSettings::refreshSelectiveSyncStatus() { - QString msg; + QString unsyncedMsg; + auto cnt = 0; const auto folders = FolderMan::instance()->map().values(); + _ui->bigFolderUi->setVisible(false); + for (const auto folder : folders) { if (folder->accountState() != _accountState) { continue; } auto ok = false; + auto blacklistOk = false; const auto undecidedList = folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncUndecidedList, &ok); + const auto blacklist = folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, &blacklistOk); + for (const auto &it : undecidedList) { // FIXME: add the folder alias in a hoover hint. // folder->alias() + QLatin1String("/") if (cnt++) { - msg += QLatin1String(", "); + unsyncedMsg += QStringLiteral(", "); } - auto myFolder = (it); - if (myFolder.endsWith('/')) { - myFolder.chop(1); - } - const auto theIndx = _model->indexForPath(folder, myFolder); - if (theIndx.isValid()) { - msg += QString::fromLatin1("%1") - .arg(Utility::escape(myFolder), Utility::escape(folder->alias())); + + const auto folderWithoutTrailingSlash = it.endsWith('/') ? it.left(it.length() - 1) : it; + + const auto folderIdx = _model->indexForPath(folder, folderWithoutTrailingSlash); + if (folderIdx.isValid()) { + const auto escapedFolderString = Utility::escape(folderWithoutTrailingSlash); + const auto escapedFolderName = Utility::escape(folder->alias()); + unsyncedMsg += QStringLiteral("%1").arg(escapedFolderString, escapedFolderName); } else { - msg += myFolder; // no link because we do not know the index yet. + unsyncedMsg += folderWithoutTrailingSlash; // no link because we do not know the index yet. } } } - if (!msg.isEmpty()) { + if (!unsyncedMsg.isEmpty()) { ConfigFile cfg; const auto info = !cfg.confirmExternalStorage() ? tr("There are folders that were not synchronized because they are too big: ") : @@ -1519,7 +1525,7 @@ void AccountSettings::refreshSelectiveSyncStatus() tr("There are folders that were not synchronized because they are external storages: ") : tr("There are folders that were not synchronized because they are too big or external storages: "); - _ui->selectiveSyncNotification->setText(info + msg); + _ui->selectiveSyncNotification->setText(info + unsyncedMsg); _ui->bigFolderUi->setVisible(true); } } From 017dca232872e5ef20f4c8113b3a274860db6760 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 4 Jul 2023 23:34:14 +0800 Subject: [PATCH 19/57] Move findPathInList from discoveryphase to common syncjournaldb Signed-off-by: Claudio Cambra --- src/common/syncjournaldb.cpp | 27 ++++++++++++++++++++++++++ src/common/syncjournaldb.h | 3 +++ src/libsync/discoveryphase.cpp | 35 ++++------------------------------ 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/src/common/syncjournaldb.cpp b/src/common/syncjournaldb.cpp index 23e33c16999c..7cbc9b540778 100644 --- a/src/common/syncjournaldb.cpp +++ b/src/common/syncjournaldb.cpp @@ -205,6 +205,33 @@ bool SyncJournalDb::maybeMigrateDb(const QString &localPath, const QString &abso return true; } +bool SyncJournalDb::findPathInSelectiveSyncList(const QStringList &list, const QString &path) +{ + Q_ASSERT(std::is_sorted(list.begin(), list.end())); + + if (list.size() == 1 && list.first() == QLatin1String("/")) { + // Special case for the case "/" is there, it matches everything + return true; + } + + QString pathSlash = path + QLatin1Char('/'); + + // Since the list is sorted, we can do a binary search. + // If the path is a prefix of another item or right after in the lexical order. + auto it = std::lower_bound(list.begin(), list.end(), pathSlash); + + if (it != list.end() && *it == pathSlash) { + return true; + } + + if (it == list.begin()) { + return false; + } + --it; + Q_ASSERT(it->endsWith(QLatin1Char('/'))); // Folder::setSelectiveSyncBlackList makes sure of that + return pathSlash.startsWith(*it); +} + bool SyncJournalDb::exists() { QMutexLocker locker(&_mutex); diff --git a/src/common/syncjournaldb.h b/src/common/syncjournaldb.h index d9a3e36b84b3..eb0ab0e21e0a 100644 --- a/src/common/syncjournaldb.h +++ b/src/common/syncjournaldb.h @@ -58,6 +58,9 @@ class OCSYNC_EXPORT SyncJournalDb : public QObject /// Migrate a csync_journal to the new path, if necessary. Returns false on error static bool maybeMigrateDb(const QString &localPath, const QString &absoluteJournalPath); + /// Given a sorted list of paths ending with '/', return whether or not the given path is within one of the paths of the list + static bool findPathInSelectiveSyncList(const QStringList &list, const QString &path); + // To verify that the record could be found check with SyncJournalFileRecord::isValid() [[nodiscard]] bool getFileRecord(const QString &filename, SyncJournalFileRecord *rec) { return getFileRecord(filename.toUtf8(), rec); } [[nodiscard]] bool getFileRecord(const QByteArray &filename, SyncJournalFileRecord *rec); diff --git a/src/libsync/discoveryphase.cpp b/src/libsync/discoveryphase.cpp index 14ff897947f8..e1d95c28a183 100644 --- a/src/libsync/discoveryphase.cpp +++ b/src/libsync/discoveryphase.cpp @@ -40,34 +40,6 @@ namespace OCC { Q_LOGGING_CATEGORY(lcDiscovery, "nextcloud.sync.discovery", QtInfoMsg) -/* Given a sorted list of paths ending with '/', return whether or not the given path is within one of the paths of the list*/ -static bool findPathInList(const QStringList &list, const QString &path) -{ - Q_ASSERT(std::is_sorted(list.begin(), list.end())); - - if (list.size() == 1 && list.first() == QLatin1String("/")) { - // Special case for the case "/" is there, it matches everything - return true; - } - - QString pathSlash = path + QLatin1Char('/'); - - // Since the list is sorted, we can do a binary search. - // If the path is a prefix of another item or right after in the lexical order. - auto it = std::lower_bound(list.begin(), list.end(), pathSlash); - - if (it != list.end() && *it == pathSlash) { - return true; - } - - if (it == list.begin()) { - return false; - } - --it; - Q_ASSERT(it->endsWith(QLatin1Char('/'))); // Folder::setSelectiveSyncBlackList makes sure of that - return pathSlash.startsWith(*it); -} - bool DiscoveryPhase::isInSelectiveSyncBlackList(const QString &path) const { if (_selectiveSyncBlackList.isEmpty()) { @@ -76,7 +48,7 @@ bool DiscoveryPhase::isInSelectiveSyncBlackList(const QString &path) const } // Block if it is in the black list - if (findPathInList(_selectiveSyncBlackList, path)) { + if (SyncJournalDb::findPathInSelectiveSyncList(_selectiveSyncBlackList, path)) { return true; } @@ -138,7 +110,7 @@ void DiscoveryPhase::checkSelectiveSyncNewFolder(const QString &path, } // If this path or the parent is in the white list, then we do not block this file - if (findPathInList(_selectiveSyncWhiteList, path)) { + if (SyncJournalDb::findPathInSelectiveSyncList(_selectiveSyncWhiteList, path)) { return callback(false); } @@ -159,7 +131,8 @@ void DiscoveryPhase::checkSelectiveSyncNewFolder(const QString &path, void DiscoveryPhase::checkSelectiveSyncExistingFolder(const QString &path) { // If no size limit is enforced, or if is in whitelist (explicitly allowed) or in blacklist (explicitly disallowed), do nothing. - if (!notifyExistingFolderOverLimit() || findPathInList(_selectiveSyncWhiteList, path) || findPathInList(_selectiveSyncBlackList, path)) { + if (!notifyExistingFolderOverLimit() || SyncJournalDb::findPathInSelectiveSyncList(_selectiveSyncWhiteList, path) + || SyncJournalDb::findPathInSelectiveSyncList(_selectiveSyncBlackList, path)) { return; } From e05cf7665a875d90c46029f0ca837f8dab756694 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 4 Jul 2023 23:55:05 +0800 Subject: [PATCH 20/57] Add specific message for existing folders exceeding warning limit in accountsettings Signed-off-by: Claudio Cambra --- src/gui/accountsettings.cpp | 67 ++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 20 deletions(-) diff --git a/src/gui/accountsettings.cpp b/src/gui/accountsettings.cpp index 623d8e8e18ed..230337cd2bea 100644 --- a/src/gui/accountsettings.cpp +++ b/src/gui/accountsettings.cpp @@ -14,6 +14,7 @@ #include "accountsettings.h" +#include "common/syncjournaldb.h" #include "common/syncjournalfilerecord.h" #include "qmessagebox.h" #include "ui_accountsettings.h" @@ -1480,11 +1481,22 @@ void AccountSettings::folderTerminateSyncAndUpdateBlackList(const QStringList &b void AccountSettings::refreshSelectiveSyncStatus() { - QString unsyncedMsg; + QString unsyncedFoldersString; + QString becameBigFoldersString; - auto cnt = 0; const auto folders = FolderMan::instance()->map().values(); + static const auto folderSeparatorString = QStringLiteral(", "); + static const auto folderLinkString = [](const QString &slashlessFolderPath, const QString &folderName) { + return QStringLiteral("%1").arg(slashlessFolderPath, folderName); + }; + static const auto appendFolderDisplayString = [](QString &foldersString, const QString &folderDisplayString) { + if (!foldersString.isEmpty()) { + foldersString += folderSeparatorString; + } + foldersString += folderDisplayString; + }; + _ui->bigFolderUi->setVisible(false); for (const auto folder : folders) { @@ -1500,34 +1512,49 @@ void AccountSettings::refreshSelectiveSyncStatus() for (const auto &it : undecidedList) { // FIXME: add the folder alias in a hoover hint. // folder->alias() + QLatin1String("/") - if (cnt++) { - unsyncedMsg += QStringLiteral(", "); - } + const auto folderTrailingSlash = it.endsWith('/') ? it : it + QChar('/'); const auto folderWithoutTrailingSlash = it.endsWith('/') ? it.left(it.length() - 1) : it; - + const auto escapedFolderString = Utility::escape(folderWithoutTrailingSlash); + const auto escapedFolderName = Utility::escape(folder->alias()); const auto folderIdx = _model->indexForPath(folder, folderWithoutTrailingSlash); - if (folderIdx.isValid()) { - const auto escapedFolderString = Utility::escape(folderWithoutTrailingSlash); - const auto escapedFolderName = Utility::escape(folder->alias()); - unsyncedMsg += QStringLiteral("%1").arg(escapedFolderString, escapedFolderName); + + // If we do not know the index yet then do not provide a link string + const auto folderDisplayString = folderIdx.isValid() ? folderLinkString(escapedFolderString, escapedFolderName) : folderWithoutTrailingSlash; + + // The new big folder procedure automatically places these new big folders in the blacklist. + // This is not the case for existing folders discovered to have gone beyond the limit. + // So we need to check if the folder is in the blacklist or not and tweak the message accordingly. + if (SyncJournalDb::findPathInSelectiveSyncList(blacklist, folderTrailingSlash)) { + appendFolderDisplayString(unsyncedFoldersString, folderDisplayString); } else { - unsyncedMsg += folderWithoutTrailingSlash; // no link because we do not know the index yet. + appendFolderDisplayString(becameBigFoldersString, folderDisplayString); } } } - if (!unsyncedMsg.isEmpty()) { - ConfigFile cfg; - const auto info = !cfg.confirmExternalStorage() ? - tr("There are folders that were not synchronized because they are too big: ") : - !cfg.newBigFolderSizeLimit().first ? - tr("There are folders that were not synchronized because they are external storages: ") : - tr("There are folders that were not synchronized because they are too big or external storages: "); + ConfigFile cfg; + QString infoString; + + if (!unsyncedFoldersString.isEmpty()) { + infoString += !cfg.confirmExternalStorage() ? tr("There are folders that were not synchronized because they are too big: ") + : !cfg.newBigFolderSizeLimit().first ? tr("There are folders that were not synchronized because they are external storages: ") + : tr("There are folders that were not synchronized because they are too big or external storages: "); - _ui->selectiveSyncNotification->setText(info + unsyncedMsg); - _ui->bigFolderUi->setVisible(true); + infoString += unsyncedFoldersString; } + + if (!becameBigFoldersString.isEmpty()) { + if (!infoString.isEmpty()) { + infoString += QStringLiteral("\n"); + } + + const auto folderSizeLimitString = QString::number(cfg.newBigFolderSizeLimit().second); + infoString += tr("There are folders that have grown in size beyond %1MB: %2").arg(folderSizeLimitString, becameBigFoldersString); + } + + _ui->selectiveSyncNotification->setText(infoString); + _ui->bigFolderUi->setVisible(!infoString.isEmpty()); } bool AccountSettings::event(QEvent *e) From 993ee6cb5b4f0b493e009a916a6e3838bcda5250 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 5 Jul 2023 00:39:03 +0800 Subject: [PATCH 21/57] Remove redundant "optional" gui log functions which relly just call gui log under the hood Signed-off-by: Claudio Cambra --- src/gui/folder.cpp | 8 ++++---- src/gui/owncloudgui.cpp | 13 ++----------- src/gui/owncloudgui.h | 1 - src/libsync/logger.cpp | 5 ----- src/libsync/logger.h | 2 -- 5 files changed, 6 insertions(+), 23 deletions(-) diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index 9879ed30284f..5d6caf2f10d0 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -486,7 +486,7 @@ void Folder::createGuiLog(const QString &filename, LogStatus status, int count, if (!text.isEmpty()) { // Ignores the settings in case of an error or conflict if(status == LogStatusError || status == LogStatusConflict) - logger->postOptionalGuiLog(tr("Sync Activity"), text); + logger->postGuiLog(tr("Sync Activity"), text); } } } @@ -1213,7 +1213,7 @@ void Folder::slotNewBigFolderDiscovered(const QString &newF, bool isExternal) message += tr("Please go in the settings to select it if you wish to download it."); auto logger = Logger::instance(); - logger->postOptionalGuiLog(Theme::instance()->appNameGUI(), message); + logger->postGuiLog(Theme::instance()->appNameGUI(), message); } } @@ -1234,7 +1234,7 @@ void Folder::slotExistingFolderNowBig(const QString &folderPath) const auto message = tr("A folder has surpassed the set folder size limit of %1MB: %2.\n" "Please go into the settings and disable it if you wish to stop synchronising it.") .arg(QString::number(ConfigFile().newBigFolderSizeLimit().second), folderPath); - Logger::instance()->postOptionalGuiLog(Theme::instance()->appNameGUI(), message); + Logger::instance()->postGuiLog(Theme::instance()->appNameGUI(), message); } } @@ -1303,7 +1303,7 @@ void Folder::warnOnNewExcludedItem(const SyncJournalFileRecord &record, const QS "It will not be synchronized.") .arg(fi.filePath()); - Logger::instance()->postOptionalGuiLog(Theme::instance()->appNameGUI(), message); + Logger::instance()->postGuiLog(Theme::instance()->appNameGUI(), message); } void Folder::slotWatcherUnreliable(const QString &message) diff --git a/src/gui/owncloudgui.cpp b/src/gui/owncloudgui.cpp index 2ea725564937..9d1d5856f340 100644 --- a/src/gui/owncloudgui.cpp +++ b/src/gui/owncloudgui.cpp @@ -107,12 +107,8 @@ ownCloudGui::ownCloudGui(Application *parent) connect(folderMan, &FolderMan::folderSyncStateChange, this, &ownCloudGui::slotSyncStateChange); - connect(Logger::instance(), &Logger::guiLog, - this, &ownCloudGui::slotShowTrayMessage); - connect(Logger::instance(), &Logger::optionalGuiLog, - this, &ownCloudGui::slotShowOptionalTrayMessage); - connect(Logger::instance(), &Logger::guiMessage, - this, &ownCloudGui::slotShowGuiMessage); + connect(Logger::instance(), &Logger::guiLog, this, &ownCloudGui::slotShowTrayMessage); + connect(Logger::instance(), &Logger::guiMessage, this, &ownCloudGui::slotShowGuiMessage); qmlRegisterType("com.nextcloud.desktopclient", 1, 0, "SyncStatusSummary"); qmlRegisterType("com.nextcloud.desktopclient", 1, 0, "EmojiModel"); @@ -426,11 +422,6 @@ void ownCloudGui::slotShowTrayUpdateMessage(const QString &title, const QString } } -void ownCloudGui::slotShowOptionalTrayMessage(const QString &title, const QString &msg) -{ - slotShowTrayMessage(title, msg); -} - /* * open the folder with the given Alias */ diff --git a/src/gui/owncloudgui.h b/src/gui/owncloudgui.h index 41c7895ef686..8315fe228caa 100644 --- a/src/gui/owncloudgui.h +++ b/src/gui/owncloudgui.h @@ -76,7 +76,6 @@ public slots: void slotComputeOverallSyncStatus(); void slotShowTrayMessage(const QString &title, const QString &msg); void slotShowTrayUpdateMessage(const QString &title, const QString &msg, const QUrl &webUrl); - void slotShowOptionalTrayMessage(const QString &title, const QString &msg); void slotFolderOpenAction(const QString &alias); void slotUpdateProgress(const QString &folder, const OCC::ProgressInfo &progress); void slotShowGuiMessage(const QString &title, const QString &message); diff --git a/src/libsync/logger.cpp b/src/libsync/logger.cpp index fffbbc14c636..8cd029cf9b1e 100644 --- a/src/libsync/logger.cpp +++ b/src/libsync/logger.cpp @@ -100,11 +100,6 @@ void Logger::postGuiLog(const QString &title, const QString &message) emit guiLog(title, message); } -void Logger::postOptionalGuiLog(const QString &title, const QString &message) -{ - emit optionalGuiLog(title, message); -} - void Logger::postGuiMessage(const QString &title, const QString &message) { emit guiMessage(title, message); diff --git a/src/libsync/logger.h b/src/libsync/logger.h index 5fd14431e5ee..bfcb2989d5db 100644 --- a/src/libsync/logger.h +++ b/src/libsync/logger.h @@ -42,7 +42,6 @@ class OWNCLOUDSYNC_EXPORT Logger : public QObject static Logger *instance(); void postGuiLog(const QString &title, const QString &message); - void postOptionalGuiLog(const QString &title, const QString &message); void postGuiMessage(const QString &title, const QString &message); QString logFile() const; @@ -87,7 +86,6 @@ class OWNCLOUDSYNC_EXPORT Logger : public QObject void guiLog(const QString &, const QString &); void guiMessage(const QString &, const QString &); - void optionalGuiLog(const QString &, const QString &); public slots: void enterNextLogFile(); From 2bcefd61057a21fc2021ce186abf2e973ff76cef Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 5 Jul 2023 17:16:03 +0800 Subject: [PATCH 22/57] Add methods to Folder so that other classes can easily add folder paths to white/blacklists Signed-off-by: Claudio Cambra --- src/gui/folder.cpp | 24 ++++++++++++++++++++++++ src/gui/folder.h | 5 +++++ 2 files changed, 29 insertions(+) diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index 5d6caf2f10d0..29c4e901989e 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -13,6 +13,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ +#include "common/syncjournaldb.h" #include "config.h" #include "account.h" @@ -842,6 +843,29 @@ bool Folder::pathIsIgnored(const QString &path) const return false; } +void Folder::appendPathToSelectiveSyncList(const QString &path, const SyncJournalDb::SelectiveSyncListType listType) +{ + const auto folderPath = trailingSlashPath(path); + const auto journal = journalDb(); + auto ok = false; + auto list = journal->getSelectiveSyncList(listType, &ok); + + if (ok) { + list.append(folderPath); + journal->setSelectiveSyncList(listType, list); + } +} + +void Folder::whitelistPath(const QString &path) +{ + appendPathToSelectiveSyncList(path, SyncJournalDb::SelectiveSyncWhiteList); +} + +void Folder::blacklistPath(const QString &path) +{ + appendPathToSelectiveSyncList(path, SyncJournalDb::SelectiveSyncBlackList); +} + bool Folder::isFileExcludedAbsolute(const QString &fullPath) const { return _engine->excludedFiles().isExcluded(fullPath, path(), _definition.ignoreHiddenFiles); diff --git a/src/gui/folder.h b/src/gui/folder.h index 78f4023f07eb..5ffbde52d143 100644 --- a/src/gui/folder.h +++ b/src/gui/folder.h @@ -301,6 +301,9 @@ class Folder : public QObject QString fileFromLocalPath(const QString &localPath) const; + void whitelistPath(const QString &path); + void blacklistPath(const QString &path); + signals: void syncStateChange(); void syncStarted(); @@ -468,6 +471,8 @@ private slots: void correctPlaceholderFiles(); + void appendPathToSelectiveSyncList(const QString &path, const SyncJournalDb::SelectiveSyncListType listType); + AccountStatePtr _accountState; FolderDefinition _definition; QString _canonicalLocalPath; // As returned with QFileInfo:canonicalFilePath. Always ends with "/" From 7e17e9226bdc88e9153dd15330242807dd93b6dd Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 5 Jul 2023 17:16:36 +0800 Subject: [PATCH 23/57] Add convenience methods to folderman so that classes can add and remove paths from folders without needing to know which folder the path belongs to Signed-off-by: Claudio Cambra --- src/gui/folderman.cpp | 20 ++++++++++++++++++++ src/gui/folderman.h | 3 +++ 2 files changed, 23 insertions(+) diff --git a/src/gui/folderman.cpp b/src/gui/folderman.cpp index 27a79520036f..198731a8b0b6 100644 --- a/src/gui/folderman.cpp +++ b/src/gui/folderman.cpp @@ -1259,6 +1259,26 @@ Folder *FolderMan::folderForPath(const QString &path) return it != folders.cend() ? *it : nullptr; } +void FolderMan::whitelistFolderPath(const QString &path) +{ + const auto folder = folderForPath(path); + if (!folder) { + return; + } + + folder->whitelistPath(path); +} + +void FolderMan::blacklistFolderPath(const QString &path) +{ + const auto folder = folderForPath(path); + if (!folder) { + return; + } + + folder->blacklistPath(path); +} + QStringList FolderMan::findFileInLocalFolders(const QString &relPath, const AccountPtr acc) { QStringList re; diff --git a/src/gui/folderman.h b/src/gui/folderman.h index 6c236ce8e8a4..03cee25b90f7 100644 --- a/src/gui/folderman.h +++ b/src/gui/folderman.h @@ -104,6 +104,9 @@ class FolderMan : public QObject /** Returns the folder which the file or directory stored in path is in */ Folder *folderForPath(const QString &path); + void whitelistFolderPath(const QString &path); + void blacklistFolderPath(const QString &path); + /** * returns a list of local files that exist on the local harddisk for an * incoming relative server path. The method checks with all existing sync From 29cc9c06f98478b499d13807482b91bcc2db5999 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 5 Jul 2023 17:17:28 +0800 Subject: [PATCH 24/57] Handle WHITELIST_FOLDER and BLACKLIST_FOLDER verbs in activitylistmodel Signed-off-by: Claudio Cambra --- src/gui/tray/activitylistmodel.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/gui/tray/activitylistmodel.cpp b/src/gui/tray/activitylistmodel.cpp index f6c28a85d359..69cb0d2910cf 100644 --- a/src/gui/tray/activitylistmodel.cpp +++ b/src/gui/tray/activitylistmodel.cpp @@ -836,6 +836,12 @@ void ActivityListModel::slotTriggerAction(const int activityIndex, const int act (activity._syncFileItemStatus == SyncFileItem::Conflict || activity._syncFileItemStatus == SyncFileItem::FileNameClash)) { slotTriggerDefaultAction(activityIndex); return; + } else if (action._verb == "WHITELIST_FOLDER" && !activity._folder.isEmpty()) { + FolderMan::instance()->whitelistFolderPath(activity._folder); + return; + } else if (action._verb == "BLACKLIST_FOLDER" && !activity._folder.isEmpty()) { + FolderMan::instance()->blacklistFolderPath(activity._folder); + return; } emit sendNotificationRequest(activity._accName, action._link, action._verb, activityIndex); From 10989fa8106998a8246292cd284d3c8e2f02da9d Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 5 Jul 2023 21:35:33 +0800 Subject: [PATCH 25/57] Add method to get user pointer in UserModel by AccountState pointer Signed-off-by: Claudio Cambra --- src/gui/tray/usermodel.cpp | 15 +++++++++++++++ src/gui/tray/usermodel.h | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/gui/tray/usermodel.cpp b/src/gui/tray/usermodel.cpp index e9a5ed73ae43..2d4bef2fb98b 100644 --- a/src/gui/tray/usermodel.cpp +++ b/src/gui/tray/usermodel.cpp @@ -1526,6 +1526,21 @@ User *UserModel::currentUser() const return _users[currentUserId()]; } +User *UserModel::findUserForAccount(AccountState *account) const +{ + Q_ASSERT(account); + + const auto it = std::find_if(_users.cbegin(), _users.cend(), [account](const User *user) { + return user->account()->id() == account->account()->id(); + }); + + if (it == _users.cend()) { + return nullptr; + } + + return *it; +} + int UserModel::findUserIdForAccount(AccountState *account) const { const auto it = std::find_if(std::cbegin(_users), std::cend(_users), [=](const User *user) { diff --git a/src/gui/tray/usermodel.h b/src/gui/tray/usermodel.h index bfeaff46d248..b39a49b5d806 100644 --- a/src/gui/tray/usermodel.h +++ b/src/gui/tray/usermodel.h @@ -210,8 +210,8 @@ class UserModel : public QAbstractListModel [[nodiscard]] QImage avatarById(const int id) const; [[nodiscard]] User *currentUser() const; - - int findUserIdForAccount(AccountState *account) const; + [[nodiscard]] User *findUserForAccount(AccountState *account) const; + [[nodiscard]] int findUserIdForAccount(AccountState *account) const; Q_INVOKABLE int numUsers(); Q_INVOKABLE QString currentUserServer(); From e985367b5e97ae26e73cccbc625d63803d926236 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 5 Jul 2023 22:05:21 +0800 Subject: [PATCH 26/57] Add method to add notification activity in activity list in User Signed-off-by: Claudio Cambra --- src/gui/tray/usermodel.cpp | 9 +++++++++ src/gui/tray/usermodel.h | 6 ++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/gui/tray/usermodel.cpp b/src/gui/tray/usermodel.cpp index 2d4bef2fb98b..926a159c6b44 100644 --- a/src/gui/tray/usermodel.cpp +++ b/src/gui/tray/usermodel.cpp @@ -733,6 +733,15 @@ void User::slotAddErrorToGui(const QString &folderAlias, const SyncFileItem::Sta } } +void User::slotAddNotification(const Folder *folder, const Activity &activity) +{ + if (!isActivityOfCurrentAccount(folder)) { + return; + } + + _activityModel->addNotificationToActivityList(activity); +} + bool User::isActivityOfCurrentAccount(const Folder *folder) const { return folder->accountState() == _account.data(); diff --git a/src/gui/tray/usermodel.h b/src/gui/tray/usermodel.h index b39a49b5d806..c92e98b7c938 100644 --- a/src/gui/tray/usermodel.h +++ b/src/gui/tray/usermodel.h @@ -8,12 +8,13 @@ #include #include -#include "activitylistmodel.h" #include "accountfwd.h" #include "accountmanager.h" +#include "activitydata.h" +#include "activitylistmodel.h" #include "folderman.h" -#include "userstatusselectormodel.h" #include "userstatusconnector.h" +#include "userstatusselectormodel.h" #include namespace OCC { @@ -118,6 +119,7 @@ public slots: void slotProgressInfo(const QString &folder, const OCC::ProgressInfo &progress); void slotAddError(const QString &folderAlias, const QString &message, OCC::ErrorCategory category); void slotAddErrorToGui(const QString &folderAlias, const OCC::SyncFileItem::Status status, const QString &errorMessage, const QString &subject, const OCC::ErrorCategory category); + void slotAddNotification(const OCC::Folder *folder, const OCC::Activity &activity); void slotNotificationRequestFinished(int statusCode); void slotNotifyNetworkError(QNetworkReply *reply); void slotEndNotificationRequest(int replyCode); From fe59d4ec7948d931402bcee9cbf62e0baca7933e Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 5 Jul 2023 23:32:08 +0800 Subject: [PATCH 27/57] Ensure whitelist/blacklist procedure removes activity Signed-off-by: Claudio Cambra --- src/gui/tray/activitylistmodel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/tray/activitylistmodel.cpp b/src/gui/tray/activitylistmodel.cpp index 69cb0d2910cf..87df7e1b6b4f 100644 --- a/src/gui/tray/activitylistmodel.cpp +++ b/src/gui/tray/activitylistmodel.cpp @@ -838,10 +838,10 @@ void ActivityListModel::slotTriggerAction(const int activityIndex, const int act return; } else if (action._verb == "WHITELIST_FOLDER" && !activity._folder.isEmpty()) { FolderMan::instance()->whitelistFolderPath(activity._folder); - return; + removeActivityFromActivityList(activity); } else if (action._verb == "BLACKLIST_FOLDER" && !activity._folder.isEmpty()) { FolderMan::instance()->blacklistFolderPath(activity._folder); - return; + removeActivityFromActivityList(activity); } emit sendNotificationRequest(activity._accName, action._link, action._verb, activityIndex); From 12931069c021edda9ce418c38f36094e5af658a1 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 5 Jul 2023 23:32:46 +0800 Subject: [PATCH 28/57] Create more than one converted activity link, therby respecting maxactionbuttons Signed-off-by: Claudio Cambra --- src/gui/tray/activitylistmodel.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gui/tray/activitylistmodel.cpp b/src/gui/tray/activitylistmodel.cpp index 87df7e1b6b4f..4dab1d8ae2ef 100644 --- a/src/gui/tray/activitylistmodel.cpp +++ b/src/gui/tray/activitylistmodel.cpp @@ -870,12 +870,13 @@ QVariantList ActivityListModel::convertLinksToActionButtons(const Activity &acti QVariantList customList; for (const auto &activityLink : activity._links) { - if (!activityLink._primary) { + // Use the isDismissable model role to present custom dismiss button if needed + // Also don't show "View chat" for talk activities, default action will open chat anyway + if (activityLink._verb == "DELETE" || (activityLink._verb == "WEB" && activity._objectType == "chat")) { continue; } customList << ActivityListModel::convertLinkToActionButton(activityLink); - break; } return customList; From c975d40fb8cda8a78d910342276e707677746390 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 5 Jul 2023 23:33:43 +0800 Subject: [PATCH 29/57] Do not create dismiss links for notifications as these are not used anyway Signed-off-by: Claudio Cambra --- src/gui/tray/notificationhandler.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/gui/tray/notificationhandler.cpp b/src/gui/tray/notificationhandler.cpp index 62ff2f32832b..a1321d4c7dc0 100644 --- a/src/gui/tray/notificationhandler.cpp +++ b/src/gui/tray/notificationhandler.cpp @@ -149,14 +149,6 @@ void ServerNotificationHandler::slotNotificationsReceived(const QJsonDocument &j a._links.insert(al._primary? 0 : a._links.size(), al); } - if (a._links.isEmpty()) { - ActivityLink dismissLink; - dismissLink._label = tr("Dismiss"); - dismissLink._verb = "DELETE"; - dismissLink._primary = false; - a._links.insert(0, dismissLink); - } - QUrl link(json.value("link").toString()); if (!link.isEmpty()) { if (link.host().isEmpty()) { From 7d1fa16a3f3daeb4bacb54db37e4567d018368dd Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 5 Jul 2023 23:35:13 +0800 Subject: [PATCH 30/57] Add activity for existing folder going over set limit Signed-off-by: Claudio Cambra --- src/gui/folder.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index 29c4e901989e..81c63dc56144 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -1259,6 +1259,29 @@ void Folder::slotExistingFolderNowBig(const QString &folderPath) "Please go into the settings and disable it if you wish to stop synchronising it.") .arg(QString::number(ConfigFile().newBigFolderSizeLimit().second), folderPath); Logger::instance()->postGuiLog(Theme::instance()->appNameGUI(), message); + + auto blacklistActivityLink = ActivityLink(); + blacklistActivityLink._label = tr("Stop syncing"); + blacklistActivityLink._primary = true; + blacklistActivityLink._verb = "BLACKLIST_FOLDER"; + + auto whitelistActivityLink = ActivityLink(); + whitelistActivityLink._label = tr("Keep syncing"); + whitelistActivityLink._primary = false; + whitelistActivityLink._verb = "WHITELIST_FOLDER"; + + auto existingFolderNowBigActivity = Activity(); + existingFolderNowBigActivity._type = Activity::NotificationType; + existingFolderNowBigActivity._dateTime = QDateTime::fromString(QDateTime::currentDateTime().toString(), Qt::ISODate); + existingFolderNowBigActivity._subject = + tr("The folder %1 has surpassed the set folder size limit of %2MB.").arg(folderPath, QString::number(ConfigFile().newBigFolderSizeLimit().second)); + existingFolderNowBigActivity._message = tr("Would you like to stop syncing this folder?"); + existingFolderNowBigActivity._accName = _accountState->account()->displayName(); + existingFolderNowBigActivity._folder = alias(); + existingFolderNowBigActivity._links = {blacklistActivityLink, whitelistActivityLink}; + + const auto user = UserModel::instance()->findUserForAccount(_accountState.data()); + user->slotAddNotification(this, existingFolderNowBigActivity); } } From 5f250a5dad3fbe709488e271f4692c05f3984264 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 5 Jul 2023 23:44:45 +0800 Subject: [PATCH 31/57] Ensure newly whitelisted or blacklisted paths are removed from undecided list Signed-off-by: Claudio Cambra --- src/gui/folder.cpp | 15 +++++++++++++++ src/gui/folder.h | 1 + 2 files changed, 16 insertions(+) diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index 81c63dc56144..dd246f109437 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -856,14 +856,29 @@ void Folder::appendPathToSelectiveSyncList(const QString &path, const SyncJourna } } +void Folder::removePathFromSelectiveSyncList(const QString &path, const SyncJournalDb::SelectiveSyncListType listType) +{ + const auto folderPath = trailingSlashPath(path); + const auto journal = journalDb(); + auto ok = false; + auto list = journal->getSelectiveSyncList(listType, &ok); + + if (ok) { + list.removeAll(folderPath); + journal->setSelectiveSyncList(listType, list); + } +} + void Folder::whitelistPath(const QString &path) { appendPathToSelectiveSyncList(path, SyncJournalDb::SelectiveSyncWhiteList); + removePathFromSelectiveSyncList(path, SyncJournalDb::SelectiveSyncUndecidedList); } void Folder::blacklistPath(const QString &path) { appendPathToSelectiveSyncList(path, SyncJournalDb::SelectiveSyncBlackList); + removePathFromSelectiveSyncList(path, SyncJournalDb::SelectiveSyncUndecidedList); } bool Folder::isFileExcludedAbsolute(const QString &fullPath) const diff --git a/src/gui/folder.h b/src/gui/folder.h index 5ffbde52d143..821f90716b8d 100644 --- a/src/gui/folder.h +++ b/src/gui/folder.h @@ -472,6 +472,7 @@ private slots: void correctPlaceholderFiles(); void appendPathToSelectiveSyncList(const QString &path, const SyncJournalDb::SelectiveSyncListType listType); + void removePathFromSelectiveSyncList(const QString &path, const SyncJournalDb::SelectiveSyncListType listType); AccountStatePtr _accountState; FolderDefinition _definition; From e2535d6ce819c02afd2af5bd7259b8511054ba53 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Thu, 6 Jul 2023 02:24:10 +0800 Subject: [PATCH 32/57] Prevent double notification of folder now big in activities Signed-off-by: Claudio Cambra --- src/gui/folder.cpp | 1 + src/gui/tray/usermodel.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index dd246f109437..205ab534fbc9 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -1294,6 +1294,7 @@ void Folder::slotExistingFolderNowBig(const QString &folderPath) existingFolderNowBigActivity._accName = _accountState->account()->displayName(); existingFolderNowBigActivity._folder = alias(); existingFolderNowBigActivity._links = {blacklistActivityLink, whitelistActivityLink}; + existingFolderNowBigActivity._id = qHash(existingFolderNowBigActivity._file); const auto user = UserModel::instance()->findUserForAccount(_accountState.data()); user->slotAddNotification(this, existingFolderNowBigActivity); diff --git a/src/gui/tray/usermodel.cpp b/src/gui/tray/usermodel.cpp index 926a159c6b44..896ff85e4d86 100644 --- a/src/gui/tray/usermodel.cpp +++ b/src/gui/tray/usermodel.cpp @@ -735,10 +735,11 @@ void User::slotAddErrorToGui(const QString &folderAlias, const SyncFileItem::Sta void User::slotAddNotification(const Folder *folder, const Activity &activity) { - if (!isActivityOfCurrentAccount(folder)) { + if (!isActivityOfCurrentAccount(folder) || _notifiedNotifications.contains(activity._id)) { return; } + _notifiedNotifications.insert(activity._id); _activityModel->addNotificationToActivityList(activity); } From f9bde98aad57091c73b34a2bbbaa5a4629ac6efc Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Thu, 6 Jul 2023 02:38:23 +0800 Subject: [PATCH 33/57] Prevent possible accountsettings crash by sorting blacklist before checking Signed-off-by: Claudio Cambra --- src/gui/accountsettings.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gui/accountsettings.cpp b/src/gui/accountsettings.cpp index 230337cd2bea..2009412d69a7 100644 --- a/src/gui/accountsettings.cpp +++ b/src/gui/accountsettings.cpp @@ -1507,7 +1507,8 @@ void AccountSettings::refreshSelectiveSyncStatus() auto ok = false; auto blacklistOk = false; const auto undecidedList = folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncUndecidedList, &ok); - const auto blacklist = folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, &blacklistOk); + auto blacklist = folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, &blacklistOk); + blacklist.sort(); for (const auto &it : undecidedList) { // FIXME: add the folder alias in a hoover hint. From 6682b3ade4f526db5226e3989b7215a53fe1c0eb Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Thu, 6 Jul 2023 02:38:49 +0800 Subject: [PATCH 34/57] Ensure folder whitelist and blacklist helper methods remove path from other lists Signed-off-by: Claudio Cambra --- src/gui/folder.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index 205ab534fbc9..d40017dec309 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -871,14 +871,16 @@ void Folder::removePathFromSelectiveSyncList(const QString &path, const SyncJour void Folder::whitelistPath(const QString &path) { - appendPathToSelectiveSyncList(path, SyncJournalDb::SelectiveSyncWhiteList); removePathFromSelectiveSyncList(path, SyncJournalDb::SelectiveSyncUndecidedList); + removePathFromSelectiveSyncList(path, SyncJournalDb::SelectiveSyncBlackList); + appendPathToSelectiveSyncList(path, SyncJournalDb::SelectiveSyncWhiteList); } void Folder::blacklistPath(const QString &path) { - appendPathToSelectiveSyncList(path, SyncJournalDb::SelectiveSyncBlackList); removePathFromSelectiveSyncList(path, SyncJournalDb::SelectiveSyncUndecidedList); + removePathFromSelectiveSyncList(path, SyncJournalDb::SelectiveSyncWhiteList); + appendPathToSelectiveSyncList(path, SyncJournalDb::SelectiveSyncBlackList); } bool Folder::isFileExcludedAbsolute(const QString &fullPath) const From a93279bcbf0cc637313f0c85fef54e0d2788a20e Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Thu, 6 Jul 2023 02:47:05 +0800 Subject: [PATCH 35/57] Default to adding existing now big folders to whitelist when undecided Signed-off-by: Claudio Cambra --- src/gui/folder.cpp | 11 +++++++++++ src/gui/folderman.cpp | 8 ++++++-- src/gui/folderman.h | 1 + src/gui/tray/activitylistmodel.cpp | 10 ++++++---- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index d40017dec309..934acd751965 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -1263,6 +1263,16 @@ void Folder::slotExistingFolderNowBig(const QString &folderPath) const auto trailSlashFolderPath = trailingSlashPath(folderPath); const auto journal = journalDb(); + // Add the entry to the whitelist if it is neither in the blacklist or whitelist already + bool ok1 = false; + bool ok2 = false; + auto blacklist = journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, &ok1); + auto whitelist = journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncWhiteList, &ok2); + if (ok1 && ok2 && !blacklist.contains(trailSlashFolderPath) && !whitelist.contains(trailSlashFolderPath)) { + whitelist.append(trailSlashFolderPath); + journal->setSelectiveSyncList(SyncJournalDb::SelectiveSyncWhiteList, whitelist); + } + auto undecidedListQueryOk = false; auto undecidedList = journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncUndecidedList, &undecidedListQueryOk); if (undecidedListQueryOk) { @@ -1295,6 +1305,7 @@ void Folder::slotExistingFolderNowBig(const QString &folderPath) existingFolderNowBigActivity._message = tr("Would you like to stop syncing this folder?"); existingFolderNowBigActivity._accName = _accountState->account()->displayName(); existingFolderNowBigActivity._folder = alias(); + existingFolderNowBigActivity._file = cleanPath() + '/' + trailSlashFolderPath; existingFolderNowBigActivity._links = {blacklistActivityLink, whitelistActivityLink}; existingFolderNowBigActivity._id = qHash(existingFolderNowBigActivity._file); diff --git a/src/gui/folderman.cpp b/src/gui/folderman.cpp index 198731a8b0b6..efbf46d906f5 100644 --- a/src/gui/folderman.cpp +++ b/src/gui/folderman.cpp @@ -1266,7 +1266,9 @@ void FolderMan::whitelistFolderPath(const QString &path) return; } - folder->whitelistPath(path); + const QString folderPath = folder->cleanPath() + QLatin1Char('/'); + const auto relPath = path.mid(folderPath.length()); + folder->whitelistPath(relPath); } void FolderMan::blacklistFolderPath(const QString &path) @@ -1276,7 +1278,9 @@ void FolderMan::blacklistFolderPath(const QString &path) return; } - folder->blacklistPath(path); + const QString folderPath = folder->cleanPath() + QLatin1Char('/'); + const auto relPath = path.mid(folderPath.length()); + folder->blacklistPath(relPath); } QStringList FolderMan::findFileInLocalFolders(const QString &relPath, const AccountPtr acc) diff --git a/src/gui/folderman.h b/src/gui/folderman.h index 03cee25b90f7..a79f1119355c 100644 --- a/src/gui/folderman.h +++ b/src/gui/folderman.h @@ -104,6 +104,7 @@ class FolderMan : public QObject /** Returns the folder which the file or directory stored in path is in */ Folder *folderForPath(const QString &path); + // Takes local file paths and finds the corresponding folder, adding to correct selective sync list void whitelistFolderPath(const QString &path); void blacklistFolderPath(const QString &path); diff --git a/src/gui/tray/activitylistmodel.cpp b/src/gui/tray/activitylistmodel.cpp index 4dab1d8ae2ef..46aa5ff1fd6b 100644 --- a/src/gui/tray/activitylistmodel.cpp +++ b/src/gui/tray/activitylistmodel.cpp @@ -836,12 +836,14 @@ void ActivityListModel::slotTriggerAction(const int activityIndex, const int act (activity._syncFileItemStatus == SyncFileItem::Conflict || activity._syncFileItemStatus == SyncFileItem::FileNameClash)) { slotTriggerDefaultAction(activityIndex); return; - } else if (action._verb == "WHITELIST_FOLDER" && !activity._folder.isEmpty()) { - FolderMan::instance()->whitelistFolderPath(activity._folder); + } else if (action._verb == "WHITELIST_FOLDER" && !activity._file.isEmpty()) { // _folder == folder alias/name, _file == folder/file path + FolderMan::instance()->whitelistFolderPath(activity._file); removeActivityFromActivityList(activity); - } else if (action._verb == "BLACKLIST_FOLDER" && !activity._folder.isEmpty()) { - FolderMan::instance()->blacklistFolderPath(activity._folder); + return; + } else if (action._verb == "BLACKLIST_FOLDER" && !activity._file.isEmpty()) { + FolderMan::instance()->blacklistFolderPath(activity._file); removeActivityFromActivityList(activity); + return; } emit sendNotificationRequest(activity._accName, action._link, action._verb, activityIndex); From 74c4ec88a295d007a6d6958e8262acb4821f7ade Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 10 Jul 2023 17:14:40 +0800 Subject: [PATCH 36/57] Const autofy variables in ProcessDirectoryJob::processFile Signed-off-by: Claudio Cambra --- src/libsync/discovery.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp index 3205580eeb3e..9361930944fe 100644 --- a/src/libsync/discovery.cpp +++ b/src/libsync/discovery.cpp @@ -457,8 +457,8 @@ void ProcessDirectoryJob::processFile(PathTuple path, const LocalInfo &localEntry, const RemoteInfo &serverEntry, const SyncJournalFileRecord &dbEntry) { - const char *hasServer = serverEntry.isValid() ? "true" : _queryServer == ParentNotChanged ? "db" : "false"; - const char *hasLocal = localEntry.isValid() ? "true" : _queryLocal == ParentNotChanged ? "db" : "false"; + const auto hasServer = serverEntry.isValid() ? "true" : _queryServer == ParentNotChanged ? "db" : "false"; + const auto hasLocal = localEntry.isValid() ? "true" : _queryLocal == ParentNotChanged ? "db" : "false"; const auto serverFileIsLocked = (serverEntry.isValid() ? (serverEntry.locked == SyncFileItem::LockStatus::LockedItem ? "locked" : "not locked") : ""); const auto localFileIsLocked = dbEntry._lockstate._locked ? "locked" : "not locked"; qCInfo(lcDisco).nospace() << "Processing " << path._original @@ -488,7 +488,7 @@ void ProcessDirectoryJob::processFile(PathTuple path, return; // Ignore this. } - auto item = SyncFileItem::fromSyncJournalFileRecord(dbEntry); + const auto item = SyncFileItem::fromSyncJournalFileRecord(dbEntry); item->_file = path._target; item->_originalFile = path._original; item->_previousSize = dbEntry._fileSize; From 1b73011f598db065e66ebe04ce0d182ca352cedc Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 10 Jul 2023 17:20:23 +0800 Subject: [PATCH 37/57] Constify SyncJournalDb::findPathInSelectiveSyncList Signed-off-by: Claudio Cambra --- src/common/syncjournaldb.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/common/syncjournaldb.cpp b/src/common/syncjournaldb.cpp index 7cbc9b540778..fb192e21e306 100644 --- a/src/common/syncjournaldb.cpp +++ b/src/common/syncjournaldb.cpp @@ -207,24 +207,24 @@ bool SyncJournalDb::maybeMigrateDb(const QString &localPath, const QString &abso bool SyncJournalDb::findPathInSelectiveSyncList(const QStringList &list, const QString &path) { - Q_ASSERT(std::is_sorted(list.begin(), list.end())); + Q_ASSERT(std::is_sorted(list.cbegin(), list.cend())); - if (list.size() == 1 && list.first() == QLatin1String("/")) { + if (list.size() == 1 && list.first() == QStringLiteral("/")) { // Special case for the case "/" is there, it matches everything return true; } - QString pathSlash = path + QLatin1Char('/'); + const QString pathSlash = path + QLatin1Char('/'); // Since the list is sorted, we can do a binary search. // If the path is a prefix of another item or right after in the lexical order. - auto it = std::lower_bound(list.begin(), list.end(), pathSlash); + auto it = std::lower_bound(list.cbegin(), list.cend(), pathSlash); - if (it != list.end() && *it == pathSlash) { + if (it != list.cend() && *it == pathSlash) { return true; } - if (it == list.begin()) { + if (it == list.cbegin()) { return false; } --it; From 82f6fd5c2751d4ed6d0af33a2dc60c036be1016d Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 10 Jul 2023 17:20:44 +0800 Subject: [PATCH 38/57] Remove use of old foreach in SyncJournalDb::setSelectiveSyncList Signed-off-by: Claudio Cambra --- src/common/syncjournaldb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/syncjournaldb.cpp b/src/common/syncjournaldb.cpp index fb192e21e306..c58bd695e2e6 100644 --- a/src/common/syncjournaldb.cpp +++ b/src/common/syncjournaldb.cpp @@ -2013,7 +2013,7 @@ void SyncJournalDb::setSelectiveSyncList(SyncJournalDb::SelectiveSyncListType ty } SqlQuery insQuery("INSERT INTO selectivesync VALUES (?1, ?2)", _db); - foreach (const auto &path, list) { + for (const auto &path : list) { insQuery.reset_and_clear_bindings(); insQuery.bindValue(1, path); insQuery.bindValue(2, int(type)); From 9fa5ae36045649bafa158e2fc755c18d88bdb3f3 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 10 Jul 2023 17:21:40 +0800 Subject: [PATCH 39/57] Use simpler sort methods for white and black lists in discoveryphase Signed-off-by: Claudio Cambra --- src/libsync/discoveryphase.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libsync/discoveryphase.cpp b/src/libsync/discoveryphase.cpp index e1d95c28a183..81aa34ea8f11 100644 --- a/src/libsync/discoveryphase.cpp +++ b/src/libsync/discoveryphase.cpp @@ -248,13 +248,13 @@ void DiscoveryPhase::startJob(ProcessDirectoryJob *job) void DiscoveryPhase::setSelectiveSyncBlackList(const QStringList &list) { _selectiveSyncBlackList = list; - std::sort(_selectiveSyncBlackList.begin(), _selectiveSyncBlackList.end()); + _selectiveSyncBlackList.sort(); } void DiscoveryPhase::setSelectiveSyncWhiteList(const QStringList &list) { _selectiveSyncWhiteList = list; - std::sort(_selectiveSyncWhiteList.begin(), _selectiveSyncWhiteList.end()); + _selectiveSyncWhiteList.sort(); } void DiscoveryPhase::scheduleMoreJobs() From ac8e9c427499d5e77212e8849df05788d0402cf4 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 10 Jul 2023 17:22:14 +0800 Subject: [PATCH 40/57] Fix trailing slash procedure in DiscoveryPhase::checkSelectiveSyncNewFolder Signed-off-by: Claudio Cambra --- src/libsync/discoveryphase.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libsync/discoveryphase.cpp b/src/libsync/discoveryphase.cpp index 81aa34ea8f11..e992159a9331 100644 --- a/src/libsync/discoveryphase.cpp +++ b/src/libsync/discoveryphase.cpp @@ -122,7 +122,8 @@ void DiscoveryPhase::checkSelectiveSyncNewFolder(const QString &path, } // it is not too big, put it in the white list (so we will not do more query for the children) and and do not block. - const auto sanitisedPath = path.endsWith(QLatin1Char('/')) ? path + QLatin1Char('/') : path; + static const auto slash = QLatin1Char('/'); + const auto sanitisedPath = path.endsWith(slash) ? path : path + slash; _selectiveSyncWhiteList.insert(std::upper_bound(_selectiveSyncWhiteList.begin(), _selectiveSyncWhiteList.end(), sanitisedPath), sanitisedPath); return callback(false); }); From 3b208bf838a5f9f2702896116574b87fa8f401b4 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 10 Jul 2023 17:27:58 +0800 Subject: [PATCH 41/57] Move trailingSlashPath convenience function into Utility class Signed-off-by: Claudio Cambra --- src/common/utility.cpp | 6 ++++++ src/common/utility.h | 2 ++ src/gui/folder.cpp | 23 +++++++++-------------- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/common/utility.cpp b/src/common/utility.cpp index 9baf1377720f..61f2489e4255 100644 --- a/src/common/utility.cpp +++ b/src/common/utility.cpp @@ -723,4 +723,10 @@ bool Utility::isCaseClashConflictFile(const QString &name) return bname.contains(QStringLiteral("(case clash from")); } +QString Utility::trailingSlashPath(const QString &path) +{ + static const auto slash = QLatin1Char('/'); + return path.endsWith(slash) ? path : QString(path + slash); +} + } // namespace OCC diff --git a/src/common/utility.h b/src/common/utility.h index 0dd693f284a7..b554a196be18 100644 --- a/src/common/utility.h +++ b/src/common/utility.h @@ -255,6 +255,8 @@ namespace Utility { */ OCSYNC_EXPORT void registerUriHandlerForLocalEditing(); + OCSYNC_EXPORT QString trailingSlashPath(const QString &path); + #ifdef Q_OS_WIN OCSYNC_EXPORT bool registryKeyExists(HKEY hRootKey, const QString &subKey); OCSYNC_EXPORT QVariant registryGetKeyValue(HKEY hRootKey, const QString &subKey, const QString &valueName); diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index 934acd751965..f8a60416f1bc 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -52,11 +52,6 @@ namespace { #define VERSION_C constexpr auto versionC = "version"; #endif - -QString trailingSlashPath(const QString &path) -{ - return path.endsWith('/') ? path : QString(path + QStringLiteral("/")); -} } namespace OCC { @@ -177,7 +172,7 @@ void Folder::checkLocalPath() _canonicalLocalPath = _definition.localPath; } - _canonicalLocalPath = trailingSlashPath(_canonicalLocalPath); + _canonicalLocalPath = Utility::trailingSlashPath(_canonicalLocalPath); if (fi.isDir() && fi.isReadable()) { qCDebug(lcFolder) << "Checked local path ok"; @@ -222,7 +217,7 @@ QString Folder::path() const QString Folder::shortGuiLocalPath() const { QString p = _definition.localPath; - const auto home = trailingSlashPath(QDir::homePath()); + const auto home = Utility::trailingSlashPath(QDir::homePath()); if (p.startsWith(home)) { p = p.mid(home.length()); @@ -272,7 +267,7 @@ QString Folder::remotePath() const QString Folder::remotePathTrailingSlash() const { - return trailingSlashPath(remotePath()); + return Utility::trailingSlashPath(remotePath()); } QUrl Folder::remoteUrl() const @@ -845,7 +840,7 @@ bool Folder::pathIsIgnored(const QString &path) const void Folder::appendPathToSelectiveSyncList(const QString &path, const SyncJournalDb::SelectiveSyncListType listType) { - const auto folderPath = trailingSlashPath(path); + const auto folderPath = Utility::trailingSlashPath(path); const auto journal = journalDb(); auto ok = false; auto list = journal->getSelectiveSyncList(listType, &ok); @@ -858,7 +853,7 @@ void Folder::appendPathToSelectiveSyncList(const QString &path, const SyncJourna void Folder::removePathFromSelectiveSyncList(const QString &path, const SyncJournalDb::SelectiveSyncListType listType) { - const auto folderPath = trailingSlashPath(path); + const auto folderPath = Utility::trailingSlashPath(path); const auto journal = journalDb(); auto ok = false; auto list = journal->getSelectiveSyncList(listType, &ok); @@ -1226,7 +1221,7 @@ void Folder::slotItemCompleted(const SyncFileItemPtr &item, ErrorCategory errorC void Folder::slotNewBigFolderDiscovered(const QString &newF, bool isExternal) { - const auto newFolder = trailingSlashPath(newF); + const auto newFolder = Utility::trailingSlashPath(newF); auto journal = journalDb(); // Add the entry to the blacklist if it is neither in the blacklist or whitelist already @@ -1260,7 +1255,7 @@ void Folder::slotNewBigFolderDiscovered(const QString &newF, bool isExternal) void Folder::slotExistingFolderNowBig(const QString &folderPath) { - const auto trailSlashFolderPath = trailingSlashPath(folderPath); + const auto trailSlashFolderPath = Utility::trailingSlashPath(folderPath); const auto journal = journalDb(); // Add the entry to the whitelist if it is neither in the blacklist or whitelist already @@ -1547,7 +1542,7 @@ void Folder::removeLocalE2eFiles() } if (!parentPathEncrypted) { - const auto pathAdjusted = trailingSlashPath(rec._path); + const auto pathAdjusted = Utility::trailingSlashPath(rec._path); e2eFoldersToBlacklist.append(pathAdjusted); } } @@ -1663,7 +1658,7 @@ bool FolderDefinition::load(QSettings &settings, const QString &alias, QString FolderDefinition::prepareLocalPath(const QString &path) { const auto normalisedPath = QDir::fromNativeSeparators(path); - return trailingSlashPath(normalisedPath); + return Utility::trailingSlashPath(normalisedPath); } QString FolderDefinition::prepareTargetPath(const QString &path) From 21656cce17463a6d74665b465b694f78a892b535 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 10 Jul 2023 17:51:24 +0800 Subject: [PATCH 42/57] Replace all manual isntancesof adding trailing slash with use of utility method Signed-off-by: Claudio Cambra --- src/common/syncjournaldb.cpp | 5 +---- src/gui/accountsettings.cpp | 2 +- src/gui/folderstatusmodel.cpp | 11 +++++------ src/gui/folderwatcher_win.h | 5 +++-- src/gui/owncloudsetupwizard.cpp | 23 +++++++++-------------- src/gui/selectivesyncdialog.cpp | 21 +++++++++------------ src/gui/socketapi/socketuploadjob.cpp | 3 ++- src/gui/wizard/webviewpage.cpp | 11 ++++------- src/libsync/configfile.cpp | 5 +---- src/libsync/discovery.cpp | 2 +- src/libsync/discoveryphase.cpp | 4 ++-- src/libsync/owncloudpropagator.h | 17 ++++++++--------- src/libsync/syncengine.cpp | 8 ++------ src/libsync/theme.cpp | 10 +++++----- test/syncenginetestutils.cpp | 18 +++++------------- 15 files changed, 58 insertions(+), 87 deletions(-) diff --git a/src/common/syncjournaldb.cpp b/src/common/syncjournaldb.cpp index c58bd695e2e6..03719c6d2bf8 100644 --- a/src/common/syncjournaldb.cpp +++ b/src/common/syncjournaldb.cpp @@ -1985,10 +1985,7 @@ QStringList SyncJournalDb::getSelectiveSyncList(SyncJournalDb::SelectiveSyncList if (!next.hasData) break; - auto entry = query->stringValue(0); - if (!entry.endsWith(QLatin1Char('/'))) { - entry.append(QLatin1Char('/')); - } + const auto entry = Utility::trailingSlashPath(query->stringValue(0)); result.append(entry); } *ok = true; diff --git a/src/gui/accountsettings.cpp b/src/gui/accountsettings.cpp index 2009412d69a7..cc300174d506 100644 --- a/src/gui/accountsettings.cpp +++ b/src/gui/accountsettings.cpp @@ -1514,7 +1514,7 @@ void AccountSettings::refreshSelectiveSyncStatus() // FIXME: add the folder alias in a hoover hint. // folder->alias() + QLatin1String("/") - const auto folderTrailingSlash = it.endsWith('/') ? it : it + QChar('/'); + const auto folderTrailingSlash = Utility::trailingSlashPath(it); const auto folderWithoutTrailingSlash = it.endsWith('/') ? it.left(it.length() - 1) : it; const auto escapedFolderString = Utility::escape(folderWithoutTrailingSlash); const auto escapedFolderName = Utility::escape(folder->alias()); diff --git a/src/gui/folderstatusmodel.cpp b/src/gui/folderstatusmodel.cpp index 8a92b291ab67..061efc8624b0 100644 --- a/src/gui/folderstatusmodel.cpp +++ b/src/gui/folderstatusmodel.cpp @@ -13,12 +13,13 @@ */ #include "folderstatusmodel.h" -#include "folderman.h" #include "accountstate.h" #include "common/asserts.h" -#include -#include +#include "common/utility.h" +#include "folderman.h" #include "folderstatusdelegate.h" +#include +#include #include #include @@ -703,9 +704,7 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list) parentInfo->_fetched = true; QUrl url = parentInfo->_folder->remoteUrl(); - QString pathToRemove = url.path(); - if (!pathToRemove.endsWith('/')) - pathToRemove += '/'; + const auto pathToRemove = Utility::trailingSlashPath(url.path()); QStringList selectiveSyncBlackList; bool ok1 = true; diff --git a/src/gui/folderwatcher_win.h b/src/gui/folderwatcher_win.h index d72665f0607d..6c4a261e0e05 100644 --- a/src/gui/folderwatcher_win.h +++ b/src/gui/folderwatcher_win.h @@ -15,8 +15,9 @@ #ifndef MIRALL_FOLDERWATCHER_WIN_H #define MIRALL_FOLDERWATCHER_WIN_H -#include +#include "common/utility.h" #include +#include #include namespace OCC { @@ -33,7 +34,7 @@ class WatcherThread : public QThread public: WatcherThread(const QString &path) : QThread() - , _path(path + (path.endsWith(QLatin1Char('/')) ? QString() : QStringLiteral("/"))) + , _path(Utility::trailingSlashPath(path)) , _directory(0) , _resultEvent(0) , _stopEvent(0) diff --git a/src/gui/owncloudsetupwizard.cpp b/src/gui/owncloudsetupwizard.cpp index 551fc487dc72..b0354b58e03b 100644 --- a/src/gui/owncloudsetupwizard.cpp +++ b/src/gui/owncloudsetupwizard.cpp @@ -20,19 +20,20 @@ #include #include -#include "wizard/owncloudwizardcommon.h" -#include "wizard/owncloudwizard.h" -#include "owncloudsetupwizard.h" -#include "configfile.h" -#include "folderman.h" #include "accessmanager.h" #include "account.h" -#include "networkjobs.h" -#include "sslerrordialog.h" #include "accountmanager.h" #include "clientproxy.h" +#include "common/utility.h" +#include "configfile.h" #include "filesystem.h" +#include "folderman.h" +#include "networkjobs.h" #include "owncloudgui.h" +#include "owncloudsetupwizard.h" +#include "sslerrordialog.h" +#include "wizard/owncloudwizard.h" +#include "wizard/owncloudwizardcommon.h" #include "creds/credentialsfactory.h" #include "creds/abstractcredentials.h" @@ -125,13 +126,7 @@ void OwncloudSetupWizard::startWizard() } // remember the local folder to compare later if it changed, but clean first - QString lf = QDir::fromNativeSeparators(localFolder); - if (!lf.endsWith(QLatin1Char('/'))) { - lf.append(QLatin1Char('/')); - } - - _initLocalFolder = lf; - + _initLocalFolder = Utility::trailingSlashPath(QDir::fromNativeSeparators(localFolder)); _ocWizard->setRemoteFolder(_remoteFolder); const auto isEnforcedServerSetup = diff --git a/src/gui/selectivesyncdialog.cpp b/src/gui/selectivesyncdialog.cpp index 4f4ca1ec1fc2..d83d1b9cdf7b 100644 --- a/src/gui/selectivesyncdialog.cpp +++ b/src/gui/selectivesyncdialog.cpp @@ -12,23 +12,23 @@ * for more details. */ #include "selectivesyncdialog.h" -#include "folder.h" #include "account.h" +#include "common/utility.h" +#include "configfile.h" +#include "folder.h" +#include "folderman.h" #include "networkjobs.h" #include "theme.h" -#include "folderman.h" -#include "configfile.h" #include -#include -#include -#include #include #include -#include +#include #include +#include +#include #include -#include #include +#include namespace OCC { @@ -203,10 +203,7 @@ void SelectiveSyncWidget::slotUpdateDirectories(QStringList list) auto *root = dynamic_cast(_folderTree->topLevelItem(0)); QUrl url = _account->davUrl(); - QString pathToRemove = url.path(); - if (!pathToRemove.endsWith('/')) { - pathToRemove.append('/'); - } + auto pathToRemove = Utility::trailingSlashPath(url.path()); pathToRemove.append(_folderPath); if (!_folderPath.isEmpty()) pathToRemove.append('/'); diff --git a/src/gui/socketapi/socketuploadjob.cpp b/src/gui/socketapi/socketuploadjob.cpp index b8ee787a1145..6e4ebc968909 100644 --- a/src/gui/socketapi/socketuploadjob.cpp +++ b/src/gui/socketapi/socketuploadjob.cpp @@ -13,6 +13,7 @@ */ #include "socketuploadjob.h" +#include "common/utility.h" #include "socketapi_p.h" #include "accountmanager.h" @@ -55,7 +56,7 @@ SocketUploadJob::SocketUploadJob(const QSharedPointer &job) SyncOptions opt; opt.fillFromEnvironmentVariables(); opt.verifyChunkSizes(); - _engine = new SyncEngine(account->account(), _localPath.endsWith(QLatin1Char('/')) ? _localPath : _localPath + QLatin1Char('/'), opt, _remotePath, _db); + _engine = new SyncEngine(account->account(), Utility::trailingSlashPath(_localPath), opt, _remotePath, _db); _engine->setParent(_db); connect(_engine, &OCC::SyncEngine::itemCompleted, this, [this](const OCC::SyncFileItemPtr item) { diff --git a/src/gui/wizard/webviewpage.cpp b/src/gui/wizard/webviewpage.cpp index 0b5ccd9ca59f..8e492ea27605 100644 --- a/src/gui/wizard/webviewpage.cpp +++ b/src/gui/wizard/webviewpage.cpp @@ -6,10 +6,11 @@ #include #include -#include "owncloudwizard.h" +#include "account.h" +#include "common/utility.h" #include "creds/webflowcredentials.h" +#include "owncloudwizard.h" #include "webview.h" -#include "account.h" namespace OCC { @@ -46,11 +47,7 @@ void WebViewPage::initializePage() { if (_ocWizard->registration()) { url = "https://nextcloud.com/register"; } else { - url = _ocWizard->ocUrl(); - if (!url.endsWith('/')) { - url += "/"; - } - url += "index.php/login/flow"; + url = Utility::trailingSlashPath(_ocWizard->ocUrl()) + "index.php/login/flow"; } qCInfo(lcWizardWebiewPage()) << "Url to auth at: " << url; _webView->setUrl(QUrl(url)); diff --git a/src/libsync/configfile.cpp b/src/libsync/configfile.cpp index 86e39d956172..5bbcc817d345 100644 --- a/src/libsync/configfile.cpp +++ b/src/libsync/configfile.cpp @@ -363,11 +363,8 @@ QString ConfigFile::configPath() const _confDir = newLocation; } } - QString dir = _confDir; - if (!dir.endsWith(QLatin1Char('/'))) - dir.append(QLatin1Char('/')); - return dir; + return Utility::trailingSlashPath(_confDir); } static const QLatin1String exclFile("sync-exclude.lst"); diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp index 9361930944fe..8d24b56f633a 100644 --- a/src/libsync/discovery.cpp +++ b/src/libsync/discovery.cpp @@ -437,7 +437,7 @@ void ProcessDirectoryJob::checkAndUpdateSelectiveSyncListsForE2eeFolders(const Q { bool ok = false; - const auto pathWithTrailingSpace = path.endsWith(QLatin1Char('/')) ? path : path + QLatin1Char('/'); + const auto pathWithTrailingSpace = Utility::trailingSlashPath(path); auto blackListSet = _discoveryData->_statedb->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, &ok).toSet(); blackListSet.insert(pathWithTrailingSpace); diff --git a/src/libsync/discoveryphase.cpp b/src/libsync/discoveryphase.cpp index e992159a9331..9034da4b094c 100644 --- a/src/libsync/discoveryphase.cpp +++ b/src/libsync/discoveryphase.cpp @@ -13,6 +13,7 @@ */ #include "discoveryphase.h" +#include "common/utility.h" #include "configfile.h" #include "discovery.h" #include "helpers.h" @@ -122,8 +123,7 @@ void DiscoveryPhase::checkSelectiveSyncNewFolder(const QString &path, } // it is not too big, put it in the white list (so we will not do more query for the children) and and do not block. - static const auto slash = QLatin1Char('/'); - const auto sanitisedPath = path.endsWith(slash) ? path : path + slash; + const auto sanitisedPath = Utility::trailingSlashPath(path); _selectiveSyncWhiteList.insert(std::upper_bound(_selectiveSyncWhiteList.begin(), _selectiveSyncWhiteList.end(), sanitisedPath), sanitisedPath); return callback(false); }); diff --git a/src/libsync/owncloudpropagator.h b/src/libsync/owncloudpropagator.h index f38291b1ed46..e837641a49bd 100644 --- a/src/libsync/owncloudpropagator.h +++ b/src/libsync/owncloudpropagator.h @@ -25,13 +25,14 @@ #include #include +#include "accountfwd.h" +#include "bandwidthmanager.h" +#include "common/syncjournaldb.h" +#include "common/utility.h" #include "csync.h" +#include "progressdispatcher.h" #include "syncfileitem.h" -#include "common/syncjournaldb.h" -#include "bandwidthmanager.h" -#include "accountfwd.h" #include "syncoptions.h" -#include "progressdispatcher.h" #include @@ -416,15 +417,13 @@ class OWNCLOUDSYNC_EXPORT OwncloudPropagator : public QObject bool _finishedEmited = false; // used to ensure that finished is only emitted once public: - OwncloudPropagator(AccountPtr account, const QString &localDir, - const QString &remoteFolder, SyncJournalDb *progressDb, - QSet &bulkUploadBlackList) + OwncloudPropagator(AccountPtr account, const QString &localDir, const QString &remoteFolder, SyncJournalDb *progressDb, QSet &bulkUploadBlackList) : _journal(progressDb) , _bandwidthManager(this) , _chunkSize(10 * 1000 * 1000) // 10 MB, overridden in setSyncOptions , _account(account) - , _localDir((localDir.endsWith(QChar('/'))) ? localDir : localDir + '/') - , _remoteFolder((remoteFolder.endsWith(QChar('/'))) ? remoteFolder : remoteFolder + '/') + , _localDir(Utility::trailingSlashPath(localDir)) + , _remoteFolder(Utility::trailingSlashPath(remoteFolder)) , _bulkUploadBlackList(bulkUploadBlackList) { qRegisterMetaType("PropagatorJob::AbortType"); diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index b4fadc45399d..52985fd4454a 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -617,12 +617,8 @@ void SyncEngine::startSync() _discoveryPhase->_excludes->reloadExcludeFiles(); } _discoveryPhase->_statedb = _journal; - _discoveryPhase->_localDir = _localPath; - if (!_discoveryPhase->_localDir.endsWith('/')) - _discoveryPhase->_localDir+='/'; - _discoveryPhase->_remoteFolder = _remotePath; - if (!_discoveryPhase->_remoteFolder.endsWith('/')) - _discoveryPhase->_remoteFolder+='/'; + _discoveryPhase->_localDir = Utility::trailingSlashPath(_localPath); + _discoveryPhase->_remoteFolder = Utility::trailingSlashPath(_remotePath); _discoveryPhase->_syncOptions = _syncOptions; _discoveryPhase->_shouldDiscoverLocaly = [this](const QString &path) { const auto result = shouldDiscoverLocally(path); diff --git a/src/libsync/theme.cpp b/src/libsync/theme.cpp index a8bcd127365b..fb589c29d601 100644 --- a/src/libsync/theme.cpp +++ b/src/libsync/theme.cpp @@ -408,12 +408,12 @@ QString Theme::helpUrl() const QString Theme::conflictHelpUrl() const { - auto baseUrl = helpUrl(); - if (baseUrl.isEmpty()) + const auto baseUrl = helpUrl(); + if (baseUrl.isEmpty()) { return QString(); - if (!baseUrl.endsWith('/')) - baseUrl.append('/'); - return baseUrl + QStringLiteral("conflicts.html"); + } + + return Utility::trailingSlashPath(baseUrl) + QStringLiteral("conflicts.html"); } QString Theme::overrideServerUrl() const diff --git a/test/syncenginetestutils.cpp b/test/syncenginetestutils.cpp index 15c7f5a48924..76617d7518ad 100644 --- a/test/syncenginetestutils.cpp +++ b/test/syncenginetestutils.cpp @@ -6,9 +6,10 @@ */ #include "syncenginetestutils.h" -#include "httplogger.h" #include "accessmanager.h" +#include "common/utility.h" #include "gui/sharepermissions.h" +#include "httplogger.h" #include #include @@ -285,11 +286,7 @@ QString FileInfo::path() const QString FileInfo::absolutePath() const { - if (parentPath.endsWith(QLatin1Char('/'))) { - return parentPath + name; - } else { - return parentPath + QLatin1Char('/') + name; - } + return OCC::Utility::trailingSlashPath(parentPath) + name; } void FileInfo::fixupParentPathRecursively() @@ -339,10 +336,7 @@ FakePropfindReply::FakePropfindReply(FileInfo &remoteRootFileInfo, QNetworkAcces auto writeFileResponse = [&](const FileInfo &fileInfo) { xml.writeStartElement(davUri, QStringLiteral("response")); - auto url = QString::fromUtf8(QUrl::toPercentEncoding(fileInfo.absolutePath(), "/")); - if (!url.endsWith(QChar('/'))) { - url.append(QChar('/')); - } + const auto url = OCC::Utility::trailingSlashPath(QString::fromUtf8(QUrl::toPercentEncoding(fileInfo.absolutePath(), "/"))); const auto href = OCC::Utility::concatUrlPath(prefix, url).path(); xml.writeTextElement(davUri, QStringLiteral("href"), href); xml.writeStartElement(davUri, QStringLiteral("propstat")); @@ -1178,9 +1172,7 @@ FileInfo FakeFolder::currentLocalState() QString FakeFolder::localPath() const { // SyncEngine wants a trailing slash - if (_tempDir.path().endsWith(QLatin1Char('/'))) - return _tempDir.path(); - return _tempDir.path() + QLatin1Char('/'); + return OCC::Utility::trailingSlashPath(_tempDir.path()); } void FakeFolder::scheduleSync() From 4cdf525df2dccc4a82e50e552037e723067f940f Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 18 Jul 2023 17:17:51 +0800 Subject: [PATCH 43/57] Report size for directories in syncenginetestutils propfind response Signed-off-by: Claudio Cambra --- test/syncenginetestutils.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/syncenginetestutils.cpp b/test/syncenginetestutils.cpp index 76617d7518ad..82aa701664d1 100644 --- a/test/syncenginetestutils.cpp +++ b/test/syncenginetestutils.cpp @@ -346,6 +346,12 @@ FakePropfindReply::FakePropfindReply(FileInfo &remoteRootFileInfo, QNetworkAcces xml.writeStartElement(davUri, QStringLiteral("resourcetype")); xml.writeEmptyElement(davUri, QStringLiteral("collection")); xml.writeEndElement(); // resourcetype + + auto totalSize = 0; + for (const auto &child : fileInfo.children.values()) { + totalSize += child.size; + } + xml.writeTextElement(ocUri, QStringLiteral("size"), QString::number(totalSize)); } else xml.writeEmptyElement(davUri, QStringLiteral("resourcetype")); From 0b13be663b9d4e91ce97e3806cf6c42dbd96c26d Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 18 Jul 2023 17:19:39 +0800 Subject: [PATCH 44/57] Add test for correct reporting of existing folder overcoming size limit Signed-off-by: Claudio Cambra --- test/testsyncengine.cpp | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/test/testsyncengine.cpp b/test/testsyncengine.cpp index ec5aebeb2489..9516a05570dc 100644 --- a/test/testsyncengine.cpp +++ b/test/testsyncengine.cpp @@ -7,9 +7,10 @@ #include "syncenginetestutils.h" -#include "syncengine.h" -#include "propagatorjobs.h" #include "caseclashconflictsolver.h" +#include "configfile.h" +#include "propagatorjobs.h" +#include "syncengine.h" #include @@ -1663,6 +1664,35 @@ private slots: fakeFolder.remoteModifier().remove(testUpperCaseFile); QVERIFY(fakeFolder.syncOnce()); } + + void testExistingFolderBecameBig() + { + constexpr auto testFolder = "folder"; + constexpr auto testSmallFile = "folder/small_file.txt"; + constexpr auto testLargeFile = "folder/large_file.txt"; + + QTemporaryDir dir; + ConfigFile::setConfDir(dir.path()); // we don't want to pollute the user's config file + auto config = ConfigFile(); + config.setNotifyExistingFoldersOverLimit(true); + + FakeFolder fakeFolder{FileInfo{}}; + QSignalSpy spy(&fakeFolder.syncEngine(), &SyncEngine::existingFolderNowBig); + + auto syncOptions = fakeFolder.syncEngine().syncOptions(); + syncOptions._newBigFolderSizeLimit = 128; // 128 bytes + fakeFolder.syncEngine().setSyncOptions(syncOptions); + + fakeFolder.remoteModifier().mkdir(testFolder); + fakeFolder.remoteModifier().insert(testSmallFile, 64); + fakeFolder.syncEngine().setLocalDiscoveryOptions(OCC::LocalDiscoveryStyle::DatabaseAndFilesystem); + QVERIFY(fakeFolder.syncOnce()); + QCOMPARE(spy.count(), 0); + + fakeFolder.remoteModifier().insert(testLargeFile, 256); + QVERIFY(fakeFolder.syncOnce()); + QCOMPARE(spy.count(), 1); + } }; QTEST_GUILESS_MAIN(TestSyncEngine) From 1342f81cab4cd8010ccdbce18d555592b8bc8979 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 24 Jul 2023 15:46:39 +0800 Subject: [PATCH 45/57] Add config option to stop synchronising existing folders when they grow beyond size Signed-off-by: Claudio Cambra --- src/gui/folder.cpp | 35 ++++++++++++++++++++++++----------- src/libsync/configfile.cpp | 13 +++++++++++++ src/libsync/configfile.h | 2 ++ 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index f8a60416f1bc..5b5563d5794b 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -1257,6 +1257,7 @@ void Folder::slotExistingFolderNowBig(const QString &folderPath) { const auto trailSlashFolderPath = Utility::trailingSlashPath(folderPath); const auto journal = journalDb(); + const auto stopSyncing = ConfigFile().stopSyncingExistingFoldersOverLimit(); // Add the entry to the whitelist if it is neither in the blacklist or whitelist already bool ok1 = false; @@ -1264,8 +1265,13 @@ void Folder::slotExistingFolderNowBig(const QString &folderPath) auto blacklist = journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, &ok1); auto whitelist = journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncWhiteList, &ok2); if (ok1 && ok2 && !blacklist.contains(trailSlashFolderPath) && !whitelist.contains(trailSlashFolderPath)) { - whitelist.append(trailSlashFolderPath); - journal->setSelectiveSyncList(SyncJournalDb::SelectiveSyncWhiteList, whitelist); + if (stopSyncing) { + blacklist.append(trailSlashFolderPath); + journal->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, blacklist); + } else { + whitelist.append(trailSlashFolderPath); + journal->setSelectiveSyncList(SyncJournalDb::SelectiveSyncWhiteList, whitelist); + } } auto undecidedListQueryOk = false; @@ -1277,21 +1283,28 @@ void Folder::slotExistingFolderNowBig(const QString &folderPath) emit newBigFolderDiscovered(trailSlashFolderPath); } - const auto message = tr("A folder has surpassed the set folder size limit of %1MB: %2.\n" - "Please go into the settings and disable it if you wish to stop synchronising it.") - .arg(QString::number(ConfigFile().newBigFolderSizeLimit().second), folderPath); + const auto messageInstruction = + stopSyncing ? "Synchronisation of this folder has been disabled." : "Synchronisation of this folder can be disabled in the settings window."; + const auto message = tr("A folder has surpassed the set folder size limit of %1MB: %2.\n%3") + .arg(QString::number(ConfigFile().newBigFolderSizeLimit().second), folderPath, messageInstruction); Logger::instance()->postGuiLog(Theme::instance()->appNameGUI(), message); - auto blacklistActivityLink = ActivityLink(); - blacklistActivityLink._label = tr("Stop syncing"); - blacklistActivityLink._primary = true; - blacklistActivityLink._verb = "BLACKLIST_FOLDER"; - auto whitelistActivityLink = ActivityLink(); whitelistActivityLink._label = tr("Keep syncing"); whitelistActivityLink._primary = false; whitelistActivityLink._verb = "WHITELIST_FOLDER"; + QVector activityLinks = {whitelistActivityLink}; + + if (!stopSyncing) { + auto blacklistActivityLink = ActivityLink(); + blacklistActivityLink._label = tr("Stop syncing"); + blacklistActivityLink._primary = true; + blacklistActivityLink._verb = "BLACKLIST_FOLDER"; + + activityLinks.append(blacklistActivityLink); + } + auto existingFolderNowBigActivity = Activity(); existingFolderNowBigActivity._type = Activity::NotificationType; existingFolderNowBigActivity._dateTime = QDateTime::fromString(QDateTime::currentDateTime().toString(), Qt::ISODate); @@ -1301,7 +1314,7 @@ void Folder::slotExistingFolderNowBig(const QString &folderPath) existingFolderNowBigActivity._accName = _accountState->account()->displayName(); existingFolderNowBigActivity._folder = alias(); existingFolderNowBigActivity._file = cleanPath() + '/' + trailSlashFolderPath; - existingFolderNowBigActivity._links = {blacklistActivityLink, whitelistActivityLink}; + existingFolderNowBigActivity._links = activityLinks; existingFolderNowBigActivity._id = qHash(existingFolderNowBigActivity._file); const auto user = UserModel::instance()->findUserForAccount(_accountState.data()); diff --git a/src/libsync/configfile.cpp b/src/libsync/configfile.cpp index 5bbcc817d345..5931929d559d 100644 --- a/src/libsync/configfile.cpp +++ b/src/libsync/configfile.cpp @@ -100,6 +100,7 @@ static constexpr char downloadLimitC[] = "BWLimit/downloadLimit"; static constexpr char newBigFolderSizeLimitC[] = "newBigFolderSizeLimit"; static constexpr char useNewBigFolderSizeLimitC[] = "useNewBigFolderSizeLimit"; static constexpr char notifyExistingFoldersOverLimitC[] = "notifyExistingFoldersOverLimit"; +static constexpr char stopSyncingExistingFoldersOverLimitC[] = "stopSyncingExistingFoldersOverLimit"; static constexpr char confirmExternalStorageC[] = "confirmExternalStorage"; static constexpr char moveToTrashC[] = "moveToTrash"; @@ -968,6 +969,18 @@ void ConfigFile::setNotifyExistingFoldersOverLimit(const bool notify) setValue(notifyExistingFoldersOverLimitC, notify); } +bool ConfigFile::stopSyncingExistingFoldersOverLimit() const +{ + const auto notifyExistingBigEnabled = notifyExistingFoldersOverLimit(); + const auto fallback = getValue(stopSyncingExistingFoldersOverLimitC, {}, notifyExistingBigEnabled); + return getPolicySetting(QString(stopSyncingExistingFoldersOverLimitC), fallback).toBool(); +} + +void ConfigFile::setStopSyncingExistingFoldersOverLimit(const bool stopSyncing) +{ + setValue(stopSyncingExistingFoldersOverLimitC, stopSyncing); +} + void ConfigFile::setConfirmExternalStorage(bool isChecked) { setValue(confirmExternalStorageC, isChecked); diff --git a/src/libsync/configfile.h b/src/libsync/configfile.h index a947fec2bd6e..6906c21b7600 100644 --- a/src/libsync/configfile.h +++ b/src/libsync/configfile.h @@ -143,6 +143,8 @@ class OWNCLOUDSYNC_EXPORT ConfigFile void setNewBigFolderSizeLimit(bool isChecked, qint64 mbytes); [[nodiscard]] bool notifyExistingFoldersOverLimit() const; void setNotifyExistingFoldersOverLimit(const bool notify); + [[nodiscard]] bool stopSyncingExistingFoldersOverLimit() const; + void setStopSyncingExistingFoldersOverLimit(const bool stopSyncing); [[nodiscard]] bool useNewBigFolderSizeLimit() const; [[nodiscard]] bool confirmExternalStorage() const; void setConfirmExternalStorage(bool); From bd7ccf5738808d3187f240f33502e1e44b881906 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 24 Jul 2023 15:50:03 +0800 Subject: [PATCH 46/57] Add UI element in general settings to stop syncing existing folders that have now overcome limit Signed-off-by: Claudio Cambra --- src/gui/generalsettings.cpp | 13 ++++++++++--- src/gui/generalsettings.ui | 39 ++++++++++++++++++++++++++----------- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/src/gui/generalsettings.cpp b/src/gui/generalsettings.cpp index 82931e5fc708..394d13c0d977 100644 --- a/src/gui/generalsettings.cpp +++ b/src/gui/generalsettings.cpp @@ -187,6 +187,7 @@ GeneralSettings::GeneralSettings(QWidget *parent) connect(_ui->newFolderLimitCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::saveMiscSettings); connect(_ui->newFolderLimitSpinBox, static_cast(&QSpinBox::valueChanged), this, &GeneralSettings::saveMiscSettings); connect(_ui->existingFolderLimitCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::saveMiscSettings); + connect(_ui->stopExistingFolderNowBigSyncCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::saveMiscSettings); connect(_ui->newExternalStorage, &QAbstractButton::toggled, this, &GeneralSettings::saveMiscSettings); connect(_ui->moveFilesToTrashCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::saveMiscSettings); @@ -262,8 +263,10 @@ void GeneralSettings::loadMiscSettings() auto newFolderLimit = cfgFile.newBigFolderSizeLimit(); _ui->newFolderLimitCheckBox->setChecked(newFolderLimit.first); _ui->newFolderLimitSpinBox->setValue(newFolderLimit.second); - _ui->existingFolderLimitCheckBox->setEnabled(newFolderLimit.first); - _ui->existingFolderLimitCheckBox->setChecked(newFolderLimit.first && cfgFile.notifyExistingFoldersOverLimit()); + _ui->existingFolderLimitCheckBox->setEnabled(_ui->newFolderLimitCheckBox->isChecked()); + _ui->existingFolderLimitCheckBox->setChecked(_ui->newFolderLimitCheckBox->isChecked() && cfgFile.notifyExistingFoldersOverLimit()); + _ui->stopExistingFolderNowBigSyncCheckBox->setEnabled(_ui->existingFolderLimitCheckBox->isChecked()); + _ui->stopExistingFolderNowBigSyncCheckBox->setChecked(_ui->existingFolderLimitCheckBox->isChecked() && cfgFile.stopSyncingExistingFoldersOverLimit()); _ui->newExternalStorage->setChecked(cfgFile.confirmExternalStorage()); _ui->monoIconsCheckBox->setChecked(cfgFile.monoIcons()); } @@ -433,6 +436,8 @@ void GeneralSettings::saveMiscSettings() const auto useMonoIcons = _ui->monoIconsCheckBox->isChecked(); const auto newFolderLimitEnabled = _ui->newFolderLimitCheckBox->isChecked(); + const auto existingFolderLimitEnabled = newFolderLimitEnabled && _ui->existingFolderLimitCheckBox->isChecked(); + const auto stopSyncingExistingFoldersOverLimit = existingFolderLimitEnabled && _ui->stopExistingFolderNowBigSyncCheckBox->isChecked(); Theme::instance()->setSystrayUseMonoIcons(useMonoIcons); cfgFile.setMonoIcons(useMonoIcons); @@ -440,9 +445,11 @@ void GeneralSettings::saveMiscSettings() cfgFile.setMoveToTrash(_ui->moveFilesToTrashCheckBox->isChecked()); cfgFile.setNewBigFolderSizeLimit(newFolderLimitEnabled, _ui->newFolderLimitSpinBox->value()); cfgFile.setConfirmExternalStorage(_ui->newExternalStorage->isChecked()); - cfgFile.setNotifyExistingFoldersOverLimit(_ui->existingFolderLimitCheckBox->isChecked()); + cfgFile.setNotifyExistingFoldersOverLimit(existingFolderLimitEnabled); + cfgFile.setStopSyncingExistingFoldersOverLimit(stopSyncingExistingFoldersOverLimit); _ui->existingFolderLimitCheckBox->setEnabled(newFolderLimitEnabled); + _ui->stopExistingFolderNowBigSyncCheckBox->setEnabled(existingFolderLimitEnabled); } void GeneralSettings::slotToggleLaunchOnStartup(bool enable) diff --git a/src/gui/generalsettings.ui b/src/gui/generalsettings.ui index ee037123133c..dad84c66c7ea 100644 --- a/src/gui/generalsettings.ui +++ b/src/gui/generalsettings.ui @@ -6,8 +6,8 @@ 0 0 - 556 - 613 + 581 + 663 @@ -223,6 +223,9 @@ + + 0 + @@ -246,31 +249,45 @@ - + + + MB + + + + + + + + + Qt::Horizontal + + QSizePolicy::Fixed + - 40 + 20 20 - + - MB + Notify when synchronised folders grow larger than specified limit - + - + Qt::Horizontal @@ -279,16 +296,16 @@ - 20 + 40 20 - + - Notify when synchronised folders grow larger than specified limit + Automatically disable synchronisation of folders that overcome limit From 2324a60c761c7e97d796cc5ce71088620338601f Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 24 Jul 2023 16:06:00 +0800 Subject: [PATCH 47/57] Stop current sync when existing folder becomes big and we have enabled disabling sync of folders that become big Signed-off-by: Claudio Cambra --- src/gui/folder.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index 5b5563d5794b..3c0e5fd6e42d 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -1268,6 +1268,8 @@ void Folder::slotExistingFolderNowBig(const QString &folderPath) if (stopSyncing) { blacklist.append(trailSlashFolderPath); journal->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, blacklist); + slotTerminateSync(); + scheduleThisFolderSoon(); } else { whitelist.append(trailSlashFolderPath); journal->setSelectiveSyncList(SyncJournalDb::SelectiveSyncWhiteList, whitelist); From 449bac837bd0b45d0459409c5ee9f0e5173feda9 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 24 Jul 2023 17:05:44 +0800 Subject: [PATCH 48/57] Do not always re-notify user on existing folder now big if this folder already black or white listed Signed-off-by: Claudio Cambra --- src/gui/folder.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index 3c0e5fd6e42d..1453cfbfacec 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -1264,15 +1264,24 @@ void Folder::slotExistingFolderNowBig(const QString &folderPath) bool ok2 = false; auto blacklist = journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, &ok1); auto whitelist = journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncWhiteList, &ok2); - if (ok1 && ok2 && !blacklist.contains(trailSlashFolderPath) && !whitelist.contains(trailSlashFolderPath)) { + + + const auto inDecidedLists = blacklist.contains(trailSlashFolderPath) || whitelist.contains(trailSlashFolderPath); + if (inDecidedLists) { + return; + } + + auto relevantList = stopSyncing ? blacklist : whitelist; + const auto relevantListType = stopSyncing ? SyncJournalDb::SelectiveSyncBlackList : SyncJournalDb::SelectiveSyncWhiteList; + + if (ok1 && ok2 && !inDecidedLists) { + relevantList.append(trailSlashFolderPath); + journal->setSelectiveSyncList(relevantListType, relevantList); + if (stopSyncing) { - blacklist.append(trailSlashFolderPath); - journal->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, blacklist); + // Abort current down sync and start again slotTerminateSync(); scheduleThisFolderSoon(); - } else { - whitelist.append(trailSlashFolderPath); - journal->setSelectiveSyncList(SyncJournalDb::SelectiveSyncWhiteList, whitelist); } } From af45017e9484ca63b3dc3da5479f5d595c8a61de Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 24 Jul 2023 17:11:47 +0800 Subject: [PATCH 49/57] Do not clear the whitelist on sync success Signed-off-by: Claudio Cambra --- src/gui/folder.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index 1453cfbfacec..02caa5322e81 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -1130,11 +1130,6 @@ void Folder::slotSyncFinished(bool success) qCInfo(lcFolder) << "the last" << _consecutiveFailingSyncs << "syncs failed"; } - if (_syncResult.status() == SyncResult::Success && success) { - // Clear the white list as all the folders that should be on that list are sync-ed - journalDb()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncWhiteList, QStringList()); - } - if ((_syncResult.status() == SyncResult::Success || _syncResult.status() == SyncResult::Problem) && success) { @@ -1265,7 +1260,6 @@ void Folder::slotExistingFolderNowBig(const QString &folderPath) auto blacklist = journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, &ok1); auto whitelist = journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncWhiteList, &ok2); - const auto inDecidedLists = blacklist.contains(trailSlashFolderPath) || whitelist.contains(trailSlashFolderPath); if (inDecidedLists) { return; From 9b5391bdc185492c2844deef928f44955f361f46 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Thu, 3 Aug 2023 14:59:58 +0800 Subject: [PATCH 50/57] Ensure generated activity buttons are within count set by maxActionButtons Signed-off-by: Claudio Cambra --- src/gui/tray/activitylistmodel.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gui/tray/activitylistmodel.cpp b/src/gui/tray/activitylistmodel.cpp index 46aa5ff1fd6b..4068813dd608 100644 --- a/src/gui/tray/activitylistmodel.cpp +++ b/src/gui/tray/activitylistmodel.cpp @@ -871,7 +871,9 @@ QVariantList ActivityListModel::convertLinksToActionButtons(const Activity &acti { QVariantList customList; - for (const auto &activityLink : activity._links) { + for (int i = 0; i < activity._links.size() && static_cast(i) <= maxActionButtons(); ++i) { + const auto activityLink = activity._links[i]; + // Use the isDismissable model role to present custom dismiss button if needed // Also don't show "View chat" for talk activities, default action will open chat anyway if (activityLink._verb == "DELETE" || (activityLink._verb == "WEB" && activity._objectType == "chat")) { From e987fe88c5af3ca0ae25f6dc74666f2b91de336d Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Thu, 3 Aug 2023 15:00:20 +0800 Subject: [PATCH 51/57] Fix generation of context menu activity links Signed-off-by: Claudio Cambra --- src/gui/tray/activitylistmodel.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/gui/tray/activitylistmodel.cpp b/src/gui/tray/activitylistmodel.cpp index 4068813dd608..76fb6f8dbe64 100644 --- a/src/gui/tray/activitylistmodel.cpp +++ b/src/gui/tray/activitylistmodel.cpp @@ -904,16 +904,16 @@ QVariant ActivityListModel::convertLinkToActionButton(const OCC::ActivityLink &a QVariantList ActivityListModel::convertLinksToMenuEntries(const Activity &activity) { + if (static_cast(activity._links.size()) <= maxActionButtons()) { + return {}; + } + QVariantList customList; - if (static_cast(activity._links.size()) > maxActionButtons()) { - for (int i = 0; i < activity._links.size(); ++i) { - const auto &activityLink = activity._links[i]; - if (!activityLink._primary) { - customList << QVariantMap{ - {QStringLiteral("actionIndex"), i}, {QStringLiteral("label"), activityLink._label}}; - } - } + for (int i = maxActionButtons(); i < activity._links.size(); ++i) { + const auto activityLinkLabel = activity._links[i]._label; + const auto menuEntry = QVariantMap{{"actionIndex", i}, {"label", activityLinkLabel}}; + customList << menuEntry; } return customList; From 5db79261f6ebd21f4ec10fc2282c78b27fcc6e6c Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Thu, 3 Aug 2023 15:00:55 +0800 Subject: [PATCH 52/57] Fix breakages in testactivitylistmodel caused by recent activitylistmodel changes Signed-off-by: Claudio Cambra --- test/testactivitylistmodel.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/test/testactivitylistmodel.cpp b/test/testactivitylistmodel.cpp index 7e2a734e7532..f5a69b3ea8c5 100644 --- a/test/testactivitylistmodel.cpp +++ b/test/testactivitylistmodel.cpp @@ -268,8 +268,7 @@ private slots: const auto actionsLinks = index.data(OCC::ActivityListModel::ActionsLinksRole).toList(); if (!actionsLinks.isEmpty()) { - const auto actionsLinksContextMenu = - index.data(OCC::ActivityListModel::ActionsLinksContextMenuRole).toList(); + const auto actionsLinksContextMenu = index.data(OCC::ActivityListModel::ActionsLinksContextMenuRole).toList(); // context menu must be shorter than total action links QVERIFY(actionsLinksContextMenu.isEmpty() || actionsLinksContextMenu.size() < actionsLinks.size()); @@ -281,8 +280,7 @@ private slots: const auto objectType = index.data(OCC::ActivityListModel::ObjectTypeRole).toString(); - const auto actionButtonsLinks = - index.data(OCC::ActivityListModel::ActionsLinksForActionButtonsRole).toList(); + const auto actionButtonsLinks = index.data(OCC::ActivityListModel::ActionsLinksForActionButtonsRole).toList(); // Login attempt notification if (objectType == QStringLiteral("2fa_id")) { @@ -323,15 +321,12 @@ private slots: QVERIFY(actionButtonsLinks[0].value()._label == QObject::tr("Reply")); if (static_cast(actionsLinks.size()) > OCC::ActivityListModel::maxActionButtons()) { - // in case total actions is longer than ActivityListModel::maxActionButtons, only one button must be present in a list of action buttons - QVERIFY(actionButtonsLinks.size() == 1); - const auto actionButtonsAndContextMenuEntries = actionButtonsLinks + actionsLinksContextMenu; + QCOMPARE(actionButtonsLinks.size(), OCC::ActivityListModel::maxActionButtons()); // in case total actions is longer than ActivityListModel::maxActionButtons, then a sum of action buttons and action menu entries must be equal to a total of action links - QVERIFY(actionButtonsLinks.size() + actionsLinksContextMenu.size() == actionsLinks.size()); + QCOMPARE(actionButtonsLinks.size() + actionsLinksContextMenu.size(), actionsLinks.size()); } } else if ((objectType == QStringLiteral("call"))) { - QVERIFY( - actionButtonsLinks[0].value()._label == QStringLiteral("Call back")); + QVERIFY(actionButtonsLinks[0].value()._label == QStringLiteral("Call back")); } } } From e1f342c320d6dc34866171eccd9f16c023a932a7 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Thu, 3 Aug 2023 15:13:51 +0800 Subject: [PATCH 53/57] Declare whitelist and blacklist activity verbs as static constexprs in activitydata, refer to this in usage Signed-off-by: Claudio Cambra --- src/gui/folder.cpp | 4 ++-- src/gui/tray/activitydata.h | 3 +++ src/gui/tray/activitylistmodel.cpp | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index 02caa5322e81..e43bcea068c7 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -1297,7 +1297,7 @@ void Folder::slotExistingFolderNowBig(const QString &folderPath) auto whitelistActivityLink = ActivityLink(); whitelistActivityLink._label = tr("Keep syncing"); whitelistActivityLink._primary = false; - whitelistActivityLink._verb = "WHITELIST_FOLDER"; + whitelistActivityLink._verb = ActivityLink::WhitelistFolderVerb; QVector activityLinks = {whitelistActivityLink}; @@ -1305,7 +1305,7 @@ void Folder::slotExistingFolderNowBig(const QString &folderPath) auto blacklistActivityLink = ActivityLink(); blacklistActivityLink._label = tr("Stop syncing"); blacklistActivityLink._primary = true; - blacklistActivityLink._verb = "BLACKLIST_FOLDER"; + blacklistActivityLink._verb = ActivityLink::BlacklistFolderVerb; activityLinks.append(blacklistActivityLink); } diff --git a/src/gui/tray/activitydata.h b/src/gui/tray/activitydata.h index 29df3c878cc3..e613ad5bf5dd 100644 --- a/src/gui/tray/activitydata.h +++ b/src/gui/tray/activitydata.h @@ -45,6 +45,9 @@ class ActivityLink public: static ActivityLink createFomJsonObject(const QJsonObject &obj); + static constexpr auto WhitelistFolderVerb = "WHITELIST_FOLDER"; + static constexpr auto BlacklistFolderVerb = "BLACKLIST_FOLDER"; + public: QString _imageSource; QString _imageSourceHovered; diff --git a/src/gui/tray/activitylistmodel.cpp b/src/gui/tray/activitylistmodel.cpp index 76fb6f8dbe64..b43cb45c016f 100644 --- a/src/gui/tray/activitylistmodel.cpp +++ b/src/gui/tray/activitylistmodel.cpp @@ -836,11 +836,11 @@ void ActivityListModel::slotTriggerAction(const int activityIndex, const int act (activity._syncFileItemStatus == SyncFileItem::Conflict || activity._syncFileItemStatus == SyncFileItem::FileNameClash)) { slotTriggerDefaultAction(activityIndex); return; - } else if (action._verb == "WHITELIST_FOLDER" && !activity._file.isEmpty()) { // _folder == folder alias/name, _file == folder/file path + } else if (action._verb == ActivityLink::WhitelistFolderVerb && !activity._file.isEmpty()) { // _folder == folder alias/name, _file == folder/file path FolderMan::instance()->whitelistFolderPath(activity._file); removeActivityFromActivityList(activity); return; - } else if (action._verb == "BLACKLIST_FOLDER" && !activity._file.isEmpty()) { + } else if (action._verb == ActivityLink::BlacklistFolderVerb && !activity._file.isEmpty()) { FolderMan::instance()->blacklistFolderPath(activity._file); removeActivityFromActivityList(activity); return; From cbad4347bd51dd58fb26adb4ef9c0c458840b727 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Thu, 3 Aug 2023 15:19:22 +0800 Subject: [PATCH 54/57] Extract notifying of existing folder now big to separate static method Signed-off-by: Claudio Cambra --- src/gui/folder.cpp | 16 +++++++++++----- src/gui/folder.h | 2 ++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index e43bcea068c7..ea483ce73fe8 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -1288,11 +1288,7 @@ void Folder::slotExistingFolderNowBig(const QString &folderPath) emit newBigFolderDiscovered(trailSlashFolderPath); } - const auto messageInstruction = - stopSyncing ? "Synchronisation of this folder has been disabled." : "Synchronisation of this folder can be disabled in the settings window."; - const auto message = tr("A folder has surpassed the set folder size limit of %1MB: %2.\n%3") - .arg(QString::number(ConfigFile().newBigFolderSizeLimit().second), folderPath, messageInstruction); - Logger::instance()->postGuiLog(Theme::instance()->appNameGUI(), message); + postExistingFolderNowBigNotification(folderPath); auto whitelistActivityLink = ActivityLink(); whitelistActivityLink._label = tr("Keep syncing"); @@ -1327,6 +1323,16 @@ void Folder::slotExistingFolderNowBig(const QString &folderPath) } } +void Folder::postExistingFolderNowBigNotification(const QString &folderPath) +{ + const auto stopSyncing = ConfigFile().stopSyncingExistingFoldersOverLimit(); + const auto messageInstruction = + stopSyncing ? "Synchronisation of this folder has been disabled." : "Synchronisation of this folder can be disabled in the settings window."; + const auto message = tr("A folder has surpassed the set folder size limit of %1MB: %2.\n%3") + .arg(QString::number(ConfigFile().newBigFolderSizeLimit().second), folderPath, messageInstruction); + Logger::instance()->postGuiLog(Theme::instance()->appNameGUI(), message); +} + void Folder::slotLogPropagationStart() { _fileLog->logLap("Propagation starts"); diff --git a/src/gui/folder.h b/src/gui/folder.h index 821f90716b8d..77550e48903d 100644 --- a/src/gui/folder.h +++ b/src/gui/folder.h @@ -474,6 +474,8 @@ private slots: void appendPathToSelectiveSyncList(const QString &path, const SyncJournalDb::SelectiveSyncListType listType); void removePathFromSelectiveSyncList(const QString &path, const SyncJournalDb::SelectiveSyncListType listType); + static void postExistingFolderNowBigNotification(const QString &folderPath); + AccountStatePtr _accountState; FolderDefinition _definition; QString _canonicalLocalPath; // As returned with QFileInfo:canonicalFilePath. Always ends with "/" From a7fcb846ab98318f061b65227dc85fdefd87e1d0 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Thu, 3 Aug 2023 15:25:15 +0800 Subject: [PATCH 55/57] Extract folder now big activity generation into separate method Signed-off-by: Claudio Cambra --- src/gui/folder.cpp | 69 +++++++++++++++++++++++++--------------------- src/gui/folder.h | 1 + 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index ea483ce73fe8..0511cba81a10 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -1289,37 +1289,7 @@ void Folder::slotExistingFolderNowBig(const QString &folderPath) } postExistingFolderNowBigNotification(folderPath); - - auto whitelistActivityLink = ActivityLink(); - whitelistActivityLink._label = tr("Keep syncing"); - whitelistActivityLink._primary = false; - whitelistActivityLink._verb = ActivityLink::WhitelistFolderVerb; - - QVector activityLinks = {whitelistActivityLink}; - - if (!stopSyncing) { - auto blacklistActivityLink = ActivityLink(); - blacklistActivityLink._label = tr("Stop syncing"); - blacklistActivityLink._primary = true; - blacklistActivityLink._verb = ActivityLink::BlacklistFolderVerb; - - activityLinks.append(blacklistActivityLink); - } - - auto existingFolderNowBigActivity = Activity(); - existingFolderNowBigActivity._type = Activity::NotificationType; - existingFolderNowBigActivity._dateTime = QDateTime::fromString(QDateTime::currentDateTime().toString(), Qt::ISODate); - existingFolderNowBigActivity._subject = - tr("The folder %1 has surpassed the set folder size limit of %2MB.").arg(folderPath, QString::number(ConfigFile().newBigFolderSizeLimit().second)); - existingFolderNowBigActivity._message = tr("Would you like to stop syncing this folder?"); - existingFolderNowBigActivity._accName = _accountState->account()->displayName(); - existingFolderNowBigActivity._folder = alias(); - existingFolderNowBigActivity._file = cleanPath() + '/' + trailSlashFolderPath; - existingFolderNowBigActivity._links = activityLinks; - existingFolderNowBigActivity._id = qHash(existingFolderNowBigActivity._file); - - const auto user = UserModel::instance()->findUserForAccount(_accountState.data()); - user->slotAddNotification(this, existingFolderNowBigActivity); + postExistingFolderNowBigActivity(folderPath); } } @@ -1333,6 +1303,43 @@ void Folder::postExistingFolderNowBigNotification(const QString &folderPath) Logger::instance()->postGuiLog(Theme::instance()->appNameGUI(), message); } +void Folder::postExistingFolderNowBigActivity(const QString &folderPath) const +{ + const auto stopSyncing = ConfigFile().stopSyncingExistingFoldersOverLimit(); + const auto trailSlashFolderPath = Utility::trailingSlashPath(folderPath); + + auto whitelistActivityLink = ActivityLink(); + whitelistActivityLink._label = tr("Keep syncing"); + whitelistActivityLink._primary = false; + whitelistActivityLink._verb = ActivityLink::WhitelistFolderVerb; + + QVector activityLinks = {whitelistActivityLink}; + + if (!stopSyncing) { + auto blacklistActivityLink = ActivityLink(); + blacklistActivityLink._label = tr("Stop syncing"); + blacklistActivityLink._primary = true; + blacklistActivityLink._verb = ActivityLink::BlacklistFolderVerb; + + activityLinks.append(blacklistActivityLink); + } + + auto existingFolderNowBigActivity = Activity(); + existingFolderNowBigActivity._type = Activity::NotificationType; + existingFolderNowBigActivity._dateTime = QDateTime::fromString(QDateTime::currentDateTime().toString(), Qt::ISODate); + existingFolderNowBigActivity._subject = + tr("The folder %1 has surpassed the set folder size limit of %2MB.").arg(folderPath, QString::number(ConfigFile().newBigFolderSizeLimit().second)); + existingFolderNowBigActivity._message = tr("Would you like to stop syncing this folder?"); + existingFolderNowBigActivity._accName = _accountState->account()->displayName(); + existingFolderNowBigActivity._folder = alias(); + existingFolderNowBigActivity._file = cleanPath() + '/' + trailSlashFolderPath; + existingFolderNowBigActivity._links = activityLinks; + existingFolderNowBigActivity._id = qHash(existingFolderNowBigActivity._file); + + const auto user = UserModel::instance()->findUserForAccount(_accountState.data()); + user->slotAddNotification(this, existingFolderNowBigActivity); +} + void Folder::slotLogPropagationStart() { _fileLog->logLap("Propagation starts"); diff --git a/src/gui/folder.h b/src/gui/folder.h index 77550e48903d..13f493102ee9 100644 --- a/src/gui/folder.h +++ b/src/gui/folder.h @@ -475,6 +475,7 @@ private slots: void removePathFromSelectiveSyncList(const QString &path, const SyncJournalDb::SelectiveSyncListType listType); static void postExistingFolderNowBigNotification(const QString &folderPath); + void postExistingFolderNowBigActivity(const QString &folderPath) const; AccountStatePtr _accountState; FolderDefinition _definition; From 6d5662152068fee1b7f4379c5e8b2a0e4f7df9fc Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Thu, 3 Aug 2023 15:34:39 +0800 Subject: [PATCH 56/57] Deduplicate whitelistFolderPath and blacklistFolderPath Signed-off-by: Claudio Cambra --- src/gui/folderman.cpp | 28 ++++++++++++++++++---------- src/gui/folderman.h | 2 ++ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/gui/folderman.cpp b/src/gui/folderman.cpp index efbf46d906f5..f41a3142480b 100644 --- a/src/gui/folderman.cpp +++ b/src/gui/folderman.cpp @@ -1259,7 +1259,7 @@ Folder *FolderMan::folderForPath(const QString &path) return it != folders.cend() ? *it : nullptr; } -void FolderMan::whitelistFolderPath(const QString &path) +void FolderMan::addFolderToSelectiveSyncList(const QString &path, const SyncJournalDb::SelectiveSyncListType list) { const auto folder = folderForPath(path); if (!folder) { @@ -1268,19 +1268,27 @@ void FolderMan::whitelistFolderPath(const QString &path) const QString folderPath = folder->cleanPath() + QLatin1Char('/'); const auto relPath = path.mid(folderPath.length()); - folder->whitelistPath(relPath); + + switch (list) { + case SyncJournalDb::SelectiveSyncListType::SelectiveSyncWhiteList: + folder->whitelistPath(relPath); + break; + case SyncJournalDb::SelectiveSyncListType::SelectiveSyncBlackList: + folder->blacklistPath(relPath); + break; + default: + Q_UNREACHABLE(); + } } -void FolderMan::blacklistFolderPath(const QString &path) +void FolderMan::whitelistFolderPath(const QString &path) { - const auto folder = folderForPath(path); - if (!folder) { - return; - } + addFolderToSelectiveSyncList(path, SyncJournalDb::SelectiveSyncListType::SelectiveSyncWhiteList); +} - const QString folderPath = folder->cleanPath() + QLatin1Char('/'); - const auto relPath = path.mid(folderPath.length()); - folder->blacklistPath(relPath); +void FolderMan::blacklistFolderPath(const QString &path) +{ + addFolderToSelectiveSyncList(path, SyncJournalDb::SelectiveSyncListType::SelectiveSyncBlackList); } QStringList FolderMan::findFileInLocalFolders(const QString &relPath, const AccountPtr acc) diff --git a/src/gui/folderman.h b/src/gui/folderman.h index a79f1119355c..684ba29829e4 100644 --- a/src/gui/folderman.h +++ b/src/gui/folderman.h @@ -358,6 +358,8 @@ private slots: [[nodiscard]] bool isSwitchToVfsNeeded(const FolderDefinition &folderDefinition) const; + void addFolderToSelectiveSyncList(const QString &path, const SyncJournalDb::SelectiveSyncListType list); + QSet _disabledFolders; Folder::Map _folderMap; QString _folderConfigPath; From a5d7c75e6f9e0e385b86780090c5c0c39e7bd54e Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Thu, 3 Aug 2023 17:24:32 +0800 Subject: [PATCH 57/57] Add additional logging for folder size check Signed-off-by: Claudio Cambra --- src/libsync/discoveryphase.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libsync/discoveryphase.cpp b/src/libsync/discoveryphase.cpp index 9034da4b094c..27936ad866a2 100644 --- a/src/libsync/discoveryphase.cpp +++ b/src/libsync/discoveryphase.cpp @@ -84,6 +84,7 @@ void DiscoveryPhase::checkFolderSizeLimit(const QString &path, const std::functi connect(propfindJob, &PropfindJob::result, this, [=](const QVariantMap &values) { const auto result = values.value(QLatin1String("size")).toLongLong(); const auto limit = _syncOptions._newBigFolderSizeLimit; + qCDebug(lcDiscovery) << "Folder size check complete for" << path << "result:" << result << "limit:" << limit; return completionCallback(result >= limit); }); propfindJob->start();