nextcloud-desktop/src/gui/folderwizard.cpp

550 lines
17 KiB
C++
Raw Normal View History

2011-04-06 13:48:02 +04:00
/*
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "folderwizard.h"
#include "folderman.h"
2014-11-10 01:25:57 +03:00
#include "configfile.h"
#include "theme.h"
#include "networkjobs.h"
#include "account.h"
#include "selectivesyncdialog.h"
#include "accountstate.h"
#include "creds/abstractcredentials.h"
2012-05-21 18:48:49 +04:00
#include <QDebug>
#include <QDesktopServices>
#include <QDir>
#include <QFileDialog>
#include <QFileInfo>
2013-08-01 20:22:13 +04:00
#include <QFileIconProvider>
#include <QInputDialog>
#include <QUrl>
#include <QValidator>
#include <QWizardPage>
#include <QTreeWidget>
#include <QVBoxLayout>
#include <stdlib.h>
2014-11-10 00:34:07 +03:00
namespace OCC
2011-04-05 13:10:44 +04:00
{
QString FormatWarningsWizardPage::formatWarnings(const QStringList &warnings) const
{
QString ret;
if (warnings.count() == 1) {
2015-02-06 00:00:13 +03:00
ret = tr("<b>Warning:</b> %1").arg(warnings.first());
} else if (warnings.count() > 1) {
2015-02-06 00:00:13 +03:00
ret = tr("<b>Warning:</b>") + " <ul>";
Q_FOREACH(QString warning, warnings) {
ret += QString::fromLatin1("<li>%1</li>").arg(warning);
}
ret += "</ul>";
}
return ret;
}
FolderWizardLocalPath::FolderWizardLocalPath()
: FormatWarningsWizardPage()
{
_ui.setupUi(this);
2012-08-17 19:13:17 +04:00
registerField(QLatin1String("sourceFolder*"), _ui.localFolderLineEdit);
connect(_ui.localFolderChooseBtn, SIGNAL(clicked()), this, SLOT(slotChooseLocalFolder()));
_ui.localFolderChooseBtn->setToolTip(tr("Click to select a local folder to sync."));
QString defaultPath = QString::fromLatin1( "%1/%2").arg( QDir::homePath() ).arg(Theme::instance()->appName() );
_ui.localFolderLineEdit->setText( QDir::toNativeSeparators( defaultPath ) );
_ui.localFolderLineEdit->setToolTip(tr("Enter the path to the local folder."));
2012-08-17 19:13:17 +04:00
registerField(QLatin1String("alias*"), _ui.aliasLineEdit);
_ui.aliasLineEdit->setText( Theme::instance()->appNameGUI() );
_ui.aliasLineEdit->setToolTip(tr("The directory alias is a descriptive name for this sync connection."));
_ui.warnLabel->setTextFormat(Qt::RichText);
2011-10-11 17:39:25 +04:00
_ui.warnLabel->hide();
}
FolderWizardLocalPath::~FolderWizardLocalPath()
{
2011-10-11 17:39:25 +04:00
}
void FolderWizardLocalPath::initializePage()
{
_ui.warnLabel->hide();
}
void FolderWizardLocalPath::cleanupPage()
{
_ui.warnLabel->hide();
}
bool FolderWizardLocalPath::isComplete() const
{
QString errorStr = FolderMan::instance()->checkPathValidityForNewFolder(
QDir::fromNativeSeparators(_ui.localFolderLineEdit->text()));
bool isOk = errorStr.isEmpty();
QStringList warnStrings;
if (!isOk) {
warnStrings << errorStr;
}
// check if the alias is unique.
QString alias = _ui.aliasLineEdit->text();
2011-10-11 17:39:25 +04:00
if( alias.isEmpty() ) {
warnStrings.append( tr("The alias can not be empty. Please provide a descriptive alias word.") );
2011-10-11 17:39:25 +04:00
isOk = false;
}
auto map = FolderMan::instance()->map();
Folder::Map::const_iterator i = map.constBegin();
2011-10-11 17:39:25 +04:00
bool goon = true;
while( goon && i != map.constEnd() ) {
2013-07-08 03:44:29 +04:00
Folder *f = i.value();
2011-10-11 17:39:25 +04:00
if( f ) {
qDebug() << "Checking local alias: " << f->alias();
2011-10-11 17:39:25 +04:00
if( f->alias() == alias ) {
warnStrings.append( tr("The alias <i>%1</i> is already in use. Please pick another alias.").arg(alias) );
2011-10-11 17:39:25 +04:00
isOk = false;
goon = false;
}
}
2011-10-11 17:39:25 +04:00
i++;
}
_ui.warnLabel->setWordWrap(true);
2011-10-11 17:39:25 +04:00
if( isOk ) {
_ui.warnLabel->hide();
2012-08-17 19:13:17 +04:00
_ui.warnLabel->setText( QString::null );
2011-10-11 17:39:25 +04:00
} else {
_ui.warnLabel->show();
QString warnings = formatWarnings(warnStrings);
_ui.warnLabel->setText( warnings );
}
return isOk;
}
void FolderWizardLocalPath::slotChooseLocalFolder()
{
QString sf = QDesktopServices::storageLocation(QDesktopServices::HomeLocation);
QDir d(sf);
// open the first entry of the home dir. Otherwise the dir picker comes
// up with the closed home dir icon, stupid Qt default...
QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks,
QDir::DirsFirst|QDir::Name);
if(dirs.count() > 0) sf += "/"+dirs.at(0); // Take the first dir in home dir.
QString dir = QFileDialog::getExistingDirectory(this,
tr("Select the source folder"),
sf);
if (!dir.isEmpty()) {
// set the last directory component name as alias
_ui.localFolderLineEdit->setText(QDir::toNativeSeparators(dir));
QDir pickedDir(dir);
QString newAlias = pickedDir.dirName();
if( !newAlias.isEmpty() ) {
_ui.aliasLineEdit->setText(newAlias);
}
}
emit completeChanged();
}
// =================================================================================
FolderWizardRemotePath::FolderWizardRemotePath(AccountPtr account)
: FormatWarningsWizardPage()
,_warnWasVisible(false)
,_account(account)
{
_ui.setupUi(this);
_ui.warnFrame->hide();
_ui.folderTreeWidget->setSortingEnabled(true);
_ui.folderTreeWidget->sortByColumn(0, Qt::AscendingOrder);
connect(_ui.addFolderButton, SIGNAL(clicked()), SLOT(slotAddRemoteFolder()));
connect(_ui.refreshButton, SIGNAL(clicked()), SLOT(slotRefreshFolders()));
2013-10-23 16:48:44 +04:00
connect(_ui.folderTreeWidget, SIGNAL(itemExpanded(QTreeWidgetItem*)), SLOT(slotItemExpanded(QTreeWidgetItem*)));
connect(_ui.folderTreeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), SLOT(slotCurrentItemChanged(QTreeWidgetItem*)));
connect(_ui.folderEntry, SIGNAL(textEdited(QString)), SLOT(slotFolderEntryEdited(QString)));
2013-10-23 16:48:44 +04:00
_lscolTimer.setInterval(500);
_lscolTimer.setSingleShot(true);
connect(&_lscolTimer, SIGNAL(timeout()), SLOT(slotLsColFolderEntry()));
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
_ui.folderTreeWidget->header()->setSectionResizeMode(0,QHeaderView::ResizeToContents);
// Make sure that there will be a scrollbar when the contents is too wide
_ui.folderTreeWidget->header()->setStretchLastSection(false);
#endif
}
void FolderWizardRemotePath::slotAddRemoteFolder()
{
QTreeWidgetItem *current = _ui.folderTreeWidget->currentItem();
QString parent('/');
if (current) {
parent = current->data(0, Qt::UserRole).toString();
}
QInputDialog *dlg = new QInputDialog(this);
dlg->setWindowTitle(tr("Create Remote Folder"));
dlg->setLabelText(tr("Enter the name of the new folder to be created below '%1':")
.arg(parent));
dlg->open(this, SLOT(slotCreateRemoteFolder(QString)));
dlg->setAttribute(Qt::WA_DeleteOnClose);
}
void FolderWizardRemotePath::slotCreateRemoteFolder(const QString &folder)
{
if( folder.isEmpty() ) return;
2013-10-23 16:48:44 +04:00
QTreeWidgetItem *current = _ui.folderTreeWidget->currentItem();
QString fullPath;
if (current) {
fullPath = current->data(0, Qt::UserRole).toString();
}
fullPath += "/" + folder;
MkColJob *job = new MkColJob(_account, fullPath, this);
2013-10-23 16:48:44 +04:00
/* check the owncloud configuration file and query the ownCloud */
2013-11-11 18:20:07 +04:00
connect(job, SIGNAL(finished(QNetworkReply::NetworkError)),
SLOT(slotCreateRemoteFolderFinished(QNetworkReply::NetworkError)));
connect(job, SIGNAL(networkError(QNetworkReply*)), SLOT(slotHandleNetworkError(QNetworkReply*)));
2013-11-14 22:20:09 +04:00
job->start();
}
void FolderWizardRemotePath::slotCreateRemoteFolderFinished(QNetworkReply::NetworkError error)
{
2013-11-11 18:20:07 +04:00
if (error == QNetworkReply::NoError) {
qDebug() << "** webdav mkdir request finished";
showWarn(tr("Folder was successfully created on %1.").arg(Theme::instance()->appNameGUI()));
slotRefreshFolders();
_ui.folderEntry->setText(static_cast<MkColJob *>(sender())->path());
slotLsColFolderEntry();
2013-11-11 18:20:07 +04:00
}
2013-10-23 16:48:44 +04:00
}
void FolderWizardRemotePath::slotHandleNetworkError(QNetworkReply *reply)
2013-10-23 16:48:44 +04:00
{
qDebug() << "** webdav mkdir request failed:" << reply->error();
if( reply && !_account->credentials()->stillValid(reply) ) {
showWarn(tr("Authentication failed accessing %1").arg(Theme::instance()->appNameGUI()));
} else {
showWarn(tr("Failed to create the folder on %1. Please check manually.")
.arg(Theme::instance()->appNameGUI()));
}
}
static QTreeWidgetItem* findFirstChild(QTreeWidgetItem *parent, const QString& text)
{
for (int i = 0; i < parent->childCount(); ++i) {
QTreeWidgetItem *child = parent->child(i);
if (child->text(0) == text) {
return child;
}
}
2013-08-07 14:17:46 +04:00
return 0;
}
void FolderWizardRemotePath::recursiveInsert(QTreeWidgetItem *parent, QStringList pathTrail, QString path)
{
if (pathTrail.isEmpty())
return;
const QString parentPath = parent->data(0, Qt::UserRole).toString();
const QString folderName = pathTrail.first();
QString folderPath;
if (parentPath == QLatin1String("/")) {
folderPath = folderName;
} else {
folderPath = parentPath + "/" + folderName;
}
QTreeWidgetItem *item = findFirstChild(parent, folderName);
if (!item) {
item = new QTreeWidgetItem(parent);
QFileIconProvider prov;
QIcon folderIcon = prov.icon(QFileIconProvider::Folder);
item->setIcon(0, folderIcon);
item->setText(0, folderName);
item->setData(0, Qt::UserRole, folderPath);
item->setToolTip(0, folderPath);
item->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator);
}
pathTrail.removeFirst();
recursiveInsert(item, pathTrail, path);
}
bool FolderWizardRemotePath::selectByPath(QString path)
{
if (path.startsWith(QLatin1Char('/'))) {
path = path.mid(1);
}
if (path.endsWith(QLatin1Char('/'))) {
path.chop(1);
}
QTreeWidgetItem *it = _ui.folderTreeWidget->topLevelItem(0);
if (!path.isEmpty()) {
const QStringList pathTrail = path.split(QLatin1Char('/'));
foreach (const QString& path, pathTrail) {
if (!it) {
return false;
}
it = findFirstChild(it, path);
}
}
if (!it) {
return false;
}
_ui.folderTreeWidget->setCurrentItem(it);
return true;
}
void FolderWizardRemotePath::slotUpdateDirectories(const QStringList &list)
{
QString webdavFolder = QUrl(_account->davUrl()).path();
QTreeWidgetItem *root = _ui.folderTreeWidget->topLevelItem(0);
if (!root) {
root = new QTreeWidgetItem(_ui.folderTreeWidget);
root->setText(0, Theme::instance()->appNameGUI());
root->setIcon(0, Theme::instance()->applicationIcon());
root->setToolTip(0, tr("Choose this to sync the entire account"));
root->setData(0, Qt::UserRole, "/");
}
2013-08-01 20:22:13 +04:00
foreach (QString path, list) {
path.remove(webdavFolder);
QStringList paths = path.split('/');
if (paths.last().isEmpty()) paths.removeLast();
recursiveInsert(root, paths, path);
}
root->setExpanded(true);
}
void FolderWizardRemotePath::slotRefreshFolders()
{
LsColJob *job = new LsColJob(_account, "/", this);
job->setProperties(QList<QByteArray>() << "resourcetype");
connect(job, SIGNAL(directoryListingSubfolders(QStringList)),
2013-10-23 16:48:44 +04:00
SLOT(slotUpdateDirectories(QStringList)));
2013-11-14 22:20:09 +04:00
job->start();
_ui.folderTreeWidget->clear();
_ui.folderEntry->clear();
}
void FolderWizardRemotePath::slotItemExpanded(QTreeWidgetItem *item)
{
2013-10-23 16:48:44 +04:00
QString dir = item->data(0, Qt::UserRole).toString();
LsColJob *job = new LsColJob(_account, dir, this);
job->setProperties(QList<QByteArray>() << "resourcetype");
connect(job, SIGNAL(directoryListingSubfolders(QStringList)),
2013-10-23 16:48:44 +04:00
SLOT(slotUpdateDirectories(QStringList)));
2013-11-14 22:20:09 +04:00
job->start();
}
void FolderWizardRemotePath::slotCurrentItemChanged(QTreeWidgetItem *item)
{
if (item) {
QString dir = item->data(0, Qt::UserRole).toString();
if (!dir.startsWith(QLatin1Char('/'))) {
dir.prepend(QLatin1Char('/'));
}
_ui.folderEntry->setText(dir);
}
emit completeChanged();
}
void FolderWizardRemotePath::slotFolderEntryEdited(const QString& text)
{
if (selectByPath(text)) {
_lscolTimer.stop();
return;
}
_ui.folderTreeWidget->setCurrentItem(0);
_lscolTimer.start(); // avoid sending a request on each keystroke
}
void FolderWizardRemotePath::slotLsColFolderEntry()
{
QString path = _ui.folderEntry->text();
if (path.startsWith(QLatin1Char('/')))
path = path.mid(1);
LsColJob *job = new LsColJob(_account, path, this);
job->setProperties(QList<QByteArray>() << "resourcetype");
connect(job, SIGNAL(directoryListingSubfolders(QStringList)),
SLOT(slotTypedPathFound(QStringList)));
job->start();
}
void FolderWizardRemotePath::slotTypedPathFound(const QStringList& subpaths)
{
slotUpdateDirectories(subpaths);
selectByPath(_ui.folderEntry->text());
}
FolderWizardRemotePath::~FolderWizardRemotePath()
{
}
bool FolderWizardRemotePath::isComplete() const
{
if (!_ui.folderTreeWidget->currentItem())
return false;
QStringList warnStrings;
QString dir = _ui.folderTreeWidget->currentItem()->data(0, Qt::UserRole).toString();
if (!dir.startsWith(QLatin1Char('/'))) {
dir.prepend(QLatin1Char('/'));
}
wizard()->setProperty("targetPath", dir);
Folder::Map map = FolderMan::instance()->map();
Folder::Map::const_iterator i = map.constBegin();
for(i = map.constBegin();i != map.constEnd(); i++ ) {
Folder *f = static_cast<Folder*>(i.value());
if (f->accountState()->account() != _account) {
continue;
}
2013-10-21 23:42:52 +04:00
QString curDir = f->remotePath();
if (!curDir.startsWith(QLatin1Char('/'))) {
curDir.prepend(QLatin1Char('/'));
}
if (QDir::cleanPath(dir) == QDir::cleanPath(curDir)) {
warnStrings.append(tr("This folder is already being synced."));
} else if (dir.startsWith(curDir + QLatin1Char('/'))) {
warnStrings.append(tr("You are already syncing <i>%1</i>, which is a parent folder of <i>%2</i>.").arg(curDir).arg(dir));
}
if (curDir == QLatin1String("/")) {
warnStrings.append(tr("You are already syncing all your files. Syncing another folder is <b>not</b> supported. "
"If you want to sync multiple folders, please remove the currently configured "
"root folder sync."));
}
}
showWarn(formatWarnings(warnStrings));
return warnStrings.isEmpty();
}
void FolderWizardRemotePath::cleanupPage()
{
showWarn();
}
void FolderWizardRemotePath::initializePage()
{
showWarn();
2013-10-23 16:48:44 +04:00
slotRefreshFolders();
}
2011-10-05 19:49:03 +04:00
void FolderWizardRemotePath::showWarn( const QString& msg ) const
{
if( msg.isEmpty() ) {
_ui.warnFrame->hide();
} else {
_ui.warnFrame->show();
_ui.warnLabel->setText( msg );
}
2011-10-12 17:14:39 +04:00
}
// ====================================================================================
FolderWizardSelectiveSync::FolderWizardSelectiveSync(AccountPtr account)
{
QVBoxLayout *layout = new QVBoxLayout(this);
_treeView = new SelectiveSyncTreeView(account, this);
layout->addWidget(new QLabel(tr("Choose What to Sync: You can optionally deselect remote subfolders you do not wish to synchronize.")));
layout->addWidget(_treeView);
}
FolderWizardSelectiveSync::~FolderWizardSelectiveSync()
{
}
void FolderWizardSelectiveSync::initializePage()
{
QString alias = wizard()->field(QLatin1String("alias")).toString();
QString targetPath = wizard()->property("targetPath").toString();
if (targetPath.startsWith('/')) {
targetPath = targetPath.mid(1);
}
2014-08-18 17:45:58 +04:00
_treeView->setFolderInfo(targetPath, alias);
QWizardPage::initializePage();
}
bool FolderWizardSelectiveSync::validatePage()
{
wizard()->setProperty("selectiveSyncBlackList", QVariant(_treeView->createBlackList()));
return true;
}
void FolderWizardSelectiveSync::cleanupPage()
{
QString alias = wizard()->field(QLatin1String("alias")).toString();
QString targetPath = wizard()->property("targetPath").toString();
2014-08-18 17:45:58 +04:00
_treeView->setFolderInfo(targetPath, alias);
QWizardPage::cleanupPage();
}
// ====================================================================================
/**
* Folder wizard itself
*/
FolderWizard::FolderWizard(AccountPtr account, QWidget *parent)
: QWizard(parent),
_folderWizardSourcePage(new FolderWizardLocalPath),
_folderWizardTargetPage(0),
_folderWizardSelectiveSyncPage(new FolderWizardSelectiveSync(account))
2011-04-05 13:10:44 +04:00
{
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
setPage(Page_Source, _folderWizardSourcePage );
2013-04-08 16:50:26 +04:00
if (!Theme::instance()->singleSyncFolder()) {
_folderWizardTargetPage = new FolderWizardRemotePath(account);
2013-04-08 16:50:26 +04:00
setPage(Page_Target, _folderWizardTargetPage );
}
setPage(Page_SelectiveSync, _folderWizardSelectiveSyncPage);
2013-04-08 16:50:26 +04:00
setWindowTitle( tr("Add Folder") );
setOptions(QWizard::CancelButtonOnLeft);
setButtonText(QWizard::FinishButton, tr("Add Folder"));
2011-04-05 13:10:44 +04:00
}
2013-04-08 16:50:26 +04:00
FolderWizard::~FolderWizard()
{
}
} // end namespace
2011-04-05 13:10:44 +04:00