Skip to content

Commit

Permalink
Display both files and some metadata in the case clash conflict resol…
Browse files Browse the repository at this point in the history
…ution dialog

Signed-off-by: Claudio Cambra <claudio.cambra@nextcloud.com>
  • Loading branch information
claucambra committed Dec 14, 2022
1 parent 9603ca1 commit cb6e5ca
Show file tree
Hide file tree
Showing 4 changed files with 347 additions and 10 deletions.
101 changes: 97 additions & 4 deletions src/gui/caseclashfilenamedialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
#include <QDialogButtonBox>
#include <QFileInfo>
#include <QPushButton>
#include <QDirIterator>
#include <QDesktopServices>

#include <array>

Expand Down Expand Up @@ -64,34 +66,66 @@ QString illegalCharacterListToString(const QVector<QChar> &illegalCharacters)

namespace OCC {

CaseClashFilenameDialog::CaseClashFilenameDialog(AccountPtr account, Folder *folder, QString filePath, QWidget *parent)
CaseClashFilenameDialog::CaseClashFilenameDialog(AccountPtr account,
Folder *folder,
QString conflictFilePath,
QString conflictTaggedPath,
QWidget *parent)
: QDialog(parent)
, _ui(new Ui::CaseClashFilenameDialog)
, _account(account)
, _folder(folder)
, _filePath(std::move(filePath))
, _filePath(std::move(conflictFilePath))
{
Q_ASSERT(_account);
Q_ASSERT(_folder);

const auto filePathFileInfo = QFileInfo(_filePath);
const auto conflictFileName = filePathFileInfo.fileName();

_relativeFilePath = filePathFileInfo.path() + QStringLiteral("/");
_relativeFilePath = _relativeFilePath.replace(folder->path(), QStringLiteral(""));
_relativeFilePath = _relativeFilePath.isEmpty() ? QStringLiteral("") : _relativeFilePath + QStringLiteral("/");

_originalFileName = _relativeFilePath + filePathFileInfo.fileName();
_originalFileName = _relativeFilePath + conflictFileName;

_ui->setupUi(this);
_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
_ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Rename file"));

_ui->descriptionLabel->setText(tr("The file \"%1\" could not be synced because of a case clash conflict with an existing file on this system.").arg(_originalFileName));
_ui->explanationLabel->setText(tr("%1 does not support equal file names with only letter casing differences.").arg(QSysInfo::prettyProductName()));
_ui->filenameLineEdit->setText(filePathFileInfo.fileName());
_ui->filenameLineEdit->setText(conflictFileName);

const auto preexistingConflictingFile = caseClashConflictFile(_filePath);
updateFileWidgetGroup(preexistingConflictingFile,
tr("Open local version"),
_ui->localVersionFilename,
_ui->localVersionLink,
_ui->localVersionMtime,
_ui->localVersionSize,
_ui->localVersionButton);

updateFileWidgetGroup(conflictTaggedPath,
tr("Open server version"),
_ui->remoteVersionFilename,
_ui->remoteVersionLink,
_ui->remoteVersionMtime,
_ui->remoteVersionSize,
_ui->remoteVersionButton);
// Display incoming conflict filename, not conflict-tagged filename
_ui->remoteVersionFilename->setText(filePathFileInfo.fileName());

adjustSize();

connect(_ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(_ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
connect(_ui->localVersionButton, &QToolButton::clicked, this, [preexistingConflictingFile] {
QDesktopServices::openUrl(QUrl::fromLocalFile(preexistingConflictingFile));
});
connect(_ui->remoteVersionButton, &QToolButton::clicked, this, [conflictTaggedPath] {
QDesktopServices::openUrl(QUrl::fromLocalFile(conflictTaggedPath));
});

_ui->errorLabel->setText({}/*
tr("Checking rename permissions …")*/);
Expand All @@ -106,6 +140,65 @@ CaseClashFilenameDialog::CaseClashFilenameDialog(AccountPtr account, Folder *fol

CaseClashFilenameDialog::~CaseClashFilenameDialog() = default;

QString CaseClashFilenameDialog::caseClashConflictFile(const QString &conflictFilePath)
{
const auto filePathFileInfo = QFileInfo(conflictFilePath);
const auto conflictFileName = filePathFileInfo.fileName();

QDirIterator it(filePathFileInfo.path(), QDirIterator::Subdirectories);

while(it.hasNext()) {
const auto filePath = it.next();
qDebug() << filePath;
QFileInfo fileInfo(filePath);

if(fileInfo.isDir()) {
continue;
}

const auto currentFileName = fileInfo.fileName();
if (currentFileName.compare(conflictFileName, Qt::CaseInsensitive) == 0 &&
currentFileName != conflictFileName) {

return filePath;
}
}

return {};
}

void CaseClashFilenameDialog::updateFileWidgetGroup(const QString &filePath,
const QString &linkText,
QLabel *filenameLabel,
QLabel *linkLabel,
QLabel *mtimeLabel,
QLabel *sizeLabel,
QToolButton *button) const
{
const auto filePathFileInfo = QFileInfo(filePath);
const auto filename = filePathFileInfo.fileName();
const auto lastModifiedString = filePathFileInfo.lastModified().toString();
const auto fileSizeString = locale().formattedDataSize(filePathFileInfo.size());
const auto fileUrl = QUrl::fromLocalFile(filePath).toString();
const auto linkString = QStringLiteral("<a href='%1'>%2</a>").arg(fileUrl, linkText);
const auto mime = QMimeDatabase().mimeTypeForFile(_filePath);
QIcon fileTypeIcon;

qDebug() << filePath << filePathFileInfo.exists() << filename << lastModifiedString << fileSizeString << fileUrl << linkString << mime;

if (QIcon::hasThemeIcon(mime.iconName())) {
fileTypeIcon = QIcon::fromTheme(mime.iconName());
} else {
fileTypeIcon = QIcon(":/qt-project.org/styles/commonstyle/images/file-128.png");
}

filenameLabel->setText(filename);
mtimeLabel->setText(lastModifiedString);
sizeLabel->setText(fileSizeString);
linkLabel->setText(linkString);
button->setIcon(fileTypeIcon);
}

void CaseClashFilenameDialog::checkIfAllowedToRename()
{
const auto propfindJob = new PropfindJob(_account, QDir::cleanPath(_folder->remotePath() + _originalFileName));
Expand Down
19 changes: 18 additions & 1 deletion src/gui/caseclashfilenamedialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#include <memory>

#include <QDialog>
#include <QLabel>
#include <QToolButton>

namespace OCC {

Expand All @@ -35,7 +37,11 @@ class CaseClashFilenameDialog : public QDialog
Q_OBJECT

public:
explicit CaseClashFilenameDialog(AccountPtr account, Folder *folder, QString filePath, QWidget *parent = nullptr);
explicit CaseClashFilenameDialog(AccountPtr account,
Folder *folder,
QString conflictFilePath,
QString conflictTaggedPath,
QWidget *parent = nullptr);

~CaseClashFilenameDialog() override;

Expand All @@ -45,6 +51,9 @@ class CaseClashFilenameDialog : public QDialog
void successfulRename(const QString &filePath);

private:
// Find the conflicting file path
static QString caseClashConflictFile(const QString &conflictFilePath);

std::unique_ptr<Ui::CaseClashFilenameDialog> _ui;

AccountPtr _account;
Expand All @@ -65,7 +74,15 @@ class CaseClashFilenameDialog : public QDialog
bool processLeadingOrTrailingSpacesError(const QString &fileName);
void onPropfindPermissionSuccess(const QVariantMap &values);
void onPropfindPermissionError(QNetworkReply *reply = nullptr);

private slots:
void useInvalidName();
void updateFileWidgetGroup(const QString &filePath,
const QString &linkText,
QLabel *filenameLabel,
QLabel *linkLabel,
QLabel *mtimeLabel,
QLabel *sizeLabel,
QToolButton *button) const;
};
}
Loading

0 comments on commit cb6e5ca

Please sign in to comment.