Skip to content

Commit

Permalink
Update Word files VFS placeholders each time they get opened by track…
Browse files Browse the repository at this point in the history
…ing the 'lock' files

Signed-off-by: alex-z <[email protected]>
  • Loading branch information
allexzander authored and backportbot-nextcloud[bot] committed Sep 6, 2023
1 parent 28e7b38 commit a94a918
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 27 deletions.
20 changes: 18 additions & 2 deletions src/gui/folder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -643,8 +643,11 @@ void Folder::slotFilesLockReleased(const QSet<QString> &files)
for (const auto &file : files) {
const auto fileRecordPath = fileFromLocalPath(file);
SyncJournalFileRecord rec;
const auto canUnlockFile = journalDb()->getFileRecord(fileRecordPath, &rec)
&& rec.isValid()
const auto isFileRecordValid = journalDb()->getFileRecord(fileRecordPath, &rec) && rec.isValid();
if (isFileRecordValid) {
[[maybe_unused]] const auto result = _vfs->updateMetadata(path() + rec.path(), rec._modtime, rec._fileSize, rec._fileId);
}
const auto canUnlockFile = isFileRecordValid
&& rec._lockstate._locked
&& rec._lockstate._lockOwnerType == static_cast<qint64>(SyncFileItem::LockOwnerType::UserLock)
&& rec._lockstate._lockOwnerId == _accountState->account()->davUser();
Expand All @@ -669,6 +672,18 @@ void Folder::slotFilesLockReleased(const QSet<QString> &files)
}
}

void Folder::slotFilesLockImposed(const QSet<QString> &files)
{
qCDebug(lcFolder) << "Lock files detected for office files" << files;
for (const auto &file : files) {
const auto fileRecordPath = fileFromLocalPath(file);
SyncJournalFileRecord rec;
if (journalDb()->getFileRecord(fileRecordPath, &rec) && rec.isValid()) {
[[maybe_unused]] const auto result = _vfs->updateMetadata(path() + rec.path(), rec._modtime, rec._fileSize, rec._fileId);
}
}
}

void Folder::implicitlyHydrateFile(const QString &relativepath)
{
qCInfo(lcFolder) << "Implicitly hydrate virtual file:" << relativepath;
Expand Down Expand Up @@ -1375,6 +1390,7 @@ void Folder::registerFolderWatcher()
if (_accountState->account()->capabilities().filesLockAvailable()) {
connect(_folderWatcher.data(), &FolderWatcher::filesLockReleased, this, &Folder::slotFilesLockReleased);
}
connect(_folderWatcher.data(), &FolderWatcher::filesLockImposed, this, &Folder::slotFilesLockImposed, Qt::UniqueConnection);
_folderWatcher->init(path());
_folderWatcher->startNotificatonTest(path() + QLatin1String(".nextcloudsync.log"));
}
Expand Down
5 changes: 5 additions & 0 deletions src/gui/folder.h
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,11 @@ public slots:
*/
void slotFilesLockReleased(const QSet<QString> &files);

/*
* Triggered when lock files were added
*/
void slotFilesLockImposed(const QSet<QString> &files);

/**
* Mark a virtual file as being requested for download, and start a sync.
*
Expand Down
59 changes: 36 additions & 23 deletions src/gui/folderwatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ void FolderWatcher::changeDetected(const QStringList &paths)

QSet<QString> changedPaths;
QSet<QString> unlockedFiles;
QSet<QString> lockedFiles;

for (int i = 0; i < paths.size(); ++i) {
QString path = paths[i];
Expand All @@ -178,15 +179,21 @@ void FolderWatcher::changeDetected(const QStringList &paths)
_testNotificationPath.clear();
}

const auto checkResult = checkIfFileIsLockOrUnlock(path);
if (_shouldWatchForFileUnlocking) {
const auto unlockedFilePath = possiblyAddUnlockedFilePath(path);
if (!unlockedFilePath.isEmpty()) {
unlockedFiles.insert(unlockedFilePath);
if (checkResult.type == FileLockingInfo::Type::Unlocked && !checkResult.path.isEmpty()) {
unlockedFiles.insert(checkResult.path);
}

qCDebug(lcFolderWatcher) << "Unlocked files:" << unlockedFiles.values();
}

if (checkResult.type == FileLockingInfo::Type::Locked && !checkResult.path.isEmpty()) {
lockedFiles.insert(checkResult.path);
}

qCDebug(lcFolderWatcher) << "Locked files:" << lockedFiles.values();

// ------- handle ignores:
if (pathIsIgnored(path)) {
continue;
Expand All @@ -199,6 +206,10 @@ void FolderWatcher::changeDetected(const QStringList &paths)
emit filesLockReleased(unlockedFiles);
}

if (!lockedFiles.isEmpty()) {
emit filesLockImposed(lockedFiles);
}

if (changedPaths.isEmpty()) {
return;
}
Expand All @@ -214,12 +225,14 @@ void FolderWatcher::folderAccountCapabilitiesChanged()
_shouldWatchForFileUnlocking = _folder->accountState()->account()->capabilities().filesLockAvailable();
}

QString FolderWatcher::possiblyAddUnlockedFilePath(const QString &path)
FolderWatcher::FileLockingInfo FolderWatcher::checkIfFileIsLockOrUnlock(const QString &path) const
{
qCDebug(lcFolderWatcher) << "Checking if it is a lock file:" << path;

FileLockingInfo result;
const auto pathSplit = path.split(QLatin1Char('/'), Qt::SkipEmptyParts);
if (pathSplit.isEmpty()) {
return {};
return result;
}
QString lockFilePatternFound;
for (const auto &lockFilePattern : lockFilePatterns) {
Expand All @@ -229,8 +242,8 @@ QString FolderWatcher::possiblyAddUnlockedFilePath(const QString &path)
}
}

if (lockFilePatternFound.isEmpty() || QFileInfo::exists(path)) {
return {};
if (lockFilePatternFound.isEmpty()) {
return result;
}

qCDebug(lcFolderWatcher) << "Found a lock file with prefix:" << lockFilePatternFound << "in path:" << path;
Expand All @@ -239,7 +252,7 @@ QString FolderWatcher::possiblyAddUnlockedFilePath(const QString &path)
auto lockFilePathWithoutPrefixSplit = lockFilePathWitoutPrefix.split(QLatin1Char('.'));

if (lockFilePathWithoutPrefixSplit.size() < 2) {
return {};
return result;
}

auto extensionSanitized = lockFilePathWithoutPrefixSplit.takeLast().toStdString();
Expand All @@ -252,26 +265,26 @@ QString FolderWatcher::possiblyAddUnlockedFilePath(const QString &path)
);

lockFilePathWithoutPrefixSplit.push_back(QString::fromStdString(extensionSanitized));
auto unlockedFilePath = lockFilePathWithoutPrefixSplit.join(QLatin1Char('.'));

if (!QFile::exists(unlockedFilePath)) {
unlockedFilePath.clear();
qCDebug(lcFolderWatcher) << "Assumed unlocked file path" << unlockedFilePath << "does not exist. Going to try to find matching file";
auto splitFilePath = unlockedFilePath.split(QLatin1Char('/'));
if (splitFilePath.size() > 1) {
const auto lockFileName = splitFilePath.takeLast();
// some software will modify lock file name such that it does not correspond to original file (removing some symbols from the name, so we will search for a matching file
unlockedFilePath = findMatchingUnlockedFileInDir(splitFilePath.join(QLatin1Char('/')), lockFileName);
}
const auto lockFilePathWithoutPrefix = lockFilePathWithoutPrefixSplit.join(QLatin1Char('.'));

qCDebug(lcFolderWatcher) << "Assumed locked/unlocked file path" << lockFilePathWithoutPrefix << "Going to try to find matching file";
auto splitFilePath = lockFilePathWithoutPrefix.split(QLatin1Char('/'));
if (splitFilePath.size() > 1) {
const auto lockFileNameWithoutPrefix = splitFilePath.takeLast();
// some software will modify lock file name such that it does not correspond to original file (removing some symbols from the name, so we will search
// for a matching file
result.path = findMatchingUnlockedFileInDir(splitFilePath.join(QLatin1Char('/')), lockFileNameWithoutPrefix);
}

if (unlockedFilePath.isEmpty() || !QFile::exists(unlockedFilePath)) {
return {};
if (result.path.isEmpty() || !QFile::exists(result.path)) {
result.path.clear();
return result;
}
return unlockedFilePath;
result.type = QFile::exists(path) ? FileLockingInfo::Type::Locked : FileLockingInfo::Type::Unlocked;
return result;
}

QString FolderWatcher::findMatchingUnlockedFileInDir(const QString &dirPath, const QString &lockFileName)
QString FolderWatcher::findMatchingUnlockedFileInDir(const QString &dirPath, const QString &lockFileName) const
{
QString foundFilePath;
const QDir dir(dirPath);
Expand Down
16 changes: 14 additions & 2 deletions src/gui/folderwatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ class Folder;
class FolderWatcher : public QObject
{
Q_OBJECT

struct FileLockingInfo {
enum class Type { Unset = -1, Locked, Unlocked };
QString path;
Type type = Type::Unset;
};

public:
// Construct, connect signals, call init()
explicit FolderWatcher(Folder *folder = nullptr);
Expand Down Expand Up @@ -90,6 +97,11 @@ class FolderWatcher : public QObject
*/
void filesLockReleased(const QSet<QString> &files);

/*
* Emitted when lock files were added
*/
void filesLockImposed(const QSet<QString> &files);

/**
* Emitted if some notifications were lost.
*
Expand Down Expand Up @@ -129,8 +141,8 @@ private slots:

void appendSubPaths(QDir dir, QStringList& subPaths);

QString possiblyAddUnlockedFilePath(const QString &path);
QString findMatchingUnlockedFileInDir(const QString &dirPath, const QString &lockFileName);
[[nodiscard]] FileLockingInfo checkIfFileIsLockOrUnlock(const QString &path) const;
[[nodiscard]] QString findMatchingUnlockedFileInDir(const QString &dirPath, const QString &lockFileName) const;

/* Check if the path should be igored by the FolderWatcher. */
[[nodiscard]] bool pathIsIgnored(const QString &path) const;
Expand Down

0 comments on commit a94a918

Please sign in to comment.