1
0
mirror of https://github.com/chylex/Nextcloud-Desktop.git synced 2026-04-06 16:34:18 +02:00

Compare commits

..

103 Commits

Author SHA1 Message Date
Adrian Brzezinski
5adbc01ef1 * fix for issue no. 1351 2019-11-04 22:48:42 +01:00
Sergey Zolotarev
c4a04bfd05 Don't run connection wizard when quitting the application
Signed-off-by: Sergey Zolotarev <sryze@protonmail.com>
2019-11-04 22:40:18 +01:00
Sergey Zolotarev
03711429f4 Replace isQuitting flag with disconnect()
Signed-off-by: Sergey Zolotarev <sryze@protonmail.com>
2019-11-04 22:40:07 +01:00
Michael Schuster
e3cb040c3c Update submodules for Qt 5.12.5 (qtmacgoodies)
Fetch the new submodule commits to get these fixes:
- Merge upstream: Retrieve the associated NSView more reliably: https://github.com/camilasan/qtmacgoodies/pull/1
- Bugfix for Qt 5.12.5 - Redraw the ToolBar: https://github.com/camilasan/qtmacgoodies/pull/2

Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-11-04 22:38:21 +01:00
Camila San
83a21179fa Update translation files.
Signed-off-by: Camila San <hello@camila.codes>
2019-11-04 20:54:47 +01:00
XNG
829da85aa5 Apply http2 patch from owncloud
Signed-off-by: XNG <Milokita@users.noreply.github.com>
2019-11-04 20:22:31 +01:00
Camila San
3b1ac89312 Fix remote wipe when a proxy is configured.
Signed-off-by: Camila San <hello@camila.codes>
2019-11-04 20:20:00 +01:00
Michael Schuster
57df29e7c9 Merge pull request #1582 from nextcloud/backport/1580/stable-2.6
[stable-2.6] Fix updater message: Download link instead of "use the system's updat…
2019-11-04 19:58:08 +01:00
Michael Schuster
d774067004 Fix updater message: Download link instead of "use the system's update tool"
Provide a download link to the new version instead of the confusing message that
users should use their "system's update tool to install it".

Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-11-04 18:56:20 +00:00
Camila San
728154386c Once client gets 401/403 from the server, check if remote wipe was requested.
- When the the users logs because of 401 or 403 errors, it checks if the
server requested the remote wipe. If yes, locally deletes account and folders
connected to the account and notify the server. If no, proceeds to ask the
user to login again.
- The app password is restored in the keychain.
- WIP: The change also includes a test class for RemoteWipe.

Signed-off-by: Camila San <hello@camila.codes>
2019-11-04 19:31:17 +01:00
tuxmaster5000
bc31182c63 Rename owncloud tests to nextcloud 2019-11-04 19:31:07 +01:00
Michael Schuster
a6bb84080a Merge pull request #1555 from nextcloud/backport/1554/stable-2.6
[stable-2.6] Fix duplicate items in Apps menu (a bug introduced in #1477)
2019-10-23 23:49:01 +02:00
Sergey Zolotarev
576ba7c011 Fix duplicate items in Apps menu (a bug introduced in #1477)
Signed-off-by: Sergey Zolotarev <sryze@protonmail.com>
2019-10-23 21:46:35 +00:00
Camila San
b6893aad16 Bump version to 2.6.1.
Signed-off-by: Camila San <hello@camila.codes>
2019-10-17 22:35:28 +02:00
kilian.pfeiffer
59d1624ce5 changed max GUI bandwith limits 2019-10-17 21:48:21 +02:00
Ivan Čukić
a0faf1f54d Race condition in the remote size loading logic
The quota retrieval process might not be finished by the time
the used space on the server (`_rSize`) is compared against
the locally available disk space which might end up in
a "There isn't enough free space in the local folder!" message
even if there is enough free space.

This patch updates the status after the quota has been retrieved.

It also initializes `_rSize` to `-1` so that errors like this
are easier to catch in the future.
2019-10-17 21:44:58 +02:00
Michael Schuster
caa7c845c2 fix comment typo
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 21:38:18 +02:00
Michael Schuster
e43a80d0be Fix double slashes in WebDAV URLs (account setup wizard)
Sanitize URL paths to elaminate double-slashes in the URL path string,
used for the first connection by the account setup wizard.

Example: https://cloud.example.com/remote.php/webdav//

Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 21:37:41 +02:00
Michael Schuster
f060a92563 Fix outdated link to server admin docs
Sets the target version from "15" to "latest" and removes
"index.html" because this could get obsolete in the future too.

Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 21:34:05 +02:00
Dominique Fuchs
483696261d Fixed some missing 'translatable' exclusions, added missing window titles in flow dialogs
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-10-17 21:33:34 +02:00
Dominique Fuchs
610e35ec64 Fixed unused var
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-10-17 21:22:48 +02:00
Dominique Fuchs
5fa5526ea2 Added slight svg transparency as requested in ref issue, fixed bg detection logic
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-10-17 21:22:40 +02:00
Dominique Fuchs
42d9d99a92 (Maybe) finished implementation of themed wizard buttons and accessibility refinements and thus implementation of helper fct. to retrieve themed QIcons.
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-10-17 21:22:30 +02:00
Dominique Fuchs
c3ff9ca917 [WIP] themed button implementation
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-10-17 21:22:21 +02:00
Dominique Fuchs
875f123d5b [WIP] Resource file and include changes as well as new control icons for wizard slide buttons
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-10-17 21:21:58 +02:00
Dominique Fuchs
a5f053afe4 Layout optimizations and tab access for self-hosting link
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-10-17 21:18:03 +02:00
Dominique Fuchs
63a6992f97 fix naming for slide navigation, adapted everywhere to be consistent
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-10-17 21:17:34 +02:00
Dominique Fuchs
54740378f0 Restructured layout, as the initial change were too broken (after additions through the last months)
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-10-17 21:16:51 +02:00
Dominique Fuchs
1dc443bc06 Fixed wrong resource paths
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-10-17 21:15:57 +02:00
Dominique Fuchs
e541109d7c Added newly created next/prev svg's and fixed reduntant layout parts in wizard
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-10-17 21:13:47 +02:00
Izabela Bakollari
993f124120 Add files via upload 2019-10-17 21:12:05 +02:00
Camila San
0a373ea708 Checks if exclude file is empty before creating the regular expressions.
The default file created by the application it is not empty.

Signed-off-by: Camila San <hello@camila.codes>
2019-10-17 21:03:42 +02:00
Daniel Kesselberg
0fae01495e Add server info to menu
Signed-off-by: Daniel Kesselberg <mail@danielkesselberg.de>
2019-10-17 21:03:23 +02:00
rakekniven
97867384b1 Fixed grammar
Reported at Transifex.
See https://www.transifex.com/nextcloud/nextcloud/translate/#nl/client/182396083

Signed-off-by: rakekniven <mark.ziegler@rakekniven.de>
2019-10-17 21:03:05 +02:00
Sergey Zolotarev
c54f6e83ed Prevent jumping of tray menu
Instead of adding the "Apps" menu after the apps are fetched, add it
from the start (together with other actions) but in a disabled state,
and enable it after the apps data is ready.

Signed-off-by: Sergey Zolotarev <sryze@protonmail.com>
2019-10-17 21:02:47 +02:00
Sergey Zolotarev
a9a731dfc0 Don't run connection wizard when quitting the application
Signed-off-by: Sergey Zolotarev <sryze@protonmail.com>
2019-10-17 21:02:28 +02:00
Sergey Zolotarev
d0f469bd90 Replace isQuitting flag with disconnect()
Signed-off-by: Sergey Zolotarev <sryze@protonmail.com>
2019-10-17 21:02:09 +02:00
asapelkin
374375ce3f little loops optimization
Signed-off-by: asapelkin <asapelkin0x01@ya.ru>
2019-10-17 20:55:56 +02:00
Michael Schuster
89ef03412e Pick from upstream: Update qtmacgoodies for an OSX crash fix #6930
With Qt 5.12.5 this OC crash also applies to the Nextcloud Desktop Client on macOS.

For reference please see:
- https://github.com/owncloud/client/issues/6930
- 0dc7bc3328

For the required changes in the qtmacgoodies submodule see:
https://github.com/camilasan/qtmacgoodies/pull/1

Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 20:52:42 +02:00
Nextcloud bot
07d3fe3a79 [tx-robot] updated from transifex
(cherry picked from commit 04f2bd4baa)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 20:37:56 +02:00
Nextcloud bot
24107040cc [tx-robot] updated from transifex
(cherry picked from commit 4f7d7e3601)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 20:37:49 +02:00
Nextcloud bot
9d9fc6d0bf [tx-robot] updated from transifex
(cherry picked from commit 13b2b5253e)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 20:37:41 +02:00
Nextcloud bot
51f5991f1e [tx-robot] updated from transifex
(cherry picked from commit 2121e7116e)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 20:37:32 +02:00
Javier Llorente
a8b93516cc Add sync date next to "Synchronized with local folder"
(cherry picked from commit cbc19e86fb)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 20:37:21 +02:00
Sebastian Grund
a85c228e59 issue1216: added sync-exclude entry for emacs recovery files
Signed-off-by: Sebastian Grund <grund92@gmx.de>
(cherry picked from commit a9bea53c89)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 20:37:11 +02:00
Michael Schuster
04a75eaca2 Add warning for failed chown in libsync/propagatedownload.cpp
In addition to PR 1409 generate warnings if chown fails.

See: https://github.com/nextcloud/desktop/pull/1409

Signed-off-by: Michael Schuster <michael@schuster.ms>
(cherry picked from commit 207de071f4)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 20:36:41 +02:00
Michael Schuster
2f46601396 Replace old NSI Windows setup wizard graphics
New UI resources based on current https://github.com/nextcloud/promo

New:
- Icon based on https://github.com/nextcloud/desktop/pull/1401
- Wizard image & header image (dotted background replaces old cloud background)

Signed-off-by: Michael Schuster <michael@schuster.ms>
(cherry picked from commit 819a006a17)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 20:33:28 +02:00
Dominique Fuchs
18fc6a9e0e fixed wrongly assigned pointer, didn't recognize class
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit c662ff1902)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:38:55 +02:00
Dominique Fuchs
34675e03a8 Use -Wno-gnu-zero-variadic-macro-arguments only for Clang
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit a3825080db)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:38:55 +02:00
Dominique Fuchs
4b5cf94a29 Q_UNUSED for atm unused parameters
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit a237493def)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:38:55 +02:00
Dominique Fuchs
1729e1a94c Declared Q_UNUSED for as-of-now unused parameters.
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit 3a0cd45782)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:38:55 +02:00
Dominique Fuchs
60859714ae Prevented warning regarding operator precedence - enhanced clarity by adding parentheses
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit f08cc08eb2)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:38:55 +02:00
Dominique Fuchs
b5fcfd918b removed reduntant /* within a comment
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit e3685b951c)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:38:54 +02:00
Dominique Fuchs
44176be964 Remove unnecessary argument
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit 6b04e2f77b)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:38:54 +02:00
Dominique Fuchs
3935866052 Prevent use of uninitialized folder pointer.
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit 2e8b7771b0)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:38:54 +02:00
Dominique Fuchs
1e9c45222c Added forgotten case when parsing log through gui. LockedFiles were not communicated.
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit 26e98d35e6)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:38:54 +02:00
Dominique Fuchs
adc3b1a25c initialize _modtime to prevent undefined usage
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit ab3d0141ec)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:38:54 +02:00
Dominique Fuchs
9ae0417cad Use return type to prevent warning and determine chown success. Added TODO
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit 39df36c247)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:38:54 +02:00
Dominique Fuchs
03453d6800 Removed disabling of msvc warning to prevent generating a unknown option for other compilers in turn. Now detect specifically GCC in ifdef
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit 7473cdf184)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:38:53 +02:00
Dominique Fuchs
1ac9c4ea8d Moved macro definition due to timing issues while compiling when relying on header inheritance
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit c585e81530)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:34:19 +02:00
Dominique Fuchs
986bb49a88 Conditional (based on Qt version) use of 'horizontalAdvance' to provide better UI experience. See https://doc.qt.io/qt-5/qfontmetrics-obsolete.html#width
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit c779098772)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:34:19 +02:00
Dominique Fuchs
8f39c4140e commit 222b2d did the trick. now streamlined use of https://doc.qt.io/qt-5/qtglobal.html#QT_VERSION_CHECK
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit cc07ed1ee8)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:34:18 +02:00
Dominique Fuchs
9c7903868f Further testing of cond. include of Qt library > 5.9. Utilized different macro.
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit 222b2d8645)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:34:18 +02:00
Dominique Fuchs
8ee1adf058 Fixed another logic error -> logical to bitwise OR for QTLEGACY
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit f41eeaf6ec)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:34:18 +02:00
Dominique Fuchs
27fb1fcd53 Fixed logic error in QTLEGACY macro and added forgottin #if clause for header file
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit 48097801e8)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:34:18 +02:00
Dominique Fuchs
29cc5c1e7f Added macro definition and compile-time condition to support Qt < 5.9
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit dca83aad45)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:34:18 +02:00
Dominique Fuchs
29bb76019f Indeed, DWORD is a special snowflake - only when on _WIN32
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit be7a524557)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:34:18 +02:00
Dominique Fuchs
bf6d57f327 Fixed wrongly formatted args for win32 linker flags resulting in 'unrecognized option' for all of them. Remark: /WL is for VS only, useless (and not necessary for msvc cmd)
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit 0827ff0995)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:34:17 +02:00
Dominique Fuchs
eb5ec05ef8 Fixed missing braces
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit 503b9de2a0)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:34:17 +02:00
Dominique Fuchs
c723028eae Qt: Fixed numerous deprecated calls by adapting newer ones
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit a2d47cdec4)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:34:17 +02:00
Dominique Fuchs
5d024fdf33 Added cmake preprocessor definitions when using msvc regarding the 'safe' versions of CRT functions
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit ba74c24d8f)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:34:17 +02:00
Dominique Fuchs
ed99cb297b Use existing fct for RegKeyQuery instead of redundant subroutine
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit d60a216982)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:34:17 +02:00
Dominique Fuchs
a0e794a7f1 Numoerous safe conversions implemented. Added additional Utility::convertSizeToDWORD for windows builds.
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit d6af025a46)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:34:16 +02:00
Dominique Fuchs
0761342840 Corrected namespace when calling convertSizetoUint
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit 82fa10c227)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:34:16 +02:00
Dominique Fuchs
4da9123b67 Renamed conversion function to make intention more clear. Also defaulted to 'controlled truncation' to not stupidly crash. TBD/TODO: Better handling for such things.
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit 46e0a05078)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:34:16 +02:00
Dominique Fuchs
c7158e2c7c Selectively and temporary disabled warning about unknown preprocessor declarative for msvc when using specific GCC instruction.
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit 8329de4cee)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:34:16 +02:00
Dominique Fuchs
4adc45483a Implemented Utility::convert function to convert size_t -> uint safely and on the fly. Often necessary for Qt and WIN32 functions. Using this will not generate compiler warnings of possible truncation. First call implemented in ownsql.cpp
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit b4dee67bf5)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:34:16 +02:00
Dominique Fuchs
ae0ff6b3e3 Fixed broken overloading mechanism of variadic templates. See code comment for further information.
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit 5ae3435fe6)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:34:16 +02:00
Dominique Fuchs
dc6d2e6a6d usage of UINT as iterator here because comparing with UINT retval from DragQueryFile
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit 9a256fcbfe)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:34:15 +02:00
Dominique Fuchs
5127f50d1e Removed redundant (and wrong in terms of it's value) definitions for WINVER/_WIN32_WINNT
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit 112d2bfe11)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:34:15 +02:00
Dominique Fuchs
a26f2a7359 Removed redundant (and wrong in terms of it's value) definitions for WINVER/_WIN32_WINNT
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit 69a11a7ec1)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:34:15 +02:00
Dominique Fuchs
42f1f445a9 Removed redundant (and wrong in terms of it's value) definitions for WINVER/_WIN32_WINNT
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit 3960ffea3f)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:34:15 +02:00
Dominique Fuchs
51304485c3 Updated WINVER/_WIN32_WINNT from 0x0600 to 0x0601 (e.g. Server 2008/Vista to 7) as 7 is reasonable and noted everywhere as requirement
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit 78543deee4)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 19:34:12 +02:00
Björn Bidar
63cc6edddd fix qt warning about registering a URL sheme first.
Qt recommends to register a URL scheme before installing it.
I don't know the impact of the not registering before instaling but I
think the change is pretty harmles.

See:
https://doc.qt.io/qt-5/qwebengineurlscheme.html#registerScheme
Signed-off-by: Björn Bidar <theodorstormgrade@gmail.com>
(cherry picked from commit cea0d519a4)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 18:08:39 +02:00
Dominique Fuchs
58abebe9ac Fixed e2e key transmission issue after generation (forgotten content type on sendrequest())
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
(cherry picked from commit a35b346e62)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-17 17:33:41 +02:00
Michael Schuster
1182ae9e26 Merge pull request #1515 from nextcloud/qt5-mac-5.12-prepare
Add new "styles" plugin to macOS deployment script for Qt 5.12.5
2019-10-15 23:58:26 +02:00
Michael Schuster
3407174c2f Add new "styles" plugin to macOS deployment script for Qt 5.12.5
Qt 5.12 needs this library to use the correct style for Light / Dark Mode:

  styles/libqmacstyle.dylib

The interface looks like from the 1990's without this library ;-)

Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-14 02:01:56 +02:00
Michael Schuster
913894eaa5 Merge pull request #1496 from nextcloud/backport/1495/stable-2.6
[stable-2.6] Add a 'Content-Length: 0' header to initial POST requests
2019-10-10 08:11:43 +02:00
Michael Schuster
db91552578 Add a 'Content-Length: 0' header to initial POST requests
The webserver lighttpd rejected POST requests without a Content-length
header with "411 Length Required".

See: https://github.com/nextcloud/desktop/issues/1473

Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-10 06:09:59 +00:00
Camila San
286e45bafe Bump version to 2.6.0
Signed-off-by: Camila San <hello@camila.codes>
(cherry picked from commit e0b32c19e4)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-05 15:13:59 +02:00
Michael Schuster
aa1bb470e6 fix comment typo
Signed-off-by: Michael Schuster <michael@schuster.ms>
(cherry picked from commit 905c1532fe)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-05 14:56:48 +02:00
Michael Schuster
3be9adde4b Fix double slashes in WebDAV URLs (account setup wizard)
Sanitize URL paths to elaminate double-slashes in the URL path string,
used for the first connection by the account setup wizard.

Example: https://cloud.example.com/remote.php/webdav//

Signed-off-by: Michael Schuster <michael@schuster.ms>
(cherry picked from commit 67107a4f5d)
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-10-05 14:56:15 +02:00
István Váradi
41d97abd08 Merge pull request #1475 from ivaradi/stable-2.6
Remove kdelibs5-dev from the build dependencies for Eoan
2019-10-05 09:33:00 +02:00
István Váradi
6bc232c9b4 Remove kdelibs5-dev from the build dependencies for Eoan
Signed-off-by: István Váradi <ivaradi@varadiistvan.hu>
2019-10-05 09:31:55 +02:00
István Váradi
d4a0be92ae Merge pull request #1466 from ivaradi/stable-2.6.0
Trigger builds for tagging on the 2.6.0 branch
2019-10-04 20:19:38 +02:00
István Váradi
75bf41fba1 Trigger builds for tagging on the 2.6.0 branch
Signed-off-by: István Váradi <ivaradi@varadiistvan.hu>
2019-10-02 19:18:13 +02:00
Camila San
a2bfd5039c Revert "Fix White Window issue on Windows after Qt 5.12.4 upgrade"
This reverts commit aeba2e4de6.
2019-09-27 15:04:14 +02:00
Camila San
a9ee7472b9 Improve wording of the context menu in the file manager extension.
'Share...' -> 'Share options'
'private link' -> 'internal link'
Removes 'to clipboard' from 'copy link' options.

Signed-off-by: Camila San <hello@camila.codes>
2019-09-26 13:24:10 +02:00
Michael Schuster
211d6cb162 UI improvement: Message box: Delete / Keep all files
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-09-26 12:05:34 +02:00
Mariusz Wasak
501c353291 Fix for #1382 "linux client crashes for no discernable reason"
There in no "return" in
PropagateUploadFileCommon::slotStartUpload in if (prevModtime != _item-
>_modtime) {... }

There is possibility that
PropagateItemJob::done(status, errorString)
maybe called two times from PropagateUploadFileCommon::slotStartUpload
1. in if (prevModtime != _item->_modtime) {... }
2. in if (fileIsStillChanging(*_item)) {..}
if changes in files are frequent the second call is possible.

This two calls has effect in PropagatorCompositeJob::slotSubJobFinished
and job is removed two times in _runningJobs.remove(i);
(the second time with argumetnt -1 (because first call removed job).

This return was removed in commit
efc039863b - by accident I think.

Good simulation is to synchronize firefox profile with frequent page
refresh.

Signed-off-by: Mariusz Wasak <mawasak@gmail.com>
2019-09-26 12:03:20 +02:00
Michael Schuster
aeba2e4de6 Fix White Window issue on Windows after Qt 5.12.4 upgrade
Qt 5.12.4 seems to introduce a new bug on Windows, causing the settings window
to not be redrawn when re-opening it, for example by clicking at the tray icon.

As a workaround this fix starts a 100 ms timer to be fired once upon
QDialog::showEvent is called.

Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-09-26 11:29:49 +02:00
Camila Ayres
2f9f84c1f2 Merge pull request #1447 from nextcloud/backport/1438/stable-2.6
[stable-2.6] Changes wording in the share context menu.
2019-09-26 11:16:17 +02:00
Camila San
33646b1775 Changes wording in the share context menu.
Instead of only Nextcloud it says "Share via Nextcloud".

Signed-off-by: Camila San <hello@camila.codes>
2019-09-26 09:12:28 +00:00
238 changed files with 31451 additions and 29718 deletions

View File

@@ -22,11 +22,11 @@ steps:
source /opt/qt57/bin/qt57-env.sh &&
mkdir build &&
cd build &&
cmake -D NO_SHIBBOLETH=1 -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=1 -DSANITIZE_ADDRESS=ON ../ &&
cmake -D NO_SHIBBOLETH=1 -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=1 ../ &&
make &&
useradd -m -s /bin/bash test &&
chown -R test:test . &&
su -c 'ASAN_OPTIONS=detect_leaks=0 ctest --output-on-failure' test"
su -c 'ctest --output-on-failure' test"
trigger:
branch:
- master
@@ -59,11 +59,11 @@ steps:
source /opt/qt58/bin/qt58-env.sh &&
mkdir build &&
cd build &&
cmake -D NO_SHIBBOLETH=1 -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=1 -DSANITIZE_ADDRESS=ON ../ &&
cmake -D NO_SHIBBOLETH=1 -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=1 ../ &&
make &&
useradd -m -s /bin/bash test &&
chown -R test:test . &&
su -c 'ASAN_OPTIONS=detect_leaks=0 ctest --output-on-failure' test"
su -c 'ctest --output-on-failure' test"
trigger:
branch:
- master
@@ -96,11 +96,11 @@ steps:
source /opt/qt59/bin/qt59-env.sh &&
mkdir build &&
cd build &&
cmake -D NO_SHIBBOLETH=1 -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=1 -DSANITIZE_ADDRESS=ON ../ &&
cmake -D NO_SHIBBOLETH=1 -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=1 ../ &&
make &&
useradd -m -s /bin/bash test &&
chown -R test:test . &&
su -c 'ASAN_OPTIONS=detect_leaks=0 ctest --output-on-failure' test"
su -c 'ctest --output-on-failure' test"
trigger:
branch:
- master
@@ -137,11 +137,11 @@ steps:
source /opt/qt510/bin/qt510-env.sh &&
mkdir build &&
cd build &&
cmake -D NO_SHIBBOLETH=1 -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=1 -DSANITIZE_ADDRESS=ON ../ &&
cmake -D NO_SHIBBOLETH=1 -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=1 ../ &&
make &&
useradd -m -s /bin/bash test &&
chown -R test:test . &&
su -c 'ASAN_OPTIONS=detect_leaks=0 ctest --output-on-failure' test"
su -c 'ctest --output-on-failure' test"
trigger:
branch:
- master
@@ -178,11 +178,11 @@ steps:
source /opt/qt511/bin/qt511-env.sh &&
mkdir build &&
cd build &&
cmake -D NO_SHIBBOLETH=1 -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=1 -DSANITIZE_ADDRESS=ON ../ &&
cmake -D NO_SHIBBOLETH=1 -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=1 ../ &&
make &&
useradd -m -s /bin/bash test &&
chown -R test:test . &&
su -c 'ASAN_OPTIONS=detect_leaks=0 ctest --output-on-failure' test"
su -c 'ctest --output-on-failure' test"
trigger:
branch:
- master
@@ -219,11 +219,11 @@ steps:
source /opt/qt511/bin/qt511-env.sh &&
mkdir build &&
cd build &&
cmake -D NO_SHIBBOLETH=1 -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=1 -DSANITIZE_ADDRESS=ON ../ &&
cmake -D NO_SHIBBOLETH=1 -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=1 ../ &&
make &&
useradd -m -s /bin/bash test &&
chown -R test:test . &&
su -c 'ASAN_OPTIONS=detect_leaks=0 ctest --output-on-failure' test"
su -c 'ctest --output-on-failure' test"
trigger:
branch:
- master
@@ -237,17 +237,13 @@ name: qt-5.12
steps:
- name: build and test
image: nextcloudci/client-5.12:client-5.12-5
image: nextcloudci/client-5.12:client-5.12-2
commands:
# Install QtKeyChain
- /bin/bash -c "
export CC=gcc-7 &&
export CXX=g++-7 &&
export QT_BASE_DIR=/opt/qt5.12.5 &&
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 &&
source /opt/qt512/bin/qt512-env.sh &&
cd /tmp &&
git clone https://github.com/frankosterfeld/qtkeychain.git &&
cd qtkeychain &&
@@ -261,18 +257,14 @@ steps:
- /bin/bash -c "
export CC=gcc-7 &&
export CXX=g++-7 &&
export QT_BASE_DIR=/opt/qt5.12.5 &&
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 &&
source /opt/qt512/bin/qt512-env.sh &&
mkdir build &&
cd build &&
cmake -D NO_SHIBBOLETH=1 -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=1 -DSANITIZE_ADDRESS=ON ../ &&
cmake -D NO_SHIBBOLETH=1 -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=1 ../ &&
make &&
useradd -m -s /bin/bash test &&
chown -R test:test . &&
su -c 'ASAN_OPTIONS=detect_leaks=0 ctest --output-on-failure' test"
su -c 'ctest --output-on-failure' test"
trigger:
branch:
- master
@@ -286,17 +278,13 @@ name: qt-5.12-clang
steps:
- name: build and test
image: nextcloudci/client-5.12:client-5.12-5
image: nextcloudci/client-5.12:client-5.12-2
commands:
# Install QtKeyChain
- /bin/bash -c "
export CC=clang-6.0 &&
export CXX=clang++-6.0 &&
export QT_BASE_DIR=/opt/qt5.12.5 &&
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 &&
source /opt/qt512/bin/qt512-env.sh &&
cd /tmp &&
git clone https://github.com/frankosterfeld/qtkeychain.git &&
cd qtkeychain &&
@@ -310,18 +298,14 @@ steps:
- /bin/bash -c "
export CC=clang-6.0 &&
export CXX=clang++-6.0 &&
export QT_BASE_DIR=/opt/qt5.12.5 &&
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 &&
source /opt/qt512/bin/qt512-env.sh &&
mkdir build &&
cd build &&
cmake -D NO_SHIBBOLETH=1 -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=1 -DSANITIZE_ADDRESS=ON ../ &&
cmake -D NO_SHIBBOLETH=1 -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=1 ../ &&
make &&
useradd -m -s /bin/bash test &&
chown -R test:test . &&
su -c 'ASAN_OPTIONS=detect_leaks=0 ctest --output-on-failure' test"
su -c 'ctest --output-on-failure' test"
trigger:
branch:
- master
@@ -335,10 +319,9 @@ name: AppImage
steps:
- name: build
image: nextcloudci/client-5.12:client-5.12-5
image: nextcloudci/client-5.12:client-5.12-2
commands:
- /bin/bash -c "./admin/linux/build-appimage.sh"
- /bin/bash -c "./admin/linux/upload-appimage.sh"
trigger:
branch:
- master
@@ -361,10 +344,9 @@ steps:
from_secret: DEBIAN_SECRET_IV
trigger:
branch:
- master
- stable-2.6
event:
- pull_request
- push
- tag
---
kind: pipeline
name: Documentation

3
.github/FUNDING.yml vendored
View File

@@ -1,3 +0,0 @@
# You can add one username per supported platform and one custom link
custom: https://www.bountysource.com/teams/nextcloud/issues?tracker_ids=74294474

67
.github/stale.yml vendored
View File

@@ -1,67 +0,0 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 28
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 14
# Issues with these labels will never be considered stale
exemptLabels:
- 1. to develop
- 2. to review
- 3. to release
- 4. to test
- accessibility
- backport-request
- bug
- design
- enhancement
- epic
- discussion
- documentation
- overview
- good first issue
- feature-request
- feature: :arrows_counterclockwise: sync engine
- feature: :busts_in_silhouette: sharing
- feature: :cloud: system tray
- feature: :gear: settings
- feature: :inbox_tray: install and update
- feature: :key: authentication
- feature: :lock: end to end encryption
- feature: :memo: versions
- feature: :minidisc: external storage
- feature: :minidisc: virtual drive
- feature: :new: versions
- feature: :tongue: language l10n and translations
- feature: :wheelchair: accessibility
- feature: :white_square_button: nextcloudcmd
- feature: :zap: activity and :bell: notification
- good first issue
- help wanted
- high
- integration
- low
- medium
- needs info
- os: :apple: macOS
- os: :door: Windows
- os: :penguin: Linux
- os: :smiling_imp: FreeBSD
- overview
- package: appimage
- package: debian
- package: snap
- papercut
- regression
- security
- server
- spec
- technical debt
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This request did not receive an update in the last 4 weeks.
Please take a look again and update the issue with new details,
otherwise the issue will be automatically closed in 2 weeks. Thank you!
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

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"]
path = binary
url = git://github.com/owncloud/owncloud-client-binary.git

View File

@@ -198,7 +198,7 @@ X-GNOME-Autostart-Delay=3
# Translations
Icon[cs_CZ]=@APPLICATION_ICON_NAME@
Icon[cs_CZ]=@NAZEV_IKONY_APLIKACE@
Name[cs_CZ]=@APPLICATION_NAME@ synchronizační klient pro desktop
Comment[cs_CZ]=@APPLICATION_NAME@ synchronizační klient pro desktop
GenericName[cs_CZ]=Synchronizace složek

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
Icon[el]=@APPLICATION_ICON_NAME@
Name[el]=@APPLICATION_NAME@ πρόγραμμα συγχρονισμού
Comment[el]=@APPLICATION_NAME@ πρόγραμμα συγχρονισμού
GenericName[el]=Συγχρονισμός φακέλου

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[es_AR]=@APPLICATION_ICON_NAME@
Name[es_AR]=@APPLICATION_NAME@ cliente de sincronización de escritorio
Comment[es_AR]=@APPLICATION_NAME@ cliente de sincronización de escritorio
GenericName[es_AR]=Sincronización de carpetas

View File

@@ -201,4 +201,4 @@ X-GNOME-Autostart-Delay=3
Icon[ro]=@APPLICATION_ICON_NAME@
Name[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@
Name[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
Icon[sl]=@APPLICATION_ICON_NAME@
Name[sl]=@APPLICATION_NAME@ program za usklajevanje
Comment[sl]=@APPLICATION_NAME@ program za usklajevanje
Name[sl]=@APPLICATION_NAME@ odjemalec za usklajevanje
Comment[sl]=@APPLICATION_NAME@ odjemalec za usklajevanje
GenericName[sl]=Usklajevanje map

View File

@@ -199,6 +199,4 @@ X-GNOME-Autostart-Delay=3
# Translations
Icon[sv]=@APPLICATION_ICON_NAME@
Name[sv]=@APPLICATION_NAME@ desktopssynkklient
Comment[sv]=@APPLICATION_NAME@ desktopssynkroniseringsklient
GenericName[sv]=Mappsynkronisering

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

@@ -219,12 +219,6 @@ if (APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
endif()
option(SANITIZE_ADDRESS "Enable address sanitizer in tests" OFF)
if (SANITIZE_ADDRESS)
include(SanitizerFlags)
enable_sanitizer()
endif ()
# Handle Translations, pick all client_* files from trans directory.
file( GLOB TRANS_FILES ${CMAKE_SOURCE_DIR}/translations/client_*.ts)
set(TRANSLATIONS ${TRANS_FILES})

View File

@@ -1,6 +1,3 @@
Will be tracked going forward here:
https://github.com/nextcloud/desktop/releases
2.5 Series ChangeLog
====================

View File

@@ -7,12 +7,11 @@ set( APPLICATION_UPDATE_URL "https://updates.nextcloud.org/client/" CACHE STRING
set( APPLICATION_HELP_URL "" CACHE STRING "URL for the help menu" )
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_REV_DOMAIN "com.nextcloud.desktopclient" )
set( LINUX_PACKAGE_SHORTNAME "nextcloud" )
set( LINUX_APPLICATION_ID "${APPLICATION_REV_DOMAIN}.${LINUX_PACKAGE_SHORTNAME}")
set( THEME_CLASS "NextcloudTheme" )
set( APPLICATION_REV_DOMAIN "com.nextcloud.desktopclient" )
set( WIN_SETUP_BITMAP_PATH "${CMAKE_SOURCE_DIR}/admin/win/nsi" )
set( MAC_INSTALLER_BACKGROUND_FILE "${CMAKE_SOURCE_DIR}/admin/osx/installer-background.png" CACHE STRING "The MacOSX installer background image")

View File

@@ -1,7 +1,7 @@
set( MIRALL_VERSION_MAJOR 2 )
set( MIRALL_VERSION_MINOR 7 )
set( MIRALL_VERSION_PATCH 0 )
set( MIRALL_VERSION_YEAR 2020 )
set( MIRALL_VERSION_MINOR 6 )
set( MIRALL_VERSION_PATCH 1 )
set( MIRALL_VERSION_YEAR 2019 )
set( MIRALL_SOVERSION 0 )
if ( NOT DEFINED MIRALL_VERSION_SUFFIX )

View File

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

View File

@@ -6,26 +6,23 @@ mkdir /app
mkdir /build
#Set Qt-5.12
export QT_BASE_DIR=/opt/qt5.12.5
export QT_BASE_DIR=/opt/qt512
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:$LD_LIBRARY_PATH
export PKG_CONFIG_PATH=$QT_BASE_DIR/lib/pkgconfig:$PKG_CONFIG_PATH
#Set APPID for .desktop file processing
export LINUX_APPLICATION_ID=com.nextcloud.desktopclient.nextcloud
#set defaults
export SUFFIX=${DRONE_PULL_REQUEST:=master}
if [ $SUFFIX != "master" ]; then
SUFFIX="PR-$SUFFIX"
fi
#QtKeyChain master
#QtKeyChain 0.9.1
cd /build
git clone https://github.com/frankosterfeld/qtkeychain.git
cd qtkeychain
git checkout master
git checkout v0.9.1
mkdir build
cd build
cmake -D CMAKE_INSTALL_PREFIX=/usr ../
@@ -65,12 +62,11 @@ rm -rf ./usr/share/caja-python/
rm -rf ./usr/share/nautilus-python/
rm -rf ./usr/share/nemo-python/
# Move sync exclude to right location
# Move sync exlucde to right location
mv ./etc/Nextcloud/sync-exclude.lst ./usr/bin/
rm -rf ./etc
DESKTOP_FILE=/app/usr/share/applications/${LINUX_APPLICATION_ID}.desktop
sed -i -e 's|Icon=nextcloud|Icon=Nextcloud|g' ${DESKTOP_FILE} # Bug in desktop file?
sed -i -e 's|Icon=nextcloud|Icon=Nextcloud|g' usr/share/applications/nextcloud.desktop # Bug in desktop file?
cp ./usr/share/icons/hicolor/512x512/apps/Nextcloud.png . # Workaround for linuxeployqt bug, FIXME
@@ -91,12 +87,17 @@ chmod a+x linuxdeployqt*.AppImage
rm ./linuxdeployqt-continuous-x86_64.AppImage
unset QTDIR; unset QT_PLUGIN_PATH ; unset LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/app/usr/lib/
./squashfs-root/AppRun ${DESKTOP_FILE} -bundle-non-qt-libs -qmldir=$DRONE_WORKSPACE/src/gui
./squashfs-root/AppRun /app/usr/share/applications/nextcloud.desktop -bundle-non-qt-libs
# Set origin
./squashfs-root/usr/bin/patchelf --set-rpath '$ORIGIN/' /app/usr/lib/libnextcloudsync.so.0
# 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
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

@@ -9,8 +9,6 @@ Build-Depends: cmake,
extra-cmake-modules (>= 5.16),
libkf5kio-dev,
libcmocka-dev,
libcloudproviders-dev,
libdbus-1-dev,
libhttp-dav-perl,
libinotify-dev [kfreebsd-any],
libqt5svg5-dev,

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),
kdelibs5-dev,
kio-dev,
libavcodec58,
libcmocka-dev,
libcloudproviders-dev,
libdbus-1-dev,
libhttp-dav-perl,
libinotify-dev [kfreebsd-any],
libqt5webkit5-dev,

View File

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

View File

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

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
echo "Will try to sign the installer"
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"
popd
else

View File

@@ -1,8 +1,16 @@
<RCC>
<qresource prefix="/client">
<file>resources/dialog-close.png</file>
<file>resources/dialog-ok.png</file>
<file>resources/dialog-cancel.png</file>
<file>resources/folder-sync.png</file>
<file>resources/folder-sync@2x.png</file>
<file>resources/task-ongoing.png</file>
<file>resources/view-refresh.png</file>
<file>resources/warning.png</file>
<file>resources/warning@2x.png</file>
<file>resources/settings.png</file>
<file>resources/settings@2x.png</file>
<file>resources/activity.svg</file>
<file>resources/activity.png</file>
<file>resources/activity@2x.png</file>
<file>resources/network.png</file>
@@ -12,13 +20,13 @@
<file>resources/lock-https.png</file>
<file>resources/lock-https@2x.png</file>
<file>resources/account.png</file>
<file>resources/account.svg</file>
<file>resources/more.svg</file>
<file>resources/delete.png</file>
<file>resources/close.svg</file>
<file>resources/bell.svg</file>
<file>resources/link.svg</file>
<file>resources/files.svg</file>
<file>resources/folder-grey.png</file>
<file>resources/state-error.svg</file>
<file>resources/state-warning.svg</file>
<file>resources/folder.svg</file>
@@ -30,14 +38,7 @@
<file>resources/copy.svg</file>
<file>resources/state-sync.svg</file>
<file>resources/add.png</file>
<file>resources/add-color.svg</file>
<file>resources/state-info.svg</file>
<file>resources/change.svg</file>
<file>resources/delete-color.svg</file>
</qresource>
<qresource prefix="/"/>
<qresource prefix="/qml">
<file>src/gui/tray/Window.qml</file>
<file>src/gui/tray/UserLine.qml</file>
</qresource>
</RCC>

View File

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

View File

@@ -1,17 +0,0 @@
# Enable address sanitizer (gcc/clang only)
macro(ENABLE_SANITIZER)
if (NOT CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
message(FATAL_ERROR "Sanitizer supported only for gcc/clang")
endif()
set(SANITIZER_FLAGS "-fsanitize=address -fsanitize=leak -g")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SANITIZER_FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SANITIZER_FLAGS}")
set(LINKER_FLAGS "-fsanitize=address,undefined -fuse-ld=gold")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LINKER_FLAGS}")
endmacro()

View File

@@ -8,12 +8,12 @@
#cmakedefine CRASHREPORTER_EXECUTABLE "@CRASHREPORTER_EXECUTABLE@"
#define SOCKETAPI_TEAM_IDENTIFIER_PREFIX "@SOCKETAPI_TEAM_IDENTIFIER_PREFIX@"
#cmakedefine APPLICATION_DOMAIN @APPLICATION_DOMAIN@
#cmakedefine THEME_CLASS @THEME_CLASS@
#cmakedefine THEME_INCLUDE @THEME_INCLUDE@
#cmakedefine APPLICATION_NAME "@APPLICATION_NAME@"
#cmakedefine APPLICATION_VENDOR "@APPLICATION_VENDOR@"
#cmakedefine APPLICATION_DOMAIN "@APPLICATION_DOMAIN@"
#cmakedefine APPLICATION_REV_DOMAIN "@APPLICATION_REV_DOMAIN@"
#cmakedefine APPLICATION_SHORTNAME "@APPLICATION_SHORTNAME@"
#cmakedefine APPLICATION_EXECUTABLE "@APPLICATION_EXECUTABLE@"
@@ -21,7 +21,6 @@
#cmakedefine APPLICATION_HELP_URL "@APPLICATION_HELP_URL@"
#cmakedefine APPLICATION_ICON_NAME "@APPLICATION_ICON_NAME@"
#cmakedefine APPLICATION_SERVER_URL "@APPLICATION_SERVER_URL@"
#cmakedefine LINUX_APPLICATION_ID "@LINUX_APPLICATION_ID@"
#cmakedefine APPLICATION_WIZARD_HEADER_BACKGROUND_COLOR "@APPLICATION_WIZARD_HEADER_BACKGROUND_COLOR@"
#cmakedefine APPLICATION_WIZARD_HEADER_TITLE_COLOR "@APPLICATION_WIZARD_HEADER_TITLE_COLOR@"
#cmakedefine APPLICATION_WIZARD_USE_CUSTOM_LOGO "@APPLICATION_WIZARD_USE_CUSTOM_LOGO@"

View File

@@ -13,19 +13,11 @@ desktop client.
These instructions are updated to work with version |version| of the Nextcloud Client.
You have two possibilities to clone the repo.
Getting Source Code
-------------------
First option is As [remote URL](https://help.github.com/en/articles/which-remote-url-should-i-use) you can choose between cloning with HTTPS URL's, which is recommended or cloning with SSH URL's.
[https://github.com/nextcloud/desktop.git](https://github.com/nextcloud/desktop.git)
When you don't have SSH key added to your GitHub account, than use HTTPS.
When you no part of the nextcloud organisation, clone with HTTPS:
```
$ git clone git@github.com:nextcloud/desktop.git
```
The :ref:`generic-build-instructions` pull the latest code directly from
GitHub, and work on Linux, macOS, and Windows.
macOS
-----
@@ -55,7 +47,7 @@ To set up your build environment for development using HomeBrew_:
5. Install a Qt5 version with qtwebkit support::
brew install qt5
brew install qt5 --with-qtwebkit
6. Install any missing dependencies::
@@ -75,23 +67,10 @@ To set up your build environment for development using HomeBrew_:
10. Install the Packages_ package creation tool.
11. Enable git submodules:
```
$ cd desktop
$ git submodule init
$ git submodule update
```
12. Generate the build files:
```
$ cd build
$ cmake .. -DCMAKE_INSTALL_PREFIX=~/nextcloud-desktop-client -DCMAKE_BUILD_TYPE=Debug -DNO_SHIBBOLETH=1
```
13. Compile and install:
```
$ make install
```
11. In the build directory, run ``admin/osx/create_mac.sh <build_dir> <install_dir>``.
If you have a developer signing certificate, you can specify
its Common Name as a third parameter (use quotes) to have the package
signed automatically.
.. note:: Contrary to earlier versions, Nextcloud 1.7 and later are packaged
as a ``pkg`` installer. Do not call "make package" at any time when

View File

@@ -27,7 +27,7 @@ download page.
System Requirements
----------------------------------
- Windows 8.1+
- Windows 7+
- macOS 10.7+ (**64-bit only**)
- CentOS 6 & 7 (64-bit only)
- Debian 8.0 & 9.0

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" version="1.1" viewbox="0 0 16 16"><path fill="#00d400" d="M9.02 13.98h-2v-5h-5v-2h5v-5h2v5l5-.028V8.98h-5z"/></svg>

Before

Width:  |  Height:  |  Size: 179 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" version="1.1" height="16"><path d="m8 2c-2.142 0-4.125 1.145-5.196 3l1.948 1.125c0.671-1.162 1.906-1.875 3.2476-1.875 1.1906 0 2.297 0.56157 3 1.5l-1.5 1.5h4.5v-4.5l-1.406 1.406c-1.129-1.348-2.802-2.1563-4.594-2.1563z"/><path d="m2 8.75v4.5l1.408-1.41c1.116 1.334 2.817 2.145 4.592 2.16 2.16 0.01827 4.116-1.132 5.196-3.002l-1.948-1.125c-0.677 1.171-1.9005 1.886-3.248 1.875-1.18-0.01-2.3047-0.572-3-1.5l1.5-1.5z"/></svg>

Before

Width:  |  Height:  |  Size: 493 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" version="1.1" viewBox="0 0 16 16"><path d="m3.0503 4.4645 3.5355 3.5355-3.5355 3.536 1.4142 1.414 3.5355-3.5358 3.536 3.5358 1.414-1.414-3.5358-3.536 3.5358-3.5355-1.414-1.4142-3.536 3.5355-3.5355-3.5355-1.4142 1.4142z" fill="#d40000"/></svg>

Before

Width:  |  Height:  |  Size: 306 B

BIN
resources/dialog-cancel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
resources/dialog-close.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
resources/dialog-ok.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
resources/folder-grey.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 668 B

BIN
resources/folder-sync.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

BIN
resources/task-ongoing.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
resources/view-refresh.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
resources/warning.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 596 B

BIN
resources/warning@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

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

View File

@@ -35,11 +35,11 @@ public:
QString contextMenuTitle() const
{
return _strings.value("CONTEXT_MENU_TITLE", "Nextcloud");
return _strings.value("CONTEXT_MENU_TITLE", "ownCloud");
}
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"]; }

1
src/3rdparty/qtmacgoodies vendored Submodule

View File

@@ -29,11 +29,6 @@ if(NOT MSVC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_FORTIFY_SOURCE=2")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_FORTIFY_SOURCE=2")
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()
if(WIN32)

View File

@@ -499,7 +499,7 @@ restart_sync:
}
Cmd cmd;
QString dbPath = options.source_dir + SyncJournalDb::makeDbName(credentialFreeUrl, folder, user);
QString dbPath = options.source_dir + SyncJournalDb::makeDbName(options.source_dir, credentialFreeUrl, folder, user);
SyncJournalDb db(dbPath);
if (!selectiveSyncList.empty()) {

View File

@@ -23,7 +23,6 @@
#include <QElapsedTimer>
#include <QUrl>
#include <QDir>
#include <QStandardPaths>
#include <sqlite3.h>
#include "common/syncjournaldb.h"
@@ -103,15 +102,11 @@ SyncJournalDb::SyncJournalDb(const QString &dbFilePath, QObject *parent)
}
}
QString SyncJournalDb::makeDbName(const QUrl &remoteUrl,
QString SyncJournalDb::makeDbName(const QString &localPath,
const QUrl &remoteUrl,
const QString &remotePath,
const QString &user)
{
const QString dbPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
if (!QDir(dbPath).exists()) {
QDir().mkdir(dbPath);
}
QString journalPath = QLatin1String("._sync_");
QString key = QString::fromUtf8("%1@%2:%3").arg(user, remoteUrl.toString(), remotePath);
@@ -120,16 +115,17 @@ QString SyncJournalDb::makeDbName(const QUrl &remoteUrl,
journalPath.append(ba.left(6).toHex());
journalPath.append(".db");
journalPath = dbPath + QLatin1Char('/') + journalPath;
// If the journal doesn't exist and we can't create a file
// at that location, try again with a journal name that doesn't
// have the ._ prefix.
//
// The disadvantage of that filename is that it will only be ignored
// by client versions >2.3.2.
//
// See #5633: "._*" is often forbidden on samba shared folders.
// If it exists already, the path is clearly usable
QFile file(QDir(dbPath).filePath(journalPath));
QFile file(QDir(localPath).filePath(journalPath));
if (file.exists()) {
return journalPath;
}
@@ -144,7 +140,7 @@ QString SyncJournalDb::makeDbName(const QUrl &remoteUrl,
// Can we create it if we drop the underscore?
QString alternateJournalPath = journalPath.mid(2).prepend(".");
QFile file2(QDir(dbPath).filePath(alternateJournalPath));
QFile file2(QDir(localPath).filePath(alternateJournalPath));
if (file2.open(QIODevice::ReadWrite)) {
// The alternative worked, use it
qCInfo(lcDb) << "Using alternate database path" << alternateJournalPath;

View File

@@ -46,7 +46,8 @@ public:
virtual ~SyncJournalDb();
/// Create a journal path for a specific configuration
static QString makeDbName(const QUrl &remoteUrl,
static QString makeDbName(const QString &localPath,
const QUrl &remoteUrl,
const QString &remotePath,
const QString &user);

View File

@@ -255,7 +255,7 @@ void Utility::usleep(int usec)
}
// This can be overriden from the tests
OCSYNC_EXPORT bool fsCasePreserving_override = []() -> bool {
OCSYNC_EXPORT bool fsCasePreserving_override = []()-> bool {
QByteArray env = qgetenv("OWNCLOUD_TEST_CASE_PRESERVING");
if (!env.isEmpty())
return env.toInt();
@@ -362,12 +362,12 @@ QString Utility::fileNameForGuiUse(const QString &fName)
QByteArray Utility::normalizeEtag(QByteArray etag)
{
/* strip "XXXX-gzip" */
if (etag.startsWith('"') && etag.endsWith("-gzip\"")) {
if(etag.startsWith('"') && etag.endsWith("-gzip\"")) {
etag.chop(6);
etag.remove(0, 1);
}
/* strip trailing -gzip */
if (etag.endsWith("-gzip")) {
if(etag.endsWith("-gzip")) {
etag.chop(5);
}
/* strip normal quotes */
@@ -400,7 +400,7 @@ void Utility::crash()
// without compiler warnings about possible truncation
uint Utility::convertSizeToUint(size_t &convertVar)
{
if (convertVar > UINT_MAX) {
if( convertVar > UINT_MAX ) {
//throw std::bad_cast();
convertVar = UINT_MAX; // intentionally default to wrong value here to not crash: exception handling TBD
}
@@ -409,7 +409,7 @@ uint Utility::convertSizeToUint(size_t &convertVar)
uint Utility::convertSizeToInt(size_t &convertVar)
{
if (convertVar > INT_MAX) {
if( convertVar > INT_MAX ) {
//throw std::bad_cast();
convertVar = INT_MAX; // intentionally default to wrong value here to not crash: exception handling TBD
}
@@ -465,7 +465,7 @@ QString Utility::timeAgoInWords(const QDateTime &dt, const QDateTime &from)
if (floor(secs / 3600.0) > 0) {
int hours = floor(secs / 3600.0);
if (hours == 1) {
if(hours == 1){
return (QObject::tr("%n hour ago", "", hours));
} else {
return (QObject::tr("%n hours ago", "", hours));
@@ -480,7 +480,7 @@ QString Utility::timeAgoInWords(const QDateTime &dt, const QDateTime &from)
return QObject::tr("Less than a minute ago");
}
} else if (minutes == 1) {
} else if(minutes == 1){
return (QObject::tr("%n minute ago", "", minutes));
} else {
return (QObject::tr("%n minutes ago", "", minutes));

View File

@@ -20,7 +20,6 @@
#ifndef UTILITY_H
#define UTILITY_H
#include "ocsynclib.h"
#include <QString>
#include <QByteArray>
@@ -30,7 +29,6 @@
#include <QMap>
#include <QUrl>
#include <QUrlQuery>
#include <QtQuick/QQuickImageProvider>
#include <functional>
#include <memory>

View File

@@ -47,18 +47,14 @@ QString getUserAutostartDir_private()
bool hasLaunchOnStartup_private(const QString &appName)
{
QString desktopFileLocation = getUserAutostartDir_private()
+ QLatin1String(LINUX_APPLICATION_ID)
+ QLatin1String(".desktop");
QString desktopFileLocation = getUserAutostartDir_private() + appName + QLatin1String(".desktop");
return QFile::exists(desktopFileLocation);
}
void setLaunchOnStartup_private(const QString &appName, const QString &guiName, bool enable)
{
QString userAutoStartPath = getUserAutostartDir_private();
QString desktopFileLocation = userAutoStartPath
+ QLatin1String(LINUX_APPLICATION_ID)
+ QLatin1String(".desktop");
QString desktopFileLocation = userAutoStartPath + appName + QLatin1String(".desktop");
if (enable) {
if (!QDir().exists(userAutoStartPath) && !QDir().mkpath(userAutoStartPath)) {
qCWarning(lcUtility) << "Could not create autostart folder" << userAutoStartPath;

View File

@@ -288,11 +288,7 @@ void ExcludedFiles::addManualExclude(const QByteArray &expr)
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;

View File

@@ -1,5 +1,5 @@
project(gui)
find_package(Qt5 REQUIRED COMPONENTS Widgets Svg)
find_package(Qt5 REQUIRED COMPONENTS Widgets)
set(CMAKE_AUTOMOC TRUE)
set(CMAKE_AUTOUIC TRUE)
set(CMAKE_AUTORCC TRUE)
@@ -24,6 +24,7 @@ set(client_UI_SRCS
ignorelisteditor.ui
ignorelisttablewidget.ui
networksettings.ui
activitywidget.ui
synclogdialog.ui
settingsdialog.ui
sharedialog.ui
@@ -34,13 +35,12 @@ set(client_UI_SRCS
addcertificatedialog.ui
proxyauthdialog.ui
mnemonicdialog.ui
tray/Window.qml
tray/UserLine.qml
wizard/flow2authwidget.ui
wizard/owncloudadvancedsetuppage.ui
wizard/owncloudconnectionmethoddialog.ui
wizard/owncloudhttpcredspage.ui
wizard/owncloudoauthcredspage.ui
wizard/flow2authcredspage.ui
wizard/flow2authwidget.ui
wizard/owncloudsetupnocredspage.ui
wizard/owncloudwizardresultpage.ui
wizard/webview.ui
@@ -74,6 +74,10 @@ set(client_SRCS
openfilemanager.cpp
owncloudgui.cpp
owncloudsetupwizard.cpp
activitydata.cpp
activitylistmodel.cpp
activitywidget.cpp
activityitemdelegate.cpp
selectivesyncdialog.cpp
settingsdialog.cpp
sharedialog.cpp
@@ -96,20 +100,15 @@ set(client_SRCS
synclogdialog.cpp
tooltipupdater.cpp
notificationconfirmjob.cpp
servernotificationhandler.cpp
guiutility.cpp
elidedlabel.cpp
headerbanner.cpp
iconjob.cpp
remotewipe.cpp
tray/ActivityData.cpp
tray/ActivityListModel.cpp
tray/UserModel.cpp
tray/NotificationHandler.cpp
creds/credentialsfactory.cpp
creds/httpcredentialsgui.cpp
creds/oauth.cpp
creds/flow2auth.cpp
creds/keychainchunk.cpp
creds/webflowcredentials.cpp
creds/webflowcredentialsdialog.cpp
wizard/postfixlineedit.cpp
@@ -146,6 +145,8 @@ set(updater_SRCS
IF( APPLE )
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 systray.mm)
@@ -175,6 +176,17 @@ set(3rdparty_SRC
../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
)
# We want to access Cocoa specific structures in the code above
# and need the platform plugin interface for that - which is private.
include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS})
endif()
if(NOT WIN32)
list(APPEND 3rdparty_SRC ../3rdparty/qtlockedfile/qtlockedfile_unix.cpp)
else()
@@ -298,7 +310,7 @@ else()
endif()
add_library(updater STATIC ${updater_SRCS})
target_link_libraries(updater ${synclib_NAME} Qt5::Widgets Qt5::Svg Qt5::Network Qt5::Xml Qt5::WebEngineWidgets)
target_link_libraries(updater ${synclib_NAME} Qt5::Widgets Qt5::Network Qt5::Xml Qt5::WebEngineWidgets)
target_include_directories(updater PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
set_target_properties( ${APPLICATION_EXECUTABLE} PROPERTIES
@@ -308,7 +320,7 @@ set_target_properties( ${APPLICATION_EXECUTABLE} PROPERTIES
set_target_properties( ${APPLICATION_EXECUTABLE} PROPERTIES
INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}/${APPLICATION_EXECUTABLE};${CMAKE_INSTALL_RPATH}" )
target_link_libraries( ${APPLICATION_EXECUTABLE} Qt5::Widgets Qt5::Svg Qt5::Network Qt5::Xml)
target_link_libraries( ${APPLICATION_EXECUTABLE} Qt5::Widgets Qt5::Network Qt5::Xml)
target_link_libraries( ${APPLICATION_EXECUTABLE} ${synclib_NAME} )
target_link_libraries( ${APPLICATION_EXECUTABLE} updater )
target_link_libraries( ${APPLICATION_EXECUTABLE} ${OS_SPECIFIC_LINK_LIBRARIES} )
@@ -330,6 +342,7 @@ ENDIF()
target_include_directories(${APPLICATION_EXECUTABLE} PRIVATE
${CMAKE_SOURCE_DIR}/src/3rdparty/QProgressIndicator
${CMAKE_SOURCE_DIR}/src/3rdparty/qtlockedfile
${CMAKE_SOURCE_DIR}/src/3rdparty/qtmacgoodies/src
${CMAKE_SOURCE_DIR}/src/3rdparty/qtsingleapplication
${CMAKE_SOURCE_DIR}/src/3rdparty/kmessagewidget
${CMAKE_CURRENT_BINARY_DIR}
@@ -388,7 +401,7 @@ endif()
if(NOT BUILD_OWNCLOUD_OSX_BUNDLE AND NOT WIN32)
configure_file(${CMAKE_SOURCE_DIR}/mirall.desktop.in
${CMAKE_CURRENT_BINARY_DIR}/${LINUX_APPLICATION_ID}.desktop)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${LINUX_APPLICATION_ID}.desktop DESTINATION ${DATADIR}/applications )
${CMAKE_CURRENT_BINARY_DIR}/${APPLICATION_EXECUTABLE}.desktop)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${APPLICATION_EXECUTABLE}.desktop DESTINATION ${DATADIR}/applications )
endif()

View File

@@ -89,9 +89,6 @@ private:
// Adds an account to the tracked list, emitting accountAdded()
void addAccountState(AccountState *accountState);
AccountManager() {}
QList<AccountStatePtr> _accounts;
public slots:
/// Saves account data, not including the credentials
void saveAccount(Account *a);
@@ -107,5 +104,9 @@ Q_SIGNALS:
void accountAdded(AccountState *account);
void accountRemoved(AccountState *account);
void removeAccountFolders(AccountState *account);
private:
AccountManager() {}
QList<AccountStatePtr> _accounts;
};
}

View File

@@ -57,6 +57,10 @@
#include "account.h"
#ifdef Q_OS_MAC
#include "settingsdialogmac.h"
#endif
namespace OCC {
Q_LOGGING_CATEGORY(lcAccountSettings, "nextcloud.gui.account.settings", QtInfoMsg)
@@ -109,13 +113,13 @@ protected:
AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
: QWidget(parent)
, _ui(new Ui::AccountSettings)
, ui(new Ui::AccountSettings)
, _wasDisabledBefore(false)
, _accountState(accountState)
, _quotaInfo(accountState)
, _menuShown(false)
{
_ui->setupUi(this);
ui->setupUi(this);
_model = new FolderStatusModel;
_model->setAccountState(_accountState);
@@ -123,37 +127,37 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
FolderStatusDelegate *delegate = new FolderStatusDelegate;
delegate->setParent(this);
// Connect styleChanged events to our widgets, so they can adapt (Dark-/Light-Mode switching)
connect(this, &AccountSettings::styleChanged, delegate, &FolderStatusDelegate::slotStyleChanged);
_ui->_folderList->header()->hide();
_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)
_ui->_folderList->setMinimumWidth(400);
ui->_folderList->setMinimumWidth(400);
#else
_ui->_folderList->setMinimumWidth(300);
ui->_folderList->setMinimumWidth(300);
#endif
new ToolTipUpdater(_ui->_folderList);
new ToolTipUpdater(ui->_folderList);
auto mouseCursorChanger = new MouseCursorChanger(this);
mouseCursorChanger->folderList = _ui->_folderList;
mouseCursorChanger->folderList = ui->_folderList;
mouseCursorChanger->model = _model;
_ui->_folderList->setMouseTracking(true);
_ui->_folderList->setAttribute(Qt::WA_Hover, true);
_ui->_folderList->installEventFilter(mouseCursorChanger);
ui->_folderList->setMouseTracking(true);
ui->_folderList->setAttribute(Qt::WA_Hover, true);
ui->_folderList->installEventFilter(mouseCursorChanger);
createAccountToolbox();
connect(AccountManager::instance(), &AccountManager::accountAdded,
this, &AccountSettings::slotAccountAdded);
connect(this, &AccountSettings::removeAccountFolders,
AccountManager::instance(), &AccountManager::removeAccountFolders);
connect(_ui->_folderList, &QWidget::customContextMenuRequested,
connect(ui->_folderList, &QWidget::customContextMenuRequested,
this, &AccountSettings::slotCustomContextMenuRequested);
connect(_ui->_folderList, &QAbstractItemView::clicked,
connect(ui->_folderList, &QAbstractItemView::clicked,
this, &AccountSettings::slotFolderListClicked);
connect(_ui->_folderList, &QTreeView::expanded, this, &AccountSettings::refreshSelectiveSyncStatus);
connect(_ui->_folderList, &QTreeView::collapsed, this, &AccountSettings::refreshSelectiveSyncStatus);
connect(_ui->selectiveSyncNotification, &QLabel::linkActivated,
connect(ui->_folderList, &QTreeView::expanded, this, &AccountSettings::refreshSelectiveSyncStatus);
connect(ui->_folderList, &QTreeView::collapsed, this, &AccountSettings::refreshSelectiveSyncStatus);
connect(ui->selectiveSyncNotification, &QLabel::linkActivated,
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);
refreshSelectiveSyncStatus();
connect(_model, &QAbstractItemModel::rowsInserted,
@@ -170,21 +174,20 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
addAction(syncNowWithRemoteDiscovery);
connect(_ui->selectiveSyncApply, &QAbstractButton::clicked, _model, &FolderStatusModel::slotApplySelectiveSync);
connect(_ui->selectiveSyncCancel, &QAbstractButton::clicked, _model, &FolderStatusModel::resetFolders);
connect(_ui->bigFolderApply, &QAbstractButton::clicked, _model, &FolderStatusModel::slotApplySelectiveSync);
connect(_ui->bigFolderSyncAll, &QAbstractButton::clicked, _model, &FolderStatusModel::slotSyncAllPendingBigFolders);
connect(_ui->bigFolderSyncNone, &QAbstractButton::clicked, _model, &FolderStatusModel::slotSyncNoPendingBigFolders);
connect(ui->selectiveSyncApply, &QAbstractButton::clicked, _model, &FolderStatusModel::slotApplySelectiveSync);
connect(ui->selectiveSyncCancel, &QAbstractButton::clicked, _model, &FolderStatusModel::resetFolders);
connect(ui->bigFolderApply, &QAbstractButton::clicked, _model, &FolderStatusModel::slotApplySelectiveSync);
connect(ui->bigFolderSyncAll, &QAbstractButton::clicked, _model, &FolderStatusModel::slotSyncAllPendingBigFolders);
connect(ui->bigFolderSyncNone, &QAbstractButton::clicked, _model, &FolderStatusModel::slotSyncNoPendingBigFolders);
connect(FolderMan::instance(), &FolderMan::folderListChanged, _model, &FolderStatusModel::resetFolders);
connect(this, &AccountSettings::folderChanged, _model, &FolderStatusModel::resetFolders);
// quotaProgressBar style now set in customizeStyle()
/*QColor color = palette().highlight().color();
_ui->quotaProgressBar->setStyleSheet(QString::fromLatin1(progressBarStyleC).arg(color.name()));*/
QColor color = palette().highlight().color();
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);
slotAccountStateChanged();
@@ -201,31 +204,70 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
{
slotNewMnemonicGenerated();
} else {
_ui->encryptionMessage->hide();
ui->encryptionMessage->hide();
}
}
connect(UserModel::instance(), &UserModel::addAccount,
this, &AccountSettings::slotOpenAccountWizard);
customizeStyle();
void AccountSettings::createAccountToolbox()
{
QMenu *menu = new QMenu();
connect(menu, &QMenu::aboutToShow, this, &AccountSettings::slotMenuBeforeShow);
_addAccountAction = new QAction(tr("Add new"), this);
menu->addAction(_addAccountAction);
connect(_addAccountAction, &QAction::triggered, this, &AccountSettings::slotOpenAccountWizard);
_toggleSignInOutAction = new QAction(tr("Log out"), this);
connect(_toggleSignInOutAction, &QAction::triggered, this, &AccountSettings::slotToggleSignInState);
menu->addAction(_toggleSignInOutAction);
QAction *action = new QAction(tr("Remove"), this);
menu->addAction(action);
connect(action, &QAction::triggered, this, &AccountSettings::slotDeleteAccount);
ui->_accountToolbox->setText(tr("Account") + QLatin1Char(' '));
ui->_accountToolbox->setMenu(menu);
ui->_accountToolbox->setPopupMode(QToolButton::InstantPopup);
slotAccountAdded(_accountState);
}
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);
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->show();
ui->encryptionMessage->addAction(mnemonic);
ui->encryptionMessage->show();
}
void AccountSettings::slotMenuBeforeShow() {
if (_menuShown) {
return;
}
auto menu = ui->_accountToolbox->menu();
// We can't check this during the initial creation as there is no account yet then
if (_accountState->account()->capabilities().clientSideEncryptionAvaliable()) {
QAction *mnemonic = new QAction(tr("Show E2E mnemonic"), this);
connect(mnemonic, &QAction::triggered, this, &AccountSettings::requesetMnemonic);
menu->addAction(mnemonic);
}
_menuShown = true;
}
QString AccountSettings::selectedFolderAlias() const
{
QModelIndex selected = _ui->_folderList->selectionModel()->currentIndex();
QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
if (!selected.isValid())
return "";
return _model->data(selected, FolderStatusDelegate::FolderAliasRole).toString();
@@ -238,7 +280,16 @@ void AccountSettings::slotOpenAccountWizard()
if (qgetenv("QT_QPA_PLATFORMTHEME") == "appmenu-qt5" || QSystemTrayIcon::isSystemTrayAvailable()) {
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);
}
@@ -254,7 +305,7 @@ void AccountSettings::slotToggleSignInState()
void AccountSettings::doExpand()
{
_ui->_folderList->expandToDepth(0);
ui->_folderList->expandToDepth(0);
}
void AccountSettings::slotShowMnemonic(const QString &mnemonic) {
@@ -502,7 +553,7 @@ void AccountSettings::slotEditCurrentIgnoredFiles()
void AccountSettings::slotEditCurrentLocalIgnoredFiles()
{
QModelIndex selected = _ui->_folderList->selectionModel()->currentIndex();
QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
if (!selected.isValid() || _model->classify(selected) != FolderStatusModel::SubFolder)
return;
QString fileName = _model->data(selected, FolderStatusDelegate::FolderPathRole).toString();
@@ -574,7 +625,7 @@ void AccountSettings::slotSubfolderContextMenuRequested(const QModelIndex& index
void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
{
QTreeView *tv = _ui->_folderList;
QTreeView *tv = ui->_folderList;
QModelIndex index = tv->indexAt(pos);
if (!index.isValid()) {
return;
@@ -605,7 +656,7 @@ void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
ac = menu->addAction(tr("Edit Ignored Files"));
connect(ac, &QAction::triggered, this, &AccountSettings::slotEditCurrentIgnoredFiles);
if (!_ui->_folderList->isExpanded(index)) {
if (!ui->_folderList->isExpanded(index)) {
ac = menu->addAction(tr("Choose what to sync"));
ac->setEnabled(folderConnected);
connect(ac, &QAction::triggered, this, &AccountSettings::doExpand);
@@ -644,7 +695,7 @@ void AccountSettings::slotFolderListClicked(const QModelIndex &indx)
}
if (_model->classify(indx) == FolderStatusModel::RootFolder) {
// tries to find if we clicked on the '...' button.
QTreeView *tv = _ui->_folderList;
QTreeView *tv = ui->_folderList;
auto pos = tv->mapFromGlobal(QCursor::pos());
if (FolderStatusDelegate::optionsButtonRect(tv->visualRect(indx), layoutDirection()).contains(pos)) {
slotCustomContextMenuRequested(pos);
@@ -657,8 +708,8 @@ void AccountSettings::slotFolderListClicked(const QModelIndex &indx)
// Expand root items on single click
if (_accountState && _accountState->state() == AccountState::Connected) {
bool expanded = !(_ui->_folderList->isExpanded(indx));
_ui->_folderList->setExpanded(indx, expanded);
bool expanded = !(ui->_folderList->isExpanded(indx));
ui->_folderList->setExpanded(indx, expanded);
}
}
}
@@ -695,7 +746,7 @@ void AccountSettings::slotFolderWizardAccepted()
qCInfo(lcAccountSettings) << "Creating folder" << definition.localPath;
if (!dir.mkpath(".")) {
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)));
return;
}
@@ -740,7 +791,7 @@ void AccountSettings::slotRemoveCurrentFolder()
{
FolderMan *folderMan = FolderMan::instance();
auto folder = folderMan->folder(selectedFolderAlias());
QModelIndex selected = _ui->_folderList->selectionModel()->currentIndex();
QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
if (selected.isValid() && folder) {
int row = selected.row();
@@ -782,7 +833,7 @@ void AccountSettings::slotOpenCurrentFolder()
void AccountSettings::slotOpenCurrentLocalSubFolder()
{
QModelIndex selected = _ui->_folderList->selectionModel()->currentIndex();
QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
if (!selected.isValid() || _model->classify(selected) != FolderStatusModel::SubFolder)
return;
QString fileName = _model->data(selected, FolderStatusDelegate::FolderPathRole).toString();
@@ -796,21 +847,18 @@ void AccountSettings::showConnectionLabel(const QString &message, QStringList er
"border-width: 1px; border-style: solid; border-color: #aaaaaa;"
"border-radius:5px;");
if (errors.isEmpty()) {
QString msg = message;
Theme::replaceLinkColorStringBackgroundAware(msg);
_ui->connectLabel->setText(msg);
_ui->connectLabel->setToolTip(QString());
_ui->connectLabel->setStyleSheet(QString());
ui->connectLabel->setText(message);
ui->connectLabel->setToolTip(QString());
ui->connectLabel->setStyleSheet(QString());
} else {
errors.prepend(message);
QString msg = errors.join(QLatin1String("\n"));
const QString msg = errors.join(QLatin1String("\n"));
qCDebug(lcAccountSettings) << msg;
Theme::replaceLinkColorString(msg, QColor("#c1c8e6"));
_ui->connectLabel->setText(msg);
_ui->connectLabel->setToolTip(QString());
_ui->connectLabel->setStyleSheet(errStyle);
ui->connectLabel->setText(msg);
ui->connectLabel->setToolTip(QString());
ui->connectLabel->setStyleSheet(errStyle);
}
_ui->accountStatus->setVisible(!message.isEmpty());
ui->accountStatus->setVisible(!message.isEmpty());
}
void AccountSettings::slotEnableCurrentFolder()
@@ -908,29 +956,29 @@ void AccountSettings::slotOpenOC()
void AccountSettings::slotUpdateQuota(qint64 total, qint64 used)
{
if (total > 0) {
_ui->quotaProgressBar->setVisible(true);
_ui->quotaProgressBar->setEnabled(true);
ui->quotaProgressBar->setVisible(true);
ui->quotaProgressBar->setEnabled(true);
// workaround the label only accepting ints (which may be only 32 bit wide)
const double percent = used / (double)total * 100;
const int percentInt = qMin(qRound(percent), 100);
_ui->quotaProgressBar->setValue(percentInt);
ui->quotaProgressBar->setValue(percentInt);
QString usedStr = Utility::octetsToString(used);
QString totalStr = Utility::octetsToString(total);
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);
_ui->quotaInfoLabel->setText(tr("%1 of %2 in use").arg(usedStr, totalStr));
_ui->quotaInfoLabel->setToolTip(toolTip);
_ui->quotaProgressBar->setToolTip(toolTip);
ui->quotaInfoLabel->setText(tr("%1 of %2 in use").arg(usedStr, totalStr));
ui->quotaInfoLabel->setToolTip(toolTip);
ui->quotaProgressBar->setToolTip(toolTip);
} else {
_ui->quotaProgressBar->setVisible(false);
_ui->quotaInfoLabel->setToolTip(QString());
ui->quotaProgressBar->setVisible(false);
ui->quotaInfoLabel->setToolTip(QString());
/* -1 means not computed; -2 means unknown; -3 means unlimited (#3940)*/
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 {
QString usedStr = Utility::octetsToString(used);
_ui->quotaInfoLabel->setText(tr("%1 in use").arg(usedStr));
ui->quotaInfoLabel->setText(tr("%1 in use").arg(usedStr));
}
}
}
@@ -939,7 +987,7 @@ void AccountSettings::slotAccountStateChanged()
{
int state = _accountState ? _accountState->state() : AccountState::Disconnected;
if (_accountState) {
_ui->sslButton->updateAccountState(_accountState);
ui->sslButton->updateAccountState(_accountState);
AccountPtr account = _accountState->account();
QUrl safeUrl(account->url());
safeUrl.setPassword(QString()); // Remove the password from the URL to avoid showing it in the UI
@@ -984,7 +1032,7 @@ void AccountSettings::slotAccountStateChanged()
"<a href='%1'>Click here</a> to re-open the browser.")
.arg(url.toString(QUrl::FullyEncoded)));
} else {
showConnectionLabel(tr("Connecting to %1").arg(serverWithUser));
showConnectionLabel(tr("Connecting to %1...").arg(serverWithUser));
}
} else {
showConnectionLabel(tr("No connection to %1 at %2.")
@@ -998,14 +1046,14 @@ void AccountSettings::slotAccountStateChanged()
}
/* 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) {
/* check if there are expanded root items, if so, close them */
int i;
for (i = 0; i < _model->rowCount(); ++i) {
if (_ui->_folderList->isExpanded(_model->index(i)))
_ui->_folderList->setExpanded(_model->index(i), false);
if (ui->_folderList->isExpanded(_model->index(i)))
ui->_folderList->setExpanded(_model->index(i), false);
}
} else if (_model->isDirty()) {
// If we connect and have pending changes, show the list.
@@ -1016,12 +1064,21 @@ void AccountSettings::slotAccountStateChanged()
// sync user interface buttons.
refreshSelectiveSyncStatus();
/* set the correct label for the Account toolbox button */
if (_accountState) {
if (_accountState->isSignedOut()) {
_toggleSignInOutAction->setText(tr("Log in"));
} else {
_toggleSignInOutAction->setText(tr("Log out"));
}
}
if (state == AccountState::State::Connected) {
/* TODO: We should probably do something better here.
* Verify if the user has a private key already uploaded to the server,
* if it has, do not offer to create one.
*/
qCInfo(lcAccountSettings) << "Account" << accountsState()->account()->displayName()
qCInfo(lcAccountSettings) << "Accout" << accountsState()->account()->displayName()
<< "Client Side Encryption" << accountsState()->account()->capabilities().clientSideEncryptionAvaliable();
}
}
@@ -1040,21 +1097,21 @@ void AccountSettings::slotLinkActivated(const QString &link)
// Make sure the folder itself is expanded
Folder *f = FolderMan::instance()->folder(alias);
QModelIndex folderIndx = _model->indexForPath(f, QString());
if (!_ui->_folderList->isExpanded(folderIndx)) {
_ui->_folderList->setExpanded(folderIndx, true);
if (!ui->_folderList->isExpanded(folderIndx)) {
ui->_folderList->setExpanded(folderIndx, true);
}
QModelIndex indx = _model->indexForPath(f, myFolder);
if (indx.isValid()) {
// make sure all the parents are expanded
for (auto i = indx.parent(); i.isValid(); i = i.parent()) {
if (!_ui->_folderList->isExpanded(i)) {
_ui->_folderList->setExpanded(i, true);
if (!ui->_folderList->isExpanded(i)) {
ui->_folderList->setExpanded(i, true);
}
}
_ui->_folderList->setSelectionMode(QAbstractItemView::SingleSelection);
_ui->_folderList->setCurrentIndex(indx);
_ui->_folderList->scrollTo(indx);
ui->_folderList->setSelectionMode(QAbstractItemView::SingleSelection);
ui->_folderList->setCurrentIndex(indx);
ui->_folderList->scrollTo(indx);
} else {
qCWarning(lcAccountSettings) << "Unable to find a valid index for " << myFolder;
}
@@ -1063,7 +1120,7 @@ void AccountSettings::slotLinkActivated(const QString &link)
AccountSettings::~AccountSettings()
{
delete _ui;
delete ui;
}
void AccountSettings::refreshSelectiveSyncStatus()
@@ -1101,8 +1158,8 @@ void AccountSettings::refreshSelectiveSyncStatus()
}
if (msg.isEmpty()) {
_ui->selectiveSyncButtons->setVisible(true);
_ui->bigFolderUi->setVisible(false);
ui->selectiveSyncButtons->setVisible(true);
ui->bigFolderUi->setVisible(false);
} else {
ConfigFile cfg;
QString info = !cfg.confirmExternalStorage()
@@ -1111,32 +1168,44 @@ 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 too big or external storages: ");
_ui->selectiveSyncNotification->setText(info + msg);
_ui->selectiveSyncButtons->setVisible(false);
_ui->bigFolderUi->setVisible(true);
ui->selectiveSyncNotification->setText(info + msg);
ui->selectiveSyncButtons->setVisible(false);
ui->bigFolderUi->setVisible(true);
shouldBeVisible = true;
}
_ui->selectiveSyncApply->setEnabled(_model->isDirty() || !msg.isEmpty());
bool wasVisible = !_ui->selectiveSyncStatus->isHidden();
ui->selectiveSyncApply->setEnabled(_model->isDirty() || !msg.isEmpty());
bool wasVisible = !ui->selectiveSyncStatus->isHidden();
if (wasVisible != shouldBeVisible) {
QSize hint = _ui->selectiveSyncStatus->sizeHint();
QSize hint = ui->selectiveSyncStatus->sizeHint();
if (shouldBeVisible) {
_ui->selectiveSyncStatus->setMaximumHeight(0);
_ui->selectiveSyncStatus->setVisible(true);
ui->selectiveSyncStatus->setMaximumHeight(0);
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->start(QAbstractAnimation::DeleteWhenStopped);
connect(anim, &QPropertyAnimation::finished, [this, shouldBeVisible]() {
_ui->selectiveSyncStatus->setMaximumHeight(QWIDGETSIZE_MAX);
ui->selectiveSyncStatus->setMaximumHeight(QWIDGETSIZE_MAX);
if (!shouldBeVisible) {
_ui->selectiveSyncStatus->hide();
ui->selectiveSyncStatus->hide();
}
});
}
}
void AccountSettings::slotAccountAdded(AccountState *)
{
// if the theme is limited to single account, the button must hide if
// there is already one account.
int s = AccountManager::instance()->accounts().size();
if (s > 0 && !Theme::instance()->multiAccount()) {
_addAccountAction->setVisible(false);
} else {
_addAccountAction->setVisible(true);
}
}
void AccountSettings::slotDeleteAccount()
{
// Deleting the account potentially deletes 'this', so
@@ -1179,30 +1248,12 @@ bool AccountSettings::event(QEvent *e)
// Expand the folder automatically only if there's only one, see #4283
// The 2 is 1 folder + 1 'add folder' button
if (_model->rowCount() <= 2) {
_ui->_folderList->setExpanded(_model->index(0, 0), true);
ui->_folderList->setExpanded(_model->index(0, 0), true);
}
}
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
#include "accountsettings.moc"

View File

@@ -64,13 +64,11 @@ signals:
void showIssuesList(AccountState *account);
void requesetMnemonic();
void removeAccountFolders(AccountState *account);
void styleChanged();
public slots:
void slotOpenOC();
void slotUpdateQuota(qint64, qint64);
void slotAccountStateChanged();
void slotStyleChanged();
AccountState *accountsState() { return _accountState; }
@@ -90,6 +88,7 @@ protected slots:
void slotDeleteAccount();
void slotToggleSignInState();
void slotOpenAccountWizard();
void slotAccountAdded(AccountState *);
void refreshSelectiveSyncStatus();
void slotMarkSubfolderEncrypted(const FolderStatusModel::SubFolderInfo* folderInfo);
void slotMarkSubfolderDecrypted(const FolderStatusModel::SubFolderInfo* folderInfo);
@@ -99,6 +98,8 @@ protected slots:
void doExpand();
void slotLinkActivated(const QString &link);
void slotMenuBeforeShow();
// Encryption Related Stuff.
void slotShowMnemonic(const QString &mnemonic);
void slotNewMnemonicGenerated();
@@ -128,12 +129,11 @@ private:
bool event(QEvent *) override;
void createAccountToolbox();
void openIgnoredFilesDialog(const QString & absFolderPath);
void customizeStyle();
/// Returns the alias of the selected folder, empty string if none
QString selectedFolderAlias() const;
Ui::AccountSettings *_ui;
Ui::AccountSettings *ui;
FolderStatusModel *_model;
QUrl _OCUrl;

View File

@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>581</width>
<width>582</width>
<height>557</height>
</rect>
</property>
@@ -184,6 +184,13 @@
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QToolButton" name="_accountToolbox">
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@@ -20,7 +20,6 @@
#include "creds/httpcredentials.h"
#include "logger.h"
#include "configfile.h"
#include "ocsnavigationappsjob.h"
#include <QSettings>
#include <QTimer>
@@ -28,7 +27,6 @@
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QNetworkRequest>
#include <QBuffer>
@@ -42,9 +40,9 @@ AccountState::AccountState(AccountPtr account)
, _state(AccountState::Disconnected)
, _connectionStatus(ConnectionValidator::Undefined)
, _waitingForNewCredentials(false)
, _notificationsEtagResponseHeader("*")
, _maintenanceToConnectedDelay(60000 + (qrand() % (4 * 60000))) // 1-5min delay
, _remoteWipe(new RemoteWipe(_account))
, _hasTalk(false)
{
qRegisterMetaType<AccountState *>("AccountState*");
@@ -76,11 +74,6 @@ AccountPtr AccountState::account() const
return _account;
}
bool AccountState::hasTalk() const
{
return _hasTalk;
}
AccountState::ConnectionStatus AccountState::connectionStatus() const
{
return _connectionStatus;
@@ -244,9 +237,6 @@ void AccountState::checkConnectivity()
// Use a small authed propfind as a minimal ping when we're
// already connected.
conValidator->checkAuthentication();
// Get the Apps available on the server.
fetchNavigationApps();
} else {
// Check the server and then the auth.
@@ -277,7 +267,7 @@ void AccountState::slotConnectionValidatorResult(ConnectionValidator::Status sta
// Come online gradually from 503 or maintenance mode
if (status == ConnectionValidator::Connected
&& (_connectionStatus == ConnectionValidator::ServiceUnavailable
|| _connectionStatus == ConnectionValidator::MaintenanceMode)) {
|| _connectionStatus == ConnectionValidator::MaintenanceMode)) {
if (!_timeSinceMaintenanceOver.isValid()) {
qCInfo(lcAccountState) << "AccountState reconnection: delaying for"
<< _maintenanceToConnectedDelay << "ms";
@@ -303,9 +293,6 @@ void AccountState::slotConnectionValidatorResult(ConnectionValidator::Status sta
case ConnectionValidator::Connected:
if (_state != Connected) {
setState(Connected);
// Get the Apps available on the server.
fetchNavigationApps();
}
break;
case ConnectionValidator::Undefined:
@@ -418,110 +405,4 @@ std::unique_ptr<QSettings> AccountState::settings()
return s;
}
void AccountState::fetchNavigationApps(){
OcsNavigationAppsJob *job = new OcsNavigationAppsJob(_account);
job->addRawHeader("If-None-Match", navigationAppsEtagResponseHeader());
connect(job, &OcsNavigationAppsJob::appsJobFinished, this, &AccountState::slotNavigationAppsFetched);
connect(job, &OcsNavigationAppsJob::etagResponseHeaderReceived, this, &AccountState::slotEtagResponseHeaderReceived);
connect(job, &OcsNavigationAppsJob::ocsError, this, &AccountState::slotOcsError);
job->getNavigationApps();
}
void AccountState::slotEtagResponseHeaderReceived(const QByteArray &value, int statusCode){
if(statusCode == 200){
qCDebug(lcAccountState) << "New navigation apps ETag Response Header received " << value;
setNavigationAppsEtagResponseHeader(value);
}
}
void AccountState::slotOcsError(int statusCode, const QString &message)
{
qCDebug(lcAccountState) << "Error " << statusCode << " while fetching new navigation apps: " << message;
}
void AccountState::slotNavigationAppsFetched(const QJsonDocument &reply, int statusCode)
{
if(_account){
if (statusCode == 304) {
qCWarning(lcAccountState) << "Status code " << statusCode << " Not Modified - No new navigation apps.";
} else {
_apps.clear();
_hasTalk = false;
if(!reply.isEmpty()){
auto element = reply.object().value("ocs").toObject().value("data");
auto navLinks = element.toArray();
if(navLinks.size() > 0){
foreach (const QJsonValue &value, navLinks) {
auto navLink = value.toObject();
AccountApp *app = new AccountApp(navLink.value("name").toString(), QUrl(navLink.value("href").toString()),
navLink.value("id").toString(), QUrl(navLink.value("icon").toString()));
_apps << app;
if(app->id() == QLatin1String("spreed"))
_hasTalk = true;
}
}
}
emit hasFetchedNavigationApps();
}
}
}
AccountAppList AccountState::appList() const
{
return _apps;
}
AccountApp* AccountState::findApp(const QString &appId) const
{
if(!appId.isEmpty()) {
foreach(AccountApp *app, appList()) {
if(app->id() == appId)
return app;
}
}
return nullptr;
}
/*-------------------------------------------------------------------------------------*/
AccountApp::AccountApp(const QString &name, const QUrl &url,
const QString &id, const QUrl &iconUrl,
QObject *parent)
: QObject(parent)
, _name(name)
, _url(url)
, _id(id)
, _iconUrl(iconUrl)
{
}
QString AccountApp::name() const
{
return _name;
}
QUrl AccountApp::url() const
{
return _url;
}
QString AccountApp::id() const
{
return _id;
}
QUrl AccountApp::iconUrl() const
{
return _iconUrl;
}
/*-------------------------------------------------------------------------------------*/
} // namespace OCC

View File

@@ -29,11 +29,9 @@ namespace OCC {
class AccountState;
class Account;
class AccountApp;
class RemoteWipe;
typedef QExplicitlySharedDataPointer<AccountState> AccountStatePtr;
typedef QList<AccountApp*> AccountAppList;
/**
* @brief Extra info about an ownCloud server account.
@@ -103,11 +101,6 @@ public:
bool isSignedOut() const;
bool hasTalk() const;
AccountAppList appList() const;
AccountApp* findApp(const QString &appId) const;
/** A user-triggered sign out which disconnects, stops syncs
* for the account and forgets the password. */
void signOutByUi();
@@ -168,12 +161,10 @@ public slots:
private:
void setState(State state);
void fetchNavigationApps();
signals:
void stateChanged(int state);
void isConnectedChanged();
void hasFetchedNavigationApps();
protected Q_SLOTS:
void slotConnectionValidatorResult(ConnectionValidator::Status status, const QStringList &errors);
@@ -185,17 +176,12 @@ protected Q_SLOTS:
void slotCredentialsFetched(AbstractCredentials *creds);
void slotCredentialsAsked(AbstractCredentials *creds);
void slotNavigationAppsFetched(const QJsonDocument &reply, int statusCode);
void slotEtagResponseHeaderReceived(const QByteArray &value, int statusCode);
void slotOcsError(int statusCode, const QString &message);
private:
AccountPtr _account;
State _state;
ConnectionStatus _connectionStatus;
QStringList _connectionErrors;
bool _waitingForNewCredentials;
bool _hasTalk;
QElapsedTimer _timeSinceLastETagCheck;
QPointer<ConnectionValidator> _connectionValidator;
QByteArray _notificationsEtagResponseHeader;
@@ -219,34 +205,7 @@ private:
*/
RemoteWipe *_remoteWipe;
/**
* Holds the App names and URLs available on the server
*/
AccountAppList _apps;
};
class AccountApp : public QObject
{
Q_OBJECT
public:
AccountApp(const QString &name, const QUrl &url,
const QString &id, const QUrl &iconUrl,
QObject* parent = 0);
QString name() const;
QUrl url() const;
QString id() const;
QUrl iconUrl() const;
private:
QString _name;
QUrl _url;
QString _id;
QUrl _iconUrl;
};
}
Q_DECLARE_METATYPE(OCC::AccountState *)

View File

@@ -14,14 +14,14 @@
#include <QtCore>
#include "ActivityData.h"
#include "activitydata.h"
namespace OCC {
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)

View File

@@ -56,7 +56,6 @@ public:
Type _type;
qlonglong _id;
QString _fileAction;
QString _objectType;
QString _subject;
QString _message;
@@ -65,8 +64,6 @@ public:
QUrl _link;
QDateTime _dateTime;
QString _accName;
QString _icon;
QString _iconData;
// Stores information about the error
int _status;

View File

@@ -0,0 +1,336 @@
/*
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
* Copyright (C) by Olivier Goffart <ogoffart@woboq.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 "activityitemdelegate.h"
#include "folderstatusmodel.h"
#include "folderman.h"
#include "accountstate.h"
#include "activitydata.h"
#include <theme.h>
#include <account.h>
#include <QFileIconProvider>
#include <QPainter>
#include <QApplication>
#define HASQT5_11 (QT_VERSION >= QT_VERSION_CHECK(5,11,0))
namespace OCC {
int ActivityItemDelegate::_iconHeight = 0;
int ActivityItemDelegate::_margin = 0;
int ActivityItemDelegate::_primaryButtonWidth = 0;
int ActivityItemDelegate::_secondaryButtonWidth = 0;
int ActivityItemDelegate::_spaceBetweenButtons = 0;
int ActivityItemDelegate::_timeWidth = 0;
int ActivityItemDelegate::_buttonHeight = 0;
const QString ActivityItemDelegate::_remote_share("remote_share");
const QString ActivityItemDelegate::_call("call");
int ActivityItemDelegate::iconHeight()
{
if (_iconHeight == 0) {
QStyleOptionViewItem option;
QFont font = option.font;
QFontMetrics fm(font);
_iconHeight = qRound(fm.height() / 5.0 * 8.0);
}
return _iconHeight;
}
int ActivityItemDelegate::rowHeight()
{
if (_margin == 0) {
QStyleOptionViewItem opt;
QFont f = opt.font;
QFontMetrics fm(f);
_margin = fm.height() / 2;
#if defined(Q_OS_WIN)
_margin += 5;
#endif
}
return iconHeight() + 5 * _margin;
}
QSize ActivityItemDelegate::sizeHint(const QStyleOptionViewItem &option,
const QModelIndex & /* index */) const
{
QFont font = option.font;
return QSize(0, rowHeight());
}
void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QStyledItemDelegate::paint(painter, option, index);
QFont font = option.font;
QFontMetrics fm(font);
int margin = fm.height() / 2.5;
painter->save();
int iconSize = 16;
int iconOffset = qRound(fm.height() / 4.0 * 7.0);
int offset = 4;
// get the data
Activity::Type activityType = qvariant_cast<Activity::Type>(index.data(ActionRole));
QIcon actionIcon = qvariant_cast<QIcon>(index.data(ActionIconRole));
QString objectType = qvariant_cast<QString>(index.data(ObjectTypeRole));
QString actionText = qvariant_cast<QString>(index.data(ActionTextRole));
QString messageText = qvariant_cast<QString>(index.data(MessageRole));
QList<QVariant> customList = index.data(ActionsLinksRole).toList();
QString timeText = qvariant_cast<QString>(index.data(PointInTimeRole));
bool accountOnline = qvariant_cast<bool>(index.data(AccountConnectedRole));
// activity/notification icons
QRect actionIconRect = option.rect;
actionIconRect.setLeft(option.rect.left() + iconOffset/3);
actionIconRect.setRight(option.rect.left() + iconOffset);
actionIconRect.setTop(option.rect.top() + qRound((option.rect.height() - 16)/3.0));
// subject text rect
QRect actionTextBox = actionIconRect;
#if (HASQT5_11)
int actionTextBoxWidth = fm.horizontalAdvance(actionText);
#else
int actionTextBoxWidth = fm.width(actionText);
#endif
actionTextBox.setTop(option.rect.top() + margin + offset/2);
actionTextBox.setHeight(fm.height());
actionTextBox.setLeft(actionIconRect.right() + margin);
actionTextBox.setRight(actionTextBox.left() + actionTextBoxWidth + margin);
// message text rect
QRect messageTextBox = actionTextBox;
#if (HASQT5_11)
int messageTextWidth = fm.horizontalAdvance(messageText);
#else
int messageTextWidth = fm.width(messageText);
#endif
int messageTextTop = option.rect.top() + fm.height() + margin;
if(actionText.isEmpty()) messageTextTop = option.rect.top() + margin + offset/2;
messageTextBox.setTop(messageTextTop);
messageTextBox.setHeight(fm.height());
messageTextBox.setBottom(messageTextBox.top() + fm.height());
messageTextBox.setRight(messageTextBox.left() + messageTextWidth + margin);
if(messageText.isEmpty()){
messageTextBox.setHeight(0);
messageTextBox.setBottom(messageTextBox.top());
}
// time box rect
QRect timeBox = messageTextBox;
QString timeStr = tr("%1").arg(timeText);
#if (HASQT5_11)
int timeTextWidth = fm.horizontalAdvance(timeStr);
#else
int timeTextWidth = fm.width(timeStr);
#endif
int timeTop = option.rect.top() + fm.height() + fm.height() + margin + offset/2;
if(messageText.isEmpty() || actionText.isEmpty())
timeTop = option.rect.top() + fm.height() + margin;
timeBox.setTop(timeTop);
timeBox.setHeight(fm.height());
timeBox.setBottom(timeBox.top() + fm.height());
timeBox.setRight(timeBox.left() + timeTextWidth + margin);
// buttons - default values
int rightMargin = margin;
int leftMargin = margin * offset;
int top = option.rect.top() + margin;
int buttonSize = option.rect.height()/2;
int right = option.rect.right() - rightMargin;
int left = right - buttonSize;
QStyleOptionButton secondaryButton;
secondaryButton.rect = option.rect;
secondaryButton.features |= QStyleOptionButton::Flat;
secondaryButton.state |= QStyle::State_None;
secondaryButton.rect.setLeft(left);
secondaryButton.rect.setRight(right);
secondaryButton.rect.setTop(top + margin);
secondaryButton.rect.setHeight(iconSize);
QStyleOptionButton primaryButton;
primaryButton.rect = option.rect;
primaryButton.features |= QStyleOptionButton::DefaultButton;
primaryButton.state |= QStyle::State_Raised;
primaryButton.rect.setTop(top);
primaryButton.rect.setHeight(buttonSize);
right = secondaryButton.rect.left() - rightMargin;
left = secondaryButton.rect.left() - leftMargin;
primaryButton.rect.setRight(right);
if(activityType == Activity::Type::NotificationType){
// Secondary will be 'Dismiss' or '...' multiple options button
secondaryButton.icon = QIcon(QLatin1String(":/client/resources/close.svg"));
if(customList.size() > 1)
secondaryButton.icon = QIcon(QLatin1String(":/client/resources/more.svg"));
secondaryButton.iconSize = QSize(iconSize, iconSize);
// Primary button will be 'More Information' or 'Accept'
primaryButton.text = tr("More information");
if(objectType == _remote_share) primaryButton.text = tr("Accept");
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));
#endif
// save info to be able to filter mouse clicks
_buttonHeight = buttonSize;
_primaryButtonWidth = primaryButton.rect.size().width();
_secondaryButtonWidth = secondaryButton.rect.size().width();
_spaceBetweenButtons = secondaryButton.rect.left() - primaryButton.rect.right();
} else if(activityType == Activity::SyncResultType){
// Secondary will be 'open file manager' with the folder icon
secondaryButton.icon = QIcon(QLatin1String(":/client/resources/folder.svg"));
secondaryButton.iconSize = QSize(iconSize, iconSize);
// Primary button will be '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));
#endif
// save info to be able to filter mouse clicks
_buttonHeight = buttonSize;
_primaryButtonWidth = primaryButton.rect.size().width();
_secondaryButtonWidth = secondaryButton.rect.size().width();
_spaceBetweenButtons = secondaryButton.rect.left() - primaryButton.rect.right();
} else if(activityType == Activity::SyncFileItemType){
// Secondary will be 'open file manager' with the folder icon
secondaryButton.icon = QIcon(QLatin1String(":/client/resources/folder.svg"));
secondaryButton.iconSize = QSize(iconSize, iconSize);
// No primary button on this case
// Whatever error we have at this case it is local, there is no point on opening the browser
_primaryButtonWidth = 0;
_secondaryButtonWidth = secondaryButton.rect.size().width();
_spaceBetweenButtons = secondaryButton.rect.left() - primaryButton.rect.right();
} else {
_spaceBetweenButtons = leftMargin;
_primaryButtonWidth = 0;
_secondaryButtonWidth = 0;
}
// draw the icon
QPixmap pm = actionIcon.pixmap(iconSize, iconSize, QIcon::Normal);
painter->drawPixmap(QPoint(actionIconRect.left(), actionIconRect.top()), pm);
// change pen color if use is not online
QPalette p = option.palette;
if(!accountOnline)
p.setCurrentColorGroup(QPalette::Disabled);
// change pen color if the line is selected
if (option.state & QStyle::State_Selected)
painter->setPen(p.color(QPalette::HighlightedText));
else
painter->setPen(p.color(QPalette::Text));
// calculate space for text - use the max possible before using the elipses
int spaceLeftForText = option.rect.width() - (actionIconRect.width() + margin + rightMargin + leftMargin) -
(_primaryButtonWidth + _secondaryButtonWidth + _spaceBetweenButtons);
// draw the subject
const QString elidedAction = fm.elidedText(actionText, Qt::ElideRight, spaceLeftForText);
painter->drawText(actionTextBox, elidedAction);
// draw the buttons
if(activityType == Activity::Type::NotificationType || activityType == Activity::Type::SyncResultType)
QApplication::style()->drawControl(QStyle::CE_PushButton, &primaryButton, painter);
// Since they are errors on local syncing, there is nothing to do in the server
if(activityType != Activity::Type::ActivityType)
QApplication::style()->drawControl(QStyle::CE_PushButton, &secondaryButton, painter);
// draw the message
// change pen color for the message
if(!messageText.isEmpty()){
const QString elidedMessage = fm.elidedText(messageText, Qt::ElideRight, spaceLeftForText);
painter->drawText(messageTextBox, elidedMessage);
}
// change pen color for the time
if (option.state & QStyle::State_Selected)
painter->setPen(p.color(QPalette::Disabled, QPalette::HighlightedText));
else
painter->setPen(p.color(QPalette::Disabled, QPalette::Text));
// draw the time
const QString elidedTime = fm.elidedText(timeStr, Qt::ElideRight, spaceLeftForText);
painter->drawText(timeBox, elidedTime);
painter->restore();
}
bool ActivityItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
const QStyleOptionViewItem &option, const QModelIndex &index)
{
Activity::Type activityType = qvariant_cast<Activity::Type>(index.data(ActionRole));
if(activityType != Activity::Type::ActivityType){
if (event->type() == QEvent::MouseButtonRelease){
QMouseEvent *mouseEvent = (QMouseEvent*)event;
if(mouseEvent){
int mouseEventX = mouseEvent->x();
int mouseEventY = mouseEvent->y();
int buttonsWidth = _primaryButtonWidth + _spaceBetweenButtons + _secondaryButtonWidth;
int x = option.rect.left() + option.rect.width() - buttonsWidth - _timeWidth;
int y = option.rect.top();
// clickable area for ...
if (mouseEventX > x && mouseEventX < x + buttonsWidth){
if(mouseEventY > y && mouseEventY < y + _buttonHeight){
// ...primary button ('more information' or 'accept' on notifications or 'open browser' on errors)
if (mouseEventX > x && mouseEventX < x + _primaryButtonWidth){
emit primaryButtonClickedOnItemView(index);
// ...secondary button ('dismiss' on notifications or 'open file manager' on errors)
} else {
x += _primaryButtonWidth + _spaceBetweenButtons;
if (mouseEventX > x && mouseEventX < x + _secondaryButtonWidth)
emit secondaryButtonClickedOnItemView(index);
}
}
}
}
}
}
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
} // namespace OCC

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) by Klaas Freitag <freitag@kde.org>
* Copyright (C) by Olivier Goffart <ogoffart@woboq.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 <QStyledItemDelegate>
#include <QMouseEvent>
class QMouseEvent;
namespace OCC {
/**
* @brief The ActivityItemDelegate class
* @ingroup gui
*/
class ActivityItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
enum datarole { ActionIconRole = Qt::UserRole + 1,
UserIconRole,
AccountRole,
ObjectTypeRole,
ActionsLinksRole,
ActionTextRole,
ActionRole,
MessageRole,
PathRole,
LinkRole,
PointInTimeRole,
AccountConnectedRole,
SyncFileStatusRole };
void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const override;
QSize sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const override;
bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option,
const QModelIndex &index) override;
static int rowHeight();
static int iconHeight();
signals:
void primaryButtonClickedOnItemView(const QModelIndex &index);
void secondaryButtonClickedOnItemView(const QModelIndex &index);
private:
static int _margin;
static int _iconHeight;
static int _primaryButtonWidth;
static int _secondaryButtonWidth;
static int _spaceBetweenButtons;
static int _timeWidth;
static int _buttonHeight;
static const QString _remote_share;
static const QString _call;
};
} // namespace OCC

View File

@@ -0,0 +1,341 @@
/*
* Copyright (C) by Klaas Freitag <freitag@owncloud.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 <QtCore>
#include <QAbstractListModel>
#include <QWidget>
#include <QIcon>
#include <QJsonObject>
#include <QJsonDocument>
#include "account.h"
#include "accountstate.h"
#include "accountmanager.h"
#include "folderman.h"
#include "accessmanager.h"
#include "activityitemdelegate.h"
#include "activitydata.h"
#include "activitylistmodel.h"
#include "theme.h"
#include "servernotificationhandler.h"
namespace OCC {
Q_LOGGING_CATEGORY(lcActivity, "nextcloud.gui.activity", QtInfoMsg)
ActivityListModel::ActivityListModel(AccountState *accountState, QWidget *parent)
: QAbstractListModel(parent)
, _accountState(accountState)
{
}
QVariant ActivityListModel::data(const QModelIndex &index, int role) const
{
Activity a;
// filter the get action here
// send only the text of the get action
// if there is more than one send the icon? the ...
if (!index.isValid())
return QVariant();
a = _finalList.at(index.row());
AccountStatePtr ast = AccountManager::instance()->account(a._accName);
if (!ast && _accountState != ast.data())
return QVariant();
QStringList list;
switch (role) {
case ActivityItemDelegate::PathRole:
if(!a._file.isEmpty()){
auto folder = FolderMan::instance()->folder(a._folder);
QString relPath(a._file);
if(folder) relPath.prepend(folder->remotePath());
list = FolderMan::instance()->findFileInLocalFolders(relPath, ast->account());
if (list.count() > 0) {
return QVariant(list.at(0));
}
// File does not exist anymore? Let's try to open its path
list = FolderMan::instance()->findFileInLocalFolders(QFileInfo(relPath).path(), ast->account());
if (list.count() > 0) {
return QVariant(list.at(0));
}
}
return QVariant();
break;
case ActivityItemDelegate::ActionsLinksRole:{
QList<QVariant> customList;
foreach (ActivityLink customItem, a._links) {
QVariant customVariant;
customVariant.setValue(customItem);
customList << customVariant;
}
return customList;
break;
}
case ActivityItemDelegate::ActionIconRole:
if(a._type == Activity::NotificationType){
QIcon cachedIcon = ServerNotificationHandler::iconCache.value(a._id);
if(!cachedIcon.isNull())
return cachedIcon;
else return QIcon(QLatin1String(":/client/resources/bell.svg"));
} else if(a._type == Activity::SyncResultType){
return QIcon(QLatin1String(":/client/resources/state-error.svg"));
} else if(a._type == Activity::SyncFileItemType){
if(a._status == SyncFileItem::NormalError
|| a._status == SyncFileItem::FatalError
|| a._status == SyncFileItem::DetailError
|| a._status == SyncFileItem::BlacklistedError) {
return QIcon(QLatin1String(":/client/resources/state-error.svg"));
} else if(a._status == SyncFileItem::SoftError
|| a._status == SyncFileItem::Conflict
|| a._status == SyncFileItem::Restoration
|| a._status == SyncFileItem::FileLocked){
return QIcon(QLatin1String(":/client/resources/state-warning.svg"));
} else if(a._status == SyncFileItem::FileIgnored){
return QIcon(QLatin1String(":/client/resources/state-info.svg"));
}
return QIcon(QLatin1String(":/client/resources/state-sync.svg"));
}
return QIcon(QLatin1String(":/client/resources/activity.png"));
break;
case ActivityItemDelegate::ObjectTypeRole:
return a._objectType;
break;
case ActivityItemDelegate::ActionRole:{
QVariant type;
type.setValue(a._type);
return type;
break;
}
case ActivityItemDelegate::ActionTextRole:
return a._subject;
break;
case ActivityItemDelegate::MessageRole:
return a._message;
break;
case ActivityItemDelegate::LinkRole:
return a._link;
break;
case ActivityItemDelegate::AccountRole:
return a._accName;
break;
case ActivityItemDelegate::PointInTimeRole:
return Utility::timeAgoInWords(a._dateTime);
break;
case ActivityItemDelegate::AccountConnectedRole:
return (ast && ast->isConnected());
break;
default:
return QVariant();
}
return QVariant();
}
int ActivityListModel::rowCount(const QModelIndex &) const
{
return _finalList.count();
}
bool ActivityListModel::canFetchMore(const QModelIndex &) const
{
// We need to be connected to be able to fetch more
if (_accountState && _accountState->isConnected()) {
// If the fetching is reported to be done or we are currently fetching we can't fetch more
if (!_doneFetching && !_currentlyFetching) {
return true;
}
}
return false;
}
void ActivityListModel::startFetchJob()
{
if (!_accountState->isConnected()) {
return;
}
JsonApiJob *job = new JsonApiJob(_accountState->account(), QLatin1String("ocs/v2.php/cloud/activity"), this);
QObject::connect(job, &JsonApiJob::jsonReceived,
this, &ActivityListModel::slotActivitiesReceived);
QUrlQuery params;
params.addQueryItem(QLatin1String("start"), QString::number(_currentItem));
params.addQueryItem(QLatin1String("count"), QString::number(100));
job->addQueryParams(params);
_currentlyFetching = true;
qCInfo(lcActivity) << "Start fetching activities for " << _accountState->account()->displayName();
job->start();
}
void ActivityListModel::slotActivitiesReceived(const QJsonDocument &json, int statusCode)
{
auto activities = json.object().value("ocs").toObject().value("data").toArray();
ActivityList list;
auto ast = _accountState;
if (!ast) {
return;
}
if (activities.size() == 0) {
_doneFetching = true;
}
_currentlyFetching = false;
_currentItem += activities.size();
foreach (auto activ, activities) {
auto json = activ.toObject();
Activity a;
a._type = Activity::ActivityType;
a._accName = ast->account()->displayName();
a._id = json.value("id").toInt();
a._subject = json.value("subject").toString();
a._message = json.value("message").toString();
a._file = json.value("file").toString();
a._link = QUrl(json.value("link").toString());
a._dateTime = QDateTime::fromString(json.value("date").toString(), Qt::ISODate);
list.append(a);
}
_activityLists.append(list);
emit activityJobStatusCode(statusCode);
combineActivityLists();
}
void ActivityListModel::addErrorToActivityList(Activity activity) {
qCInfo(lcActivity) << "Error successfully added to the notification list: " << activity._subject;
_notificationErrorsLists.prepend(activity);
combineActivityLists();
}
void ActivityListModel::addNotificationToActivityList(Activity activity) {
qCInfo(lcActivity) << "Notification successfully added to the notification list: " << activity._subject;
_notificationLists.prepend(activity);
combineActivityLists();
}
void ActivityListModel::clearNotifications() {
qCInfo(lcActivity) << "Clear the notifications";
_notificationLists.clear();
combineActivityLists();
}
void ActivityListModel::removeActivityFromActivityList(int row) {
Activity activity = _finalList.at(row);
removeActivityFromActivityList(activity);
combineActivityLists();
}
void ActivityListModel::addSyncFileItemToActivityList(Activity activity) {
qCInfo(lcActivity) << "Successfully added to the activity list: " << activity._subject;
_syncFileItemLists.prepend(activity);
combineActivityLists();
}
void ActivityListModel::removeActivityFromActivityList(Activity activity) {
qCInfo(lcActivity) << "Activity/Notification/Error successfully dismissed: " << activity._subject;
qCInfo(lcActivity) << "Trying to remove Activity/Notification/Error from view... ";
int index = -1;
if(activity._type == Activity::ActivityType){
index = _activityLists.indexOf(activity);
if(index != -1) _activityLists.removeAt(index);
} else if(activity._type == Activity::NotificationType){
index = _notificationLists.indexOf(activity);
if(index != -1) _notificationLists.removeAt(index);
} else {
index = _notificationErrorsLists.indexOf(activity);
if(index != -1) _notificationErrorsLists.removeAt(index);
}
if(index != -1){
qCInfo(lcActivity) << "Activity/Notification/Error successfully removed from the list.";
qCInfo(lcActivity) << "Updating Activity/Notification/Error view.";
combineActivityLists();
}
}
void ActivityListModel::combineActivityLists()
{
ActivityList resultList;
std::sort(_notificationErrorsLists.begin(), _notificationErrorsLists.end());
resultList.append(_notificationErrorsLists);
std::sort(_notificationLists.begin(), _notificationLists.end());
resultList.append(_notificationLists);
std::sort(_syncFileItemLists.begin(), _syncFileItemLists.end());
resultList.append(_syncFileItemLists);
std::sort(_activityLists.begin(), _activityLists.end());
resultList.append(_activityLists);
beginResetModel();
_finalList.clear();
endResetModel();
beginInsertRows(QModelIndex(), 0, resultList.count());
_finalList = resultList;
endInsertRows();
}
bool ActivityListModel::canFetchActivities() const {
return _accountState->isConnected() && _accountState->account()->capabilities().hasActivities();
}
void ActivityListModel::fetchMore(const QModelIndex &)
{
if (canFetchActivities()) {
startFetchJob();
} else {
_doneFetching = true;
combineActivityLists();
}
}
void ActivityListModel::slotRefreshActivity()
{
_activityLists.clear();
_doneFetching = false;
_currentItem = 0;
if (canFetchActivities()) {
startFetchJob();
} else {
_doneFetching = true;
combineActivityLists();
}
}
void ActivityListModel::slotRemoveAccount()
{
_finalList.clear();
_activityLists.clear();
_currentlyFetching = false;
_doneFetching = false;
_currentItem = 0;
}
}

View File

@@ -17,7 +17,7 @@
#include <QtCore>
#include "ActivityData.h"
#include "activitydata.h"
class QJsonDocument;
@@ -38,24 +38,7 @@ class ActivityListModel : public QAbstractListModel
{
Q_OBJECT
public:
enum DataRole {
ActionIconRole = Qt::UserRole + 1,
UserIconRole,
AccountRole,
ObjectTypeRole,
ActionsLinksRole,
ActionTextRole,
ActionTextColorRole,
ActionRole,
MessageRole,
DisplayPathRole,
PathRole,
LinkRole,
PointInTimeRole,
AccountConnectedRole,
SyncFileStatusRole};
explicit ActivityListModel(AccountState *accountState, QObject* parent = 0);
explicit ActivityListModel(AccountState *accountState, QWidget *parent = nullptr);
QVariant data(const QModelIndex &index, int role) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
@@ -68,7 +51,6 @@ public:
void addNotificationToActivityList(Activity activity);
void clearNotifications();
void addErrorToActivityList(Activity activity);
void addIgnoredFileToList(Activity newActivity);
void addSyncFileItemToActivityList(Activity activity);
void removeActivityFromActivityList(int row);
void removeActivityFromActivityList(Activity activity);
@@ -79,14 +61,10 @@ public slots:
private slots:
void slotActivitiesReceived(const QJsonDocument &json, int statusCode);
void slotIconDownloaded(QByteArray iconData);
signals:
void activityJobStatusCode(int statusCode);
protected:
QHash<int, QByteArray> roleNames() const override;
private:
void startFetchJob();
void combineActivityLists();
@@ -95,20 +73,12 @@ private:
ActivityList _activityLists;
ActivityList _syncFileItemLists;
ActivityList _notificationLists;
ActivityList _listOfIgnoredFiles;
Activity _notificationIgnoredFiles;
ActivityList _notificationErrorsLists;
ActivityList _finalList;
AccountState *_accountState;
bool _currentlyFetching = false;
bool _doneFetching = false;
int _currentItem = 0;
int _totalActivitiesFetched = 0;
int _maxActivities = 100;
int _maxActivitiesDays = 30;
bool _showMoreActivitiesAvailableEntry = false;
};
}
#endif // ACTIVITYLISTMODEL_H

626
src/gui/activitywidget.cpp Normal file
View File

@@ -0,0 +1,626 @@
/*
* Copyright (C) by Klaas Freitag <freitag@owncloud.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 <QtGui>
#include <QtWidgets>
#include "activitylistmodel.h"
#include "activitywidget.h"
#include "syncresult.h"
#include "logger.h"
#include "theme.h"
#include "folderman.h"
#include "syncfileitem.h"
#include "folder.h"
#include "openfilemanager.h"
#include "owncloudpropagator.h"
#include "account.h"
#include "accountstate.h"
#include "accountmanager.h"
#include "activityitemdelegate.h"
#include "QProgressIndicator.h"
#include "notificationconfirmjob.h"
#include "servernotificationhandler.h"
#include "theme.h"
#include "ocsjob.h"
#include "configfile.h"
#include "guiutility.h"
#include "socketapi.h"
#include "ui_activitywidget.h"
#include "syncengine.h"
#include <climits>
// time span in milliseconds which has to be between two
// refreshes of the notifications
#define NOTIFICATION_REQUEST_FREE_PERIOD 15000
namespace OCC {
ActivityWidget::ActivityWidget(AccountState *accountState, QWidget *parent)
: QWidget(parent)
, _ui(new Ui::ActivityWidget)
, _notificationRequestsRunning(0)
, _accountState(accountState)
, _accept(tr("Accept"))
, _remote_share("remote_share")
{
_ui->setupUi(this);
// Adjust copyToClipboard() when making changes here!
#if defined(Q_OS_MAC)
_ui->_activityList->setMinimumWidth(400);
#endif
_model = new ActivityListModel(accountState, this);
ActivityItemDelegate *delegate = new ActivityItemDelegate;
delegate->setParent(this);
_ui->_activityList->setItemDelegate(delegate);
_ui->_activityList->setAlternatingRowColors(true);
_ui->_activityList->setModel(_model);
showLabels();
connect(_model, &ActivityListModel::activityJobStatusCode,
this, &ActivityWidget::slotAccountActivityStatus);
connect(_model, &QAbstractItemModel::rowsInserted, this, &ActivityWidget::rowsInserted);
connect(delegate, &ActivityItemDelegate::primaryButtonClickedOnItemView, this, &ActivityWidget::slotPrimaryButtonClickedOnListView);
connect(delegate, &ActivityItemDelegate::secondaryButtonClickedOnItemView, this, &ActivityWidget::slotSecondaryButtonClickedOnListView);
connect(_ui->_activityList, &QListView::activated, this, &ActivityWidget::slotOpenFile);
connect(ProgressDispatcher::instance(), &ProgressDispatcher::progressInfo,
this, &ActivityWidget::slotProgressInfo);
connect(ProgressDispatcher::instance(), &ProgressDispatcher::itemCompleted,
this, &ActivityWidget::slotItemCompleted);
connect(ProgressDispatcher::instance(), &ProgressDispatcher::syncError,
this, &ActivityWidget::addError);
_removeTimer.setInterval(1000);
}
ActivityWidget::~ActivityWidget()
{
delete _ui;
}
void ActivityWidget::slotProgressInfo(const QString &folder, const ProgressInfo &progress)
{
if (progress.status() == ProgressInfo::Reconcile) {
// Wipe all non-persistent entries - as well as the persistent ones
// in cases where a local discovery was done.
auto f = FolderMan::instance()->folder(folder);
if (!f)
return;
const auto &engine = f->syncEngine();
const auto style = engine.lastLocalDiscoveryStyle();
foreach (Activity activity, _model->errorsList()) {
if (activity._folder != folder){
continue;
}
if (style == LocalDiscoveryStyle::FilesystemOnly){
_model->removeActivityFromActivityList(activity);
continue;
}
if(activity._status == SyncFileItem::Conflict && !QFileInfo(f->path() + activity._file).exists()){
_model->removeActivityFromActivityList(activity);
continue;
}
if(activity._status == SyncFileItem::FileLocked && !QFileInfo(f->path() + activity._file).exists()){
_model->removeActivityFromActivityList(activity);
continue;
}
if(activity._status == SyncFileItem::FileIgnored && !QFileInfo(f->path() + activity._file).exists()){
_model->removeActivityFromActivityList(activity);
continue;
}
if(!QFileInfo(f->path() + activity._file).exists()){
_model->removeActivityFromActivityList(activity);
continue;
}
auto path = QFileInfo(activity._file).dir().path().toUtf8();
if (path == ".")
path.clear();
if(engine.shouldDiscoverLocally(path))
_model->removeActivityFromActivityList(activity);
}
}
if (progress.status() == ProgressInfo::Done) {
// We keep track very well of pending conflicts.
// Inform other components about them.
QStringList conflicts;
foreach (Activity activity, _model->errorsList()) {
if (activity._folder == folder
&& activity._status == SyncFileItem::Conflict) {
conflicts.append(activity._file);
}
}
emit ProgressDispatcher::instance()->folderConflicts(folder, conflicts);
}
}
void ActivityWidget::slotItemCompleted(const QString &folder, const SyncFileItemPtr &item){
auto folderInstance = FolderMan::instance()->folder(folder);
if (!folderInstance)
return;
// check if we are adding it to the right account and if it is useful information (protocol errors)
if(folderInstance->accountState() == _accountState){
qCWarning(lcActivity) << "Item " << item->_file << " retrieved resulted in " << item->_errorString;
Activity activity;
activity._type = Activity::SyncFileItemType; //client activity
activity._status = item->_status;
activity._dateTime = QDateTime::fromString(QDateTime::currentDateTime().toString(), Qt::ISODate);
activity._message = item->_originalFile;
activity._link = folderInstance->accountState()->account()->url();
activity._accName = folderInstance->accountState()->account()->displayName();
activity._file = item->_file;
activity._folder = folder;
if(item->_status == SyncFileItem::NoStatus || item->_status == SyncFileItem::Success){
qCWarning(lcActivity) << "Item " << item->_file << " retrieved successfully.";
activity._message.prepend(" ");
activity._message.prepend(tr("Synced"));
_model->addSyncFileItemToActivityList(activity);
} else {
qCWarning(lcActivity) << "Item " << item->_file << " retrieved resulted in error " << item->_errorString;
activity._subject = item->_errorString;
// add 'protocol error' to activity list
_model->addErrorToActivityList(activity);
}
}
}
void ActivityWidget::addError(const QString &folderAlias, const QString &message,
ErrorCategory category)
{
auto folderInstance = FolderMan::instance()->folder(folderAlias);
if (!folderInstance)
return;
if(folderInstance->accountState() == _accountState){
qCWarning(lcActivity) << "Item " << folderInstance->shortGuiLocalPath() << " retrieved resulted in " << message;
Activity activity;
activity._type = Activity::SyncResultType;
activity._status = SyncResult::Error;
activity._dateTime = QDateTime::fromString(QDateTime::currentDateTime().toString(), Qt::ISODate);
activity._subject = message;
activity._message = folderInstance->shortGuiLocalPath();
activity._link = folderInstance->shortGuiLocalPath();
activity._accName = folderInstance->accountState()->account()->displayName();
activity._folder = folderAlias;
if (category == ErrorCategory::InsufficientRemoteStorage) {
ActivityLink link;
link._label = tr("Retry all uploads");
link._link = folderInstance->path();
link._verb = "";
link._isPrimary = true;
activity._links.append(link);
}
// add 'other errors' to activity list
_model->addErrorToActivityList(activity);
}
}
void ActivityWidget::slotPrimaryButtonClickedOnListView(const QModelIndex &index){
QUrl link = qvariant_cast<QString>(index.data(ActivityItemDelegate::LinkRole));
QString objectType = index.data(ActivityItemDelegate::ObjectTypeRole).toString();
if(!link.isEmpty()){
qCWarning(lcActivity) << "Opening" << link.toString() << "in browser for Notification/Activity" << qvariant_cast<QString>(index.data(ActivityItemDelegate::ActionTextRole));
Utility::openBrowser(link, this);
} else if(objectType == _remote_share){
QVariant customItem = index.data(ActivityItemDelegate::ActionsLinksRole).toList().first();
ActivityLink actionLink = qvariant_cast<ActivityLink>(customItem);
if(actionLink._label == _accept){
qCWarning(lcActivity) << objectType << "action" << actionLink._label << "for" << qvariant_cast<QString>(index.data(ActivityItemDelegate::ActionTextRole));
const QString accountName = index.data(ActivityItemDelegate::AccountRole).toString();
slotSendNotificationRequest(accountName, actionLink._link, actionLink._verb, index.row());
} else {
qCWarning(lcActivity) << "Failed: " << objectType << "action" << actionLink._label << "for" << qvariant_cast<QString>(index.data(ActivityItemDelegate::ActionTextRole));
}
}
}
void ActivityWidget::slotSecondaryButtonClickedOnListView(const QModelIndex &index){
QList<QVariant> customList = index.data(ActivityItemDelegate::ActionsLinksRole).toList();
QString objectType = index.data(ActivityItemDelegate::ObjectTypeRole).toString();
QList<ActivityLink> actionLinks;
foreach(QVariant customItem, customList){
actionLinks << qvariant_cast<ActivityLink>(customItem);
}
if(objectType == _remote_share && actionLinks.first()._label == _accept)
actionLinks.removeFirst();
if(qvariant_cast<Activity::Type>(index.data(ActivityItemDelegate::ActionRole)) == Activity::Type::NotificationType){
const QString accountName = index.data(ActivityItemDelegate::AccountRole).toString();
if(actionLinks.size() == 1){
if(actionLinks.at(0)._verb == "DELETE"){
qCWarning(lcActivity) << "Dismissing Notification/Activity" << qvariant_cast<QString>(index.data(ActivityItemDelegate::ActionTextRole));
slotSendNotificationRequest(index.data(ActivityItemDelegate::AccountRole).toString(), actionLinks.at(0)._link, actionLinks.at(0)._verb, index.row());
}
} else if(actionLinks.size() > 1){
QMenu menu;
qCWarning(lcActivity) << "Displaying menu for Notification/Activity" << qvariant_cast<QString>(index.data(ActivityItemDelegate::ActionTextRole));
foreach (ActivityLink actionLink, actionLinks) {
QAction *menuAction = new QAction(actionLink._label, &menu);
connect(menuAction, &QAction::triggered, this, [this, index, accountName, actionLink] {
this->slotSendNotificationRequest(accountName, actionLink._link, actionLink._verb, index.row());
});
menu.addAction(menuAction);
}
menu.exec(QCursor::pos());
}
}
Activity::Type activityType = qvariant_cast<Activity::Type>(index.data(ActivityItemDelegate::ActionRole));
if(activityType == Activity::Type::SyncFileItemType || activityType == Activity::Type::SyncResultType)
slotOpenFile(index);
}
void ActivityWidget::slotNotificationRequestFinished(int statusCode)
{
int row = sender()->property("activityRow").toInt();
// the ocs API returns stat code 100 or 200 inside the xml if it succeeded.
if (statusCode != OCS_SUCCESS_STATUS_CODE && statusCode != OCS_SUCCESS_STATUS_CODE_V2) {
qCWarning(lcActivity) << "Notification Request to Server failed, leave notification visible.";
} else {
// to do use the model to rebuild the list or remove the item
qCWarning(lcActivity) << "Notification Request to Server successed, rebuilding list.";
_model->removeActivityFromActivityList(row);
}
}
void ActivityWidget::slotRefreshActivities()
{
_model->slotRefreshActivity();
}
void ActivityWidget::slotRefreshNotifications()
{
// start a server notification handler if no notification requests
// are running
if (_notificationRequestsRunning == 0) {
ServerNotificationHandler *snh = new ServerNotificationHandler(_accountState);
connect(snh, &ServerNotificationHandler::newNotificationList,
this, &ActivityWidget::slotBuildNotificationDisplay);
snh->slotFetchNotifications();
} else {
qCWarning(lcActivity) << "Notification request counter not zero.";
}
}
void ActivityWidget::slotRemoveAccount()
{
_model->slotRemoveAccount();
}
void ActivityWidget::showLabels()
{
_ui->_bottomLabel->hide(); // hide whatever was there before
QString t("");
QSetIterator<QString> i(_accountsWithoutActivities);
while (i.hasNext()) {
t.append(tr("<br/>Account %1 does not have activities enabled.").arg(i.next()));
}
if(!t.isEmpty()){
_ui->_bottomLabel->setTextFormat(Qt::RichText);
_ui->_bottomLabel->setText(t);
_ui->_bottomLabel->show();
}
}
void ActivityWidget::slotAccountActivityStatus(int statusCode)
{
if (!(_accountState && _accountState->account())) {
return;
}
if (statusCode == 999) {
_accountsWithoutActivities.insert(_accountState->account()->displayName());
} else {
_accountsWithoutActivities.remove(_accountState->account()->displayName());
}
checkActivityWidgetVisibility();
showLabels();
}
// FIXME: Reused from protocol widget. Move over to utilities.
QString ActivityWidget::timeString(QDateTime dt, QLocale::FormatType format) const
{
const QLocale loc = QLocale::system();
QString dtFormat = loc.dateTimeFormat(format);
static const QRegExp re("(HH|H|hh|h):mm(?!:s)");
dtFormat.replace(re, "\\1:mm:ss");
return loc.toString(dt, dtFormat);
}
void ActivityWidget::storeActivityList(QTextStream &ts)
{
ActivityList activities = _model->activityList();
foreach (Activity activity, activities) {
ts << right
// account name
<< qSetFieldWidth(activity._accName.length())
<< activity._accName
// separator
<< qSetFieldWidth(2) << " - "
// date and time
<< qSetFieldWidth(activity._dateTime.toString().length())
<< activity._dateTime.toString()
// separator
<< qSetFieldWidth(2) << " - "
// fileq
<< qSetFieldWidth(activity._file.length())
<< activity._file
// separator
<< qSetFieldWidth(2) << " - "
// subject
<< qSetFieldWidth(activity._subject.length())
<< activity._subject
// separator
<< qSetFieldWidth(2) << " - "
// message
<< qSetFieldWidth(activity._message.length())
<< activity._message
<< endl;
}
}
void ActivityWidget::checkActivityWidgetVisibility()
{
int accountCount = AccountManager::instance()->accounts().count();
bool hasAccountsWithActivity =
_accountsWithoutActivities.count() != accountCount;
_ui->_activityList->setVisible(hasAccountsWithActivity);
emit hideActivityTab(!hasAccountsWithActivity);
}
void ActivityWidget::slotOpenFile(QModelIndex indx)
{
qCDebug(lcActivity) << indx.isValid() << indx.data(ActivityItemDelegate::PathRole).toString() << QFile::exists(indx.data(ActivityItemDelegate::PathRole).toString());
if (indx.isValid()) {
QString fullPath = indx.data(ActivityItemDelegate::PathRole).toString();
if(!fullPath.isEmpty()){
if (QFile::exists(fullPath)) {
showInFileManager(fullPath);
}
}
}
}
// GUI: Display the notifications.
// All notifications in list are coming from the same account
// but in the _widgetForNotifId hash widgets for all accounts are
// collected.
void ActivityWidget::slotBuildNotificationDisplay(const ActivityList &list)
{
// Whether a new notification was added to the list
bool newNotificationShown = false;
_model->clearNotifications();
foreach (auto activity, list) {
if (_blacklistedNotifications.contains(activity)) {
qCInfo(lcActivity) << "Activity in blacklist, skip";
continue;
}
// handle gui logs. In order to NOT annoy the user with every fetching of the
// notifications the notification id is stored in a Set. Only if an id
// is not in the set, it qualifies for guiLog.
// Important: The _guiLoggedNotifications set must be wiped regularly which
// will repeat the gui log.
// after one hour, clear the gui log notification store
if (_guiLogTimer.elapsed() > 60 * 60 * 1000) {
_guiLoggedNotifications.clear();
}
if (!_guiLoggedNotifications.contains(activity._id)) {
newNotificationShown = true;
_guiLoggedNotifications.insert(activity._id);
// Assemble a tray notification for the NEW notification
ConfigFile cfg;
if(cfg.optionalServerNotifications()){
if(AccountManager::instance()->accounts().count() == 1){
emit guiLog(activity._subject, "");
} else {
emit guiLog(activity._subject, activity._accName);
}
}
}
_model->addNotificationToActivityList(activity);
}
// restart the gui log timer now that we show a new notification
if(newNotificationShown) {
_guiLogTimer.start();
}
}
void ActivityWidget::slotSendNotificationRequest(const QString &accountName, const QString &link, const QByteArray &verb, int row)
{
qCInfo(lcActivity) << "Server Notification Request " << verb << link << "on account" << accountName;
const QStringList validVerbs = QStringList() << "GET"
<< "PUT"
<< "POST"
<< "DELETE";
if (validVerbs.contains(verb)) {
AccountStatePtr acc = AccountManager::instance()->account(accountName);
if (acc) {
NotificationConfirmJob *job = new NotificationConfirmJob(acc->account());
QUrl l(link);
job->setLinkAndVerb(l, verb);
job->setProperty("activityRow", QVariant::fromValue(row));
connect(job, &AbstractNetworkJob::networkError,
this, &ActivityWidget::slotNotifyNetworkError);
connect(job, &NotificationConfirmJob::jobFinished,
this, &ActivityWidget::slotNotifyServerFinished);
job->start();
// count the number of running notification requests. If this member var
// is larger than zero, no new fetching of notifications is started
_notificationRequestsRunning++;
}
} else {
qCWarning(lcActivity) << "Notification Links: Invalid verb:" << verb;
}
}
void ActivityWidget::endNotificationRequest(int replyCode)
{
_notificationRequestsRunning--;
slotNotificationRequestFinished(replyCode);
}
void ActivityWidget::slotNotifyNetworkError(QNetworkReply *reply)
{
NotificationConfirmJob *job = qobject_cast<NotificationConfirmJob *>(sender());
if (!job) {
return;
}
int resultCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
endNotificationRequest(resultCode);
qCWarning(lcActivity) << "Server notify job failed with code " << resultCode;
}
void ActivityWidget::slotNotifyServerFinished(const QString &reply, int replyCode)
{
NotificationConfirmJob *job = qobject_cast<NotificationConfirmJob *>(sender());
if (!job) {
return;
}
endNotificationRequest(replyCode);
qCInfo(lcActivity) << "Server Notification reply code" << replyCode << reply;
}
/* ==================================================================== */
ActivitySettings::ActivitySettings(AccountState *accountState, QWidget *parent)
: QWidget(parent)
, _accountState(accountState)
{
_vbox = new QVBoxLayout(this);
setLayout(_vbox);
_activityWidget = new ActivityWidget(_accountState, this);
_vbox->insertWidget(1, _activityWidget);
connect(_activityWidget, &ActivityWidget::guiLog, this, &ActivitySettings::guiLog);
connect(&_notificationCheckTimer, &QTimer::timeout,
this, &ActivitySettings::slotRegularNotificationCheck);
// Add a progress indicator to spin if the acitivity list is updated.
_progressIndicator = new QProgressIndicator(this);
// connect a model signal to stop the animation
connect(_activityWidget, &ActivityWidget::rowsInserted, _progressIndicator, &QProgressIndicator::stopAnimation);
connect(_activityWidget, &ActivityWidget::rowsInserted, this, &ActivitySettings::slotDisplayActivities);
}
void ActivitySettings::slotDisplayActivities(){
_vbox->removeWidget(_progressIndicator);
}
void ActivitySettings::setNotificationRefreshInterval(std::chrono::milliseconds interval)
{
qCDebug(lcActivity) << "Starting Notification refresh timer with " << interval.count() / 1000 << " sec interval";
_notificationCheckTimer.start(interval.count());
}
void ActivitySettings::slotRemoveAccount()
{
_activityWidget->slotRemoveAccount();
}
void ActivitySettings::slotRefresh()
{
// QElapsedTimer isn't actually constructed as invalid.
if (!_timeSinceLastCheck.contains(_accountState)) {
_timeSinceLastCheck[_accountState].invalidate();
}
QElapsedTimer &timer = _timeSinceLastCheck[_accountState];
// Fetch Activities only if visible and if last check is longer than 15 secs ago
if (timer.isValid() && timer.elapsed() < NOTIFICATION_REQUEST_FREE_PERIOD) {
qCDebug(lcActivity) << "Do not check as last check is only secs ago: " << timer.elapsed() / 1000;
return;
}
if (_accountState && _accountState->isConnected()) {
if (isVisible() || !timer.isValid()) {
_vbox->insertWidget(0, _progressIndicator);
_vbox->setAlignment(_progressIndicator, Qt::AlignHCenter);
_progressIndicator->startAnimation();
_activityWidget->slotRefreshActivities();
}
_activityWidget->slotRefreshNotifications();
timer.start();
}
}
void ActivitySettings::slotRegularNotificationCheck()
{
slotRefresh();
}
bool ActivitySettings::event(QEvent *e)
{
if (e->type() == QEvent::Show) {
slotRefresh();
}
return QWidget::event(e);
}
ActivitySettings::~ActivitySettings()
{
}
}

160
src/gui/activitywidget.h Normal file
View File

@@ -0,0 +1,160 @@
/*
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#ifndef ACTIVITYWIDGET_H
#define ACTIVITYWIDGET_H
#include <QDialog>
#include <QDateTime>
#include <QLocale>
#include <QAbstractListModel>
#include <chrono>
#include "progressdispatcher.h"
#include "owncloudgui.h"
#include "account.h"
#include "activitydata.h"
#include "accountmanager.h"
#include "ui_activitywidget.h"
class QPushButton;
class QProgressIndicator;
namespace OCC {
class Account;
class AccountStatusPtr;
class JsonApiJob;
class ActivityListModel;
namespace Ui {
class ActivityWidget;
}
class Application;
/**
* @brief The ActivityWidget class
* @ingroup gui
*
* The list widget to display the activities, contained in the
* subsequent ActivitySettings widget.
*/
class ActivityWidget : public QWidget
{
Q_OBJECT
public:
explicit ActivityWidget(AccountState *accountState, QWidget *parent = nullptr);
~ActivityWidget();
QSize sizeHint() const override { return ownCloudGui::settingsDialogSize(); }
void storeActivityList(QTextStream &ts);
/**
* Adjusts the activity tab's and some widgets' visibility
*
* Based on whether activities are enabled and whether notifications are
* available.
*/
void checkActivityWidgetVisibility();
public slots:
void slotOpenFile(QModelIndex indx);
void slotRefreshActivities();
void slotRefreshNotifications();
void slotRemoveAccount();
void slotAccountActivityStatus(int statusCode);
void addError(const QString &folderAlias, const QString &message, ErrorCategory category);
void slotProgressInfo(const QString &folder, const ProgressInfo &progress);
void slotItemCompleted(const QString &folder, const SyncFileItemPtr &item);
signals:
void guiLog(const QString &, const QString &);
void rowsInserted();
void hideActivityTab(bool);
void sendNotificationRequest(const QString &accountName, const QString &link, const QByteArray &verb, int row);
private slots:
void slotBuildNotificationDisplay(const ActivityList &list);
void slotSendNotificationRequest(const QString &accountName, const QString &link, const QByteArray &verb, int row);
void slotNotifyNetworkError(QNetworkReply *);
void slotNotifyServerFinished(const QString &reply, int replyCode);
void endNotificationRequest(int replyCode);
void slotNotificationRequestFinished(int statusCode);
void slotPrimaryButtonClickedOnListView(const QModelIndex &index);
void slotSecondaryButtonClickedOnListView(const QModelIndex &index);
private:
void showLabels();
QString timeString(QDateTime dt, QLocale::FormatType format) const;
Ui::ActivityWidget *_ui;
QSet<QString> _accountsWithoutActivities;
QElapsedTimer _guiLogTimer;
QSet<int> _guiLoggedNotifications;
ActivityList _blacklistedNotifications;
QTimer _removeTimer;
// number of currently running notification requests. If non zero,
// no query for notifications is started.
int _notificationRequestsRunning;
ActivityListModel *_model;
AccountState *_accountState;
const QString _accept;
const QString _remote_share;
};
/**
* @brief The ActivitySettings class
* @ingroup gui
*
* Implements a tab for the settings dialog, displaying the three activity
* lists.
*/
class ActivitySettings : public QWidget
{
Q_OBJECT
public:
explicit ActivitySettings(AccountState *accountState, QWidget *parent = nullptr);
~ActivitySettings();
QSize sizeHint() const override { return ownCloudGui::settingsDialogSize(); }
public slots:
void slotRefresh();
void slotRemoveAccount();
void setNotificationRefreshInterval(std::chrono::milliseconds interval);
private slots:
void slotRegularNotificationCheck();
void slotDisplayActivities();
signals:
void guiLog(const QString &, const QString &);
private:
bool event(QEvent *e) override;
ActivityWidget *_activityWidget;
QProgressIndicator *_progressIndicator;
QVBoxLayout *_vbox;
QTimer _notificationCheckTimer;
QHash<AccountState *, QElapsedTimer> _timeSinceLastCheck;
AccountState *_accountState;
};
}
#endif // ActivityWIDGET_H

98
src/gui/activitywidget.ui Normal file
View File

@@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OCC::ActivityWidget</class>
<widget class="QWidget" name="OCC::ActivityWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>652</width>
<height>556</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QListView" name="_activityList">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="lineWidth">
<number>1</number>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="defaultDropAction">
<enum>Qt::IgnoreAction</enum>
</property>
<property name="resizeMode">
<enum>QListView::Adjust</enum>
</property>
<property name="viewMode">
<enum>QListView::ListMode</enum>
</property>
<property name="modelColumn">
<number>0</number>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="_bottomLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">TextLabel</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>_activityList</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@@ -52,7 +52,6 @@
#include <QMenu>
#include <QMessageBox>
#include <QDesktopServices>
#include <QGuiApplication>
class QSocket;
@@ -109,6 +108,7 @@ Application::Application(int &argc, char **argv)
, _userTriggeredConnect(false)
, _debugMode(false)
, _backgroundMode(false)
, _isQuitting(false)
{
_startedAt.start();
@@ -123,15 +123,6 @@ Application::Application(int &argc, char **argv)
// TODO: Can't set this without breaking current config paths
// setOrganizationName(QLatin1String(APPLICATION_VENDOR));
setOrganizationDomain(QLatin1String(APPLICATION_REV_DOMAIN));
// setDesktopFilename to provide wayland compatibility (in general: conformance with naming standards)
// but only on Qt >= 5.7, where setDesktopFilename was introduced
#if (QT_VERSION >= 0x050700)
QString desktopFileName = QString(QLatin1String(LINUX_APPLICATION_ID)
+ QLatin1String(".desktop"));
setDesktopFileName(desktopFileName);
#endif
setApplicationName(_theme->appName());
setWindowIcon(_theme->applicationIcon());
setAttribute(Qt::AA_UseHighDpiPixmaps, true);
@@ -263,11 +254,6 @@ Application::Application(int &argc, char **argv)
// Cleanup at Quit.
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);
_gui->createTray();
}
Application::~Application()
@@ -300,7 +286,7 @@ void Application::slotAccountStateRemoved(AccountState *accountState)
}
// if there is no more account, show the wizard.
if (AccountManager::instance()->accounts().isEmpty()) {
if (!_isQuitting && AccountManager::instance()->accounts().isEmpty()) {
// allow to add a new account if there is non any more. Always think
// about single account theming!
OwncloudSetupWizard::runWizard(this, SLOT(slotownCloudWizardDone(int)));
@@ -323,6 +309,8 @@ void Application::slotAccountStateAdded(AccountState *accountState)
void Application::slotCleanup()
{
_isQuitting = true;
AccountManager::instance()->save();
FolderMan::instance()->unloadAndDeleteAllFolders();
@@ -392,7 +380,7 @@ void Application::slotownCloudWizardDone(int res)
Utility::setLaunchOnStartup(_theme->appName(), _theme->appNameGUI(), true);
}
Systray::instance()->showWindow();
_gui->slotShowSettings();
}
}
@@ -660,9 +648,5 @@ void Application::showSettingsDialog()
_gui->slotShowSettings();
}
void Application::slotGuiIsShowingSettings()
{
emit isShowingSettingsDialog();
}
} // namespace OCC

View File

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

View File

@@ -249,11 +249,6 @@ void ConnectionValidator::slotCapabilitiesRecieved(const QJsonDocument &json)
return;
}
// Check for the directEditing capability
QUrl directEditingURL = QUrl(caps["files"].toObject()["directEditing"].toObject()["url"].toString());
QString directEditingETag = caps["files"].toObject()["directEditing"].toObject()["etag"].toString();
_account->fetchDirectEditors(directEditingURL, directEditingETag);
fetchUser();
}

View File

@@ -14,12 +14,10 @@
*/
#include <QDesktopServices>
#include <QApplication>
#include <QClipboard>
#include <QTimer>
#include <QBuffer>
#include "account.h"
#include "flow2auth.h"
#include "creds/flow2auth.h"
#include <QJsonObject>
#include <QJsonDocument>
#include "theme.h"
@@ -30,17 +28,6 @@ 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()
{
}
@@ -60,23 +47,7 @@ QUrl Flow2Auth::authorisationLink() const
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);
_pollTimer.stop();
// Step 1: Initiate a login, do an anonymous POST request
QUrl url = Utility::concatUrlPath(_account->url().toString(), QLatin1String("/index.php/login/v2"));
@@ -88,18 +59,14 @@ void Flow2Auth::fetchNewToken(const TokenAction action)
auto job = _account->sendRequest("POST", url, req);
job->setTimeout(qMin(30 * 1000ll, job->timeoutMsec()));
QObject::connect(job, &SimpleNetworkJob::finishedSignal, this, [this, action](QNetworkReply *reply) {
QObject::connect(job, &SimpleNetworkJob::finishedSignal, this, [this](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();
}
QString pollToken = json.value("poll").toObject().value("token").toString();
QString pollEndpoint = json.value("poll").toObject().value("endpoint").toString();
QUrl loginUrl = json["login"].toString();
if (reply->error() != QNetworkReply::NoError || jsonParseError.error != QJsonParseError::NoError
|| json.isEmpty() || pollToken.isEmpty() || pollEndpoint.isEmpty() || loginUrl.isEmpty()) {
@@ -118,9 +85,7 @@ void Flow2Auth::fetchNewToken(const TokenAction action)
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;
emit result(Error);
return;
}
@@ -134,50 +99,23 @@ void Flow2Auth::fetchNewToken(const TokenAction action)
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);
_pollTimer.setInterval(polltime.count());
QObject::connect(&_pollTimer, &QTimer::timeout, this, &Flow2Auth::slotPollTimerTimeout);
_pollTimer.start();
if(!_pollTimer.isActive()) {
_pollTimer.start();
// Try to open Browser
if (!QDesktopServices::openUrl(authorisationLink())) {
// We cannot open the browser, then we claim we don't support Flow2Auth.
// Our UI callee should ask the user to copy and open the link.
emit result(NotSupported, QString());
}
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);
_pollTimer.stop();
// Step 2: Poll
QNetworkRequest req;
@@ -194,15 +132,10 @@ void Flow2Auth::slotPollTimerTimeout()
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();
}
QUrl serverUrl = json["server"].toString();
QString loginName = json["loginName"].toString();
QString appPassword = json["appPassword"].toString();
if (reply->error() != QNetworkReply::NoError || jsonParseError.error != QJsonParseError::NoError
|| json.isEmpty() || serverUrl.isEmpty() || loginName.isEmpty() || appPassword.isEmpty()) {
@@ -222,50 +155,26 @@ void Flow2Auth::slotPollTimerTimeout()
}
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;
_pollTimer.start();
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);
emit result(LoggedIn, 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

@@ -25,23 +25,17 @@ 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(Account *account, QObject *parent)
: QObject(parent)
, _account(account)
{
}
~Flow2Auth();
enum Result { NotSupported,
@@ -50,7 +44,6 @@ public:
Q_ENUM(Result);
void start();
void openBrowser();
void copyLinkToClipboard();
QUrl authorisationLink() const;
signals:
@@ -58,29 +51,18 @@ 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();
void result(Flow2Auth::Result result, const QString &user = QString(), const QString &appPassword = QString());
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

@@ -18,7 +18,6 @@
#include "theme.h"
#include "wizard/webview.h"
#include "webflowcredentialsdialog.h"
#include "keychainchunk.h"
using namespace QKeychain;
@@ -76,7 +75,6 @@ private:
QPointer<const WebFlowCredentials> _cred;
};
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
static void addSettingsToJob(Account *account, QKeychain::Job *job)
{
Q_UNUSED(account)
@@ -84,7 +82,6 @@ static void addSettingsToJob(Account *account, QKeychain::Job *job)
settings->setParent(job); // make the job parent to make setting deleted properly
job->setSettings(settings.release());
}
#endif
WebFlowCredentials::WebFlowCredentials()
: _ready(false)
@@ -150,32 +147,25 @@ void WebFlowCredentials::fetchFromKeychain() {
}
void WebFlowCredentials::askFromUser() {
// Determine if the old flow has to be used (GS for now)
// 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);
// LoginFlowV2 > WebViewFlow > OAuth > Shib > Basic
bool useFlow2 = (_account->serverVersionInt() >= Account::makeServerVersion(16, 0, 0));
_askDialog = new WebFlowCredentialsDialog(_account, useFlow2);
_askDialog = new WebFlowCredentialsDialog(_account, useFlow2);
if (!useFlow2) {
QUrl url = _account->url();
QString path = url.path() + "/index.php/login/flow";
url.setPath(path);
_askDialog->setUrl(url);
}
if (!useFlow2) {
QUrl url = _account->url();
QString path = url.path() + "/index.php/login/flow";
url.setPath(path);
_askDialog->setUrl(url);
}
QString msg = tr("You have been logged out of %1 as user %2. Please login again")
.arg(_account->displayName(), _user);
_askDialog->setInfo(msg);
QString msg = tr("You have been logged out of %1 as user %2. Please login again")
.arg(_account->displayName(), _user);
_askDialog->setInfo(msg);
_askDialog->show();
_askDialog->show();
connect(_askDialog, &WebFlowCredentialsDialog::urlCatched, this, &WebFlowCredentials::slotAskFromUserCredentialsProvided);
connect(_askDialog, &WebFlowCredentialsDialog::onClose, this, &WebFlowCredentials::slotAskFromUserCancelled);
});
job->start();
connect(_askDialog, &WebFlowCredentialsDialog::urlCatched, this, &WebFlowCredentials::slotAskFromUserCredentialsProvided);
qCDebug(lcWebFlowCredentials()) << "User needs to reauth!";
}
@@ -209,18 +199,10 @@ void WebFlowCredentials::slotAskFromUserCredentialsProvided(const QString &user,
emit asked();
_askDialog->close();
_askDialog->deleteLater();
delete _askDialog;
_askDialog = nullptr;
}
void WebFlowCredentials::slotAskFromUserCancelled() {
qCDebug(lcWebFlowCredentials()) << "User cancelled reauth!";
emit asked();
_askDialog->deleteLater();
_askDialog = nullptr;
}
bool WebFlowCredentials::stillValid(QNetworkReply *reply) {
if (reply->error() != QNetworkReply::NoError) {
@@ -241,32 +223,33 @@ void WebFlowCredentials::persist() {
// write cert if there is one
if (!_clientSslCertificate.isNull()) {
auto *job = new KeychainChunk::WriteJob(_account,
_user + clientCertificatePEMC,
_clientSslCertificate.toPem());
connect(job, &KeychainChunk::WriteJob::finished, this, &WebFlowCredentials::slotWriteClientCertPEMJobDone);
WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
addSettingsToJob(_account, job);
job->setInsecureFallback(false);
connect(job, &Job::finished, this, &WebFlowCredentials::slotWriteClientCertPEMJobDone);
job->setKey(keychainKey(_account->url().toString(), _user + clientCertificatePEMC, _account->id()));
job->setBinaryData(_clientSslCertificate.toPem());
job->start();
} else {
// no cert, just write credentials
slotWriteClientCertPEMJobDone(nullptr);
slotWriteClientCertPEMJobDone();
}
}
void WebFlowCredentials::slotWriteClientCertPEMJobDone(KeychainChunk::WriteJob *writeJob)
void WebFlowCredentials::slotWriteClientCertPEMJobDone()
{
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);
WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
addSettingsToJob(_account, job);
job->setInsecureFallback(false);
connect(job, &Job::finished, this, &WebFlowCredentials::slotWriteClientKeyPEMJobDone);
job->setKey(keychainKey(_account->url().toString(), _user + clientKeyPEMC, _account->id()));
job->setBinaryData(_clientSslKey.toPem());
job->start();
} else {
// no key, just write credentials
slotWriteClientKeyPEMJobDone(nullptr);
slotWriteClientKeyPEMJobDone();
}
}
@@ -281,7 +264,7 @@ void WebFlowCredentials::writeSingleClientCaCertPEM()
// 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";
qCWarning(lcWebFlowCredentials) << "Maximum client CA cert count exceeded while writing slot" << QString::number(index) << "), cutting off after " << QString::number(_clientSslCaCertificatesMaxCount) << "certs";
_clientSslCaCertificatesWriteQueue.clear();
@@ -289,21 +272,20 @@ void WebFlowCredentials::writeSingleClientCaCertPEM()
return;
}
auto *job = new KeychainChunk::WriteJob(_account,
_user + clientCaCertificatePEMC + QString::number(index),
cert.toPem());
connect(job, &KeychainChunk::WriteJob::finished, this, &WebFlowCredentials::slotWriteClientCaCertsPEMJobDone);
WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
addSettingsToJob(_account, job);
job->setInsecureFallback(false);
connect(job, &Job::finished, this, &WebFlowCredentials::slotWriteClientCaCertsPEMJobDone);
job->setKey(keychainKey(_account->url().toString(), _user + clientCaCertificatePEMC + QString::number(index), _account->id()));
job->setBinaryData(cert.toPem());
job->start();
} else {
slotWriteClientCaCertsPEMJobDone(nullptr);
}
}
void WebFlowCredentials::slotWriteClientKeyPEMJobDone(KeychainChunk::WriteJob *writeJob)
void WebFlowCredentials::slotWriteClientKeyPEMJobDone()
{
if(writeJob)
writeJob->deleteLater();
_clientSslCaCertificatesWriteQueue.clear();
// write ca certs if there are any
@@ -318,16 +300,16 @@ void WebFlowCredentials::slotWriteClientKeyPEMJobDone(KeychainChunk::WriteJob *w
}
}
void WebFlowCredentials::slotWriteClientCaCertsPEMJobDone(KeychainChunk::WriteJob *writeJob)
void WebFlowCredentials::slotWriteClientCaCertsPEMJobDone(QKeychain::Job *incomingJob)
{
// errors / next ca cert?
if (writeJob && !_clientSslCaCertificates.isEmpty()) {
if (incomingJob && !_clientSslCaCertificates.isEmpty()) {
WritePasswordJob *writeJob = static_cast<WritePasswordJob *>(incomingJob);
if (writeJob->error() != NoError) {
qCWarning(lcWebFlowCredentials) << "Error while writing client CA cert" << writeJob->errorString();
}
writeJob->deleteLater();
if (!_clientSslCaCertificatesWriteQueue.isEmpty()) {
// next ca cert
writeSingleClientCaCertPEM();
@@ -337,9 +319,7 @@ void WebFlowCredentials::slotWriteClientCaCertsPEMJobDone(KeychainChunk::WriteJo
// done storing ca certs, time for the password
WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
addSettingsToJob(_account, job);
#endif
job->setInsecureFallback(false);
connect(job, &Job::finished, this, &WebFlowCredentials::slotWriteJobDone);
job->setKey(keychainKey(_account->url().toString(), _user, _account->id()));
@@ -389,15 +369,18 @@ void WebFlowCredentials::forgetSensitiveData() {
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
job->setInsecureFallback(false);
job->setKey(kck);
connect(job, &Job::finished, this, [](QKeychain::Job *job) {
DeletePasswordJob *djob = qobject_cast<DeletePasswordJob *>(job);
djob->deleteLater();
});
job->start();
invalidateToken();
deleteKeychainEntries();
/* IMPORTANT
* 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.
*/
//deleteKeychainEntries();
}
void WebFlowCredentials::setAccount(Account *account) {
@@ -437,29 +420,35 @@ void WebFlowCredentials::slotFinished(QNetworkReply *reply) {
_credentialsValid = true;
/// Used later for remote wipe
_account->writeAppPasswordOnce(_password);
_account->setAppPassword(_password);
}
}
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);
const QString kck = keychainKey(
_account->url().toString(),
_user + clientCertificatePEMC,
_keychainMigration ? QString() : _account->id());
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
addSettingsToJob(_account, job);
job->setInsecureFallback(false);
job->setKey(kck);
connect(job, &Job::finished, this, &WebFlowCredentials::slotReadClientCertPEMJobDone);
job->start();
}
void WebFlowCredentials::slotReadClientCertPEMJobDone(KeychainChunk::ReadJob *readJob)
void WebFlowCredentials::slotReadClientCertPEMJobDone(QKeychain::Job *incomingJob)
{
#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)) {
Q_ASSERT(!incomingJob->insecureFallback()); // If insecureFallback is set, the next test would be pointless
if (_retryOnKeyChainError && (incomingJob->error() == QKeychain::NoBackendAvailable
|| incomingJob->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();
qCInfo(lcWebFlowCredentials) << "Backend unavailable (yet?) Retrying in a few seconds." << incomingJob->errorString();
QTimer::singleShot(10000, this, &WebFlowCredentials::fetchFromKeychainHelper);
_retryOnKeyChainError = false;
return;
@@ -468,6 +457,7 @@ void WebFlowCredentials::slotReadClientCertPEMJobDone(KeychainChunk::ReadJob *re
#endif
// Store PEM in memory
ReadPasswordJob *readJob = static_cast<ReadPasswordJob *>(incomingJob);
if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
QList<QSslCertificate> sslCertificateList = QSslCertificate::fromData(readJob->binaryData(), QSsl::Pem);
if (sslCertificateList.length() >= 1) {
@@ -475,19 +465,25 @@ void WebFlowCredentials::slotReadClientCertPEMJobDone(KeychainChunk::ReadJob *re
}
}
readJob->deleteLater();
// Load key too
auto *job = new KeychainChunk::ReadJob(_account,
_user + clientKeyPEMC,
_keychainMigration);
connect(job, &KeychainChunk::ReadJob::finished, this, &WebFlowCredentials::slotReadClientKeyPEMJobDone);
const QString kck = keychainKey(
_account->url().toString(),
_user + clientKeyPEMC,
_keychainMigration ? QString() : _account->id());
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
addSettingsToJob(_account, job);
job->setInsecureFallback(false);
job->setKey(kck);
connect(job, &Job::finished, this, &WebFlowCredentials::slotReadClientKeyPEMJobDone);
job->start();
}
void WebFlowCredentials::slotReadClientKeyPEMJobDone(KeychainChunk::ReadJob *readJob)
void WebFlowCredentials::slotReadClientKeyPEMJobDone(QKeychain::Job *incomingJob)
{
// Store key in memory
ReadPasswordJob *readJob = static_cast<ReadPasswordJob *>(incomingJob);
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
@@ -502,13 +498,8 @@ void WebFlowCredentials::slotReadClientKeyPEMJobDone(KeychainChunk::ReadJob *rea
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();
@@ -519,20 +510,28 @@ 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);
const QString kck = keychainKey(
_account->url().toString(),
_user + clientCaCertificatePEMC + QString::number(_clientSslCaCertificates.count()),
_keychainMigration ? QString() : _account->id());
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
addSettingsToJob(_account, job);
job->setInsecureFallback(false);
job->setKey(kck);
connect(job, &Job::finished, this, &WebFlowCredentials::slotReadClientCaCertsPEMJobDone);
job->start();
} else {
qCWarning(lcWebFlowCredentials) << "Maximum client CA cert count exceeded while reading, ignoring after" << _clientSslCaCertificatesMaxCount;
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
void WebFlowCredentials::slotReadClientCaCertsPEMJobDone(QKeychain::Job *incomingJob) {
// Store key in memory
ReadPasswordJob *readJob = static_cast<ReadPasswordJob *>(incomingJob);
if (readJob) {
if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
QList<QSslCertificate> sslCertificateList = QSslCertificate::fromData(readJob->binaryData(), QSsl::Pem);
@@ -540,19 +539,15 @@ void WebFlowCredentials::slotReadClientCaCertsPEMJobDone(KeychainChunk::ReadJob
_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();
qCWarning(lcWebFlowCredentials) << "Unable to read client CA cert slot " << QString::number(_clientSslCaCertificates.count()) << readJob->errorString();
}
}
readJob->deleteLater();
}
// Now fetch the actual server password
@@ -562,9 +557,7 @@ void WebFlowCredentials::slotReadClientCaCertsPEMJobDone(KeychainChunk::ReadJob
_keychainMigration ? QString() : _account->id());
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
addSettingsToJob(_account, job);
#endif
job->setInsecureFallback(false);
job->setKey(kck);
connect(job, &Job::finished, this, &WebFlowCredentials::slotReadPasswordJobDone);
@@ -572,7 +565,7 @@ void WebFlowCredentials::slotReadClientCaCertsPEMJobDone(KeychainChunk::ReadJob
}
void WebFlowCredentials::slotReadPasswordJobDone(Job *incomingJob) {
QKeychain::ReadPasswordJob *job = qobject_cast<ReadPasswordJob *>(incomingJob);
QKeychain::ReadPasswordJob *job = static_cast<ReadPasswordJob *>(incomingJob);
QKeychain::Error error = job->error();
// If we could not find the entry try the old entries
@@ -595,8 +588,6 @@ void WebFlowCredentials::slotReadPasswordJobDone(Job *incomingJob) {
}
emit fetched();
job->deleteLater();
// If keychain data was read from legacy location, wipe these entries and store new ones
if (_keychainMigration && _ready) {
_keychainMigration = false;
@@ -607,60 +598,23 @@ void WebFlowCredentials::slotReadPasswordJobDone(Job *incomingJob) {
}
void WebFlowCredentials::deleteKeychainEntries(bool oldKeychainEntries) {
auto startDeleteJob = [this, oldKeychainEntries](QString key) {
auto startDeleteJob = [this, oldKeychainEntries](QString user) {
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
addSettingsToJob(_account, job);
#endif
job->setInsecureFallback(false);
job->setInsecureFallback(true);
job->setKey(keychainKey(_account->url().toString(),
key,
user,
oldKeychainEntries ? QString() : _account->id()));
connect(job, &Job::finished, this, [](QKeychain::Job *job) {
DeletePasswordJob *djob = qobject_cast<DeletePasswordJob *>(job);
djob->deleteLater();
});
job->start();
};
startDeleteJob(_user);
startDeleteJob(_user + clientKeyPEMC);
startDeleteJob(_user + clientCertificatePEMC);
/* 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 -->
for (auto i = 0; i < _clientSslCaCertificates.count(); i++) {
startDeleteJob(_user + clientCaCertificatePEMC + QString::number(i));
}
// <-- FIXME MS@2019-12-07
}
} // namespace OCC
}

View File

@@ -19,11 +19,6 @@ namespace QKeychain {
namespace OCC {
namespace KeychainChunk {
class ReadJob;
class WriteJob;
}
class WebFlowCredentialsDialog;
class WebFlowCredentials : public AbstractCredentials
@@ -66,16 +61,15 @@ private slots:
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 slotReadClientCertPEMJobDone(QKeychain::Job *incomingJob);
void slotReadClientKeyPEMJobDone(QKeychain::Job *incomingJob);
void slotReadClientCaCertsPEMJobDone(QKeychain::Job *incommingJob);
void slotReadPasswordJobDone(QKeychain::Job *incomingJob);
void slotWriteClientCertPEMJobDone(KeychainChunk::WriteJob *writeJob);
void slotWriteClientKeyPEMJobDone(KeychainChunk::WriteJob *writeJob);
void slotWriteClientCaCertsPEMJobDone(KeychainChunk::WriteJob *writeJob);
void slotWriteClientCertPEMJobDone();
void slotWriteClientKeyPEMJobDone();
void slotWriteClientCaCertsPEMJobDone(QKeychain::Job *incomingJob);
void slotWriteJobDone(QKeychain::Job *);
private:
@@ -89,7 +83,7 @@ private:
void writeSingleClientCaCertPEM();
/*
* Since we're limited by Windows limits, we just create our own
* 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
@@ -127,6 +121,6 @@ protected:
WebFlowCredentialsDialog *_askDialog;
};
} // namespace OCC
}
#endif // WEBFLOWCREDENTIALS_H

View File

@@ -4,9 +4,6 @@
#include <QLabel>
#include "theme.h"
#include "application.h"
#include "owncloudgui.h"
#include "headerbanner.h"
#include "wizard/owncloudwizardcommon.h"
#include "wizard/webview.h"
#include "wizard/flow2authwidget.h"
@@ -22,59 +19,31 @@ WebFlowCredentialsDialog::WebFlowCredentialsDialog(Account *account, bool useFlo
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
_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();
_containerLayout->addWidget(_infoLabel);
_layout->addWidget(_infoLabel);
if (_useFlow2) {
_flow2AuthWidget = new Flow2AuthWidget();
_containerLayout->addWidget(_flow2AuthWidget);
_flow2AuthWidget = new Flow2AuthWidget(account);
_layout->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);
connect(_flow2AuthWidget, &Flow2AuthWidget::urlCatched, this, &WebFlowCredentialsDialog::urlCatched);
} else {
_webView = new WebView();
_containerLayout->addWidget(_webView);
_layout->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->hide();
_containerLayout->addWidget(_errorLabel);
_layout->addWidget(_errorLabel);
WizardCommon::initErrorLabel(_errorLabel);
_layout->addLayout(_containerLayout);
setLayout(_layout);
customizeStyle();
}
void WebFlowCredentialsDialog::closeEvent(QCloseEvent* e) {
@@ -83,17 +52,11 @@ void WebFlowCredentialsDialog::closeEvent(QCloseEvent* e) {
if (_webView) {
// Force calling WebView::~WebView() earlier so that _profile and _page are
// deleted in the correct order.
_webView->deleteLater();
_webView = nullptr;
delete _webView;
}
if (_flow2AuthWidget) {
_flow2AuthWidget->resetAuth();
_flow2AuthWidget->deleteLater();
_flow2AuthWidget = nullptr;
}
emit onClose();
if (_flow2AuthWidget)
delete _flow2AuthWidget;
}
void WebFlowCredentialsDialog::setUrl(const QUrl &url) {
@@ -106,9 +69,6 @@ void WebFlowCredentialsDialog::setInfo(const QString &msg) {
}
void WebFlowCredentialsDialog::setError(const QString &error) {
// bring window to top
slotShowSettingsDialog();
if (_useFlow2 && _flow2AuthWidget) {
_flow2AuthWidget->setError(error);
return;
@@ -122,49 +82,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

@@ -5,14 +5,12 @@
#include <QUrl>
#include "accountfwd.h"
#include "creds/flow2auth.h"
class QLabel;
class QVBoxLayout;
namespace OCC {
class HeaderBanner;
class WebView;
class Flow2AuthWidget;
@@ -32,21 +30,11 @@ public:
protected:
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:
void urlCatched(const QString user, const QString pass, const QString host);
void styleChanged();
void onActivate();
void onClose();
private:
void customizeStyle();
bool _useFlow2;
Flow2AuthWidget *_flow2AuthWidget;
@@ -55,10 +43,8 @@ private:
QLabel *_errorLabel;
QLabel *_infoLabel;
QVBoxLayout *_layout;
QVBoxLayout *_containerLayout;
HeaderBanner *_headerBanner;
};
} // namespace OCC
}
#endif // WEBFLOWCREDENTIALSDIALOG_H

View File

@@ -914,8 +914,6 @@ void Folder::slotItemCompleted(const SyncFileItemPtr &item)
_folderWatcher->removePath(path() + item->_file);
_folderWatcher->addPath(path() + item->destination());
break;
default:
break;
}
}
@@ -1206,7 +1204,7 @@ QString FolderDefinition::absoluteJournalPath() const
QString FolderDefinition::defaultJournalPath(AccountPtr account)
{
return SyncJournalDb::makeDbName(account->url(), targetPath, account->credentials()->user());
return SyncJournalDb::makeDbName(localPath, account->url(), targetPath, account->credentials()->user());
}
} // namespace OCC

View File

@@ -57,7 +57,7 @@ public:
QString alias;
/// path on local machine
QString localPath;
/// path to the journal, usually in QStandardPaths::AppDataLocation
/// path to the journal, usually relative to localPath
QString journalPath;
/// path on remote
QString targetPath;

View File

@@ -210,27 +210,6 @@ void FolderMan::setupFoldersHelper(QSettings &settings, AccountStatePtr account,
folderDefinition.journalPath = defaultJournalPath;
}
// Migration #2: journalPath now in DataAppDir, not root of local tree (cross-platform persistent user roaming files)
if (folderDefinition.journalPath.at(0) == QChar('.')) {
QFile oldJournal(folderDefinition.localPath + "/" + folderDefinition.journalPath);
QFile oldJournalShm(folderDefinition.localPath + "/" + folderDefinition.journalPath.append("-shm"));
QFile oldJournalWal(folderDefinition.localPath + "/" + folderDefinition.journalPath.append("-wal"));
folderDefinition.journalPath = defaultJournalPath;
socketApi()->slotUnregisterPath(folderAlias);
auto settings = account->settings();
Folder *f = addFolderInternal(folderDefinition, account.data());
f->saveToSettings();
oldJournal.remove();
oldJournalShm.remove();
oldJournalWal.remove();
return;
}
// Migration: ._ files sometimes don't work
// So if the configured journalPath is the default one ("._sync_*.db")
// but the current default doesn't have the underscore, switch to the

View File

@@ -40,7 +40,7 @@ namespace OCC {
FolderStatusDelegate::FolderStatusDelegate()
: QStyledItemDelegate()
{
customizeStyle();
m_moreIcon = QIcon(QLatin1String(":/client/resources/more.svg"));
}
QString FolderStatusDelegate::addFolderText()
@@ -273,11 +273,6 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
rect.setHeight(texts.count() * subFm.height() + 2 * 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->setPen(QColor(0xaa, 0xaa, 0xaa));
painter->drawRoundedRect(QStyle::visualRect(option.direction, option.rect, rect),
@@ -295,11 +290,6 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
textRect.translate(0, textRect.height());
}
// restore previous state
painter->setBrush(oldBrush);
painter->setPen(oldPen);
painter->setFont(oldFont);
h = rect.bottom() + margin;
};
@@ -359,7 +349,7 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
btnOpt.arrowType = Qt::NoArrow;
btnOpt.subControls = QStyle::SC_ToolButton;
btnOpt.rect = optionsButtonVisualRect;
btnOpt.icon = _iconMore;
btnOpt.icon = m_moreIcon;
int e = QApplication::style()->pixelMetric(QStyle::PM_ButtonIconSize);
btnOpt.iconSize = QSize(e,e);
QApplication::style()->drawComplexControl(QStyle::CC_ToolButton, &btnOpt, painter);
@@ -433,14 +423,5 @@ QRect FolderStatusDelegate::errorsListRect(QRect within)
return within;
}
void FolderStatusDelegate::slotStyleChanged()
{
customizeStyle();
}
void FolderStatusDelegate::customizeStyle()
{
_iconMore = Theme::createColorAwareIcon(QLatin1String(":/client/resources/more.svg"));
}
} // namespace OCC

View File

@@ -26,6 +26,7 @@ class FolderStatusDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
QIcon m_moreIcon;
FolderStatusDelegate();
enum datarole { FolderAliasRole = Qt::UserRole + 100,
@@ -61,16 +62,9 @@ public:
static QRect errorsListRect(QRect within);
static int rootFolderHeightWithoutErrors(const QFontMetrics &fm, const QFontMetrics &aliasFm);
public slots:
void slotStyleChanged();
private:
void customizeStyle();
static QString addFolderText();
QPersistentModelIndex _pressedIndex;
QIcon _iconMore;
};
} // 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.")
+ QString("\n") + x->_lastErrorString);
} else {
return tr("Fetching folder list from server");
return tr("Fetching folder list from server...");
}
break;
default:
@@ -1099,15 +1099,15 @@ void FolderStatusModel::slotFolderSyncStateChange(Folder *f)
}
QString message;
if (pos <= 0) {
message = tr("Waiting");
message = tr("Waiting...");
} else {
message = tr("Waiting for %n other folder(s)", "", pos);
message = tr("Waiting for %n other folder(s)...", "", pos);
}
pi = SubFolderInfo::Progress();
pi._overallSyncString = message;
} else if (state == SyncResult::SyncPrepare) {
pi = SubFolderInfo::Progress();
pi._overallSyncString = tr("Preparing to sync");
pi._overallSyncString = tr("Preparing to sync...");
} else if (state == SyncResult::Problem || state == SyncResult::Success) {
// Reset the progress info after a sync.
pi = SubFolderInfo::Progress();

View File

@@ -57,13 +57,6 @@ GeneralSettings::GeneralSettings(QWidget *parent)
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()));
connect(_ui->autostartCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::slotToggleLaunchOnStartup);
@@ -77,8 +70,7 @@ GeneralSettings::GeneralSettings(QWidget *parent)
connect(_ui->legalNoticeButton, &QPushButton::clicked, this, &GeneralSettings::slotShowLegalNotice);
loadMiscSettings();
// updater info now set in: customizeStyle
//slotUpdateInfo();
slotUpdateInfo();
// misc
connect(_ui->monoIconsCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::saveMiscSettings);
@@ -117,8 +109,6 @@ GeneralSettings::GeneralSettings(QWidget *parent)
// accountAdded means the wizard was finished and the wizard might change some options.
connect(AccountManager::instance(), &AccountManager::accountAdded, this, &GeneralSettings::loadMiscSettings);
customizeStyle();
}
GeneralSettings::~GeneralSettings()
@@ -159,11 +149,7 @@ void GeneralSettings::slotUpdateInfo()
connect(updater, &OCUpdater::downloadStateChanged, this, &GeneralSettings::slotUpdateInfo, Qt::UniqueConnection);
connect(_ui->restartButton, &QAbstractButton::clicked, updater, &OCUpdater::slotStartInstaller, Qt::UniqueConnection);
connect(_ui->restartButton, &QAbstractButton::clicked, qApp, &QApplication::quit, Qt::UniqueConnection);
QString status = updater->statusString();
Theme::replaceLinkColorStringBackgroundAware(status);
_ui->updateStateLabel->setText(status);
_ui->updateStateLabel->setText(updater->statusString());
_ui->restartButton->setVisible(updater->downloadState() == OCUpdater::DownloadComplete);
} else {
// can't have those infos from sparkle currently
@@ -225,20 +211,4 @@ void GeneralSettings::slotShowLegalNotice()
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

View File

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

View File

@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>516</width>
<width>785</width>
<height>523</height>
</rect>
</property>

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

View File

@@ -1,93 +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$
**
****************************************************************************/
#ifndef HEADERBANNER_H
#define HEADERBANNER_H
#include <QWidget>
class QLabel;
class QGridLayout;
class QPixmap;
namespace OCC {
class HeaderBanner : public QWidget
{
Q_OBJECT
public:
HeaderBanner(QWidget *parent = 0);
void setup(const QString &title, const QPixmap &logo, const QPixmap &banner,
const Qt::TextFormat titleFormat, const QString &styleSheet);
protected:
void paintEvent(QPaintEvent *event) override;
private:
QLabel *titleLabel;
QLabel *logoLabel;
QGridLayout *layout;
QPixmap bannerPixmap;
};
} // namespace OCC
#endif // HEADERBANNER_H

View File

@@ -23,9 +23,6 @@ IconJob::IconJob(const QUrl &url, QObject *parent) :
this, &IconJob::finished);
QNetworkRequest request(url);
#if (QT_VERSION >= 0x050600)
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
#endif
_accessManager.get(request);
}

View File

@@ -24,7 +24,7 @@ LegalNotice::LegalNotice(QDialog *parent)
{
_ui->setupUi(this);
QString notice = tr("<p>Copyright 2017-2020 Nextcloud GmbH<br />"
QString notice = tr("<p>Copyright 2017-2018 Nextcloud GmbH<br />"
"Copyright 2012-2018 ownCloud GmbH</p>");
notice += tr("<p>Licensed under the GNU General Public License (GPL) Version 2.0 or any later version.</p>");

View File

@@ -32,11 +32,6 @@ NavigationPaneHelper::NavigationPaneHelper(FolderMan *folderMan)
_updateCloudStorageRegistryTimer.setSingleShot(true);
connect(&_updateCloudStorageRegistryTimer, &QTimer::timeout, this, &NavigationPaneHelper::updateCloudStorageRegistry);
// Ensure that the folder integration stays persistent in Explorer,
// the uninstaller removes the folder upon updating the client.
_showInExplorerNavigationPane = !_showInExplorerNavigationPane;
setShowInExplorerNavigationPane(!_showInExplorerNavigationPane);
}
void NavigationPaneHelper::setShowInExplorerNavigationPane(bool show)
@@ -144,9 +139,7 @@ void NavigationPaneHelper::updateCloudStorageRegistry()
#else
// This code path should only occur on Windows (the config will be false, and the checkbox invisible on other platforms).
// Add runtime checks rather than #ifdefing out the whole code to help catch breakages when developing on other platforms.
// Don't crash, by any means!
// Q_ASSERT(false);
Q_ASSERT(false);
#endif
}
}

View File

@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>516</width>
<width>563</width>
<height>444</height>
</rect>
</property>

View File

@@ -73,17 +73,6 @@ void OcsShareJob::setPassword(const QString &shareId, const QString &password)
start();
}
void OcsShareJob::setNote(const QString &shareId, const QString &note)
{
appendPath(shareId);
setVerb("PUT");
addParam(QString::fromLatin1("note"), note);
_value = note;
start();
}
void OcsShareJob::setPublicUpload(const QString &shareId, bool publicUpload)
{
appendPath(shareId);

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