Compare commits
71 Commits
v2.5.0-rc1
...
v2.5.0-rc2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6dae188ed9 | ||
|
|
2b760cd63b | ||
|
|
54b490c8d1 | ||
|
|
1e6b0b8924 | ||
|
|
ef15534c64 | ||
|
|
ea806a1287 | ||
|
|
b904d79d8f | ||
|
|
0c535872cb | ||
|
|
15553df34e | ||
|
|
028979d752 | ||
|
|
1e1d116052 | ||
|
|
045bba0161 | ||
|
|
b44408a91b | ||
|
|
af8a2819be | ||
|
|
17d05a2ad4 | ||
|
|
0f767ed995 | ||
|
|
e6f1d7632a | ||
|
|
40c36a9ed3 | ||
|
|
67714d33db | ||
|
|
e01cb108e1 | ||
|
|
3e634dad70 | ||
|
|
c2d947ee02 | ||
|
|
28fe702e8e | ||
|
|
baf4d5f9fc | ||
|
|
188a1a6e24 | ||
|
|
899fca12ba | ||
|
|
0f1395fdf0 | ||
|
|
c9f720b68d | ||
|
|
b30a8528bd | ||
|
|
bb4c180c89 | ||
|
|
a23dd9dfb3 | ||
|
|
3b5d8f0277 | ||
|
|
feb9a69703 | ||
|
|
fd2293b067 | ||
|
|
5cfdc71ca5 | ||
|
|
d3ee6ed9c5 | ||
|
|
08879c0f14 | ||
|
|
b7d0e5672a | ||
|
|
a39d4777fd | ||
|
|
cb4450864e | ||
|
|
ef3aeb65cc | ||
|
|
aca75ee982 | ||
|
|
326c872ba7 | ||
|
|
dd39887f2b | ||
|
|
8df9746005 | ||
|
|
668c53a0af | ||
|
|
30032f9c0c | ||
|
|
f2bc5c0482 | ||
|
|
1d3d261e38 | ||
|
|
c40793257d | ||
|
|
85b9f94a36 | ||
|
|
8688fabee3 | ||
|
|
e88b81c6c9 | ||
|
|
2344db8937 | ||
|
|
f48aabf344 | ||
|
|
ef8e029cbf | ||
|
|
23883b2b60 | ||
|
|
4bd3829f78 | ||
|
|
4e3b408c50 | ||
|
|
620908523e | ||
|
|
8a996ba9a9 | ||
|
|
4bd2545dad | ||
|
|
3b2c6d2202 | ||
|
|
109947ba3f | ||
|
|
5b228d1afd | ||
|
|
3ec4fc6145 | ||
|
|
b3e4ec9454 | ||
|
|
39c6196487 | ||
|
|
986cf448a9 | ||
|
|
7019f03e46 | ||
|
|
797b40237e |
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 ../ &&
|
||||
|
||||
@@ -198,7 +198,7 @@ X-GNOME-Autostart-Delay=3
|
||||
|
||||
|
||||
# Translations
|
||||
Comment[de]=@APPLICATION_NAME@ Client zur Desktop-Synchronisation
|
||||
Icon[de]=@APPLICATION_ICON_NAME@
|
||||
Name[de]=@APPLICATION_NAME@ Client zur Desktop-Synchronisation
|
||||
GenericName[de]=Synchronisationsordner
|
||||
Comment[de_DE]=@APPLICATION_NAME@ Client zur Desktop-Synchronisation
|
||||
Icon[de_DE]=@APPLICATION_ICON_NAME@
|
||||
Name[de_DE]=@APPLICATION_NAME@ Client zur Desktop-Synchronisation
|
||||
GenericName[de_DE]=Synchronisationsordner
|
||||
|
||||
204
.tx/nextcloud.client-desktop/gl_translation
Normal file
@@ -0,0 +1,204 @@
|
||||
[Desktop Entry]
|
||||
Categories=Utility;X-SuSE-SyncUtility;
|
||||
Type=Application
|
||||
Exec=@APPLICATION_EXECUTABLE@
|
||||
Name=@APPLICATION_NAME@ desktop sync client
|
||||
Comment=@APPLICATION_NAME@ desktop synchronization client
|
||||
GenericName=Folder Sync
|
||||
Icon=@APPLICATION_ICON_NAME@
|
||||
Keywords=@APPLICATION_NAME@;syncing;file;sharing;
|
||||
X-GNOME-Autostart-Delay=3
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
Comment[gl]=@APPLICATION_NAME@ cliente de sincronización para escritorio
|
||||
Icon[gl]=@APPLICATION_ICON_NAME@
|
||||
Name[gl]=@APPLICATION_NAME@ cliente de sincr escritorio
|
||||
GenericName[gl]=Sincr. de cartafol
|
||||
204
.tx/nextcloud.client-desktop/hu_translation
Normal file
@@ -0,0 +1,204 @@
|
||||
[Desktop Entry]
|
||||
Categories=Utility;X-SuSE-SyncUtility;
|
||||
Type=Application
|
||||
Exec=@APPLICATION_EXECUTABLE@
|
||||
Name=@APPLICATION_NAME@ desktop sync client
|
||||
Comment=@APPLICATION_NAME@ desktop synchronization client
|
||||
GenericName=Folder Sync
|
||||
Icon=@APPLICATION_ICON_NAME@
|
||||
Keywords=@APPLICATION_NAME@;syncing;file;sharing;
|
||||
X-GNOME-Autostart-Delay=3
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
Comment[hu_HU]=@APPLICATION_NAME@ deszktop szinkronizációs kliens
|
||||
Icon[hu_HU]=@APPLICATION_ICON_NAME@
|
||||
Name[hu_HU]=@APPLICATION_NAME@ deszktop szinkronizációs kliens
|
||||
GenericName[hu_HU]=Mappa szinkronizáció
|
||||
@@ -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 ../
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
--- nextcloud-client-2.4.0.orig/src/gui/wizard/owncloudoauthcredspage.cpp
|
||||
+++ nextcloud-client-2.4.0/src/gui/wizard/owncloudoauthcredspage.cpp
|
||||
@@ -53,10 +53,8 @@ OwncloudOAuthCredsPage::OwncloudOAuthCredsPage()
|
||||
_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));
|
||||
- });
|
||||
+ auto action = menu->addAction(tr("Copy link to clipboard"));
|
||||
+ connect(action, &QAction::triggered, this, &OwncloudOAuthCredsPage::copyLinkToClipboard);
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
menu->popup(_ui.openLinkButton->mapToGlobal(pos));
|
||||
});
|
||||
@@ -131,4 +129,11 @@ bool OwncloudOAuthCredsPage::isComplete() const
|
||||
return false; /* We can never go forward manually */
|
||||
}
|
||||
|
||||
+void OwncloudOAuthCredsPage::copyLinkToClipboard()
|
||||
+{
|
||||
+ if (_asyncAuth)
|
||||
+ QApplication::clipboard()->setText(_asyncAuth->authorisationLink().toString(QUrl::FullyEncoded));
|
||||
+}
|
||||
+
|
||||
+
|
||||
} // namespace OCC
|
||||
--- nextcloud-client-2.4.0.orig/src/gui/wizard/owncloudoauthcredspage.h
|
||||
+++ nextcloud-client-2.4.0/src/gui/wizard/owncloudoauthcredspage.h
|
||||
@@ -57,6 +57,10 @@ public:
|
||||
QString _refreshToken;
|
||||
QScopedPointer<OAuth> _asyncAuth;
|
||||
Ui_OwncloudOAuthCredsPage _ui;
|
||||
+
|
||||
+protected slots:
|
||||
+ void copyLinkToClipboard();
|
||||
+
|
||||
};
|
||||
|
||||
} // namespace OCC
|
||||
@@ -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
|
||||
@@ -116,6 +117,7 @@ set(client_SRCS
|
||||
wizard/owncloudwizardresultpage.cpp
|
||||
wizard/webviewpage.cpp
|
||||
wizard/webview.cpp
|
||||
wizard/slideshow.cpp
|
||||
)
|
||||
|
||||
IF(NOT NO_SHIBBOLETH)
|
||||
|
||||
@@ -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;
|
||||
@@ -253,6 +253,20 @@ AccountPtr AccountManager::loadAccountHelper(QSettings &settings)
|
||||
acc->setUrl(urlConfig.toUrl());
|
||||
}
|
||||
|
||||
// Migrate to webflow
|
||||
if (authType == QLatin1String("http")) {
|
||||
authType = "webflow";
|
||||
settings.setValue(QLatin1String(authTypeC), authType);
|
||||
|
||||
foreach(QString key, settings.childKeys()) {
|
||||
if (!key.startsWith("http_"))
|
||||
continue;
|
||||
auto newkey = QString::fromLatin1("webflow_").append(key.mid(5));
|
||||
settings.setValue(newkey, settings.value((key)));
|
||||
settings.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
qCInfo(lcAccountManager) << "Account for" << acc->url() << "using auth type" << authType;
|
||||
|
||||
acc->_serverVersion = settings.value(QLatin1String(serverVersionC)).toString();
|
||||
|
||||
@@ -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/>
|
||||
|
||||
@@ -81,6 +81,18 @@
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QListView" name="_activityList">
|
||||
<property name="sizePolicy">
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -54,8 +54,9 @@ private:
|
||||
};
|
||||
|
||||
WebFlowCredentials::WebFlowCredentials()
|
||||
: _ready(false),
|
||||
_credentialsValid(false)
|
||||
: _ready(false)
|
||||
, _credentialsValid(false)
|
||||
, _keychainMigration(false)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -67,6 +68,7 @@ WebFlowCredentials::WebFlowCredentials(const QString &user, const QString &passw
|
||||
, _clientSslCertificate(certificate)
|
||||
, _ready(true)
|
||||
, _credentialsValid(true)
|
||||
, _keychainMigration(false)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -260,7 +262,7 @@ void WebFlowCredentials::fetchFromKeychainHelper() {
|
||||
const QString kck = keychainKey(
|
||||
_account->url().toString(),
|
||||
_user,
|
||||
_account->id());
|
||||
_keychainMigration ? QString() : _account->id());
|
||||
|
||||
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
|
||||
job->setInsecureFallback(false);
|
||||
@@ -273,6 +275,13 @@ void WebFlowCredentials::slotReadPasswordJobDone(Job *incomingJob) {
|
||||
QKeychain::ReadPasswordJob *job = static_cast<ReadPasswordJob *>(incomingJob);
|
||||
QKeychain::Error error = job->error();
|
||||
|
||||
// If we could not find the entry try the old entries
|
||||
if (!_keychainMigration && error == QKeychain::EntryNotFound) {
|
||||
_keychainMigration = true;
|
||||
fetchFromKeychainHelper();
|
||||
return;
|
||||
}
|
||||
|
||||
if (error == QKeychain::NoError) {
|
||||
_password = job->textData();
|
||||
_ready = true;
|
||||
@@ -280,8 +289,22 @@ void WebFlowCredentials::slotReadPasswordJobDone(Job *incomingJob) {
|
||||
} else {
|
||||
_ready = false;
|
||||
}
|
||||
|
||||
emit fetched();
|
||||
|
||||
// If keychain data was read from legacy location, wipe these entries and store new ones
|
||||
if (_keychainMigration && _ready) {
|
||||
_keychainMigration = false;
|
||||
persist();
|
||||
deleteOldKeychainEntries();
|
||||
qCWarning(lcWebFlowCredentials) << "Migrated old keychain entries";
|
||||
}
|
||||
}
|
||||
|
||||
void WebFlowCredentials::deleteOldKeychainEntries() {
|
||||
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
|
||||
job->setInsecureFallback(false);
|
||||
job->setKey(keychainKey(_account->url().toString(), _user, QString()));
|
||||
job->start();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -52,14 +52,8 @@ private slots:
|
||||
void slotAskFromUserCredentialsProvided(const QString &user, const QString &pass, const QString &host);
|
||||
|
||||
private:
|
||||
/** Reads data from keychain locations
|
||||
*
|
||||
* Goes through
|
||||
* slotReadClientCertPEMJobDone to
|
||||
* slotReadClientCertPEMJobDone to
|
||||
* slotReadJobDone
|
||||
*/
|
||||
void fetchFromKeychainHelper();
|
||||
void deleteOldKeychainEntries();
|
||||
|
||||
QString fetchUser();
|
||||
|
||||
@@ -70,6 +64,7 @@ private:
|
||||
|
||||
bool _ready;
|
||||
bool _credentialsValid;
|
||||
bool _keychainMigration;
|
||||
|
||||
WebFlowCredentialsDialog *_askDialog;
|
||||
};
|
||||
|
||||
@@ -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
@@ -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
@@ -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."));
|
||||
|
||||
|
||||
@@ -122,9 +122,6 @@ SettingsDialog::SettingsDialog(ownCloudGui *gui, QWidget *parent)
|
||||
|
||||
QTimer::singleShot(1, this, &SettingsDialog::showFirstPage);
|
||||
|
||||
QPushButton *closeButton = _ui->buttonBox->button(QDialogButtonBox::Close);
|
||||
connect(closeButton, SIGNAL(clicked()), SLOT(accept()));
|
||||
|
||||
QAction *showLogWindow = new QAction(this);
|
||||
showLogWindow->setShortcut(QKeySequence("F12"));
|
||||
connect(showLogWindow, &QAction::triggered, gui, &ownCloudGui::slotToggleLogBrowser);
|
||||
|
||||
@@ -29,28 +29,6 @@
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::NoButton</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QStackedWidget" name="stack">
|
||||
<property name="palette">
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -190,28 +190,6 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="slideImage">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../../../theme.qrc">:/client/theme/colored/wizard-files.svg</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<spacer name="verticalSpacer_4">
|
||||
<property name="orientation">
|
||||
@@ -229,7 +207,7 @@
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="slideLabel">
|
||||
<widget class="OCC::SlideShow" name="slideShow">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>12</pointsize>
|
||||
@@ -237,12 +215,6 @@
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>SlideshowLabel</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
@@ -437,6 +409,11 @@
|
||||
<extends>QLineEdit</extends>
|
||||
<header>wizard/postfixlineedit.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>OCC::SlideShow</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>wizard/slideshow.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../../../theme.qrc"/>
|
||||
|
||||
@@ -24,13 +24,13 @@
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QGraphicsPixmapItem>
|
||||
#include <QtSvg/QSvgRenderer>
|
||||
|
||||
#include "QProgressIndicator.h"
|
||||
|
||||
#include "wizard/owncloudwizardcommon.h"
|
||||
#include "wizard/owncloudsetuppage.h"
|
||||
#include "wizard/owncloudconnectionmethoddialog.h"
|
||||
#include "wizard/slideshow.h"
|
||||
#include "theme.h"
|
||||
#include "account.h"
|
||||
#include "config.h"
|
||||
@@ -80,18 +80,16 @@ OwncloudSetupPage::OwncloudSetupPage(QWidget *parent)
|
||||
connect(_ui.createAccountButton, &QPushButton::clicked, this, &OwncloudSetupPage::slotGotoProviderList);
|
||||
|
||||
_ui.login->hide();
|
||||
_slideshow.append(qMakePair(QString("nextcloud"), tr("Keep your data secure and under your control")));
|
||||
_slideshow.append(qMakePair(QString("files"), tr("Secure collaboration & file exchange")));
|
||||
_slideshow.append(qMakePair(QString("groupware"), tr("Easy-to-use web mail, calendaring & contacts")));
|
||||
_slideshow.append(qMakePair(QString("talk"), tr("Screensharing, online meetings & web conferences")));
|
||||
_ui.slideShow->addSlide(Theme::hidpiFileName(":/client/theme/colored/wizard-nextcloud.png"), tr("Keep your data secure and under your control"));
|
||||
_ui.slideShow->addSlide(Theme::hidpiFileName(":/client/theme/colored/wizard-files.png"), tr("Secure collaboration & file exchange"));
|
||||
_ui.slideShow->addSlide(Theme::hidpiFileName(":/client/theme/colored/wizard-groupware.png"), tr("Easy-to-use web mail, calendaring & contacts"));
|
||||
_ui.slideShow->addSlide(Theme::hidpiFileName(":/client/theme/colored/wizard-talk.png"), tr("Screensharing, online meetings & web conferences"));
|
||||
connect(_ui.slideShow, &SlideShow::clicked, _ui.slideShow, &SlideShow::nextSlide);
|
||||
_ui.slideShow->startShow(2500);
|
||||
|
||||
_ui.slideLabel->setStyleSheet(QString("color:%1;").arg(theme->wizardHeaderBackgroundColor().name()));
|
||||
_currentSlide = -1;
|
||||
nextSlide();
|
||||
|
||||
QTimer *timer = new QTimer(this);
|
||||
connect(timer, SIGNAL(timeout()), this, SLOT(nextSlide()));
|
||||
timer->start(2500);
|
||||
QPalette pal = _ui.slideShow->palette();
|
||||
pal.setColor(QPalette::WindowText, theme->wizardHeaderBackgroundColor());
|
||||
_ui.slideShow->setPalette(pal);
|
||||
#else
|
||||
_ui.createAccountButton->hide();
|
||||
_ui.slideImage->hide();
|
||||
@@ -102,20 +100,6 @@ OwncloudSetupPage::OwncloudSetupPage(QWidget *parent)
|
||||
setStyleSheet(QString("background-color:%1; color:%2 QLabel { color:%2; } QSpacerItem { color: red; }").arg(theme->wizardHeaderBackgroundColor().name(), theme->wizardHeaderTitleColor().name()));
|
||||
}
|
||||
|
||||
#ifdef WITH_PROVIDERS
|
||||
void OwncloudSetupPage::nextSlide()
|
||||
{
|
||||
if (_currentSlide < _slideshow.length() - 1) {
|
||||
_currentSlide++;
|
||||
} else {
|
||||
_currentSlide = 0;
|
||||
}
|
||||
QPixmap pixmap = QIcon(Theme::hidpiFileName(":/client/theme/colored/wizard-" + _slideshow.at(_currentSlide).first + ".svg")).pixmap(QSize(1024, 1024));
|
||||
_ui.slideImage->setPixmap(pixmap.scaled(QSize(_ui.slideImage->size().height(), _ui.slideImage->size().height()), Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||
_ui.slideLabel->setText(_slideshow.at(_currentSlide).second);
|
||||
}
|
||||
#endif
|
||||
|
||||
void OwncloudSetupPage::setServerUrl(const QString &newUrl)
|
||||
{
|
||||
_ocWizard->setRegistration(false);
|
||||
@@ -293,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
|
||||
|
||||
@@ -62,9 +62,6 @@ public slots:
|
||||
void startSpinner();
|
||||
void stopSpinner();
|
||||
void slotCertificateAccepted();
|
||||
#ifdef WITH_PROVIDERS
|
||||
void nextSlide();
|
||||
#endif
|
||||
|
||||
protected slots:
|
||||
void slotUrlChanged(const QString &);
|
||||
@@ -96,10 +93,6 @@ private:
|
||||
QString _remoteFolder;
|
||||
AddCertificateDialog *addCertDial;
|
||||
OwncloudWizard *_ocWizard;
|
||||
|
||||
QList<QPair<QString, QString>> _slideshow;
|
||||
int _currentSlide;
|
||||
|
||||
};
|
||||
|
||||
} // namespace OCC
|
||||
|
||||
193
src/gui/wizard/slideshow.cpp
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* 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 "slideshow.h"
|
||||
#include <QGuiApplication>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QStyle>
|
||||
#include <QStyleHints>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
static const int Spacing = 6;
|
||||
static const int SlideDuration = 1000;
|
||||
static const int SlideDistance = 400;
|
||||
|
||||
SlideShow::SlideShow(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
}
|
||||
|
||||
void SlideShow::addSlide(const QPixmap &pixmap, const QString &label)
|
||||
{
|
||||
_labels += label;
|
||||
_pixmaps += pixmap;
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
bool SlideShow::isActive() const
|
||||
{
|
||||
return _timer.isActive();
|
||||
}
|
||||
|
||||
int SlideShow::interval() const
|
||||
{
|
||||
return _interval;
|
||||
}
|
||||
|
||||
void SlideShow::setInterval(int interval)
|
||||
{
|
||||
if (_interval == interval)
|
||||
return;
|
||||
|
||||
_interval = interval;
|
||||
maybeRestartTimer();
|
||||
}
|
||||
|
||||
int SlideShow::currentSlide() const
|
||||
{
|
||||
return _currentIndex;
|
||||
}
|
||||
|
||||
void SlideShow::setCurrentSlide(int index)
|
||||
{
|
||||
if (_currentIndex == index)
|
||||
return;
|
||||
|
||||
if (!_animation) {
|
||||
_animation = new QVariantAnimation(this);
|
||||
_animation->setDuration(SlideDuration);
|
||||
_animation->setEasingCurve(QEasingCurve::OutCubic);
|
||||
_animation->setStartValue(static_cast<qreal>(_currentIndex));
|
||||
connect(_animation.data(), SIGNAL(valueChanged(QVariant)), this, SLOT(update()));
|
||||
}
|
||||
_animation->setEndValue(static_cast<qreal>(index));
|
||||
_animation->start(QAbstractAnimation::DeleteWhenStopped);
|
||||
|
||||
_reverse = index < _currentIndex;
|
||||
_currentIndex = index;
|
||||
maybeRestartTimer();
|
||||
update();
|
||||
emit currentSlideChanged(index);
|
||||
}
|
||||
|
||||
QSize SlideShow::sizeHint() const
|
||||
{
|
||||
QFontMetrics fm = fontMetrics();
|
||||
QSize labelSize(0, fm.height());
|
||||
for (const QString &label : _labels) {
|
||||
labelSize.setWidth(std::max(fm.width(label), labelSize.width()));
|
||||
}
|
||||
QSize pixmapSize;
|
||||
for (const QPixmap &pixmap : _pixmaps) {
|
||||
pixmapSize.setWidth(std::max(pixmap.width(), pixmapSize.width()));
|
||||
pixmapSize.setHeight(std::max(pixmap.height(), pixmapSize.height()));
|
||||
}
|
||||
return QSize(std::max(labelSize.width(), pixmapSize.width()), labelSize.height() + Spacing + pixmapSize.height());
|
||||
}
|
||||
|
||||
void SlideShow::startShow(int interval)
|
||||
{
|
||||
if (interval > 0)
|
||||
_interval = interval;
|
||||
_timer.start(_interval, this);
|
||||
}
|
||||
|
||||
void SlideShow::stopShow()
|
||||
{
|
||||
_timer.stop();
|
||||
}
|
||||
|
||||
void SlideShow::nextSlide()
|
||||
{
|
||||
setCurrentSlide((_currentIndex + 1) % _labels.count());
|
||||
_reverse = false;
|
||||
}
|
||||
|
||||
void SlideShow::previousSlide()
|
||||
{
|
||||
setCurrentSlide((_currentIndex > 0 ? _currentIndex : _labels.count()) - 1);
|
||||
_reverse = true;
|
||||
}
|
||||
|
||||
void SlideShow::reset()
|
||||
{
|
||||
stopShow();
|
||||
_pixmaps.clear();
|
||||
_labels.clear();
|
||||
updateGeometry();
|
||||
update();
|
||||
}
|
||||
|
||||
void SlideShow::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
_pressPoint = event->pos();
|
||||
}
|
||||
|
||||
void SlideShow::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (QLineF(_pressPoint, event->pos()).length() < QGuiApplication::styleHints()->startDragDistance())
|
||||
emit clicked();
|
||||
}
|
||||
|
||||
void SlideShow::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
||||
if (_animation) {
|
||||
int from = _animation->startValue().toInt();
|
||||
int to = _animation->endValue().toInt();
|
||||
qreal progress = _animation->easingCurve().valueForProgress(_animation->currentTime() / static_cast<qreal>(_animation->duration()));
|
||||
|
||||
painter.save();
|
||||
painter.setOpacity(1.0 - progress);
|
||||
painter.translate(progress * (_reverse ? SlideDistance : -SlideDistance), 0);
|
||||
drawSlide(&painter, from);
|
||||
|
||||
painter.restore();
|
||||
painter.setOpacity(progress);
|
||||
painter.translate((1.0 - progress) * (_reverse ? -SlideDistance : SlideDistance), 0);
|
||||
drawSlide(&painter, to);
|
||||
} else {
|
||||
drawSlide(&painter, _currentIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void SlideShow::timerEvent(QTimerEvent *event)
|
||||
{
|
||||
if (event->timerId() == _timer.timerId())
|
||||
nextSlide();
|
||||
}
|
||||
|
||||
void SlideShow::maybeRestartTimer()
|
||||
{
|
||||
if (!isActive())
|
||||
return;
|
||||
|
||||
startShow();
|
||||
}
|
||||
|
||||
void SlideShow::drawSlide(QPainter *painter, int index)
|
||||
{
|
||||
QString label = _labels.value(index);
|
||||
QRect labelRect = style()->itemTextRect(fontMetrics(), rect(), Qt::AlignBottom | Qt::AlignHCenter, isEnabled(), label);
|
||||
style()->drawItemText(painter, labelRect, Qt::AlignCenter, palette(), isEnabled(), label, QPalette::WindowText);
|
||||
|
||||
QPixmap pixmap = _pixmaps.value(index);
|
||||
QRect pixmapRect = style()->itemPixmapRect(QRect(0, 0, width(), labelRect.top() - Spacing), Qt::AlignCenter, pixmap);
|
||||
style()->drawItemPixmap(painter, pixmapRect, Qt::AlignCenter, pixmap);
|
||||
}
|
||||
|
||||
} // namespace OCC
|
||||
83
src/gui/wizard/slideshow.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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 OCC_SLIDESHOW_H
|
||||
#define OCC_SLIDESHOW_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QBasicTimer>
|
||||
#include <QPointer>
|
||||
#include <QVariantAnimation>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
/**
|
||||
* @brief The SlideShow class
|
||||
* @ingroup gui
|
||||
*/
|
||||
class SlideShow : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int interval READ interval WRITE setInterval)
|
||||
Q_PROPERTY(int currentSlide READ currentSlide WRITE setCurrentSlide NOTIFY currentSlideChanged)
|
||||
|
||||
public:
|
||||
explicit SlideShow(QWidget* parent = nullptr);
|
||||
|
||||
void addSlide(const QPixmap &pixmap, const QString &label);
|
||||
|
||||
bool isActive() const;
|
||||
|
||||
int interval() const;
|
||||
void setInterval(int interval);
|
||||
|
||||
int currentSlide() const;
|
||||
void setCurrentSlide(int index);
|
||||
|
||||
QSize sizeHint() const override;
|
||||
|
||||
public slots:
|
||||
void startShow(int interval = 0);
|
||||
void stopShow();
|
||||
void nextSlide();
|
||||
void previousSlide();
|
||||
void reset();
|
||||
|
||||
signals:
|
||||
void clicked();
|
||||
void currentSlideChanged(int index);
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *event);
|
||||
void mouseReleaseEvent(QMouseEvent *event);
|
||||
void paintEvent(QPaintEvent *event);
|
||||
void timerEvent(QTimerEvent *event);
|
||||
|
||||
private:
|
||||
void maybeRestartTimer();
|
||||
void drawSlide(QPainter *painter, int index);
|
||||
|
||||
bool _reverse = false;
|
||||
int _interval = 2500;
|
||||
int _currentIndex = 0;
|
||||
QPoint _pressPoint;
|
||||
QBasicTimer _timer;
|
||||
QStringList _labels;
|
||||
QVector<QPixmap> _pixmaps;
|
||||
QPointer<QVariantAnimation> _animation = nullptr;
|
||||
};
|
||||
|
||||
} // namespace OCC
|
||||
|
||||
#endif // OCC_SLIDESHOW_H
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
|
||||
@@ -96,6 +96,40 @@ static chrono::milliseconds millisecondsValue(const QSettings &setting, const ch
|
||||
return chrono::milliseconds(setting.value(QLatin1String(key), qlonglong(defaultValue.count())).toLongLong());
|
||||
}
|
||||
|
||||
|
||||
bool copy_dir_recursive(QString from_dir, QString to_dir)
|
||||
{
|
||||
QDir dir;
|
||||
dir.setPath(from_dir);
|
||||
|
||||
from_dir += QDir::separator();
|
||||
to_dir += QDir::separator();
|
||||
|
||||
foreach (QString copy_file, dir.entryList(QDir::Files)) {
|
||||
QString from = from_dir + copy_file;
|
||||
QString to = to_dir + copy_file;
|
||||
|
||||
if (QFile::copy(from, to) == false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (QString copy_dir, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
|
||||
QString from = from_dir + copy_dir;
|
||||
QString to = to_dir + copy_dir;
|
||||
|
||||
if (dir.mkpath(to) == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (copy_dir_recursive(from, to) == false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ConfigFile::ConfigFile()
|
||||
{
|
||||
// QDesktopServices uses the application name to create a config path
|
||||
@@ -262,9 +296,26 @@ QVariant ConfigFile::getPolicySetting(const QString &setting, const QVariant &de
|
||||
QString ConfigFile::configPath() const
|
||||
{
|
||||
if (_confDir.isEmpty()) {
|
||||
// On Unix, use the AppConfigLocation for the settings, that's configurable with the XDG_CONFIG_HOME env variable.
|
||||
// On Windows, use AppDataLocation, that's where the roaming data is and where we should store the config file
|
||||
_confDir = QStandardPaths::writableLocation(Utility::isWindows() ? QStandardPaths::AppDataLocation : QStandardPaths::AppConfigLocation);
|
||||
if (!Utility::isWindows()) {
|
||||
// On Unix, use the AppConfigLocation for the settings, that's configurable with the XDG_CONFIG_HOME env variable.
|
||||
_confDir = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
|
||||
} else {
|
||||
// On Windows, use AppDataLocation, that's where the roaming data is and where we should store the config file
|
||||
auto newLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||
|
||||
// Check if this is the first time loading the new location
|
||||
if (!QFileInfo(newLocation).isDir()) {
|
||||
// Migrate data to the new locations
|
||||
auto oldLocation = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
|
||||
|
||||
// Only migrate if the old location exists.
|
||||
if (QFileInfo(oldLocation).isDir()) {
|
||||
QDir().mkpath(newLocation);
|
||||
copy_dir_recursive(oldLocation, newLocation);
|
||||
}
|
||||
}
|
||||
_confDir = newLocation;
|
||||
}
|
||||
}
|
||||
QString dir = _confDir;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
12
theme.qrc
@@ -117,9 +117,13 @@
|
||||
<file>theme/white/state-warning-64.png</file>
|
||||
<file>theme/white/state-warning-128.png</file>
|
||||
<file>theme/white/state-warning-256.png</file>
|
||||
<file>theme/colored/wizard-files.svg</file>
|
||||
<file>theme/colored/wizard-groupware.svg</file>
|
||||
<file>theme/colored/wizard-nextcloud.svg</file>
|
||||
<file>theme/colored/wizard-talk.svg</file>
|
||||
<file>theme/colored/wizard-files.png</file>
|
||||
<file>theme/colored/wizard-files@2x.png</file>
|
||||
<file>theme/colored/wizard-groupware.png</file>
|
||||
<file>theme/colored/wizard-groupware@2x.png</file>
|
||||
<file>theme/colored/wizard-nextcloud.png</file>
|
||||
<file>theme/colored/wizard-nextcloud@2x.png</file>
|
||||
<file>theme/colored/wizard-talk.png</file>
|
||||
<file>theme/colored/wizard-talk@2x.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
BIN
theme/colored/wizard-files.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
theme/colored/wizard-files@2x.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
theme/colored/wizard-groupware.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
theme/colored/wizard-groupware@2x.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
theme/colored/wizard-nextcloud.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
theme/colored/wizard-nextcloud@2x.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
theme/colored/wizard-talk.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
theme/colored/wizard-talk@2x.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |