1
0
mirror of https://github.com/chylex/Nextcloud-Desktop.git synced 2026-04-10 09:57:41 +02:00

Compare commits

..

2 Commits

Author SHA1 Message Date
Camila Ayres
477614b8c9 Merge pull request #1355 from nextcloud/backport/1354/stable-2.5.3
[stable-2.5.3] Minor text change in the link to help in the tab 'General'.

Signed-off-by: Camila San <hello@camila.codes>
2019-07-25 12:20:49 +02:00
Camila San
9d060e5e55 Minor text change in the link to help in the tab 'General'.
Also uses helpUrl() retrieve the correct APPLICATION_HELP_URL
instead of APPLICATION_DOMAIN.

Signed-off-by: Camila San <hello@camila.codes>
2019-07-24 17:08:31 +00:00
272 changed files with 36551 additions and 52589 deletions

View File

@@ -237,17 +237,13 @@ name: qt-5.12
steps: steps:
- name: build and test - name: build and test
image: nextcloudci/client-5.12:client-5.12-5 image: nextcloudci/client-5.12:client-5.12-2
commands: commands:
# Install QtKeyChain # Install QtKeyChain
- /bin/bash -c " - /bin/bash -c "
export CC=gcc-7 && export CC=gcc-7 &&
export CXX=g++-7 && export CXX=g++-7 &&
export QT_BASE_DIR=/opt/qt5.12.5 && source /opt/qt512/bin/qt512-env.sh &&
export QTDIR=\$QT_BASE_DIR &&
export PATH=\$QT_BASE_DIR/bin:\$PATH &&
export LD_LIBRARY_PATH=\$QT_BASE_DIR/lib/x86_64-linux-gnu:\$QT_BASE_DIR/lib:/usr/local/lib:\$LD_LIBRARY_PATH &&
export PKG_CONFIG_PATH=\$QT_BASE_DIR/lib/pkgconfig:\$PKG_CONFIG_PATH &&
cd /tmp && cd /tmp &&
git clone https://github.com/frankosterfeld/qtkeychain.git && git clone https://github.com/frankosterfeld/qtkeychain.git &&
cd qtkeychain && cd qtkeychain &&
@@ -261,11 +257,7 @@ steps:
- /bin/bash -c " - /bin/bash -c "
export CC=gcc-7 && export CC=gcc-7 &&
export CXX=g++-7 && export CXX=g++-7 &&
export QT_BASE_DIR=/opt/qt5.12.5 && source /opt/qt512/bin/qt512-env.sh &&
export QTDIR=\$QT_BASE_DIR &&
export PATH=\$QT_BASE_DIR/bin:\$PATH &&
export LD_LIBRARY_PATH=\$QT_BASE_DIR/lib/x86_64-linux-gnu:\$QT_BASE_DIR/lib:/usr/local/lib:\$LD_LIBRARY_PATH &&
export PKG_CONFIG_PATH=\$QT_BASE_DIR/lib/pkgconfig:\$PKG_CONFIG_PATH &&
mkdir build && mkdir build &&
cd build && cd build &&
cmake -D NO_SHIBBOLETH=1 -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=1 ../ && cmake -D NO_SHIBBOLETH=1 -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=1 ../ &&
@@ -286,17 +278,13 @@ name: qt-5.12-clang
steps: steps:
- name: build and test - name: build and test
image: nextcloudci/client-5.12:client-5.12-5 image: nextcloudci/client-5.12:client-5.12-2
commands: commands:
# Install QtKeyChain # Install QtKeyChain
- /bin/bash -c " - /bin/bash -c "
export CC=clang-6.0 && export CC=clang-6.0 &&
export CXX=clang++-6.0 && export CXX=clang++-6.0 &&
export QT_BASE_DIR=/opt/qt5.12.5 && source /opt/qt512/bin/qt512-env.sh &&
export QTDIR=\$QT_BASE_DIR &&
export PATH=\$QT_BASE_DIR/bin:\$PATH &&
export LD_LIBRARY_PATH=\$QT_BASE_DIR/lib/x86_64-linux-gnu:\$QT_BASE_DIR/lib:/usr/local/lib:\$LD_LIBRARY_PATH &&
export PKG_CONFIG_PATH=\$QT_BASE_DIR/lib/pkgconfig:\$PKG_CONFIG_PATH &&
cd /tmp && cd /tmp &&
git clone https://github.com/frankosterfeld/qtkeychain.git && git clone https://github.com/frankosterfeld/qtkeychain.git &&
cd qtkeychain && cd qtkeychain &&
@@ -310,11 +298,7 @@ steps:
- /bin/bash -c " - /bin/bash -c "
export CC=clang-6.0 && export CC=clang-6.0 &&
export CXX=clang++-6.0 && export CXX=clang++-6.0 &&
export QT_BASE_DIR=/opt/qt5.12.5 && source /opt/qt512/bin/qt512-env.sh &&
export QTDIR=\$QT_BASE_DIR &&
export PATH=\$QT_BASE_DIR/bin:\$PATH &&
export LD_LIBRARY_PATH=\$QT_BASE_DIR/lib/x86_64-linux-gnu:\$QT_BASE_DIR/lib:/usr/local/lib:\$LD_LIBRARY_PATH &&
export PKG_CONFIG_PATH=\$QT_BASE_DIR/lib/pkgconfig:\$PKG_CONFIG_PATH &&
mkdir build && mkdir build &&
cd build && cd build &&
cmake -D NO_SHIBBOLETH=1 -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=1 ../ && cmake -D NO_SHIBBOLETH=1 -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=1 ../ &&
@@ -335,35 +319,16 @@ name: AppImage
steps: steps:
- name: build - name: build
image: nextcloudci/client-5.12:client-5.12-5 image: nextcloudci/client-5.12:client-5.12-2
commands: commands:
- /bin/bash -c "./admin/linux/build-appimage.sh" - /bin/bash -c "./admin/linux/build-appimage.sh"
- /bin/bash -c "./admin/linux/upload-appimage.sh"
trigger: trigger:
branch: branch:
- master - master
event: event:
- pull_request - pull_request
- push - push
---
kind: pipeline
name: Debian
steps:
- name: build
image: nextcloudci/client-debian-ci:client-debian-ci-2
commands:
- /bin/bash -c "./admin/linux/debian/drone-build.sh"
environment:
DEBIAN_SECRET_KEY:
from_secret: DEBIAN_SECRET_KEY
DEBIAN_SECRET_IV:
from_secret: DEBIAN_SECRET_IV
trigger:
branch:
- stable-2.6
event:
- tag
--- ---
kind: pipeline kind: pipeline
name: Documentation name: Documentation

9
.gitignore vendored
View File

@@ -15,18 +15,9 @@ cscope.*
tags tags
t1.cfg t1.cfg
## Ignore Visual Studio Code config & environment files
.vs/
.vscode/
## Ignore Visual Studio temporary files, build results, and ## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons. ## files generated by popular Visual Studio add-ons.
# CMake integration on VS2019+
CMakeSettings.json
# User-specific files # User-specific files
*.suo *.suo
*.user *.user

3
.gitmodules vendored
View File

@@ -1,3 +1,6 @@
[submodule "src/3rdparty/qtmacgoodies"]
path = src/3rdparty/qtmacgoodies
url = https://github.com/camilasan/qtmacgoodies.git
[submodule "binary"] [submodule "binary"]
path = binary path = binary
url = git://github.com/owncloud/owncloud-client-binary.git url = git://github.com/owncloud/owncloud-client-binary.git

View File

@@ -1,204 +0,0 @@
[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
Icon[cy_GB]=@APPLICATION_ICON_NAME@
Name[cy_GB]=@APPLICATION_NAME@ cleient cydweddu bwrdd gwaith
Comment[cy_GB]=@APPLICATION_NAME@ cleient cydweddu bwrdd gwaith
GenericName[cy_GB]=Cydweddu Ffolder

View File

@@ -198,7 +198,4 @@ X-GNOME-Autostart-Delay=3
# Translations # Translations
Icon[el]=@APPLICATION_ICON_NAME@
Name[el]=@APPLICATION_NAME@ πρόγραμμα συγχρονισμού
Comment[el]=@APPLICATION_NAME@ πρόγραμμα συγχρονισμού
GenericName[el]=Συγχρονισμός φακέλου GenericName[el]=Συγχρονισμός φακέλου

View File

@@ -198,7 +198,6 @@ X-GNOME-Autostart-Delay=3
# Translations # Translations
Icon[hr]=@APPLICATION_ICON_NAME@ Name[hr]=sinkronizacija računala
Name[hr]=@APPLICATION_NAME@ klijent za sink. računala Comment[hr]=klijent za sinkronizaciju računala
Comment[hr]=@APPLICATION_NAME@ klijent za sinkronizaciju računala
GenericName[hr]=Sinkronizacija mapa GenericName[hr]=Sinkronizacija mapa

View File

@@ -1,204 +0,0 @@
[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
Icon[mk]=@APPLICATION_ICON_NAME@
Name[mk]=@APPLICATION_NAME@ клиент за синхронизација на компјутер
Comment[mk]=@APPLICATION_NAME@ клиент за синхронизација на компјутер
GenericName[mk]=Папка за синхронизација

View File

@@ -201,4 +201,4 @@ X-GNOME-Autostart-Delay=3
Icon[ro]=@APPLICATION_ICON_NAME@ Icon[ro]=@APPLICATION_ICON_NAME@
Name[ro]=@APPLICATION_NAME@ client de sincronizare pentru desktop Name[ro]=@APPLICATION_NAME@ client de sincronizare pentru desktop
Comment[ro]=@APPLICATION_NAME@ client de sincronizare pentru desktop Comment[ro]=@APPLICATION_NAME@ client de sincronizare pentru desktop
GenericName[ro]=Sincronizare director GenericName[ro]=Sincronizare dosare

View File

@@ -201,4 +201,4 @@ X-GNOME-Autostart-Delay=3
Icon[sk_SK]=@APPLICATION_ICON_NAME@ Icon[sk_SK]=@APPLICATION_ICON_NAME@
Name[sk_SK]=@APPLICATION_NAME@ synchronizačný klient pre PC Name[sk_SK]=@APPLICATION_NAME@ synchronizačný klient pre PC
Comment[sk_SK]=@APPLICATION_NAME@ synchronizačný klient pre PC Comment[sk_SK]=@APPLICATION_NAME@ synchronizačný klient pre PC
GenericName[sk_SK]=Synchronizácia priečinkov GenericName[sk_SK]=Synchnonizácia priečinkov

View File

@@ -199,6 +199,6 @@ X-GNOME-Autostart-Delay=3
# Translations # Translations
Icon[sl]=@APPLICATION_ICON_NAME@ Icon[sl]=@APPLICATION_ICON_NAME@
Name[sl]=@APPLICATION_NAME@ program za usklajevanje Name[sl]=@APPLICATION_NAME@ odjemalec za usklajevanje
Comment[sl]=@APPLICATION_NAME@ program za usklajevanje Comment[sl]=@APPLICATION_NAME@ odjemalec za usklajevanje
GenericName[sl]=Usklajevanje map GenericName[sl]=Usklajevanje map

View File

@@ -1,204 +0,0 @@
[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
Icon[sw]=@APPLICATION_ICON_NAME@
Name[sw]=Teja ya @APPLICATION_NAME@ ya kufanana faili kwa seva na faili ziko hapa
Comment[sw]=Teja ya @APPLICATION_NAME@ ya kufanana faili kwa seva na faili ziko hapa
GenericName[sw]=Fanana Kabrasha

View File

@@ -203,16 +203,8 @@ if( WIN32 )
add_definitions( -D__USE_MINGW_ANSI_STDIO=1 ) add_definitions( -D__USE_MINGW_ANSI_STDIO=1 )
add_definitions( -DNOMINMAX ) add_definitions( -DNOMINMAX )
# Get APIs from from Vista onwards. # Get APIs from from Vista onwards.
add_definitions( -D_WIN32_WINNT=0x0601 ) add_definitions( -D_WIN32_WINNT=0x0600)
add_definitions( -DWINVER=0x0601 ) add_definitions( -DWINVER=0x0600)
if( MSVC )
# Use automatic overload for suitable CRT safe-functions
# See https://docs.microsoft.com/de-de/cpp/c-runtime-library/security-features-in-the-crt?view=vs-2019
add_definitions( -D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1 )
# Also: Disable compiler warnings because we don't use Windows CRT safe-functions explicitly and don't intend to
# as this is a pure cross-platform source the only alternative would be a ton of ifdefs with calls to the _s version
add_definitions( -D_CRT_SECURE_NO_WARNINGS )
endif( MSVC )
endif( WIN32 ) endif( WIN32 )
if (APPLE) if (APPLE)

350
ChangeLog
View File

@@ -1,138 +1,5 @@
2.5 Series ChangeLog ChangeLog
==================== =========
version 2.5.3 (release 2019-07-22)
* Fix empty file wording in error log (small)
* Add Qt-5.12 to CI
* Fix a minor typo
* Libcloudproviders: Add missing check for Qt5DBus
* Fix several memory leaks in cloudproviders and add translation support
* Share link fixing
* New drone config
* Uses configuraion to determine if it should show empty folder popup.
* Simplify cmake command to make copy-pastable
* Updated default remote poll to 5 seconds #1115
* Fix memory leak with device pointer
* Added a nice UI for the E2E-enabled account first connect
* This should fix issue #1000
* Adds parameter to retrieve shares with its reshares.
* Fixed typo
* Fixed typo in "certificate"
* WebView: Properly handle usernames with spaces and plus signs in it
* Add error category for http file lock error status 423.
* Displays the uid_owner of a shared file.
* Minor text change in the link to help in the tab 'General'.
version 2.5.2 (release 2019-04-11)
* Handle spaces in username properly in login flow
* Wizard: show an error message if there is no enough free space in the local folder
* Removed whitespace from string
* Do not add double slash to login flow url
* Fix login flow with system proxy
* Start with easier theming
* Do not display dismissed notifications
* Fixed l18n issue. Added space for separating string
* Add invalid certiticate messagebox
* Correct app passwords link
* Be less verbose with logging
* Fix typo in translation string
* Add a command line option to launch the client in the background
* Support Ubuntu Disco Dingo
* Added missing Include
* Make sure _profile and _page are deleted in the correct order
* Fix KDEInstallDirs deprecation warnings
* Removed Stylesheet
version 2.5.1 (release 2019-01-06)
* Fixup the port in server notification URLs
* GUI: let Clang-Tidy modernize nullptr & override usage
* Improve the slide show
* Libsync: let Clang-Tidy modernize nullptr & override usage
* SettingsDialog: fix a little glitch in the account tool button size
* SettingsDialog: tweak color aware icons
* More verbose error and proper app name on configuration read error
* Fix cmake build using WITH_PROVIDERS=OFF
* Debian/Ubuntu target repository update
* Change man page names and contents for nextcloud
* Share dialog alignment
* Fixed typo
* Change link to docs for NC 15
* Do not fetch activities if they are not enabled
* Do not read system exclude list if user exclude is present
* Fix the activity loop
* Write the actual folder to the log
* Fix appname for Nautilus integration script
version 2.5.0 (release 2018-11-14)
* End to end encryption
* New Web login flow
* UI improvements: Notifications
* UI improvements: refactoring of Activities
* SyncJournal: Clear etag filter before sync
* Partial local discovery: Fix scheduling logic
* Sync hidden files by default
* Larger Windows App Icon
* Show a tray message when a folder watcher becomes unreliable #6119
* Create symlinks for the small-letter application icon file names
* In setup wizard put link to nextcloud installation
* Web view scales vertically
* Add a WebFlowCredentialsAccessManager
* Mac Application Icon
* Ensure GETFileJob notices finishing #6581
* OAuth2: Try to refresh the token even if the credentials weren't ready.
* Tray workarounds #6545
* UpdateInfo: Remove unused code
* OAuth: Remove the timeout
* TestOAuth: Don't have global static QObject
* Log: Adjust update/reconcile log verbosity
* Reconcile: When detecting a local move, keep the local mtime
* Wizard enhancement
* FolderMan::checkPathValidityForNewFolder: make sure to work when fold…
* Update: Report on readdir() errors #6610
* Use encode()/decode() with Python 3 only
* Sqlite: Update bundled version to 3.24.0
* Do not require server replies to contain an mtime
* Settings: Attempt to fix rename issue on old macOS
* Support higher resolution theme icons
* OAuth: Fix infinite loop when the refresh token is expired
* Windows: Don't ignore files with FILE_ATTRIBUTE_TEMPORARY
* Data-Fingerprint: Fix backup detection when fingerprint is empty
* Nautilus: Fix GET_MENU_ITEMS with utf8 filenames #6643
* Windows: Release handle/fd when file open fails #6699
* SettingsDialog: Show the page for the newly created account
* Updates submodule qtmacgoodies.
* Fixes #665 Adds slot for confirmShare button.
* Rename INSTALL to INSTALL.md for Preview :)
* Add cmake temporary stuff
* Inform user that configuration is not writable
* Uses QByteArray to store private key.
* Fix cmake command for linux in README too
* Build fix: remove an unused QtSvg/QSvgRenderer include
* Qtkeychain: 0.8.0 -> 0.9.1
* Setup wizard: implement an animated and interactive slide show
* Theming for general settings ui
* Make the "Add Folder Sync Connection" button act like a button
* Allow to use the login flow with a self signed certificate
* Fix warning in ShareUserGroupWidget
* Copy over config file to new location on windows
* Update to translate strings
* Migrate http auth to webflow
* Margins
* Qt 5.5 compatibility patch for Xenial
* Fix cmake build of documentation
* Use Nextcloud
* Update isntaller background for OSX
* Fix ActivityWidget palette
* SettingsDialog: disable unnecessary wrapping for the about label
* Added default scheme when server returns just a host
* Removed explicit initialization; Fixed RAND_bytes not found
* Actually open the activity view on a click for more info
* Use a format that supports alpha channels for avatars
* L10n. Added space for correct grammar.
2.4 Series ChangeLog
====================
version 2.4.1 (2017-02-xx) version 2.4.1 (2017-02-xx)
* Ignore files with file names that can't be encoded for the filesystem (#6287, #5676, #5719) * Ignore files with file names that can't be encoded for the filesystem (#6287, #5676, #5719)
@@ -247,10 +114,6 @@ version 2.4.0 (2017-12-21)
* Compile with stack-smashing protection * Compile with stack-smashing protection
* Updater: Rudimentary support for beta channel (#6048) * Updater: Rudimentary support for beta channel (#6048)
2.3 Series ChangeLog
====================
version 2.3.4 (2017-11-02) version 2.3.4 (2017-11-02)
* Checksums: Use addData function to avoid endless loop CPU load issues with Office files * Checksums: Use addData function to avoid endless loop CPU load issues with Office files
* Packaging: Require ZLIB * Packaging: Require ZLIB
@@ -321,10 +184,6 @@ version 2.3.0 (2017-03-03)
* Improved documentation * Improved documentation
* Crash fixes * Crash fixes
2.2 Series ChangeLog
====================
version 2.2.4 (release 2016-09-27) version 2.2.4 (release 2016-09-27)
* Dolphin Plugin: Use the Application name for the socket path (#5172) * Dolphin Plugin: Use the Application name for the socket path (#5172)
* SyncEngine: Fix renaming of folder when file are changed (#5195) * SyncEngine: Fix renaming of folder when file are changed (#5195)
@@ -389,10 +248,6 @@ version 2.2.0 (release 2016-05-12)
* Update of QtKeyChain to support Windows credential store * Update of QtKeyChain to support Windows credential store
* Packaging of dolphin overlay icon module for bleeding edge distros * Packaging of dolphin overlay icon module for bleeding edge distros
2.1 Series ChangeLog
====================
version 2.1.1 (release 2016-02-10) version 2.1.1 (release 2016-02-10)
* UI improvements for HiDPI screens, error messages, RTL languages * UI improvements for HiDPI screens, error messages, RTL languages
* Fix occurences of "Connection Closed" when a new unauthenticated TCP socket is used * Fix occurences of "Connection Closed" when a new unauthenticated TCP socket is used
@@ -426,8 +281,8 @@ version 2.1 (release 2015-12-03)
* Improved reconnecting after network change/disconnect (#4167 #3969 ...) * Improved reconnecting after network change/disconnect (#4167 #3969 ...)
* Improved performance in Windows file system discovery * Improved performance in Windows file system discovery
* Removed libneon-based propagator. As a consequence, The client can no * Removed libneon-based propagator. As a consequence, The client can no
* longer provide bandwith limiting on Linux-distributions where it is longer provide bandwith limiting on Linux-distributions where it is
* using Qt < 5.4 using Qt < 5.4
* Performance improvements in the logging functions * Performance improvements in the logging functions
* Ensured that local disk space problems are handled gracefully (#2939) * Ensured that local disk space problems are handled gracefully (#2939)
* Improved handling of checksums: transport validation, db (#3735) * Improved handling of checksums: transport validation, db (#3735)
@@ -463,10 +318,6 @@ version 2.1 (release 2015-12-03)
* Organized patches to our base Qt version into admin/qt/patches * Organized patches to our base Qt version into admin/qt/patches
* Plus: A lot of unmentioned improvements and fixes * Plus: A lot of unmentioned improvements and fixes
2.0 Series ChangeLog
====================
version 2.0.2 (release 2015-10-21) version 2.0.2 (release 2015-10-21)
* csync_file_stat_s: Save a bit of memory * csync_file_stat_s: Save a bit of memory
* Shibboleth: Add our base user agent to WebKit * Shibboleth: Add our base user agent to WebKit
@@ -542,10 +393,6 @@ version 2.0.0 (release 2015-08-25)
* Bandwidth Throttling: Provide automatic limit setting for downloads (#3084) * Bandwidth Throttling: Provide automatic limit setting for downloads (#3084)
* Systray: Workaround for issue with Qt 5.5.0 #3656 * Systray: Workaround for issue with Qt 5.5.0 #3656
1.8 Series ChangeLog
====================
version 1.8.4 (release 2015-07-13) version 1.8.4 (release 2015-07-13)
* Release to ship a security release of openSSL. No source changes of the ownCloud Client code. * Release to ship a security release of openSSL. No source changes of the ownCloud Client code.
@@ -555,7 +402,7 @@ version 1.8.3 (release 2015-06-23)
* Ignores: Force a remote discovery after ignore list change (#3172) * Ignores: Force a remote discovery after ignore list change (#3172)
* Shibboleth: Avoid crash by letting the webview use its own QNAM (#3359) * Shibboleth: Avoid crash by letting the webview use its own QNAM (#3359)
* System Ignores: Removed *.tmp from system ignore again. If a user * System Ignores: Removed *.tmp from system ignore again. If a user
* wants to ignore *.tmp, it needs to be added to the user ignore list. wants to ignore *.tmp, it needs to be added to the user ignore list.
version 1.8.2 (release 2015-06-08) version 1.8.2 (release 2015-06-08)
* Improve reporting of server error messages (#3220) * Improve reporting of server error messages (#3220)
@@ -568,16 +415,16 @@ version 1.8.2 (release 2015-06-08)
* HTTP: Add the branding name to the UserAgent string * HTTP: Add the branding name to the UserAgent string
* ConnectonValidator: Always run with new credentials (#3266) * ConnectonValidator: Always run with new credentials (#3266)
* Recall Feature: Admins can trigger an upload of a file from * Recall Feature: Admins can trigger an upload of a file from
* client to server again (#3246) client to server again (#3246)
* Propagator: Add 'Content-Length: 0' header to MKCOL request (#3256) * Propagator: Add 'Content-Length: 0' header to MKCOL request (#3256)
* Switch on checksum verification through branding or config * Switch on checksum verification through branding or config
* Add ability for checksum verification of up and download * Add ability for checksum verification of up and download
* Fix opening external links for some labels (#3135) * Fix opening external links for some labels (#3135)
* AccountState: Run only a single validator, allow error message * AccountState: Run only a single validator, allow error message
* overriding (#3236, #3153) overriding (#3236, #3153)
* SyncJournalDB: Minor fixes and simplificatons * SyncJournalDB: Minor fixes and simplificatons
* SyncEngine: Force re-read of folder Etags for upgrades from * SyncEngine: Force re-read of folder Etags for upgrades from
* 1.8.0 and 1.8.1 1.8.0 and 1.8.1
* Propagator: Limit length of temporary file name (#2789) * Propagator: Limit length of temporary file name (#2789)
* ShareDialog: Password ui fixes (#3189) * ShareDialog: Password ui fixes (#3189)
* Fix startup hang by removing QSettings lock file (#3175) * Fix startup hang by removing QSettings lock file (#3175)
@@ -598,12 +445,12 @@ version 1.8.2 (release 2015-06-08)
version 1.8.1 (release 2015-05-07) version 1.8.1 (release 2015-05-07)
* Make "operation canceled" error a soft error * Make "operation canceled" error a soft error
* Do not throw an error for files that are scheduled to be removed, * Do not throw an error for files that are scheduled to be removed,
* but can not be found on the server. #2919 but can not be found on the server. #2919
* Windows: Reset QNAM to proper function after hibernation. #2899 #2895 #2973 * Windows: Reset QNAM to proper function after hibernation. #2899 #2895 #2973
* Fix argument verification of --confdir #2453 * Fix argument verification of --confdir #2453
* Fix a crash when accessing a dangling UploadDevice pointer #2984 * Fix a crash when accessing a dangling UploadDevice pointer #2984
* Add-folder wizard: Make sure there is a scrollbar if folder names * Add-folder wizard: Make sure there is a scrollbar if folder names
* are too long #2962 are too long #2962
* Add-folder Wizard: Select the newly created folder * Add-folder Wizard: Select the newly created folder
* Activity: Correctly restore column sizes #3005 * Activity: Correctly restore column sizes #3005
* SSL Button: do not crash on empty certificate chain * SSL Button: do not crash on empty certificate chain
@@ -611,8 +458,8 @@ version 1.8.1 (release 2015-05-07)
* Lookup system proxy async to avoid hangs #2993 #2802 * Lookup system proxy async to avoid hangs #2993 #2802
* ShareDialog: Some GUI refinements * ShareDialog: Some GUI refinements
* ShareDialog: On creation of a share always retrieve the share * ShareDialog: On creation of a share always retrieve the share
* This makes sure that if a default expiration date is set this is reflected This makes sure that if a default expiration date is set this is reflected
* in the dialog. #2889 in the dialog. #2889
* ShareDialog: Only show share dialog if we are connected. * ShareDialog: Only show share dialog if we are connected.
* HttpCreds: Fill pw dialog with previous password. #2848 #2879 * HttpCreds: Fill pw dialog with previous password. #2848 #2879
* HttpCreds: Delete password from old location. #2186 * HttpCreds: Delete password from old location. #2186
@@ -621,7 +468,7 @@ version 1.8.1 (release 2015-05-07)
* ProtocolWidget: Always add seconds to the DateTime locale. #2535 * ProtocolWidget: Always add seconds to the DateTime locale. #2535
* Updater: Give context as to which app is about to be updated #3040 * Updater: Give context as to which app is about to be updated #3040
* Windows: Add version information for owncloud.exe. This should help us know * Windows: Add version information for owncloud.exe. This should help us know
* what version or build number a crash report was generated with. what version or build number a crash report was generated with.
* Fix a crash on shutdown in ~SocketApi #3057 * Fix a crash on shutdown in ~SocketApi #3057
* SyncEngine: Show more timing measurements #3064 * SyncEngine: Show more timing measurements #3064
* Discovery: Add warning if returned etag is 0 * Discovery: Add warning if returned etag is 0
@@ -644,8 +491,8 @@ version 1.8.1 (release 2015-05-07)
version 1.8.0 (release 2015-03-17) version 1.8.0 (release 2015-03-17)
* Mac OS: HIDPI support * Mac OS: HIDPI support
* Support Sharing from desktop: Added a share dialog that can be * Support Sharing from desktop: Added a share dialog that can be
* opened by context menu in the file managers (Win, Mac, Nautilus) opened by context menu in the file managers (Win, Mac, Nautilus)
* Supports public links with password enforcement Supports public links with password enforcement
* Enhanced usage of parallel HTTP requests for ownCloud 8 servers * Enhanced usage of parallel HTTP requests for ownCloud 8 servers
* Renamed github repository from mirall to client. * Renamed github repository from mirall to client.
* Mac OS: Use native notification support * Mac OS: Use native notification support
@@ -658,7 +505,7 @@ version 1.8.0 (release 2015-03-17)
* Build with Qt 5.4 * Build with Qt 5.4
* Dropped libneon dependency if Qt 5.4 is available * Dropped libneon dependency if Qt 5.4 is available
* Keep files open very short, that avoid lock problems on Windows * Keep files open very short, that avoid lock problems on Windows
* especially with office software but also others. especially with office software but also others.
* Merged some NetBSD patches * Merged some NetBSD patches
* Selective sync support for owncloudcmd * Selective sync support for owncloudcmd
* Reorganize the source repository * Reorganize the source repository
@@ -667,17 +514,13 @@ version 1.8.0 (release 2015-03-17)
* A huge amount of bug fixes in all areas of the client. * A huge amount of bug fixes in all areas of the client.
* almost 700 commits since 1.7.1 * almost 700 commits since 1.7.1
1.7 Series ChangeLog
====================
version 1.7.1 (release 2014-12-18) version 1.7.1 (release 2014-12-18)
* Documentation fixes and updates * Documentation fixes and updates
* Nautilus Python plugin fixed for Python 3 * Nautilus Python plugin fixed for Python 3
* GUI wording fixes plus improved log messages * GUI wording fixes plus improved log messages
* Fix hidning of the database files in the sync directories * Fix hidning of the database files in the sync directories
* Compare http download size with the header value to avoid broken * Compare http download size with the header value to avoid broken
* downloads, bug #2528 downloads, bug #2528
* Avoid initial ETag fetch job at startup, which is not needed. * Avoid initial ETag fetch job at startup, which is not needed.
* Add chunk size http header to PUT requests * Add chunk size http header to PUT requests
* Fixed deteteCookie method of our CookieJar, fix for Shibboleth * Fixed deteteCookie method of our CookieJar, fix for Shibboleth
@@ -700,20 +543,21 @@ version 1.7.1 (release 2014-12-18)
* Win32: Improve reliability of Installer, fix removal of Shell Extensions * Win32: Improve reliability of Installer, fix removal of Shell Extensions
version 1.7.0 (release 2014-11-07) version 1.7.0 (release 2014-11-07)
* oC7 Sharing: Handle new sharing options of ownCloud 7 correctly. * oC7 Sharing: Handle new sharing options of ownCloud 7 correctly.
* Added Selective sync: Ability to unselect server folders which are * Added Selective sync: Ability to unselect server folders which are
* excluded from syncing, plus GUI and setup GUI excluded from syncing, plus GUI and setup GUI
* Added overlay icons for Windows Explorer, Mac OS Finder and GNOME Nautilus. * Added overlay icons for Windows Explorer, Mac OS Finder and GNOME Nautilus.
* Information is provided by the client via a local socket / named pipe API Information is provided by the client via a local socket / named pipe API
* which provides information about the sync status of files. which provides information about the sync status of files.
* Improved local change detection: consider file size, detect files * Improved local change detection: consider file size, detect files
* with ongoing changes and do not upload immediately with ongoing changes and do not upload immediately
* Improved HTTP request timeout handler: all successful requests reset * Improved HTTP request timeout handler: all successful requests reset
* the timeout counter the timeout counter
* Improvements for syncing command line tool: netrc support, improved * Improvements for syncing command line tool: netrc support, improved
* SSL support, non interactive mode SSL support, non interactive mode
* Permission system: ownCloud 7 delivers file and folder permissions, * Permission system: ownCloud 7 delivers file and folder permissions,
* added ability to deal with it for shared folders and more. added ability to deal with it for shared folders and more.
* Ignore handling: Do not recurse into ignored or excluded directories * Ignore handling: Do not recurse into ignored or excluded directories
* Major sync journal database improvements for more stability and performance * Major sync journal database improvements for more stability and performance
* New library interface to sqlite3 * New library interface to sqlite3
@@ -722,40 +566,35 @@ version 1.7.0 (release 2014-11-07)
* Improved logging: more useful meta info, removed noise * Improved logging: more useful meta info, removed noise
* Updated to latest Qt5 versions on Windows and OS X * Updated to latest Qt5 versions on Windows and OS X
* Fixed data loss when renaming a download temporary fails and there was * Fixed data loss when renaming a download temporary fails and there was
* a conflict at the same time. a conflict at the same time.
* Fixed missing warnings about reusing a sync folder when the back button * Fixed missing warnings about reusing a sync folder when the back button
* was used in the advanced folder setup wizard. was used in the advanced folder setup wizard.
* The 'Retry Sync' button now also restarts all downloads. * The 'Retry Sync' button now also restarts all downloads.
* Clean up temporary downloads and some extra database files when wiping a * Clean up temporary downloads and some extra database files when wiping a
* folder. folder.
* OS X: Sparkle update to provide pkg format properly * OS X: Sparkle update to provide pkg format properly
* OS X: Change distribution format from dmg to pkg with new installer. * OS X: Change distribution format from dmg to pkg with new installer.
* Windows: Fix handling of filenames with trailing dot or space * Windows: Fix handling of filenames with trailing dot or space
* Windows: Don't use the wrong way to get file mtimes in the legacy propagator. * Windows: Don't use the wrong way to get file mtimes in the legacy propagator.
1.6 Series ChangeLog
====================
version 1.6.4 (release 2014-10-22) version 1.6.4 (release 2014-10-22)
* Fix startup logic, fixes bug #1989 * Fix startup logic, fixes bug #1989
* Fix raise dialog on X11 * Fix raise dialog on X11
* Win32: fix overflow when computing the size of file > 4GiB * Win32: fix overflow when computing the size of file > 4GiB
* Use a fixed function to get files modification time, the * Use a fixed function to get files modification time, the
* original one was broken for certain timezone issues, see original one was broken for certain timezone issues, see
* core bug #9781 for details core bug #9781 for details
* Added some missing copyright headers * Added some missing copyright headers
* Avoid data corruption due to wrong error handling, bug #2280 * Avoid data corruption due to wrong error handling, bug #2280
* Do improved request timeout handling to reduce the number of * Do improved request timeout handling to reduce the number of
* timed out jobs, bug #2155 timed out jobs, bug #2155
* version 1.6.3 (release 2014-09-03) version 1.6.3 (release 2014-09-03)
* Fixed updater on OS X * Fixed updater on OS X
* Fixed memory leak in SSL button that could lead to quick memory draining * Fixed memory leak in SSL button that could lead to quick memory draining
* Fixed upload problem with files >4 GB * Fixed upload problem with files >4 GB
* MacOSX, Linux: Bring Settings window to front properly * MacOSX, Linux: Bring Settings window to front properly
* Branded clients: If no configuration is detected, try to import the data * Branded clients: If no configuration is detected, try to import the data
* from a previously configured community edition. from a previously configured community edition.
version 1.6.2 (release 2014-07-28 ) version 1.6.2 (release 2014-07-28 )
* Limit the HTTP buffer size when downloading to limit memory consumption. * Limit the HTTP buffer size when downloading to limit memory consumption.
@@ -763,7 +602,7 @@ version 1.6.2 (release 2014-07-28 )
* Fix local file name clash detection for MacOSX. * Fix local file name clash detection for MacOSX.
* Limit maximum wait time to ten seconds in network limiting. * Limit maximum wait time to ten seconds in network limiting.
* Fix data corruption while trying to resume and the server does * Fix data corruption while trying to resume and the server does
* not support it. not support it.
* HTTP Credentials: Read password from legacy place if not found. * HTTP Credentials: Read password from legacy place if not found.
* Shibboleth: Fix the waiting curser that would not disapear (#1915) * Shibboleth: Fix the waiting curser that would not disapear (#1915)
* Limit memory usage to avoid mem wasting and crashes * Limit memory usage to avoid mem wasting and crashes
@@ -777,18 +616,18 @@ version 1.6.1 (release 2014-06-26 )
* Fix openSSL problems for windows deployment * Fix openSSL problems for windows deployment
* Fix syncing a folder with '#' in the name * Fix syncing a folder with '#' in the name
* Fix #1845: do not update parent directory etag before sub * Fix #1845: do not update parent directory etag before sub
* directories are removed directories are removed
* Fix reappearing directories if dirs are removed during its * Fix reappearing directories if dirs are removed during its
* upload upload
* Fix app version in settings dialog, General tab * Fix app version in settings dialog, General tab
* Fix crash in FolderWizard when going offline * Fix crash in FolderWizard when going offline
* Shibboleth fixes * Shibboleth fixes
* More specific error messages (file remove during upload, open * More specific error messages (file remove during upload, open
* local sync file) local sync file)
* Use QSet rather than QHash in SyncEngine (save memory) * Use QSet rather than QHash in SyncEngine (save memory)
* Fix some memory leaks * Fix some memory leaks
* Fix some thread race problems, ie. wait for neon thread to finish * Fix some thread race problems, ie. wait for neon thread to finish
* before the propagator is shut down before the propagator is shut down
* Fix a lot of issues and warnings found by Coverity * Fix a lot of issues and warnings found by Coverity
* Fix Mac some settings dialog problems * Fix Mac some settings dialog problems
@@ -811,16 +650,16 @@ version 1.6.0 (release 2014-05-30 )
* Introduce a general timeout of 300s for network operations * Introduce a general timeout of 300s for network operations
* Improve error handling, blacklisting * Improve error handling, blacklisting
* Job-based change propagation, enables faster parallel up/downloads * Job-based change propagation, enables faster parallel up/downloads
* (right now only if no bandwidth limit is set and no proxy is used) (right now only if no bandwidth limit is set and no proxy is used)
* Significantly reduced CPU load when checking for local and remote changes * Significantly reduced CPU load when checking for local and remote changes
* Speed up file stat code on Windows * Speed up file stat code on Windows
* Enforce Qt5 for Windows and Mac OS X builds * Enforce Qt5 for Windows and Mac OS X builds
* Improved owncloudcmd: SSL support, documentation * Improved owncloudcmd: SSL support, documentation
* Added advanced logging of operations (file .???.log in sync * Added advanced logging of operations (file .???.log in sync
* directory) directory)
* Avoid creating a temporary copy of the sync database (.ctmp) * Avoid creating a temporary copy of the sync database (.ctmp)
* Enable support for TLS 1.2 negotiation on platforms that use * Enable support for TLS 1.2 negotiation on platforms that use
* Qt 5.2 or later Qt 5.2 or later
* Forward server exception messages to client error messages * Forward server exception messages to client error messages
* Mac OS X: Support Notification Center in OS X 10.8+ * Mac OS X: Support Notification Center in OS X 10.8+
* Mac OS X: Use native settings dialog * Mac OS X: Use native settings dialog
@@ -829,15 +668,11 @@ version 1.6.0 (release 2014-05-30 )
* Remove vio abstraction in csync * Remove vio abstraction in csync
* Avoid data loss when a client file system is not case sensitive * Avoid data loss when a client file system is not case sensitive
1.5 Series ChangeLog
====================
version 1.5.3 (release 2014-03-10 ) version 1.5.3 (release 2014-03-10 )
* Fix usage of proxies after first sync run (#1502, #1524, #1459, #1521) * Fix usage of proxies after first sync run (#1502, #1524, #1459, #1521)
* Do not wipe the credentials from config for reconnect (#1499, #1503) * Do not wipe the credentials from config for reconnect (#1499, #1503)
* Do not erase the full account config if an old version of the client stored * Do not erase the full account config if an old version of the client stored
* the password (related to above) the password (related to above)
* Fix layout of the network tab (fixes #1491) * Fix layout of the network tab (fixes #1491)
* Handle authentication requests by a Shibboleth IdP * Handle authentication requests by a Shibboleth IdP
* Shibboleth: If no connection is available, don't open the login window * Shibboleth: If no connection is available, don't open the login window
@@ -866,34 +701,34 @@ version 1.5.2 (release 2014-02-26 )
version 1.5.1 (release 2014-02-13 ) version 1.5.1 (release 2014-02-13 )
* Added an auto updater that updates the client if a * Added an auto updater that updates the client if a
* more recent version was found automatically (Windows, Mac OS X) more recent version was found automatically (Windows, Mac OS X)
* Added a button to the account dialog that gives information * Added a button to the account dialog that gives information
* about the encryption layer used for communication, plus a about the encryption layer used for communication, plus a
* certificate information widget certificate information widget
* Preserve the permission settings of local files rather than * Preserve the permission settings of local files rather than
* setting them to a default (Bug #820) setting them to a default (Bug #820)
* Handle windows lnk files correctly (Bug #1307) * Handle windows lnk files correctly (Bug #1307)
* Detect removes and renames in read only shares and * Detect removes and renames in read only shares and
* restore the gone away files. (Bug #1386) restore the gone away files. (Bug #1386)
* Fixes sign in/sign out and password dialog. (Bug #1353) * Fixes sign in/sign out and password dialog. (Bug #1353)
* Fixed error messages (Bug #1394) * Fixed error messages (Bug #1394)
* Lots of fixes for building with Qt5 * Lots of fixes for building with Qt5
* Changes to network limits are now also applied during a * Changes to network limits are now also applied during a
* sync run sync run
* Fixed mem leak after via valgrind on Mac * Fixed mem leak after via valgrind on Mac
* Imported the ocsync library into miralls repository. * Imported the ocsync library into miralls repository.
* Adopted all build systems and packaging to that. Adopted all build systems and packaging to that.
* Introduce a new linux packaging scheme following the * Introduce a new linux packaging scheme following the
* debian upstream scheme debian upstream scheme
* Use a refactored Linux file system watcher based on * Use a refactored Linux file system watcher based on
* inotify, incl. unit tests inotify, incl. unit tests
* Wizard: Gracefully fall back to HTTP if HTTPS connection * Wizard: Gracefully fall back to HTTP if HTTPS connection
* fails, issuing a warning fails, issuing a warning
* Fixed translation misses in the propagator * Fixed translation misses in the propagator
* Fixes in proxy configuration * Fixes in proxy configuration
* Fixes in sync journal handling * Fixes in sync journal handling
* Fix the upload progress if the local source is still * Fix the upload progress if the local source is still
* changing when the upload begins. changing when the upload begins.
* Add proxy support to owncloud commandline client * Add proxy support to owncloud commandline client
* NSIS fixes * NSIS fixes
* A lot of other fixes and minor improvements * A lot of other fixes and minor improvements
@@ -930,10 +765,6 @@ version 1.5.0 (release 2013-12-12 ), csync 0.91.4 required
* Windows: Fix rename of temporary files * Windows: Fix rename of temporary files
* Windows: Fix move file operation * Windows: Fix move file operation
1.4 Series ChangeLog
====================
version 1.4.2 (release 2013-10-18 ), csync 0.90.4 required version 1.4.2 (release 2013-10-18 ), csync 0.90.4 required
* Do not show the warning icon in the tray (#944) * Do not show the warning icon in the tray (#944)
* Fix manual proxy support when switching (#1016) * Fix manual proxy support when switching (#1016)
@@ -949,11 +780,12 @@ version 1.4.2 (release 2013-10-18 ), csync 0.90.4 required
* Progress: Show number of deletes. * Progress: Show number of deletes.
version 1.4.1 (release 2013-09-24 ), csync 0.90.1 required version 1.4.1 (release 2013-09-24 ), csync 0.90.1 required
* Translation and documentation fixes. * Translation and documentation fixes.
* Fixed error display in settings/status dialog, displays multi * Fixed error display in settings/status dialog, displays multi
* line error messages now correctly. line error messages now correctly.
* Wait up to 30 secs before complaining about missing systray * Wait up to 30 secs before complaining about missing systray
* Fixes bug #949 Fixes bug #949
* Fixed utf8 issues with basic auth authentication, fixes bug #941 * Fixed utf8 issues with basic auth authentication, fixes bug #941
* Fixed remote folder selector, avoid recursive syncing, fixes bug #962 * Fixed remote folder selector, avoid recursive syncing, fixes bug #962
* Handle and display network problems at startup correctly. * Handle and display network problems at startup correctly.
@@ -970,6 +802,7 @@ version 1.4.1 (release 2013-09-24 ), csync 0.90.1 required
* Various minor code fixes * Various minor code fixes
version 1.4.0 (release 2013-09-04 ), csync 0.90.0 required version 1.4.0 (release 2013-09-04 ), csync 0.90.0 required
* New Scheduler: Only sync when there are actual changes in the server * New Scheduler: Only sync when there are actual changes in the server
* Add a Settings Dialog, move Proxy Settings there * Add a Settings Dialog, move Proxy Settings there
* Transform folder Status Dialog into Account Settings, provide feedback via context menu * Transform folder Status Dialog into Account Settings, provide feedback via context menu
@@ -980,7 +813,7 @@ version 1.4.0 (release 2013-09-04 ), csync 0.90.0 required
* Move ability to switch to mono icons from a switch to a Settings option * Move ability to switch to mono icons from a switch to a Settings option
* Add "Launch on System Startup" GUI option * Add "Launch on System Startup" GUI option
* Add "Show Desktop Nofications"GUI option (enabled by default) * Add "Show Desktop Nofications"GUI option (enabled by default)
* top optionally disable sync notifications top optionally disable sync notifications
* Add Help item, pointing to online reference * Add Help item, pointing to online reference
* Implement graphical selection of remote folders in FolderWizard * Implement graphical selection of remote folders in FolderWizard
* Allow custom ignore patterns * Allow custom ignore patterns
@@ -999,14 +832,11 @@ version 1.4.0 (release 2013-09-04 ), csync 0.90.0 required
* Require Qt 4.7 * Require Qt 4.7
* Known issue: Under certain conditions, a file will only get uploaded after up to five minutes * Known issue: Under certain conditions, a file will only get uploaded after up to five minutes
1.3 Series ChangeLog
====================
version 1.3.0 (release 2013-06-25 ), csync 0.80.0 required version 1.3.0 (release 2013-06-25 ), csync 0.80.0 required
* Default proxy port to 8080 * Default proxy port to 8080
* Don't lose proxy settings when changing passwords * Don't lose proxy settings when changing passwords
* Support SOCKS5 proxy (useful in combination with ssh* *D) * Support SOCKS5 proxy (useful in combination with ssh *D)
* Propagate proxy changes to csync at runtime * Propagate proxy changes to csync at runtime
* Improve proxy wizard * Improve proxy wizard
* Display proxy errors * Display proxy errors
@@ -1022,7 +852,7 @@ version 1.3.0 (release 2013-06-25 ), csync 0.80.0 required
* Remove journal when reusing a directory that used to have a journal before * Remove journal when reusing a directory that used to have a journal before
* Visual clean up of status dialog items * Visual clean up of status dialog items
* Wizard: When changing the URL or user name, allow the user to push his data * Wizard: When changing the URL or user name, allow the user to push his data
* to the new location or wipe the folder and start from scratch to the new location or wipe the folder and start from scratch
* Wizard: Make setting a custom folder as a sync target work again * Wizard: Make setting a custom folder as a sync target work again
* Fix application icon * Fix application icon
* User-Agent now contains "Mozilla/5.0" and the Platform name (for firewall/proxy compat) * User-Agent now contains "Mozilla/5.0" and the Platform name (for firewall/proxy compat)
@@ -1030,10 +860,6 @@ version 1.3.0 (release 2013-06-25 ), csync 0.80.0 required
* New setup wizard, defaulting to root syncing (only for new setups) * New setup wizard, defaulting to root syncing (only for new setups)
* Improved thread stop/termination * Improved thread stop/termination
1.2 Series ChangeLog
====================
version 1.2.5 (release 2013-04-23 ), csync 0.70.7 required version 1.2.5 (release 2013-04-23 ), csync 0.70.7 required
* [Fixes] NSIS installer fixes * [Fixes] NSIS installer fixes
* [Fixes] Fix crash race by making certificateChain() thread safe * [Fixes] Fix crash race by making certificateChain() thread safe
@@ -1099,10 +925,6 @@ version 1.2.0 (release 2013-01-24 ), csync 0.70.2 required
* [Platform] cmake fixes. * [Platform] cmake fixes.
* [Platform] Improved, more detailed error reporting. * [Platform] Improved, more detailed error reporting.
1.1 Series ChangeLog
====================
version 1.1.4 (release 2012-12-19 ), csync 0.60.4 required version 1.1.4 (release 2012-12-19 ), csync 0.60.4 required
* No changes to mirall, only csync fixes. * No changes to mirall, only csync fixes.
@@ -1112,7 +934,7 @@ version 1.1.3 (release 2012-11-30 ), csync 0.60.3 required
version 1.1.2 (release 2012-11-26 ), csync 0.60.2 required version 1.1.2 (release 2012-11-26 ), csync 0.60.2 required
* [Fixes] Allow to properly cancel the password dialog. * [Fixes] Allow to properly cancel the password dialog.
* [Fixes] Share folder name correctly percent encoded with old Qt * [Fixes] Share folder name correctly percent encoded with old Qt
* * * * 4.6 builds ie. Debian. 4.6 builds ie. Debian.
* [Fixes] If local sync dir is not existing, create it. * [Fixes] If local sync dir is not existing, create it.
* [Fixes] lots of other minor fixes. * [Fixes] lots of other minor fixes.
* [GUI] Display error messages in status dialog. * [GUI] Display error messages in status dialog.
@@ -1120,30 +942,30 @@ version 1.1.2 (release 2012-11-26 ), csync 0.60.2 required
* [GUI] Show username for connection in statusdialog. * [GUI] Show username for connection in statusdialog.
* [GUI] Show intro wizard on new connection setup. * [GUI] Show intro wizard on new connection setup.
* [APP] Use CredentialStore to better support various credential * [APP] Use CredentialStore to better support various credential
* * * backends. backends.
* [APP] Handle missing local folder more robust: Create it if * [APP] Handle missing local folder more robust: Create it if
* * * missing instead of ignoring. missing instead of ignoring.
* [APP] Simplify treewalk code. * [APP] Simplify treewalk code.
* [Platform] Fix Mac building * [Platform] Fix Mac building
version 1.1.1 (release 2012-10-18), csync 0.60.1 required version 1.1.1 (release 2012-10-18), csync 0.60.1 required
* [GUI]* Allow changing folder name in single folder mode * [GUI] Allow changing folder name in single folder mode
* [GUI]* Windows: Add license to installer * [GUI] Windows: Add license to installer
* [GUI]* owncloud --logwindow will bring up the log window * [GUI] owncloud --logwindow will bring up the log window
* * * * in an already running instance in an already running instance
* [Fixes] Make sure SSL errors are always handled * [Fixes] Make sure SSL errors are always handled
* [Fixes] Allow special characters in folder alias * [Fixes] Allow special characters in folder alias
* [Fixes] Proper workaround for Menu bug in Ubuntu * [Fixes] Proper workaround for Menu bug in Ubuntu
* [Fixes] csync: Fix improper memory cleanup which could * [Fixes] csync: Fix improper memory cleanup which could
* * * * cause memory leaks and crashes cause memory leaks and crashes
* [Fixes] csync: Fix memory leak * [Fixes] csync: Fix memory leak
* [Fixes] csync: Allow single quote (') in file names * [Fixes] csync: Allow single quote (') in file names
* [Fixes] csync: Remove stray temporary files * [Fixes] csync: Remove stray temporary files
* [GUI]* Reworked tray context menu. * [GUI] Reworked tray context menu.
* [GUI]* Users can now sync the server root folder. * [GUI] Users can now sync the server root folder.
* [Fixes] Proxy support: now supports Proxy Auto-Configuration (PAC) * [Fixes] Proxy support: now supports Proxy Auto-Configuration (PAC)
* * * * on Windows, reliability fixes across all OSes. on Windows, reliability fixes across all OSes.
* [Fixes] Url entry field in setup assistant handles http/https correctly. * [Fixes] Url entry field in setup assistant handles http/https correctly.
* [Fixes] Button enable state in status dialog. * [Fixes] Button enable state in status dialog.
* [Fixes] Crash fixed on ending the client, tray icon related. * [Fixes] Crash fixed on ending the client, tray icon related.
@@ -1156,15 +978,11 @@ version 1.1.1 (release 2012-10-18), csync 0.60.1 required
* [Platform] Windows: ownCloud gets added to autorun by default. * [Platform] Windows: ownCloud gets added to autorun by default.
* [Platform] insert correct version info from cmake. * [Platform] insert correct version info from cmake.
* [Platform] csync conf file and database were moved to the users app data * [Platform] csync conf file and database were moved to the users app data
* * * * * directory, away from the .csync dir. directory, away from the .csync dir.
** * * Renamed exclude.lst to sync-exclude.lst and moved it to * Renamed exclude.lst to sync-exclude.lst and moved it to
* * * * /etc/appName()/ for more clean packaging. From the user path, /etc/appName()/ for more clean packaging. From the user path,
* * * * still exclude.lst is read if sync-exclude.lst is not existing. still exclude.lst is read if sync-exclude.lst is not existing.
** * * Placed custom.ini with customization options to /etc/appName() * Placed custom.ini with customization options to /etc/appName()
1.0 Series ChangeLog
====================
version 1.0.5 (release 2012-08-14), csync 0.50.8 required version 1.0.5 (release 2012-08-14), csync 0.50.8 required
* [Fixes] Fixed setup dialog: Really use https if checkbox is activated. * [Fixes] Fixed setup dialog: Really use https if checkbox is activated.
@@ -1182,23 +1000,23 @@ version 1.0.4 (release 2012-08-10), csync 0.50.8 required
* [GUI] Removed Log Window Button, log available through command line. * [GUI] Removed Log Window Button, log available through command line.
* [GUI] Proxy configuration dialog added. * [GUI] Proxy configuration dialog added.
* [GUI] Added Translations to languages Slovenian, Polish, Catalan, * [GUI] Added Translations to languages Slovenian, Polish, Catalan,
* * * Portuguese (Brazil), German, Greek, Spanish, Czech, Italian, Slovak, Portuguese (Brazil), German, Greek, Spanish, Czech, Italian, Slovak,
* * * French, Russian, Japanese, Swedish, Portuguese (Portugal) French, Russian, Japanese, Swedish, Portuguese (Portugal)
* * * all with translation rate >90%. all with translation rate >90%.
* [Fixes] Loading of self signed certs into Networkmanager (#oc-843) * [Fixes] Loading of self signed certs into Networkmanager (#oc-843)
* [Fixes] Win32: Handle SSL dll loading correctly. * [Fixes] Win32: Handle SSL dll loading correctly.
* [Fixes] Many other small fixes and improvements. * [Fixes] Many other small fixes and improvements.
version 1.0.3 (release 2012-06-19), csync 0.50.7 required version 1.0.3 (release 2012-06-19), csync 0.50.7 required
* [GUI] Added a log window which catches the logging if required and * [GUI] Added a log window which catches the logging if required and
* * * allows to save for information. allows to save for information.
* [CMI] Added options --help, --logfile and --logflush * [CMI] Added options --help, --logfile and --logflush
* [APP] Allow to specify sync frequency in the config file. * [APP] Allow to specify sync frequency in the config file.
* [Fixes] Do not use csync database files from a sync before. * [Fixes] Do not use csync database files from a sync before.
* [Fixes] In Connection wizard, write the final config onyl if * [Fixes] In Connection wizard, write the final config onyl if
* * * * the user really accepted. Also remove the former database. the user really accepted. Also remove the former database.
* [Fixes] More user expected behaviour deletion of sync folder local * [Fixes] More user expected behaviour deletion of sync folder local
* * * * and remote. and remote.
* [Fixes] Allow special characters in the sync directory names * [Fixes] Allow special characters in the sync directory names
* [Fixes] Win32: Fixed directory removal with special character dirs. * [Fixes] Win32: Fixed directory removal with special character dirs.
* [Fixes] MacOS: Do not flood the system log any more * [Fixes] MacOS: Do not flood the system log any more
@@ -1217,7 +1035,7 @@ version 1.0.2 (release 2012-05-18), csync 0.50.6 required
* [Fixes] Dialogs comes to front on click * [Fixes] Dialogs comes to front on click
* [Fixes] Open local sync folder from tray and status for win32 * [Fixes] Open local sync folder from tray and status for win32
* [Fixes] Load exclude.lst correctly on MacOSX * [Fixes] Load exclude.lst correctly on MacOSX
* + csync fixes. + csync fixes.
version 1.0.1 (release 2012-04-18), csync 0.50.5 required version 1.0.1 (release 2012-04-18), csync 0.50.5 required
* [Security] Support SSL Connections * [Security] Support SSL Connections

View File

@@ -3,10 +3,10 @@ set( APPLICATION_SHORTNAME "Nextcloud" )
set( APPLICATION_EXECUTABLE "nextcloud" ) set( APPLICATION_EXECUTABLE "nextcloud" )
set( APPLICATION_DOMAIN "nextcloud.com" ) set( APPLICATION_DOMAIN "nextcloud.com" )
set( APPLICATION_VENDOR "Nextcloud GmbH" ) set( APPLICATION_VENDOR "Nextcloud GmbH" )
set( APPLICATION_UPDATE_URL "https://updates.nextcloud.org/client/" CACHE STRING "URL for updater" ) set( APPLICATION_UPDATE_URL "https://updates.nextcloud.org/client/" CACHE string "URL for updater" )
set( APPLICATION_HELP_URL "" CACHE STRING "URL for the help menu" ) set( APPLICATION_HELP_URL "" CACHE string "URL for the help menu" )
set( APPLICATION_ICON_NAME "Nextcloud" ) set( APPLICATION_ICON_NAME "Nextcloud" )
set( APPLICATION_SERVER_URL "" CACHE STRING "URL for the server to use. If entered the server can only connect to this instance" ) set( APPLICATION_SERVER_URL "" CACHE string "URL for the server to use. If entered the server can only connect to this instance" )
set( LINUX_PACKAGE_SHORTNAME "nextcloud" ) set( LINUX_PACKAGE_SHORTNAME "nextcloud" )
@@ -20,14 +20,14 @@ set( MAC_INSTALLER_BACKGROUND_FILE "${CMAKE_SOURCE_DIR}/admin/osx/installer-back
# set( APPLICATION_LICENSE "${OEM_THEME_DIR}/license.txt ) # set( APPLICATION_LICENSE "${OEM_THEME_DIR}/license.txt )
option( WITH_CRASHREPORTER "Build crashreporter" OFF ) option( WITH_CRASHREPORTER "Build crashreporter" OFF )
#set( CRASHREPORTER_SUBMIT_URL "https://crash-reports.owncloud.com/submit" CACHE STRING "URL for crash reporter" ) #set( CRASHREPORTER_SUBMIT_URL "https://crash-reports.owncloud.com/submit" CACHE string "URL for crash reporter" )
#set( CRASHREPORTER_ICON ":/owncloud-icon.png" ) #set( CRASHREPORTER_ICON ":/owncloud-icon.png" )
option( WITH_PROVIDERS "Build with providers list" ON ) option( WITH_PROVIDERS "Build with providers list" ON )
## Theming options ## Theming options
set( APPLICATION_WIZARD_HEADER_BACKGROUND_COLOR "#0082c9" CACHE STRING "Hex color of the wizard header background") set( APPLICATION_WIZARD_HEADER_BACKGROUND_COLOR "#0082c9" CACHE string "Hex color of the wizard header background")
set( APPLICATION_WIZARD_HEADER_TITLE_COLOR "#ffffff" CACHE STRING "Hex color of the text in the wizard header") set( APPLICATION_WIZARD_HEADER_TITLE_COLOR "#ffffff" CACHE string "Hex color of the text in the wizard header")
option( APPLICATION_WIZARD_USE_CUSTOM_LOGO "Use the logo from ':/client/theme/colored/wizard_logo.png' else the default application icon is used" ON ) option( APPLICATION_WIZARD_USE_CUSTOM_LOGO "Use the logo from ':/client/theme/colored/wizard_logo.png' else the default application icon is used" ON )

View File

@@ -1,6 +1,6 @@
set( MIRALL_VERSION_MAJOR 2 ) set( MIRALL_VERSION_MAJOR 2 )
set( MIRALL_VERSION_MINOR 6 ) set( MIRALL_VERSION_MINOR 5 )
set( MIRALL_VERSION_PATCH 2 ) set( MIRALL_VERSION_PATCH 3 )
set( MIRALL_VERSION_YEAR 2019 ) set( MIRALL_VERSION_YEAR 2019 )
set( MIRALL_SOVERSION 0 ) set( MIRALL_SOVERSION 0 )

View File

@@ -14,7 +14,6 @@ RUN apt-get update -q && DEBIAN_FRONTEND=noninteractive apt-get install -q -y --
libsqlite3-dev \ libsqlite3-dev \
libssl-dev \ libssl-dev \
libcmocka-dev \ libcmocka-dev \
libcloudproviders-dev \
qt5-default \ qt5-default \
qttools5-dev-tools \ qttools5-dev-tools \
libqt5webkit5-dev \ libqt5webkit5-dev \

View File

@@ -6,7 +6,7 @@ mkdir /app
mkdir /build mkdir /build
#Set Qt-5.12 #Set Qt-5.12
export QT_BASE_DIR=/opt/qt5.12.5 export QT_BASE_DIR=/opt/qt512
export QTDIR=$QT_BASE_DIR export QTDIR=$QT_BASE_DIR
export PATH=$QT_BASE_DIR/bin:$PATH export PATH=$QT_BASE_DIR/bin:$PATH
export LD_LIBRARY_PATH=$QT_BASE_DIR/lib/x86_64-linux-gnu:$QT_BASE_DIR/lib:$LD_LIBRARY_PATH export LD_LIBRARY_PATH=$QT_BASE_DIR/lib/x86_64-linux-gnu:$QT_BASE_DIR/lib:$LD_LIBRARY_PATH
@@ -66,8 +66,7 @@ rm -rf ./usr/share/nemo-python/
mv ./etc/Nextcloud/sync-exclude.lst ./usr/bin/ mv ./etc/Nextcloud/sync-exclude.lst ./usr/bin/
rm -rf ./etc rm -rf ./etc
DESKTOP_FILE=/app/usr/share/applications/${LINUX_APPLICATION_ID}.desktop sed -i -e 's|Icon=nextcloud|Icon=Nextcloud|g' usr/share/applications/nextcloud.desktop # Bug in desktop file?
sed -i -e 's|Icon=nextcloud|Icon=Nextcloud|g' ${DESKTOP_FILE} # Bug in desktop file?
cp ./usr/share/icons/hicolor/512x512/apps/Nextcloud.png . # Workaround for linuxeployqt bug, FIXME cp ./usr/share/icons/hicolor/512x512/apps/Nextcloud.png . # Workaround for linuxeployqt bug, FIXME
@@ -88,12 +87,17 @@ chmod a+x linuxdeployqt*.AppImage
rm ./linuxdeployqt-continuous-x86_64.AppImage rm ./linuxdeployqt-continuous-x86_64.AppImage
unset QTDIR; unset QT_PLUGIN_PATH ; unset LD_LIBRARY_PATH unset QTDIR; unset QT_PLUGIN_PATH ; unset LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/app/usr/lib/ export LD_LIBRARY_PATH=/app/usr/lib/
./squashfs-root/AppRun ${DESKTOP_FILE} -bundle-non-qt-libs ./squashfs-root/AppRun /app/usr/share/applications/nextcloud.desktop -bundle-non-qt-libs
# Set origin # Set origin
./squashfs-root/usr/bin/patchelf --set-rpath '$ORIGIN/' /app/usr/lib/libnextcloudsync.so.0 ./squashfs-root/usr/bin/patchelf --set-rpath '$ORIGIN/' /app/usr/lib/libnextcloudsync.so.0
# Build AppImage # Build AppImage
./squashfs-root/AppRun ${DESKTOP_FILE} -appimage ./squashfs-root/AppRun /app/usr/share/applications/nextcloud.desktop -appimage
mv Nextcloud*.AppImage Nextcloud-${SUFFIX}-${DRONE_COMMIT}-x86_64.AppImage mv Nextcloud*.AppImage Nextcloud-${SUFFIX}-${DRONE_COMMIT}-x86_64.AppImage
curl --upload-file $(readlink -f ./Nextcloud*.AppImage) https://transfer.sh/Nextcloud-${SUFFIX}-${DRONE_COMMIT}-x86_64.AppImage
echo
echo "Get the AppImage at the link above!"

View File

@@ -1,22 +1,22 @@
nextcloud-client (2.3.3-1.0~eoan1) eoan; urgency=medium nextcloud-client (2.3.3-1.0~cosmic1) cosmic; urgency=medium
* Debian build support for the forked client. * Debian build support for the forked client.
-- István Váradi <ivaradi@varadiistvan.hu> Mon, 6 Nov 2017 20:20:04 +0100 -- István Váradi <ivaradi@varadiistvan.hu> Mon, 6 Nov 2017 20:20:04 +0100
nextcloud-client (2.3.1-1.0~eoan1) eoan; urgency=medium nextcloud-client (2.3.1-1.0~cosmic1) cosmic; urgency=medium
* New upstream version * New upstream version
-- István Váradi <ivaradi@varadiistvan.hu> Thu, 23 Mar 2017 19:07:36 +0100 -- István Váradi <ivaradi@varadiistvan.hu> Thu, 23 Mar 2017 19:07:36 +0100
nextcloud-client (2.3.0-1.0~eoan1) eoan; urgency=medium nextcloud-client (2.3.0-1.0~cosmic1) cosmic; urgency=medium
* New upstream version * New upstream version
-- István Váradi <ivaradi@varadiistvan.hu> Tue, 21 Mar 2017 19:34:13 +0100 -- István Váradi <ivaradi@varadiistvan.hu> Tue, 21 Mar 2017 19:34:13 +0100
nextcloud-client (2.2.4-1.4~eoan1) eoan; urgency=medium nextcloud-client (2.2.4-1.4~cosmic1) cosmic; urgency=medium
* The locale-specific icon names are correct too * The locale-specific icon names are correct too

View File

@@ -1,90 +0,0 @@
Source: nextcloud-client
Section: contrib/devel
Priority: optional
Maintainer: István Váradi <ivaradi@varadiistvan.hu>
Build-Depends: cmake,
debhelper,
cdbs,
dh-python,
extra-cmake-modules (>= 5.16),
libkf5kio-dev,
libcmocka-dev,
libcloudproviders-dev,
libdbus-1-dev,
libhttp-dav-perl,
libinotify-dev [kfreebsd-any],
libqt5svg5-dev,
libqt5webkit5-dev,
libsqlite3-dev,
libssl-dev (>= 1.1.0),
zlib1g-dev,
optipng,
pkg-kde-tools,
python-sphinx | python3-sphinx,
python3-all,
qt5keychain-dev,
qtwebengine5-dev,
qtdeclarative5-dev,
qttools5-dev,
qttools5-dev-tools,
xvfb
Standards-Version: 3.9.8
Homepage: https://github.com/nextcloud/client_theming
#Vcs-Git: git://anonscm.debian.org/collab-maint/nextcloud-client.git
#Vcs-Browser: https://anonscm.debian.org/cgit/collab-maint/nextcloud-client.git
Package: nextcloud-client
Architecture: any
Depends: libnextcloudsync0 (=${binary:Version}), ${shlibs:Depends}, ${misc:Depends}, nextcloud-client-l10n
Description: Nextcloud desktop sync client
Use the desktop client to keep your files synchronized
between your Nextcloud server and your desktop. Select
one or more directories on your local machine and always
have access to your latest files wherever you are.
Package: libnextcloudsync0
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: Nextcloud sync library
Used by the Nextcloud desktop client as the synchronization engine.
Package: libnextcloudsync-dev
Architecture: any
Section: contrib/libdevel
Depends: libnextcloudsync0 (=${binary:Version}), ${misc:Depends}
Description: Nextcloud sync library development files
The headers and development library for the Nextcloud sync library.
Package: nextcloud-client-l10n
Architecture: all
Depends: ${misc:Depends}
Description: Nextcloud client internatialization files
The translation files.
Package: nextcloud-client-nautilus
Architecture: all
Depends: nextcloud-client (>=${binary:Version}), libnextcloudsync0, python-nautilus, nautilus, ${misc:Depends}
Description: Nautilus plugin for Nextcloud
This package contains a Nautilus plugin to display
synchronization status icons for Nextcloud files.
Package: nextcloud-client-nemo
Architecture: all
Depends: nextcloud-client (>=${binary:Version}), libnextcloudsync0, python-nemo | nemo-python, nemo, ${misc:Depends}
Description: Nemo plugin for Nextcloud
This package contains a Nemo plugin to display
synchronization status icons for Nextcloud files.
Package: nextcloud-client-caja
Architecture: all
Depends: nextcloud-client (>=${binary:Version}), libnextcloudsync0, python-caja, caja, ${misc:Depends}
Description: Caja plugin for Nextcloud
This package contains a Caja plugin to display
synchronization status icons for Nextcloud files.
Package: nextcloud-client-dolphin
Architecture: any
Depends: dolphin (>= 4:15.12.1), libnextcloudsync0 (= ${binary:Version}), nextcloud-client, ${misc:Depends}, ${shlibs:Depends}
Description: Dolphin plugin for Nextcloud
This package contains a Dolphin plugin to display
synchronization status icons for Nextcloud files.

View File

@@ -1,48 +0,0 @@
nextcloud-client (2.3.3-1.0~oldstable1) oldstable; urgency=medium
* Debian build support for the forked client.
-- István Váradi <ivaradi@varadiistvan.hu> Mon, 6 Nov 2017 20:20:04 +0100
nextcloud-client (2.3.1-1.0) oldstable; urgency=medium
* New upstream version
-- István Váradi <ivaradi@varadiistvan.hu> Thu, 23 Mar 2017 19:07:36 +0100
nextcloud-client (2.3.0-1.0) oldstable; urgency=medium
* New upstream version
-- István Váradi <ivaradi@varadiistvan.hu> Tue, 21 Mar 2017 19:34:13 +0100
nextcloud-client (2.2.4-1.4) oldstable; urgency=medium
* The locale-specific icon names are correct too
-- István Váradi <ivaradi@varadiistvan.hu> Tue, 7 Feb 2017 19:55:40 +0100
nextcloud-client (2.2.4-1.3) oldstable; urgency=medium
* Caja syncstate plugin is built.
* The syncstate plugin has application-specific name
-- István Váradi <ivaradi@varadiistvan.hu> Fri, 27 Jan 2017 19:34:18 +0100
nextcloud-client (2.2.4-1.2) oldstable; urgency=medium
* Fixed appname in the Nemo syncstate extension.
-- István Váradi <ivaradi@varadiistvan.hu> Thu, 19 Jan 2017 16:46:50 +0100
nextcloud-client (2.2.4-1.1) oldstable; urgency=medium
* Added Nautilus and Nemo syncstate extensions.
-- István Váradi <ivaradi@varadiistvan.hu> Tue, 17 Jan 2017 19:55:32 +0100
nextcloud-client (2.2.4-1.0) oldstable; urgency=medium
* Initial release.
-- István Váradi <ivaradi@varadiistvan.hu> Wed, 14 Dec 2016 20:07:46 +0100

View File

@@ -1,84 +0,0 @@
Source: nextcloud-client
Section: contrib/devel
Priority: optional
Maintainer: István Váradi <ivaradi@varadiistvan.hu>
Build-Depends: cmake,
debhelper,
cdbs,
dh-python,
extra-cmake-modules (>= 5.16),
kdelibs5-dev,
kio-dev,
libcmocka-dev,
libdbus-1-dev,
libhttp-dav-perl,
libinotify-dev [kfreebsd-any],
libqt5webkit5-dev,
libqt5svg5-dev,
libsqlite3-dev,
libssl-dev (>= 1.1.0),
zlib1g-dev,
optipng,
pkg-kde-tools,
python-sphinx | python3-sphinx,
python3-all,
qt5keychain-dev,
qtwebengine5-dev,
qtdeclarative5-dev,
qttools5-dev,
qttools5-dev-tools,
xvfb
Standards-Version: 3.9.8
Homepage: https://github.com/nextcloud/client_theming
#Vcs-Git: git://anonscm.debian.org/collab-maint/nextcloud-client.git
#Vcs-Browser: https://anonscm.debian.org/cgit/collab-maint/nextcloud-client.git
Package: nextcloud-client
Architecture: any
Depends: libnextcloudsync0 (=${binary:Version}), ${shlibs:Depends}, ${misc:Depends}, nextcloud-client-l10n
Recommends: libgnome-keyring0
Description: Nextcloud desktop sync client
Use the desktop client to keep your files synchronized
between your Nextcloud server and your desktop. Select
one or more directories on your local machine and always
have access to your latest files wherever you are.
Package: libnextcloudsync0
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: Nextcloud sync library
Used by the Nextcloud desktop client as the synchronization engine.
Package: libnextcloudsync-dev
Architecture: any
Section: contrib/libdevel
Depends: libnextcloudsync0 (=${binary:Version}), ${misc:Depends}
Description: Nextcloud sync library development files
The headers and development library for the Nextcloud sync library.
Package: nextcloud-client-l10n
Architecture: all
Depends: ${misc:Depends}
Description: Nextcloud client internatialization files
The translation files.
Package: nextcloud-client-nautilus
Architecture: all
Depends: nextcloud-client (>=${binary:Version}), libnextcloudsync0, python-nautilus, nautilus, ${misc:Depends}
Description: Nautilus plugin for Nextcloud
This package contains a Nautilus plugin to display
synchronization status icons for Nextcloud files.
Package: nextcloud-client-nemo
Architecture: all
Depends: nextcloud-client (>=${binary:Version}), libnextcloudsync0, python-nemo, nemo, ${misc:Depends}
Description: Nemo plugin for Nextcloud
This package contains a Nemo plugin to display
synchronization status icons for Nextcloud files.
Package: nextcloud-client-caja
Architecture: all
Depends: nextcloud-client (>=${binary:Version}), libnextcloudsync0, python-caja, caja, ${misc:Depends}
Description: Caja plugin for Nextcloud
This package contains a Caja plugin to display
synchronization status icons for Nextcloud files.

View File

@@ -9,10 +9,7 @@ Build-Depends: cmake,
extra-cmake-modules (>= 5.16), extra-cmake-modules (>= 5.16),
kdelibs5-dev, kdelibs5-dev,
kio-dev, kio-dev,
libavcodec58,
libcmocka-dev, libcmocka-dev,
libcloudproviders-dev,
libdbus-1-dev,
libhttp-dav-perl, libhttp-dav-perl,
libinotify-dev [kfreebsd-any], libinotify-dev [kfreebsd-any],
libqt5webkit5-dev, libqt5webkit5-dev,

View File

@@ -1,12 +1,40 @@
--- nextcloud-client-2.5.3.orig/src/3rdparty/kmessagewidget/kmessagewidget.cpp 2019-07-26 18:40:34.949349387 +0000 --- nextcloud-client-2.4.0.orig/src/gui/wizard/owncloudoauthcredspage.cpp
+++ nextcloud-client-2.5.3/src/3rdparty/kmessagewidget/kmessagewidget.cpp 2019-07-26 18:41:39.866478051 +0000 +++ nextcloud-client-2.4.0/src/gui/wizard/owncloudoauthcredspage.cpp
@@ -105,6 +105,9 @@ @@ -53,10 +53,8 @@ OwncloudOAuthCredsPage::OwncloudOAuthCredsPage()
q->setMessageType(KMessageWidget::Information); _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 */
} }
+template <typename T> +void OwncloudOAuthCredsPage::copyLinkToClipboard()
+constexpr typename std::add_const<T>::type &qAsConst(T &t) noexcept { return t; } +{
+ if (_asyncAuth)
+ QApplication::clipboard()->setText(_asyncAuth->authorisationLink().toString(QUrl::FullyEncoded));
+}
+ +
void KMessageWidgetPrivate::createLayout() +
{ } // namespace OCC
delete content->layout(); --- 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

@@ -10,8 +10,6 @@ Build-Depends: cmake,
kdelibs5-dev, kdelibs5-dev,
libkf5kio-dev, libkf5kio-dev,
libcmocka-dev, libcmocka-dev,
libcloudproviders-dev,
libdbus-1-dev,
libhttp-dav-perl, libhttp-dav-perl,
libinotify-dev [kfreebsd-any], libinotify-dev [kfreebsd-any],
libqt5svg5-dev, libqt5svg5-dev,

View File

@@ -51,7 +51,7 @@ if ! wget http://ppa.launchpad.net/${repo}/ubuntu/pool/main/n/nextcloud-client/n
origsourceopt="-sa" origsourceopt="-sa"
fi fi
for distribution in xenial bionic disco eoan stable oldstable; do for distribution in xenial bionic cosmic disco stable; do
rm -rf nextcloud-client_${basever} rm -rf nextcloud-client_${basever}
cp -a ${DRONE_WORKSPACE} nextcloud-client_${basever} cp -a ${DRONE_WORKSPACE} nextcloud-client_${basever}
@@ -98,46 +98,26 @@ if test "${pull_request}" = "master"; then
PPA=$PPA_BETA PPA=$PPA_BETA
OBS_PROJECT=$OBS_PROJECT_BETA OBS_PROJECT=$OBS_PROJECT_BETA
fi fi
OBS_SUBDIR="${OBS_PROJECT}/${OBS_PACKAGE}"
if test -f ~/.has_ppa_keys; then if test -f ~/.has_ppa_keys; then
for changes in nextcloud-client_*~+([a-z])1_source.changes; do for changes in nextcloud-client_*~+([a-z])1_source.changes; do
case "${changes}" in dput $PPA $changes > /dev/null
*oldstable1*)
;;
*)
dput $PPA $changes > /dev/null
;;
esac
done done
for distribution in stable oldstable; do mkdir osc
if test "${distribution}" = "oldstable"; then cd osc
pkgsuffix=".${distribution}" osc co ${OBS_PROJECT} ${OBS_PACKAGE}
pkgvertag="~${distribution}1" if test "$(ls ${OBS_SUBDIR})"; then
else osc delete ${OBS_SUBDIR}/*
pkgsuffix="" fi
pkgvertag="" cp ../nextcloud-client*.orig.tar.* ${OBS_SUBDIR}/
fi cp ../nextcloud-client_*[0-9.][0-9].dsc ${OBS_SUBDIR}/
cp ../nextcloud-client_*[0-9.][0-9].debian.tar* ${OBS_SUBDIR}/
cp ../nextcloud-client_*[0-9.][0-9]_source.changes ${OBS_SUBDIR}/
osc add ${OBS_SUBDIR}/*
package="${OBS_PACKAGE}${pkgsuffix}" cd ${OBS_SUBDIR}
OBS_SUBDIR="${OBS_PROJECT}/${package}" osc commit -m "Travis update"
mkdir -p osc
pushd osc
osc co ${OBS_PROJECT} ${package}
if test "$(ls ${OBS_SUBDIR})"; then
osc delete ${OBS_SUBDIR}/*
fi
cp ../nextcloud-client*.orig.tar.* ${OBS_SUBDIR}/
cp ../nextcloud-client_*[0-9.][0-9]${pkgvertag}.dsc ${OBS_SUBDIR}/
cp ../nextcloud-client_*[0-9.][0-9]${pkgvertag}.debian.tar* ${OBS_SUBDIR}/
cp ../nextcloud-client_*[0-9.][0-9]${pkgvertag}_source.changes ${OBS_SUBDIR}/
osc add ${OBS_SUBDIR}/*
cd ${OBS_SUBDIR}
osc commit -m "Travis update"
popd
done
fi fi
fi fi

View File

@@ -70,9 +70,7 @@ def collectEntries(baseCommit, baseVersion, kind):
lastVersionTag = None lastVersionTag = None
lastCMAKEVersion = None lastCMAKEVersion = None
for line in output.splitlines(): for line in output.splitlines():
words = line.split("\t") (commit, name, email, date, revdate, subject) = line.split("\t")
(commit, name, email, date, revdate) = words[0:5]
subject = "\t".join(words[5:])
revdate = datetime.datetime.utcfromtimestamp(long(revdate)).strftime("%Y%m%d.%H%M%S") revdate = datetime.datetime.utcfromtimestamp(long(revdate)).strftime("%Y%m%d.%H%M%S")
kind = "beta" kind = "beta"

View File

@@ -1,21 +0,0 @@
#! /bin/bash
set -xe
cd /build
# Upload AppImage
APPIMAGE=$(readlink -f ./Nextcloud*.AppImage)
BASENAME=$(basename ${APPIMAGE})
if curl --max-time 900 --upload-file ${APPIMAGE} https://transfer.sh/${BASENAME}
then
echo
echo "Get the AppImage at the link above!"
else
echo
echo "Upload failed, however this is an optional step."
fi
# Don't let the Drone build fail
exit 0

View File

@@ -49,7 +49,7 @@ fi
if [ ! -z "$identity" ]; then if [ ! -z "$identity" ]; then
echo "Will try to sign the installer" echo "Will try to sign the installer"
pushd $install_path pushd $install_path
productsign --timestamp --sign "$identity" "$installer_file" "$installer_file.new" productsign --sign "$identity" "$installer_file" "$installer_file.new"
mv "$installer_file".new "$installer_file" mv "$installer_file".new "$installer_file"
popd popd
else else

View File

@@ -32,12 +32,11 @@ FRAMEWORK_SEARCH_PATH=[
os.path.join(os.environ['HOME'], 'Library/Frameworks') os.path.join(os.environ['HOME'], 'Library/Frameworks')
] ]
LIBRARY_SEARCH_PATH=['/usr/local/lib', '/usr/local/Qt-5.12.5/lib', '.'] LIBRARY_SEARCH_PATH=['/usr/local/lib', '/usr/local/Qt-5.6.2/lib', '.']
QT_PLUGINS = [ QT_PLUGINS = [
'sqldrivers/libqsqlite.dylib', 'sqldrivers/libqsqlite.dylib',
'platforms/libqcocoa.dylib', 'platforms/libqcocoa.dylib',
'styles/libqmacstyle.dylib',
'imageformats/libqgif.dylib', 'imageformats/libqgif.dylib',
'imageformats/libqico.dylib', 'imageformats/libqico.dylib',
'imageformats/libqjpeg.dylib', 'imageformats/libqjpeg.dylib',
@@ -47,7 +46,7 @@ QT_PLUGINS = [
QT_PLUGINS_SEARCH_PATH=[ QT_PLUGINS_SEARCH_PATH=[
# os.path.join(os.environ['QTDIR'], 'plugins'), # os.path.join(os.environ['QTDIR'], 'plugins'),
# '/usr/local/Cellar/qt/5.2.1/plugins', # '/usr/local/Cellar/qt/5.2.1/plugins',
'/usr/local/Qt-5.12.5/plugins', '/usr/local/Qt-5.6.2/plugins',
] ]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

After

Width:  |  Height:  |  Size: 151 KiB

2
binary

Submodule binary updated: 09f12de312...3425fab2c6

View File

@@ -38,7 +38,6 @@
<file>resources/copy.svg</file> <file>resources/copy.svg</file>
<file>resources/state-sync.svg</file> <file>resources/state-sync.svg</file>
<file>resources/add.png</file> <file>resources/add.png</file>
<file>resources/state-info.svg</file>
</qresource> </qresource>
<qresource prefix="/"/> <qresource prefix="/"/>
</RCC> </RCC>

View File

@@ -47,13 +47,7 @@ macro (KDE4_ADD_APP_ICON appsources pattern)
endif (fn MATCHES ".*128.*") endif (fn MATCHES ".*128.*")
if (fn MATCHES ".*256.*" ) if (fn MATCHES ".*256.*" )
list (APPEND _icons ${it}) list (APPEND _icons ${it})
endif (fn MATCHES ".*256.*") endif (fn MATCHES ".*256.*")
if (fn MATCHES ".*512.*" )
list (APPEND _icons ${it})
endif (fn MATCHES ".*512.*")
if (fn MATCHES ".*1024.*" )
list (APPEND _icons ${it})
endif (fn MATCHES ".*1024.*")
endforeach (it) endforeach (it)
if (_icons) if (_icons)
add_custom_command(OUTPUT ${_outfilename}.ico ${_outfilename}.rc add_custom_command(OUTPUT ${_outfilename}.ico ${_outfilename}.rc
@@ -110,14 +104,14 @@ macro (KDE4_ADD_APP_ICON appsources pattern)
foreach (it ${files}) foreach (it ${files})
if (it MATCHES ".*sidebar-16.*") if (it MATCHES ".*sidebar-16.*")
configure_file(${it} ${appsources}.iconset/sidebar_16x16.png COPYONLY) configure_file(${it} ${appsources}.iconset/sidebar_16x16.png COPYONLY)
elseif (it MATCHES ".*sidebar-32.*") elseif (it MATCHES ".*sidebar-18.*")
configure_file(${it} ${appsources}.iconset/sidebar_18x18.png COPYONLY) configure_file(${it} ${appsources}.iconset/sidebar_18x18.png COPYONLY)
elseif (it MATCHES ".*sidebar-32.*")
configure_file(${it} ${appsources}.iconset/sidebar_16x16@2x.png COPYONLY) configure_file(${it} ${appsources}.iconset/sidebar_16x16@2x.png COPYONLY)
elseif (it MATCHES ".*sidebar-64.*")
configure_file(${it} ${appsources}.iconset/sidebar_18x18@2x.png COPYONLY)
elseif (it MATCHES ".*sidebar-128.*")
configure_file(${it} ${appsources}.iconset/sidebar_32x32.png COPYONLY) configure_file(${it} ${appsources}.iconset/sidebar_32x32.png COPYONLY)
elseif (it MATCHES ".*sidebar-256.*") elseif (it MATCHES ".*sidebar-36.*")
configure_file(${it} ${appsources}.iconset/sidebar_18x18@2x.png COPYONLY)
elseif (it MATCHES ".*sidebar-64.*")
configure_file(${it} ${appsources}.iconset/sidebar_32x32@2x.png COPYONLY) configure_file(${it} ${appsources}.iconset/sidebar_32x32@2x.png COPYONLY)
endif() endif()
endforeach (it) endforeach (it)

View File

@@ -8,8 +8,8 @@
# #
# ecm_add_app_icon(<sources_var> # ecm_add_app_icon(<sources_var>
# ICONS <icon> [<icon> [...]] # ICONS <icon> [<icon> [...]]
# [SIDEBAR_ICONS <icon> [<icon> [...]] # Since 5.49 # [SIDEBAR_ICONS <icon> [<icon> [...]] # Since 5.4x
# [OUTFILE_BASENAME <name>]) # Since 5.49 # [OUTFILE_BASE <name>]) # Since 5.4x
# ) # )
# #
# The given icons, whose names must match the pattern:: # The given icons, whose names must match the pattern::
@@ -27,21 +27,20 @@
# #
# ``SIDEBAR_ICONS`` can be used to add Mac OS X sidebar # ``SIDEBAR_ICONS`` can be used to add Mac OS X sidebar
# icons to the generated iconset. They are used when a folder monitored by the # icons to the generated iconset. They are used when a folder monitored by the
# application is dragged into Finder's sidebar. Since 5.49. # application is dragged into Finder's sidebar. Since 5.4x.
# #
# ``OUTFILE_BASENAME`` will be used as the basename for the icon file. If # ``OUTFILE_BASE`` will be used as the basename for the icon file. If
# you specify it, the icon file will be called ``<OUTFILE_BASENAME>.icns`` on Mac OS X # you specify it, the icon file will be called ``<OUTFILE_BASE>.icns`` on Mac OS X
# and ``<OUTFILE_BASENAME>.ico`` on Windows. If you don't specify it, it defaults # and ``<OUTFILE_BASE>.ico`` on Windows. If you don't specify it, it defaults
# to ``<sources_var>.<ext>``. Since 5.49. # to ``<sources_var>.<ext>``. Since 5.4x.
# #
# #
# Windows notes # Windows notes
# * Icons are compiled into the executable using a resource file. # * Icons are compiled into the executable using a resource file.
# * Icons may not show up in Windows Explorer if the executable # * Icons may not show up in Windows Explorer if the executable
# target does not have the ``WIN32_EXECUTABLE`` property set. # target does not have the ``WIN32_EXECUTABLE`` property set.
# * One of the tools png2ico (See :find-module:`FindPng2Ico`) or # * The tool png2ico is required. See :find-module:`FindPng2Ico`.
# icotool (see :find-module:`FindIcoTool`) is required. # * Supported sizes: 16, 32, 48, 64, 128.
# * Supported sizes: 16, 24, 32, 48, 64, 128, 256, 512 and 1024.
# #
# Mac OS X notes # Mac OS X notes
# * The executable target must have the ``MACOSX_BUNDLE`` property set. # * The executable target must have the ``MACOSX_BUNDLE`` property set.
@@ -102,7 +101,7 @@ include(CMakeParseArguments)
function(ecm_add_app_icon appsources) function(ecm_add_app_icon appsources)
set(options) set(options)
set(oneValueArgs OUTFILE_BASENAME) set(oneValueArgs OUTFILE_BASE)
set(multiValueArgs ICONS SIDEBAR_ICONS) set(multiValueArgs ICONS SIDEBAR_ICONS)
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
@@ -139,9 +138,9 @@ function(ecm_add_app_icon appsources)
endif() endif()
_ecm_add_app_icon_categorize_icons("${ARG_ICONS}" "icons" "16;24;32;48;64;128;256;512;1024") _ecm_add_app_icon_categorize_icons("${ARG_ICONS}" "icons" "16;32;48;64;128;256;512;1024")
if(ARG_SIDEBAR_ICONS) if(ARG_SIDEBAR_ICONS)
_ecm_add_app_icon_categorize_icons("${ARG_SIDEBAR_ICONS}" "sidebar_icons" "16;32;64;128;256") _ecm_add_app_icon_categorize_icons("${ARG_SIDEBAR_ICONS}" "sidebar_icons" "16;18;32;36;64")
endif() endif()
set(mac_icons set(mac_icons
@@ -152,37 +151,31 @@ function(ecm_add_app_icon appsources)
${icons_at_128px} ${icons_at_128px}
${icons_at_256px} ${icons_at_256px}
${icons_at_512px} ${icons_at_512px}
${icons_at_1024px}) ${icons_at_1024px}
set(mac_sidebar_icons
# Sidebar Icons: https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/Finder.html#//apple_ref/doc/uid/TP40014214-CH15-SW15 # Sidebar Icons: https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/Finder.html#//apple_ref/doc/uid/TP40014214-CH15-SW15
${sidebar_icons_at_16px} ${sidebar_icons_at_16px}
${sidebar_icons_at_18px}
${sidebar_icons_at_32px} ${sidebar_icons_at_32px}
${sidebar_icons_at_64px} ${sidebar_icons_at_36px}
${sidebar_icons_at_128px} ${sidebar_icons_at_64px})
${sidebar_icons_at_256px}) if (NOT icons_at_128px)
message(AUTHOR_WARNING "No 128px icon provided; this will not work on Mac OS X")
if (NOT (mac_icons OR mac_sidebar_icons))
message(AUTHOR_WARNING "No icons suitable for use on macOS provided")
endif() endif()
set(windows_icons ${icons_at_16px} set(windows_icons ${icons_at_16px}
${icons_at_24px} ${icons_at_32px}
${icons_at_32px} ${icons_at_48px}
${icons_at_48px} ${icons_at_64px}
${icons_at_64px} ${icons_at_128px}
${icons_at_128px} ${icons_at_256px})
${icons_at_256px} if (NOT windows_icons)
${icons_at_512px}
${icons_at_1024px})
if (NOT (windows_icons))
message(AUTHOR_WARNING "No icons suitable for use on Windows provided") message(AUTHOR_WARNING "No icons suitable for use on Windows provided")
endif() endif()
if (ARG_OUTFILE_BASENAME) if (ARG_OUTFILE_BASE)
set (_outfilebasename "${ARG_OUTFILE_BASENAME}") set (_outfilebasename "${ARG_OUTFILE_BASE}")
else() else()
set (_outfilebasename "${appsources}") set (_outfilebasename "${appsources}")
endif() endif()
@@ -192,15 +185,26 @@ function(ecm_add_app_icon appsources)
set(saved_CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}") set(saved_CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}")
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_FIND_MODULE_DIR}) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_FIND_MODULE_DIR})
find_package(Png2Ico) find_package(Png2Ico)
find_package(IcoTool)
set(CMAKE_MODULE_PATH "${saved_CMAKE_MODULE_PATH}") set(CMAKE_MODULE_PATH "${saved_CMAKE_MODULE_PATH}")
function(create_windows_icon_and_rc command args deps) if (Png2Ico_FOUND)
if (Png2Ico_HAS_RCFILE_ARGUMENT)
add_custom_command(
OUTPUT "${_outfilename}.rc" "${_outfilename}.ico"
COMMAND Png2Ico::Png2Ico
ARGS
--rcfile "${_outfilename}.rc"
"${_outfilename}.ico"
${windows_icons}
DEPENDS ${windows_icons}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
)
else()
add_custom_command( add_custom_command(
OUTPUT "${_outfilename}.ico" OUTPUT "${_outfilename}.ico"
COMMAND ${command} COMMAND Png2Ico::Png2Ico
ARGS ${args} ARGS "${_outfilename}.ico" ${windows_icons}
DEPENDS ${deps} DEPENDS ${windows_icons}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
) )
# this bit's a little hacky to make the dependency stuff work # this bit's a little hacky to make the dependency stuff work
@@ -212,72 +216,12 @@ function(ecm_add_app_icon appsources)
DEPENDS "${_outfilename}.ico" DEPENDS "${_outfilename}.ico"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
) )
endfunction() endif()
if (IcoTool_FOUND)
list(APPEND icotool_args "-c" "-o" "${_outfilename}.ico")
# According to https://stackoverflow.com/a/40851713/2886832
# Windows always chooses the first icon above 255px, all other ones will be ignored
set(maxSize 0)
foreach(size 256 512 1024)
if(icons_at_${size}px)
set(maxSize "${size}")
endif()
endforeach()
foreach(size 16 24 32 48 64 128 ${maxSize})
if(NOT icons_at_${size}px)
continue()
endif()
set(icotool_icon_arg "")
if(size STREQUAL "${maxSize}")
# maxSize icon needs to be included as raw png
list(APPEND icotool_args "-r")
endif()
foreach(icon ${icons_at_${size}px})
list(APPEND icotool_args "${icons_at_${size}px}")
endforeach()
endforeach()
create_windows_icon_and_rc(IcoTool::IcoTool "${icotool_args}" "${windows_icons_modern}")
set(${appsources} "${${appsources}};${_outfilename}.rc" PARENT_SCOPE) set(${appsources} "${${appsources}};${_outfilename}.rc" PARENT_SCOPE)
# standard png2ico has no rcfile argument
# NOTE: We generally use https://github.com/hiiamok/png2ImageMagickICO
# or similar on windows, which is why we provide resolutions >= 256px here.
# Standard png2ico will fail with this.
elseif(Png2Ico_FOUND AND NOT Png2Ico_HAS_RCFILE_ARGUMENT AND windows_icons)
set(png2ico_args)
list(APPEND png2ico_args "${_outfilename}.ico")
list(APPEND png2ico_args "${windows_icons}")
create_windows_icon_and_rc(Png2Ico::Png2Ico "${png2ico_args}" "${windows_icons}")
set(${appsources} "${${appsources}};${_outfilename}.rc" PARENT_SCOPE)
# png2ico from kdewin provides rcfile argument
elseif(Png2Ico_FOUND AND windows_icons)
add_custom_command(
OUTPUT "${_outfilename}.rc" "${_outfilename}.ico"
COMMAND Png2Ico::Png2Ico
ARGS
--rcfile "${_outfilename}.rc"
"${_outfilename}.ico"
${windows_icons}
DEPENDS ${windows_icons}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
)
set(${appsources} "${${appsources}};${_outfilename}.rc" PARENT_SCOPE)
# else none of the supported tools was found
else() else()
message(WARNING "Unable to find the png2ico or icotool utilities or icons in matching sizes - application will not have an application icon!") message(WARNING "Unable to find the png2ico utility - application will not have an application icon!")
endif() endif()
elseif (APPLE AND mac_icons)
elseif (APPLE AND (mac_icons OR mac_sidebar_icons))
# first generate .iconset directory structure, then convert to .icns format using the Mac OS X "iconutil" utility, # first generate .iconset directory structure, then convert to .icns format using the Mac OS X "iconutil" utility,
# to create retina compatible icon, you need png source files in pixel resolution 16x16, 32x32, 64x64, 128x128, # to create retina compatible icon, you need png source files in pixel resolution 16x16, 32x32, 64x64, 128x128,
# 256x256, 512x512, 1024x1024 # 256x256, 512x512, 1024x1024
@@ -302,11 +246,8 @@ function(ecm_add_app_icon appsources)
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
) )
list(APPEND iconset_icons list(APPEND iconset_icons
"${_outfilename}.iconset/${type}_${sizename}.png") "${_outfilename}.iconset/${type}_${sizename}.png")
endmacro() endmacro()
# List of supported sizes and filenames taken from:
# https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Optimizing/Optimizing.html#//apple_ref/doc/uid/TP40012302-CH7-SW4
foreach(size 16 32 128 256 512) foreach(size 16 32 128 256 512)
math(EXPR double_size "2 * ${size}") math(EXPR double_size "2 * ${size}")
foreach(file ${icons_at_${size}px}) foreach(file ${icons_at_${size}px})
@@ -317,25 +258,14 @@ function(ecm_add_app_icon appsources)
endforeach() endforeach()
endforeach() endforeach()
# List of supported sizes and filenames taken from: foreach(size 16 18 32)
# https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/Finder.html#//apple_ref/doc/uid/TP40014214-CH15-SW15 math(EXPR double_size "2 * ${size}")
foreach(file ${sidebar_icons_at_16px}) foreach(file ${sidebar_icons_at_${size}px})
copy_icon("${file}" "16x16" "sidebar") copy_icon("${file}" "${size}x${size}" "sidebar")
endforeach() endforeach()
foreach(file ${sidebar_icons_at_32px}) foreach(file ${sidebar_icons_at_${double_size}px})
copy_icon("${file}" "16x16@2x" "sidebar") copy_icon("${file}" "${size}x${size}@2x" "sidebar")
endforeach() endforeach()
foreach(file ${sidebar_icons_at_32px})
copy_icon("${file}" "18x18" "sidebar")
endforeach()
foreach(file ${sidebar_icons_at_64px})
copy_icon("${file}" "18x18@2x" "sidebar")
endforeach()
foreach(file ${sidebar_icons_at_128px})
copy_icon("${file}" "32x32" "sidebar")
endforeach()
foreach(file ${sidebar_icons_at_256px})
copy_icon("${file}" "32x32@2x" "sidebar")
endforeach() endforeach()
# generate .icns icon file # generate .icns icon file

View File

@@ -27,7 +27,7 @@
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>@MIRALL_VERSION_STRING@</string> <string>@MIRALL_VERSION_STRING@</string>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string>(C) 2014-2019 @APPLICATION_VENDOR@</string> <string>(C) 2014-2018 @APPLICATION_VENDOR@</string>
<key>NSSupportsAutomaticGraphicsSwitching</key> <key>NSSupportsAutomaticGraphicsSwitching</key>
<true/> <true/>
<key>SUShowReleaseNotes</key> <key>SUShowReleaseNotes</key>

View File

@@ -3,11 +3,7 @@
# For details see the accompanying COPYING* file. # For details see the accompanying COPYING* file.
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wno-long-long -Wno-gnu-zero-variadic-macro-arguments")
# Use this only for Clang
if (CMAKE_CXX_COMPILER MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wno-long-long -Wno-gnu-zero-variadic-macro-arguments")
endif()
# Fix sqlite compilation on macOS # Fix sqlite compilation on macOS
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-incompatible-pointer-types-discards-qualifiers") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-incompatible-pointer-types-discards-qualifiers")

View File

@@ -99,7 +99,7 @@ Activity
.. index:: activity, recent changes, sync activity .. index:: activity, recent changes, sync activity
The Activity window, which can be invoked either from the main menu (``Recent The Activity window, which can be invoked either from the main menu (``Recent
Changes -> View more activity``) or the Activity tab on the left side of the settings Changes -> Details``) or the Activity tab on the left side of the settings
window, provides an in-depth account of the recent sync activity. It will show window, provides an in-depth account of the recent sync activity. It will show
files that have not been synced because they are on the ignored files list, or files that have not been synced because they are on the ignored files list, or
because they cannot be synced in a cross-platform manner due to containing because they cannot be synced in a cross-platform manner due to containing

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 4.2333 4.2333" version="1.1" height="16" width="16"><g id="g3830" transform="matrix(.87498 0 0 .87498 .26458 -255.9)"><circle id="circle3818" stroke-width=".25066" fill="#2268ab" r="2.1167" cy="294.88" cx="2.1167" /><g id="g3828" stroke-linejoin="round" stroke-linecap="round" fill="none" /></g><path style="fill:#ffffff;stroke-width:0.17859235" d="m 1.6076619,2.1122981 c 0.027682,0.068222 0.058043,0.1232286 0.115014,0.043934 0.072686,-0.047862 0.314322,-0.2548509 0.29682,-0.061078 C 1.953774,2.4553739 1.8705497,2.8125586 1.8105428,3.1738508 1.7403561,3.3728027 1.9237704,3.5430012 2.1028984,3.4078068 2.295421,3.3181535 2.4582973,3.1779584 2.6256382,3.0488362 2.599921,2.9911507 2.5809903,2.9077482 2.5191973,2.9868644 2.4356161,3.0297263 2.2566665,3.2222491 2.2163047,3.07116 2.2725613,2.681829 2.3904322,2.3041062 2.4600833,1.9170966 2.5309844,1.7376113 2.3950755,1.5200858 2.210054,1.6736753 1.985742,1.7836882 1.8010774,1.9562083 1.6076619,2.1122981 Z M 2.4041839,0.77839186 C 2.1702279,0.77446305 2.0636081,1.1609366 2.2889917,1.2561264 2.4716917,1.3236342 2.659928,1.1286114 2.6086721,0.94358974 2.5911701,0.8467927 2.5018738,0.77035521 2.4038266,0.77749894 Z" /></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -467,7 +467,6 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_ENTITLEMENTS = FinderSyncExt/FinderSyncExt.entitlements; CODE_SIGN_ENTITLEMENTS = FinderSyncExt/FinderSyncExt.entitlements;
CODE_SIGN_IDENTITY = "-"; CODE_SIGN_IDENTITY = "-";
CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";

View File

@@ -35,11 +35,11 @@ public:
QString contextMenuTitle() const QString contextMenuTitle() const
{ {
return _strings.value("CONTEXT_MENU_TITLE", "Nextcloud"); return _strings.value("CONTEXT_MENU_TITLE", "ownCloud");
} }
QString shareActionTitle() const QString shareActionTitle() const
{ {
return _strings.value("SHARE_MENU_TITLE", "Share"); return _strings.value("SHARE_MENU_TITLE", "Share...");
} }
QString copyPrivateLinkTitle() const { return _strings["COPY_PRIVATE_LINK_MENU_TITLE"]; } QString copyPrivateLinkTitle() const { return _strings["COPY_PRIVATE_LINK_MENU_TITLE"]; }

View File

@@ -89,7 +89,7 @@ IFACEMETHODIMP OCContextMenu::Initialize(
HDROP hDrop = static_cast<HDROP>(GlobalLock(stm.hGlobal)); HDROP hDrop = static_cast<HDROP>(GlobalLock(stm.hGlobal));
if (hDrop) { if (hDrop) {
UINT nFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0); UINT nFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);
for (UINT i = 0; i < nFiles; ++i) { for (int i = 0; i < nFiles; ++i) {
// Get the path of the file. // Get the path of the file.
wchar_t buffer[MAX_PATH]; wchar_t buffer[MAX_PATH];

View File

@@ -5,8 +5,6 @@
// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
// Note: Here was a #define for windows target version #define WINVER 0x0501
// e.g. WINVER / _WIN32_WINNT, see https://devblogs.microsoft.com/oldnewthing/20070411-00/?p=27283 #define _WIN32_WINNT 0x0501
// Unnecessary because we define both in desktop/CMakeLists.txt
#include <SDKDDKVer.h> #include <SDKDDKVer.h>

View File

@@ -13,10 +13,8 @@
*/ */
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#define WINVER 0x0501
// Note: Here was a #define for windows target version #define _WIN32_WINNT 0x0501
// e.g. WINVER / _WIN32_WINNT, see https://devblogs.microsoft.com/oldnewthing/20070411-00/?p=27283
// Unnecessary because we define both in desktop/CMakeLists.txt
#include "CommunicationSocket.h" #include "CommunicationSocket.h"
#include "RegistryUtil.h" #include "RegistryUtil.h"

View File

@@ -1,9 +1,7 @@
#pragma once #pragma once
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#define WINVER 0x0501
// Note: Here was a #define for windows target version #define _WIN32_WINNT 0x0501
// e.g. WINVER / _WIN32_WINNT, see https://devblogs.microsoft.com/oldnewthing/20070411-00/?p=27283
// Unnecessary because we define both in desktop/CMakeLists.txt
#include <windows.h> #include <windows.h>

1
src/3rdparty/qtmacgoodies vendored Submodule

View File

@@ -29,17 +29,12 @@ if(NOT MSVC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_FORTIFY_SOURCE=2") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_FORTIFY_SOURCE=2")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_FORTIFY_SOURCE=2") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_FORTIFY_SOURCE=2")
endif() endif()
# Calling Qt's qCWarning(category, ...) with no params for "..." is a GNU
# extension (C++11 §16.3/4 forbids them). Silence clang's warnings.
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-gnu-zero-variadic-macro-arguments")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-gnu-zero-variadic-macro-arguments")
endif() endif()
if(WIN32) if(WIN32)
# Enable DEP & ASLR # Enable DEP & ASLR
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /nxcompat /dynamicbase") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--nxcompat -Wl,--dynamicbase")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /nxcompat /dynamicbase") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--nxcompat -Wl,--dynamicbase")
elseif(UNIX AND NOT APPLE) elseif(UNIX AND NOT APPLE)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,relro -Wl,-z,now") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,relro -Wl,-z,now")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,relro -Wl,-z,now") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,relro -Wl,-z,now")

View File

@@ -11,50 +11,43 @@
// For overloading macros by argument count // For overloading macros by argument count
// See stackoverflow.com/questions/16683146/can-macros-be-overloaded-by-number-of-arguments // See stackoverflow.com/questions/16683146/can-macros-be-overloaded-by-number-of-arguments
// Bugfix 08/09/2019: Broken arg expansion led to always collapsing to 1 arg (XXXX_1 overload result) #define OC_ASSERT_CAT(A, B) A##B
// See also: https://stackoverflow.com/questions/9183993/msvc-variadic-macro-expansion #define OC_ASSERT_SELECT(NAME, NUM) OC_ASSERT_CAT(NAME##_, NUM)
#define OC_ASSERT_GLUE(x, y) x y
#define OC_ASSERT_GET_COUNT(_1, _2, _3, COUNT, ...) COUNT #define OC_ASSERT_GET_COUNT(_1, _2, _3, COUNT, ...) COUNT
#define OC_ASSERT_EXPAND_ARGS(args) OC_ASSERT_GET_COUNT args #define OC_ASSERT_VA_SIZE(...) OC_ASSERT_GET_COUNT(__VA_ARGS__, 3, 2, 1, 0)
#define OC_ASSERT_VA_SIZE(...) OC_ASSERT_EXPAND_ARGS((__VA_ARGS__, 3, 2, 1, 0))
#define OC_ASSERT_SELECT2(NAME, COUNT) NAME##COUNT #define OC_ASSERT_OVERLOAD(NAME, ...) OC_ASSERT_SELECT(NAME, OC_ASSERT_VA_SIZE(__VA_ARGS__)) \
#define OC_ASSERT_SELECT1(NAME, COUNT) OC_ASSERT_SELECT2(NAME, COUNT) (__VA_ARGS__)
#define OC_ASSERT_SELECT(NAME, COUNT) OC_ASSERT_SELECT1(NAME, COUNT)
#define OC_ASSERT_OVERLOAD(NAME, ...) OC_ASSERT_GLUE(OC_ASSERT_SELECT(NAME, OC_ASSERT_VA_SIZE(__VA_ARGS__)), \
(__VA_ARGS__))
// Default assert: If the condition is false in debug builds, terminate. // Default assert: If the condition is false in debug builds, terminate.
// //
// Prints a message on failure, even in release builds. // Prints a message on failure, even in release builds.
#define ASSERT1(cond) \ #define ASSERT(...) OC_ASSERT_OVERLOAD(ASSERT, __VA_ARGS__)
#define ASSERT_1(cond) \
if (!(cond)) { \ if (!(cond)) { \
OC_ASSERT_MSG("ASSERT: \"%s\" in file %s, line %d", #cond, __FILE__, __LINE__); \ OC_ASSERT_MSG("ASSERT: \"%s\" in file %s, line %d", #cond, __FILE__, __LINE__); \
} else { \ } else { \
} }
#define ASSERT2(cond, message) \ #define ASSERT_2(cond, message) \
if (!(cond)) { \ if (!(cond)) { \
OC_ASSERT_MSG("ASSERT: \"%s\" in file %s, line %d with message: %s", #cond, __FILE__, __LINE__, message); \ OC_ASSERT_MSG("ASSERT: \"%s\" in file %s, line %d with message: %s", #cond, __FILE__, __LINE__, message); \
} else { \ } else { \
} }
#define ASSERT(...) OC_ASSERT_OVERLOAD(ASSERT, __VA_ARGS__)
// Enforce condition to be true, even in release builds. // Enforce condition to be true, even in release builds.
// //
// Prints 'message' and aborts execution if 'cond' is false. // Prints 'message' and aborts execution if 'cond' is false.
#define ENFORCE1(cond) \ #define ENFORCE(...) OC_ASSERT_OVERLOAD(ENFORCE, __VA_ARGS__)
#define ENFORCE_1(cond) \
if (!(cond)) { \ if (!(cond)) { \
qFatal("ENFORCE: \"%s\" in file %s, line %d", #cond, __FILE__, __LINE__); \ qFatal("ENFORCE: \"%s\" in file %s, line %d", #cond, __FILE__, __LINE__); \
} else { \ } else { \
} }
#define ENFORCE2(cond, message) \ #define ENFORCE_2(cond, message) \
if (!(cond)) { \ if (!(cond)) { \
qFatal("ENFORCE: \"%s\" in file %s, line %d with message: %s", #cond, __FILE__, __LINE__, message); \ qFatal("ENFORCE: \"%s\" in file %s, line %d with message: %s", #cond, __FILE__, __LINE__, message); \
} else { \ } else { \
} }
#define ENFORCE(...) OC_ASSERT_OVERLOAD(ENFORCE, __VA_ARGS__)
// An assert that is only present in debug builds: typically used for // An assert that is only present in debug builds: typically used for
// asserts that are too expensive for release mode. // asserts that are too expensive for release mode.

View File

@@ -207,10 +207,7 @@ static inline uint64_t c_jhash64(const uint8_t *k, uint64_t length, uint64_t int
/* handle the last 23 bytes */ /* handle the last 23 bytes */
c += length; c += length;
switch(len) { switch(len) {
// pragma only for GCC (and clang continues to pretend to be it by defining __GNUC__)
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
#endif
case 23: c+=((uint64_t)k[22]<<56); case 23: c+=((uint64_t)k[22]<<56);
case 22: c+=((uint64_t)k[21]<<48); case 22: c+=((uint64_t)k[21]<<48);
case 21: c+=((uint64_t)k[20]<<40); case 21: c+=((uint64_t)k[20]<<40);

View File

@@ -281,8 +281,8 @@ int SqlQuery::prepare(const QByteArray &sql, bool allow_failure)
*/ */
static bool startsWithInsensitive(const QByteArray &a, const char *b) static bool startsWithInsensitive(const QByteArray &a, const char *b)
{ {
size_t len = strlen(b); int len = strlen(b);
return a.size() >= len && qstrnicmp(a.constData(), b, Utility::convertSizeToUint(len)) == 0; return a.size() >= len && qstrnicmp(a.constData(), b, len) == 0;
} }
bool SqlQuery::isSelect() bool SqlQuery::isSelect()

View File

@@ -103,7 +103,6 @@ public:
: _chunk(0) : _chunk(0)
, _transferid(0) , _transferid(0)
, _size(0) , _size(0)
, _modtime(0)
, _errorCount(0) , _errorCount(0)
, _valid(false) , _valid(false)
{ {

View File

@@ -396,26 +396,6 @@ void Utility::crash()
*a = 1; *a = 1;
} }
// Use this functions to retrieve uint/int (often required by Qt and WIN32) from size_t
// without compiler warnings about possible truncation
uint Utility::convertSizeToUint(size_t &convertVar)
{
if( convertVar > UINT_MAX ) {
//throw std::bad_cast();
convertVar = UINT_MAX; // intentionally default to wrong value here to not crash: exception handling TBD
}
return static_cast<uint>(convertVar);
}
uint Utility::convertSizeToInt(size_t &convertVar)
{
if( convertVar > INT_MAX ) {
//throw std::bad_cast();
convertVar = INT_MAX; // intentionally default to wrong value here to not crash: exception handling TBD
}
return static_cast<int>(convertVar);
}
// read the output of the owncloud --version command from the owncloud // read the output of the owncloud --version command from the owncloud
// version that is on disk. This works for most versions of the client, // version that is on disk. This works for most versions of the client,
// because clients that do not yet know the --version flag return the // because clients that do not yet know the --version flag return the

View File

@@ -55,12 +55,6 @@ namespace Utility {
OCSYNC_EXPORT QByteArray userAgentString(); OCSYNC_EXPORT QByteArray userAgentString();
OCSYNC_EXPORT bool hasLaunchOnStartup(const QString &appName); OCSYNC_EXPORT bool hasLaunchOnStartup(const QString &appName);
OCSYNC_EXPORT void setLaunchOnStartup(const QString &appName, const QString &guiName, bool launch); OCSYNC_EXPORT void setLaunchOnStartup(const QString &appName, const QString &guiName, bool launch);
OCSYNC_EXPORT uint convertSizeToUint(size_t &convertVar);
OCSYNC_EXPORT uint convertSizeToInt(size_t &convertVar);
#ifdef Q_OS_WIN
OCSYNC_EXPORT DWORD convertSizeToDWORD(size_t &convertVar);
#endif
/** /**
* Return the amount of free space available. * Return the amount of free space available.

View File

@@ -87,17 +87,9 @@ void setLaunchOnStartup_private(const QString &appName, const QString &guiName,
} }
} }
// TODO: Right now only detection on toggle/startup, not when windows theme is switched while nextcloud is running
static inline bool hasDarkSystray_private() static inline bool hasDarkSystray_private()
{ {
if(Utility::registryGetKeyValue( HKEY_CURRENT_USER, return true;
"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
"SystemUsesLightTheme" ) == 1) {
return false;
}
else {
return true;
}
} }
QVariant Utility::registryGetKeyValue(HKEY hRootKey, const QString &subKey, const QString &valueName) QVariant Utility::registryGetKeyValue(HKEY hRootKey, const QString &subKey, const QString &valueName)
@@ -264,13 +256,4 @@ bool Utility::registryWalkSubKeys(HKEY hRootKey, const QString &subKey, const st
return retCode != ERROR_NO_MORE_ITEMS; return retCode != ERROR_NO_MORE_ITEMS;
} }
DWORD Utility::convertSizeToDWORD(size_t &convertVar)
{
if( convertVar > UINT_MAX ) {
//throw std::bad_cast();
convertVar = UINT_MAX; // intentionally default to wrong value here to not crash: exception handling TBD
}
return static_cast<DWORD>(convertVar);
}
} // namespace OCC } // namespace OCC

View File

@@ -73,7 +73,7 @@ static void csync_exclude_expand_escapes(QByteArray &input)
line[o++] = line[i]; line[o++] = line[i];
} }
} }
input.resize(OCC::Utility::convertSizeToUint(o)); input.resize(o);
} }
// See http://support.microsoft.com/kb/74496 and // See http://support.microsoft.com/kb/74496 and
@@ -236,29 +236,13 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(const char *path, bool excludeC
return match; return match;
} }
static QByteArray leftIncludeLast(const QByteArray & arr, char c)
{
// left up to and including `c`
return arr.left(arr.lastIndexOf(c, arr.size() - 2) + 1);
}
using namespace OCC; using namespace OCC;
ExcludedFiles::ExcludedFiles(QString localPath) ExcludedFiles::ExcludedFiles()
: _localPath(std::move(localPath))
{ {
Q_ASSERT(_localPath.endsWith("/"));
// Windows used to use PathMatchSpec which allows *foo to match abc/deffoo. // Windows used to use PathMatchSpec which allows *foo to match abc/deffoo.
_wildcardsMatchSlash = Utility::isWindows(); _wildcardsMatchSlash = Utility::isWindows();
// We're in a detached exclude probably coming from a partial sync or test
if (_localPath.isEmpty())
return;
// Load exclude file from base dir
QFileInfo fi(_localPath + ".sync-exclude.lst");
if (fi.isReadable())
addInTreeExcludeFilePath(fi.absoluteFilePath());
} }
ExcludedFiles::~ExcludedFiles() ExcludedFiles::~ExcludedFiles()
@@ -267,13 +251,7 @@ ExcludedFiles::~ExcludedFiles()
void ExcludedFiles::addExcludeFilePath(const QString &path) void ExcludedFiles::addExcludeFilePath(const QString &path)
{ {
_excludeFiles[_localPath.toUtf8()].append(path); _excludeFiles.insert(path);
}
void ExcludedFiles::addInTreeExcludeFilePath(const QString &path)
{
BasePathByteArray basePath = leftIncludeLast(path.toUtf8(), '/');
_excludeFiles[basePath].append(path);
} }
void ExcludedFiles::setExcludeConflictFiles(bool onoff) void ExcludedFiles::setExcludeConflictFiles(bool onoff)
@@ -283,22 +261,9 @@ void ExcludedFiles::setExcludeConflictFiles(bool onoff)
void ExcludedFiles::addManualExclude(const QByteArray &expr) void ExcludedFiles::addManualExclude(const QByteArray &expr)
{ {
addManualExclude(expr, _localPath.toUtf8()); _manualExcludes.append(expr);
} _allExcludes.append(expr);
prepare();
void ExcludedFiles::addManualExclude(const QByteArray &expr, const QByteArray &basePath)
{
#if defined(Q_OS_WIN)
Q_ASSERT(basePath.size() >= 2 && basePath.at(1) == ':');
#else
Q_ASSERT(basePath.startsWith('/'));
#endif
Q_ASSERT(basePath.endsWith('/'));
auto key = basePath;
_manualExcludes[key].append(expr);
_allExcludes[key].append(expr);
prepare(key);
} }
void ExcludedFiles::clearManualExcludes() void ExcludedFiles::clearManualExcludes()
@@ -313,51 +278,26 @@ void ExcludedFiles::setWildcardsMatchSlash(bool onoff)
prepare(); prepare();
} }
bool ExcludedFiles::loadExcludeFile(const QByteArray & basePath, const QString & file)
{
QFile f(file);
if (!f.open(QIODevice::ReadOnly))
return false;
while (!f.atEnd()) {
QByteArray line = f.readLine().trimmed();
if (line.isEmpty() || line.startsWith('#'))
continue;
csync_exclude_expand_escapes(line);
_allExcludes[basePath].append(line);
}
// nothing to prepare if the user decided to not exclude anything
if(_allExcludes.size())
prepare(basePath);
return true;
}
bool ExcludedFiles::reloadExcludeFiles() bool ExcludedFiles::reloadExcludeFiles()
{ {
_allExcludes.clear(); _allExcludes.clear();
// clear all regex
_bnameTraversalRegexFile.clear();
_bnameTraversalRegexDir.clear();
_fullTraversalRegexFile.clear();
_fullTraversalRegexDir.clear();
_fullRegexFile.clear();
_fullRegexDir.clear();
bool success = true; bool success = true;
for (const auto& basePath : _excludeFiles.keys()) { foreach (const QString &file, _excludeFiles) {
for (const auto& file : _excludeFiles.value(basePath)) { QFile f(file);
success = loadExcludeFile(basePath, file); if (!f.open(QIODevice::ReadOnly)) {
success = false;
continue;
}
while (!f.atEnd()) {
QByteArray line = f.readLine().trimmed();
if (line.isEmpty() || line.startsWith('#'))
continue;
csync_exclude_expand_escapes(line);
_allExcludes.append(line);
} }
} }
_allExcludes.append(_manualExcludes);
auto endManual = _manualExcludes.cend(); prepare();
for (auto kv = _manualExcludes.cbegin(); kv != endManual; ++kv) {
_allExcludes[kv.key()].append(kv.value());
prepare(kv.key());
}
return success; return success;
} }
@@ -371,15 +311,13 @@ bool ExcludedFiles::isExcluded(
return true; return true;
} }
//TODO this seems a waste, hidden files are ignored before hitting this function it seems
if (excludeHidden) { if (excludeHidden) {
QString path = filePath; QString path = filePath;
// Check all path subcomponents, but to *not* check the base path: // Check all path subcomponents, but to *not* check the base path:
// We do want to be able to sync with a hidden folder as the target. // We do want to be able to sync with a hidden folder as the target.
while (path.size() > basePath.size()) { while (path.size() > basePath.size()) {
QFileInfo fi(path); QFileInfo fi(path);
if (fi.fileName() != ".sync-exclude.lst" if (fi.isHidden() || fi.fileName().startsWith(QLatin1Char('.'))) {
&& (fi.isHidden() || fi.fileName().startsWith(QLatin1Char('.')))) {
return true; return true;
} }
@@ -402,7 +340,7 @@ bool ExcludedFiles::isExcluded(
return fullPatternMatch(relativePath.toUtf8(), type) != CSYNC_NOT_EXCLUDED; return fullPatternMatch(relativePath.toUtf8(), type) != CSYNC_NOT_EXCLUDED;
} }
CSYNC_EXCLUDE_TYPE ExcludedFiles::traversalPatternMatch(const char *path, ItemType filetype) CSYNC_EXCLUDE_TYPE ExcludedFiles::traversalPatternMatch(const char *path, ItemType filetype) const
{ {
auto match = _csync_excluded_common(path, _excludeConflictFiles); auto match = _csync_excluded_common(path, _excludeConflictFiles);
if (match != CSYNC_NOT_EXCLUDED) if (match != CSYNC_NOT_EXCLUDED)
@@ -410,15 +348,6 @@ CSYNC_EXCLUDE_TYPE ExcludedFiles::traversalPatternMatch(const char *path, ItemTy
if (_allExcludes.isEmpty()) if (_allExcludes.isEmpty())
return CSYNC_NOT_EXCLUDED; return CSYNC_NOT_EXCLUDED;
// Directories are guaranteed to be visited before their files
if (filetype == ItemTypeDirectory) {
QFileInfo fi = QFileInfo(_localPath + path + "/.sync-exclude.lst");
if (fi.isReadable()) {
addInTreeExcludeFilePath(fi.absoluteFilePath());
loadExcludeFile(fi.absolutePath().toUtf8(), fi.absoluteFilePath());
}
}
// Check the bname part of the path to see whether the full // Check the bname part of the path to see whether the full
// regex should be run. // regex should be run.
@@ -430,51 +359,33 @@ CSYNC_EXCLUDE_TYPE ExcludedFiles::traversalPatternMatch(const char *path, ItemTy
} }
QString bnameStr = QString::fromUtf8(bname); QString bnameStr = QString::fromUtf8(bname);
QByteArray basePath(_localPath.toUtf8() + path); QRegularExpressionMatch m;
while (basePath.size() > _localPath.size()) { if (filetype == ItemTypeDirectory) {
basePath = leftIncludeLast(basePath, '/'); m = _bnameTraversalRegexDir.match(bnameStr);
QRegularExpressionMatch m; } else {
if (filetype == ItemTypeDirectory m = _bnameTraversalRegexFile.match(bnameStr);
&& _bnameTraversalRegexDir.contains(basePath)) { }
m = _bnameTraversalRegexDir[basePath].match(bnameStr); if (!m.hasMatch())
} else if (filetype == ItemTypeFile return CSYNC_NOT_EXCLUDED;
&& _bnameTraversalRegexFile.contains(basePath)) { if (m.capturedStart(QStringLiteral("exclude")) != -1) {
m = _bnameTraversalRegexFile[basePath].match(bnameStr); return CSYNC_FILE_EXCLUDE_LIST;
} else { } else if (m.capturedStart(QStringLiteral("excluderemove")) != -1) {
continue; return CSYNC_FILE_EXCLUDE_AND_REMOVE;
}
if (!m.hasMatch())
return CSYNC_NOT_EXCLUDED;
if (m.capturedStart(QStringLiteral("exclude")) != -1) {
return CSYNC_FILE_EXCLUDE_LIST;
} else if (m.capturedStart(QStringLiteral("excluderemove")) != -1) {
return CSYNC_FILE_EXCLUDE_AND_REMOVE;
}
} }
// third capture: full path matching is triggered // third capture: full path matching is triggered
QString pathStr = QString::fromUtf8(path); QString pathStr = QString::fromUtf8(path);
basePath = _localPath.toUtf8() + path;
while (basePath.size() > _localPath.size()) {
basePath = leftIncludeLast(basePath, '/');
QRegularExpressionMatch m;
if (filetype == ItemTypeDirectory
&& _fullTraversalRegexDir.contains(basePath)) {
m = _fullTraversalRegexDir[basePath].match(pathStr);
} else if (filetype == ItemTypeFile
&& _fullTraversalRegexFile.contains(basePath)) {
m = _fullTraversalRegexFile[basePath].match(pathStr);
} else {
continue;
}
if (m.hasMatch()) { if (filetype == ItemTypeDirectory) {
if (m.capturedStart(QStringLiteral("exclude")) != -1) { m = _fullTraversalRegexDir.match(pathStr);
return CSYNC_FILE_EXCLUDE_LIST; } else {
} else if (m.capturedStart(QStringLiteral("excluderemove")) != -1) { m = _fullTraversalRegexFile.match(pathStr);
return CSYNC_FILE_EXCLUDE_AND_REMOVE; }
} if (m.hasMatch()) {
if (m.capturedStart(QStringLiteral("exclude")) != -1) {
return CSYNC_FILE_EXCLUDE_LIST;
} else if (m.capturedStart(QStringLiteral("excluderemove")) != -1) {
return CSYNC_FILE_EXCLUDE_AND_REMOVE;
} }
} }
return CSYNC_NOT_EXCLUDED; return CSYNC_NOT_EXCLUDED;
@@ -489,38 +400,23 @@ CSYNC_EXCLUDE_TYPE ExcludedFiles::fullPatternMatch(const char *path, ItemType fi
return CSYNC_NOT_EXCLUDED; return CSYNC_NOT_EXCLUDED;
QString p = QString::fromUtf8(path); QString p = QString::fromUtf8(path);
// `path` seems to always be relative to `_localPath`, the tests however have not been QRegularExpressionMatch m;
// written that way... this makes the tests happy for now. TODO Fix the tests at some point if (filetype == ItemTypeDirectory) {
if (path[0] == '/') m = _fullRegexDir.match(p);
++path; } else {
m = _fullRegexFile.match(p);
QByteArray basePath(_localPath.toUtf8() + path); }
while (basePath.size() > _localPath.size()) { if (m.hasMatch()) {
basePath = leftIncludeLast(basePath, '/'); if (m.capturedStart(QStringLiteral("exclude")) != -1) {
QRegularExpressionMatch m; return CSYNC_FILE_EXCLUDE_LIST;
if (filetype == ItemTypeDirectory } else if (m.capturedStart(QStringLiteral("excluderemove")) != -1) {
&& _fullRegexDir.contains(basePath)) { return CSYNC_FILE_EXCLUDE_AND_REMOVE;
m = _fullRegexDir[basePath].match(p);
} else if (filetype == ItemTypeFile
&& _fullRegexFile.contains(basePath)) {
m = _fullRegexFile[basePath].match(p);
} else {
continue;
}
if (m.hasMatch()) {
if (m.capturedStart(QStringLiteral("exclude")) != -1) {
return CSYNC_FILE_EXCLUDE_LIST;
} else if (m.capturedStart(QStringLiteral("excluderemove")) != -1) {
return CSYNC_FILE_EXCLUDE_AND_REMOVE;
}
} }
} }
return CSYNC_NOT_EXCLUDED; return CSYNC_NOT_EXCLUDED;
} }
auto ExcludedFiles::csyncTraversalMatchFun() auto ExcludedFiles::csyncTraversalMatchFun() const
-> std::function<CSYNC_EXCLUDE_TYPE(const char *path, ItemType filetype)> -> std::function<CSYNC_EXCLUDE_TYPE(const char *path, ItemType filetype)>
{ {
return [this](const char *path, ItemType filetype) { return this->traversalPatternMatch(path, filetype); }; return [this](const char *path, ItemType filetype) { return this->traversalPatternMatch(path, filetype); };
@@ -659,22 +555,6 @@ static QString extractBnameTrigger(const QString &exclude, bool wildcardsMatchSl
void ExcludedFiles::prepare() void ExcludedFiles::prepare()
{ {
// clear all regex
_bnameTraversalRegexFile.clear();
_bnameTraversalRegexDir.clear();
_fullTraversalRegexFile.clear();
_fullTraversalRegexDir.clear();
_fullRegexFile.clear();
_fullRegexDir.clear();
for (auto const & basePath : _allExcludes.keys())
prepare(basePath);
}
void ExcludedFiles::prepare(const BasePathByteArray & basePath)
{
Q_ASSERT(_allExcludes.contains(basePath));
// Build regular expressions for the different cases. // Build regular expressions for the different cases.
// //
// To compose the _bnameTraversalRegex, _fullTraversalRegex and _fullRegex // To compose the _bnameTraversalRegex, _fullTraversalRegex and _fullRegex
@@ -716,7 +596,7 @@ void ExcludedFiles::prepare(const BasePathByteArray & basePath)
pattern.append(appendMe); pattern.append(appendMe);
}; };
for (auto exclude : _allExcludes.value(basePath)) { for (auto exclude : _allExcludes) {
if (exclude[0] == '\n') if (exclude[0] == '\n')
continue; // empty line continue; // empty line
if (exclude[0] == '\r') if (exclude[0] == '\r')
@@ -738,15 +618,6 @@ void ExcludedFiles::prepare(const BasePathByteArray & basePath)
auto &fullFileDir = removeExcluded ? fullFileDirRemove : fullFileDirKeep; auto &fullFileDir = removeExcluded ? fullFileDirRemove : fullFileDirKeep;
auto &fullDir = removeExcluded ? fullDirRemove : fullDirKeep; auto &fullDir = removeExcluded ? fullDirRemove : fullDirKeep;
if (fullPath) {
// The full pattern is matched against a path relative to _localPath, however exclude is
// relative to basePath at this point.
// We know for sure that both _localPath and basePath are absolute and that basePath is
// contained in _localPath. So we can simply remove it from the begining.
auto relPath = basePath.mid(_localPath.size());
// Make exclude relative to _localPath
exclude.prepend(relPath);
}
auto regexExclude = convertToRegexpSyntax(QString::fromUtf8(exclude), _wildcardsMatchSlash); auto regexExclude = convertToRegexpSyntax(QString::fromUtf8(exclude), _wildcardsMatchSlash);
if (!fullPath) { if (!fullPath) {
regexAppend(bnameFileDir, bnameDir, regexExclude, matchDirOnly); regexAppend(bnameFileDir, bnameDir, regexExclude, matchDirOnly);
@@ -783,11 +654,11 @@ void ExcludedFiles::prepare(const BasePathByteArray & basePath)
// (exclude)|(excluderemove)|(bname triggers). // (exclude)|(excluderemove)|(bname triggers).
// If the third group matches, the fullActivatedRegex needs to be applied // If the third group matches, the fullActivatedRegex needs to be applied
// to the full path. // to the full path.
_bnameTraversalRegexFile[basePath].setPattern( _bnameTraversalRegexFile.setPattern(
"^(?P<exclude>" + bnameFileDirKeep + ")$|" "^(?P<exclude>" + bnameFileDirKeep + ")$|"
+ "^(?P<excluderemove>" + bnameFileDirRemove + ")$|" + "^(?P<excluderemove>" + bnameFileDirRemove + ")$|"
+ "^(?P<trigger>" + bnameTriggerFileDir + ")$"); + "^(?P<trigger>" + bnameTriggerFileDir + ")$");
_bnameTraversalRegexDir[basePath].setPattern( _bnameTraversalRegexDir.setPattern(
"^(?P<exclude>" + bnameFileDirKeep + "|" + bnameDirKeep + ")$|" "^(?P<exclude>" + bnameFileDirKeep + "|" + bnameDirKeep + ")$|"
+ "^(?P<excluderemove>" + bnameFileDirRemove + "|" + bnameDirRemove + ")$|" + "^(?P<excluderemove>" + bnameFileDirRemove + "|" + bnameDirRemove + ")$|"
+ "^(?P<trigger>" + bnameTriggerFileDir + "|" + bnameTriggerDir + ")$"); + "^(?P<trigger>" + bnameTriggerFileDir + "|" + bnameTriggerDir + ")$");
@@ -796,13 +667,13 @@ void ExcludedFiles::prepare(const BasePathByteArray & basePath)
// the bname regex matches. Its basic form is (exclude)|(excluderemove)". // the bname regex matches. Its basic form is (exclude)|(excluderemove)".
// This pattern can be much simpler than fullRegex since we can assume a traversal // This pattern can be much simpler than fullRegex since we can assume a traversal
// situation and doesn't need to look for bname patterns in parent paths. // situation and doesn't need to look for bname patterns in parent paths.
_fullTraversalRegexFile[basePath].setPattern( _fullTraversalRegexFile.setPattern(
QLatin1String("") QLatin1String("")
// Full patterns are anchored to the beginning // Full patterns are anchored to the beginning
+ "^(?P<exclude>" + fullFileDirKeep + ")(?:$|/)" + "^(?P<exclude>" + fullFileDirKeep + ")(?:$|/)"
+ "|" + "|"
+ "^(?P<excluderemove>" + fullFileDirRemove + ")(?:$|/)"); + "^(?P<excluderemove>" + fullFileDirRemove + ")(?:$|/)");
_fullTraversalRegexDir[basePath].setPattern( _fullTraversalRegexDir.setPattern(
QLatin1String("") QLatin1String("")
+ "^(?P<exclude>" + fullFileDirKeep + "|" + fullDirKeep + ")(?:$|/)" + "^(?P<exclude>" + fullFileDirKeep + "|" + fullDirKeep + ")(?:$|/)"
+ "|" + "|"
@@ -810,7 +681,7 @@ void ExcludedFiles::prepare(const BasePathByteArray & basePath)
// The full regex is applied to the full path and incorporates both bname and // The full regex is applied to the full path and incorporates both bname and
// full-path patterns. It has the form "(exclude)|(excluderemove)". // full-path patterns. It has the form "(exclude)|(excluderemove)".
_fullRegexFile[basePath].setPattern( _fullRegexFile.setPattern(
QLatin1String("(?P<exclude>") QLatin1String("(?P<exclude>")
// Full patterns are anchored to the beginning // Full patterns are anchored to the beginning
+ "^(?:" + fullFileDirKeep + ")(?:$|/)" + "|" + "^(?:" + fullFileDirKeep + ")(?:$|/)" + "|"
@@ -826,7 +697,7 @@ void ExcludedFiles::prepare(const BasePathByteArray & basePath)
+ "(?:^|/)(?:" + bnameFileDirRemove + ")(?:$|/)" + "|" + "(?:^|/)(?:" + bnameFileDirRemove + ")(?:$|/)" + "|"
+ "(?:^|/)(?:" + bnameDirRemove + ")/" + "(?:^|/)(?:" + bnameDirRemove + ")/"
+ ")"); + ")");
_fullRegexDir[basePath].setPattern( _fullRegexDir.setPattern(
QLatin1String("(?P<exclude>") QLatin1String("(?P<exclude>")
+ "^(?:" + fullFileDirKeep + "|" + fullDirKeep + ")(?:$|/)" + "|" + "^(?:" + fullFileDirKeep + "|" + fullDirKeep + ")(?:$|/)" + "|"
+ "(?:^|/)(?:" + bnameFileDirKeep + "|" + bnameDirKeep + ")(?:$|/)" + "(?:^|/)(?:" + bnameFileDirKeep + "|" + bnameDirKeep + ")(?:$|/)"
@@ -840,16 +711,16 @@ void ExcludedFiles::prepare(const BasePathByteArray & basePath)
QRegularExpression::PatternOptions patternOptions = QRegularExpression::NoPatternOption; QRegularExpression::PatternOptions patternOptions = QRegularExpression::NoPatternOption;
if (OCC::Utility::fsCasePreserving()) if (OCC::Utility::fsCasePreserving())
patternOptions |= QRegularExpression::CaseInsensitiveOption; patternOptions |= QRegularExpression::CaseInsensitiveOption;
_bnameTraversalRegexFile[basePath].setPatternOptions(patternOptions); _bnameTraversalRegexFile.setPatternOptions(patternOptions);
_bnameTraversalRegexFile[basePath].optimize(); _bnameTraversalRegexFile.optimize();
_bnameTraversalRegexDir[basePath].setPatternOptions(patternOptions); _bnameTraversalRegexDir.setPatternOptions(patternOptions);
_bnameTraversalRegexDir[basePath].optimize(); _bnameTraversalRegexDir.optimize();
_fullTraversalRegexFile[basePath].setPatternOptions(patternOptions); _fullTraversalRegexFile.setPatternOptions(patternOptions);
_fullTraversalRegexFile[basePath].optimize(); _fullTraversalRegexFile.optimize();
_fullTraversalRegexDir[basePath].setPatternOptions(patternOptions); _fullTraversalRegexDir.setPatternOptions(patternOptions);
_fullTraversalRegexDir[basePath].optimize(); _fullTraversalRegexDir.optimize();
_fullRegexFile[basePath].setPatternOptions(patternOptions); _fullRegexFile.setPatternOptions(patternOptions);
_fullRegexFile[basePath].optimize(); _fullRegexFile.optimize();
_fullRegexDir[basePath].setPatternOptions(patternOptions); _fullRegexDir.setPatternOptions(patternOptions);
_fullRegexDir[basePath].optimize(); _fullRegexDir.optimize();
} }

View File

@@ -66,7 +66,7 @@ class OCSYNC_EXPORT ExcludedFiles : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
ExcludedFiles(QString localPath = "/"); ExcludedFiles();
~ExcludedFiles(); ~ExcludedFiles();
/** /**
@@ -75,7 +75,6 @@ public:
* Does not load the file. Use reloadExcludeFiles() afterwards. * Does not load the file. Use reloadExcludeFiles() afterwards.
*/ */
void addExcludeFilePath(const QString &path); void addExcludeFilePath(const QString &path);
void addInTreeExcludeFilePath(const QString &path);
/** /**
* Whether conflict files shall be excluded. * Whether conflict files shall be excluded.
@@ -96,13 +95,12 @@ public:
bool excludeHidden) const; bool excludeHidden) const;
/** /**
* Adds an exclude pattern anchored to base path * Adds an exclude pattern.
* *
* Primarily used in tests. Patterns added this way are preserved when * Primarily used in tests. Patterns added this way are preserved when
* reloadExcludeFiles() is called. * reloadExcludeFiles() is called.
*/ */
void addManualExclude(const QByteArray &expr); void addManualExclude(const QByteArray &expr);
void addManualExclude(const QByteArray &expr, const QByteArray &basePath);
/** /**
* Removes all manually added exclude patterns. * Removes all manually added exclude patterns.
@@ -123,7 +121,7 @@ public:
* Careful: The function will only be valid for as long as this * Careful: The function will only be valid for as long as this
* ExcludedFiles instance stays alive. * ExcludedFiles instance stays alive.
*/ */
auto csyncTraversalMatchFun() auto csyncTraversalMatchFun() const
-> std::function<CSYNC_EXCLUDE_TYPE(const char *path, ItemType filetype)>; -> std::function<CSYNC_EXCLUDE_TYPE(const char *path, ItemType filetype)>;
public slots: public slots:
@@ -131,10 +129,6 @@ public slots:
* Reloads the exclude patterns from the registered paths. * Reloads the exclude patterns from the registered paths.
*/ */
bool reloadExcludeFiles(); bool reloadExcludeFiles();
/**
* Loads the exclude patterns from file the registered base paths.
*/
bool loadExcludeFile(const QByteArray & basePath, const QString & file);
private: private:
/** /**
@@ -162,32 +156,10 @@ private:
* Note that this only matches patterns. It does not check whether the file * Note that this only matches patterns. It does not check whether the file
* or directory pointed to is hidden (or whether it even exists). * or directory pointed to is hidden (or whether it even exists).
*/ */
CSYNC_EXCLUDE_TYPE traversalPatternMatch(const char *path, ItemType filetype); CSYNC_EXCLUDE_TYPE traversalPatternMatch(const char *path, ItemType filetype) const;
// Our BasePath need to end with '/'
class BasePathByteArray : public QByteArray
{
public:
BasePathByteArray(QByteArray && other)
: QByteArray(std::move(other))
{
Q_ASSERT(this->endsWith('/'));
}
BasePathByteArray(const QByteArray & other)
: QByteArray(other)
{
Q_ASSERT(this->endsWith('/'));
}
BasePathByteArray(const char * data, int size = -1)
: BasePathByteArray(QByteArray(data, size))
{
}
};
/** /**
* Generate optimized regular expressions for the exclude patterns anchored to basePath. * Generate optimized regular expressions for the exclude patterns.
* *
* The optimization works in two steps: First, all supported patterns are put * The optimization works in two steps: First, all supported patterns are put
* into _fullRegexFile/_fullRegexDir. These regexes can be applied to the full * into _fullRegexFile/_fullRegexDir. These regexes can be applied to the full
@@ -215,28 +187,24 @@ private:
* full matcher would exclude. Example: "b" is excluded. traversal("b/c") * full matcher would exclude. Example: "b" is excluded. traversal("b/c")
* returns not-excluded because "c" isn't a bname activation pattern. * returns not-excluded because "c" isn't a bname activation pattern.
*/ */
void prepare(const BasePathByteArray & basePath);
void prepare(); void prepare();
QString _localPath;
/// Files to load excludes from /// Files to load excludes from
QMap<BasePathByteArray, QList<QString>> _excludeFiles; QSet<QString> _excludeFiles;
/// Exclude patterns added with addManualExclude() /// Exclude patterns added with addManualExclude()
QMap<BasePathByteArray, QList<QByteArray>> _manualExcludes; QList<QByteArray> _manualExcludes;
/// List of all active exclude patterns /// List of all active exclude patterns
QMap<BasePathByteArray, QList<QByteArray>> _allExcludes; QList<QByteArray> _allExcludes;
/// see prepare() /// see prepare()
QMap<BasePathByteArray, QRegularExpression> _bnameTraversalRegexFile; QRegularExpression _bnameTraversalRegexFile;
QMap<BasePathByteArray, QRegularExpression> _bnameTraversalRegexDir; QRegularExpression _bnameTraversalRegexDir;
QMap<BasePathByteArray, QRegularExpression> _fullTraversalRegexFile; QRegularExpression _fullTraversalRegexFile;
QMap<BasePathByteArray, QRegularExpression> _fullTraversalRegexDir; QRegularExpression _fullTraversalRegexDir;
QMap<BasePathByteArray, QRegularExpression> _fullRegexFile; QRegularExpression _fullRegexFile;
QMap<BasePathByteArray, QRegularExpression> _fullRegexDir; QRegularExpression _fullRegexDir;
bool _excludeConflictFiles = true; bool _excludeConflictFiles = true;

View File

@@ -124,9 +124,7 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
* because it's a hidden file that should not be synced. * because it's a hidden file that should not be synced.
* This code should probably be in csync_exclude, but it does not have the fs parameter. * This code should probably be in csync_exclude, but it does not have the fs parameter.
* Keep it here for now */ * Keep it here for now */
if (ctx->ignore_hidden_files if (ctx->ignore_hidden_files && (fs->is_hidden)) {
&& fs->is_hidden
&& !fs->path.endsWith(".sync-exclude.lst")) {
qCInfo(lcUpdate, "file excluded because it is a hidden file: %s", fs->path.constData()); qCInfo(lcUpdate, "file excluded because it is a hidden file: %s", fs->path.constData());
excluded = CSYNC_FILE_EXCLUDE_HIDDEN; excluded = CSYNC_FILE_EXCLUDE_HIDDEN;
} }
@@ -724,8 +722,7 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
if (ctx->current == LOCAL_REPLICA) { if (ctx->current == LOCAL_REPLICA) {
ASSERT(dirent->path.startsWith(ctx->local.uri)); // path is relative to uri ASSERT(dirent->path.startsWith(ctx->local.uri)); // path is relative to uri
// "len + 1" to include the slash in-between. // "len + 1" to include the slash in-between.
size_t uriLength = strlen(ctx->local.uri); dirent->path = dirent->path.mid(strlen(ctx->local.uri) + 1);
dirent->path = dirent->path.mid(OCC::Utility::convertSizeToInt(uriLength) + 1);
} }
previous_fs = ctx->current_fs; previous_fs = ctx->current_fs;

View File

@@ -38,7 +38,6 @@
#include "c_alloc.h" #include "c_alloc.h"
#include "c_string.h" #include "c_string.h"
#include "common/filesystembase.h" #include "common/filesystembase.h"
#include "common/utility.h"
/* Convert a locale String to UTF8 */ /* Convert a locale String to UTF8 */
QByteArray c_utf8_from_locale(const mbchar_t *wstr) QByteArray c_utf8_from_locale(const mbchar_t *wstr)
@@ -53,10 +52,10 @@ QByteArray c_utf8_from_locale(const mbchar_t *wstr)
size_t len; size_t len;
len = wcslen(wstr); len = wcslen(wstr);
/* Call once to get the required size. */ /* Call once to get the required size. */
size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr, OCC::Utility::convertSizeToInt(len), NULL, 0, NULL, NULL); size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr, len, NULL, 0, NULL, NULL);
if (size_needed > 0) { if (size_needed > 0) {
dst.resize(size_needed); dst.resize(size_needed);
WideCharToMultiByte(CP_UTF8, 0, wstr, OCC::Utility::convertSizeToInt(len), dst.data(), size_needed, NULL, NULL); WideCharToMultiByte(CP_UTF8, 0, wstr, len, dst.data(), size_needed, NULL, NULL);
} }
return dst; return dst;
#else #else
@@ -96,7 +95,7 @@ mbchar_t* c_utf8_string_to_locale(const char *str)
int size_needed; int size_needed;
len = strlen(str); len = strlen(str);
size_needed = MultiByteToWideChar(CP_UTF8, 0, str, OCC::Utility::convertSizeToInt(len), NULL, 0); size_needed = MultiByteToWideChar(CP_UTF8, 0, str, len, NULL, 0);
if (size_needed > 0) { if (size_needed > 0) {
int size_char = (size_needed + 1) * sizeof(mbchar_t); int size_char = (size_needed + 1) * sizeof(mbchar_t);
dst = (mbchar_t*)c_malloc(size_char); dst = (mbchar_t*)c_malloc(size_char);
@@ -115,8 +114,7 @@ mbchar_t* c_utf8_string_to_locale(const char *str)
return NULL; return NULL;
} else { } else {
#ifdef _WIN32 #ifdef _WIN32
size_t strLength = strlen(str); QByteArray unc_str = OCC::FileSystem::pathtoUNC(QByteArray::fromRawData(str, strlen(str)));
QByteArray unc_str = OCC::FileSystem::pathtoUNC(QByteArray::fromRawData(str, OCC::Utility::convertSizeToInt(strLength)));
mbchar_t *dst = c_utf8_string_to_locale(unc_str); mbchar_t *dst = c_utf8_string_to_locale(unc_str);
return dst; return dst;
#else #else

View File

@@ -57,7 +57,7 @@ csync_vio_handle_t *csync_vio_local_opendir(const char *name) {
handle = (dhandle_t*)c_malloc(sizeof(dhandle_t)); handle = (dhandle_t*)c_malloc(sizeof(dhandle_t));
// the file wildcard has to be attached // the file wildcard has to be attached
size_t len_name = strlen(name); int len_name = strlen(name);
if( len_name ) { if( len_name ) {
char *h = NULL; char *h = NULL;

View File

@@ -22,7 +22,6 @@ set(client_UI_SRCS
generalsettings.ui generalsettings.ui
legalnotice.ui legalnotice.ui
ignorelisteditor.ui ignorelisteditor.ui
ignorelisttablewidget.ui
networksettings.ui networksettings.ui
activitywidget.ui activitywidget.ui
synclogdialog.ui synclogdialog.ui
@@ -35,7 +34,6 @@ set(client_UI_SRCS
addcertificatedialog.ui addcertificatedialog.ui
proxyauthdialog.ui proxyauthdialog.ui
mnemonicdialog.ui mnemonicdialog.ui
wizard/flow2authwidget.ui
wizard/owncloudadvancedsetuppage.ui wizard/owncloudadvancedsetuppage.ui
wizard/owncloudconnectionmethoddialog.ui wizard/owncloudconnectionmethoddialog.ui
wizard/owncloudhttpcredspage.ui wizard/owncloudhttpcredspage.ui
@@ -61,7 +59,6 @@ set(client_SRCS
generalsettings.cpp generalsettings.cpp
legalnotice.cpp legalnotice.cpp
ignorelisteditor.cpp ignorelisteditor.cpp
ignorelisttablewidget.cpp
lockwatcher.cpp lockwatcher.cpp
logbrowser.cpp logbrowser.cpp
navigationpanehelper.cpp navigationpanehelper.cpp
@@ -102,14 +99,10 @@ set(client_SRCS
servernotificationhandler.cpp servernotificationhandler.cpp
guiutility.cpp guiutility.cpp
elidedlabel.cpp elidedlabel.cpp
headerbanner.cpp
iconjob.cpp iconjob.cpp
remotewipe.cpp
creds/credentialsfactory.cpp creds/credentialsfactory.cpp
creds/httpcredentialsgui.cpp creds/httpcredentialsgui.cpp
creds/oauth.cpp creds/oauth.cpp
creds/flow2auth.cpp
creds/keychainchunk.cpp
creds/webflowcredentials.cpp creds/webflowcredentials.cpp
creds/webflowcredentialsdialog.cpp creds/webflowcredentialsdialog.cpp
wizard/postfixlineedit.cpp wizard/postfixlineedit.cpp
@@ -118,8 +111,6 @@ set(client_SRCS
wizard/owncloudconnectionmethoddialog.cpp wizard/owncloudconnectionmethoddialog.cpp
wizard/owncloudhttpcredspage.cpp wizard/owncloudhttpcredspage.cpp
wizard/owncloudoauthcredspage.cpp wizard/owncloudoauthcredspage.cpp
wizard/flow2authcredspage.cpp
wizard/flow2authwidget.cpp
wizard/owncloudsetuppage.cpp wizard/owncloudsetuppage.cpp
wizard/owncloudwizardcommon.cpp wizard/owncloudwizardcommon.cpp
wizard/owncloudwizard.cpp wizard/owncloudwizard.cpp
@@ -146,6 +137,8 @@ set(updater_SRCS
IF( APPLE ) IF( APPLE )
list(APPEND client_SRCS cocoainitializer_mac.mm) list(APPEND client_SRCS cocoainitializer_mac.mm)
list(APPEND client_SRCS settingsdialogmac.cpp)
list(REMOVE_ITEM client_SRCS settingsdialog.cpp)
list(APPEND client_SRCS socketapisocket_mac.mm) list(APPEND client_SRCS socketapisocket_mac.mm)
list(APPEND client_SRCS systray.mm) list(APPEND client_SRCS systray.mm)
@@ -175,6 +168,14 @@ set(3rdparty_SRC
../3rdparty/kmessagewidget/kmessagewidget.cpp ../3rdparty/kmessagewidget/kmessagewidget.cpp
) )
if (APPLE)
list(APPEND 3rdparty_SRC
../3rdparty/qtmacgoodies/src/macpreferenceswindow.mm
../3rdparty/qtmacgoodies/src/macstandardicon.mm
../3rdparty/qtmacgoodies/src/macwindow.mm
)
endif()
if(NOT WIN32) if(NOT WIN32)
list(APPEND 3rdparty_SRC ../3rdparty/qtlockedfile/qtlockedfile_unix.cpp) list(APPEND 3rdparty_SRC ../3rdparty/qtlockedfile/qtlockedfile_unix.cpp)
else() else()
@@ -243,7 +244,7 @@ if(APPLE)
file(GLOB_RECURSE OWNCLOUD_SIDEBAR_ICONS "${theme_dir}/colored/*-${APPLICATION_ICON_NAME}-sidebar*") file(GLOB_RECURSE OWNCLOUD_SIDEBAR_ICONS "${theme_dir}/colored/*-${APPLICATION_ICON_NAME}-sidebar*")
MESSAGE(STATUS "OWNCLOUD_SIDEBAR_ICONS: ${APPLICATION_ICON_NAME}: ${OWNCLOUD_SIDEBAR_ICONS}") MESSAGE(STATUS "OWNCLOUD_SIDEBAR_ICONS: ${APPLICATION_ICON_NAME}: ${OWNCLOUD_SIDEBAR_ICONS}")
endif() endif()
ecm_add_app_icon(final_src ICONS "${OWNCLOUD_ICONS}" SIDEBAR_ICONS "${OWNCLOUD_SIDEBAR_ICONS}" OUTFILE_BASENAME "${APPLICATION_ICON_NAME}") ecm_add_app_icon(final_src ICONS "${OWNCLOUD_ICONS}" SIDEBAR_ICONS "${OWNCLOUD_SIDEBAR_ICONS}" OUTFILE_BASE "${APPLICATION_ICON_NAME}")
if(UNIX AND NOT APPLE) if(UNIX AND NOT APPLE)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIE") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIE")
@@ -265,9 +266,6 @@ if(NOT BUILD_OWNCLOUD_OSX_BUNDLE)
endforeach(_file) endforeach(_file)
install(FILES ${client_I18N} DESTINATION ${SHAREDIR}/${APPLICATION_EXECUTABLE}/i18n) install(FILES ${client_I18N} DESTINATION ${SHAREDIR}/${APPLICATION_EXECUTABLE}/i18n)
else() else()
file(GLOB_RECURSE VISUAL_ELEMENTS "${theme_dir}/colored/*-${APPLICATION_ICON_NAME}-w10startmenu*")
install(FILES ${VISUAL_ELEMENTS} DESTINATION bin/visualelements)
install(FILES "${theme_dir}/${APPLICATION_EXECUTABLE}.VisualElementsManifest.xml" DESTINATION bin)
install(FILES ${client_I18N} DESTINATION i18n) install(FILES ${client_I18N} DESTINATION i18n)
endif() endif()
@@ -330,6 +328,7 @@ ENDIF()
target_include_directories(${APPLICATION_EXECUTABLE} PRIVATE target_include_directories(${APPLICATION_EXECUTABLE} PRIVATE
${CMAKE_SOURCE_DIR}/src/3rdparty/QProgressIndicator ${CMAKE_SOURCE_DIR}/src/3rdparty/QProgressIndicator
${CMAKE_SOURCE_DIR}/src/3rdparty/qtlockedfile ${CMAKE_SOURCE_DIR}/src/3rdparty/qtlockedfile
${CMAKE_SOURCE_DIR}/src/3rdparty/qtmacgoodies/src
${CMAKE_SOURCE_DIR}/src/3rdparty/qtsingleapplication ${CMAKE_SOURCE_DIR}/src/3rdparty/qtsingleapplication
${CMAKE_SOURCE_DIR}/src/3rdparty/kmessagewidget ${CMAKE_SOURCE_DIR}/src/3rdparty/kmessagewidget
${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}

View File

@@ -368,7 +368,6 @@ void AccountManager::shutdown()
_accounts.clear(); _accounts.clear();
foreach (const auto &acc, accountsCopy) { foreach (const auto &acc, accountsCopy) {
emit accountRemoved(acc.data()); emit accountRemoved(acc.data());
emit removeAccountFolders(acc.data());
} }
} }

View File

@@ -70,6 +70,7 @@ public:
*/ */
void deleteAccount(AccountState *account); void deleteAccount(AccountState *account);
/** /**
* Creates an account and sets up some basic handlers. * Creates an account and sets up some basic handlers.
* Does *not* add the account to the account manager just yet. * Does *not* add the account to the account manager just yet.
@@ -103,7 +104,6 @@ public slots:
Q_SIGNALS: Q_SIGNALS:
void accountAdded(AccountState *account); void accountAdded(AccountState *account);
void accountRemoved(AccountState *account); void accountRemoved(AccountState *account);
void removeAccountFolders(AccountState *account);
private: private:
AccountManager() {} AccountManager() {}

View File

@@ -35,12 +35,10 @@
#include "filesystem.h" #include "filesystem.h"
#include "clientsideencryptionjobs.h" #include "clientsideencryptionjobs.h"
#include "syncresult.h" #include "syncresult.h"
#include "ignorelisttablewidget.h"
#include <math.h> #include <math.h>
#include <QDesktopServices> #include <QDesktopServices>
#include <QDialogButtonBox>
#include <QDir> #include <QDir>
#include <QListWidgetItem> #include <QListWidgetItem>
#include <QMessageBox> #include <QMessageBox>
@@ -57,6 +55,10 @@
#include "account.h" #include "account.h"
#ifdef Q_OS_MAC
#include "settingsdialogmac.h"
#endif
namespace OCC { namespace OCC {
Q_LOGGING_CATEGORY(lcAccountSettings, "nextcloud.gui.account.settings", QtInfoMsg) Q_LOGGING_CATEGORY(lcAccountSettings, "nextcloud.gui.account.settings", QtInfoMsg)
@@ -109,13 +111,13 @@ protected:
AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent) AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
: QWidget(parent) : QWidget(parent)
, _ui(new Ui::AccountSettings) , ui(new Ui::AccountSettings)
, _wasDisabledBefore(false) , _wasDisabledBefore(false)
, _accountState(accountState) , _accountState(accountState)
, _quotaInfo(accountState) , _quotaInfo(accountState)
, _menuShown(false) , _menuShown(false)
{ {
_ui->setupUi(this); ui->setupUi(this);
_model = new FolderStatusModel; _model = new FolderStatusModel;
_model->setAccountState(_accountState); _model->setAccountState(_accountState);
@@ -123,40 +125,35 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
FolderStatusDelegate *delegate = new FolderStatusDelegate; FolderStatusDelegate *delegate = new FolderStatusDelegate;
delegate->setParent(this); delegate->setParent(this);
// Connect styleChanged events to our widgets, so they can adapt (Dark-/Light-Mode switching) ui->_folderList->header()->hide();
connect(this, &AccountSettings::styleChanged, delegate, &FolderStatusDelegate::slotStyleChanged); ui->_folderList->setItemDelegate(delegate);
ui->_folderList->setModel(_model);
_ui->_folderList->header()->hide();
_ui->_folderList->setItemDelegate(delegate);
_ui->_folderList->setModel(_model);
#if defined(Q_OS_MAC) #if defined(Q_OS_MAC)
_ui->_folderList->setMinimumWidth(400); ui->_folderList->setMinimumWidth(400);
#else #else
_ui->_folderList->setMinimumWidth(300); ui->_folderList->setMinimumWidth(300);
#endif #endif
new ToolTipUpdater(_ui->_folderList); new ToolTipUpdater(ui->_folderList);
auto mouseCursorChanger = new MouseCursorChanger(this); auto mouseCursorChanger = new MouseCursorChanger(this);
mouseCursorChanger->folderList = _ui->_folderList; mouseCursorChanger->folderList = ui->_folderList;
mouseCursorChanger->model = _model; mouseCursorChanger->model = _model;
_ui->_folderList->setMouseTracking(true); ui->_folderList->setMouseTracking(true);
_ui->_folderList->setAttribute(Qt::WA_Hover, true); ui->_folderList->setAttribute(Qt::WA_Hover, true);
_ui->_folderList->installEventFilter(mouseCursorChanger); ui->_folderList->installEventFilter(mouseCursorChanger);
createAccountToolbox(); createAccountToolbox();
connect(AccountManager::instance(), &AccountManager::accountAdded, connect(AccountManager::instance(), &AccountManager::accountAdded,
this, &AccountSettings::slotAccountAdded); this, &AccountSettings::slotAccountAdded);
connect(this, &AccountSettings::removeAccountFolders, connect(ui->_folderList, &QWidget::customContextMenuRequested,
AccountManager::instance(), &AccountManager::removeAccountFolders);
connect(_ui->_folderList, &QWidget::customContextMenuRequested,
this, &AccountSettings::slotCustomContextMenuRequested); this, &AccountSettings::slotCustomContextMenuRequested);
connect(_ui->_folderList, &QAbstractItemView::clicked, connect(ui->_folderList, &QAbstractItemView::clicked,
this, &AccountSettings::slotFolderListClicked); this, &AccountSettings::slotFolderListClicked);
connect(_ui->_folderList, &QTreeView::expanded, this, &AccountSettings::refreshSelectiveSyncStatus); connect(ui->_folderList, &QTreeView::expanded, this, &AccountSettings::refreshSelectiveSyncStatus);
connect(_ui->_folderList, &QTreeView::collapsed, this, &AccountSettings::refreshSelectiveSyncStatus); connect(ui->_folderList, &QTreeView::collapsed, this, &AccountSettings::refreshSelectiveSyncStatus);
connect(_ui->selectiveSyncNotification, &QLabel::linkActivated, connect(ui->selectiveSyncNotification, &QLabel::linkActivated,
this, &AccountSettings::slotLinkActivated); this, &AccountSettings::slotLinkActivated);
connect(_model, &FolderStatusModel::suggestExpand, _ui->_folderList, &QTreeView::expand); connect(_model, &FolderStatusModel::suggestExpand, ui->_folderList, &QTreeView::expand);
connect(_model, &FolderStatusModel::dirtyChanged, this, &AccountSettings::refreshSelectiveSyncStatus); connect(_model, &FolderStatusModel::dirtyChanged, this, &AccountSettings::refreshSelectiveSyncStatus);
refreshSelectiveSyncStatus(); refreshSelectiveSyncStatus();
connect(_model, &QAbstractItemModel::rowsInserted, connect(_model, &QAbstractItemModel::rowsInserted,
@@ -173,21 +170,20 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
addAction(syncNowWithRemoteDiscovery); addAction(syncNowWithRemoteDiscovery);
connect(_ui->selectiveSyncApply, &QAbstractButton::clicked, _model, &FolderStatusModel::slotApplySelectiveSync); connect(ui->selectiveSyncApply, &QAbstractButton::clicked, _model, &FolderStatusModel::slotApplySelectiveSync);
connect(_ui->selectiveSyncCancel, &QAbstractButton::clicked, _model, &FolderStatusModel::resetFolders); connect(ui->selectiveSyncCancel, &QAbstractButton::clicked, _model, &FolderStatusModel::resetFolders);
connect(_ui->bigFolderApply, &QAbstractButton::clicked, _model, &FolderStatusModel::slotApplySelectiveSync); connect(ui->bigFolderApply, &QAbstractButton::clicked, _model, &FolderStatusModel::slotApplySelectiveSync);
connect(_ui->bigFolderSyncAll, &QAbstractButton::clicked, _model, &FolderStatusModel::slotSyncAllPendingBigFolders); connect(ui->bigFolderSyncAll, &QAbstractButton::clicked, _model, &FolderStatusModel::slotSyncAllPendingBigFolders);
connect(_ui->bigFolderSyncNone, &QAbstractButton::clicked, _model, &FolderStatusModel::slotSyncNoPendingBigFolders); connect(ui->bigFolderSyncNone, &QAbstractButton::clicked, _model, &FolderStatusModel::slotSyncNoPendingBigFolders);
connect(FolderMan::instance(), &FolderMan::folderListChanged, _model, &FolderStatusModel::resetFolders); connect(FolderMan::instance(), &FolderMan::folderListChanged, _model, &FolderStatusModel::resetFolders);
connect(this, &AccountSettings::folderChanged, _model, &FolderStatusModel::resetFolders); connect(this, &AccountSettings::folderChanged, _model, &FolderStatusModel::resetFolders);
// quotaProgressBar style now set in customizeStyle() QColor color = palette().highlight().color();
/*QColor color = palette().highlight().color(); ui->quotaProgressBar->setStyleSheet(QString::fromLatin1(progressBarStyleC).arg(color.name()));
_ui->quotaProgressBar->setStyleSheet(QString::fromLatin1(progressBarStyleC).arg(color.name()));*/
_ui->connectLabel->setText(tr("No account configured.")); ui->connectLabel->setText(tr("No account configured."));
connect(_accountState, &AccountState::stateChanged, this, &AccountSettings::slotAccountStateChanged); connect(_accountState, &AccountState::stateChanged, this, &AccountSettings::slotAccountStateChanged);
slotAccountStateChanged(); slotAccountStateChanged();
@@ -204,10 +200,8 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
{ {
slotNewMnemonicGenerated(); slotNewMnemonicGenerated();
} else { } else {
_ui->encryptionMessage->hide(); ui->encryptionMessage->hide();
} }
customizeStyle();
} }
@@ -229,9 +223,9 @@ void AccountSettings::createAccountToolbox()
menu->addAction(action); menu->addAction(action);
connect(action, &QAction::triggered, this, &AccountSettings::slotDeleteAccount); connect(action, &QAction::triggered, this, &AccountSettings::slotDeleteAccount);
_ui->_accountToolbox->setText(tr("Account") + QLatin1Char(' ')); ui->_accountToolbox->setText(tr("Account") + QLatin1Char(' '));
_ui->_accountToolbox->setMenu(menu); ui->_accountToolbox->setMenu(menu);
_ui->_accountToolbox->setPopupMode(QToolButton::InstantPopup); ui->_accountToolbox->setPopupMode(QToolButton::InstantPopup);
slotAccountAdded(_accountState); slotAccountAdded(_accountState);
} }
@@ -239,14 +233,14 @@ void AccountSettings::createAccountToolbox()
void AccountSettings::slotNewMnemonicGenerated() void AccountSettings::slotNewMnemonicGenerated()
{ {
_ui->encryptionMessage->setText(tr("This account supports end-to-end encryption")); ui->encryptionMessage->setText(tr("This account supports end-to-end encryption"));
QAction *mnemonic = new QAction(tr("Enable encryption"), this); QAction *mnemonic = new QAction(tr("Enable encryption"), this);
connect(mnemonic, &QAction::triggered, this, &AccountSettings::requesetMnemonic); connect(mnemonic, &QAction::triggered, this, &AccountSettings::requesetMnemonic);
connect(mnemonic, &QAction::triggered, _ui->encryptionMessage, &KMessageWidget::hide); connect(mnemonic, &QAction::triggered, ui->encryptionMessage, &KMessageWidget::hide);
_ui->encryptionMessage->addAction(mnemonic); ui->encryptionMessage->addAction(mnemonic);
_ui->encryptionMessage->show(); ui->encryptionMessage->show();
} }
void AccountSettings::slotMenuBeforeShow() { void AccountSettings::slotMenuBeforeShow() {
@@ -254,7 +248,7 @@ void AccountSettings::slotMenuBeforeShow() {
return; return;
} }
auto menu = _ui->_accountToolbox->menu(); auto menu = ui->_accountToolbox->menu();
// We can't check this during the initial creation as there is no account yet then // We can't check this during the initial creation as there is no account yet then
if (_accountState->account()->capabilities().clientSideEncryptionAvaliable()) { if (_accountState->account()->capabilities().clientSideEncryptionAvaliable()) {
@@ -269,7 +263,7 @@ void AccountSettings::slotMenuBeforeShow() {
QString AccountSettings::selectedFolderAlias() const QString AccountSettings::selectedFolderAlias() const
{ {
QModelIndex selected = _ui->_folderList->selectionModel()->currentIndex(); QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
if (!selected.isValid()) if (!selected.isValid())
return ""; return "";
return _model->data(selected, FolderStatusDelegate::FolderAliasRole).toString(); return _model->data(selected, FolderStatusDelegate::FolderAliasRole).toString();
@@ -282,7 +276,16 @@ void AccountSettings::slotOpenAccountWizard()
if (qgetenv("QT_QPA_PLATFORMTHEME") == "appmenu-qt5" || QSystemTrayIcon::isSystemTrayAvailable()) { if (qgetenv("QT_QPA_PLATFORMTHEME") == "appmenu-qt5" || QSystemTrayIcon::isSystemTrayAvailable()) {
topLevelWidget()->close(); topLevelWidget()->close();
} }
#ifdef Q_OS_MAC
qCDebug(lcAccountSettings) << parent() << topLevelWidget();
SettingsDialogMac *sd = qobject_cast<SettingsDialogMac *>(topLevelWidget());
if (sd) {
sd->showActivityPage();
} else {
qFatal("nope");
}
#endif
OwncloudSetupWizard::runWizard(qApp, SLOT(slotownCloudWizardDone(int)), nullptr); OwncloudSetupWizard::runWizard(qApp, SLOT(slotownCloudWizardDone(int)), nullptr);
} }
@@ -298,7 +301,7 @@ void AccountSettings::slotToggleSignInState()
void AccountSettings::doExpand() void AccountSettings::doExpand()
{ {
_ui->_folderList->expandToDepth(0); ui->_folderList->expandToDepth(0);
} }
void AccountSettings::slotShowMnemonic(const QString &mnemonic) { void AccountSettings::slotShowMnemonic(const QString &mnemonic) {
@@ -329,9 +332,9 @@ void AccountSettings::slotEncryptionFlagError(const QByteArray& fileId, int http
void AccountSettings::slotLockForEncryptionSuccess(const QByteArray& fileId, const QByteArray &token) void AccountSettings::slotLockForEncryptionSuccess(const QByteArray& fileId, const QByteArray &token)
{ {
accountsState()->account()->e2e()->setTokenForFolder(fileId, token); accountsState()->account()->e2e()->setTokenForFolder(fileId, token);
FolderMetadata emptyMetadata(accountsState()->account()); FolderMetadata emptyMetadata(accountsState()->account());
auto encryptedMetadata = emptyMetadata.encryptedMetadata(); auto encryptedMetadata = emptyMetadata.encryptedMetadata();
if (encryptedMetadata.isEmpty()) { if (encryptedMetadata.isEmpty()) {
//TODO: Mark the folder as unencrypted as the metadata generation failed. //TODO: Mark the folder as unencrypted as the metadata generation failed.
@@ -343,36 +346,36 @@ void AccountSettings::slotLockForEncryptionSuccess(const QByteArray& fileId, con
return; return;
} }
auto storeMetadataJob = new StoreMetaDataApiJob(accountsState()->account(), fileId, emptyMetadata.encryptedMetadata()); auto storeMetadataJob = new StoreMetaDataApiJob(accountsState()->account(), fileId, emptyMetadata.encryptedMetadata());
connect(storeMetadataJob, &StoreMetaDataApiJob::success, connect(storeMetadataJob, &StoreMetaDataApiJob::success,
this, &AccountSettings::slotUploadMetadataSuccess); this, &AccountSettings::slotUploadMetadataSuccess);
connect(storeMetadataJob, &StoreMetaDataApiJob::error, connect(storeMetadataJob, &StoreMetaDataApiJob::error,
this, &AccountSettings::slotUpdateMetadataError); this, &AccountSettings::slotUpdateMetadataError);
storeMetadataJob->start(); storeMetadataJob->start();
} }
void AccountSettings::slotUploadMetadataSuccess(const QByteArray& folderId) void AccountSettings::slotUploadMetadataSuccess(const QByteArray& folderId)
{ {
const auto token = accountsState()->account()->e2e()->tokenForFolder(folderId); const auto token = accountsState()->account()->e2e()->tokenForFolder(folderId);
auto unlockJob = new UnlockEncryptFolderApiJob(accountsState()->account(), folderId, token); auto unlockJob = new UnlockEncryptFolderApiJob(accountsState()->account(), folderId, token);
connect(unlockJob, &UnlockEncryptFolderApiJob::success, connect(unlockJob, &UnlockEncryptFolderApiJob::success,
this, &AccountSettings::slotUnlockFolderSuccess); this, &AccountSettings::slotUnlockFolderSuccess);
connect(unlockJob, &UnlockEncryptFolderApiJob::error, connect(unlockJob, &UnlockEncryptFolderApiJob::error,
this, &AccountSettings::slotUnlockFolderError); this, &AccountSettings::slotUnlockFolderError);
unlockJob->start(); unlockJob->start();
} }
void AccountSettings::slotUpdateMetadataError(const QByteArray& folderId, int httpReturnCode) void AccountSettings::slotUpdateMetadataError(const QByteArray& folderId, int httpReturnCode)
{ {
Q_UNUSED(httpReturnCode); Q_UNUSED(httpReturnCode);
const auto token = accountsState()->account()->e2e()->tokenForFolder(folderId); const auto token = accountsState()->account()->e2e()->tokenForFolder(folderId);
auto unlockJob = new UnlockEncryptFolderApiJob(accountsState()->account(), folderId, token); auto unlockJob = new UnlockEncryptFolderApiJob(accountsState()->account(), folderId, token);
connect(unlockJob, &UnlockEncryptFolderApiJob::success, connect(unlockJob, &UnlockEncryptFolderApiJob::success,
this, &AccountSettings::slotUnlockFolderSuccess); this, &AccountSettings::slotUnlockFolderSuccess);
connect(unlockJob, &UnlockEncryptFolderApiJob::error, connect(unlockJob, &UnlockEncryptFolderApiJob::error,
this, &AccountSettings::slotUnlockFolderError); this, &AccountSettings::slotUnlockFolderError);
unlockJob->start(); unlockJob->start();
} }
void AccountSettings::slotLockForEncryptionError(const QByteArray& fileId, int httpErrorCode) void AccountSettings::slotLockForEncryptionError(const QByteArray& fileId, int httpErrorCode)
@@ -419,7 +422,7 @@ bool AccountSettings::canEncryptOrDecrypt (const FolderStatusModel::SubFolderInf
return true; return true;
} }
void AccountSettings::slotMarkSubfolderEncrypted(const FolderStatusModel::SubFolderInfo* folderInfo) void AccountSettings::slotMarkSubfolderEncrpted(const FolderStatusModel::SubFolderInfo* folderInfo)
{ {
if (!canEncryptOrDecrypt(folderInfo)) { if (!canEncryptOrDecrypt(folderInfo)) {
return; return;
@@ -536,51 +539,6 @@ void AccountSettings::slotLockForDecryptionError(const QByteArray& fileId, int h
qDebug() << "Error Locking for decryption"; qDebug() << "Error Locking for decryption";
} }
void AccountSettings::slotEditCurrentIgnoredFiles()
{
Folder *f = FolderMan::instance()->folder(selectedFolderAlias());
if (f == nullptr)
return;
openIgnoredFilesDialog(f->path());
}
void AccountSettings::slotEditCurrentLocalIgnoredFiles()
{
QModelIndex selected = _ui->_folderList->selectionModel()->currentIndex();
if (!selected.isValid() || _model->classify(selected) != FolderStatusModel::SubFolder)
return;
QString fileName = _model->data(selected, FolderStatusDelegate::FolderPathRole).toString();
openIgnoredFilesDialog(fileName);
}
void AccountSettings::openIgnoredFilesDialog(const QString & absFolderPath)
{
Q_ASSERT(absFolderPath.startsWith('/'));
Q_ASSERT(absFolderPath.endsWith('/'));
const QString ignoreFile = absFolderPath + ".sync-exclude.lst";
auto layout = new QVBoxLayout();
auto ignoreListWidget = new IgnoreListTableWidget(this);
ignoreListWidget->readIgnoreFile(ignoreFile);
layout->addWidget(ignoreListWidget);
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
layout->addWidget(buttonBox);
auto dialog = new QDialog();
dialog->setLayout(layout);
connect(buttonBox, &QDialogButtonBox::clicked, [=](QAbstractButton * button) {
if (buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole)
ignoreListWidget->slotWriteIgnoreFile(ignoreFile);
dialog->close();
});
connect(buttonBox, &QDialogButtonBox::rejected,
dialog, &QDialog::close);
dialog->open();
}
void AccountSettings::slotSubfolderContextMenuRequested(const QModelIndex& index, const QPoint& pos) void AccountSettings::slotSubfolderContextMenuRequested(const QModelIndex& index, const QPoint& pos)
{ {
Q_UNUSED(pos); Q_UNUSED(pos);
@@ -603,22 +561,18 @@ void AccountSettings::slotSubfolderContextMenuRequested(const QModelIndex& index
if (!isEncrypted) { if (!isEncrypted) {
ac = menu.addAction(tr("Encrypt")); ac = menu.addAction(tr("Encrypt"));
connect(ac, &QAction::triggered, [this, &info] { slotMarkSubfolderEncrypted(info); }); connect(ac, &QAction::triggered, [this, &info] { slotMarkSubfolderEncrpted(info); });
} else { } else {
// Ingore decrypting for now since it only works with an empty folder // Ingore decrypting for now since it only works with an empty folder
// connect(ac, &QAction::triggered, [this, &info] { slotMarkSubfolderDecrypted(info); }); // connect(ac, &QAction::triggered, [this, &info] { slotMarkSubfolderDecrypted(info); });
} }
} }
ac = menu.addAction(tr("Edit Ignored Files"));
connect(ac, &QAction::triggered, this, &AccountSettings::slotEditCurrentLocalIgnoredFiles);
menu.exec(QCursor::pos()); menu.exec(QCursor::pos());
} }
void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos) void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
{ {
QTreeView *tv = _ui->_folderList; QTreeView *tv = ui->_folderList;
QModelIndex index = tv->indexAt(pos); QModelIndex index = tv->indexAt(pos);
if (!index.isValid()) { if (!index.isValid()) {
return; return;
@@ -646,10 +600,7 @@ void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
QAction *ac = menu->addAction(tr("Open folder")); QAction *ac = menu->addAction(tr("Open folder"));
connect(ac, &QAction::triggered, this, &AccountSettings::slotOpenCurrentFolder); connect(ac, &QAction::triggered, this, &AccountSettings::slotOpenCurrentFolder);
ac = menu->addAction(tr("Edit Ignored Files")); if (!ui->_folderList->isExpanded(index)) {
connect(ac, &QAction::triggered, this, &AccountSettings::slotEditCurrentIgnoredFiles);
if (!_ui->_folderList->isExpanded(index)) {
ac = menu->addAction(tr("Choose what to sync")); ac = menu->addAction(tr("Choose what to sync"));
ac->setEnabled(folderConnected); ac->setEnabled(folderConnected);
connect(ac, &QAction::triggered, this, &AccountSettings::doExpand); connect(ac, &QAction::triggered, this, &AccountSettings::doExpand);
@@ -688,7 +639,7 @@ void AccountSettings::slotFolderListClicked(const QModelIndex &indx)
} }
if (_model->classify(indx) == FolderStatusModel::RootFolder) { if (_model->classify(indx) == FolderStatusModel::RootFolder) {
// tries to find if we clicked on the '...' button. // tries to find if we clicked on the '...' button.
QTreeView *tv = _ui->_folderList; QTreeView *tv = ui->_folderList;
auto pos = tv->mapFromGlobal(QCursor::pos()); auto pos = tv->mapFromGlobal(QCursor::pos());
if (FolderStatusDelegate::optionsButtonRect(tv->visualRect(indx), layoutDirection()).contains(pos)) { if (FolderStatusDelegate::optionsButtonRect(tv->visualRect(indx), layoutDirection()).contains(pos)) {
slotCustomContextMenuRequested(pos); slotCustomContextMenuRequested(pos);
@@ -701,8 +652,8 @@ void AccountSettings::slotFolderListClicked(const QModelIndex &indx)
// Expand root items on single click // Expand root items on single click
if (_accountState && _accountState->state() == AccountState::Connected) { if (_accountState && _accountState->state() == AccountState::Connected) {
bool expanded = !(_ui->_folderList->isExpanded(indx)); bool expanded = !(ui->_folderList->isExpanded(indx));
_ui->_folderList->setExpanded(indx, expanded); ui->_folderList->setExpanded(indx, expanded);
} }
} }
} }
@@ -739,7 +690,7 @@ void AccountSettings::slotFolderWizardAccepted()
qCInfo(lcAccountSettings) << "Creating folder" << definition.localPath; qCInfo(lcAccountSettings) << "Creating folder" << definition.localPath;
if (!dir.mkpath(".")) { if (!dir.mkpath(".")) {
QMessageBox::warning(this, tr("Folder creation failed"), QMessageBox::warning(this, tr("Folder creation failed"),
tr("<p>Could not create local folder <i>%1</i>.</p>") tr("<p>Could not create local folder <i>%1</i>.")
.arg(QDir::toNativeSeparators(definition.localPath))); .arg(QDir::toNativeSeparators(definition.localPath)));
return; return;
} }
@@ -784,7 +735,7 @@ void AccountSettings::slotRemoveCurrentFolder()
{ {
FolderMan *folderMan = FolderMan::instance(); FolderMan *folderMan = FolderMan::instance();
auto folder = folderMan->folder(selectedFolderAlias()); auto folder = folderMan->folder(selectedFolderAlias());
QModelIndex selected = _ui->_folderList->selectionModel()->currentIndex(); QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
if (selected.isValid() && folder) { if (selected.isValid() && folder) {
int row = selected.row(); int row = selected.row();
@@ -826,7 +777,7 @@ void AccountSettings::slotOpenCurrentFolder()
void AccountSettings::slotOpenCurrentLocalSubFolder() void AccountSettings::slotOpenCurrentLocalSubFolder()
{ {
QModelIndex selected = _ui->_folderList->selectionModel()->currentIndex(); QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
if (!selected.isValid() || _model->classify(selected) != FolderStatusModel::SubFolder) if (!selected.isValid() || _model->classify(selected) != FolderStatusModel::SubFolder)
return; return;
QString fileName = _model->data(selected, FolderStatusDelegate::FolderPathRole).toString(); QString fileName = _model->data(selected, FolderStatusDelegate::FolderPathRole).toString();
@@ -840,21 +791,18 @@ void AccountSettings::showConnectionLabel(const QString &message, QStringList er
"border-width: 1px; border-style: solid; border-color: #aaaaaa;" "border-width: 1px; border-style: solid; border-color: #aaaaaa;"
"border-radius:5px;"); "border-radius:5px;");
if (errors.isEmpty()) { if (errors.isEmpty()) {
QString msg = message; ui->connectLabel->setText(message);
Theme::replaceLinkColorStringBackgroundAware(msg); ui->connectLabel->setToolTip(QString());
_ui->connectLabel->setText(msg); ui->connectLabel->setStyleSheet(QString());
_ui->connectLabel->setToolTip(QString());
_ui->connectLabel->setStyleSheet(QString());
} else { } else {
errors.prepend(message); errors.prepend(message);
QString msg = errors.join(QLatin1String("\n")); const QString msg = errors.join(QLatin1String("\n"));
qCDebug(lcAccountSettings) << msg; qCDebug(lcAccountSettings) << msg;
Theme::replaceLinkColorString(msg, QColor("#c1c8e6")); ui->connectLabel->setText(msg);
_ui->connectLabel->setText(msg); ui->connectLabel->setToolTip(QString());
_ui->connectLabel->setToolTip(QString()); ui->connectLabel->setStyleSheet(errStyle);
_ui->connectLabel->setStyleSheet(errStyle);
} }
_ui->accountStatus->setVisible(!message.isEmpty()); ui->accountStatus->setVisible(!message.isEmpty());
} }
void AccountSettings::slotEnableCurrentFolder() void AccountSettings::slotEnableCurrentFolder()
@@ -952,29 +900,29 @@ void AccountSettings::slotOpenOC()
void AccountSettings::slotUpdateQuota(qint64 total, qint64 used) void AccountSettings::slotUpdateQuota(qint64 total, qint64 used)
{ {
if (total > 0) { if (total > 0) {
_ui->quotaProgressBar->setVisible(true); ui->quotaProgressBar->setVisible(true);
_ui->quotaProgressBar->setEnabled(true); ui->quotaProgressBar->setEnabled(true);
// workaround the label only accepting ints (which may be only 32 bit wide) // workaround the label only accepting ints (which may be only 32 bit wide)
const double percent = used / (double)total * 100; const double percent = used / (double)total * 100;
const int percentInt = qMin(qRound(percent), 100); const int percentInt = qMin(qRound(percent), 100);
_ui->quotaProgressBar->setValue(percentInt); ui->quotaProgressBar->setValue(percentInt);
QString usedStr = Utility::octetsToString(used); QString usedStr = Utility::octetsToString(used);
QString totalStr = Utility::octetsToString(total); QString totalStr = Utility::octetsToString(total);
QString percentStr = Utility::compactFormatDouble(percent, 1); QString percentStr = Utility::compactFormatDouble(percent, 1);
QString toolTip = tr("%1 (%3%) of %2 in use. Some folders, including network mounted or shared folders, might have different limits.").arg(usedStr, totalStr, percentStr); QString toolTip = tr("%1 (%3%) of %2 in use. Some folders, including network mounted or shared folders, might have different limits.").arg(usedStr, totalStr, percentStr);
_ui->quotaInfoLabel->setText(tr("%1 of %2 in use").arg(usedStr, totalStr)); ui->quotaInfoLabel->setText(tr("%1 of %2 in use").arg(usedStr, totalStr));
_ui->quotaInfoLabel->setToolTip(toolTip); ui->quotaInfoLabel->setToolTip(toolTip);
_ui->quotaProgressBar->setToolTip(toolTip); ui->quotaProgressBar->setToolTip(toolTip);
} else { } else {
_ui->quotaProgressBar->setVisible(false); ui->quotaProgressBar->setVisible(false);
_ui->quotaInfoLabel->setToolTip(QString()); ui->quotaInfoLabel->setToolTip(QString());
/* -1 means not computed; -2 means unknown; -3 means unlimited (#3940)*/ /* -1 means not computed; -2 means unknown; -3 means unlimited (#3940)*/
if (total == 0 || total == -1) { if (total == 0 || total == -1) {
_ui->quotaInfoLabel->setText(tr("Currently there is no storage usage information available.")); ui->quotaInfoLabel->setText(tr("Currently there is no storage usage information available."));
} else { } else {
QString usedStr = Utility::octetsToString(used); QString usedStr = Utility::octetsToString(used);
_ui->quotaInfoLabel->setText(tr("%1 in use").arg(usedStr)); ui->quotaInfoLabel->setText(tr("%1 in use").arg(usedStr));
} }
} }
} }
@@ -983,7 +931,7 @@ void AccountSettings::slotAccountStateChanged()
{ {
int state = _accountState ? _accountState->state() : AccountState::Disconnected; int state = _accountState ? _accountState->state() : AccountState::Disconnected;
if (_accountState) { if (_accountState) {
_ui->sslButton->updateAccountState(_accountState); ui->sslButton->updateAccountState(_accountState);
AccountPtr account = _accountState->account(); AccountPtr account = _accountState->account();
QUrl safeUrl(account->url()); QUrl safeUrl(account->url());
safeUrl.setPassword(QString()); // Remove the password from the URL to avoid showing it in the UI safeUrl.setPassword(QString()); // Remove the password from the URL to avoid showing it in the UI
@@ -1028,7 +976,7 @@ void AccountSettings::slotAccountStateChanged()
"<a href='%1'>Click here</a> to re-open the browser.") "<a href='%1'>Click here</a> to re-open the browser.")
.arg(url.toString(QUrl::FullyEncoded))); .arg(url.toString(QUrl::FullyEncoded)));
} else { } else {
showConnectionLabel(tr("Connecting to %1").arg(serverWithUser)); showConnectionLabel(tr("Connecting to %1...").arg(serverWithUser));
} }
} else { } else {
showConnectionLabel(tr("No connection to %1 at %2.") showConnectionLabel(tr("No connection to %1 at %2.")
@@ -1042,14 +990,14 @@ void AccountSettings::slotAccountStateChanged()
} }
/* Allow to expand the item if the account is connected. */ /* Allow to expand the item if the account is connected. */
_ui->_folderList->setItemsExpandable(state == AccountState::Connected); ui->_folderList->setItemsExpandable(state == AccountState::Connected);
if (state != AccountState::Connected) { if (state != AccountState::Connected) {
/* check if there are expanded root items, if so, close them */ /* check if there are expanded root items, if so, close them */
int i; int i;
for (i = 0; i < _model->rowCount(); ++i) { for (i = 0; i < _model->rowCount(); ++i) {
if (_ui->_folderList->isExpanded(_model->index(i))) if (ui->_folderList->isExpanded(_model->index(i)))
_ui->_folderList->setExpanded(_model->index(i), false); ui->_folderList->setExpanded(_model->index(i), false);
} }
} else if (_model->isDirty()) { } else if (_model->isDirty()) {
// If we connect and have pending changes, show the list. // If we connect and have pending changes, show the list.
@@ -1093,21 +1041,21 @@ void AccountSettings::slotLinkActivated(const QString &link)
// Make sure the folder itself is expanded // Make sure the folder itself is expanded
Folder *f = FolderMan::instance()->folder(alias); Folder *f = FolderMan::instance()->folder(alias);
QModelIndex folderIndx = _model->indexForPath(f, QString()); QModelIndex folderIndx = _model->indexForPath(f, QString());
if (!_ui->_folderList->isExpanded(folderIndx)) { if (!ui->_folderList->isExpanded(folderIndx)) {
_ui->_folderList->setExpanded(folderIndx, true); ui->_folderList->setExpanded(folderIndx, true);
} }
QModelIndex indx = _model->indexForPath(f, myFolder); QModelIndex indx = _model->indexForPath(f, myFolder);
if (indx.isValid()) { if (indx.isValid()) {
// make sure all the parents are expanded // make sure all the parents are expanded
for (auto i = indx.parent(); i.isValid(); i = i.parent()) { for (auto i = indx.parent(); i.isValid(); i = i.parent()) {
if (!_ui->_folderList->isExpanded(i)) { if (!ui->_folderList->isExpanded(i)) {
_ui->_folderList->setExpanded(i, true); ui->_folderList->setExpanded(i, true);
} }
} }
_ui->_folderList->setSelectionMode(QAbstractItemView::SingleSelection); ui->_folderList->setSelectionMode(QAbstractItemView::SingleSelection);
_ui->_folderList->setCurrentIndex(indx); ui->_folderList->setCurrentIndex(indx);
_ui->_folderList->scrollTo(indx); ui->_folderList->scrollTo(indx);
} else { } else {
qCWarning(lcAccountSettings) << "Unable to find a valid index for " << myFolder; qCWarning(lcAccountSettings) << "Unable to find a valid index for " << myFolder;
} }
@@ -1116,7 +1064,7 @@ void AccountSettings::slotLinkActivated(const QString &link)
AccountSettings::~AccountSettings() AccountSettings::~AccountSettings()
{ {
delete _ui; delete ui;
} }
void AccountSettings::refreshSelectiveSyncStatus() void AccountSettings::refreshSelectiveSyncStatus()
@@ -1154,8 +1102,8 @@ void AccountSettings::refreshSelectiveSyncStatus()
} }
if (msg.isEmpty()) { if (msg.isEmpty()) {
_ui->selectiveSyncButtons->setVisible(true); ui->selectiveSyncButtons->setVisible(true);
_ui->bigFolderUi->setVisible(false); ui->bigFolderUi->setVisible(false);
} else { } else {
ConfigFile cfg; ConfigFile cfg;
QString info = !cfg.confirmExternalStorage() QString info = !cfg.confirmExternalStorage()
@@ -1164,27 +1112,27 @@ void AccountSettings::refreshSelectiveSyncStatus()
? tr("There are folders that were not synchronized because they are external storages: ") ? tr("There are folders that were not synchronized because they are external storages: ")
: tr("There are folders that were not synchronized because they are too big or external storages: "); : tr("There are folders that were not synchronized because they are too big or external storages: ");
_ui->selectiveSyncNotification->setText(info + msg); ui->selectiveSyncNotification->setText(info + msg);
_ui->selectiveSyncButtons->setVisible(false); ui->selectiveSyncButtons->setVisible(false);
_ui->bigFolderUi->setVisible(true); ui->bigFolderUi->setVisible(true);
shouldBeVisible = true; shouldBeVisible = true;
} }
_ui->selectiveSyncApply->setEnabled(_model->isDirty() || !msg.isEmpty()); ui->selectiveSyncApply->setEnabled(_model->isDirty() || !msg.isEmpty());
bool wasVisible = !_ui->selectiveSyncStatus->isHidden(); bool wasVisible = !ui->selectiveSyncStatus->isHidden();
if (wasVisible != shouldBeVisible) { if (wasVisible != shouldBeVisible) {
QSize hint = _ui->selectiveSyncStatus->sizeHint(); QSize hint = ui->selectiveSyncStatus->sizeHint();
if (shouldBeVisible) { if (shouldBeVisible) {
_ui->selectiveSyncStatus->setMaximumHeight(0); ui->selectiveSyncStatus->setMaximumHeight(0);
_ui->selectiveSyncStatus->setVisible(true); ui->selectiveSyncStatus->setVisible(true);
} }
auto anim = new QPropertyAnimation(_ui->selectiveSyncStatus, "maximumHeight", _ui->selectiveSyncStatus); auto anim = new QPropertyAnimation(ui->selectiveSyncStatus, "maximumHeight", ui->selectiveSyncStatus);
anim->setEndValue(shouldBeVisible ? hint.height() : 0); anim->setEndValue(shouldBeVisible ? hint.height() : 0);
anim->start(QAbstractAnimation::DeleteWhenStopped); anim->start(QAbstractAnimation::DeleteWhenStopped);
connect(anim, &QPropertyAnimation::finished, [this, shouldBeVisible]() { connect(anim, &QPropertyAnimation::finished, [this, shouldBeVisible]() {
_ui->selectiveSyncStatus->setMaximumHeight(QWIDGETSIZE_MAX); ui->selectiveSyncStatus->setMaximumHeight(QWIDGETSIZE_MAX);
if (!shouldBeVisible) { if (!shouldBeVisible) {
_ui->selectiveSyncStatus->hide(); ui->selectiveSyncStatus->hide();
} }
}); });
} }
@@ -1244,30 +1192,12 @@ bool AccountSettings::event(QEvent *e)
// Expand the folder automatically only if there's only one, see #4283 // Expand the folder automatically only if there's only one, see #4283
// The 2 is 1 folder + 1 'add folder' button // The 2 is 1 folder + 1 'add folder' button
if (_model->rowCount() <= 2) { if (_model->rowCount() <= 2) {
_ui->_folderList->setExpanded(_model->index(0, 0), true); ui->_folderList->setExpanded(_model->index(0, 0), true);
} }
} }
return QWidget::event(e); return QWidget::event(e);
} }
void AccountSettings::slotStyleChanged()
{
customizeStyle();
// Notify the other widgets (Dark-/Light-Mode switching)
emit styleChanged();
}
void AccountSettings::customizeStyle()
{
QString msg = _ui->connectLabel->text();
Theme::replaceLinkColorStringBackgroundAware(msg);
_ui->connectLabel->setText(msg);
QColor color = palette().highlight().color();
_ui->quotaProgressBar->setStyleSheet(QString::fromLatin1(progressBarStyleC).arg(color.name()));
}
} // namespace OCC } // namespace OCC
#include "accountsettings.moc" #include "accountsettings.moc"

View File

@@ -63,14 +63,11 @@ signals:
void openFolderAlias(const QString &); void openFolderAlias(const QString &);
void showIssuesList(AccountState *account); void showIssuesList(AccountState *account);
void requesetMnemonic(); void requesetMnemonic();
void removeAccountFolders(AccountState *account);
void styleChanged();
public slots: public slots:
void slotOpenOC(); void slotOpenOC();
void slotUpdateQuota(qint64, qint64); void slotUpdateQuota(qint64, qint64);
void slotAccountStateChanged(); void slotAccountStateChanged();
void slotStyleChanged();
AccountState *accountsState() { return _accountState; } AccountState *accountsState() { return _accountState; }
@@ -83,8 +80,6 @@ protected slots:
void slotRemoveCurrentFolder(); void slotRemoveCurrentFolder();
void slotOpenCurrentFolder(); // sync folder void slotOpenCurrentFolder(); // sync folder
void slotOpenCurrentLocalSubFolder(); // selected subfolder in sync folder void slotOpenCurrentLocalSubFolder(); // selected subfolder in sync folder
void slotEditCurrentIgnoredFiles();
void slotEditCurrentLocalIgnoredFiles();
void slotFolderWizardAccepted(); void slotFolderWizardAccepted();
void slotFolderWizardRejected(); void slotFolderWizardRejected();
void slotDeleteAccount(); void slotDeleteAccount();
@@ -92,7 +87,7 @@ protected slots:
void slotOpenAccountWizard(); void slotOpenAccountWizard();
void slotAccountAdded(AccountState *); void slotAccountAdded(AccountState *);
void refreshSelectiveSyncStatus(); void refreshSelectiveSyncStatus();
void slotMarkSubfolderEncrypted(const FolderStatusModel::SubFolderInfo* folderInfo); void slotMarkSubfolderEncrpted(const FolderStatusModel::SubFolderInfo* folderInfo);
void slotMarkSubfolderDecrypted(const FolderStatusModel::SubFolderInfo* folderInfo); void slotMarkSubfolderDecrypted(const FolderStatusModel::SubFolderInfo* folderInfo);
void slotSubfolderContextMenuRequested(const QModelIndex& idx, const QPoint& point); void slotSubfolderContextMenuRequested(const QModelIndex& idx, const QPoint& point);
void slotCustomContextMenuRequested(const QPoint &); void slotCustomContextMenuRequested(const QPoint &);
@@ -115,7 +110,7 @@ protected slots:
void slotUploadMetadataSuccess(const QByteArray& folderId); void slotUploadMetadataSuccess(const QByteArray& folderId);
void slotUpdateMetadataError(const QByteArray& folderId, int httpReturnCode); void slotUpdateMetadataError(const QByteArray& folderId, int httpReturnCode);
// Remove Encryption Bit. // Remove Encryotion Bit.
void slotLockForDecryptionSuccess(const QByteArray& folderId, const QByteArray& token); void slotLockForDecryptionSuccess(const QByteArray& folderId, const QByteArray& token);
void slotLockForDecryptionError(const QByteArray& folderId, int httpReturnCode); void slotLockForDecryptionError(const QByteArray& folderId, int httpReturnCode);
void slotDeleteMetadataSuccess(const QByteArray& folderId); void slotDeleteMetadataSuccess(const QByteArray& folderId);
@@ -130,13 +125,11 @@ private:
QStringList errors = QStringList()); QStringList errors = QStringList());
bool event(QEvent *) override; bool event(QEvent *) override;
void createAccountToolbox(); void createAccountToolbox();
void openIgnoredFilesDialog(const QString & absFolderPath);
void customizeStyle();
/// Returns the alias of the selected folder, empty string if none /// Returns the alias of the selected folder, empty string if none
QString selectedFolderAlias() const; QString selectedFolderAlias() const;
Ui::AccountSettings *_ui; Ui::AccountSettings *ui;
FolderStatusModel *_model; FolderStatusModel *_model;
QUrl _OCUrl; QUrl _OCUrl;

View File

@@ -11,7 +11,7 @@
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string notr="true">Form</string> <string>Form</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="4" column="0"> <item row="4" column="0">
@@ -187,7 +187,7 @@
<item row="0" column="2"> <item row="0" column="2">
<widget class="QToolButton" name="_accountToolbox"> <widget class="QToolButton" name="_accountToolbox">
<property name="text"> <property name="text">
<string notr="true">...</string> <string>...</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@@ -14,7 +14,6 @@
#include "accountstate.h" #include "accountstate.h"
#include "accountmanager.h" #include "accountmanager.h"
#include "remotewipe.h"
#include "account.h" #include "account.h"
#include "creds/abstractcredentials.h" #include "creds/abstractcredentials.h"
#include "creds/httpcredentials.h" #include "creds/httpcredentials.h"
@@ -25,11 +24,6 @@
#include <QTimer> #include <QTimer>
#include <qfontmetrics.h> #include <qfontmetrics.h>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkRequest>
#include <QBuffer>
namespace OCC { namespace OCC {
Q_LOGGING_CATEGORY(lcAccountState, "nextcloud.gui.account.state", QtInfoMsg) Q_LOGGING_CATEGORY(lcAccountState, "nextcloud.gui.account.state", QtInfoMsg)
@@ -42,12 +36,11 @@ AccountState::AccountState(AccountPtr account)
, _waitingForNewCredentials(false) , _waitingForNewCredentials(false)
, _notificationsEtagResponseHeader("*") , _notificationsEtagResponseHeader("*")
, _maintenanceToConnectedDelay(60000 + (qrand() % (4 * 60000))) // 1-5min delay , _maintenanceToConnectedDelay(60000 + (qrand() % (4 * 60000))) // 1-5min delay
, _remoteWipe(new RemoteWipe(_account))
{ {
qRegisterMetaType<AccountState *>("AccountState*"); qRegisterMetaType<AccountState *>("AccountState*");
connect(account.data(), &Account::invalidCredentials, connect(account.data(), &Account::invalidCredentials,
this, &AccountState::slotHandleRemoteWipeCheck); this, &AccountState::slotInvalidCredentials);
connect(account.data(), &Account::credentialsFetched, connect(account.data(), &Account::credentialsFetched,
this, &AccountState::slotCredentialsFetched); this, &AccountState::slotCredentialsFetched);
connect(account.data(), &Account::credentialsAsked, connect(account.data(), &Account::credentialsAsked,
@@ -310,7 +303,7 @@ void AccountState::slotConnectionValidatorResult(ConnectionValidator::Status sta
break; break;
case ConnectionValidator::CredentialsWrong: case ConnectionValidator::CredentialsWrong:
case ConnectionValidator::CredentialsNotReady: case ConnectionValidator::CredentialsNotReady:
handleInvalidCredentials(); slotInvalidCredentials();
break; break;
case ConnectionValidator::SslError: case ConnectionValidator::SslError:
setState(SignedOut); setState(SignedOut);
@@ -329,20 +322,7 @@ void AccountState::slotConnectionValidatorResult(ConnectionValidator::Status sta
} }
} }
void AccountState::slotHandleRemoteWipeCheck() void AccountState::slotInvalidCredentials()
{
// make sure it changes account state and icons
signOutByUi();
qCInfo(lcAccountState) << "Invalid credentials for" << _account->url().toString()
<< "checking for remote wipe request";
_waitingForNewCredentials = false;
setState(SignedOut);
}
void AccountState::handleInvalidCredentials()
{ {
if (isSignedOut() || _waitingForNewCredentials) if (isSignedOut() || _waitingForNewCredentials)
return; return;
@@ -363,7 +343,6 @@ void AccountState::handleInvalidCredentials()
account()->credentials()->askFromUser(); account()->credentials()->askFromUser();
} }
void AccountState::slotCredentialsFetched(AbstractCredentials *) void AccountState::slotCredentialsFetched(AbstractCredentials *)
{ {
// Make a connection attempt, no matter whether the credentials are // Make a connection attempt, no matter whether the credentials are

View File

@@ -29,7 +29,6 @@ namespace OCC {
class AccountState; class AccountState;
class Account; class Account;
class RemoteWipe;
typedef QExplicitlySharedDataPointer<AccountState> AccountStatePtr; typedef QExplicitlySharedDataPointer<AccountState> AccountStatePtr;
@@ -151,9 +150,6 @@ public:
*/ */
void setNavigationAppsEtagResponseHeader(const QByteArray &value); void setNavigationAppsEtagResponseHeader(const QByteArray &value);
///Asks for user credentials
void handleInvalidCredentials();
public slots: public slots:
/// Triggers a ping to the server to update state and /// Triggers a ping to the server to update state and
/// connection status and errors. /// connection status and errors.
@@ -168,11 +164,7 @@ signals:
protected Q_SLOTS: protected Q_SLOTS:
void slotConnectionValidatorResult(ConnectionValidator::Status status, const QStringList &errors); void slotConnectionValidatorResult(ConnectionValidator::Status status, const QStringList &errors);
void slotInvalidCredentials();
/// When client gets a 401 or 403 checks if server requested remote wipe
/// before asking for user credentials again
void slotHandleRemoteWipeCheck();
void slotCredentialsFetched(AbstractCredentials *creds); void slotCredentialsFetched(AbstractCredentials *creds);
void slotCredentialsAsked(AbstractCredentials *creds); void slotCredentialsAsked(AbstractCredentials *creds);
@@ -198,13 +190,6 @@ private:
* Milliseconds for which to delay reconnection after 503/maintenance. * Milliseconds for which to delay reconnection after 503/maintenance.
*/ */
int _maintenanceToConnectedDelay; int _maintenanceToConnectedDelay;
/**
* Connects remote wipe check with the account
* the log out triggers the check (loads app password -> create request)
*/
RemoteWipe *_remoteWipe;
}; };
} }

View File

@@ -21,7 +21,7 @@ namespace OCC {
bool operator<(const Activity &rhs, const Activity &lhs) bool operator<(const Activity &rhs, const Activity &lhs)
{ {
return rhs._dateTime > lhs._dateTime; return rhs._dateTime.toMSecsSinceEpoch() > lhs._dateTime.toMSecsSinceEpoch();
} }
bool operator==(const Activity &rhs, const Activity &lhs) bool operator==(const Activity &rhs, const Activity &lhs)

View File

@@ -15,7 +15,6 @@
*/ */
#include "activityitemdelegate.h" #include "activityitemdelegate.h"
#include "activitylistmodel.h"
#include "folderstatusmodel.h" #include "folderstatusmodel.h"
#include "folderman.h" #include "folderman.h"
#include "accountstate.h" #include "accountstate.h"
@@ -27,14 +26,6 @@
#include <QPainter> #include <QPainter>
#include <QApplication> #include <QApplication>
#define FIXME_USE_HIGH_DPI_RATIO
#ifdef FIXME_USE_HIGH_DPI_RATIO
// FIXME: Find a better way to calculate the text width on high-dpi displays (Retina).
#include <QDesktopWidget>
#endif
#define HASQT5_11 (QT_VERSION >= QT_VERSION_CHECK(5,11,0))
namespace OCC { namespace OCC {
int ActivityItemDelegate::_iconHeight = 0; int ActivityItemDelegate::_iconHeight = 0;
@@ -47,12 +38,6 @@ int ActivityItemDelegate::_buttonHeight = 0;
const QString ActivityItemDelegate::_remote_share("remote_share"); const QString ActivityItemDelegate::_remote_share("remote_share");
const QString ActivityItemDelegate::_call("call"); const QString ActivityItemDelegate::_call("call");
ActivityItemDelegate::ActivityItemDelegate()
: QStyledItemDelegate()
{
customizeStyle();
}
int ActivityItemDelegate::iconHeight() int ActivityItemDelegate::iconHeight()
{ {
if (_iconHeight == 0) { if (_iconHeight == 0) {
@@ -102,26 +87,10 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
int iconSize = 16; int iconSize = 16;
int iconOffset = qRound(fm.height() / 4.0 * 7.0); int iconOffset = qRound(fm.height() / 4.0 * 7.0);
int offset = 4; int offset = 4;
const bool isSelected = (option.state & QStyle::State_Selected);
#ifdef FIXME_USE_HIGH_DPI_RATIO
// FIXME: Find a better way to calculate the text width on high-dpi displays (Retina).
const int device_pixel_ration = QApplication::desktop()->devicePixelRatio();
int pixel_ratio = (device_pixel_ration > 1 ? device_pixel_ration : 1);
#endif
// get the data // get the data
Activity::Type activityType = qvariant_cast<Activity::Type>(index.data(ActionRole)); Activity::Type activityType = qvariant_cast<Activity::Type>(index.data(ActionRole));
QIcon actionIcon; QIcon actionIcon = qvariant_cast<QIcon>(index.data(ActionIconRole));
const ActivityListModel::ActionIcon icn = qvariant_cast<ActivityListModel::ActionIcon>(index.data(ActionIconRole));
switch(icn.iconType) {
case ActivityListModel::ActivityIconType::iconUseCached: actionIcon = icn.cachedIcon; break;
case ActivityListModel::ActivityIconType::iconActivity: actionIcon = (isSelected ? _iconActivity_sel : _iconActivity); break;
case ActivityListModel::ActivityIconType::iconBell: actionIcon = (isSelected ? _iconBell_sel : _iconBell); break;
case ActivityListModel::ActivityIconType::iconStateError: actionIcon = _iconStateError; break;
case ActivityListModel::ActivityIconType::iconStateWarning: actionIcon = _iconStateWarning; break;
case ActivityListModel::ActivityIconType::iconStateInfo: actionIcon = _iconStateInfo; break;
case ActivityListModel::ActivityIconType::iconStateSync: actionIcon = _iconStateSync; break;
}
QString objectType = qvariant_cast<QString>(index.data(ObjectTypeRole)); QString objectType = qvariant_cast<QString>(index.data(ObjectTypeRole));
QString actionText = qvariant_cast<QString>(index.data(ActionTextRole)); QString actionText = qvariant_cast<QString>(index.data(ActionTextRole));
QString messageText = qvariant_cast<QString>(index.data(MessageRole)); QString messageText = qvariant_cast<QString>(index.data(MessageRole));
@@ -137,27 +106,15 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
// subject text rect // subject text rect
QRect actionTextBox = actionIconRect; QRect actionTextBox = actionIconRect;
#if (HASQT5_11)
int actionTextBoxWidth = fm.horizontalAdvance(actionText);
#else
int actionTextBoxWidth = fm.width(actionText); int actionTextBoxWidth = fm.width(actionText);
#endif
actionTextBox.setTop(option.rect.top() + margin + offset/2); actionTextBox.setTop(option.rect.top() + margin + offset/2);
actionTextBox.setHeight(fm.height()); actionTextBox.setHeight(fm.height());
actionTextBox.setLeft(actionIconRect.right() + margin); actionTextBox.setLeft(actionIconRect.right() + margin);
#ifdef FIXME_USE_HIGH_DPI_RATIO
// FIXME: Find a better way to calculate the text width on high-dpi displays (Retina).
actionTextBoxWidth *= pixel_ratio;
#endif
actionTextBox.setRight(actionTextBox.left() + actionTextBoxWidth + margin); actionTextBox.setRight(actionTextBox.left() + actionTextBoxWidth + margin);
// message text rect // message text rect
QRect messageTextBox = actionTextBox; QRect messageTextBox = actionTextBox;
#if (HASQT5_11)
int messageTextWidth = fm.horizontalAdvance(messageText);
#else
int messageTextWidth = fm.width(messageText); int messageTextWidth = fm.width(messageText);
#endif
int messageTextTop = option.rect.top() + fm.height() + margin; int messageTextTop = option.rect.top() + fm.height() + margin;
if(actionText.isEmpty()) messageTextTop = option.rect.top() + margin + offset/2; if(actionText.isEmpty()) messageTextTop = option.rect.top() + margin + offset/2;
messageTextBox.setTop(messageTextTop); messageTextBox.setTop(messageTextTop);
@@ -171,21 +128,14 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
// time box rect // time box rect
QRect timeBox = messageTextBox; QRect timeBox = messageTextBox;
#if (HASQT5_11) QString timeStr = tr("%1").arg(timeText);
int timeTextWidth = fm.horizontalAdvance(timeText); int timeTextWidth = fm.width(timeStr);
#else
int timeTextWidth = fm.width(timeText);
#endif
int timeTop = option.rect.top() + fm.height() + fm.height() + margin + offset/2; int timeTop = option.rect.top() + fm.height() + fm.height() + margin + offset/2;
if(messageText.isEmpty() || actionText.isEmpty()) if(messageText.isEmpty() || actionText.isEmpty())
timeTop = option.rect.top() + fm.height() + margin; timeTop = option.rect.top() + fm.height() + margin;
timeBox.setTop(timeTop); timeBox.setTop(timeTop);
timeBox.setHeight(fm.height()); timeBox.setHeight(fm.height());
timeBox.setBottom(timeBox.top() + fm.height()); timeBox.setBottom(timeBox.top() + fm.height());
#ifdef FIXME_USE_HIGH_DPI_RATIO
// FIXME: Find a better way to calculate the text width on high-dpi displays (Retina).
timeTextWidth *= pixel_ratio;
#endif
timeBox.setRight(timeBox.left() + timeTextWidth + margin); timeBox.setRight(timeBox.left() + timeTextWidth + margin);
// buttons - default values // buttons - default values
@@ -220,9 +170,9 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
if(activityType == Activity::Type::NotificationType){ if(activityType == Activity::Type::NotificationType){
// Secondary will be 'Dismiss' or '...' multiple options button // Secondary will be 'Dismiss' or '...' multiple options button
secondaryButton.icon = (isSelected ? _iconClose_sel : _iconClose); secondaryButton.icon = QIcon(QLatin1String(":/client/resources/close.svg"));
if(customList.size() > 1) if(customList.size() > 1)
secondaryButton.icon = (isSelected ? _iconMore_sel : _iconMore); secondaryButton.icon = QIcon(QLatin1String(":/client/resources/more.svg"));
secondaryButton.iconSize = QSize(iconSize, iconSize); secondaryButton.iconSize = QSize(iconSize, iconSize);
// Primary button will be 'More Information' or 'Accept' // Primary button will be 'More Information' or 'Accept'
@@ -230,11 +180,7 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
if(objectType == _remote_share) primaryButton.text = tr("Accept"); if(objectType == _remote_share) primaryButton.text = tr("Accept");
if(objectType == _call) primaryButton.text = tr("Join"); if(objectType == _call) primaryButton.text = tr("Join");
#if (HASQT5_11)
primaryButton.rect.setLeft(left - margin * 2 - fm.horizontalAdvance(primaryButton.text));
#else
primaryButton.rect.setLeft(left - margin * 2 - fm.width(primaryButton.text)); primaryButton.rect.setLeft(left - margin * 2 - fm.width(primaryButton.text));
#endif
// save info to be able to filter mouse clicks // save info to be able to filter mouse clicks
_buttonHeight = buttonSize; _buttonHeight = buttonSize;
@@ -245,17 +191,12 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
} else if(activityType == Activity::SyncResultType){ } else if(activityType == Activity::SyncResultType){
// Secondary will be 'open file manager' with the folder icon // Secondary will be 'open file manager' with the folder icon
secondaryButton.icon = _iconFolder; secondaryButton.icon = QIcon(QLatin1String(":/client/resources/folder.svg"));
secondaryButton.iconSize = QSize(iconSize, iconSize); secondaryButton.iconSize = QSize(iconSize, iconSize);
// Primary button will be 'open browser' // Primary button will be 'open browser'
primaryButton.text = tr("Open Browser"); primaryButton.text = tr("Open Browser");
#if (HASQT5_11)
primaryButton.rect.setLeft(left - margin * 2 - fm.horizontalAdvance(primaryButton.text));
#else
primaryButton.rect.setLeft(left - margin * 2 - fm.width(primaryButton.text)); primaryButton.rect.setLeft(left - margin * 2 - fm.width(primaryButton.text));
#endif
// save info to be able to filter mouse clicks // save info to be able to filter mouse clicks
_buttonHeight = buttonSize; _buttonHeight = buttonSize;
@@ -266,7 +207,7 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
} else if(activityType == Activity::SyncFileItemType){ } else if(activityType == Activity::SyncFileItemType){
// Secondary will be 'open file manager' with the folder icon // Secondary will be 'open file manager' with the folder icon
secondaryButton.icon = _iconFolder; secondaryButton.icon = QIcon(QLatin1String(":/client/resources/folder.svg"));
secondaryButton.iconSize = QSize(iconSize, iconSize); secondaryButton.iconSize = QSize(iconSize, iconSize);
// No primary button on this case // No primary button on this case
@@ -291,7 +232,7 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
p.setCurrentColorGroup(QPalette::Disabled); p.setCurrentColorGroup(QPalette::Disabled);
// change pen color if the line is selected // change pen color if the line is selected
if (isSelected) if (option.state & QStyle::State_Selected)
painter->setPen(p.color(QPalette::HighlightedText)); painter->setPen(p.color(QPalette::HighlightedText));
else else
painter->setPen(p.color(QPalette::Text)); painter->setPen(p.color(QPalette::Text));
@@ -305,15 +246,8 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
painter->drawText(actionTextBox, elidedAction); painter->drawText(actionTextBox, elidedAction);
// draw the buttons // draw the buttons
if(activityType == Activity::Type::NotificationType || activityType == Activity::Type::SyncResultType) { if(activityType == Activity::Type::NotificationType || activityType == Activity::Type::SyncResultType)
primaryButton.palette = p;
if (isSelected)
primaryButton.palette.setColor(QPalette::ButtonText, p.color(QPalette::HighlightedText));
else
primaryButton.palette.setColor(QPalette::ButtonText, p.color(QPalette::Text));
QApplication::style()->drawControl(QStyle::CE_PushButton, &primaryButton, painter); QApplication::style()->drawControl(QStyle::CE_PushButton, &primaryButton, painter);
}
// Since they are errors on local syncing, there is nothing to do in the server // Since they are errors on local syncing, there is nothing to do in the server
if(activityType != Activity::Type::ActivityType) if(activityType != Activity::Type::ActivityType)
@@ -327,13 +261,13 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
} }
// change pen color for the time // change pen color for the time
if (isSelected) if (option.state & QStyle::State_Selected)
painter->setPen(p.color(QPalette::Disabled, QPalette::HighlightedText)); painter->setPen(p.color(QPalette::Disabled, QPalette::HighlightedText));
else else
painter->setPen(p.color(QPalette::Disabled, QPalette::Text)); painter->setPen(p.color(QPalette::Disabled, QPalette::Text));
// draw the time // draw the time
const QString elidedTime = fm.elidedText(timeText, Qt::ElideRight, spaceLeftForText); const QString elidedTime = fm.elidedText(timeStr, Qt::ElideRight, spaceLeftForText);
painter->drawText(timeBox, elidedTime); painter->drawText(timeBox, elidedTime);
painter->restore(); painter->restore();
@@ -376,32 +310,4 @@ bool ActivityItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
return QStyledItemDelegate::editorEvent(event, model, option, index); return QStyledItemDelegate::editorEvent(event, model, option, index);
} }
void ActivityItemDelegate::slotStyleChanged()
{
customizeStyle();
}
void ActivityItemDelegate::customizeStyle()
{
QPalette pal;
pal.setColor(QPalette::Base, QColor(0,0,0)); // use dark background colour to invert icons
_iconClose = Theme::createColorAwareIcon(QLatin1String(":/client/resources/close.svg"));
_iconClose_sel = Theme::createColorAwareIcon(QLatin1String(":/client/resources/close.svg"), pal);
_iconMore = Theme::createColorAwareIcon(QLatin1String(":/client/resources/more.svg"));
_iconMore_sel = Theme::createColorAwareIcon(QLatin1String(":/client/resources/more.svg"), pal);
_iconFolder = QIcon(QLatin1String(":/client/resources/folder.svg"));
_iconActivity = Theme::createColorAwareIcon(QLatin1String(":/client/resources/activity.png"));
_iconActivity_sel = Theme::createColorAwareIcon(QLatin1String(":/client/resources/activity.png"), pal);
_iconBell = Theme::createColorAwareIcon(QLatin1String(":/client/resources/bell.svg"));
_iconBell_sel = Theme::createColorAwareIcon(QLatin1String(":/client/resources/bell.svg"), pal);
_iconStateError = QIcon(QLatin1String(":/client/resources/state-error.svg"));
_iconStateWarning = QIcon(QLatin1String(":/client/resources/state-warning.svg"));
_iconStateInfo = QIcon(QLatin1String(":/client/resources/state-info.svg"));
_iconStateSync = QIcon(QLatin1String(":/client/resources/state-sync.svg"));
}
} // namespace OCC } // namespace OCC

View File

@@ -43,8 +43,6 @@ public:
AccountConnectedRole, AccountConnectedRole,
SyncFileStatusRole }; SyncFileStatusRole };
ActivityItemDelegate();
void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const override; void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const override;
QSize sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const override; QSize sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const override;
bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option,
@@ -53,16 +51,11 @@ public:
static int rowHeight(); static int rowHeight();
static int iconHeight(); static int iconHeight();
public slots:
void slotStyleChanged();
signals: signals:
void primaryButtonClickedOnItemView(const QModelIndex &index); void primaryButtonClickedOnItemView(const QModelIndex &index);
void secondaryButtonClickedOnItemView(const QModelIndex &index); void secondaryButtonClickedOnItemView(const QModelIndex &index);
private: private:
void customizeStyle();
static int _margin; static int _margin;
static int _iconHeight; static int _iconHeight;
static int _primaryButtonWidth; static int _primaryButtonWidth;
@@ -72,23 +65,6 @@ private:
static int _buttonHeight; static int _buttonHeight;
static const QString _remote_share; static const QString _remote_share;
static const QString _call; static const QString _call;
QIcon _iconClose;
QIcon _iconClose_sel;
QIcon _iconMore;
QIcon _iconMore_sel;
QIcon _iconFolder;
QIcon _iconActivity;
QIcon _iconActivity_sel;
QIcon _iconBell;
QIcon _iconBell_sel;
QIcon _iconStateError;
QIcon _iconStateWarning;
QIcon _iconStateInfo;
QIcon _iconStateSync;
}; };
} // namespace OCC } // namespace OCC

View File

@@ -71,14 +71,13 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
return QVariant(list.at(0)); return QVariant(list.at(0));
} }
// File does not exist anymore? Let's try to open its path // File does not exist anymore? Let's try to open its path
if(QFileInfo(relPath).exists()) { list = FolderMan::instance()->findFileInLocalFolders(QFileInfo(relPath).path(), ast->account());
list = FolderMan::instance()->findFileInLocalFolders(QFileInfo(relPath).path(), ast->account()); if (list.count() > 0) {
if (list.count() > 0) { return QVariant(list.at(0));
return QVariant(list.at(0));
}
} }
} }
return QVariant(); return QVariant();
break;
case ActivityItemDelegate::ActionsLinksRole:{ case ActivityItemDelegate::ActionsLinksRole:{
QList<QVariant> customList; QList<QVariant> customList;
foreach (ActivityLink customItem, a._links) { foreach (ActivityLink customItem, a._links) {
@@ -87,61 +86,60 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
customList << customVariant; customList << customVariant;
} }
return customList; return customList;
break;
} }
case ActivityItemDelegate::ActionIconRole:{ case ActivityItemDelegate::ActionIconRole:
ActionIcon actionIcon;
if(a._type == Activity::NotificationType){ if(a._type == Activity::NotificationType){
QIcon cachedIcon = ServerNotificationHandler::iconCache.value(a._id); QIcon cachedIcon = ServerNotificationHandler::iconCache.value(a._id);
if(!cachedIcon.isNull()) { if(!cachedIcon.isNull())
actionIcon.iconType = ActivityIconType::iconUseCached; return cachedIcon;
actionIcon.cachedIcon = cachedIcon; else return QIcon(QLatin1String(":/client/resources/bell.svg"));
} else {
actionIcon.iconType = ActivityIconType::iconBell;
}
} else if(a._type == Activity::SyncResultType){ } else if(a._type == Activity::SyncResultType){
actionIcon.iconType = ActivityIconType::iconStateError; return QIcon(QLatin1String(":/client/resources/state-error.svg"));
} else if(a._type == Activity::SyncFileItemType){ } else if(a._type == Activity::SyncFileItemType){
if(a._status == SyncFileItem::NormalError if(a._status == SyncFileItem::NormalError
|| a._status == SyncFileItem::FatalError || a._status == SyncFileItem::FatalError
|| a._status == SyncFileItem::DetailError || a._status == SyncFileItem::DetailError
|| a._status == SyncFileItem::BlacklistedError) { || a._status == SyncFileItem::BlacklistedError) {
actionIcon.iconType = ActivityIconType::iconStateError; return QIcon(QLatin1String(":/client/resources/state-error.svg"));
} else if(a._status == SyncFileItem::SoftError } else if(a._status == SyncFileItem::SoftError
|| a._status == SyncFileItem::FileIgnored
|| a._status == SyncFileItem::Conflict || a._status == SyncFileItem::Conflict
|| a._status == SyncFileItem::Restoration || a._status == SyncFileItem::Restoration
|| a._status == SyncFileItem::FileLocked){ || a._status == SyncFileItem::FileLocked){
actionIcon.iconType = ActivityIconType::iconStateWarning; return QIcon(QLatin1String(":/client/resources/state-warning.svg"));
} else if(a._status == SyncFileItem::FileIgnored){
actionIcon.iconType = ActivityIconType::iconStateInfo;
} else {
actionIcon.iconType = ActivityIconType::iconStateSync;
} }
} else { return QIcon(QLatin1String(":/client/resources/state-sync.svg"));
actionIcon.iconType = ActivityIconType::iconActivity;
} }
QVariant icn; return QIcon(QLatin1String(":/client/resources/activity.png"));
icn.setValue(actionIcon); break;
return icn;
}
case ActivityItemDelegate::ObjectTypeRole: case ActivityItemDelegate::ObjectTypeRole:
return a._objectType; return a._objectType;
break;
case ActivityItemDelegate::ActionRole:{ case ActivityItemDelegate::ActionRole:{
QVariant type; QVariant type;
type.setValue(a._type); type.setValue(a._type);
return type; return type;
break;
} }
case ActivityItemDelegate::ActionTextRole: case ActivityItemDelegate::ActionTextRole:
return a._subject; return a._subject;
break;
case ActivityItemDelegate::MessageRole: case ActivityItemDelegate::MessageRole:
return a._message; return a._message;
break;
case ActivityItemDelegate::LinkRole: case ActivityItemDelegate::LinkRole:
return a._link; return a._link;
break;
case ActivityItemDelegate::AccountRole: case ActivityItemDelegate::AccountRole:
return a._accName; return a._accName;
break;
case ActivityItemDelegate::PointInTimeRole: case ActivityItemDelegate::PointInTimeRole:
return QString("%1 (%2)").arg(a._dateTime.toLocalTime().toString(Qt::DefaultLocaleShortDate), Utility::timeAgoInWords(a._dateTime.toLocalTime())); return Utility::timeAgoInWords(a._dateTime);
break;
case ActivityItemDelegate::AccountConnectedRole: case ActivityItemDelegate::AccountConnectedRole:
return (ast && ast->isConnected()); return (ast && ast->isConnected());
break;
default: default:
return QVariant(); return QVariant();
} }
@@ -230,29 +228,6 @@ void ActivityListModel::addErrorToActivityList(Activity activity) {
combineActivityLists(); combineActivityLists();
} }
void ActivityListModel::addIgnoredFileToList(Activity newActivity) {
qCInfo(lcActivity) << "First checking for duplicates then add file to the notification list of ignored files: " << newActivity._file;
bool duplicate = false;
if(_listOfIgnoredFiles.size() == 0){
_notificationIgnoredFiles = newActivity;
_notificationIgnoredFiles._subject = tr("Files from the ignore list as well as symbolic links are not synced. This includes:");
_listOfIgnoredFiles.append(newActivity);
return;
}
foreach(Activity activity, _listOfIgnoredFiles){
if(activity._file == newActivity._file){
duplicate = true;
break;
}
}
if(!duplicate){
_notificationIgnoredFiles._message.append(", " + newActivity._file);
}
}
void ActivityListModel::addNotificationToActivityList(Activity activity) { void ActivityListModel::addNotificationToActivityList(Activity activity) {
qCInfo(lcActivity) << "Notification successfully added to the notification list: " << activity._subject; qCInfo(lcActivity) << "Notification successfully added to the notification list: " << activity._subject;
_notificationLists.prepend(activity); _notificationLists.prepend(activity);
@@ -266,7 +241,7 @@ void ActivityListModel::clearNotifications() {
} }
void ActivityListModel::removeActivityFromActivityList(int row) { void ActivityListModel::removeActivityFromActivityList(int row) {
Activity activity = _finalList.at(row); Activity activity = _finalList.at(row);
removeActivityFromActivityList(activity); removeActivityFromActivityList(activity);
combineActivityLists(); combineActivityLists();
} }
@@ -300,31 +275,22 @@ void ActivityListModel::removeActivityFromActivityList(Activity activity) {
} }
} }
void ActivityListModel::combineActivityLists() void ActivityListModel::combineActivityLists()
{ {
ActivityList resultList; ActivityList resultList;
if(_notificationErrorsLists.count() > 0) { std::sort(_notificationErrorsLists.begin(), _notificationErrorsLists.end());
std::sort(_notificationErrorsLists.begin(), _notificationErrorsLists.end()); resultList.append(_notificationErrorsLists);
resultList.append(_notificationErrorsLists);
}
if(_listOfIgnoredFiles.size() > 0)
resultList.append(_notificationIgnoredFiles);
if(_notificationLists.count() > 0) { std::sort(_notificationLists.begin(), _notificationLists.end());
std::sort(_notificationLists.begin(), _notificationLists.end()); resultList.append(_notificationLists);
resultList.append(_notificationLists);
}
if(_syncFileItemLists.count() > 0) { std::sort(_syncFileItemLists.begin(), _syncFileItemLists.end());
std::sort(_syncFileItemLists.begin(), _syncFileItemLists.end()); resultList.append(_syncFileItemLists);
resultList.append(_syncFileItemLists);
}
if(_activityLists.count() > 0) { std::sort(_activityLists.begin(), _activityLists.end());
std::sort(_activityLists.begin(), _activityLists.end()); resultList.append(_activityLists);
resultList.append(_activityLists);
}
beginResetModel(); beginResetModel();
_finalList.clear(); _finalList.clear();

View File

@@ -38,20 +38,6 @@ class ActivityListModel : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
public: public:
enum ActivityIconType {
iconUseCached = 0,
iconActivity,
iconBell,
iconStateError,
iconStateWarning,
iconStateInfo,
iconStateSync
};
struct ActionIcon {
ActivityIconType iconType;
QIcon cachedIcon;
};
explicit ActivityListModel(AccountState *accountState, QWidget *parent = nullptr); explicit ActivityListModel(AccountState *accountState, QWidget *parent = nullptr);
QVariant data(const QModelIndex &index, int role) const override; QVariant data(const QModelIndex &index, int role) const override;
@@ -65,7 +51,6 @@ public:
void addNotificationToActivityList(Activity activity); void addNotificationToActivityList(Activity activity);
void clearNotifications(); void clearNotifications();
void addErrorToActivityList(Activity activity); void addErrorToActivityList(Activity activity);
void addIgnoredFileToList(Activity newActivity);
void addSyncFileItemToActivityList(Activity activity); void addSyncFileItemToActivityList(Activity activity);
void removeActivityFromActivityList(int row); void removeActivityFromActivityList(int row);
void removeActivityFromActivityList(Activity activity); void removeActivityFromActivityList(Activity activity);
@@ -88,8 +73,6 @@ private:
ActivityList _activityLists; ActivityList _activityLists;
ActivityList _syncFileItemLists; ActivityList _syncFileItemLists;
ActivityList _notificationLists; ActivityList _notificationLists;
ActivityList _listOfIgnoredFiles;
Activity _notificationIgnoredFiles;
ActivityList _notificationErrorsLists; ActivityList _notificationErrorsLists;
ActivityList _finalList; ActivityList _finalList;
AccountState *_accountState; AccountState *_accountState;
@@ -98,7 +81,4 @@ private:
int _currentItem = 0; int _currentItem = 0;
}; };
} }
Q_DECLARE_METATYPE(OCC::ActivityListModel::ActionIcon)
#endif // ACTIVITYLISTMODEL_H #endif // ACTIVITYLISTMODEL_H

View File

@@ -89,9 +89,6 @@ ActivityWidget::ActivityWidget(AccountState *accountState, QWidget *parent)
this, &ActivityWidget::addError); this, &ActivityWidget::addError);
_removeTimer.setInterval(1000); _removeTimer.setInterval(1000);
// Connect styleChanged events to our widgets, so they can adapt (Dark-/Light-Mode switching)
connect(this, &ActivityWidget::styleChanged, delegate, &ActivityItemDelegate::slotStyleChanged);
} }
ActivityWidget::~ActivityWidget() ActivityWidget::~ActivityWidget()
@@ -130,12 +127,11 @@ void ActivityWidget::slotProgressInfo(const QString &folder, const ProgressInfo
} }
if(activity._status == SyncFileItem::FileIgnored && !QFileInfo(f->path() + activity._file).exists()) { if(activity._status == SyncFileItem::FileIgnored && !QFileInfo(f->path() + activity._file).exists()){
_model->removeActivityFromActivityList(activity); _model->removeActivityFromActivityList(activity);
continue; continue;
} }
if(!QFileInfo(f->path() + activity._file).exists()){ if(!QFileInfo(f->path() + activity._file).exists()){
_model->removeActivityFromActivityList(activity); _model->removeActivityFromActivityList(activity);
continue; continue;
@@ -179,7 +175,7 @@ void ActivityWidget::slotItemCompleted(const QString &folder, const SyncFileItem
Activity activity; Activity activity;
activity._type = Activity::SyncFileItemType; //client activity activity._type = Activity::SyncFileItemType; //client activity
activity._status = item->_status; activity._status = item->_status;
activity._dateTime = QDateTime::currentDateTime(); activity._dateTime = QDateTime::fromString(QDateTime::currentDateTime().toString(), Qt::ISODate);
activity._message = item->_originalFile; activity._message = item->_originalFile;
activity._link = folderInstance->accountState()->account()->url(); activity._link = folderInstance->accountState()->account()->url();
activity._accName = folderInstance->accountState()->account()->displayName(); activity._accName = folderInstance->accountState()->account()->displayName();
@@ -195,12 +191,8 @@ void ActivityWidget::slotItemCompleted(const QString &folder, const SyncFileItem
qCWarning(lcActivity) << "Item " << item->_file << " retrieved resulted in error " << item->_errorString; qCWarning(lcActivity) << "Item " << item->_file << " retrieved resulted in error " << item->_errorString;
activity._subject = item->_errorString; activity._subject = item->_errorString;
if(item->_status == SyncFileItem::Status::FileIgnored) { // add 'protocol error' to activity list
_model->addIgnoredFileToList(activity); _model->addErrorToActivityList(activity);
} else {
// add 'protocol error' to activity list
_model->addErrorToActivityList(activity);
}
} }
} }
} }
@@ -551,12 +543,6 @@ void ActivityWidget::slotNotifyServerFinished(const QString &reply, int replyCod
qCInfo(lcActivity) << "Server Notification reply code" << replyCode << reply; qCInfo(lcActivity) << "Server Notification reply code" << replyCode << reply;
} }
void ActivityWidget::slotStyleChanged()
{
// Notify the other widgets (Dark-/Light-Mode switching)
emit styleChanged();
}
/* ==================================================================== */ /* ==================================================================== */
ActivitySettings::ActivitySettings(AccountState *accountState, QWidget *parent) ActivitySettings::ActivitySettings(AccountState *accountState, QWidget *parent)
@@ -579,9 +565,6 @@ ActivitySettings::ActivitySettings(AccountState *accountState, QWidget *parent)
// connect a model signal to stop the animation // connect a model signal to stop the animation
connect(_activityWidget, &ActivityWidget::rowsInserted, _progressIndicator, &QProgressIndicator::stopAnimation); connect(_activityWidget, &ActivityWidget::rowsInserted, _progressIndicator, &QProgressIndicator::stopAnimation);
connect(_activityWidget, &ActivityWidget::rowsInserted, this, &ActivitySettings::slotDisplayActivities); connect(_activityWidget, &ActivityWidget::rowsInserted, this, &ActivitySettings::slotDisplayActivities);
// Connect styleChanged events to our widgets, so they can adapt (Dark-/Light-Mode switching)
connect(this, &ActivitySettings::styleChanged, _activityWidget, &ActivityWidget::slotStyleChanged);
} }
void ActivitySettings::slotDisplayActivities(){ void ActivitySettings::slotDisplayActivities(){
@@ -640,14 +623,4 @@ bool ActivitySettings::event(QEvent *e)
ActivitySettings::~ActivitySettings() ActivitySettings::~ActivitySettings()
{ {
} }
void ActivitySettings::slotStyleChanged()
{
if(_progressIndicator)
_progressIndicator->setColor(QGuiApplication::palette().color(QPalette::Text));
// Notify the other widgets (Dark-/Light-Mode switching)
emit styleChanged();
}
} }

View File

@@ -78,14 +78,12 @@ public slots:
void addError(const QString &folderAlias, const QString &message, ErrorCategory category); void addError(const QString &folderAlias, const QString &message, ErrorCategory category);
void slotProgressInfo(const QString &folder, const ProgressInfo &progress); void slotProgressInfo(const QString &folder, const ProgressInfo &progress);
void slotItemCompleted(const QString &folder, const SyncFileItemPtr &item); void slotItemCompleted(const QString &folder, const SyncFileItemPtr &item);
void slotStyleChanged();
signals: signals:
void guiLog(const QString &, const QString &); void guiLog(const QString &, const QString &);
void rowsInserted(); void rowsInserted();
void hideActivityTab(bool); void hideActivityTab(bool);
void sendNotificationRequest(const QString &accountName, const QString &link, const QByteArray &verb, int row); void sendNotificationRequest(const QString &accountName, const QString &link, const QByteArray &verb, int row);
void styleChanged();
private slots: private slots:
void slotBuildNotificationDisplay(const ActivityList &list); void slotBuildNotificationDisplay(const ActivityList &list);
@@ -98,7 +96,6 @@ private slots:
void slotSecondaryButtonClickedOnListView(const QModelIndex &index); void slotSecondaryButtonClickedOnListView(const QModelIndex &index);
private: private:
void customizeStyle();
void showLabels(); void showLabels();
QString timeString(QDateTime dt, QLocale::FormatType format) const; QString timeString(QDateTime dt, QLocale::FormatType format) const;
Ui::ActivityWidget *_ui; Ui::ActivityWidget *_ui;
@@ -140,7 +137,6 @@ public slots:
void slotRefresh(); void slotRefresh();
void slotRemoveAccount(); void slotRemoveAccount();
void setNotificationRefreshInterval(std::chrono::milliseconds interval); void setNotificationRefreshInterval(std::chrono::milliseconds interval);
void slotStyleChanged();
private slots: private slots:
void slotRegularNotificationCheck(); void slotRegularNotificationCheck();
@@ -148,7 +144,6 @@ private slots:
signals: signals:
void guiLog(const QString &, const QString &); void guiLog(const QString &, const QString &);
void styleChanged();
private: private:
bool event(QEvent *e) override; bool event(QEvent *e) override;

View File

@@ -11,7 +11,7 @@
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string notr="true">Form</string> <string>Form</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint"> <property name="sizeConstraint">
@@ -81,7 +81,7 @@
</sizepolicy> </sizepolicy>
</property> </property>
<property name="text"> <property name="text">
<string notr="true">TextLabel</string> <string>TextLabel</string>
</property> </property>
<property name="textFormat"> <property name="textFormat">
<enum>Qt::RichText</enum> <enum>Qt::RichText</enum>

View File

@@ -108,7 +108,6 @@ Application::Application(int &argc, char **argv)
, _userTriggeredConnect(false) , _userTriggeredConnect(false)
, _debugMode(false) , _debugMode(false)
, _backgroundMode(false) , _backgroundMode(false)
, _isQuitting(false)
{ {
_startedAt.start(); _startedAt.start();
@@ -254,9 +253,6 @@ Application::Application(int &argc, char **argv)
// Cleanup at Quit. // Cleanup at Quit.
connect(this, &QCoreApplication::aboutToQuit, this, &Application::slotCleanup); connect(this, &QCoreApplication::aboutToQuit, this, &Application::slotCleanup);
// Allow other classes to hook into isShowingSettingsDialog() signals (re-auth widgets, for example)
connect(_gui.data(), &ownCloudGui::isShowingSettingsDialog, this, &Application::slotGuiIsShowingSettings);
} }
Application::~Application() Application::~Application()
@@ -268,8 +264,6 @@ Application::~Application()
} }
// Remove the account from the account manager so it can be deleted. // Remove the account from the account manager so it can be deleted.
disconnect(AccountManager::instance(), &AccountManager::accountRemoved,
this, &Application::slotAccountStateRemoved);
AccountManager::instance()->shutdown(); AccountManager::instance()->shutdown();
} }
@@ -289,7 +283,7 @@ void Application::slotAccountStateRemoved(AccountState *accountState)
} }
// if there is no more account, show the wizard. // if there is no more account, show the wizard.
if (!_isQuitting && AccountManager::instance()->accounts().isEmpty()) { if (AccountManager::instance()->accounts().isEmpty()) {
// allow to add a new account if there is non any more. Always think // allow to add a new account if there is non any more. Always think
// about single account theming! // about single account theming!
OwncloudSetupWizard::runWizard(this, SLOT(slotownCloudWizardDone(int))); OwncloudSetupWizard::runWizard(this, SLOT(slotownCloudWizardDone(int)));
@@ -312,8 +306,6 @@ void Application::slotAccountStateAdded(AccountState *accountState)
void Application::slotCleanup() void Application::slotCleanup()
{ {
_isQuitting = true;
AccountManager::instance()->save(); AccountManager::instance()->save();
FolderMan::instance()->unloadAndDeleteAllFolders(); FolderMan::instance()->unloadAndDeleteAllFolders();
@@ -651,9 +643,5 @@ void Application::showSettingsDialog()
_gui->slotShowSettings(); _gui->slotShowSettings();
} }
void Application::slotGuiIsShowingSettings()
{
emit isShowingSettingsDialog();
}
} // namespace OCC } // namespace OCC

View File

@@ -82,7 +82,6 @@ protected:
signals: signals:
void folderRemoved(); void folderRemoved();
void folderStateChanged(Folder *); void folderStateChanged(Folder *);
void isShowingSettingsDialog();
protected slots: protected slots:
void slotParseMessage(const QString &, QObject *); void slotParseMessage(const QString &, QObject *);
@@ -92,7 +91,6 @@ protected slots:
void slotAccountStateAdded(AccountState *accountState); void slotAccountStateAdded(AccountState *accountState);
void slotAccountStateRemoved(AccountState *accountState); void slotAccountStateRemoved(AccountState *accountState);
void slotSystemOnlineConfigurationChanged(QNetworkConfiguration); void slotSystemOnlineConfigurationChanged(QNetworkConfiguration);
void slotGuiIsShowingSettings();
private: private:
void setHelp(); void setHelp();
@@ -116,7 +114,6 @@ private:
bool _userTriggeredConnect; bool _userTriggeredConnect;
bool _debugMode; bool _debugMode;
bool _backgroundMode; bool _backgroundMode;
bool _isQuitting;
ClientProxy _proxy; ClientProxy _proxy;

View File

@@ -1,271 +0,0 @@
/*
* Copyright (C) by Olivier Goffart <ogoffart@woboq.com>
* Copyright (C) by Michael Schuster <michael@nextcloud.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 <QDesktopServices>
#include <QApplication>
#include <QClipboard>
#include <QTimer>
#include <QBuffer>
#include "account.h"
#include "flow2auth.h"
#include <QJsonObject>
#include <QJsonDocument>
#include "theme.h"
#include "networkjobs.h"
#include "configfile.h"
namespace OCC {
Q_LOGGING_CATEGORY(lcFlow2auth, "nextcloud.sync.credentials.flow2auth", QtInfoMsg)
Flow2Auth::Flow2Auth(Account *account, QObject *parent)
: QObject(parent)
, _account(account)
, _isBusy(false)
, _hasToken(false)
{
_pollTimer.setInterval(1000);
QObject::connect(&_pollTimer, &QTimer::timeout, this, &Flow2Auth::slotPollTimerTimeout);
}
Flow2Auth::~Flow2Auth()
{
}
void Flow2Auth::start()
{
// Note: All startup code is in openBrowser() to allow reinitiate a new request with
// fresh tokens. Opening the same pollEndpoint link twice triggers an expiration
// message by the server (security, intended design).
openBrowser();
}
QUrl Flow2Auth::authorisationLink() const
{
return _loginUrl;
}
void Flow2Auth::openBrowser()
{
fetchNewToken(TokenAction::actionOpenBrowser);
}
void Flow2Auth::copyLinkToClipboard()
{
fetchNewToken(TokenAction::actionCopyLinkToClipboard);
}
void Flow2Auth::fetchNewToken(const TokenAction action)
{
if(_isBusy)
return;
_isBusy = true;
_hasToken = false;
emit statusChanged(PollStatus::statusFetchToken, 0);
// Step 1: Initiate a login, do an anonymous POST request
QUrl url = Utility::concatUrlPath(_account->url().toString(), QLatin1String("/index.php/login/v2"));
// add 'Content-Length: 0' header (see https://github.com/nextcloud/desktop/issues/1473)
QNetworkRequest req;
req.setHeader(QNetworkRequest::ContentLengthHeader, "0");
auto job = _account->sendRequest("POST", url, req);
job->setTimeout(qMin(30 * 1000ll, job->timeoutMsec()));
QObject::connect(job, &SimpleNetworkJob::finishedSignal, this, [this, action](QNetworkReply *reply) {
auto jsonData = reply->readAll();
QJsonParseError jsonParseError;
QJsonObject json = QJsonDocument::fromJson(jsonData, &jsonParseError).object();
QString pollToken, pollEndpoint, loginUrl;
if (reply->error() == QNetworkReply::NoError && jsonParseError.error == QJsonParseError::NoError
&& !json.isEmpty()) {
pollToken = json.value("poll").toObject().value("token").toString();
pollEndpoint = json.value("poll").toObject().value("endpoint").toString();
loginUrl = json["login"].toString();
}
if (reply->error() != QNetworkReply::NoError || jsonParseError.error != QJsonParseError::NoError
|| json.isEmpty() || pollToken.isEmpty() || pollEndpoint.isEmpty() || loginUrl.isEmpty()) {
QString errorReason;
QString errorFromJson = json["error"].toString();
if (!errorFromJson.isEmpty()) {
errorReason = tr("Error returned from the server: <em>%1</em>")
.arg(errorFromJson.toHtmlEscaped());
} else if (reply->error() != QNetworkReply::NoError) {
errorReason = tr("There was an error accessing the 'token' endpoint: <br><em>%1</em>")
.arg(reply->errorString().toHtmlEscaped());
} else if (jsonParseError.error != QJsonParseError::NoError) {
errorReason = tr("Could not parse the JSON returned from the server: <br><em>%1</em>")
.arg(jsonParseError.errorString());
} else {
errorReason = tr("The reply from the server did not contain all expected fields");
}
qCWarning(lcFlow2auth) << "Error when getting the loginUrl" << json << errorReason;
emit result(Error, errorReason);
_pollTimer.stop();
_isBusy = false;
return;
}
_loginUrl = loginUrl;
_pollToken = pollToken;
_pollEndpoint = pollEndpoint;
// Start polling
ConfigFile cfg;
std::chrono::milliseconds polltime = cfg.remotePollInterval();
qCInfo(lcFlow2auth) << "setting remote poll timer interval to" << polltime.count() << "msec";
_secondsInterval = (polltime.count() / 1000);
_secondsLeft = _secondsInterval;
emit statusChanged(PollStatus::statusPollCountdown, _secondsLeft);
if(!_pollTimer.isActive()) {
_pollTimer.start();
}
switch(action)
{
case actionOpenBrowser:
// Try to open Browser
if (!QDesktopServices::openUrl(authorisationLink())) {
// We cannot open the browser, then we claim we don't support Flow2Auth.
// Our UI callee will ask the user to copy and open the link.
emit result(NotSupported);
}
break;
case actionCopyLinkToClipboard:
QApplication::clipboard()->setText(authorisationLink().toString(QUrl::FullyEncoded));
emit statusChanged(PollStatus::statusCopyLinkToClipboard, 0);
break;
}
_isBusy = false;
_hasToken = true;
});
}
void Flow2Auth::slotPollTimerTimeout()
{
if(_isBusy || !_hasToken)
return;
_isBusy = true;
_secondsLeft--;
if(_secondsLeft > 0) {
emit statusChanged(PollStatus::statusPollCountdown, _secondsLeft);
_isBusy = false;
return;
}
emit statusChanged(PollStatus::statusPollNow, 0);
// Step 2: Poll
QNetworkRequest req;
req.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
auto requestBody = new QBuffer;
QUrlQuery arguments(QString("token=%1").arg(_pollToken));
requestBody->setData(arguments.query(QUrl::FullyEncoded).toLatin1());
auto job = _account->sendRequest("POST", _pollEndpoint, req, requestBody);
job->setTimeout(qMin(30 * 1000ll, job->timeoutMsec()));
QObject::connect(job, &SimpleNetworkJob::finishedSignal, this, [this](QNetworkReply *reply) {
auto jsonData = reply->readAll();
QJsonParseError jsonParseError;
QJsonObject json = QJsonDocument::fromJson(jsonData, &jsonParseError).object();
QUrl serverUrl;
QString loginName, appPassword;
if (reply->error() == QNetworkReply::NoError && jsonParseError.error == QJsonParseError::NoError
&& !json.isEmpty()) {
serverUrl = json["server"].toString();
loginName = json["loginName"].toString();
appPassword = json["appPassword"].toString();
}
if (reply->error() != QNetworkReply::NoError || jsonParseError.error != QJsonParseError::NoError
|| json.isEmpty() || serverUrl.isEmpty() || loginName.isEmpty() || appPassword.isEmpty()) {
QString errorReason;
QString errorFromJson = json["error"].toString();
if (!errorFromJson.isEmpty()) {
errorReason = tr("Error returned from the server: <em>%1</em>")
.arg(errorFromJson.toHtmlEscaped());
} else if (reply->error() != QNetworkReply::NoError) {
errorReason = tr("There was an error accessing the 'token' endpoint: <br><em>%1</em>")
.arg(reply->errorString().toHtmlEscaped());
} else if (jsonParseError.error != QJsonParseError::NoError) {
errorReason = tr("Could not parse the JSON returned from the server: <br><em>%1</em>")
.arg(jsonParseError.errorString());
} else {
errorReason = tr("The reply from the server did not contain all expected fields");
}
qCDebug(lcFlow2auth) << "Error when polling for the appPassword" << json << errorReason;
// We get a 404 until authentication is done, so don't show this error in the GUI.
if(reply->error() != QNetworkReply::ContentNotFoundError)
emit result(Error, errorReason);
// Forget sensitive data
appPassword.clear();
loginName.clear();
// Failed: poll again
_secondsLeft = _secondsInterval;
_isBusy = false;
return;
}
_pollTimer.stop();
// Success
qCInfo(lcFlow2auth) << "Success getting the appPassword for user: " << loginName << ", server: " << serverUrl.toString();
_account->setUrl(serverUrl);
emit result(LoggedIn, QString(), loginName, appPassword);
// Forget sensitive data
appPassword.clear();
loginName.clear();
_loginUrl.clear();
_pollToken.clear();
_pollEndpoint.clear();
_isBusy = false;
_hasToken = false;
});
}
void Flow2Auth::slotPollNow()
{
// poll now if we're not already doing so
if(_isBusy || !_hasToken)
return;
_secondsLeft = 1;
slotPollTimerTimeout();
}
} // namespace OCC

View File

@@ -1,86 +0,0 @@
/*
* Copyright (C) by Olivier Goffart <ogoffart@woboq.com>
* Copyright (C) by Michael Schuster <michael@nextcloud.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.
*/
#pragma once
#include <QPointer>
#include <QUrl>
#include <QTimer>
#include "accountfwd.h"
namespace OCC {
/**
* Job that does the authorization, grants and fetches the access token via Login Flow v2
*
* See: https://docs.nextcloud.com/server/latest/developer_manual/client_apis/LoginFlow/index.html#login-flow-v2
*/
class Flow2Auth : public QObject
{
Q_OBJECT
public:
enum TokenAction {
actionOpenBrowser = 1,
actionCopyLinkToClipboard
};
enum PollStatus {
statusPollCountdown = 1,
statusPollNow,
statusFetchToken,
statusCopyLinkToClipboard
};
Flow2Auth(Account *account, QObject *parent);
~Flow2Auth();
enum Result { NotSupported,
LoggedIn,
Error };
Q_ENUM(Result);
void start();
void openBrowser();
void copyLinkToClipboard();
QUrl authorisationLink() const;
signals:
/**
* The state has changed.
* when logged in, appPassword has the value of the app password.
*/
void result(Flow2Auth::Result result, const QString &errorString = QString(),
const QString &user = QString(), const QString &appPassword = QString());
void statusChanged(const PollStatus status, int secondsLeft);
public slots:
void slotPollNow();
private slots:
void slotPollTimerTimeout();
private:
void fetchNewToken(const TokenAction action);
Account *_account;
QUrl _loginUrl;
QString _pollToken;
QString _pollEndpoint;
QTimer _pollTimer;
int _secondsLeft;
int _secondsInterval;
bool _isBusy;
bool _hasToken;
};
} // namespace OCC

View File

@@ -1,221 +0,0 @@
/*
* Copyright (C) by Michael Schuster <michael@nextcloud.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 "account.h"
#include "keychainchunk.h"
#include "theme.h"
#include "networkjobs.h"
#include "configfile.h"
#include "creds/abstractcredentials.h"
using namespace QKeychain;
namespace OCC {
Q_LOGGING_CATEGORY(lcKeychainChunk, "nextcloud.sync.credentials.keychainchunk", QtInfoMsg)
namespace KeychainChunk {
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
static void addSettingsToJob(Account *account, QKeychain::Job *job)
{
Q_UNUSED(account)
auto settings = ConfigFile::settingsWithGroup(Theme::instance()->appName());
settings->setParent(job); // make the job parent to make setting deleted properly
job->setSettings(settings.release());
}
#endif
/*
* Job
*/
Job::Job(QObject *parent)
: QObject(parent)
{
_serviceName = Theme::instance()->appName();
}
/*
* WriteJob
*/
WriteJob::WriteJob(Account *account, const QString &key, const QByteArray &data, QObject *parent)
: Job(parent)
{
_account = account;
_key = key;
// Windows workaround: Split the private key into chunks of 2048 bytes,
// to allow 4k (4096 bit) keys to be saved (obey Windows's limits)
_chunkBuffer = data;
_chunkCount = 0;
}
void WriteJob::start()
{
slotWriteJobDone(nullptr);
}
void WriteJob::slotWriteJobDone(QKeychain::Job *incomingJob)
{
QKeychain::WritePasswordJob *writeJob = static_cast<QKeychain::WritePasswordJob *>(incomingJob);
// errors?
if (writeJob) {
_error = writeJob->error();
_errorString = writeJob->errorString();
if (writeJob->error() != NoError) {
qCWarning(lcKeychainChunk) << "Error while writing" << writeJob->key() << "chunk" << writeJob->errorString();
_chunkBuffer.clear();
}
}
// write a chunk if there is any in the buffer
if (!_chunkBuffer.isEmpty()) {
#if defined(Q_OS_WIN)
// Windows workaround: Split the data into chunks of 2048 bytes,
// to allow 4k (4096 bit) keys to be saved (obey Windows's limits)
auto chunk = _chunkBuffer.left(KeychainChunk::ChunkSize);
_chunkBuffer = _chunkBuffer.right(_chunkBuffer.size() - chunk.size());
#else
// write full data in one chunk on non-Windows, as usual
auto chunk = _chunkBuffer;
_chunkBuffer.clear();
#endif
auto index = (_chunkCount++);
// keep the limit
if (_chunkCount > KeychainChunk::MaxChunks) {
qCWarning(lcKeychainChunk) << "Maximum chunk count exceeded while writing" << writeJob->key() << "chunk" << QString::number(index) << "cutting off after" << QString::number(KeychainChunk::MaxChunks) << "chunks";
writeJob->deleteLater();
_chunkBuffer.clear();
emit finished(this);
return;
}
const QString kck = AbstractCredentials::keychainKey(
_account->url().toString(),
_key + (index > 0 ? (QString(".") + QString::number(index)) : QString()),
_account->id());
QKeychain::WritePasswordJob *job = new QKeychain::WritePasswordJob(_serviceName);
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
addSettingsToJob(_account, job);
#endif
job->setInsecureFallback(_insecureFallback);
connect(job, &QKeychain::Job::finished, this, &KeychainChunk::WriteJob::slotWriteJobDone);
// only add the key's (sub)"index" after the first element, to stay compatible with older versions and non-Windows
job->setKey(kck);
job->setBinaryData(chunk);
job->start();
chunk.clear();
} else {
emit finished(this);
}
writeJob->deleteLater();
}
/*
* ReadJob
*/
ReadJob::ReadJob(Account *account, const QString &key, const bool &keychainMigration, QObject *parent)
: Job(parent)
{
_account = account;
_key = key;
_keychainMigration = keychainMigration;
_chunkCount = 0;
_chunkBuffer.clear();
}
void ReadJob::start()
{
_chunkCount = 0;
_chunkBuffer.clear();
const QString kck = AbstractCredentials::keychainKey(
_account->url().toString(),
_key,
_keychainMigration ? QString() : _account->id());
QKeychain::ReadPasswordJob *job = new QKeychain::ReadPasswordJob(_serviceName);
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
addSettingsToJob(_account, job);
#endif
job->setInsecureFallback(_insecureFallback);
job->setKey(kck);
connect(job, &QKeychain::Job::finished, this, &KeychainChunk::ReadJob::slotReadJobDone);
job->start();
}
void ReadJob::slotReadJobDone(QKeychain::Job *incomingJob)
{
// Errors or next chunk?
QKeychain::ReadPasswordJob *readJob = static_cast<QKeychain::ReadPasswordJob *>(incomingJob);
if (readJob) {
if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
_chunkBuffer.append(readJob->binaryData());
_chunkCount++;
#if defined(Q_OS_WIN)
// try to fetch next chunk
if (_chunkCount < KeychainChunk::MaxChunks) {
const QString kck = AbstractCredentials::keychainKey(
_account->url().toString(),
_key + QString(".") + QString::number(_chunkCount),
_keychainMigration ? QString() : _account->id());
QKeychain::ReadPasswordJob *job = new QKeychain::ReadPasswordJob(_serviceName);
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
addSettingsToJob(_account, job);
#endif
job->setInsecureFallback(_insecureFallback);
job->setKey(kck);
connect(job, &QKeychain::Job::finished, this, &KeychainChunk::ReadJob::slotReadJobDone);
job->start();
readJob->deleteLater();
return;
} else {
qCWarning(lcKeychainChunk) << "Maximum chunk count for" << readJob->key() << "reached, ignoring after" << KeychainChunk::MaxChunks;
}
#endif
} else {
if (readJob->error() != QKeychain::Error::EntryNotFound ||
((readJob->error() == QKeychain::Error::EntryNotFound) && _chunkCount == 0)) {
_error = readJob->error();
_errorString = readJob->errorString();
qCWarning(lcKeychainChunk) << "Unable to read" << readJob->key() << "chunk" << QString::number(_chunkCount) << readJob->errorString();
}
}
readJob->deleteLater();
}
emit finished(this);
}
} // namespace KeychainChunk
} // namespace OCC

View File

@@ -1,120 +0,0 @@
/*
* Copyright (C) by Michael Schuster <michael@nextcloud.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.
*/
#pragma once
#ifndef KEYCHAINCHUNK_H
#define KEYCHAINCHUNK_H
#include <QObject>
#include <keychain.h>
#include "accountfwd.h"
// We don't support insecure fallback
// #define KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK
namespace OCC {
namespace KeychainChunk {
/*
* Workaround for Windows:
*
* Split the keychain entry's data into chunks of 2048 bytes,
* to allow 4k (4096 bit) keys / large certs to be saved (see limits in webflowcredentials.h)
*/
static constexpr int ChunkSize = 2048;
static constexpr int MaxChunks = 10;
/*
* @brief: Abstract base class for KeychainChunk jobs.
*/
class Job : public QObject {
Q_OBJECT
public:
Job(QObject *parent = nullptr);
const QKeychain::Error error() const {
return _error;
}
const QString errorString() const {
return _errorString;
}
QByteArray binaryData() const {
return _chunkBuffer;
}
const bool insecureFallback() const {
return _insecureFallback;
}
// If we use it but don't support insecure fallback, give us nice compilation errors ;p
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
void setInsecureFallback(const bool &insecureFallback)
{
_insecureFallback = insecureFallback;
}
#endif
protected:
QString _serviceName;
Account *_account;
QString _key;
bool _insecureFallback = false;
bool _keychainMigration = false;
QKeychain::Error _error = QKeychain::NoError;
QString _errorString;
int _chunkCount = 0;
QByteArray _chunkBuffer;
}; // class Job
/*
* @brief: Simple wrapper class for QKeychain::WritePasswordJob, splits too large keychain entry's data into chunks on Windows
*/
class WriteJob : public KeychainChunk::Job {
Q_OBJECT
public:
WriteJob(Account *account, const QString &key, const QByteArray &data, QObject *parent = nullptr);
void start();
signals:
void finished(KeychainChunk::WriteJob *incomingJob);
private slots:
void slotWriteJobDone(QKeychain::Job *incomingJob);
}; // class WriteJob
/*
* @brief: Simple wrapper class for QKeychain::ReadPasswordJob, splits too large keychain entry's data into chunks on Windows
*/
class ReadJob : public KeychainChunk::Job {
Q_OBJECT
public:
ReadJob(Account *account, const QString &key, const bool &keychainMigration, QObject *parent = nullptr);
void start();
signals:
void finished(KeychainChunk::ReadJob *incomingJob);
private slots:
void slotReadJobDone(QKeychain::Job *incomingJob);
}; // class ReadJob
} // namespace KeychainChunk
} // namespace OCC
#endif // KEYCHAINCHUNK_H

View File

@@ -14,11 +14,9 @@
#include "accessmanager.h" #include "accessmanager.h"
#include "account.h" #include "account.h"
#include "configfile.h"
#include "theme.h" #include "theme.h"
#include "wizard/webview.h" #include "wizard/webview.h"
#include "webflowcredentialsdialog.h" #include "webflowcredentialsdialog.h"
#include "keychainchunk.h"
using namespace QKeychain; using namespace QKeychain;
@@ -26,13 +24,6 @@ namespace OCC {
Q_LOGGING_CATEGORY(lcWebFlowCredentials, "sync.credentials.webflow", QtInfoMsg) Q_LOGGING_CATEGORY(lcWebFlowCredentials, "sync.credentials.webflow", QtInfoMsg)
namespace {
const char userC[] = "user";
const char clientCertificatePEMC[] = "_clientCertificatePEM";
const char clientKeyPEMC[] = "_clientKeyPEM";
const char clientCaCertificatePEMC[] = "_clientCaCertificatePEM";
} // ns
class WebFlowCredentialsAccessManager : public AccessManager class WebFlowCredentialsAccessManager : public AccessManager
{ {
public: public:
@@ -46,27 +37,13 @@ protected:
QNetworkReply *createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData) override QNetworkReply *createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData) override
{ {
QNetworkRequest req(request); QNetworkRequest req(request);
if (!req.attribute(WebFlowCredentials::DontAddCredentialsAttribute).toBool()) { if (!req.attribute(HttpCredentials::DontAddCredentialsAttribute).toBool()) {
if (_cred && !_cred->password().isEmpty()) { if (_cred && !_cred->password().isEmpty()) {
QByteArray credHash = QByteArray(_cred->user().toUtf8() + ":" + _cred->password().toUtf8()).toBase64(); QByteArray credHash = QByteArray(_cred->user().toUtf8() + ":" + _cred->password().toUtf8()).toBase64();
req.setRawHeader("Authorization", "Basic " + credHash); req.setRawHeader("Authorization", "Basic " + credHash);
} }
} }
if (_cred && !_cred->_clientSslKey.isNull() && !_cred->_clientSslCertificate.isNull()) {
// SSL configuration
QSslConfiguration sslConfiguration = req.sslConfiguration();
sslConfiguration.setLocalCertificate(_cred->_clientSslCertificate);
sslConfiguration.setPrivateKey(_cred->_clientSslKey);
// Merge client side CA with system CA
auto ca = sslConfiguration.systemCaCertificates();
ca.append(_cred->_clientSslCaCertificates);
sslConfiguration.setCaCertificates(ca);
req.setSslConfiguration(sslConfiguration);
}
return AccessManager::createRequest(op, req, outgoingData); return AccessManager::createRequest(op, req, outgoingData);
} }
@@ -76,35 +53,22 @@ private:
QPointer<const WebFlowCredentials> _cred; QPointer<const WebFlowCredentials> _cred;
}; };
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
static void addSettingsToJob(Account *account, QKeychain::Job *job)
{
Q_UNUSED(account)
auto settings = ConfigFile::settingsWithGroup(Theme::instance()->appName());
settings->setParent(job); // make the job parent to make setting deleted properly
job->setSettings(settings.release());
}
#endif
WebFlowCredentials::WebFlowCredentials() WebFlowCredentials::WebFlowCredentials()
: _ready(false) : _ready(false)
, _credentialsValid(false) , _credentialsValid(false)
, _keychainMigration(false) , _keychainMigration(false)
, _retryOnKeyChainError(false)
{ {
} }
WebFlowCredentials::WebFlowCredentials(const QString &user, const QString &password, const QSslCertificate &certificate, const QSslKey &key, const QList<QSslCertificate> &caCertificates) WebFlowCredentials::WebFlowCredentials(const QString &user, const QString &password, const QSslCertificate &certificate, const QSslKey &key)
: _user(user) : _user(user)
, _password(password) , _password(password)
, _clientSslKey(key) , _clientSslKey(key)
, _clientSslCertificate(certificate) , _clientSslCertificate(certificate)
, _clientSslCaCertificates(caCertificates)
, _ready(true) , _ready(true)
, _credentialsValid(true) , _credentialsValid(true)
, _keychainMigration(false) , _keychainMigration(false)
, _retryOnKeyChainError(false)
{ {
} }
@@ -138,50 +102,38 @@ bool WebFlowCredentials::ready() const {
void WebFlowCredentials::fetchFromKeychain() { void WebFlowCredentials::fetchFromKeychain() {
_wasFetched = true; _wasFetched = true;
// Make sure we get the user from the config file // Make sure we get the user fromt he config file
fetchUser(); fetchUser();
if (ready()) { if (ready()) {
emit fetched(); emit fetched();
} else { } else {
qCInfo(lcWebFlowCredentials()) << "Fetch from keychain!"; qCInfo(lcWebFlowCredentials()) << "Fetch from keyhchain!";
fetchFromKeychainHelper(); fetchFromKeychainHelper();
} }
} }
void WebFlowCredentials::askFromUser() { void WebFlowCredentials::askFromUser() {
// Determine if the old flow has to be used (GS for now) _askDialog = new WebFlowCredentialsDialog();
// Do a DetermineAuthTypeJob to make sure that the server is still using Flow2
DetermineAuthTypeJob *job = new DetermineAuthTypeJob(_account->sharedFromThis(), this);
connect(job, &DetermineAuthTypeJob::authType, [this](DetermineAuthTypeJob::AuthType type) {
// LoginFlowV2 > WebViewFlow > OAuth > Shib > Basic
bool useFlow2 = (type != DetermineAuthTypeJob::WebViewFlow);
_askDialog = new WebFlowCredentialsDialog(_account, useFlow2); QUrl url = _account->url();
QString path = url.path() + "/index.php/login/flow";
url.setPath(path);
_askDialog->setUrl(url);
if (!useFlow2) { QString msg = tr("You have been logged out of %1 as user %2. Please login again")
QUrl url = _account->url(); .arg(_account->displayName(), _user);
QString path = url.path() + "/index.php/login/flow"; _askDialog->setInfo(msg);
url.setPath(path);
_askDialog->setUrl(url);
}
QString msg = tr("You have been logged out of %1 as user %2. Please login again") _askDialog->show();
.arg(_account->displayName(), _user);
_askDialog->setInfo(msg);
_askDialog->show(); connect(_askDialog, &WebFlowCredentialsDialog::urlCatched, this, &WebFlowCredentials::slotAskFromUserCredentialsProvided);
connect(_askDialog, &WebFlowCredentialsDialog::urlCatched, this, &WebFlowCredentials::slotAskFromUserCredentialsProvided);
connect(_askDialog, &WebFlowCredentialsDialog::onClose, this, &WebFlowCredentials::slotAskFromUserCancelled);
});
job->start();
qCDebug(lcWebFlowCredentials()) << "User needs to reauth!"; qCDebug(lcWebFlowCredentials()) << "User needs to reauth!";
} }
void WebFlowCredentials::slotAskFromUserCredentialsProvided(const QString &user, const QString &pass, const QString &host) { void WebFlowCredentials::slotAskFromUserCredentialsProvided(const QString &user, const QString &pass, const QString &host) {
Q_UNUSED(host) Q_UNUSED(host);
if (_user != user) { if (_user != user) {
qCInfo(lcWebFlowCredentials()) << "Authed with the wrong user!"; qCInfo(lcWebFlowCredentials()) << "Authed with the wrong user!";
@@ -190,12 +142,10 @@ void WebFlowCredentials::slotAskFromUserCredentialsProvided(const QString &user,
.arg(_user); .arg(_user);
_askDialog->setError(msg); _askDialog->setError(msg);
if (!_askDialog->isUsingFlow2()) { QUrl url = _account->url();
QUrl url = _account->url(); QString path = url.path() + "/index.php/login/flow";
QString path = url.path() + "/index.php/login/flow"; url.setPath(path);
url.setPath(path); _askDialog->setUrl(url);
_askDialog->setUrl(url);
}
return; return;
} }
@@ -209,18 +159,10 @@ void WebFlowCredentials::slotAskFromUserCredentialsProvided(const QString &user,
emit asked(); emit asked();
_askDialog->close(); _askDialog->close();
_askDialog->deleteLater(); delete _askDialog;
_askDialog = nullptr; _askDialog = nullptr;
} }
void WebFlowCredentials::slotAskFromUserCancelled() {
qCDebug(lcWebFlowCredentials()) << "User cancelled reauth!";
emit asked();
_askDialog->deleteLater();
_askDialog = nullptr;
}
bool WebFlowCredentials::stillValid(QNetworkReply *reply) { bool WebFlowCredentials::stillValid(QNetworkReply *reply) {
if (reply->error() != QNetworkReply::NoError) { if (reply->error() != QNetworkReply::NoError) {
@@ -236,130 +178,17 @@ void WebFlowCredentials::persist() {
return; return;
} }
_account->setCredentialSetting(userC, _user); _account->setCredentialSetting("user", _user);
_account->wantsAccountSaved(_account); _account->wantsAccountSaved(_account);
// write cert if there is one //TODO: Add ssl cert and key storing
if (!_clientSslCertificate.isNull()) {
auto *job = new KeychainChunk::WriteJob(_account,
_user + clientCertificatePEMC,
_clientSslCertificate.toPem());
connect(job, &KeychainChunk::WriteJob::finished, this, &WebFlowCredentials::slotWriteClientCertPEMJobDone);
job->start();
} else {
// no cert, just write credentials
slotWriteClientCertPEMJobDone(nullptr);
}
}
void WebFlowCredentials::slotWriteClientCertPEMJobDone(KeychainChunk::WriteJob *writeJob)
{
if(writeJob)
writeJob->deleteLater();
// write ssl key if there is one
if (!_clientSslKey.isNull()) {
auto *job = new KeychainChunk::WriteJob(_account,
_user + clientKeyPEMC,
_clientSslKey.toPem());
connect(job, &KeychainChunk::WriteJob::finished, this, &WebFlowCredentials::slotWriteClientKeyPEMJobDone);
job->start();
} else {
// no key, just write credentials
slotWriteClientKeyPEMJobDone(nullptr);
}
}
void WebFlowCredentials::writeSingleClientCaCertPEM()
{
// write a ca cert if there is any in the queue
if (!_clientSslCaCertificatesWriteQueue.isEmpty()) {
// grab and remove the first cert from the queue
auto cert = _clientSslCaCertificatesWriteQueue.dequeue();
auto index = (_clientSslCaCertificates.count() - _clientSslCaCertificatesWriteQueue.count()) - 1;
// keep the limit
if (index > (_clientSslCaCertificatesMaxCount - 1)) {
qCWarning(lcWebFlowCredentials) << "Maximum client CA cert count exceeded while writing slot" << QString::number(index) << "cutting off after" << QString::number(_clientSslCaCertificatesMaxCount) << "certs";
_clientSslCaCertificatesWriteQueue.clear();
slotWriteClientCaCertsPEMJobDone(nullptr);
return;
}
auto *job = new KeychainChunk::WriteJob(_account,
_user + clientCaCertificatePEMC + QString::number(index),
cert.toPem());
connect(job, &KeychainChunk::WriteJob::finished, this, &WebFlowCredentials::slotWriteClientCaCertsPEMJobDone);
job->start();
} else {
slotWriteClientCaCertsPEMJobDone(nullptr);
}
}
void WebFlowCredentials::slotWriteClientKeyPEMJobDone(KeychainChunk::WriteJob *writeJob)
{
if(writeJob)
writeJob->deleteLater();
_clientSslCaCertificatesWriteQueue.clear();
// write ca certs if there are any
if (!_clientSslCaCertificates.isEmpty()) {
// queue the certs to avoid trouble on Windows (Workaround for CredWriteW used by QtKeychain)
_clientSslCaCertificatesWriteQueue.append(_clientSslCaCertificates);
// first ca cert
writeSingleClientCaCertPEM();
} else {
slotWriteClientCaCertsPEMJobDone(nullptr);
}
}
void WebFlowCredentials::slotWriteClientCaCertsPEMJobDone(KeychainChunk::WriteJob *writeJob)
{
// errors / next ca cert?
if (writeJob && !_clientSslCaCertificates.isEmpty()) {
if (writeJob->error() != NoError) {
qCWarning(lcWebFlowCredentials) << "Error while writing client CA cert" << writeJob->errorString();
}
writeJob->deleteLater();
if (!_clientSslCaCertificatesWriteQueue.isEmpty()) {
// next ca cert
writeSingleClientCaCertPEM();
return;
}
}
// done storing ca certs, time for the password
WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName()); WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
addSettingsToJob(_account, job);
#endif
job->setInsecureFallback(false); job->setInsecureFallback(false);
connect(job, &Job::finished, this, &WebFlowCredentials::slotWriteJobDone);
job->setKey(keychainKey(_account->url().toString(), _user, _account->id())); job->setKey(keychainKey(_account->url().toString(), _user, _account->id()));
job->setTextData(_password); job->setTextData(_password);
job->start(); job->start();
} }
void WebFlowCredentials::slotWriteJobDone(QKeychain::Job *job)
{
delete job->settings();
switch (job->error()) {
case NoError:
break;
default:
qCWarning(lcWebFlowCredentials) << "Error while writing password" << job->errorString();
}
WritePasswordJob *wjob = qobject_cast<WritePasswordJob *>(job);
wjob->deleteLater();
}
void WebFlowCredentials::invalidateToken() { void WebFlowCredentials::invalidateToken() {
// clear the session cookie. // clear the session cookie.
_account->clearCookieJar(); _account->clearCookieJar();
@@ -372,14 +201,12 @@ void WebFlowCredentials::invalidateToken() {
QTimer::singleShot(0, _account, &Account::clearQNAMCache); QTimer::singleShot(0, _account, &Account::clearQNAMCache);
} }
void WebFlowCredentials::forgetSensitiveData() { void WebFlowCredentials::forgetSensitiveData(){
_password = QString(); _password = QString();
_ready = false; _ready = false;
fetchUser(); fetchUser();
_account->deleteAppPassword();
const QString kck = keychainKey(_account->url().toString(), _user, _account->id()); const QString kck = keychainKey(_account->url().toString(), _user, _account->id());
if (kck.isEmpty()) { if (kck.isEmpty()) {
qCDebug(lcWebFlowCredentials()) << "InvalidateToken: User is empty, bailing out!"; qCDebug(lcWebFlowCredentials()) << "InvalidateToken: User is empty, bailing out!";
@@ -389,15 +216,9 @@ void WebFlowCredentials::forgetSensitiveData() {
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName()); DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
job->setInsecureFallback(false); job->setInsecureFallback(false);
job->setKey(kck); job->setKey(kck);
connect(job, &Job::finished, this, [](QKeychain::Job *job) {
DeletePasswordJob *djob = qobject_cast<DeletePasswordJob *>(job);
djob->deleteLater();
});
job->start(); job->start();
invalidateToken(); invalidateToken();
deleteKeychainEntries();
} }
void WebFlowCredentials::setAccount(Account *account) { void WebFlowCredentials::setAccount(Account *account) {
@@ -408,12 +229,12 @@ void WebFlowCredentials::setAccount(Account *account) {
} }
QString WebFlowCredentials::fetchUser() { QString WebFlowCredentials::fetchUser() {
_user = _account->credentialSetting(userC).toString(); _user = _account->credentialSetting("user").toString();
return _user; return _user;
} }
void WebFlowCredentials::slotAuthentication(QNetworkReply *reply, QAuthenticator *authenticator) { void WebFlowCredentials::slotAuthentication(QNetworkReply *reply, QAuthenticator *authenticator) {
Q_UNUSED(reply) Q_UNUSED(reply);
if (!_ready) { if (!_ready) {
return; return;
@@ -435,136 +256,16 @@ void WebFlowCredentials::slotFinished(QNetworkReply *reply) {
if (reply->error() == QNetworkReply::NoError) { if (reply->error() == QNetworkReply::NoError) {
_credentialsValid = true; _credentialsValid = true;
/// Used later for remote wipe
_account->writeAppPasswordOnce(_password);
} }
} }
void WebFlowCredentials::fetchFromKeychainHelper() { void WebFlowCredentials::fetchFromKeychainHelper() {
// Read client cert from keychain
auto *job = new KeychainChunk::ReadJob(_account,
_user + clientCertificatePEMC,
_keychainMigration);
connect(job, &KeychainChunk::ReadJob::finished, this, &WebFlowCredentials::slotReadClientCertPEMJobDone);
job->start();
}
void WebFlowCredentials::slotReadClientCertPEMJobDone(KeychainChunk::ReadJob *readJob)
{
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
Q_ASSERT(!readJob->insecureFallback()); // If insecureFallback is set, the next test would be pointless
if (_retryOnKeyChainError && (readJob->error() == QKeychain::NoBackendAvailable
|| readJob->error() == QKeychain::OtherError)) {
// Could be that the backend was not yet available. Wait some extra seconds.
// (Issues #4274 and #6522)
// (For kwallet, the error is OtherError instead of NoBackendAvailable, maybe a bug in QtKeychain)
qCInfo(lcWebFlowCredentials) << "Backend unavailable (yet?) Retrying in a few seconds." << readJob->errorString();
QTimer::singleShot(10000, this, &WebFlowCredentials::fetchFromKeychainHelper);
_retryOnKeyChainError = false;
return;
}
_retryOnKeyChainError = false;
#endif
// Store PEM in memory
if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
QList<QSslCertificate> sslCertificateList = QSslCertificate::fromData(readJob->binaryData(), QSsl::Pem);
if (sslCertificateList.length() >= 1) {
_clientSslCertificate = sslCertificateList.at(0);
}
}
readJob->deleteLater();
// Load key too
auto *job = new KeychainChunk::ReadJob(_account,
_user + clientKeyPEMC,
_keychainMigration);
connect(job, &KeychainChunk::ReadJob::finished, this, &WebFlowCredentials::slotReadClientKeyPEMJobDone);
job->start();
}
void WebFlowCredentials::slotReadClientKeyPEMJobDone(KeychainChunk::ReadJob *readJob)
{
// Store key in memory
if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
QByteArray clientKeyPEM = readJob->binaryData();
// FIXME Unfortunately Qt has a bug and we can't just use QSsl::Opaque to let it
// load whatever we have. So we try until it works.
_clientSslKey = QSslKey(clientKeyPEM, QSsl::Rsa);
if (_clientSslKey.isNull()) {
_clientSslKey = QSslKey(clientKeyPEM, QSsl::Dsa);
}
if (_clientSslKey.isNull()) {
_clientSslKey = QSslKey(clientKeyPEM, QSsl::Ec);
}
if (_clientSslKey.isNull()) {
qCWarning(lcWebFlowCredentials) << "Could not load SSL key into Qt!";
}
clientKeyPEM.clear();
} else {
qCWarning(lcWebFlowCredentials) << "Unable to read client key" << readJob->errorString();
}
readJob->deleteLater();
// Start fetching client CA certs
_clientSslCaCertificates.clear();
readSingleClientCaCertPEM();
}
void WebFlowCredentials::readSingleClientCaCertPEM()
{
// try to fetch a client ca cert
if (_clientSslCaCertificates.count() < _clientSslCaCertificatesMaxCount) {
auto *job = new KeychainChunk::ReadJob(_account,
_user + clientCaCertificatePEMC + QString::number(_clientSslCaCertificates.count()),
_keychainMigration);
connect(job, &KeychainChunk::ReadJob::finished, this, &WebFlowCredentials::slotReadClientCaCertsPEMJobDone);
job->start();
} else {
qCWarning(lcWebFlowCredentials) << "Maximum client CA cert count exceeded while reading, ignoring after" << _clientSslCaCertificatesMaxCount;
slotReadClientCaCertsPEMJobDone(nullptr);
}
}
void WebFlowCredentials::slotReadClientCaCertsPEMJobDone(KeychainChunk::ReadJob *readJob) {
// Store cert in memory
if (readJob) {
if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
QList<QSslCertificate> sslCertificateList = QSslCertificate::fromData(readJob->binaryData(), QSsl::Pem);
if (sslCertificateList.length() >= 1) {
_clientSslCaCertificates.append(sslCertificateList.at(0));
}
readJob->deleteLater();
// try next cert
readSingleClientCaCertPEM();
return;
} else {
if (readJob->error() != QKeychain::Error::EntryNotFound ||
((readJob->error() == QKeychain::Error::EntryNotFound) && _clientSslCaCertificates.count() == 0)) {
qCWarning(lcWebFlowCredentials) << "Unable to read client CA cert slot" << QString::number(_clientSslCaCertificates.count()) << readJob->errorString();
}
}
readJob->deleteLater();
}
// Now fetch the actual server password
const QString kck = keychainKey( const QString kck = keychainKey(
_account->url().toString(), _account->url().toString(),
_user, _user,
_keychainMigration ? QString() : _account->id()); _keychainMigration ? QString() : _account->id());
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName()); ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
addSettingsToJob(_account, job);
#endif
job->setInsecureFallback(false); job->setInsecureFallback(false);
job->setKey(kck); job->setKey(kck);
connect(job, &Job::finished, this, &WebFlowCredentials::slotReadPasswordJobDone); connect(job, &Job::finished, this, &WebFlowCredentials::slotReadPasswordJobDone);
@@ -572,7 +273,7 @@ void WebFlowCredentials::slotReadClientCaCertsPEMJobDone(KeychainChunk::ReadJob
} }
void WebFlowCredentials::slotReadPasswordJobDone(Job *incomingJob) { void WebFlowCredentials::slotReadPasswordJobDone(Job *incomingJob) {
QKeychain::ReadPasswordJob *job = qobject_cast<ReadPasswordJob *>(incomingJob); QKeychain::ReadPasswordJob *job = static_cast<ReadPasswordJob *>(incomingJob);
QKeychain::Error error = job->error(); QKeychain::Error error = job->error();
// If we could not find the entry try the old entries // If we could not find the entry try the old entries
@@ -582,10 +283,6 @@ void WebFlowCredentials::slotReadPasswordJobDone(Job *incomingJob) {
return; return;
} }
if (_user.isEmpty()) {
qCWarning(lcWebFlowCredentials) << "Strange: User is empty!";
}
if (error == QKeychain::NoError) { if (error == QKeychain::NoError) {
_password = job->textData(); _password = job->textData();
_ready = true; _ready = true;
@@ -595,72 +292,20 @@ void WebFlowCredentials::slotReadPasswordJobDone(Job *incomingJob) {
} }
emit fetched(); emit fetched();
job->deleteLater();
// If keychain data was read from legacy location, wipe these entries and store new ones // If keychain data was read from legacy location, wipe these entries and store new ones
if (_keychainMigration && _ready) { if (_keychainMigration && _ready) {
_keychainMigration = false; _keychainMigration = false;
persist(); persist();
deleteKeychainEntries(true); // true: delete old entries deleteOldKeychainEntries();
qCInfo(lcWebFlowCredentials) << "Migrated old keychain entries"; qCInfo(lcWebFlowCredentials) << "Migrated old keychain entries";
} }
} }
void WebFlowCredentials::deleteKeychainEntries(bool oldKeychainEntries) { void WebFlowCredentials::deleteOldKeychainEntries() {
auto startDeleteJob = [this, oldKeychainEntries](QString key) { DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName()); job->setInsecureFallback(false);
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK) job->setKey(keychainKey(_account->url().toString(), _user, QString()));
addSettingsToJob(_account, job); job->start();
#endif
job->setInsecureFallback(false);
job->setKey(keychainKey(_account->url().toString(),
key,
oldKeychainEntries ? QString() : _account->id()));
connect(job, &Job::finished, this, [](QKeychain::Job *job) {
DeletePasswordJob *djob = qobject_cast<DeletePasswordJob *>(job);
djob->deleteLater();
});
job->start();
};
startDeleteJob(_user);
/* IMPORTANT - remove later - FIXME MS@2019-12-07 -->
* TODO: For "Log out" & "Remove account": Remove client CA certs and KEY!
*
* Disabled as long as selecting another cert is not supported by the UI.
*
* Being able to specify a new certificate is important anyway: expiry etc.
*
* We introduce this dirty hack here, to allow deleting them upon Remote Wipe.
*/
if(_account->isRemoteWipeRequested_HACK()) {
// <-- FIXME MS@2019-12-07
startDeleteJob(_user + clientKeyPEMC);
startDeleteJob(_user + clientCertificatePEMC);
for (auto i = 0; i < _clientSslCaCertificates.count(); i++) {
startDeleteJob(_user + clientCaCertificatePEMC + QString::number(i));
}
#if defined(Q_OS_WIN)
// Also delete key / cert sub-chunks (Windows workaround)
// The first chunk (0) has no suffix, to stay compatible with older versions and non-Windows
for (auto chunk = 1; chunk < KeychainChunk::MaxChunks; chunk++) {
const QString strChunkSuffix = QString(".") + QString::number(chunk);
startDeleteJob(_user + clientKeyPEMC + strChunkSuffix);
startDeleteJob(_user + clientCertificatePEMC + strChunkSuffix);
for (auto i = 0; i < _clientSslCaCertificates.count(); i++) {
startDeleteJob(_user + clientCaCertificatePEMC + QString::number(i));
}
}
#endif
// FIXME MS@2019-12-07 -->
}
// <-- FIXME MS@2019-12-07
} }
} // namespace OCC }

View File

@@ -3,8 +3,6 @@
#include <QSslCertificate> #include <QSslCertificate>
#include <QSslKey> #include <QSslKey>
#include <QNetworkRequest>
#include <QQueue>
#include "creds/abstractcredentials.h" #include "creds/abstractcredentials.h"
@@ -19,29 +17,14 @@ namespace QKeychain {
namespace OCC { namespace OCC {
namespace KeychainChunk {
class ReadJob;
class WriteJob;
}
class WebFlowCredentialsDialog; class WebFlowCredentialsDialog;
class WebFlowCredentials : public AbstractCredentials class WebFlowCredentials : public AbstractCredentials
{ {
Q_OBJECT Q_OBJECT
friend class WebFlowCredentialsAccessManager;
public: public:
/// Don't add credentials if this is set on a QNetworkRequest
static constexpr QNetworkRequest::Attribute DontAddCredentialsAttribute = QNetworkRequest::User;
explicit WebFlowCredentials(); explicit WebFlowCredentials();
WebFlowCredentials( WebFlowCredentials(const QString &user, const QString &password, const QSslCertificate &certificate = QSslCertificate(), const QSslKey &key = QSslKey());
const QString &user,
const QString &password,
const QSslCertificate &certificate = QSslCertificate(),
const QSslKey &key = QSslKey(),
const QList<QSslCertificate> &caCertificates = QList<QSslCertificate>());
QString authType() const override; QString authType() const override;
QString user() const override; QString user() const override;
@@ -65,51 +48,12 @@ private slots:
void slotAuthentication(QNetworkReply *reply, QAuthenticator *authenticator); void slotAuthentication(QNetworkReply *reply, QAuthenticator *authenticator);
void slotFinished(QNetworkReply *reply); void slotFinished(QNetworkReply *reply);
void slotAskFromUserCredentialsProvided(const QString &user, const QString &pass, const QString &host);
void slotAskFromUserCancelled();
void slotReadClientCertPEMJobDone(KeychainChunk::ReadJob *readJob);
void slotReadClientKeyPEMJobDone(KeychainChunk::ReadJob *readJob);
void slotReadClientCaCertsPEMJobDone(KeychainChunk::ReadJob *readJob);
void slotReadPasswordJobDone(QKeychain::Job *incomingJob); void slotReadPasswordJobDone(QKeychain::Job *incomingJob);
void slotAskFromUserCredentialsProvided(const QString &user, const QString &pass, const QString &host);
void slotWriteClientCertPEMJobDone(KeychainChunk::WriteJob *writeJob);
void slotWriteClientKeyPEMJobDone(KeychainChunk::WriteJob *writeJob);
void slotWriteClientCaCertsPEMJobDone(KeychainChunk::WriteJob *writeJob);
void slotWriteJobDone(QKeychain::Job *);
private: private:
/*
* Windows: Workaround for CredWriteW used by QtKeychain
*
* Saving all client CA's within one credential may result in:
* Error: "Credential size exceeds maximum size of 2560"
*/
void readSingleClientCaCertPEM();
void writeSingleClientCaCertPEM();
/*
* Since we're limited by Windows limits, we just create our own
* limit to avoid evil things happening by endless recursion
*
* Better than storing the count and relying on maybe-hacked values
*/
static constexpr int _clientSslCaCertificatesMaxCount = 10;
QQueue<QSslCertificate> _clientSslCaCertificatesWriteQueue;
protected:
/** Reads data from keychain locations
*
* Goes through
* slotReadClientCertPEMJobDone to
* slotReadClientKeyPEMJobDone to
* slotReadClientCaCertsPEMJobDone to
* slotReadJobDone
*/
void fetchFromKeychainHelper(); void fetchFromKeychainHelper();
void deleteOldKeychainEntries();
/// Wipes legacy keychain locations
void deleteKeychainEntries(bool oldKeychainEntries = false);
QString fetchUser(); QString fetchUser();
@@ -117,16 +61,14 @@ protected:
QString _password; QString _password;
QSslKey _clientSslKey; QSslKey _clientSslKey;
QSslCertificate _clientSslCertificate; QSslCertificate _clientSslCertificate;
QList<QSslCertificate> _clientSslCaCertificates;
bool _ready; bool _ready;
bool _credentialsValid; bool _credentialsValid;
bool _keychainMigration; bool _keychainMigration;
bool _retryOnKeyChainError = true; // true if we haven't done yet any reading from keychain
WebFlowCredentialsDialog *_askDialog; WebFlowCredentialsDialog *_askDialog;
}; };
} // namespace OCC }
#endif // WEBFLOWCREDENTIALS_H #endif // WEBFLOWCREDENTIALS_H

View File

@@ -3,102 +3,42 @@
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QLabel> #include <QLabel>
#include "theme.h"
#include "application.h"
#include "owncloudgui.h"
#include "headerbanner.h"
#include "wizard/owncloudwizardcommon.h"
#include "wizard/webview.h" #include "wizard/webview.h"
#include "wizard/flow2authwidget.h"
namespace OCC { namespace OCC {
WebFlowCredentialsDialog::WebFlowCredentialsDialog(Account *account, bool useFlow2, QWidget *parent) WebFlowCredentialsDialog::WebFlowCredentialsDialog(QWidget *parent)
: QDialog(parent), : QDialog(parent)
_useFlow2(useFlow2),
_flow2AuthWidget(nullptr),
_webView(nullptr)
{ {
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
_layout = new QVBoxLayout(this); _layout = new QVBoxLayout(this);
int spacing = _layout->spacing();
int margin = _layout->margin();
_layout->setSpacing(0);
_layout->setMargin(0);
if(_useFlow2) {
_headerBanner = new HeaderBanner(this);
_layout->addWidget(_headerBanner);
Theme *theme = Theme::instance();
_headerBanner->setup(tr("Log in"), theme->wizardHeaderLogo(), theme->wizardHeaderBanner(),
Qt::AutoText, QString::fromLatin1("color:#fff;"));
}
_containerLayout = new QVBoxLayout(this);
_containerLayout->setSpacing(spacing);
_containerLayout->setMargin(margin);
//QString msg = tr("You have been logged out of %1 as user %2, please login again")
// .arg(_account->displayName(), _user);
_infoLabel = new QLabel(); _infoLabel = new QLabel();
_containerLayout->addWidget(_infoLabel); _layout->addWidget(_infoLabel);
if (_useFlow2) { _webView = new WebView();
_flow2AuthWidget = new Flow2AuthWidget(); _layout->addWidget(_webView);
_containerLayout->addWidget(_flow2AuthWidget);
connect(_flow2AuthWidget, &Flow2AuthWidget::authResult, this, &WebFlowCredentialsDialog::slotFlow2AuthResult);
// Connect styleChanged events to our widgets, so they can adapt (Dark-/Light-Mode switching)
connect(this, &WebFlowCredentialsDialog::styleChanged, _flow2AuthWidget, &Flow2AuthWidget::slotStyleChanged);
// allow Flow2 page to poll on window activation
connect(this, &WebFlowCredentialsDialog::onActivate, _flow2AuthWidget, &Flow2AuthWidget::slotPollNow);
_flow2AuthWidget->startAuth(account);
} else {
_webView = new WebView();
_containerLayout->addWidget(_webView);
connect(_webView, &WebView::urlCatched, this, &WebFlowCredentialsDialog::urlCatched);
}
auto app = static_cast<Application *>(qApp);
connect(app, &Application::isShowingSettingsDialog, this, &WebFlowCredentialsDialog::slotShowSettingsDialog);
_errorLabel = new QLabel(); _errorLabel = new QLabel();
_errorLabel->hide(); _errorLabel->hide();
_containerLayout->addWidget(_errorLabel); _layout->addWidget(_errorLabel);
WizardCommon::initErrorLabel(_errorLabel);
_layout->addLayout(_containerLayout);
setLayout(_layout); setLayout(_layout);
customizeStyle(); connect(_webView, &WebView::urlCatched, this, &WebFlowCredentialsDialog::urlCatched);
} }
void WebFlowCredentialsDialog::closeEvent(QCloseEvent* e) { void WebFlowCredentialsDialog::closeEvent(QCloseEvent* e) {
Q_UNUSED(e) Q_UNUSED(e);
if (_webView) { // Force calling WebView::~WebView() earlier so that _profile and _page are
// Force calling WebView::~WebView() earlier so that _profile and _page are // deleted in the correct order.
// deleted in the correct order. delete _webView;
_webView->deleteLater();
_webView = nullptr;
}
if (_flow2AuthWidget) {
_flow2AuthWidget->resetAuth();
_flow2AuthWidget->deleteLater();
_flow2AuthWidget = nullptr;
}
emit onClose();
} }
void WebFlowCredentialsDialog::setUrl(const QUrl &url) { void WebFlowCredentialsDialog::setUrl(const QUrl &url) {
if (_webView) _webView->setUrl(url);
_webView->setUrl(url);
} }
void WebFlowCredentialsDialog::setInfo(const QString &msg) { void WebFlowCredentialsDialog::setInfo(const QString &msg) {
@@ -106,14 +46,6 @@ void WebFlowCredentialsDialog::setInfo(const QString &msg) {
} }
void WebFlowCredentialsDialog::setError(const QString &error) { void WebFlowCredentialsDialog::setError(const QString &error) {
// bring window to top
slotShowSettingsDialog();
if (_useFlow2 && _flow2AuthWidget) {
_flow2AuthWidget->setError(error);
return;
}
if (error.isEmpty()) { if (error.isEmpty()) {
_errorLabel->hide(); _errorLabel->hide();
} else { } else {
@@ -122,49 +54,4 @@ void WebFlowCredentialsDialog::setError(const QString &error) {
} }
} }
void WebFlowCredentialsDialog::changeEvent(QEvent *e)
{
switch (e->type()) {
case QEvent::StyleChange:
case QEvent::PaletteChange:
case QEvent::ThemeChange:
customizeStyle();
// Notify the other widgets (Dark-/Light-Mode switching)
emit styleChanged();
break;
case QEvent::ActivationChange:
if(isActiveWindow())
emit onActivate();
break;
default:
break;
}
QDialog::changeEvent(e);
} }
void WebFlowCredentialsDialog::customizeStyle()
{
// HINT: Customize dialog's own style here, if necessary in the future (Dark-/Light-Mode switching)
}
void WebFlowCredentialsDialog::slotShowSettingsDialog()
{
// bring window to top but slightly delay, to avoid being hidden behind the SettingsDialog
QTimer::singleShot(100, this, [this] {
ownCloudGui::raiseDialog(this);
});
}
void WebFlowCredentialsDialog::slotFlow2AuthResult(Flow2Auth::Result r, const QString &errorString, const QString &user, const QString &appPassword)
{
if(r == Flow2Auth::LoggedIn) {
emit urlCatched(user, appPassword, QString());
} else {
// bring window to top
slotShowSettingsDialog();
}
}
} // namespace OCC

View File

@@ -4,61 +4,36 @@
#include <QDialog> #include <QDialog>
#include <QUrl> #include <QUrl>
#include "accountfwd.h"
#include "creds/flow2auth.h"
class QLabel; class QLabel;
class QVBoxLayout; class QVBoxLayout;
namespace OCC { namespace OCC {
class HeaderBanner;
class WebView; class WebView;
class Flow2AuthWidget;
class WebFlowCredentialsDialog : public QDialog class WebFlowCredentialsDialog : public QDialog
{ {
Q_OBJECT Q_OBJECT
public: public:
WebFlowCredentialsDialog(Account *account, bool useFlow2, QWidget *parent = nullptr); WebFlowCredentialsDialog(QWidget *parent = nullptr);
void setUrl(const QUrl &url); void setUrl(const QUrl &url);
void setInfo(const QString &msg); void setInfo(const QString &msg);
void setError(const QString &error); void setError(const QString &error);
bool isUsingFlow2() const {
return _useFlow2;
}
protected: protected:
void closeEvent(QCloseEvent * e) override; void closeEvent(QCloseEvent * e) override;
void changeEvent(QEvent *) override;
public slots:
void slotFlow2AuthResult(Flow2Auth::Result, const QString &errorString, const QString &user, const QString &appPassword);
void slotShowSettingsDialog();
signals: signals:
void urlCatched(const QString user, const QString pass, const QString host); void urlCatched(const QString user, const QString pass, const QString host);
void styleChanged();
void onActivate();
void onClose();
private: private:
void customizeStyle();
bool _useFlow2;
Flow2AuthWidget *_flow2AuthWidget;
WebView *_webView; WebView *_webView;
QLabel *_errorLabel; QLabel *_errorLabel;
QLabel *_infoLabel; QLabel *_infoLabel;
QVBoxLayout *_layout; QVBoxLayout *_layout;
QVBoxLayout *_containerLayout;
HeaderBanner *_headerBanner;
}; };
} // namespace OCC }
#endif // WEBFLOWCREDENTIALSDIALOG_H #endif // WEBFLOWCREDENTIALSDIALOG_H

View File

@@ -417,13 +417,6 @@ void Folder::createGuiLog(const QString &filename, LogStatus status, int count,
text = tr("%1 could not be synced due to an error. See the log for details.").arg(file); text = tr("%1 could not be synced due to an error. See the log for details.").arg(file);
} }
break; break;
case LogStatusFileLocked:
if (count > 1) {
text = tr("%1 and %n other file(s) are currently locked.", "", count -1).arg(file);
} else {
text = tr("%1 is currently locked.").arg(file);
}
break;
} }
if (!text.isEmpty()) { if (!text.isEmpty()) {
@@ -902,19 +895,13 @@ void Folder::slotItemCompleted(const SyncFileItemPtr &item)
} }
// add new directories or remove gone away dirs to the watcher // add new directories or remove gone away dirs to the watcher
if (_folderWatcher && item->isDirectory()) { if (item->isDirectory() && item->_instruction == CSYNC_INSTRUCTION_NEW) {
switch (item->_instruction) { if (_folderWatcher)
case CSYNC_INSTRUCTION_NEW: _folderWatcher->addPath(path() + item->_file);
_folderWatcher->addPath(path() + item->_file); }
break; if (item->isDirectory() && item->_instruction == CSYNC_INSTRUCTION_REMOVE) {
case CSYNC_INSTRUCTION_REMOVE: if (_folderWatcher)
_folderWatcher->removePath(path() + item->_file); _folderWatcher->removePath(path() + item->_file);
break;
case CSYNC_INSTRUCTION_RENAME:
_folderWatcher->removePath(path() + item->_file);
_folderWatcher->addPath(path() + item->destination());
break;
}
} }
// Success and failure of sync items adjust what the next sync is // Success and failure of sync items adjust what the next sync is
@@ -1085,20 +1072,20 @@ void Folder::slotAboutToRemoveAllFiles(SyncFileItem::Direction dir, bool *cancel
if (!cfgFile.promptDeleteFiles()) if (!cfgFile.promptDeleteFiles())
return; return;
QString msg = dir == SyncFileItem::Down ? tr("All files in the sync folder '%1' were deleted on the server.\n" QString msg = dir == SyncFileItem::Down ? tr("All files in the sync folder '%1' folder were deleted on the server.\n"
"These deletes will be synchronized to your local sync folder, making such files " "These deletes will be synchronized to your local sync folder, making such files "
"unavailable unless you have a right to restore. \n" "unavailable unless you have a right to restore. \n"
"If you decide to restore the files, they will be re-synced with the server if you have rights to do so.\n" "If you decide to keep the files, they will be re-synced with the server if you have rights to do so.\n"
"If you decide to delete the files, they will be unavailable to you, unless you are the owner.") "If you decide to delete the files, they will be unavailable to you, unless you are the owner.")
: tr("All files got deleted from your local sync folder '%1'.\n" : tr("All the files in your local sync folder '%1' were deleted. These deletes will be "
"These files will be deleted from the server and will not be available on your other devices if they " "synchronized with your server, making such files unavailable unless restored.\n"
"will not be restored.\n" "Are you sure you want to sync those actions with the server?\n"
"If this action was unintended you can restore the lost data now."); "If this was an accident and you decide to keep your files, they will be re-synced from the server.");
QMessageBox msgBox(QMessageBox::Warning, tr("Delete all files?"), QMessageBox msgBox(QMessageBox::Warning, tr("Download new files?"),
msg.arg(shortGuiLocalPath())); msg.arg(shortGuiLocalPath()));
msgBox.setWindowFlags(msgBox.windowFlags() | Qt::WindowStaysOnTopHint); msgBox.setWindowFlags(msgBox.windowFlags() | Qt::WindowStaysOnTopHint);
msgBox.addButton(tr("Delete all files"), QMessageBox::DestructiveRole); msgBox.addButton(tr("Download new files"), QMessageBox::DestructiveRole);
QPushButton *keepBtn = msgBox.addButton(tr("Restore deleted files"), QMessageBox::AcceptRole); QPushButton *keepBtn = msgBox.addButton(tr("Keep local files"), QMessageBox::AcceptRole);
if (msgBox.exec() == -1) { if (msgBox.exec() == -1) {
*cancel = true; *cancel = true;
return; return;

View File

@@ -75,7 +75,7 @@ FolderMan::FolderMan(QObject *parent)
this, &FolderMan::slotScheduleFolderByTime); this, &FolderMan::slotScheduleFolderByTime);
_timeScheduler.start(); _timeScheduler.start();
connect(AccountManager::instance(), &AccountManager::removeAccountFolders, connect(AccountManager::instance(), &AccountManager::accountRemoved,
this, &FolderMan::slotRemoveFoldersForAccount); this, &FolderMan::slotRemoveFoldersForAccount);
connect(_lockWatcher.data(), &LockWatcher::fileUnlocked, connect(_lockWatcher.data(), &LockWatcher::fileUnlocked,
@@ -443,7 +443,7 @@ void FolderMan::slotFolderSyncPaused(Folder *f, bool paused)
void FolderMan::slotFolderCanSyncChanged() void FolderMan::slotFolderCanSyncChanged()
{ {
Folder *f = qobject_cast<Folder *>(sender()); Folder *f = qobject_cast<Folder *>(sender());
ASSERT(f); ASSERT(f);
if (f->canSync()) { if (f->canSync()) {
_socketApi->slotRegisterPath(f->alias()); _socketApi->slotRegisterPath(f->alias());
} else { } else {
@@ -562,7 +562,7 @@ void FolderMan::slotEtagJobDestroyed(QObject * /*o*/)
void FolderMan::slotRunOneEtagJob() void FolderMan::slotRunOneEtagJob()
{ {
if (_currentEtagJob.isNull()) { if (_currentEtagJob.isNull()) {
Folder *folder = nullptr; Folder *folder;
foreach (Folder *f, _folderMap) { foreach (Folder *f, _folderMap) {
if (f->etagJob()) { if (f->etagJob()) {
// Caveat: always grabs the first folder with a job, but we think this is Ok for now and avoids us having a seperate queue. // Caveat: always grabs the first folder with a job, but we think this is Ok for now and avoids us having a seperate queue.
@@ -1084,73 +1084,6 @@ bool FolderMan::startFromScratch(const QString &localFolder)
return true; return true;
} }
void FolderMan::slotWipeFolderForAccount(AccountState *accountState)
{
QVarLengthArray<Folder *, 16> foldersToRemove;
Folder::MapIterator i(_folderMap);
while (i.hasNext()) {
i.next();
Folder *folder = i.value();
if (folder->accountState() == accountState) {
foldersToRemove.append(folder);
}
}
bool success = false;
foreach (const auto &f, foldersToRemove) {
if (!f) {
qCCritical(lcFolderMan) << "Can not remove null folder";
return;
}
qCInfo(lcFolderMan) << "Removing " << f->alias();
const bool currentlyRunning = (_currentSyncFolder == f);
if (currentlyRunning) {
// abort the sync now
terminateSyncProcess();
}
if (_scheduledFolders.removeAll(f) > 0) {
emit scheduleQueueChanged();
}
// wipe database
f->wipe();
// wipe data
QDir userFolder(f->path());
if (userFolder.exists()) {
success = userFolder.removeRecursively();
if (!success) {
qCWarning(lcFolderMan) << "Failed to remove existing folder " << f->path();
} else {
qCInfo(lcFolderMan) << "wipe: Removed file " << f->path();
}
} else {
success = true;
qCWarning(lcFolderMan) << "folder does not exist, can not remove.";
}
f->setSyncPaused(true);
// remove the folder configuration
f->removeFromSettings();
unloadFolder(f);
if (currentlyRunning) {
delete f;
}
_navigationPaneHelper.scheduleUpdateCloudStorageRegistry();
}
emit folderListChanged(_folderMap);
emit wipeDone(accountState, success);
}
void FolderMan::setDirtyProxy() void FolderMan::setDirtyProxy()
{ {
foreach (Folder *f, _folderMap.values()) { foreach (Folder *f, _folderMap.values()) {

View File

@@ -207,11 +207,6 @@ signals:
*/ */
void folderListChanged(const Folder::Map &); void folderListChanged(const Folder::Map &);
/**
* Emitted once slotRemoveFoldersForAccount is done wiping
*/
void wipeDone(AccountState *account, bool success);
public slots: public slots:
/** /**
@@ -236,9 +231,6 @@ public slots:
// slot to schedule an ETag job (from Folder only) // slot to schedule an ETag job (from Folder only)
void slotScheduleETagJob(const QString &alias, RequestEtagJob *job); void slotScheduleETagJob(const QString &alias, RequestEtagJob *job);
/** Wipe folder */
void slotWipeFolderForAccount(AccountState *accountState);
private slots: private slots:
void slotFolderSyncPaused(Folder *, bool paused); void slotFolderSyncPaused(Folder *, bool paused);
void slotFolderCanSyncChanged(); void slotFolderCanSyncChanged();

View File

@@ -40,7 +40,7 @@ namespace OCC {
FolderStatusDelegate::FolderStatusDelegate() FolderStatusDelegate::FolderStatusDelegate()
: QStyledItemDelegate() : QStyledItemDelegate()
{ {
customizeStyle(); m_moreIcon = QIcon(QLatin1String(":/client/resources/more.svg"));
} }
QString FolderStatusDelegate::addFolderText() QString FolderStatusDelegate::addFolderText()
@@ -168,7 +168,6 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
QString itemString = qvariant_cast<QString>(index.data(SyncProgressItemString)); QString itemString = qvariant_cast<QString>(index.data(SyncProgressItemString));
int warningCount = qvariant_cast<int>(index.data(WarningCount)); int warningCount = qvariant_cast<int>(index.data(WarningCount));
bool syncOngoing = qvariant_cast<bool>(index.data(SyncRunning)); bool syncOngoing = qvariant_cast<bool>(index.data(SyncRunning));
QDateTime syncDate = qvariant_cast<QDateTime>(index.data(SyncDate));
bool syncEnabled = qvariant_cast<bool>(index.data(FolderAccountConnected)); bool syncEnabled = qvariant_cast<bool>(index.data(FolderAccountConnected));
QRect iconRect = option.rect; QRect iconRect = option.rect;
@@ -253,7 +252,7 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
if (!showProgess) { if (!showProgess) {
painter->setFont(subFont); painter->setFont(subFont);
QString elidedRemotePathText = subFm.elidedText( QString elidedRemotePathText = subFm.elidedText(
tr("Synchronized with local folder (%1)").arg(syncDate.toTimeSpec(Qt::LocalTime).toString(Qt::SystemLocaleShortDate)), tr("Synchronized with local folder"),
Qt::ElideRight, remotePathRect.width()); Qt::ElideRight, remotePathRect.width());
painter->drawText(QStyle::visualRect(option.direction, option.rect, remotePathRect), painter->drawText(QStyle::visualRect(option.direction, option.rect, remotePathRect),
textAlign, elidedRemotePathText); textAlign, elidedRemotePathText);
@@ -273,11 +272,6 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
rect.setHeight(texts.count() * subFm.height() + 2 * margin); rect.setHeight(texts.count() * subFm.height() + 2 * margin);
rect.setRight(option.rect.right() - margin); rect.setRight(option.rect.right() - margin);
// save previous state to not mess up colours with the background (fixes issue: https://github.com/nextcloud/desktop/issues/1237)
auto oldBrush = painter->brush();
auto oldPen = painter->pen();
auto oldFont = painter->font();
painter->setBrush(color); painter->setBrush(color);
painter->setPen(QColor(0xaa, 0xaa, 0xaa)); painter->setPen(QColor(0xaa, 0xaa, 0xaa));
painter->drawRoundedRect(QStyle::visualRect(option.direction, option.rect, rect), painter->drawRoundedRect(QStyle::visualRect(option.direction, option.rect, rect),
@@ -295,11 +289,6 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
textRect.translate(0, textRect.height()); textRect.translate(0, textRect.height());
} }
// restore previous state
painter->setBrush(oldBrush);
painter->setPen(oldPen);
painter->setFont(oldFont);
h = rect.bottom() + margin; h = rect.bottom() + margin;
}; };
@@ -359,7 +348,7 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
btnOpt.arrowType = Qt::NoArrow; btnOpt.arrowType = Qt::NoArrow;
btnOpt.subControls = QStyle::SC_ToolButton; btnOpt.subControls = QStyle::SC_ToolButton;
btnOpt.rect = optionsButtonVisualRect; btnOpt.rect = optionsButtonVisualRect;
btnOpt.icon = _iconMore; btnOpt.icon = m_moreIcon;
int e = QApplication::style()->pixelMetric(QStyle::PM_ButtonIconSize); int e = QApplication::style()->pixelMetric(QStyle::PM_ButtonIconSize);
btnOpt.iconSize = QSize(e,e); btnOpt.iconSize = QSize(e,e);
QApplication::style()->drawComplexControl(QStyle::CC_ToolButton, &btnOpt, painter); QApplication::style()->drawComplexControl(QStyle::CC_ToolButton, &btnOpt, painter);
@@ -433,14 +422,5 @@ QRect FolderStatusDelegate::errorsListRect(QRect within)
return within; return within;
} }
void FolderStatusDelegate::slotStyleChanged()
{
customizeStyle();
}
void FolderStatusDelegate::customizeStyle()
{
_iconMore = Theme::createColorAwareIcon(QLatin1String(":/client/resources/more.svg"));
}
} // namespace OCC } // namespace OCC

View File

@@ -26,6 +26,7 @@ class FolderStatusDelegate : public QStyledItemDelegate
{ {
Q_OBJECT Q_OBJECT
public: public:
QIcon m_moreIcon;
FolderStatusDelegate(); FolderStatusDelegate();
enum datarole { FolderAliasRole = Qt::UserRole + 100, enum datarole { FolderAliasRole = Qt::UserRole + 100,
@@ -43,7 +44,6 @@ public:
SyncProgressItemString, SyncProgressItemString,
WarningCount, WarningCount,
SyncRunning, SyncRunning,
SyncDate,
AddButton // 1 = enabled; 2 = disabled AddButton // 1 = enabled; 2 = disabled
}; };
@@ -61,16 +61,9 @@ public:
static QRect errorsListRect(QRect within); static QRect errorsListRect(QRect within);
static int rootFolderHeightWithoutErrors(const QFontMetrics &fm, const QFontMetrics &aliasFm); static int rootFolderHeightWithoutErrors(const QFontMetrics &fm, const QFontMetrics &aliasFm);
public slots:
void slotStyleChanged();
private: private:
void customizeStyle();
static QString addFolderText(); static QString addFolderText();
QPersistentModelIndex _pressedIndex; QPersistentModelIndex _pressedIndex;
QIcon _iconMore;
}; };
} // namespace OCC } // namespace OCC

View File

@@ -186,7 +186,7 @@ QVariant FolderStatusModel::data(const QModelIndex &index, int role) const
return QVariant(tr("Error while loading the list of folders from the server.") return QVariant(tr("Error while loading the list of folders from the server.")
+ QString("\n") + x->_lastErrorString); + QString("\n") + x->_lastErrorString);
} else { } else {
return tr("Fetching folder list from server"); return tr("Fetching folder list from server...");
} }
break; break;
default: default:
@@ -218,8 +218,6 @@ QVariant FolderStatusModel::data(const QModelIndex &index, int role) const
return f->syncResult().errorStrings(); return f->syncResult().errorStrings();
case FolderStatusDelegate::SyncRunning: case FolderStatusDelegate::SyncRunning:
return f->syncResult().status() == SyncResult::SyncRunning; return f->syncResult().status() == SyncResult::SyncRunning;
case FolderStatusDelegate::SyncDate:
return f->syncResult().syncTime();
case FolderStatusDelegate::HeaderRole: case FolderStatusDelegate::HeaderRole:
return f->shortGuiRemotePathOrAppName(); return f->shortGuiRemotePathOrAppName();
case FolderStatusDelegate::FolderAliasRole: case FolderStatusDelegate::FolderAliasRole:
@@ -1099,15 +1097,15 @@ void FolderStatusModel::slotFolderSyncStateChange(Folder *f)
} }
QString message; QString message;
if (pos <= 0) { if (pos <= 0) {
message = tr("Waiting"); message = tr("Waiting...");
} else { } else {
message = tr("Waiting for %n other folder(s)", "", pos); message = tr("Waiting for %n other folder(s)...", "", pos);
} }
pi = SubFolderInfo::Progress(); pi = SubFolderInfo::Progress();
pi._overallSyncString = message; pi._overallSyncString = message;
} else if (state == SyncResult::SyncPrepare) { } else if (state == SyncResult::SyncPrepare) {
pi = SubFolderInfo::Progress(); pi = SubFolderInfo::Progress();
pi._overallSyncString = tr("Preparing to sync"); pi._overallSyncString = tr("Preparing to sync...");
} else if (state == SyncResult::Problem || state == SyncResult::Success) { } else if (state == SyncResult::Problem || state == SyncResult::Success) {
// Reset the progress info after a sync. // Reset the progress info after a sync.
pi = SubFolderInfo::Progress(); pi = SubFolderInfo::Progress();

View File

@@ -19,8 +19,6 @@
#include "folderwatcher.h" #include "folderwatcher.h"
#include "folderwatcher_win.h" #include "folderwatcher_win.h"
#include "common/utility.h"
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <tchar.h> #include <tchar.h>
@@ -54,7 +52,7 @@ void WatcherThread::watchChanges(size_t fileNotifyBufferSize,
// QVarLengthArray ensures the stack-buffer is aligned like double and qint64. // QVarLengthArray ensures the stack-buffer is aligned like double and qint64.
QVarLengthArray<char, 4096 * 10> fileNotifyBuffer; QVarLengthArray<char, 4096 * 10> fileNotifyBuffer;
fileNotifyBuffer.resize(OCC::Utility::convertSizeToInt(fileNotifyBufferSize)); fileNotifyBuffer.resize(fileNotifyBufferSize);
const size_t fileNameBufferSize = 4096; const size_t fileNameBufferSize = 4096;
TCHAR fileNameBuffer[fileNameBufferSize]; TCHAR fileNameBuffer[fileNameBufferSize];
@@ -68,7 +66,7 @@ void WatcherThread::watchChanges(size_t fileNotifyBufferSize,
DWORD dwBytesReturned = 0; DWORD dwBytesReturned = 0;
SecureZeroMemory(pFileNotifyBuffer, fileNotifyBufferSize); SecureZeroMemory(pFileNotifyBuffer, fileNotifyBufferSize);
if (!ReadDirectoryChangesW(_directory, (LPVOID)pFileNotifyBuffer, if (!ReadDirectoryChangesW(_directory, (LPVOID)pFileNotifyBuffer,
OCC::Utility::convertSizeToDWORD(fileNotifyBufferSize), true, fileNotifyBufferSize, true,
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE,
&dwBytesReturned, &dwBytesReturned,
&overlapped, &overlapped,
@@ -115,7 +113,7 @@ void WatcherThread::watchChanges(size_t fileNotifyBufferSize,
FILE_NOTIFY_INFORMATION *curEntry = pFileNotifyBuffer; FILE_NOTIFY_INFORMATION *curEntry = pFileNotifyBuffer;
forever { forever {
size_t len = curEntry->FileNameLength / 2; size_t len = curEntry->FileNameLength / 2;
QString file = _path + "\\" + QString::fromWCharArray(curEntry->FileName, OCC::Utility::convertSizeToInt(len)); QString file = _path + "\\" + QString::fromWCharArray(curEntry->FileName, len);
// Unless the file was removed or renamed, get its full long name // Unless the file was removed or renamed, get its full long name
// TODO: We could still try expanding the path in the tricky cases... // TODO: We could still try expanding the path in the tricky cases...
@@ -124,7 +122,7 @@ void WatcherThread::watchChanges(size_t fileNotifyBufferSize,
&& curEntry->Action != FILE_ACTION_RENAMED_OLD_NAME) { && curEntry->Action != FILE_ACTION_RENAMED_OLD_NAME) {
size_t longNameSize = GetLongPathNameW(reinterpret_cast<LPCWSTR>(file.utf16()), fileNameBuffer, fileNameBufferSize); size_t longNameSize = GetLongPathNameW(reinterpret_cast<LPCWSTR>(file.utf16()), fileNameBuffer, fileNameBufferSize);
if (longNameSize > 0) { if (longNameSize > 0) {
longfile = QString::fromUtf16(reinterpret_cast<const ushort *>(fileNameBuffer), OCC::Utility::convertSizeToInt(longNameSize)); longfile = QString::fromUtf16(reinterpret_cast<const ushort *>(fileNameBuffer), longNameSize);
} else { } else {
qCWarning(lcFolderWatcher) << "Error converting file name to full length, keeping original name."; qCWarning(lcFolderWatcher) << "Error converting file name to full length, keeping original name.";
} }

View File

@@ -11,7 +11,7 @@
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string notr="true">Form</string> <string>Form</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout_2"> <layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="0"> <item row="2" column="0">

View File

@@ -11,7 +11,7 @@
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string notr="true">Form</string> <string>Form</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout_6"> <layout class="QGridLayout" name="gridLayout_6">
<item row="1" column="0"> <item row="1" column="0">
@@ -107,7 +107,7 @@
<enum>QFrame::Plain</enum> <enum>QFrame::Plain</enum>
</property> </property>
<property name="text"> <property name="text">
<string notr="true">TextLabel</string> <string>TextLabel</string>
</property> </property>
<property name="textFormat"> <property name="textFormat">
<enum>Qt::RichText</enum> <enum>Qt::RichText</enum>

View File

@@ -26,7 +26,6 @@
#include "updater/updater.h" #include "updater/updater.h"
#include "updater/ocupdater.h" #include "updater/ocupdater.h"
#include "ignorelisteditor.h" #include "ignorelisteditor.h"
#include "common/utility.h"
#include "config.h" #include "config.h"
@@ -36,12 +35,6 @@
#include <QDir> #include <QDir>
#include <QScopedValueRollback> #include <QScopedValueRollback>
#define QTLEGACY (QT_VERSION < QT_VERSION_CHECK(5,9,0))
#if !(QTLEGACY)
#include <QOperatingSystemVersion>
#endif
namespace OCC { namespace OCC {
GeneralSettings::GeneralSettings(QWidget *parent) GeneralSettings::GeneralSettings(QWidget *parent)
@@ -57,13 +50,6 @@ GeneralSettings::GeneralSettings(QWidget *parent)
connect(_ui->showInExplorerNavigationPaneCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::slotShowInExplorerNavigationPane); connect(_ui->showInExplorerNavigationPaneCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::slotShowInExplorerNavigationPane);
// Rename 'Explorer' appropriately on non-Windows
#ifdef Q_OS_MAC
QString txt = _ui->showInExplorerNavigationPaneCheckBox->text();
txt.replace(QString::fromLatin1("Explorer"), QString::fromLatin1("Finder"));
_ui->showInExplorerNavigationPaneCheckBox->setText(txt);
#endif
_ui->autostartCheckBox->setChecked(Utility::hasLaunchOnStartup(Theme::instance()->appName())); _ui->autostartCheckBox->setChecked(Utility::hasLaunchOnStartup(Theme::instance()->appName()));
connect(_ui->autostartCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::slotToggleLaunchOnStartup); connect(_ui->autostartCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::slotToggleLaunchOnStartup);
@@ -77,8 +63,7 @@ GeneralSettings::GeneralSettings(QWidget *parent)
connect(_ui->legalNoticeButton, &QPushButton::clicked, this, &GeneralSettings::slotShowLegalNotice); connect(_ui->legalNoticeButton, &QPushButton::clicked, this, &GeneralSettings::slotShowLegalNotice);
loadMiscSettings(); loadMiscSettings();
// updater info now set in: customizeStyle slotUpdateInfo();
//slotUpdateInfo();
// misc // misc
connect(_ui->monoIconsCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::saveMiscSettings); connect(_ui->monoIconsCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::saveMiscSettings);
@@ -94,13 +79,9 @@ GeneralSettings::GeneralSettings(QWidget *parent)
// Hide on non-Windows, or WindowsVersion < 10. // Hide on non-Windows, or WindowsVersion < 10.
// The condition should match the default value of ConfigFile::showInExplorerNavigationPane. // The condition should match the default value of ConfigFile::showInExplorerNavigationPane.
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#if QTLEGACY if (QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS10)
if (QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS10)
#else
if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows10)
#endif
_ui->showInExplorerNavigationPaneCheckBox->setVisible(false);
#endif #endif
_ui->showInExplorerNavigationPaneCheckBox->setVisible(false);
/* Set the left contents margin of the layout to zero to make the checkboxes /* Set the left contents margin of the layout to zero to make the checkboxes
* align properly vertically , fixes bug #3758 * align properly vertically , fixes bug #3758
@@ -117,8 +98,6 @@ GeneralSettings::GeneralSettings(QWidget *parent)
// accountAdded means the wizard was finished and the wizard might change some options. // accountAdded means the wizard was finished and the wizard might change some options.
connect(AccountManager::instance(), &AccountManager::accountAdded, this, &GeneralSettings::loadMiscSettings); connect(AccountManager::instance(), &AccountManager::accountAdded, this, &GeneralSettings::loadMiscSettings);
customizeStyle();
} }
GeneralSettings::~GeneralSettings() GeneralSettings::~GeneralSettings()
@@ -159,11 +138,7 @@ void GeneralSettings::slotUpdateInfo()
connect(updater, &OCUpdater::downloadStateChanged, this, &GeneralSettings::slotUpdateInfo, Qt::UniqueConnection); connect(updater, &OCUpdater::downloadStateChanged, this, &GeneralSettings::slotUpdateInfo, Qt::UniqueConnection);
connect(_ui->restartButton, &QAbstractButton::clicked, updater, &OCUpdater::slotStartInstaller, Qt::UniqueConnection); connect(_ui->restartButton, &QAbstractButton::clicked, updater, &OCUpdater::slotStartInstaller, Qt::UniqueConnection);
connect(_ui->restartButton, &QAbstractButton::clicked, qApp, &QApplication::quit, Qt::UniqueConnection); connect(_ui->restartButton, &QAbstractButton::clicked, qApp, &QApplication::quit, Qt::UniqueConnection);
_ui->updateStateLabel->setText(updater->statusString());
QString status = updater->statusString();
Theme::replaceLinkColorStringBackgroundAware(status);
_ui->updateStateLabel->setText(status);
_ui->restartButton->setVisible(updater->downloadState() == OCUpdater::DownloadComplete); _ui->restartButton->setVisible(updater->downloadState() == OCUpdater::DownloadComplete);
} else { } else {
// can't have those infos from sparkle currently // can't have those infos from sparkle currently
@@ -209,7 +184,6 @@ void GeneralSettings::slotShowInExplorerNavigationPane(bool checked)
void GeneralSettings::slotIgnoreFilesEditor() void GeneralSettings::slotIgnoreFilesEditor()
{ {
if (_ignoreEditor.isNull()) { if (_ignoreEditor.isNull()) {
ConfigFile cfgFile;
_ignoreEditor = new IgnoreListEditor(this); _ignoreEditor = new IgnoreListEditor(this);
_ignoreEditor->setAttribute(Qt::WA_DeleteOnClose, true); _ignoreEditor->setAttribute(Qt::WA_DeleteOnClose, true);
_ignoreEditor->open(); _ignoreEditor->open();
@@ -225,20 +199,4 @@ void GeneralSettings::slotShowLegalNotice()
delete notice; delete notice;
} }
void GeneralSettings::slotStyleChanged()
{
customizeStyle();
}
void GeneralSettings::customizeStyle()
{
// setup about section
QString about = Theme::instance()->about();
Theme::replaceLinkColorStringBackgroundAware(about);
_ui->aboutLabel->setText(about);
// updater info
slotUpdateInfo();
}
} // namespace OCC } // namespace OCC

View File

@@ -39,9 +39,6 @@ public:
~GeneralSettings(); ~GeneralSettings();
QSize sizeHint() const override; QSize sizeHint() const override;
public slots:
void slotStyleChanged();
private slots: private slots:
void saveMiscSettings(); void saveMiscSettings();
void slotToggleLaunchOnStartup(bool); void slotToggleLaunchOnStartup(bool);
@@ -53,8 +50,6 @@ private slots:
void slotShowLegalNotice(); void slotShowLegalNotice();
private: private:
void customizeStyle();
Ui::GeneralSettings *_ui; Ui::GeneralSettings *_ui;
QPointer<IgnoreListEditor> _ignoreEditor; QPointer<IgnoreListEditor> _ignoreEditor;
QPointer<SyncLogDialog> _syncLogDialog; QPointer<SyncLogDialog> _syncLogDialog;

View File

@@ -11,7 +11,7 @@
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string notr="true">Form</string> <string>Form</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout_3"> <layout class="QGridLayout" name="gridLayout_3">
<item row="2" column="0"> <item row="2" column="0">

View File

@@ -1,146 +0,0 @@
/*
* Copyright (C) by Michael Schuster <michael@nextcloud.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.
*/
/****************************************************************************
**
** Based on Qt sourcecode:
** qt5/qtbase/src/widgets/dialogs/qwizard.cpp
**
** https://code.qt.io/cgit/qt/qtbase.git/tree/src/widgets/dialogs/qwizard.cpp?h=v5.13.0
**
** Original license:
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtWidgets module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "headerbanner.h"
#include <QVBoxLayout>
#include <QLabel>
#include <QPainter>
#include <QStyle>
#include <QGuiApplication>
namespace OCC {
// These fudge terms were needed a few places to obtain pixel-perfect results
const int GapBetweenLogoAndRightEdge = 5;
const int ModernHeaderTopMargin = 2;
HeaderBanner::HeaderBanner(QWidget *parent)
: QWidget(parent)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
setBackgroundRole(QPalette::Base);
titleLabel = new QLabel(this);
titleLabel->setBackgroundRole(QPalette::Base);
logoLabel = new QLabel(this);
QFont font = titleLabel->font();
font.setBold(true);
titleLabel->setFont(font);
layout = new QGridLayout(this);
layout->setContentsMargins(QMargins());
layout->setSpacing(0);
layout->setRowMinimumHeight(3, 1);
layout->setRowStretch(4, 1);
layout->setColumnStretch(2, 1);
layout->setColumnMinimumWidth(4, 2 * GapBetweenLogoAndRightEdge);
layout->setColumnMinimumWidth(6, GapBetweenLogoAndRightEdge);
layout->addWidget(titleLabel, 1, 1, 5, 1);
layout->addWidget(logoLabel, 1, 5, 5, 1);
}
void HeaderBanner::setup(const QString &title, const QPixmap &logo, const QPixmap &banner,
const Qt::TextFormat titleFormat, const QString &styleSheet)
{
QStyle *style = parentWidget()->style();
//const int layoutHorizontalSpacing = style->pixelMetric(QStyle::PM_LayoutHorizontalSpacing);
int topLevelMarginLeft = style->pixelMetric(QStyle::PM_LayoutLeftMargin, 0, parentWidget());
int topLevelMarginRight = style->pixelMetric(QStyle::PM_LayoutRightMargin, 0, parentWidget());
int topLevelMarginTop = style->pixelMetric(QStyle::PM_LayoutTopMargin, 0, parentWidget());
//int topLevelMarginBottom = style->pixelMetric(QStyle::PM_LayoutBottomMargin, 0, parentWidget());
layout->setRowMinimumHeight(0, ModernHeaderTopMargin);
layout->setRowMinimumHeight(1, topLevelMarginTop - ModernHeaderTopMargin - 1);
layout->setRowMinimumHeight(6, 3);
int minColumnWidth0 = topLevelMarginLeft + topLevelMarginRight;
int minColumnWidth1 = topLevelMarginLeft + topLevelMarginRight + 1;
layout->setColumnMinimumWidth(0, minColumnWidth0);
layout->setColumnMinimumWidth(1, minColumnWidth1);
titleLabel->setTextFormat(titleFormat);
titleLabel->setText(title);
if(!styleSheet.isEmpty())
titleLabel->setStyleSheet(styleSheet);
logoLabel->setPixmap(logo);
bannerPixmap = banner;
if (bannerPixmap.isNull()) {
QSize size = layout->totalMinimumSize();
setMinimumSize(size);
setMaximumSize(QWIDGETSIZE_MAX, size.height());
} else {
setFixedHeight(banner.height() + 2);
}
updateGeometry();
}
void HeaderBanner::paintEvent(QPaintEvent * /* event */)
{
QPainter painter(this);
painter.drawPixmap(0, 0, width(), bannerPixmap.height(), bannerPixmap);
int x = width() - 2;
int y = height() - 2;
const QPalette &pal = QGuiApplication::palette();
painter.setPen(pal.mid().color());
painter.drawLine(0, y, x, y);
painter.setPen(pal.base().color());
painter.drawPoint(x + 1, y);
painter.drawLine(0, y + 1, x + 1, y + 1);
}
} // namespace OCC

Some files were not shown because too many files have changed in this diff Show More