1
0
mirror of https://github.com/chylex/Nextcloud-Desktop.git synced 2026-04-04 12:11:33 +02:00

Compare commits

..

71 Commits

Author SHA1 Message Date
Nextcloud bot
6dae188ed9 [tx-robot] updated from transifex 2018-11-05 01:49:43 +00:00
Roeland Jago Douma
2b760cd63b Merge pull request #781 from ivaradi/qt5.5-compat
Qt 5.5 compatibility patch for Xenial
2018-11-04 21:08:44 +01:00
Roeland Jago Douma
54b490c8d1 Merge branch 'master' into qt5.5-compat 2018-11-04 20:41:33 +01:00
Roeland Jago Douma
1e6b0b8924 Merge pull request #780 from jpnurmi/margins
Margins
2018-11-04 20:20:42 +01:00
Roeland Jago Douma
ef15534c64 Merge branch 'master' into margins 2018-11-04 19:40:40 +01:00
István Váradi
ea806a1287 Patch for Xenial to be able to compile with Qt 5.5 2018-11-04 17:28:13 +01:00
Roeland Jago Douma
b904d79d8f Merge pull request #776 from nextcloud/feature/migrate_http_to_webflow
Migrate http auth to webflow
2018-11-04 14:03:13 +01:00
Roeland Jago Douma
0c535872cb Merge branch 'master' into feature/migrate_http_to_webflow 2018-11-04 13:47:04 +01:00
Nextcloud bot
15553df34e [tx-robot] updated from transifex 2018-11-04 01:54:37 +00:00
J-P Nurmi
028979d752 ActivityWidget: remove double margins
To align margins with the account settings tab, so the content doesn't
jump when switching tabs.
2018-11-03 23:03:54 +01:00
J-P Nurmi
1e1d116052 SettingsDialog: remove an unused button box
This makes the bottom margin aligned with horizontal margins, and
eliminates a warning on startup:

    [unknown QObject::connect: Cannot connect (null)::clicked() to OCC::SettingsDialog::accept()
2018-11-03 22:49:48 +01:00
Roeland Jago Douma
045bba0161 Migrate http auth to webflow
This moves all the basic http auth over to the webflow mechanism.
This thus also makes sure that if the password changes a webflow page
pops up. And thus will directly move them over to apptokens then.

Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
2018-11-03 12:37:53 +01:00
Nextcloud bot
b44408a91b [tx-robot] updated from transifex 2018-11-03 01:49:48 +00:00
Roeland Jago Douma
af8a2819be Merge pull request #768 from nextcloud/fix/766/copy_over_old_windows_config
Copy over config file to new location on windows
2018-11-02 21:01:22 +01:00
Roeland Jago Douma
17d05a2ad4 Merge pull request #750 from jpnurmi/slideshow
Setup wizard: implement an animated and interactive slide show
2018-11-02 12:21:07 +01:00
Roeland Jago Douma
0f767ed995 Increase durations a bit
Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
2018-11-02 11:10:57 +01:00
Roeland Jago Douma
e6f1d7632a Merge branch 'master' into slideshow 2018-11-02 10:43:01 +01:00
Roeland Jago Douma
40c36a9ed3 Merge pull request #756 from jpnurmi/add-button
Make the "Add Folder Sync Connection" button act like a button
2018-11-02 10:39:00 +01:00
Roeland Jago Douma
67714d33db Merge pull request #744 from jpnurmi/remove-qtsvg-include
Build fix: remove an unused QtSvg/QSvgRenderer include
2018-11-02 10:31:01 +01:00
Roeland Jago Douma
e01cb108e1 Merge pull request #754 from kevin147147/patch-1
theming for general settings ui
2018-11-02 10:08:18 +01:00
Roeland Jago Douma
3e634dad70 Merge pull request #764 from Awesome-Technologies/user_group_widget
Fix warning in ShareUserGroupWidget
2018-11-02 10:05:14 +01:00
Nextcloud bot
c2d947ee02 [tx-robot] updated from transifex 2018-11-02 01:51:45 +00:00
Roeland Jago Douma
28fe702e8e Merge pull request #769 from nextcloud/l10n/update
Update to translate strings
2018-11-01 16:13:10 +01:00
Roeland Jago Douma
baf4d5f9fc Update to translate strings
Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
2018-11-01 15:22:41 +01:00
Roeland Jago Douma
188a1a6e24 Copy over config file to new location on windows
Fixes #766

Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
2018-11-01 15:02:31 +01:00
Roeland Jago Douma
899fca12ba Merge pull request #721 from storca/patch-1
Inform user that configuration is not writable
2018-11-01 11:44:37 +01:00
Roeland Jago Douma
0f1395fdf0 Merge pull request #758 from nextcloud/self-signed-login-flow
Allow to use the login flow with a self signed certificate
2018-11-01 11:43:54 +01:00
Manuel Stahl
c9f720b68d Fix warning in ShareUserGroupWidget
Change-Id: I63091c67cd56dce241f04ed6badc090bb25cad5f
2018-11-01 10:26:19 +01:00
Roeland Jago Douma
b30a8528bd Merge pull request #645 from nextcloud/upstream/wizard
Wizard enhancement
2018-11-01 09:30:19 +01:00
Roeland Jago Douma
bb4c180c89 Merge branch 'master' into upstream/wizard 2018-11-01 08:32:12 +01:00
Roeland Jago Douma
a23dd9dfb3 Merge pull request #646 from nextcloud/upstream/pr/6656
FolderMan::checkPathValidityForNewFolder: make sure to work when fold…
2018-11-01 08:31:36 +01:00
Roeland Jago Douma
3b5d8f0277 Merge branch 'master' into upstream/pr/6656 2018-11-01 08:07:55 +01:00
Nextcloud bot
feb9a69703 [tx-robot] updated from transifex 2018-11-01 01:50:27 +00:00
Roeland Jago Douma
fd2293b067 Merge pull request #655 from nextcloud/upstream/pr/6671
Do not require server replies to contain an mtime
2018-10-31 22:19:26 +01:00
Roeland Jago Douma
5cfdc71ca5 Merge branch 'master' into upstream/pr/6671 2018-10-31 21:13:10 +01:00
Roeland Jago Douma
d3ee6ed9c5 Merge pull request #651 from nextcloud/upstream/pr/6616
Update: Report on readdir() errors #6610
2018-10-31 21:12:54 +01:00
J-P Nurmi
08879c0f14 Make the "Add Folder Sync Connection" button act like a button 2018-10-31 20:05:14 +01:00
Roeland Jago Douma
b7d0e5672a Merge branch 'master' into upstream/pr/6616 2018-10-31 13:51:34 +01:00
Nextcloud bot
a39d4777fd [tx-robot] updated from transifex 2018-10-31 01:49:53 +00:00
Roeland Jago Douma
cb4450864e Merge pull request #656 from nextcloud/upstream/pr/6672
Settings: Attempt to fix rename issue on old macOS
2018-10-30 23:04:34 +01:00
Roeland Jago Douma
ef3aeb65cc Merge pull request #658 from nextcloud/upstream/pr/6698
OAuth: Fix infinite loop when the refresh token is expired
2018-10-30 23:04:09 +01:00
Roeland Jago Douma
aca75ee982 Merge pull request #403 from nextcloud/upstream/pr/6372
Partial local discovery: Fix scheduling logic
2018-10-30 23:03:16 +01:00
Roeland Jago Douma
326c872ba7 Merge pull request #663 from nextcloud/upstream/pr/6707
Windows: Release handle/fd when file open fails #6699
2018-10-30 22:40:44 +01:00
Roeland Jago Douma
dd39887f2b Merge pull request #641 from nextcloud/upstream/pr/6621
TestOAuth: Don't have global static QObject
2018-10-30 22:38:51 +01:00
Roeland Jago Douma
8df9746005 Merge branch 'master' into upstream/pr/6372 2018-10-30 22:37:30 +01:00
Roeland Jago Douma
668c53a0af Allow to use the login flow with a self signed certificate
The QWebEngine uses a different certificate store/system. So we can't
just pass wour accepted certificates in there.

As a work around we now trust the url we set by definition. As this has
to already be approved before we access this.

Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
2018-10-30 22:23:10 +01:00
kevin147147
30032f9c0c i18n for general settings ui 2018-10-30 17:32:52 +01:00
Nextcloud bot
f2bc5c0482 [tx-robot] updated from transifex 2018-10-30 01:52:44 +00:00
J-P Nurmi
1d3d261e38 Setup wizard: implement an animated and interactive slide show 2018-10-29 19:10:27 +01:00
Nextcloud bot
c40793257d [tx-robot] updated from transifex 2018-10-29 01:51:50 +00:00
Roeland Jago Douma
85b9f94a36 Merge pull request #748 from caugner/qtkeychain-0.9.1
qtkeychain: 0.8.0 -> 0.9.1
2018-10-28 19:45:17 +01:00
Roeland Jago Douma
8688fabee3 Merge branch 'master' into qtkeychain-0.9.1 2018-10-28 19:20:16 +01:00
Nextcloud bot
e88b81c6c9 [tx-robot] updated from transifex 2018-10-27 00:51:13 +00:00
Claas Augner
2344db8937 qtkeychain: 0.8.0 -> 0.9.1 2018-10-26 18:49:03 +02:00
J-P Nurmi
f48aabf344 Build fix: remove an unused QtSvg/QSvgRenderer include
Slipped in in 156605c.
2018-10-25 21:32:43 +02:00
storca
ef8e029cbf Merge branch 'master' into patch-1 2018-10-24 18:10:35 +02:00
Julius Härtl
23883b2b60 Merge branch 'master' into patch-1 2018-10-23 23:11:21 +02:00
storca
4bd3829f78 Merge branch 'master' into patch-1 2018-10-16 21:17:58 +02:00
Camila Ayres
4e3b408c50 Merge branch 'master' into patch-1 2018-10-14 16:14:43 +02:00
storca
620908523e Update accountmanager.cpp
Fixed syntax error
Possible fix for issue #720
2018-10-13 18:45:01 +02:00
storca
8a996ba9a9 Update accountmanager.cpp
Fixed issue #720 by adding the isWrittable method
2018-10-13 18:36:40 +02:00
Christian Kamm
4bd2545dad Windows: Release handle/fd when file open fails #6699 2018-09-10 21:17:20 +02:00
Olivier Goffart
3b2c6d2202 OAuth: Fix infinite loop when the refresh token is expired
The server reply with a code 400 when the token is invalid,
the client was understanding this error as a network error, and was retying
again with the same token.

Instead, we must rely on what the json is saying, even if the reply is
not a 200 code.

Issue https://github.com/owncloud/enterprise/issues/2777
2018-09-10 20:22:40 +02:00
Markus Goetz
109947ba3f Settings: Attempt to fix rename issue on old macOS
Found by @hurradieweltgehtunter
2018-09-10 17:34:48 +02:00
Christian Kamm
5b228d1afd Do not require server replies to contain an mtime
The check was added for #6317 in commit
13eb64584f.

We did see missing mtimes in replies in tests with live servers though.
Possibly those were old incomplete responses cached in the stat cache?
2018-09-10 17:33:53 +02:00
Christian Kamm
3ec4fc6145 Update: Report on readdir() errors #6610 2018-09-10 14:33:36 +02:00
Olivier Goffart
b3e4ec9454 FolderMan::checkPathValidityForNewFolder: make sure to work when folder points to deleted folders
Note that we also needed to adjust the server url to contains the user name
in the folder wizard. (As checkPathValidityForNewFolder expect the user name)

Issue #6654
2018-09-09 22:02:44 +02:00
Olivier Goffart
39c6196487 Wizard: show a message when the URL is invalid
Rather than let Qt show "Host not found"

Issue #6646
2018-09-09 21:47:03 +02:00
Olivier Goffart
986cf448a9 Wizard OAuth2 Page: add a context menu to copy the link
Relates to https://github.com/owncloud/enterprise/issues/2600
2018-09-09 21:46:42 +02:00
Olivier Goffart
7019f03e46 TestOAuth: Don't have global static QObject
Fix a strange warning seen on the log from the CI on Windows in
https://github.com/owncloud/client/pull/6621

The test shows, at the beginning
QObject::connect: No such signal DesktopServiceHook::destroyed(QObject*)
And crashes at the and.

My guess is that when QDesktopServices::setUrlHandler is called, the
QMetaObject is not yet initialized

But this is probably not the reason of the crash
2018-09-09 18:16:59 +02:00
Christian Kamm
797b40237e Partial local discovery: Fix scheduling logic
A recent patch that touched the condition for deciding whether a
periodic full local discovery is necessary accidentally inverted the
condition.
2018-06-06 22:49:50 +02:00
90 changed files with 40326 additions and 41959 deletions

View File

@@ -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 ../ &&

View File

@@ -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

View 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

View 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ó

View File

@@ -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 ../

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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)

View File

@@ -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();

View File

@@ -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/>

View File

@@ -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">

View File

@@ -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()) {

View File

@@ -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();
}
}

View File

@@ -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;
};

View File

@@ -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);

View File

@@ -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);

View File

@@ -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.

View File

@@ -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();

View File

@@ -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

View 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

View 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

View File

@@ -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."));

View File

@@ -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);

View File

@@ -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">

View File

@@ -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);

View File

@@ -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()

View File

@@ -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"/>

View File

@@ -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

View File

@@ -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

View 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

View 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

View File

@@ -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) {
}

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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()) {

View File

@@ -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();

View File

@@ -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"));
}
};

View File

@@ -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);

View File

@@ -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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

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