mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-24 05:55:59 +03:00
Merge branch 'master' into slideshow
This commit is contained in:
commit
e6f1d7632a
67 changed files with 39422 additions and 41851 deletions
12
.drone.yml
12
.drone.yml
|
@ -14,7 +14,7 @@ pipeline:
|
|||
cd /tmp &&
|
||||
git clone https://github.com/frankosterfeld/qtkeychain.git &&
|
||||
cd qtkeychain &&
|
||||
git checkout v0.8.0 &&
|
||||
git checkout v0.9.1 &&
|
||||
mkdir build &&
|
||||
cd build &&
|
||||
cmake ../ &&
|
||||
|
@ -42,7 +42,7 @@ pipeline:
|
|||
cd /tmp &&
|
||||
git clone https://github.com/frankosterfeld/qtkeychain.git &&
|
||||
cd qtkeychain &&
|
||||
git checkout v0.8.0 &&
|
||||
git checkout v0.9.1 &&
|
||||
mkdir build &&
|
||||
cd build &&
|
||||
cmake ../ &&
|
||||
|
@ -70,7 +70,7 @@ pipeline:
|
|||
cd /tmp &&
|
||||
git clone https://github.com/frankosterfeld/qtkeychain.git &&
|
||||
cd qtkeychain &&
|
||||
git checkout v0.8.0 &&
|
||||
git checkout v0.9.1 &&
|
||||
mkdir build &&
|
||||
cd build &&
|
||||
cmake ../ &&
|
||||
|
@ -100,7 +100,7 @@ pipeline:
|
|||
cd /tmp &&
|
||||
git clone https://github.com/frankosterfeld/qtkeychain.git &&
|
||||
cd qtkeychain &&
|
||||
git checkout v0.8.0 &&
|
||||
git checkout v0.9.1 &&
|
||||
mkdir build &&
|
||||
cd build &&
|
||||
cmake ../ &&
|
||||
|
@ -132,7 +132,7 @@ pipeline:
|
|||
cd /tmp &&
|
||||
git clone https://github.com/frankosterfeld/qtkeychain.git &&
|
||||
cd qtkeychain &&
|
||||
git checkout v0.8.0 &&
|
||||
git checkout v0.9.1 &&
|
||||
mkdir build &&
|
||||
cd build &&
|
||||
cmake ../ &&
|
||||
|
@ -165,7 +165,7 @@ pipeline:
|
|||
cd /tmp &&
|
||||
git clone https://github.com/frankosterfeld/qtkeychain.git &&
|
||||
cd qtkeychain &&
|
||||
git checkout v0.8.0 &&
|
||||
git checkout v0.9.1 &&
|
||||
mkdir build &&
|
||||
cd build &&
|
||||
cmake ../ &&
|
||||
|
|
|
@ -18,11 +18,11 @@ if [ $SUFFIX != "master" ]; then
|
|||
SUFFIX="PR-$SUFFIX"
|
||||
fi
|
||||
|
||||
#QtKeyChain 0.8.0
|
||||
#QtKeyChain 0.9.1
|
||||
cd /build
|
||||
git clone https://github.com/frankosterfeld/qtkeychain.git
|
||||
cd qtkeychain
|
||||
git checkout v0.8.0
|
||||
git checkout v0.9.1
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -D CMAKE_INSTALL_PREFIX=/usr ../
|
||||
|
|
|
@ -272,10 +272,12 @@ bool FileSystem::openAndSeekFileSharedRead(QFile *file, QString *errorOrNull, qi
|
|||
int fd = _open_osfhandle((intptr_t)fileHandle, _O_RDONLY);
|
||||
if (fd == -1) {
|
||||
error = "could not make fd from handle";
|
||||
CloseHandle(fileHandle);
|
||||
return false;
|
||||
}
|
||||
if (!file->open(fd, QIODevice::ReadOnly, QFile::AutoCloseHandle)) {
|
||||
error = file->errorString();
|
||||
_close(fd); // implicitly closes fileHandle
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -666,7 +666,21 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
|
|||
goto error;
|
||||
}
|
||||
|
||||
while ((dirent = csync_vio_readdir(ctx, dh))) {
|
||||
while (true) {
|
||||
// Get the next item in the directory
|
||||
errno = 0;
|
||||
dirent = csync_vio_readdir(ctx, dh);
|
||||
if (!dirent) {
|
||||
if (errno != 0) {
|
||||
// Note: Windows vio converts any error into EACCES
|
||||
qCWarning(lcUpdate, "readdir failed for file in %s - errno %d", uri, errno);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Normal case: End of items in directory
|
||||
break;
|
||||
}
|
||||
|
||||
/* Conversion error */
|
||||
if (dirent->path.isEmpty() && !dirent->original_path.isEmpty()) {
|
||||
ctx->status_code = CSYNC_STATUS_INVALID_CHARACTERS;
|
||||
|
|
|
@ -156,6 +156,7 @@ std::unique_ptr<csync_file_stat_t> csync_vio_local_readdir(csync_vio_handle_t *d
|
|||
// might be error, check!
|
||||
int dwError = GetLastError();
|
||||
if (dwError != ERROR_NO_MORE_FILES) {
|
||||
qCWarning(lcCSyncVIOLocal, "FindNextFile error %d", dwError);
|
||||
errno = EACCES; // no more files is fine. Otherwise EACCESS
|
||||
}
|
||||
return nullptr;
|
||||
|
|
|
@ -53,6 +53,7 @@ set(client_SRCS
|
|||
folderman.cpp
|
||||
folderstatusmodel.cpp
|
||||
folderstatusdelegate.cpp
|
||||
folderstatusview.cpp
|
||||
folderwatcher.cpp
|
||||
folderwizard.cpp
|
||||
generalsettings.cpp
|
||||
|
|
|
@ -52,7 +52,7 @@ AccountManager *AccountManager::instance()
|
|||
bool AccountManager::restore()
|
||||
{
|
||||
auto settings = ConfigFile::settingsWithGroup(QLatin1String(accountsC));
|
||||
if (settings->status() != QSettings::NoError) {
|
||||
if (settings->status() != QSettings::NoError || !settings->isWritable()) {
|
||||
qCWarning(lcAccountManager) << "Could not read settings from" << settings->fileName()
|
||||
<< settings->status();
|
||||
return false;
|
||||
|
|
|
@ -117,7 +117,7 @@
|
|||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QTreeView" name="_folderList">
|
||||
<widget class="OCC::FolderStatusView" name="_folderList">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
|
@ -275,6 +275,11 @@
|
|||
<extends>QToolButton</extends>
|
||||
<header>sslbutton.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>OCC::FolderStatusView</class>
|
||||
<extends>QTreeView</extends>
|
||||
<header>folderstatusview.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
|
|
@ -125,7 +125,8 @@ Application::Application(int &argc, char **argv)
|
|||
setAttribute(Qt::AA_UseHighDpiPixmaps, true);
|
||||
|
||||
auto confDir = ConfigFile().configPath();
|
||||
if (!QFileInfo(confDir).exists()) {
|
||||
if (confDir.endsWith('/')) confDir.chop(1); // macOS 10.11.x does not like trailing slash for rename/move.
|
||||
if (!QFileInfo(confDir).isDir()) {
|
||||
// Migrate from version <= 2.4
|
||||
setApplicationName(_theme->appNameGUI());
|
||||
#ifndef QT_WARNING_DISABLE_DEPRECATED // Was added in Qt 5.9
|
||||
|
@ -136,6 +137,7 @@ Application::Application(int &argc, char **argv)
|
|||
// We need to use the deprecated QDesktopServices::storageLocation because of its Qt4
|
||||
// behavior of adding "data" to the path
|
||||
QString oldDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
|
||||
if (oldDir.endsWith('/')) oldDir.chop(1); // macOS 10.11.x does not like trailing slash for rename/move.
|
||||
QT_WARNING_POP
|
||||
setApplicationName(_theme->appName());
|
||||
if (QFileInfo(oldDir).isDir()) {
|
||||
|
|
|
@ -645,9 +645,13 @@ void Folder::startSync(const QStringList &pathList)
|
|||
}
|
||||
return interval;
|
||||
}();
|
||||
if (_folderWatcher && _folderWatcher->isReliable() && _timeSinceLastFullLocalDiscovery.isValid()
|
||||
&& (fullLocalDiscoveryInterval.count() < 0
|
||||
|| _timeSinceLastFullLocalDiscovery.hasExpired(fullLocalDiscoveryInterval.count()))) {
|
||||
bool hasDoneFullLocalDiscovery = _timeSinceLastFullLocalDiscovery.isValid();
|
||||
bool periodicFullLocalDiscoveryNow =
|
||||
fullLocalDiscoveryInterval.count() >= 0 // negative means we don't require periodic full runs
|
||||
&& _timeSinceLastFullLocalDiscovery.hasExpired(fullLocalDiscoveryInterval.count());
|
||||
if (_folderWatcher && _folderWatcher->isReliable()
|
||||
&& hasDoneFullLocalDiscovery
|
||||
&& !periodicFullLocalDiscoveryNow) {
|
||||
qCInfo(lcFolder) << "Allowing local discovery to read from the database";
|
||||
_engine->setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem, _localDiscoveryPaths);
|
||||
|
||||
|
|
|
@ -1242,25 +1242,45 @@ QString FolderMan::trayTooltipStatusString(
|
|||
return folderMessage;
|
||||
}
|
||||
|
||||
QString FolderMan::checkPathValidityForNewFolder(const QString &path, const QUrl &serverUrl, bool forNewDirectory) const
|
||||
static QString checkPathValidityRecursive(const QString &path)
|
||||
{
|
||||
if (path.isEmpty()) {
|
||||
return tr("No valid folder selected!");
|
||||
return FolderMan::tr("No valid folder selected!");
|
||||
}
|
||||
|
||||
QFileInfo selFile(path);
|
||||
|
||||
if (!selFile.exists()) {
|
||||
return checkPathValidityForNewFolder(selFile.dir().path(), serverUrl, true);
|
||||
return checkPathValidityRecursive(selFile.dir().path());
|
||||
}
|
||||
|
||||
if (!selFile.isDir()) {
|
||||
return tr("The selected path is not a folder!");
|
||||
return FolderMan::tr("The selected path is not a folder!");
|
||||
}
|
||||
|
||||
if (!selFile.isWritable()) {
|
||||
return tr("You have no permission to write to the selected folder!");
|
||||
return FolderMan::tr("You have no permission to write to the selected folder!");
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
// QFileInfo::canonicalPath returns an empty string if the file does not exist.
|
||||
// This function also works with files that does not exist and resolve the symlinks in the
|
||||
// parent directories.
|
||||
static QString canonicalPath(const QString &path)
|
||||
{
|
||||
QFileInfo selFile(path);
|
||||
if (!selFile.exists()) {
|
||||
return canonicalPath(selFile.dir().path()) + '/' + selFile.fileName();
|
||||
}
|
||||
return selFile.canonicalFilePath();
|
||||
}
|
||||
|
||||
QString FolderMan::checkPathValidityForNewFolder(const QString &path, const QUrl &serverUrl) const
|
||||
{
|
||||
QString recursiveValidity = checkPathValidityRecursive(path);
|
||||
if (!recursiveValidity.isEmpty())
|
||||
return recursiveValidity;
|
||||
|
||||
// check if the local directory isn't used yet in another ownCloud sync
|
||||
Qt::CaseSensitivity cs = Qt::CaseSensitive;
|
||||
|
@ -1268,57 +1288,28 @@ QString FolderMan::checkPathValidityForNewFolder(const QString &path, const QUrl
|
|||
cs = Qt::CaseInsensitive;
|
||||
}
|
||||
|
||||
const QString userDir = QDir::cleanPath(canonicalPath(path)) + '/';
|
||||
for (auto i = _folderMap.constBegin(); i != _folderMap.constEnd(); ++i) {
|
||||
Folder *f = static_cast<Folder *>(i.value());
|
||||
QString folderDir = QDir(f->path()).canonicalPath();
|
||||
if (folderDir.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (!folderDir.endsWith(QLatin1Char('/'), cs))
|
||||
folderDir.append(QLatin1Char('/'));
|
||||
QString folderDir = QDir::cleanPath(canonicalPath(f->path())) + '/';
|
||||
|
||||
const QString folderDirClean = QDir::cleanPath(folderDir) + '/';
|
||||
const QString userDirClean = QDir::cleanPath(path) + '/';
|
||||
|
||||
// folderDir follows sym links, path not.
|
||||
bool differentPathes = !Utility::fileNamesEqual(QDir::cleanPath(folderDir), QDir::cleanPath(path));
|
||||
|
||||
if (!forNewDirectory && differentPathes && folderDirClean.startsWith(userDirClean, cs)) {
|
||||
bool differentPaths = QString::compare(folderDir, userDir, cs) != 0;
|
||||
if (differentPaths && folderDir.startsWith(userDir, cs)) {
|
||||
return tr("The local folder %1 already contains a folder used in a folder sync connection. "
|
||||
"Please pick another one!")
|
||||
.arg(QDir::toNativeSeparators(path));
|
||||
}
|
||||
|
||||
// QDir::cleanPath keeps links
|
||||
// canonicalPath() remove symlinks and uses the symlink targets.
|
||||
QString absCleanUserFolder = QDir::cleanPath(QDir(path).canonicalPath()) + '/';
|
||||
|
||||
if ((forNewDirectory || differentPathes) && userDirClean.startsWith(folderDirClean, cs)) {
|
||||
if (differentPaths && userDir.startsWith(folderDir, cs)) {
|
||||
return tr("The local folder %1 is already contained in a folder used in a folder sync connection. "
|
||||
"Please pick another one!")
|
||||
.arg(QDir::toNativeSeparators(path));
|
||||
}
|
||||
|
||||
// both follow symlinks.
|
||||
bool cleanUserEqualsCleanFolder = Utility::fileNamesEqual(absCleanUserFolder, folderDirClean);
|
||||
if (differentPathes && absCleanUserFolder.startsWith(folderDirClean, cs) && !cleanUserEqualsCleanFolder) {
|
||||
return tr("The local folder %1 is a symbolic link. "
|
||||
"The link target is already contained in a folder used in a folder sync connection. "
|
||||
"Please pick another one!")
|
||||
.arg(QDir::toNativeSeparators(path));
|
||||
}
|
||||
|
||||
if (differentPathes && folderDirClean.startsWith(absCleanUserFolder, cs) && !cleanUserEqualsCleanFolder && !forNewDirectory) {
|
||||
return tr("The local folder %1 contains a symbolic link. "
|
||||
"The link target contains an already synced folder. "
|
||||
"Please pick another one!")
|
||||
.arg(QDir::toNativeSeparators(path));
|
||||
}
|
||||
|
||||
// if both pathes are equal, the server url needs to be different
|
||||
// otherwise it would mean that a new connection from the same local folder
|
||||
// to the same account is added which is not wanted. The account must differ.
|
||||
if (serverUrl.isValid() && Utility::fileNamesEqual(absCleanUserFolder, folderDir)) {
|
||||
if (serverUrl.isValid() && !differentPaths) {
|
||||
QUrl folderUrl = f->accountState()->account()->url();
|
||||
QString user = f->accountState()->account()->credentials()->user();
|
||||
folderUrl.setUserName(user);
|
||||
|
|
|
@ -127,11 +127,9 @@ public:
|
|||
*
|
||||
* Note that different accounts are allowed to sync to the same folder.
|
||||
*
|
||||
* \a forNewDirectory is internal and is used for recursion.
|
||||
*
|
||||
* @returns an empty string if it is allowed, or an error if it is not allowed
|
||||
*/
|
||||
QString checkPathValidityForNewFolder(const QString &path, const QUrl &serverUrl = QUrl(), bool forNewDirectory = false) const;
|
||||
QString checkPathValidityForNewFolder(const QString &path, const QUrl &serverUrl = QUrl()) const;
|
||||
|
||||
/**
|
||||
* Attempts to find a non-existing, acceptable path for creating a new sync folder.
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "folderstatusdelegate.h"
|
||||
#include "folderstatusmodel.h"
|
||||
#include "folderstatusview.h"
|
||||
#include "folderman.h"
|
||||
#include "accountstate.h"
|
||||
#include <theme.h>
|
||||
|
@ -24,6 +25,7 @@
|
|||
#include <QFileIconProvider>
|
||||
#include <QPainter>
|
||||
#include <QApplication>
|
||||
#include <QMouseEvent>
|
||||
|
||||
inline static QFont makeAliasFont(const QFont &normalFont)
|
||||
{
|
||||
|
@ -110,6 +112,10 @@ int FolderStatusDelegate::rootFolderHeightWithoutErrors(const QFontMetrics &fm,
|
|||
void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const
|
||||
{
|
||||
if (index.data(AddButton).toBool()) {
|
||||
const_cast<QStyleOptionViewItem &>(option).showDecorationSelected = false;
|
||||
}
|
||||
|
||||
QStyledItemDelegate::paint(painter, option, index);
|
||||
|
||||
auto textAlign = Qt::AlignLeft;
|
||||
|
@ -129,15 +135,15 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
|||
int margin = subFm.height() / 4;
|
||||
|
||||
if (index.data(AddButton).toBool()) {
|
||||
QSize hint = sizeHint(option, index);
|
||||
QStyleOptionButton opt;
|
||||
static_cast<QStyleOption &>(opt) = option;
|
||||
opt.state &= ~QStyle::State_Selected;
|
||||
opt.state |= QStyle::State_Raised;
|
||||
if (opt.state & QStyle::State_Enabled && opt.state & QStyle::State_MouseOver && index == _pressedIndex) {
|
||||
opt.state |= QStyle::State_Sunken;
|
||||
} else {
|
||||
opt.state |= QStyle::State_Raised;
|
||||
}
|
||||
opt.text = addFolderText();
|
||||
opt.rect.setWidth(qMin(opt.rect.width(), hint.width()));
|
||||
opt.rect.adjust(0, aliasMargin, 0, -aliasMargin);
|
||||
opt.rect = QStyle::visualRect(option.direction, option.rect, opt.rect);
|
||||
opt.rect = addButtonRect(option.rect, option.direction);
|
||||
painter->save();
|
||||
painter->setFont(qApp->font("QPushButton"));
|
||||
QApplication::style()->drawControl(QStyle::CE_PushButton, &opt, painter, option.widget);
|
||||
|
@ -352,6 +358,27 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
|||
bool FolderStatusDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
|
||||
const QStyleOptionViewItem &option, const QModelIndex &index)
|
||||
{
|
||||
switch (event->type()) {
|
||||
case QEvent::MouseButtonPress:
|
||||
case QEvent::MouseMove:
|
||||
if (const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(option.widget)) {
|
||||
QMouseEvent *me = static_cast<QMouseEvent *>(event);
|
||||
QModelIndex index;
|
||||
if (me->buttons()) {
|
||||
index = view->indexAt(me->pos());
|
||||
}
|
||||
if (_pressedIndex != index) {
|
||||
_pressedIndex = index;
|
||||
view->viewport()->update();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case QEvent::MouseButtonRelease:
|
||||
_pressedIndex = QModelIndex();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QStyledItemDelegate::editorEvent(event, model, option, index);
|
||||
}
|
||||
|
||||
|
@ -375,6 +402,16 @@ QRect FolderStatusDelegate::optionsButtonRect(QRect within, Qt::LayoutDirection
|
|||
return QStyle::visualRect(direction, within, r);
|
||||
}
|
||||
|
||||
QRect FolderStatusDelegate::addButtonRect(QRect within, Qt::LayoutDirection direction)
|
||||
{
|
||||
QFontMetrics fm(qApp->font("QPushButton"));
|
||||
QStyleOptionButton opt;
|
||||
opt.text = addFolderText();
|
||||
QSize size = QApplication::style()->sizeFromContents(QStyle::CT_PushButton, &opt, fm.size(Qt::TextSingleLine, opt.text)).expandedTo(QApplication::globalStrut());
|
||||
QRect r(QPoint(within.left(), within.top() + within.height() / 2 - size.height() / 2), size);
|
||||
return QStyle::visualRect(direction, within, r);
|
||||
}
|
||||
|
||||
QRect FolderStatusDelegate::errorsListRect(QRect within)
|
||||
{
|
||||
QFont font = QFont();
|
||||
|
|
|
@ -57,11 +57,13 @@ public:
|
|||
* return the position of the option button within the item
|
||||
*/
|
||||
static QRect optionsButtonRect(QRect within, Qt::LayoutDirection direction);
|
||||
static QRect addButtonRect(QRect within, Qt::LayoutDirection direction);
|
||||
static QRect errorsListRect(QRect within);
|
||||
static int rootFolderHeightWithoutErrors(const QFontMetrics &fm, const QFontMetrics &aliasFm);
|
||||
|
||||
private:
|
||||
static QString addFolderText();
|
||||
QPersistentModelIndex _pressedIndex;
|
||||
};
|
||||
|
||||
} // namespace OCC
|
||||
|
|
42
src/gui/folderstatusview.cpp
Normal file
42
src/gui/folderstatusview.cpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (C) 2018 by J-P Nurmi <jpnurmi@gmail.com>
|
||||
*
|
||||
* 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 "folderstatusview.h"
|
||||
#include "folderstatusdelegate.h"
|
||||
|
||||
namespace OCC {
|
||||
|
||||
FolderStatusView::FolderStatusView(QWidget *parent) : QTreeView(parent)
|
||||
{
|
||||
}
|
||||
|
||||
QModelIndex FolderStatusView::indexAt(const QPoint &point) const
|
||||
{
|
||||
QModelIndex index = QTreeView::indexAt(point);
|
||||
if (index.data(FolderStatusDelegate::AddButton).toBool() && !visualRect(index).contains(point)) {
|
||||
return QModelIndex();
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
QRect FolderStatusView::visualRect(const QModelIndex &index) const
|
||||
{
|
||||
QRect rect = QTreeView::visualRect(index);
|
||||
if (index.data(FolderStatusDelegate::AddButton).toBool()) {
|
||||
return FolderStatusDelegate::addButtonRect(rect, layoutDirection());
|
||||
}
|
||||
return rect;
|
||||
}
|
||||
|
||||
} // namespace OCC
|
39
src/gui/folderstatusview.h
Normal file
39
src/gui/folderstatusview.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (C) 2018 by J-P Nurmi <jpnurmi@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FOLDERSTATUSVIEW_H
|
||||
#define FOLDERSTATUSVIEW_H
|
||||
|
||||
#include <QTreeView>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
/**
|
||||
* @brief The FolderStatusView class
|
||||
* @ingroup gui
|
||||
*/
|
||||
class FolderStatusView : public QTreeView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FolderStatusView(QWidget *parent = nullptr);
|
||||
|
||||
QModelIndex indexAt(const QPoint &point) const override;
|
||||
QRect visualRect(const QModelIndex &index) const override;
|
||||
};
|
||||
|
||||
} // namespace OCC
|
||||
|
||||
#endif // FOLDERSTATUSVIEW_H
|
|
@ -66,8 +66,10 @@ FolderWizardLocalPath::FolderWizardLocalPath(const AccountPtr &account)
|
|||
connect(_ui.localFolderChooseBtn, &QAbstractButton::clicked, this, &FolderWizardLocalPath::slotChooseLocalFolder);
|
||||
_ui.localFolderChooseBtn->setToolTip(tr("Click to select a local folder to sync."));
|
||||
|
||||
QUrl serverUrl = _account->url();
|
||||
serverUrl.setUserName(_account->credentials()->user());
|
||||
QString defaultPath = QDir::homePath() + QLatin1Char('/') + Theme::instance()->appName();
|
||||
defaultPath = FolderMan::instance()->findGoodPathForNewSyncFolder(defaultPath, account->url());
|
||||
defaultPath = FolderMan::instance()->findGoodPathForNewSyncFolder(defaultPath, serverUrl);
|
||||
_ui.localFolderLineEdit->setText(QDir::toNativeSeparators(defaultPath));
|
||||
_ui.localFolderLineEdit->setToolTip(tr("Enter the path to the local folder."));
|
||||
|
||||
|
|
|
@ -631,7 +631,7 @@ void ShareUserLine::displayPermissions()
|
|||
|
||||
// edit is independent of reshare
|
||||
if (perm & SharePermissionShare)
|
||||
_permissionReshare->setChecked(Qt::Checked);
|
||||
_permissionReshare->setChecked(true);
|
||||
|
||||
if(!_isFile){
|
||||
_permissionCreate->setChecked(perm & SharePermissionCreate);
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
*/
|
||||
|
||||
#include <QVariant>
|
||||
#include <QMenu>
|
||||
#include <QClipboard>
|
||||
|
||||
#include "wizard/owncloudoauthcredspage.h"
|
||||
#include "theme.h"
|
||||
|
@ -48,6 +50,16 @@ OwncloudOAuthCredsPage::OwncloudOAuthCredsPage()
|
|||
if (_asyncAuth)
|
||||
_asyncAuth->openBrowser();
|
||||
});
|
||||
_ui.openLinkButton->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
QObject::connect(_ui.openLinkButton, &QWidget::customContextMenuRequested, [this](const QPoint &pos) {
|
||||
auto menu = new QMenu(_ui.openLinkButton);
|
||||
menu->addAction(tr("Copy link to clipboard"), this, [this] {
|
||||
if (_asyncAuth)
|
||||
QApplication::clipboard()->setText(_asyncAuth->authorisationLink().toString(QUrl::FullyEncoded));
|
||||
});
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
menu->popup(_ui.openLinkButton->mapToGlobal(pos));
|
||||
});
|
||||
}
|
||||
|
||||
void OwncloudOAuthCredsPage::initializePage()
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include <QNetworkAccessManager>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QGraphicsPixmapItem>
|
||||
#include <QtSvg/QSvgRenderer>
|
||||
|
||||
#include "QProgressIndicator.h"
|
||||
|
||||
|
@ -278,12 +277,19 @@ QString OwncloudSetupPage::url() const
|
|||
bool OwncloudSetupPage::validatePage()
|
||||
{
|
||||
if (!_authTypeKnown) {
|
||||
QString u = url();
|
||||
QUrl qurl(u);
|
||||
if (!qurl.isValid() || qurl.host().isEmpty()) {
|
||||
setErrorString(tr("Invalid URL"), false);
|
||||
return false;
|
||||
}
|
||||
|
||||
setErrorString(QString(), false);
|
||||
_checking = true;
|
||||
startSpinner();
|
||||
emit completeChanged();
|
||||
|
||||
emit determineAuthType(url());
|
||||
emit determineAuthType(u);
|
||||
return false;
|
||||
} else {
|
||||
// connecting is running
|
||||
|
|
|
@ -41,6 +41,13 @@ class WebEnginePage : public QWebEnginePage {
|
|||
public:
|
||||
WebEnginePage(QWebEngineProfile *profile, QObject* parent = nullptr);
|
||||
QWebEnginePage * createWindow(QWebEnginePage::WebWindowType type) override;
|
||||
void setUrl(const QUrl &url);
|
||||
|
||||
protected:
|
||||
bool certificateError(const QWebEngineCertificateError &certificateError) override;
|
||||
|
||||
private:
|
||||
QUrl _rootUrl;
|
||||
};
|
||||
|
||||
// We need a separate class here, since we cannot simply return the same WebEnginePage object
|
||||
|
@ -146,6 +153,19 @@ QWebEnginePage * WebEnginePage::createWindow(QWebEnginePage::WebWindowType type)
|
|||
return view;
|
||||
}
|
||||
|
||||
void WebEnginePage::setUrl(const QUrl &url) {
|
||||
QWebEnginePage::setUrl(url);
|
||||
_rootUrl = url;
|
||||
}
|
||||
|
||||
bool WebEnginePage::certificateError(const QWebEngineCertificateError &certificateError) {
|
||||
if (certificateError.error() == QWebEngineCertificateError::CertificateAuthorityInvalid) {
|
||||
return certificateError.url().host() == _rootUrl.host();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ExternalWebEnginePage::ExternalWebEnginePage(QWebEngineProfile *profile, QObject* parent) : QWebEnginePage(profile, parent) {
|
||||
|
||||
}
|
||||
|
|
|
@ -379,11 +379,12 @@ bool HttpCredentials::refreshAccessToken()
|
|||
QJsonParseError jsonParseError;
|
||||
QJsonObject json = QJsonDocument::fromJson(jsonData, &jsonParseError).object();
|
||||
QString accessToken = json["access_token"].toString();
|
||||
if (reply->error() != QNetworkReply::NoError || jsonParseError.error != QJsonParseError::NoError || json.isEmpty()) {
|
||||
// Network error maybe?
|
||||
if (jsonParseError.error != QJsonParseError::NoError || json.isEmpty()) {
|
||||
// Invalid or empty JSON: Network error maybe?
|
||||
qCWarning(lcHttpCredentials) << "Error while refreshing the token" << reply->errorString() << jsonData << jsonParseError.errorString();
|
||||
} else if (accessToken.isEmpty()) {
|
||||
// The token is no longer valid.
|
||||
// If the json was valid, but the reply did not contain an access token, the token
|
||||
// is considered expired. (Usually the HTTP reply code is 400)
|
||||
qCDebug(lcHttpCredentials) << "Expired refresh token. Logging out";
|
||||
_refreshToken.clear();
|
||||
} else {
|
||||
|
|
|
@ -381,13 +381,11 @@ void DiscoverySingleDirectoryJob::directoryListingIteratedSlot(QString file, con
|
|||
std::unique_ptr<csync_file_stat_t> file_stat(new csync_file_stat_t);
|
||||
file_stat->path = file.toUtf8();
|
||||
file_stat->size = -1;
|
||||
file_stat->modtime = -1;
|
||||
propertyMapToFileStat(map, file_stat.get());
|
||||
if (file_stat->type == ItemTypeDirectory)
|
||||
file_stat->size = 0;
|
||||
if (file_stat->type == ItemTypeSkip
|
||||
|| file_stat->size == -1
|
||||
|| file_stat->modtime == -1
|
||||
|| file_stat->remotePerm.isNull()
|
||||
|| file_stat->etag.isEmpty()
|
||||
|| file_stat->file_id.isEmpty()) {
|
||||
|
|
|
@ -338,7 +338,8 @@ QString Theme::about() const
|
|||
.arg("http://" MIRALL_STRINGIFY(APPLICATION_DOMAIN))
|
||||
.arg(MIRALL_STRINGIFY(APPLICATION_DOMAIN));
|
||||
|
||||
devString += tr("<p>This release was supplied by the Nextcloud GmbH</p>");
|
||||
devString += tr("<p>This release was supplied by %1</p>")
|
||||
.arg(APPLICATION_VENDOR);
|
||||
|
||||
devString += gitSHA1();
|
||||
|
||||
|
|
|
@ -146,6 +146,12 @@ private slots:
|
|||
|
||||
// Invalid paths
|
||||
QVERIFY(!folderman->checkPathValidityForNewFolder("").isNull());
|
||||
|
||||
|
||||
// REMOVE ownCloud2 from the filesystem, but keep a folder sync'ed to it.
|
||||
QDir(dirPath + "/ownCloud2/").removeRecursively();
|
||||
QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + "/ownCloud2/blublu").isNull());
|
||||
QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + "/ownCloud2/sub/subsub/sub").isNull());
|
||||
}
|
||||
|
||||
void testFindGoodPathForNewSyncFolder()
|
||||
|
@ -169,6 +175,7 @@ private slots:
|
|||
HttpCredentialsTest *cred = new HttpCredentialsTest("testuser", "secret");
|
||||
account->setCredentials(cred);
|
||||
account->setUrl( url );
|
||||
url.setUserName(cred->user());
|
||||
|
||||
AccountStatePtr newAccountState(new AccountState(account));
|
||||
FolderMan *folderman = FolderMan::instance();
|
||||
|
@ -190,6 +197,14 @@ private slots:
|
|||
QString(dirPath + "/ownCloud2/bar"));
|
||||
QCOMPARE(folderman->findGoodPathForNewSyncFolder(dirPath + "/sub", url),
|
||||
QString(dirPath + "/sub2"));
|
||||
|
||||
// REMOVE ownCloud2 from the filesystem, but keep a folder sync'ed to it.
|
||||
// We should still not suggest this folder as a new folder.
|
||||
QDir(dirPath + "/ownCloud2/").removeRecursively();
|
||||
QCOMPARE(folderman->findGoodPathForNewSyncFolder(dirPath + "/ownCloud", url),
|
||||
QString(dirPath + "/ownCloud3"));
|
||||
QCOMPARE(folderman->findGoodPathForNewSyncFolder(dirPath + "/ownCloud2", url),
|
||||
QString(dirPath + "/ownCloud22"));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ signals:
|
|||
void hooked(const QUrl &);
|
||||
public:
|
||||
DesktopServiceHook() { QDesktopServices::setUrlHandler("oauthtest", this, "hooked"); }
|
||||
} desktopServiceHook;
|
||||
};
|
||||
|
||||
static const QUrl sOAuthTestServer("oauthtest://someserver/owncloud");
|
||||
|
||||
|
@ -90,6 +90,7 @@ public:
|
|||
class OAuthTestCase : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
DesktopServiceHook desktopServiceHook;
|
||||
public:
|
||||
enum State { StartState, BrowserOpened, TokenAsked, CustomState } state = StartState;
|
||||
Q_ENUM(State);
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue