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

Compare commits

...

372 Commits

Author SHA1 Message Date
Camila San
e0b32c19e4 Bump version to 2.6.0
Signed-off-by: Camila San <hello@camila.codes>
2019-09-27 17:01:24 +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
Camila Ayres
acb9fc7c8e Merge pull request #1408 from nextcloud/flow2-ui-qt5-compat
Qt5.5 compatiblity patch for login flow V2 + UI improvement
2019-09-06 17:56:09 +02:00
Camila Ayres
081f9741e4 Merge branch 'master' into flow2-ui-qt5-compat 2019-09-06 17:32:40 +02:00
Camila Ayres
a70b7d5852 Merge pull request #1405 from DominiqueFuchs/w10-start-logo
Full-Scaled new logo in Windows 10 start menu tile
2019-09-06 17:22:10 +02:00
Michael Schuster
8b2c47cdcb Remove old Qt 5.5 patch for Xenial
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-09-06 17:05:18 +02:00
Camila Ayres
a3beb9ba41 Merge branch 'master' into w10-start-logo 2019-09-06 17:02:06 +02:00
Camila Ayres
c4dfe576d0 Merge pull request #1399 from DominiqueFuchs/master
Integrated registry check on windows when hasDarkSystray is called.
2019-09-06 17:01:53 +02:00
Camila Ayres
0841fe8dd3 Merge branch 'master' into master 2019-09-06 16:50:27 +02:00
Camila Ayres
c3b270ba26 Merge pull request #1401 from DominiqueFuchs/logo-update
Logo update
2019-09-06 16:37:59 +02:00
Camila Ayres
9ab5241459 Merge branch 'master' into w10-start-logo 2019-09-06 16:03:54 +02:00
Camila Ayres
ec603b061d Merge branch 'master' into logo-update 2019-09-06 15:42:02 +02:00
Michael Schuster
b792a627e2 Qt5.5 compatiblity patch for login flow V2 + UI improvment
Removes the right-click function for the "Re-open browser" buttons because
they are not intuitive for the user.

Adds a dedicated "Copy link" button.

Implements Qt 5.5 fixes based on: https://github.com/nextcloud/desktop/pull/1392

Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-09-06 14:40:54 +02:00
Michael Schuster
0e8c6a176b Merge branch 'master' of https://github.com/nextcloud/desktop 2019-09-06 14:39:10 +02:00
Camila Ayres
0353724472 Merge pull request #1374 from Ram-Z/per-directory-ignore
Read .sync_exclude.lst in each subdirectory
2019-09-06 14:20:34 +02:00
Martin Sucha
bbb295fea4 Use newer digest algorithms in TLS error dialog
MD5 has been broken for a long time now and SHA1 has been
deprecated as well. SHA1 is not used when issuing new
publicly trusted certificates since 1 January 2016[1] and
there are more and more effective attacks[2][3] against it,
so display SHA1 fingerprint only for old certificates
to encourage use of safer digests by users.

So, we display SHA-256 and SHA-512 fingerprints instead in
the common case.

[1] https://cabforum.org/wp-content/uploads/CA-Browser-Forum-BR-1.6.5.pdf
[2] https://shattered.io/static/shattered.pdf
[3] https://eprint.iacr.org/2019/459.pdf

Signed-off-by: Martin Sucha <git@mm.ms47.eu>
2019-09-06 14:07:07 +02:00
Camila Ayres
d36ddb752a Merge branch 'master' into per-directory-ignore 2019-09-06 13:35:28 +02:00
Dominique Fuchs
680f70aa1a Merge branch 'master' into w10-start-logo 2019-09-06 09:30:12 +02:00
Dominique Fuchs
4ea64bf9ee Final commit to resolve #22 (at least in desktop repo, final bits have to be set in client-building).
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-09-06 09:24:06 +02:00
Dominique Fuchs
c1dd0079d9 Modified CMakeLists (src/gui/) to install VisualElements logo files in appropiate install output folder
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-09-06 08:35:46 +02:00
Dominique Fuchs
f33d0a322d Renamed files for consistent sheme & use in CMakeLists (/src/gui)
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-09-06 08:23:48 +02:00
Dominique Fuchs
429c9afd60 Added files for start menu icon (70 & 150px) on Windows 10
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-09-06 07:44:49 +02:00
Nextcloud bot
a6653af440 [tx-robot] updated from transifex 2019-09-06 03:03:36 +00:00
Dominique Fuchs
abb2711d26 Merge branch 'master' into logo-update 2019-09-05 21:38:17 +02:00
Dominique Fuchs
0a5ba5a3c7 Merge pull request #1402 from DominiqueFuchs/gitignore
Updated .gitignore to integrate unwanted files when working with VSC …
2019-09-05 20:44:43 +02:00
Dominique Fuchs
0a643c06e7 Merge branch 'master' into gitignore 2019-09-05 20:31:23 +02:00
Dominique Fuchs
6f5dcfa78b Complementary renaming in theme.qrc
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-09-05 19:59:49 +02:00
Dominique Fuchs
e2f7947966 Check for possible case failure when building on drone
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-09-05 19:52:03 +02:00
Dominique Fuchs
4b9e8274b5 Merge branch 'logo-update' of https://github.com/DominiqueFuchs/desktop into logo-update 2019-09-05 20:59:34 +02:00
Dominique Fuchs
7aefa5afaf Updated CmakeLists for gui part to correct old variable (OUTFILE_BASE[NAME]) that changed by recent update of AddAppIcon modules. Fixes wrong naming of resource pack in build output.
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-09-05 20:58:05 +02:00
John Molakvoæ
1606cf2e09 Use newer digest algorithms in TLS error dialog (#1404)
Use newer digest algorithms in TLS error dialog
2019-09-05 07:12:36 +02:00
Dominique Fuchs
464cdabecf Merge branch 'master' into gitignore 2019-09-05 07:00:13 +02:00
Dominique Fuchs
defa0b2781 Merge branch 'master' into logo-update 2019-09-05 06:59:34 +02:00
John Molakvoæ
acd2425250 Merge branch 'master' into ssl-error-digests 2019-09-05 06:44:19 +02:00
Nextcloud bot
aa37a67729 [tx-robot] updated from transifex 2019-09-05 03:09:38 +00:00
Michael Schuster
1b2e64788d Merge branch 'master' into ssl-error-digests 2019-09-05 01:07:20 +02:00
Roeland Jago Douma
b8ef78205a Merge pull request #1256 from meskobalazs/master
Marking unused strings as unstranslatable
2019-09-04 23:06:08 +02:00
Martin Sucha
3e6422a889 Use newer digest algorithms in TLS error dialog
MD5 has been broken for a long time now and SHA1 has been
deprecated as well. SHA1 is not used when issuing new
publicly trusted certificates since 1 January 2016[1] and
there are more and more effective attacks[2][3] against it,
so display SHA1 fingerprint only for old certificates
to encourage use of safer digests by users.

So, we display SHA-256 and SHA-512 fingerprints instead in
the common case.

[1] https://cabforum.org/wp-content/uploads/CA-Browser-Forum-BR-1.6.5.pdf
[2] https://shattered.io/static/shattered.pdf
[3] https://eprint.iacr.org/2019/459.pdf

Signed-off-by: Martin Sucha <git@mm.ms47.eu>
2019-09-04 22:06:12 +02:00
Michael Schuster
0bcc923a8a Merge branch 'master' into gitignore 2019-09-04 21:12:48 +02:00
Roeland Jago Douma
0cb1f4d14b Merge pull request #1394 from nextcloud/webflow-client-ssl-ca-chain
Windows: Workaround for CredWriteW used by QtKeychain
2019-09-04 20:04:45 +02:00
Dominique Fuchs
7ac6df24a3 Updated ECMAddAppIcon to newest ver fromhttps://github.com/KDE/extra-cmake-modules/blob/master/modules/ECMAddAppIcon.cmake with modifications for nc workflow (incl. png2imagemagick)
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-09-04 16:51:16 +02:00
Michael Schuster
5ef9600007 Merge branch 'master' into webflow-client-ssl-ca-chain 2019-09-04 12:56:22 +02:00
Nextcloud bot
fcc84b6dc4 [tx-robot] updated from transifex 2019-09-04 03:07:24 +00:00
Dominique Fuchs
88dcbad790 Updated theme.qrc to include new icon logo files
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-09-03 19:38:01 +02:00
Dominique Fuchs
7e4323c7de Revert-corrected naming of icon files due to limitations by ECMAddAppIcon
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-09-03 18:27:37 +02:00
Dominique Fuchs
af831a7653 updated resources for new logo icon files
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-09-03 18:17:46 +02:00
Dominique Fuchs
8bd85fa71d Corrected sized icon versions to up2date windows guidelines (see also ECMAddAppIcon.cmake)
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-09-03 18:05:06 +02:00
Dominique Fuchs
9d225452bd Updated svg version of nc icon
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-09-03 18:00:51 +02:00
Dominique Fuchs
b79a82cc3f Updated AddAppIconMacro (esp. for new macOS retina guidelines) and corrected filenames to not change pattern in cmake scripts
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-09-03 17:54:22 +02:00
Dominique Fuchs
0380271499 Updated cmake module to current version (esp. worthy for newer windows versions reg. the sizes) in https://github.com/KDE/extra-cmake-modules/blob/master/modules/ECMAddAppIcon.cmake
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-09-03 17:25:05 +02:00
Dominique Fuchs
2e243e5a38 deleted old icon files (not sidebar logo files atm)
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-09-03 17:19:20 +02:00
Dominique Fuchs
e56b1a082b Updated .gitignore to integrate unwanted files when working with VSC or VS2019
Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-09-03 10:59:21 +02:00
Dominique Fuchs
ef9c092662 Added png export series of new logo based on svg material in promo branch.
Decided for new naming sheme too keep old variants in repo untill all changes (also in helper repos like client-building) are done.

Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-09-03 09:42:52 +02:00
Nextcloud bot
86f559add4 [tx-robot] updated from transifex 2019-09-03 03:04:22 +00:00
Dominique Fuchs
5bcff7dab6 Merge branch 'master' into master 2019-09-02 06:56:30 +02:00
Nextcloud bot
812e688572 [tx-robot] updated from transifex 2019-09-02 03:03:47 +00:00
Dominique Fuchs
8f3bf3313e Integrated registry check on windows when hasDarkSystray is called.
Return value determines which theme is applied for monochrome variant.
Fixes #1276 but only when monochrome option is toggled or nc starts, not automatically when changing windows theme.

Signed-off-by: Dominique Fuchs <32204802+DominiqueFuchs@users.noreply.github.com>
2019-09-01 22:13:14 +02:00
Nextcloud bot
87bf0d9a7e [tx-robot] updated from transifex 2019-09-01 03:09:08 +00:00
Nextcloud bot
bb9140d075 [tx-robot] updated from transifex 2019-08-31 03:03:58 +00:00
Michael Schuster
267224b258 Merge branch 'master' into webflow-client-ssl-ca-chain 2019-08-30 05:38:07 +02:00
Michael Schuster
61884d1ada fix indents, add comment
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-08-30 05:35:36 +02:00
Nextcloud bot
29ff9f403e [tx-robot] updated from transifex 2019-08-30 03:06:24 +00:00
Michael Schuster
b52292db92 Windows: Workaround for CredWriteW used by QtKeychain
Saving all client CA's within one credential may result in:
  Error: "Credential size exceeds maximum size of 2560"

Client CA certificates are now being stored in separate slots
within the keychain and are being processed by a queue mechanism.

IMPORTANT TODO:
forgetSensitiveData(): Invoked by "Log out" & "Remove account"

- Remove client CA certs and KEY!
  (uncomment "//deleteKeychainEntries();" )

  Disabled as long as selecting another cert is not supported by the UI.

Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-08-30 04:56:01 +02:00
Nextcloud bot
57d0a17744 [tx-robot] updated from transifex 2019-08-29 03:03:24 +00:00
Roeland Jago Douma
85a2860e86 Merge pull request #1389 from nextcloud/webflow-client-ssl
Adds SSL client cert storage to webflow + Login Flow v2
2019-08-28 07:40:39 +02:00
Nextcloud bot
7fc95c4c52 [tx-robot] updated from transifex 2019-08-28 03:10:20 +00:00
Michael Schuster
2c4336ab2a Merge branch 'master' into webflow-client-ssl 2019-08-28 00:30:36 +02:00
Roeland Jago Douma
d584bedcb6 Also store the CACertificates of the client side certificate
Else authentication will still fail in setups that have a chain of
certificates supplied.

Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
2019-08-27 09:55:41 +02:00
Nextcloud bot
5c34f9e247 [tx-robot] updated from transifex 2019-08-27 03:04:46 +00:00
Michael Schuster
dbde585049 Adds SSL client cert storage to webflow + Login Flow v2
The previous commit 50cd6af394 - Build a webflowcredentials
changed:

src/gui/wizard/flow2authcredspage.cpp in line 135 to use WebFlowCredentials
instead of HttpCredentials.
But the WebFlowCredentials class didn't include code to store and load SSL client
certificates and keys from the keychain.

This commit migrates the useful stuff from the old HttpCredentials class
into WebFlowCredentials.

Successfully tested on Windows. Please test on other systems and verify it's safe! :)

Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-08-27 03:32:21 +02:00
Michael Schuster
18404a128b Merge pull request #1384 from nextcloud/login-flow-v2
Login flow v2
2019-08-26 21:48:40 +02:00
Roeland Jago Douma
302ca0e04e Fix some compiler warnings
Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
2019-08-26 20:41:14 +02:00
Roeland Jago Douma
50cd6af394 Build a webflowcredentials
Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
2019-08-26 20:04:23 +02:00
Michael Schuster
fd8345ccbe Login Flow V2: adds re-auth upon logout, improvements
- Implements re-auth upon logout -> login
- Improves UI and security

TODO:
- SSL: Client certificate login is possible at the first time only but missing after relaunch

Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-08-26 20:03:15 +02:00
Michael Schuster
7add98e9a3 UI: don't let Flow2 and OAuth hide the wizard
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-08-26 20:03:15 +02:00
Michael Schuster
aa93a04fd6 fix comment typo in httpcredentials.cpp
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-08-26 20:03:15 +02:00
Michael Schuster
628bab92c4 fix comment typo in webflowcredentials.cpp
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-08-26 20:03:15 +02:00
Michael Schuster
12f2ea6728 Login Flow V2: remove static test url
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-08-26 20:03:15 +02:00
Michael Schuster
8fa55b97b4 Login Flow V2: 1st implementation, cleanup
This is the first draft of the Login Flow V2 authorization method.

See: https://docs.nextcloud.com/server/latest/developer_manual/client_apis/LoginFlow/index.html#login-flow-v2

- Adds the Login Fĺow V2 auth method
- Adds ability to reinitiate a new request via UI

TODO:
- Implement re-auth upon logout -> login
- Improve UI
- SSL: Client certificate login is possible at the first time only but missing after relaunch

Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-08-26 20:03:15 +02:00
Michael Schuster
2742411abd Login Flow V2: 1st test-implementation
Signed-off-by: Michael Schuster <michael@schuster.ms>
2019-08-26 20:03:15 +02:00
Nextcloud bot
9941d49579 [tx-robot] updated from transifex 2019-08-26 03:00:53 +00:00
Nextcloud bot
93f6bb2740 [tx-robot] updated from transifex 2019-08-25 03:03:22 +00:00
Nextcloud bot
95c6dd32e9 [tx-robot] updated from transifex 2019-08-24 03:03:25 +00:00
Nextcloud bot
c5f8b00a6b [tx-robot] updated from transifex 2019-08-21 03:04:55 +00:00
Nextcloud bot
7135d441e6 [tx-robot] updated from transifex 2019-08-20 03:04:04 +00:00
Roeland Jago Douma
e0a1d78441 Merge pull request #1225 from edent/patch-1
Typo
2019-08-19 15:39:10 +02:00
Nextcloud bot
eb31925a00 [tx-robot] updated from transifex 2019-08-18 03:08:13 +00:00
Nextcloud bot
6f4de8f503 [tx-robot] updated from transifex 2019-08-17 03:06:09 +00:00
Nextcloud bot
db83302546 [tx-robot] updated from transifex 2019-08-16 03:01:55 +00:00
Nextcloud bot
c74db8677b [tx-robot] updated from transifex 2019-08-15 03:02:27 +00:00
Samir Benmendil
5e3c2d2a96 Fix fullPath matching
Signed-off-by: Samir Benmendil <me@rmz.io>
2019-08-15 03:00:26 +01:00
Samir Benmendil
7a9b13a563 Manual exclude are anchored to _localPath by default
This makes a lot of sense, since there should be no file to be synced
outside of _localPath.

Signed-off-by: Samir Benmendil <me@rmz.io>
2019-08-15 03:00:26 +01:00
Samir Benmendil
e27645cb00 Extract loadExcludeFile and use it when discovering new exclude files
Signed-off-by: Samir Benmendil <me@rmz.io>
2019-08-15 03:00:26 +01:00
Samir Benmendil
34fcb13e78 Never ignore .sync-exclude, even if excludeHidden
That is unless any of the parent folders is hidden.

Signed-off-by: Samir Benmendil <me@rmz.io>
2019-08-15 03:00:26 +01:00
Samir Benmendil
758483bc6e Silence CMake warning
CMake Warning (dev) at NEXTCLOUD.cmake:31 (set):
  implicitly converting 'string' to 'STRING' type

Signed-off-by: Samir Benmendil <me@rmz.io>
2019-08-15 03:00:26 +01:00
Samir Benmendil
9f4873e864 Fix some typos
Signed-off-by: Samir Benmendil <me@rmz.io>
2019-08-15 03:00:26 +01:00
Samir Benmendil
94448a8b33 Add menu action to main sync folder too
Signed-off-by: Samir Benmendil <me@rmz.io>
2019-08-15 03:00:26 +01:00
Samir Benmendil
acf0b0f7c4 Add menu action to each subfolder
Signed-off-by: Samir Benmendil <me@rmz.io>
2019-08-15 03:00:26 +01:00
Samir Benmendil
d4816442ef Extract IgnoreListTableWidget to be reused
Signed-off-by: Samir Benmendil <me@rmz.io>
2019-08-15 03:00:26 +01:00
Samir Benmendil
e44a2302de Remove all rows in the table widget
`clearContents()` will leave the number of rows as is. This was causing
a segfault when trying to loop over the items of the widget.

Signed-off-by: Samir Benmendil <me@rmz.io>
2019-08-15 03:00:26 +01:00
Samir Benmendil
4a2b91a043 IgnoreListEditor can work on any path
Signed-off-by: Samir Benmendil <me@rmz.io>
2019-08-15 03:00:26 +01:00
Samir Benmendil
14279104ae Read .sync_exclude.lst in each subdirectory
Signed-off-by: Samir Benmendil <me@rmz.io>
2019-08-15 03:00:26 +01:00
Nextcloud bot
7843660bbf [tx-robot] updated from transifex 2019-08-14 03:05:59 +00:00
Camila Ayres
037b2338de Merge pull request #1376 from nextcloud/updates-changelog
Update ChangeLog.
2019-08-12 15:20:23 +02:00
Camila San
cfa6f13620 Updates ChangeLog.
Signed-off-by: Camila San <hello@camila.codes>
2019-08-12 15:13:05 +02:00
Nextcloud bot
00d222891c [tx-robot] updated from transifex 2019-08-12 03:03:59 +00:00
Nextcloud bot
1ed8c898e9 [tx-robot] updated from transifex 2019-08-11 03:06:12 +00:00
Nextcloud bot
c2f401a77a [tx-robot] updated from transifex 2019-08-10 03:05:46 +00:00
Camila Ayres
169dea1627 Merge pull request #1372 from ivaradi/remove-libgnome-keyring
Remove dependency on libgnome-keyring0 on Eoan
2019-08-09 11:42:24 +02:00
István Váradi
1883c04a12 Remove dependency on libgnome-keyring0 on Eoan
Signed-off-by: István Váradi <ivaradi@varadiistvan.hu>
2019-08-09 06:47:59 +02:00
Nextcloud bot
ec70ee96f3 [tx-robot] updated from transifex 2019-08-09 03:04:25 +00:00
Nextcloud bot
0337507446 [tx-robot] updated from transifex 2019-08-08 03:04:58 +00:00
Nextcloud bot
890c7d731d [tx-robot] updated from transifex 2019-08-07 03:03:54 +00:00
Nextcloud bot
3a6f9e51f5 [tx-robot] updated from transifex 2019-08-06 03:03:52 +00:00
Nextcloud bot
eb41f01857 [tx-robot] updated from transifex 2019-08-05 03:01:25 +00:00
Nextcloud bot
681c03b097 [tx-robot] updated from transifex 2019-08-04 03:06:07 +00:00
Nextcloud bot
233333a616 [tx-robot] updated from transifex 2019-08-03 03:04:01 +00:00
Camila Ayres
f9947334f6 Merge pull request #1366 from nextcloud/fix/client_side_cert
Add proper CA to client side certificate connection
2019-08-02 18:38:27 +02:00
Camila Ayres
9b6b14b374 Merge branch 'master' into fix/client_side_cert 2019-08-02 14:40:20 +02:00
Roeland Jago Douma
4c8da1a3e7 Add proper CA
If the cert has a chain of certs we should send them all properly

Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
2019-08-02 14:19:37 +02:00
Nextcloud bot
9e7868b3de [tx-robot] updated from transifex 2019-08-02 03:05:09 +00:00
Nextcloud bot
8fad2acc36 [tx-robot] updated from transifex 2019-08-01 03:08:11 +00:00
Nextcloud bot
4befda1eab [tx-robot] updated from transifex 2019-07-31 03:08:42 +00:00
István Váradi
29218158c0 Merge pull request #1360 from ivaradi/ubuntu-eoan
Remove Ubuntu Cosmic and add Eoan package
2019-07-30 07:07:53 +02:00
István Váradi
40753860cf Merge branch 'master' into ubuntu-eoan 2019-07-30 06:50:19 +02:00
István Váradi
5142cf026f Merge pull request #1359 from ivaradi/qt55-update
Update Qt 5.5 compatibility patch for Xenial
2019-07-30 06:49:59 +02:00
István Váradi
d64b477280 Merge branch 'master' into ubuntu-eoan 2019-07-30 06:36:44 +02:00
István Váradi
d0cf21d355 Merge branch 'master' into qt55-update 2019-07-30 06:36:24 +02:00
Nextcloud bot
4257f70d2e [tx-robot] updated from transifex 2019-07-30 03:05:57 +00:00
István Váradi
d000328700 Merge branch 'master' into ubuntu-eoan 2019-07-29 19:05:13 +02:00
István Váradi
b5e45711fc Merge branch 'master' into qt55-update 2019-07-29 19:04:43 +02:00
Nextcloud bot
9f94f412ef [tx-robot] updated from transifex 2019-07-29 03:03:27 +00:00
Nextcloud bot
c13d4ca40e [tx-robot] updated from transifex 2019-07-28 03:05:54 +00:00
István Váradi
04d825f4b6 Remove Ubuntu Cosmic and add Eoan package
Signed-off-by: István Váradi <ivaradi@varadiistvan.hu>
2019-07-27 11:21:39 +02:00
Nextcloud bot
b448e215ad [tx-robot] updated from transifex 2019-07-27 03:03:03 +00:00
István Váradi
2d2c1ef29e Update Qt 5.5 compatibility patch for Xenial
Signed-off-by: István Váradi <ivaradi@varadiistvan.hu>
2019-07-26 21:08:23 +02:00
Nextcloud bot
d654a65ac7 [tx-robot] updated from transifex 2019-07-26 03:03:02 +00:00
István Váradi
f462bfc7c5 Merge pull request #1222 from ivaradi/new-drone-debian
Reinstate Debian build in the new Drone config
2019-07-25 18:58:29 +02:00
István Váradi
d6ab7920d9 Fix the Debian changelog generator
Signed-off-by: István Váradi <ivaradi@varadiistvan.hu>
2019-07-25 16:48:24 +02:00
István Váradi
ec359d0e6b Reinstate Debian build in the new Drone config
Signed-off-by: István Váradi <ivaradi@varadiistvan.hu>
2019-07-25 16:48:17 +02:00
Nextcloud bot
be3713a535 [tx-robot] updated from transifex 2019-07-25 03:03:32 +00:00
Camila Ayres
a21258487f Merge pull request #1354 from nextcloud/minorchange/link-to-help-text
Minor text change in the link to help in the tab 'General'.
2019-07-24 19:07:10 +02:00
Camila San
3ea472d9f2 Minor text change in the link to help in the tab 'General'.
Also uses helpUrl() retrieve the correct APPLICATION_HELP_URL
instead of APPLICATION_DOMAIN.

Signed-off-by: Camila San <hello@camila.codes>
2019-07-24 16:39:21 +02:00
Nextcloud bot
a0587f3cde [tx-robot] updated from transifex 2019-07-24 03:05:55 +00:00
Nextcloud bot
96c35a9410 [tx-robot] updated from transifex 2019-07-23 03:09:01 +00:00
Camila Ayres
41900140df Merge pull request #1342 from gt-h6k/fixes-1187
Fixes issue #1187
2019-07-22 22:04:14 +02:00
Camila Ayres
a47dd69a71 Merge branch 'master' into fixes-1187 2019-07-22 20:45:50 +02:00
Camila Ayres
0539e34c69 Merge pull request #1275 from ivyclare/pr/607
Fixes #607
2019-07-22 20:45:28 +02:00
Camila Ayres
5d30f4b1eb Merge branch 'master' into pr/607 2019-07-22 19:38:56 +02:00
Camila Ayres
4559965abf Merge pull request #1278 from ivyclare/pr/878
Fixes issue #878
2019-07-22 19:38:36 +02:00
Camila Ayres
4801bd5818 Merge branch 'master' into pr/607 2019-07-22 16:46:45 +02:00
Camila Ayres
9411a34a2b Merge branch 'master' into pr/878 2019-07-22 16:09:52 +02:00
Camila Ayres
94bff61c45 Merge branch 'master' into fixes-1187 2019-07-22 16:09:40 +02:00
Camila Ayres
cf3dd53e12 Merge pull request #1347 from caugner/965-file-folder-ignored-as-info
Displays FileIgnored activities with an info icon
2019-07-22 16:09:27 +02:00
Camila Ayres
0ae72cd68a Merge branch 'master' into pr/878 2019-07-22 15:45:56 +02:00
Camila Ayres
88624f055d Merge branch 'master' into 965-file-folder-ignored-as-info 2019-07-22 15:44:10 +02:00
Nextcloud bot
50be2e067c [tx-robot] updated from transifex 2019-07-22 03:06:58 +00:00
Claas Augner
116c5b361f Displays FileIgnored activities with an info icon
See: https://github.com/nextcloud/desktop/issues/965
Signed-off-by: Claas Augner <git@caugner.de>
2019-07-21 22:02:15 +02:00
Nextcloud bot
ae28be4a10 [tx-robot] updated from transifex 2019-07-21 03:07:35 +00:00
Nextcloud bot
fa2f52b551 [tx-robot] updated from transifex 2019-07-20 03:03:51 +00:00
Nextcloud bot
658f6817ce [tx-robot] updated from transifex 2019-07-19 03:16:29 +00:00
Nextcloud bot
28c401236b [tx-robot] updated from transifex 2019-07-17 03:03:58 +00:00
Hiroki Goto
57ca82e620 Merge branch 'master' into fixes-1187 2019-07-15 20:08:47 +09:00
Nextcloud bot
7d700a3c84 [tx-robot] updated from transifex 2019-07-15 03:00:34 +00:00
Nextcloud bot
2c2cc5c23e [tx-robot] updated from transifex 2019-07-14 03:03:06 +00:00
gt-h6k
1763504b4e Fixes issue #1187
Signed-off-by: Hiroki Goto <git@gt-h6k.net>
2019-07-13 16:39:30 +09:00
Nextcloud bot
94db2f82a2 [tx-robot] updated from transifex 2019-07-13 03:06:30 +00:00
Nextcloud bot
d967ebac9a [tx-robot] updated from transifex 2019-07-12 02:59:46 +00:00
Nextcloud bot
6b5b3c96cf [tx-robot] updated from transifex 2019-07-11 02:58:04 +00:00
Nextcloud bot
a8bf75d496 [tx-robot] updated from transifex 2019-07-10 02:57:49 +00:00
Nextcloud bot
bb34de2bd8 [tx-robot] updated from transifex 2019-07-09 02:54:41 +00:00
Nextcloud bot
b146654474 [tx-robot] updated from transifex 2019-07-07 02:55:41 +00:00
Nextcloud bot
c897564d28 [tx-robot] updated from transifex 2019-07-05 02:57:08 +00:00
Nextcloud bot
ae9bc786ad [tx-robot] updated from transifex 2019-07-04 02:55:55 +00:00
Nextcloud bot
b2d85cc4bf [tx-robot] updated from transifex 2019-07-03 02:55:33 +00:00
Nextcloud bot
ac505aac0e [tx-robot] updated from transifex 2019-07-02 02:54:54 +00:00
Nextcloud bot
4eac71a032 [tx-robot] updated from transifex 2019-06-30 02:55:43 +00:00
Nextcloud bot
fa29fc4028 [tx-robot] updated from transifex 2019-06-28 02:56:13 +00:00
Nextcloud bot
f352af7ffb [tx-robot] updated from transifex 2019-06-27 02:57:19 +00:00
Nextcloud bot
d7098343a2 [tx-robot] updated from transifex 2019-06-26 02:56:31 +00:00
Nextcloud bot
908f4d34e3 [tx-robot] updated from transifex 2019-06-25 02:57:16 +00:00
Nextcloud bot
78c7aed3b9 [tx-robot] updated from transifex 2019-06-24 02:56:03 +00:00
Nextcloud bot
a8986124b8 [tx-robot] updated from transifex 2019-06-23 02:56:59 +00:00
Nextcloud bot
57ec1cdda4 [tx-robot] updated from transifex 2019-06-22 02:55:39 +00:00
Camila Ayres
9dc47438c9 Merge pull request #1309 from nextcloud/feature/show-shared-file-owner
Displays the uid_owner of a shared file.
2019-06-21 19:53:46 +02:00
Camila San
d6a0a5272a Displays the uid_owner of a shared file.
Also displays the sharees that reshared it via share link.

Signed-off-by: Camila San <hello@camila.codes>
2019-06-21 18:36:45 +02:00
Nextcloud bot
47ac10fb8e [tx-robot] updated from transifex 2019-06-21 02:54:12 +00:00
Nextcloud bot
459a17bd7e [tx-robot] updated from transifex 2019-06-20 02:55:55 +00:00
Nextcloud bot
de65b6fb7e [tx-robot] updated from transifex 2019-06-18 02:55:35 +00:00
Nextcloud bot
6ddfc80672 [tx-robot] updated from transifex 2019-06-17 02:54:41 +00:00
Nextcloud bot
048cf3b23e [tx-robot] updated from transifex 2019-06-16 02:56:30 +00:00
Nextcloud bot
3c958dea20 [tx-robot] updated from transifex 2019-06-13 02:48:18 +00:00
Camila Ayres
0b420c5fe6 Merge pull request #1301 from nextcloud/error-423-change-warning-color
Add error category for http file lock error status 423.
2019-06-12 11:33:25 +02:00
Camila San
bb7d330516 Add error category for http file lock error status 423.
It filters the error out of the list of blocking errors. It now shows up
in the Activities and Notificattions list as a warning.

Signed-off-by: Camila San <hello@camila.codes>
2019-06-11 23:32:10 +02:00
Nextcloud bot
795c29d5bd [tx-robot] updated from transifex 2019-06-11 02:46:16 +00:00
Nextcloud bot
ad04116c00 [tx-robot] updated from transifex 2019-06-10 02:47:53 +00:00
Nextcloud bot
42b9dec1d0 [tx-robot] updated from transifex 2019-06-09 02:47:26 +00:00
Nextcloud bot
51e398d4f4 [tx-robot] updated from transifex 2019-06-08 02:46:59 +00:00
Ivoline Ngong
5c2e8c9574 Merge branch 'master' into pr/607 2019-06-07 19:36:44 +03:00
Nextcloud bot
feee0bf62b [tx-robot] updated from transifex 2019-06-06 02:47:28 +00:00
Nextcloud bot
f6dee2b10f [tx-robot] updated from transifex 2019-06-05 02:47:31 +00:00
Nextcloud bot
57fc438b91 [tx-robot] updated from transifex 2019-06-04 02:47:14 +00:00
Camila Ayres
aa5b62c931 Add comment to Github issue_template.md.
Minor change: removes some item e.g. PHP version that are not relevant to the client.
2019-06-03 17:42:29 +02:00
Ivan Čukić
f089fa34fc Merge pull request #1241 from ivan-cukic/ivan/encryption-initial-setup
Added a nice UI for the E2E-enabled account first connect
2019-06-03 09:44:19 +02:00
Ivan Čukić
200032c9cb Merge branch 'master' into ivan/encryption-initial-setup 2019-06-03 09:11:56 +02:00
Nextcloud bot
6a294bb4a0 [tx-robot] updated from transifex 2019-06-03 02:46:03 +00:00
Nextcloud bot
4aa2e2e8c3 [tx-robot] updated from transifex 2019-06-01 02:46:48 +00:00
Nextcloud bot
4cd7095a40 [tx-robot] updated from transifex 2019-05-30 02:46:02 +00:00
Ivoline Ngong
d3c5a71689 Merge branch 'master' into pr/878 2019-05-29 21:06:19 +03:00
ivyclare
f4340b98ce Unshare action transferred to 3 dot menu
Signed-off-by: ivyclare <ivolinengong@gmail.com>
2019-05-29 20:54:53 +03:00
Camila Ayres
867e546fa9 Merge branch 'master' into master 2019-05-29 17:40:53 +02:00
Nextcloud bot
eb28fe9990 [tx-robot] updated from transifex 2019-05-29 02:56:09 +00:00
Nextcloud bot
fa5c1eff25 [tx-robot] updated from transifex 2019-05-28 03:00:44 +00:00
ivyclare
bcdc88ecf0 Change »Details…« to »View more activity…«
Signed-off-by: ivyclare <ivolinengong@gmail.com>
2019-05-28 01:14:31 +03:00
Camila Ayres
1dca017537 Merge pull request #1247 from nextcloud/backport/reshare
Adds parameter to retrieve shares with its reshares.
2019-05-27 20:16:34 +02:00
Camila San
9ff1a30dc4 Display error when current user is not allowed to reshare file/folder.
Signed-off-by: Camila San <hello@camila.codes>
2019-05-27 19:46:38 +02:00
Camila San
4f0ff154d3 Display sharees on the first show of the share dialog.
It was displaying them only when the current user would edit the share.

Signed-off-by: Camila San <hello@camila.codes>
2019-05-27 19:46:38 +02:00
Camila San
981a4ee240 Adds parameter to retrieve shares with its reshares.
If the initiator or the recipient is not the current user,
show the list of sharees without any options to edit it.

Minor change: updates api to v2.

Signed-off-by: Camila San <hello@camila.codes>
2019-05-27 19:46:38 +02:00
Nextcloud bot
378dd744fb [tx-robot] updated from transifex 2019-05-27 02:54:16 +00:00
Nextcloud bot
d38e829330 [tx-robot] updated from transifex 2019-05-25 02:55:41 +00:00
Nextcloud bot
ad65b6fdf3 [tx-robot] updated from transifex 2019-05-24 02:56:34 +00:00
Roeland Jago Douma
ea6f3be374 Merge pull request #1273 from csware/issue-1266
WebView: Properly handle usernames with spaces and plus signs in it
2019-05-23 19:59:49 +02:00
Sven Strickroth
85d217ec95 WebView: Properly handle usernames with spaces and plus signs in it
The path returned from the server encodes a space in the username with `+` and if the username contains a `+` sign it is encoded as `%2B` (cf. https://www.php.net/manual/function.urlencode.php).

Fix: Don't (double) decode the URL path and then replace `+` with space (introduced in issue #279 resp. commit 9ec61a84ce). Instead first replace `+` with space, then decode percent encoding.

Tested with a username containing a space, a username containing a `+`sign and a username containing just A-Za-z0-9- (with Nextcloud 16).

(fixes issue #1266)

Signed-off-by: Sven Strickroth <email@cs-ware.de>
2019-05-23 17:09:33 +02:00
Nextcloud bot
e8213242a9 [tx-robot] updated from transifex 2019-05-23 02:55:27 +00:00
Nextcloud bot
0c603944b8 [tx-robot] updated from transifex 2019-05-22 02:56:14 +00:00
Nextcloud bot
559187df6c [tx-robot] updated from transifex 2019-05-20 02:53:13 +00:00
Nextcloud bot
eb00500b0b [tx-robot] updated from transifex 2019-05-19 02:55:18 +00:00
Ivan Čukić
876f69dc7d Merge branch 'master' into ivan/encryption-initial-setup 2019-05-18 23:07:34 +02:00
Ivan Čukić
33605d7f65 Removed ellipsis from the button text 2019-05-18 22:58:41 +02:00
Nextcloud bot
492fc846df [tx-robot] updated from transifex 2019-05-18 02:53:47 +00:00
Nextcloud bot
febf206704 [tx-robot] updated from transifex 2019-05-17 02:59:46 +00:00
Nextcloud bot
e1eeaadaff [tx-robot] updated from transifex 2019-05-16 02:53:07 +00:00
Ivan Čukić
4f17366262 Changed the message to 'Enable encryption...' 2019-05-14 21:54:43 +02:00
Ivan Čukić
a8ac89c20c Merge branch 'master' into ivan/encryption-initial-setup 2019-05-14 21:53:57 +02:00
Balázs Meskó
a13bba7f42 Marking unused strings as unstranslatable
Signed-off-by: Balázs Meskó <mesko.balazs@fsf.hu>
2019-05-14 20:07:08 +02:00
Nextcloud bot
e0f421ac7a [tx-robot] updated from transifex 2019-05-14 02:51:16 +00:00
Roeland Jago Douma
b6c0f9db5d Merge pull request #1253 from muesli/webview-typo
Fixed typo in "certificate"
2019-05-13 07:58:52 +02:00
Roeland Jago Douma
50755d0295 Merge pull request #1251 from yarons/patch-1
Fixed typo
2019-05-13 07:58:29 +02:00
Christian Muehlhaeuser
9dc0027304 Fixed typo in "certificate"
Signed-off-by: Christian Muehlhaeuser <muesli@gmail.com>
2019-05-13 05:22:58 +02:00
Nextcloud bot
37ee52a930 [tx-robot] updated from transifex 2019-05-13 00:47:37 +00:00
Yaron Shahrabani
692d885b55 Fixed typo
Chipher -> Cipher
2019-05-12 19:11:33 +03:00
Ivan Čukić
6c0d796b4b E2E UI setup poligh
- Text changed to "Enable..." instead of "Setup"
- The close icon follows NC style
- "end-to-end" instead of "end to end"

Signed-off-by: Ivan Čukić <ivan.cukic@kde.org>
2019-05-11 19:20:19 +02:00
Ivan Čukić
db7e5e01b3 Merge branch 'master' into ivan/encryption-initial-setup 2019-05-11 19:19:00 +02:00
Nextcloud bot
ae0b1ce3d6 [tx-robot] updated from transifex 2019-05-11 00:48:37 +00:00
Camila Ayres
419b8a3ff9 Merge pull request #1245 from joshua-sterner/master
This should fix issue #1000.
2019-05-10 17:04:40 +02:00
Camila Ayres
bdfc5dcd14 Merge branch 'master' into master 2019-05-10 14:42:44 +02:00
Camila Ayres
c0704cd500 Merge pull request #1234 from curiousDTU/small-fix
updated default remote poll to 5 seconds #1115
2019-05-10 11:08:32 +02:00
Joshua Sterner
1b36dbc435 Merge branch 'master' into master 2019-05-09 23:15:54 -07:00
shobha
188374f60c updated default remote poll to 5 seconds #1115
Signed-off-by: shobha <tyagishobha@gmail.com>
2019-05-10 09:15:09 +05:30
Nextcloud bot
fd9d7563c2 [tx-robot] updated from transifex 2019-05-10 00:49:02 +00:00
Joshua Sterner
bd9fcd88d8 testMove3LevelDirWithFile now uses mv function to move the directory
Signed-off-by: Joshua Sterner <joshua.s.sterner@gmail.com>
2019-05-09 04:12:33 -07:00
Joshua Sterner
b6ff17c50b Fixed Issue #1000 - Subfolders of moved folders not synced
Signed-off-by: Joshua Sterner <joshua.s.sterner@gmail.com>
2019-05-09 04:12:33 -07:00
Nextcloud bot
2d3bac4e53 [tx-robot] updated from transifex 2019-05-09 00:48:24 +00:00
Ivan Čukić
7af786fde9 Added a nice UI for the E2E-enabled account first connect
Instead of immediately popping up the mnemonic dialogue,
only show a notification bar on the account setup page.

For the cases where the user does not want to use E2E,
this is significantly less intrusive than the old approach.
2019-05-09 00:37:24 +02:00
Camila Ayres
1f1a7a27f0 Merge pull request #1239 from nextcloud/fix/device-memory-leak
Fix memory leak with device pointer
2019-05-08 20:14:49 +02:00
Christoph Wurst
c2e3cbca31 Fix memory leak with device pointer
Downstream of https://github.com/owncloud/client/pull/6856

Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
2019-05-08 19:41:48 +02:00
Nextcloud bot
92a5e64487 [tx-robot] updated from transifex 2019-05-08 00:52:12 +00:00
Nextcloud bot
9153f710df [tx-robot] updated from transifex 2019-05-07 00:48:06 +00:00
Nextcloud bot
8151eb4837 [tx-robot] updated from transifex 2019-05-05 00:49:10 +00:00
Nextcloud bot
a5b65a73a8 [tx-robot] updated from transifex 2019-05-03 00:47:47 +00:00
Nextcloud bot
26b9bf0281 [tx-robot] updated from transifex 2019-05-02 00:48:16 +00:00
Nextcloud bot
19c968de76 [tx-robot] updated from transifex 2019-05-01 00:48:06 +00:00
Terence Eden
e64fa74899 Typo
There's no such thing as a "key*h*chain".
2019-04-28 10:03:38 +01:00
Nextcloud bot
9b3fa62b2d [tx-robot] updated from transifex 2019-04-28 00:50:09 +00:00
Nextcloud bot
68983c54bb [tx-robot] updated from transifex 2019-04-27 00:47:44 +00:00
Nextcloud bot
d9af9b895f [tx-robot] updated from transifex 2019-04-26 00:47:12 +00:00
Nextcloud bot
50f332eac7 [tx-robot] updated from transifex 2019-04-25 00:47:56 +00:00
Nextcloud bot
ad095efe3a [tx-robot] updated from transifex 2019-04-23 00:48:20 +00:00
Nextcloud bot
3c947819c1 [tx-robot] updated from transifex 2019-04-22 00:48:48 +00:00
Camila Ayres
f52f9f0abe Merge pull request #1212 from nextcloud/cmake-simplify
Simplify cmake command to make copy-pastable.
2019-04-21 19:41:52 +02:00
Camila San
75ccffa5a2 Simplify compile instructions.
Signed-off-by: Camila San <hello@camila.codes>
2019-04-21 17:13:20 +02:00
Jan-Christoph Borchardt
f0035b95bb Simplify cmake command to make copy-pastable
Signed-off-by: Jan-Christoph Borchardt <hey@jancborchardt.net>
2019-04-21 14:31:10 +02:00
Nextcloud bot
955f54cdef [tx-robot] updated from transifex 2019-04-21 00:50:00 +00:00
Nextcloud bot
c5b8cda323 [tx-robot] updated from transifex 2019-04-20 00:48:15 +00:00
Nextcloud bot
ae0dbcde5b [tx-robot] updated from transifex 2019-04-19 00:49:24 +00:00
Nextcloud bot
f32d83e993 [tx-robot] updated from transifex 2019-04-18 00:49:41 +00:00
Nextcloud bot
d7b881feb6 [tx-robot] updated from transifex 2019-04-17 00:50:11 +00:00
Nextcloud bot
6bd98518e4 [tx-robot] updated from transifex 2019-04-15 00:49:36 +00:00
Nextcloud bot
54d80bbc9b [tx-robot] updated from transifex 2019-04-14 00:51:05 +00:00
Nextcloud bot
5f9ec1762a [tx-robot] updated from transifex 2019-04-13 00:51:53 +00:00
Camila San
1c8dfd701b Increase version to 2.5.3. 2019-04-12 17:17:23 +02:00
Camila Ayres
f520d7364d Merge pull request #1118 from tuchfarber/tuchfarber/fix_empty_file_error_wording
Fix empty file wording in error log (small)
2019-04-12 17:08:27 +02:00
Camila Ayres
4d61a7c263 Merge branch 'master' into tuchfarber/fix_empty_file_error_wording 2019-04-12 16:51:36 +02:00
Camila Ayres
ac6ef500cc Merge pull request #1201 from nextcloud/bugfix/popup-empty-folder
Uses configuraion to determine if it should show empty folder popup.
2019-04-12 16:38:51 +02:00
Camila San
b5b2ef3120 Updates test to fit new configuration default value.
Signed-off-by: Camila San <hello@camila.codes>
2019-04-12 16:22:07 +02:00
Camila San
7f3c3f6eb6 Uses configuraion to determine if it should show empty folder popup.
Configuration default value is set to false because  users are getting
the popup message too often.

Signed-off-by: Camila San <hello@camila.codes>
2019-04-12 16:22:07 +02:00
Camila Ayres
936bdd04a8 Merge pull request #1185 from tintou/master
libcloudproviders: Add missing check for Qt5DBus
2019-04-12 16:09:31 +02:00
Camila Ayres
33baf40061 Merge branch 'master' into master 2019-04-12 15:50:55 +02:00
Roeland Jago Douma
6a63d8f2b9 Merge pull request #1191 from nextcloud/bugfix/1172/share-link
Share link fixing
2019-04-12 14:31:11 +02:00
Corentin Noël
b11a4217c7 Merge branch 'master' into master 2019-04-12 14:25:56 +02:00
Camila San
35114cf45c Disable options in share dialog if account state changes.
Signed-off-by: Camila San <hello@camila.codes>
2019-04-12 14:17:58 +02:00
Camila San
e07c472057 Refactores sharing link to support multiple share links.
Signed-off-by: Camila San <hello@camila.codes>
2019-04-12 14:17:58 +02:00
Camila San
0fed1cc54d Fixes deleting of one single link share.
Signed-off-by: Camila San <hello@camila.codes>
2019-04-12 14:17:57 +02:00
Roeland Jago Douma
ac257cd65f Merge pull request #1186 from tintou/tintou/i18n
Fix several memory leaks in cloudproviders and add translation support
2019-04-12 14:09:30 +02:00
Roeland Jago Douma
088b0817d8 Merge branch 'master' into tintou/i18n 2019-04-12 13:48:19 +02:00
Roeland Jago Douma
b0c75723e2 Merge pull request #1200 from nextcloud/new_drone
New drone config
2019-04-12 13:47:28 +02:00
Roeland Jago Douma
b368a93e2e New drone config
Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
2019-04-12 11:14:05 +02:00
Roeland Jago Douma
c75cb390dd Merge branch 'master' into tintou/i18n 2019-04-12 10:35:02 +02:00
Nextcloud bot
d39f826e0e [tx-robot] updated from transifex 2019-04-12 00:49:48 +00:00
Camila Ayres
1ea8b18893 Merge branch 'master' into tintou/i18n 2019-04-11 13:54:18 +02:00
Nextcloud bot
8491ef428e [tx-robot] updated from transifex 2019-04-10 00:48:53 +00:00
Julius Härtl
42ce5c0c0d Merge branch 'master' into master 2019-04-09 10:01:35 +02:00
Nextcloud bot
acca019afe [tx-robot] updated from transifex 2019-04-09 00:48:35 +00:00
Nextcloud bot
4591f9c09f [tx-robot] updated from transifex 2019-04-08 00:49:58 +00:00
Corentin Noël
088a5a9771 Fix several memory leaks in cloudproviders and add translation support 2019-04-04 22:06:22 +02:00
Corentin Noël
19079c65c1 libcloudproviders: Add missing check for Qt5DBus 2019-04-04 17:50:30 +02:00
Nextcloud bot
3804410949 [tx-robot] updated from transifex 2019-04-04 00:49:12 +00:00
Nextcloud bot
b8e874a3a7 [tx-robot] updated from transifex 2019-04-02 00:51:06 +00:00
Nextcloud bot
3a03568ec8 [tx-robot] updated from transifex 2019-04-01 00:51:27 +00:00
Nextcloud bot
40da5630e1 [tx-robot] updated from transifex 2019-03-31 01:50:45 +00:00
Nextcloud bot
b36626d50a [tx-robot] updated from transifex 2019-03-30 01:50:00 +00:00
Roeland Jago Douma
26f897d22c Merge pull request #1157 from stragu/patch-1
Fix a minor typo
2019-03-29 13:33:55 +01:00
Nextcloud bot
1b4cb6483b [tx-robot] updated from transifex 2019-03-29 01:50:20 +00:00
Nextcloud bot
9312c18c57 [tx-robot] updated from transifex 2019-03-27 11:31:33 +00:00
Nextcloud bot
96434aabe9 [tx-robot] updated from transifex 2019-03-27 01:42:51 +00:00
Nextcloud bot
95ec0777ca [tx-robot] updated from transifex 2019-03-26 01:41:08 +00:00
Nextcloud bot
283423f1bd [tx-robot] updated from transifex 2019-03-25 01:43:41 +00:00
Stéphane Guillou
3a893d6179 Fix a minor typo 2019-03-24 20:12:44 +10:00
Nextcloud bot
6a83b31c5a [tx-robot] updated from transifex 2019-03-24 01:44:42 +00:00
Nextcloud bot
9a3667c1de [tx-robot] updated from transifex 2019-03-23 01:43:05 +00:00
Nextcloud bot
90fa47bbe6 [tx-robot] updated from transifex 2019-03-22 01:44:35 +00:00
Camila Ayres
44c265e2a7 Merge pull request #1153 from nextcloud/enh/512/ci
Add Qt-5.12 to CI
2019-03-21 17:56:27 +01:00
Roeland Jago Douma
bb9017d3a6 Move over AppImage builder to 5.12
Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
2019-03-21 15:57:40 +01:00
Roeland Jago Douma
9d87f707a6 Add Qt-5.12 to CI
Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
2019-03-21 15:57:40 +01:00
Nextcloud bot
89cda9a1b4 [tx-robot] updated from transifex 2019-03-21 01:52:00 +00:00
Nextcloud bot
653e56268d [tx-robot] updated from transifex 2019-03-20 01:49:54 +00:00
Nextcloud bot
56c905819e [tx-robot] updated from transifex 2019-03-19 01:52:24 +00:00
Nextcloud bot
0d5120e7cc [tx-robot] updated from transifex 2019-03-18 01:50:05 +00:00
Nextcloud bot
1c76dd5859 [tx-robot] updated from transifex 2019-03-17 01:50:55 +00:00
Nextcloud bot
37f709fe34 [tx-robot] updated from transifex 2019-03-16 01:50:19 +00:00
Nextcloud bot
a362f32402 [tx-robot] updated from transifex 2019-03-15 01:52:48 +00:00
Nextcloud bot
7ecab308f2 [tx-robot] updated from transifex 2019-03-14 01:45:23 +00:00
Nextcloud bot
d7314adcf1 [tx-robot] updated from transifex 2019-03-13 01:45:16 +00:00
Nextcloud bot
f64035339b [tx-robot] updated from transifex 2019-03-12 01:44:20 +00:00
Nextcloud bot
0e70b8ab9f [tx-robot] updated from transifex 2019-03-10 01:44:57 +00:00
Nextcloud bot
e805184726 [tx-robot] updated from transifex 2019-03-09 01:42:32 +00:00
Nextcloud bot
b0d1e34df3 [tx-robot] updated from transifex 2019-03-08 01:44:20 +00:00
Matt Tuchfarber
0862d6cfb0 Fix empty file wording in error log
Changing empty file error text so that it reads correctly.
2019-03-07 00:18:12 -05:00
Nextcloud bot
9032b1c491 [tx-robot] updated from transifex 2019-03-07 01:44:38 +00:00
Nextcloud bot
e9111d745c [tx-robot] updated from transifex 2019-03-05 01:52:03 +00:00
Roeland Jago Douma
0e3a8856e5 Merge pull request #1092 from nextcloud/enh/debug_not_warnings
Be less verbose with logging
2019-03-04 10:19:16 +01:00
Nextcloud bot
3084b85229 [tx-robot] updated from transifex 2019-03-04 01:42:12 +00:00
Nextcloud bot
f7f0a3249f [tx-robot] updated from transifex 2019-03-03 01:43:17 +00:00
Nextcloud bot
4419a51a5d [tx-robot] updated from transifex 2019-03-02 01:43:14 +00:00
Camila Ayres
1d37e96999 Merge pull request #1119 from nextcloud/typo
Fix typo in translation string
2019-03-01 12:18:34 +01:00
Morris Jobke
158c6e8136 Fix typo in translation string 2019-03-01 11:40:20 +01:00
Nextcloud bot
423e4ef92e [tx-robot] updated from transifex 2019-03-01 01:49:47 +00:00
Nextcloud bot
4645adda0a [tx-robot] updated from transifex 2019-02-28 01:50:48 +00:00
Nextcloud bot
0252ad9a08 [tx-robot] updated from transifex 2019-02-25 01:48:24 +00:00
Nextcloud bot
089625c9fd [tx-robot] updated from transifex 2019-02-24 01:51:53 +00:00
Nextcloud bot
e63fd9b894 [tx-robot] updated from transifex 2019-02-23 01:49:05 +00:00
Nextcloud bot
9be32355f9 [tx-robot] updated from transifex 2019-02-22 01:49:45 +00:00
Nextcloud bot
984c02546e [tx-robot] updated from transifex 2019-02-21 01:48:49 +00:00
Nextcloud bot
c60b4afafb [tx-robot] updated from transifex 2019-02-20 01:48:28 +00:00
Nextcloud bot
55ff37a454 [tx-robot] updated from transifex 2019-02-16 01:48:02 +00:00
Roeland Jago Douma
bb2f179342 Be less verbose with logging
Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
2019-02-15 20:23:24 +01:00
Roeland Jago Douma
28afe8c323 Merge pull request #1087 from SimJoSt/patch-1
correct app passwords link
2019-02-14 11:51:24 +01:00
Joda Stößer
9393626ec2 correct app passwords link
Since some versions ago, the path/url for the app password settings is `/settings/user/security#security` instead of `/settings/personal#apppasswords`
2019-02-14 05:26:21 +01:00
Nextcloud bot
c0d0d39d8d [tx-robot] updated from transifex 2019-02-13 01:48:58 +00:00
Nextcloud bot
d06db158a0 [tx-robot] updated from transifex 2019-02-12 01:48:01 +00:00
Nextcloud bot
1698c39223 [tx-robot] updated from transifex 2019-02-11 01:49:14 +00:00
Nextcloud bot
42ad97de8c [tx-robot] updated from transifex 2019-02-10 01:51:00 +00:00
Nextcloud bot
29230412ed [tx-robot] updated from transifex 2019-02-09 01:49:42 +00:00
Nextcloud bot
e4bcc1a9ee [tx-robot] updated from transifex 2019-02-08 01:49:54 +00:00
Nextcloud bot
ca624def40 [tx-robot] updated from transifex 2019-02-07 01:52:13 +00:00
Nextcloud bot
bb912ecc0a [tx-robot] updated from transifex 2019-02-06 01:42:24 +00:00
206 changed files with 47088 additions and 19305 deletions

View File

@@ -1,228 +1,366 @@
clone:
git:
image: plugins/git
tags: true
depth: 1
kind: pipeline
name: qt-5.7
pipeline:
qt-5.7:
image: nextcloudci/client-5.7:client-5.7-4
commands:
# Install QtKeyChain
- /bin/bash -c "
source /opt/qt57/bin/qt57-env.sh &&
cd /tmp &&
git clone https://github.com/frankosterfeld/qtkeychain.git &&
cd qtkeychain &&
git checkout v0.9.1 &&
mkdir build &&
cd build &&
cmake ../ &&
make &&
make install"
# Build client
- /bin/bash -c "
source /opt/qt57/bin/qt57-env.sh &&
mkdir build &&
cd build &&
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 'ctest --output-on-failure' test"
when:
matrix:
TESTS: qt-5.7
qt-5.8:
image: nextcloudci/client-5.8:client-5.8-4
commands:
# Install QtKeyChain
- /bin/bash -c "
source /opt/qt58/bin/qt58-env.sh &&
cd /tmp &&
git clone https://github.com/frankosterfeld/qtkeychain.git &&
cd qtkeychain &&
git checkout v0.9.1 &&
mkdir build &&
cd build &&
cmake ../ &&
make &&
make install"
# Build client
- /bin/bash -c "
source /opt/qt58/bin/qt58-env.sh &&
mkdir build &&
cd build &&
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 'ctest --output-on-failure' test"
when:
matrix:
TESTS: qt-5.8
qt-5.9:
image: nextcloudci/client-5.9:client-5.9-5
commands:
# Install QtKeyChain
- /bin/bash -c "
source /opt/qt59/bin/qt59-env.sh &&
cd /tmp &&
git clone https://github.com/frankosterfeld/qtkeychain.git &&
cd qtkeychain &&
git checkout v0.9.1 &&
mkdir build &&
cd build &&
cmake ../ &&
make &&
make install"
# Build client
- /bin/bash -c "
source /opt/qt59/bin/qt59-env.sh &&
mkdir build &&
cd build &&
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 'ctest --output-on-failure' test"
when:
matrix:
TESTS: qt-5.9
qt-5.10:
image: nextcloudci/client-5.10:client-5.10-3
commands:
# Install QtKeyChain
- /bin/bash -c "
export CC=gcc-7 &&
export CXX=g++-7 &&
source /opt/qt510/bin/qt510-env.sh &&
cd /tmp &&
git clone https://github.com/frankosterfeld/qtkeychain.git &&
cd qtkeychain &&
git checkout v0.9.1 &&
mkdir build &&
cd build &&
cmake ../ &&
make &&
make install"
# Build client
- /bin/bash -c "
export CC=gcc-7 &&
export CXX=g++-7 &&
source /opt/qt510/bin/qt510-env.sh &&
mkdir build &&
cd build &&
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 'ctest --output-on-failure' test"
when:
matrix:
TESTS: qt-5.10
qt-5.11:
image: nextcloudci/client-5.11:client-5.11-3
commands:
# Install QtKeyChain
- /bin/bash -c "
export CC=gcc-7 &&
export CXX=g++-7 &&
source /opt/qt511/bin/qt511-env.sh &&
cd /tmp &&
git clone https://github.com/frankosterfeld/qtkeychain.git &&
cd qtkeychain &&
git checkout v0.9.1 &&
mkdir build &&
cd build &&
cmake ../ &&
make &&
make install"
# Build client
- /bin/bash -c "
export CC=gcc-7 &&
export CXX=g++-7 &&
source /opt/qt511/bin/qt511-env.sh &&
mkdir build &&
cd build &&
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 'ctest --output-on-failure' test"
when:
matrix:
TESTS: qt-5.11
steps:
- name: build and test
image: nextcloudci/client-5.7:client-5.7-4
commands:
# Install QtKeyChain
- /bin/bash -c "
source /opt/qt57/bin/qt57-env.sh &&
cd /tmp &&
git clone https://github.com/frankosterfeld/qtkeychain.git &&
cd qtkeychain &&
git checkout v0.9.1 &&
mkdir build &&
cd build &&
cmake ../ &&
make &&
make install"
# Build client
- /bin/bash -c "
source /opt/qt57/bin/qt57-env.sh &&
mkdir build &&
cd build &&
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 'ctest --output-on-failure' test"
trigger:
branch:
- master
event:
- pull_request
- push
qt-5.11-clang:
image: nextcloudci/client-5.11:client-5.11-3
commands:
# Install QtKeyChain
- /bin/bash -c "
export CC=clang-6.0 &&
export CXX=clang++-6.0 &&
source /opt/qt511/bin/qt511-env.sh &&
cd /tmp &&
git clone https://github.com/frankosterfeld/qtkeychain.git &&
cd qtkeychain &&
git checkout v0.9.1 &&
mkdir build &&
cd build &&
cmake ../ &&
make &&
make install"
# Build client
- /bin/bash -c "
export CC=clang-6.0 &&
export CXX=clang++-6.0 &&
source /opt/qt511/bin/qt511-env.sh &&
mkdir build &&
cd build &&
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 'ctest --output-on-failure' test"
when:
matrix:
TESTS: qt-5.11-clang
---
kind: pipeline
name: qt-5.8
AppImage:
image: nextcloudci/client-5.11:client-5.11-3
commands:
- /bin/bash -c "./admin/linux/build-appimage.sh"
when:
matrix:
BUILD: AppImage
steps:
- name: build and test
image: nextcloudci/client-5.8:client-5.8-4
commands:
# Install QtKeyChain
- /bin/bash -c "
source /opt/qt58/bin/qt58-env.sh &&
cd /tmp &&
git clone https://github.com/frankosterfeld/qtkeychain.git &&
cd qtkeychain &&
git checkout v0.9.1 &&
mkdir build &&
cd build &&
cmake ../ &&
make &&
make install"
# Build client
- /bin/bash -c "
source /opt/qt58/bin/qt58-env.sh &&
mkdir build &&
cd build &&
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 'ctest --output-on-failure' test"
trigger:
branch:
- master
event:
- pull_request
- push
Debian:
image: nextcloudci/client-debian-ci:client-debian-ci-2
commands:
- /bin/bash -c "./admin/linux/debian/drone-build.sh"
secrets: [ DEBIAN_SECRET_KEY, DEBIAN_SECRET_IV ]
when:
matrix:
BUILD: Debian
---
kind: pipeline
name: qt-5.9
documentation:
image: nextcloudci/documentation:documentation-5
commands:
- cd doc
- make html
when:
matrix:
TESTS: documentation
steps:
- name: build and test
image: nextcloudci/client-5.9:client-5.9-5
commands:
# Install QtKeyChain
- /bin/bash -c "
source /opt/qt59/bin/qt59-env.sh &&
cd /tmp &&
git clone https://github.com/frankosterfeld/qtkeychain.git &&
cd qtkeychain &&
git checkout v0.9.1 &&
mkdir build &&
cd build &&
cmake ../ &&
make &&
make install"
# Build client
- /bin/bash -c "
source /opt/qt59/bin/qt59-env.sh &&
mkdir build &&
cd build &&
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 'ctest --output-on-failure' test"
trigger:
branch:
- master
event:
- pull_request
- push
matrix:
include:
- TESTS: qt-5.7
- TESTS: qt-5.8
- TESTS: qt-5.9
- TESTS: qt-5.10
- TESTS: qt-5.11
- TESTS: qt-5.11-clang
- BUILD: AppImage
- BUILD: Debian
- TESTS: documentation
---
kind: pipeline
name: qt-5.10
branches: [ master, 2.* ]
steps:
- name: build and test
image: nextcloudci/client-5.10:client-5.10-3
commands:
# Install QtKeyChain
- /bin/bash -c "
export CC=gcc-7 &&
export CXX=g++-7 &&
source /opt/qt510/bin/qt510-env.sh &&
cd /tmp &&
git clone https://github.com/frankosterfeld/qtkeychain.git &&
cd qtkeychain &&
git checkout v0.9.1 &&
mkdir build &&
cd build &&
cmake ../ &&
make &&
make install"
# Build client
- /bin/bash -c "
export CC=gcc-7 &&
export CXX=g++-7 &&
source /opt/qt510/bin/qt510-env.sh &&
mkdir build &&
cd build &&
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 'ctest --output-on-failure' test"
trigger:
branch:
- master
event:
- pull_request
- push
---
kind: pipeline
name: qt-5.11
steps:
- name: build and test
image: nextcloudci/client-5.11:client-5.11-3
commands:
# Install QtKeyChain
- /bin/bash -c "
export CC=gcc-7 &&
export CXX=g++-7 &&
source /opt/qt511/bin/qt511-env.sh &&
cd /tmp &&
git clone https://github.com/frankosterfeld/qtkeychain.git &&
cd qtkeychain &&
git checkout v0.9.1 &&
mkdir build &&
cd build &&
cmake ../ &&
make &&
make install"
# Build client
- /bin/bash -c "
export CC=gcc-7 &&
export CXX=g++-7 &&
source /opt/qt511/bin/qt511-env.sh &&
mkdir build &&
cd build &&
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 'ctest --output-on-failure' test"
trigger:
branch:
- master
event:
- pull_request
- push
---
kind: pipeline
name: qt-5.11-clang
steps:
- name: build and test
image: nextcloudci/client-5.11:client-5.11-3
commands:
# Install QtKeyChain
- /bin/bash -c "
export CC=clang-6.0 &&
export CXX=clang++-6.0 &&
source /opt/qt511/bin/qt511-env.sh &&
cd /tmp &&
git clone https://github.com/frankosterfeld/qtkeychain.git &&
cd qtkeychain &&
git checkout v0.9.1 &&
mkdir build &&
cd build &&
cmake ../ &&
make &&
make install"
# Build client
- /bin/bash -c "
export CC=clang-6.0 &&
export CXX=clang++-6.0 &&
source /opt/qt511/bin/qt511-env.sh &&
mkdir build &&
cd build &&
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 'ctest --output-on-failure' test"
trigger:
branch:
- master
event:
- pull_request
- push
---
kind: pipeline
name: qt-5.12
steps:
- name: build and test
image: nextcloudci/client-5.12:client-5.12-2
commands:
# Install QtKeyChain
- /bin/bash -c "
export CC=gcc-7 &&
export CXX=g++-7 &&
source /opt/qt512/bin/qt512-env.sh &&
cd /tmp &&
git clone https://github.com/frankosterfeld/qtkeychain.git &&
cd qtkeychain &&
git checkout v0.9.1 &&
mkdir build &&
cd build &&
cmake ../ &&
make &&
make install"
# Build client
- /bin/bash -c "
export CC=gcc-7 &&
export CXX=g++-7 &&
source /opt/qt512/bin/qt512-env.sh &&
mkdir build &&
cd build &&
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 'ctest --output-on-failure' test"
trigger:
branch:
- master
event:
- pull_request
- push
---
kind: pipeline
name: qt-5.12-clang
steps:
- name: build and test
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 &&
source /opt/qt512/bin/qt512-env.sh &&
cd /tmp &&
git clone https://github.com/frankosterfeld/qtkeychain.git &&
cd qtkeychain &&
git checkout v0.9.1 &&
mkdir build &&
cd build &&
cmake ../ &&
make &&
make install"
# Build client
- /bin/bash -c "
export CC=clang-6.0 &&
export CXX=clang++-6.0 &&
source /opt/qt512/bin/qt512-env.sh &&
mkdir build &&
cd build &&
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 'ctest --output-on-failure' test"
trigger:
branch:
- master
event:
- pull_request
- push
---
kind: pipeline
name: AppImage
steps:
- name: build
image: nextcloudci/client-5.12:client-5.12-2
commands:
- /bin/bash -c "./admin/linux/build-appimage.sh"
trigger:
branch:
- master
event:
- pull_request
- push
---
kind: pipeline
name: Debian
steps:
- name: build
image: nextcloudci/client-debian-ci:client-debian-ci-2
commands:
- /bin/bash -c "./admin/linux/debian/drone-build.sh"
environment:
DEBIAN_SECRET_KEY:
from_secret: DEBIAN_SECRET_KEY
DEBIAN_SECRET_IV:
from_secret: DEBIAN_SECRET_IV
trigger:
branch:
- master
event:
- pull_request
- push
---
kind: pipeline
name: Documentation
steps:
- name: build
image: nextcloudci/documentation:documentation-5
commands:
- cd doc
- make html
trigger:
branch:
- master
event:
- pull_request
- push

View File

@@ -1,9 +1,14 @@
<!---
Please try to only report a bug if it happens with the latest version
The latest version can be seen by checking https://download.nextcloud.com/desktop/
For support try our forums: https://help.nextcloud.com
--->
<!--
Dear user,
Please understand that at the moment, we are very busy with customer issues
and some high priority development work. A lot of issues are getting reported.
Right now we can't keep up and timely respond to all of them.
We're sorry for that and are expanding our team, if you're looking for a C++
job or know somebody who is, please point them to https://nextcloud.com/jobs
Don't forget that Github is not a support system or a place to ask for
features but only a place to report verified bugs - see nextcloud.com/support
for support options!
-->
### Expected behaviour
Tell us what should happen
@@ -18,6 +23,11 @@ Tell us what happens instead
### Client configuration
Client version:
<!---
Please try to only report a bug if it happens with the latest version
The latest version can be seen by checking https://download.nextcloud.com/desktop/
For support try our forums: https://help.nextcloud.com
--->
Operating system:
@@ -34,15 +44,6 @@ Installation path of client:
<!---
Optional section. It depends on the issue.
--->
Operating system:
Web server:
Database:
PHP version:
Nextcloud version:
Storage backend (external storage):
@@ -52,8 +53,6 @@ Storage backend (external storage):
Please use Gist (https://gist.github.com/) or a similar code paster for longer
logs.
```Template for output < 10 lines```
1. Client logfile: Output of `nextcloud --logwindow` or `nextcloud --logfile log.txt`
(On Windows using `cmd.exe`, you might need to first `cd` into the Nextcloud directory)
(See also https://docs.nextcloud.com/desktop/2.3/troubleshooting.html#log-files)

10
.gitignore vendored
View File

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

View File

@@ -0,0 +1,201 @@
[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
GenericName[ar]=مزامنة المجلد

View File

@@ -0,0 +1,204 @@
[Desktop Entry]
Categories=Utility;X-SuSE-SyncUtility;
Type=Application
Exec=@APPLICATION_EXECUTABLE@
Name=@APPLICATION_NAME@ desktop sync client
Comment=@APPLICATION_NAME@ desktop synchronization client
GenericName=Folder Sync
Icon=@APPLICATION_ICON_NAME@
Keywords=@APPLICATION_NAME@;syncing;file;sharing;
X-GNOME-Autostart-Delay=3
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
Icon[bg_BG]=@APPLICATION_ICON_NAME@
Name[bg_BG]=@APPLICATION_NAME@ клиент за синхронизиране на десктоп
Comment[bg_BG]=@APPLICATION_NAME@ клиент за синхронизиране на десктоп
GenericName[bg_BG]=Синхронизиране на папка

View File

@@ -199,6 +199,6 @@ X-GNOME-Autostart-Delay=3
# Translations
Icon[ca]=@APPLICATION_ICON_NAME@
Name[ca]=@APPLICATION_NAME@ client de sincro d'escriptori
Name[ca]=@APPLICATION_NAME@ client de sincronització d'escriptori
Comment[ca]=@APPLICATION_NAME@ client de sincronització d'escriptori
GenericName[ca]=Directori de sincronització

View File

@@ -0,0 +1,204 @@
[Desktop Entry]
Categories=Utility;X-SuSE-SyncUtility;
Type=Application
Exec=@APPLICATION_EXECUTABLE@
Name=@APPLICATION_NAME@ desktop sync client
Comment=@APPLICATION_NAME@ desktop synchronization client
GenericName=Folder Sync
Icon=@APPLICATION_ICON_NAME@
Keywords=@APPLICATION_NAME@;syncing;file;sharing;
X-GNOME-Autostart-Delay=3
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
Icon[da]=@APPLICATION_ICON_NAME@
Name[da]=@APPLICATION_NAME@ Arbejdsstationsssynkroniseringsklient
Comment[da]=@APPLICATION_NAME@ Arbejdsstationsssynkroniseringsklient
GenericName[da]=Mappesynkronisering

View File

@@ -198,6 +198,7 @@ X-GNOME-Autostart-Delay=3
# Translations
Icon[he]=@APPLICATION_ICON_NAME@
Name[he]=@APPLICATION_NAME@ לקוח סנכרון לשולחן העבודה
Comment[he]=@APPLICATION_NAME@ לקוח סנכרון לשולחן העבודה
GenericName[he]=סנכרון תיקיות

View File

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

View File

@@ -199,6 +199,6 @@ X-GNOME-Autostart-Delay=3
# Translations
Icon[hu_HU]=@APPLICATION_ICON_NAME@
Name[hu_HU]=@APPLICATION_NAME@ Asztali szinkronizáló kliens
Comment[hu_HU]=@APPLICATION_NAME@ Asztali szinkronizáló kliens
GenericName[hu_HU]=Mappa szinkronizálás
Name[hu_HU]=@APPLICATION_NAME@ asztali szinkronizálási kliens
Comment[hu_HU]=@APPLICATION_NAME@ asztali szinkronizálási kliens
GenericName[hu_HU]=Mappaszinkronizálás

View File

@@ -0,0 +1,204 @@
[Desktop Entry]
Categories=Utility;X-SuSE-SyncUtility;
Type=Application
Exec=@APPLICATION_EXECUTABLE@
Name=@APPLICATION_NAME@ desktop sync client
Comment=@APPLICATION_NAME@ desktop synchronization client
GenericName=Folder Sync
Icon=@APPLICATION_ICON_NAME@
Keywords=@APPLICATION_NAME@;syncing;file;sharing;
X-GNOME-Autostart-Delay=3
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
Icon[mk]=@APPLICATION_ICON_NAME@
Name[mk]=@APPLICATION_NAME@ клиент за синхронизација на компјутер
Comment[mk]=@APPLICATION_NAME@ клиент за синхронизација на компјутер
GenericName[mk]=Папка за синхронизација

View File

@@ -201,4 +201,4 @@ X-GNOME-Autostart-Delay=3
Icon[pt_BR]=@APPLICATION_ICON_NAME@
Name[pt_BR]=@APPLICATION_NAME@ cliente de sincronização desktop
Comment[pt_BR]=@APPLICATION_NAME@ cliente de sincronização desktop
GenericName[pt_BR]=Sincronizar Pasta
GenericName[pt_BR]=Sincronizar pasta

View File

@@ -0,0 +1,204 @@
[Desktop Entry]
Categories=Utility;X-SuSE-SyncUtility;
Type=Application
Exec=@APPLICATION_EXECUTABLE@
Name=@APPLICATION_NAME@ desktop sync client
Comment=@APPLICATION_NAME@ desktop synchronization client
GenericName=Folder Sync
Icon=@APPLICATION_ICON_NAME@
Keywords=@APPLICATION_NAME@;syncing;file;sharing;
X-GNOME-Autostart-Delay=3
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
# Translations
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 dosare

View File

@@ -199,6 +199,6 @@ X-GNOME-Autostart-Delay=3
# Translations
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]=Synchnonizácia priečinka
Name[sk_SK]=@APPLICATION_NAME@ synchronizačný klient pre PC
Comment[sk_SK]=@APPLICATION_NAME@ synchronizačný klient pre PC
GenericName[sk_SK]=Synchnonizácia priečinkov

350
ChangeLog
View File

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

View File

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

View File

@@ -24,33 +24,27 @@ $ cd build
```
##### Compile and install
For development reasons it is better to install the client on user space
instead on the global system. For example you could use in the next
instructions `path-to-install-folder/` as `~/.local/` in a linux system. If
you want to install system wide you could use `/usr/local` or `/opt/nextcloud/`.
:warning: For development reasons it is better to **install the client on user space** instead on the global system. Mixing up libs/dll's of different version can lead to undefined behavior and crashes:
##### Linux
* You could use the **cmake flag** ```CMAKE_INSTALL_PREFIX``` as ```~/.local/``` in a **Linux** system. If you want to install system wide you could use ```/usr/local``` or ```/opt/nextcloud/```.
* On **Windows 10** [```$USERPROFILE```](https://docs.microsoft.com/en-us/windows/deployment/usmt/usmt-recognized-environment-variables#a-href-idbkmk-2avariables-that-are-recognized-only-in-the-user-context) refers to ```C:\Users\<USERNAME>```.
##### Linux & Mac OS
```
$ cmake .. -DCMAKE_INSTALL_PREFIX=path-to-install-folder/ -DCMAKE_BUILD_TYPE=Debug -DNO_SHIBBOLETH=1 -DQTKEYCHAIN_LIBRARY=/path-to-qt5keychain-folder/lib64/libqt5keychain.so -DQTKEYCHAIN_INCLUDE_DIR=/path-to-qt5keychain-folder/include/qt5keychain/ -DOPENSSL_ROOT_DIR=/path-to-openssl-folder/ -DOPENSSL_INCLUDE_DIR=path-to-openssl-folder/include -DOPENSSL_LIBRARIES=path-to-openssl-folder/lib
$ cmake .. -DCMAKE_INSTALL_PREFIX=~/nextcloud-desktop-client -DCMAKE_BUILD_TYPE=Debug -DNO_SHIBBOLETH=1
$ make install
```
##### Windows
```
$ cmake -G "Visual Studio 15 2017 Win64" .. -DCMAKE_INSTALL_PREFIX=path-to-install-folder/ -DCMAKE_BUILD_TYPE=Debug -DNO_SHIBBOLETH=1 -DPng2Ico_EXECUTABLE=/path-to-install-png2ico/png2ico.exe -DQTKEYCHAIN_LIBRARY=/path-to-qt5keychain-folder/lib/qt5keychain.lib -DQTKEYCHAIN_INCLUDE_DIR=/path-to-qt5keychain-folder/include/qt5keychain/ -DOPENSSL_ROOT_DIR=/path-to-openssl-folder/ -DOPENSSL_INCLUDE_DIR=path-to-openssl-folder/include -DOPENSSL_LIBRARIES=path-to-openssl-folder/lib
$ cmake -G "Visual Studio 15 2017 Win64" .. -DCMAKE_INSTALL_PREFIX=$USERPROFILE\nextcloud-desktop-client -DCMAKE_BUILD_TYPE=Debug -DNO_SHIBBOLETH=1
$ cmake --build . --config Debug --target install
```
##### Mac OS
```
$ cmake .. -DCMAKE_INSTALL_PREFIX=path-to-install-folder/ -DCMAKE_BUILD_TYPE=Debug -DNO_SHIBBOLETH=1 -DQTKEYCHAIN_LIBRARY=/path-to-qt5keychain-folder/lib/libqt5keychain.dylib -DQTKEYCHAIN_INCLUDE_DIR=/path-to-qt5keychain-folder/include/qt5keychain/ -DOPENSSL_ROOT_DIR=/path-to-openssl-folder/ -DOPENSSL_INCLUDE_DIR=path-to-openssl-folder/include -DOPENSSL_LIBRARIES=path-to-openssl-folder/lib
$ make install
```
More detailed instructions can be found at the [Desktop Client Wiki](https://github.com/nextcloud/desktop/wiki).
:information_source: More detailed instructions can be found at the [Desktop Client Wiki](https://github.com/nextcloud/desktop/wiki).
### :inbox_tray: Where to find binaries to download

View File

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

View File

@@ -5,8 +5,8 @@ set -xe
mkdir /app
mkdir /build
#Set Qt-5.11
export QT_BASE_DIR=/opt/qt511
#Set Qt-5.12
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

View File

@@ -1,22 +1,22 @@
nextcloud-client (2.3.3-1.0~cosmic1) cosmic; urgency=medium
nextcloud-client (2.3.3-1.0~eoan1) eoan; 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~cosmic1) cosmic; urgency=medium
nextcloud-client (2.3.1-1.0~eoan1) eoan; 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~cosmic1) cosmic; urgency=medium
nextcloud-client (2.3.0-1.0~eoan1) eoan; 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~cosmic1) cosmic; urgency=medium
nextcloud-client (2.2.4-1.4~eoan1) eoan; urgency=medium
* The locale-specific icon names are correct too

View File

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

View File

@@ -1,40 +1,12 @@
--- nextcloud-client-2.4.0.orig/src/gui/wizard/owncloudoauthcredspage.cpp
+++ nextcloud-client-2.4.0/src/gui/wizard/owncloudoauthcredspage.cpp
@@ -53,10 +53,8 @@ OwncloudOAuthCredsPage::OwncloudOAuthCredsPage()
_ui.openLinkButton->setContextMenuPolicy(Qt::CustomContextMenu);
QObject::connect(_ui.openLinkButton, &QWidget::customContextMenuRequested, [this](const QPoint &pos) {
auto menu = new QMenu(_ui.openLinkButton);
- menu->addAction(tr("Copy link to clipboard"), this, [this] {
- if (_asyncAuth)
- QApplication::clipboard()->setText(_asyncAuth->authorisationLink().toString(QUrl::FullyEncoded));
- });
+ auto action = menu->addAction(tr("Copy link to clipboard"));
+ connect(action, &QAction::triggered, this, &OwncloudOAuthCredsPage::copyLinkToClipboard);
menu->setAttribute(Qt::WA_DeleteOnClose);
menu->popup(_ui.openLinkButton->mapToGlobal(pos));
});
@@ -131,4 +129,11 @@ bool OwncloudOAuthCredsPage::isComplete() const
return false; /* We can never go forward manually */
--- nextcloud-client-2.5.3.orig/src/3rdparty/kmessagewidget/kmessagewidget.cpp 2019-07-26 18:40:34.949349387 +0000
+++ nextcloud-client-2.5.3/src/3rdparty/kmessagewidget/kmessagewidget.cpp 2019-07-26 18:41:39.866478051 +0000
@@ -105,6 +105,9 @@
q->setMessageType(KMessageWidget::Information);
}
+void OwncloudOAuthCredsPage::copyLinkToClipboard()
+{
+ if (_asyncAuth)
+ QApplication::clipboard()->setText(_asyncAuth->authorisationLink().toString(QUrl::FullyEncoded));
+}
+template <typename T>
+constexpr typename std::add_const<T>::type &qAsConst(T &t) noexcept { return t; }
+
+
} // namespace OCC
--- nextcloud-client-2.4.0.orig/src/gui/wizard/owncloudoauthcredspage.h
+++ nextcloud-client-2.4.0/src/gui/wizard/owncloudoauthcredspage.h
@@ -57,6 +57,10 @@ public:
QString _refreshToken;
QScopedPointer<OAuth> _asyncAuth;
Ui_OwncloudOAuthCredsPage _ui;
+
+protected slots:
+ void copyLinkToClipboard();
+
};
} // namespace OCC
void KMessageWidgetPrivate::createLayout()
{
delete content->layout();

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 cosmic disco stable; do
for distribution in xenial bionic disco eoan stable; do
rm -rf nextcloud-client_${basever}
cp -a ${DRONE_WORKSPACE} nextcloud-client_${basever}

View File

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

View File

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

View File

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

View File

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

View File

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

BIN
resources/add.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

1
resources/state-info.svg Normal file
View File

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

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -41,6 +41,7 @@ macro(libcloudproviders_add_config _sources)
endmacro(libcloudproviders_add_config _sources)
find_package(Qt5 5.6 COMPONENTS DBus)
IF (UNIX AND Qt5DBus_FOUND AND LIBCLOUDPROVIDERS_FOUND)
STRING(TOLOWER "${APPLICATION_VENDOR}" DBUS_VENDOR)
STRING(REGEX REPLACE "[^A-z0-9]" "" DBUS_VENDOR "${DBUS_VENDOR}")

View File

@@ -0,0 +1,512 @@
/* This file is part of the KDE libraries
*
* Copyright (c) 2011 Aurélien Gâteau <agateau@kde.org>
* Copyright (c) 2014 Dominik Haumann <dhaumann@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include "kmessagewidget.h"
#include <QAction>
#include <QApplication>
#include <QEvent>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QPainter>
#include <QShowEvent>
#include <QTimeLine>
#include <QToolButton>
#include <QStyle>
//---------------------------------------------------------------------
// KMessageWidgetPrivate
//---------------------------------------------------------------------
class KMessageWidgetPrivate
{
public:
void init(KMessageWidget *);
KMessageWidget *q;
QFrame *content = nullptr;
QLabel *iconLabel = nullptr;
QLabel *textLabel = nullptr;
QToolButton *closeButton = nullptr;
QTimeLine *timeLine = nullptr;
QIcon icon;
bool ignoreShowEventDoingAnimatedShow = false;
KMessageWidget::MessageType messageType;
bool wordWrap;
QList<QToolButton *> buttons;
QPixmap contentSnapShot;
void createLayout();
void applyStyleSheet();
void updateSnapShot();
void updateLayout();
void slotTimeLineChanged(qreal);
void slotTimeLineFinished();
int bestContentHeight() const;
};
void KMessageWidgetPrivate::init(KMessageWidget *q_ptr)
{
q = q_ptr;
q->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
// Note: when changing the value 500, also update KMessageWidgetTest
timeLine = new QTimeLine(500, q);
QObject::connect(timeLine, SIGNAL(valueChanged(qreal)), q, SLOT(slotTimeLineChanged(qreal)));
QObject::connect(timeLine, SIGNAL(finished()), q, SLOT(slotTimeLineFinished()));
content = new QFrame(q);
content->setObjectName(QStringLiteral("contentWidget"));
content->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
wordWrap = false;
iconLabel = new QLabel(content);
iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
iconLabel->hide();
textLabel = new QLabel(content);
textLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
textLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
QObject::connect(textLabel, &QLabel::linkActivated, q, &KMessageWidget::linkActivated);
QObject::connect(textLabel, &QLabel::linkHovered, q, &KMessageWidget::linkHovered);
QAction *closeAction = new QAction(q);
closeAction->setText(KMessageWidget::tr("&Close"));
closeAction->setToolTip(KMessageWidget::tr("Close message"));
closeAction->setIcon(QIcon(":/client/resources/close.svg")); // ivan: NC customization
QObject::connect(closeAction, &QAction::triggered, q, &KMessageWidget::animatedHide);
closeButton = new QToolButton(content);
closeButton->setAutoRaise(true);
closeButton->setDefaultAction(closeAction);
q->setMessageType(KMessageWidget::Information);
}
void KMessageWidgetPrivate::createLayout()
{
delete content->layout();
content->resize(q->size());
qDeleteAll(buttons);
buttons.clear();
Q_FOREACH (QAction *action, q->actions()) {
QToolButton *button = new QToolButton(content);
button->setDefaultAction(action);
button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
buttons.append(button);
}
// AutoRaise reduces visual clutter, but we don't want to turn it on if
// there are other buttons, otherwise the close button will look different
// from the others.
closeButton->setAutoRaise(buttons.isEmpty());
if (wordWrap) {
QGridLayout *layout = new QGridLayout(content);
// Set alignment to make sure icon does not move down if text wraps
layout->addWidget(iconLabel, 0, 0, 1, 1, Qt::AlignHCenter | Qt::AlignTop);
layout->addWidget(textLabel, 0, 1);
if (buttons.isEmpty()) {
// Use top-vertical alignment like the icon does.
layout->addWidget(closeButton, 0, 2, 1, 1, Qt::AlignHCenter | Qt::AlignTop);
} else {
// Use an additional layout in row 1 for the buttons.
QHBoxLayout *buttonLayout = new QHBoxLayout;
buttonLayout->addStretch();
Q_FOREACH (QToolButton *button, buttons) {
// For some reason, calling show() is necessary if wordwrap is true,
// otherwise the buttons do not show up. It is not needed if
// wordwrap is false.
button->show();
buttonLayout->addWidget(button);
}
buttonLayout->addWidget(closeButton);
layout->addItem(buttonLayout, 1, 0, 1, 2);
}
} else {
QHBoxLayout *layout = new QHBoxLayout(content);
layout->addWidget(iconLabel);
layout->addWidget(textLabel);
for (QToolButton *button : qAsConst(buttons)) {
layout->addWidget(button);
}
layout->addWidget(closeButton);
};
if (q->isVisible()) {
q->setFixedHeight(content->sizeHint().height());
}
q->updateGeometry();
}
void KMessageWidgetPrivate::applyStyleSheet()
{
QColor bgBaseColor;
// We have to hardcode colors here because KWidgetsAddons is a tier 1 framework
// and therefore can't depend on any other KDE Frameworks
// The following RGB color values come from the "default" scheme in kcolorscheme.cpp
switch (messageType) {
case KMessageWidget::Positive:
bgBaseColor.setRgb(39, 174, 96); // Window: ForegroundPositive
break;
case KMessageWidget::Information:
bgBaseColor.setRgb(61, 174, 233); // Window: ForegroundActive
break;
case KMessageWidget::Warning:
bgBaseColor.setRgb(246, 116, 0); // Window: ForegroundNeutral
break;
case KMessageWidget::Error:
bgBaseColor.setRgb(218, 68, 83); // Window: ForegroundNegative
break;
}
const qreal bgBaseColorAlpha = 0.2;
bgBaseColor.setAlphaF(bgBaseColorAlpha);
const QPalette palette = QGuiApplication::palette();
const QColor windowColor = palette.window().color();
const QColor textColor = palette.text().color();
const QColor border = bgBaseColor;
// Generate a final background color from overlaying bgBaseColor over windowColor
const int newRed = (bgBaseColor.red() * bgBaseColorAlpha) + (windowColor.red() * (1 - bgBaseColorAlpha));
const int newGreen = (bgBaseColor.green() * bgBaseColorAlpha) + (windowColor.green() * (1 - bgBaseColorAlpha));
const int newBlue = (bgBaseColor.blue() * bgBaseColorAlpha) + (windowColor.blue() * (1 - bgBaseColorAlpha));
const QColor bgFinalColor = QColor(newRed, newGreen, newBlue);
content->setStyleSheet(
QString::fromLatin1(".QFrame {"
"background-color: %1;"
"border-radius: 4px;"
"border: 2px solid %2;"
"margin: %3px;"
"}"
".QLabel { color: %4; }"
)
.arg(bgFinalColor.name())
.arg(border.name())
// DefaultFrameWidth returns the size of the external margin + border width. We know our border is 1px, so we subtract this from the frame normal QStyle FrameWidth to get our margin
.arg(q->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, q) - 1)
.arg(textColor.name())
);
}
void KMessageWidgetPrivate::updateLayout()
{
if (content->layout()) {
createLayout();
}
}
void KMessageWidgetPrivate::updateSnapShot()
{
// Attention: updateSnapShot calls QWidget::render(), which causes the whole
// window layouts to be activated. Calling this method from resizeEvent()
// can lead to infinite recursion, see:
// https://bugs.kde.org/show_bug.cgi?id=311336
contentSnapShot = QPixmap(content->size() * q->devicePixelRatio());
contentSnapShot.setDevicePixelRatio(q->devicePixelRatio());
contentSnapShot.fill(Qt::transparent);
content->render(&contentSnapShot, QPoint(), QRegion(), QWidget::DrawChildren);
}
void KMessageWidgetPrivate::slotTimeLineChanged(qreal value)
{
q->setFixedHeight(qMin(value * 2, qreal(1.0)) * content->height());
q->update();
}
void KMessageWidgetPrivate::slotTimeLineFinished()
{
if (timeLine->direction() == QTimeLine::Forward) {
// Show
// We set the whole geometry here, because it may be wrong if a
// KMessageWidget is shown right when the toplevel window is created.
content->setGeometry(0, 0, q->width(), bestContentHeight());
// notify about finished animation
emit q->showAnimationFinished();
} else {
// hide and notify about finished animation
q->hide();
emit q->hideAnimationFinished();
}
}
int KMessageWidgetPrivate::bestContentHeight() const
{
int height = content->heightForWidth(q->width());
if (height == -1) {
height = content->sizeHint().height();
}
return height;
}
//---------------------------------------------------------------------
// KMessageWidget
//---------------------------------------------------------------------
KMessageWidget::KMessageWidget(QWidget *parent)
: QFrame(parent)
, d(new KMessageWidgetPrivate)
{
d->init(this);
}
KMessageWidget::KMessageWidget(const QString &text, QWidget *parent)
: QFrame(parent)
, d(new KMessageWidgetPrivate)
{
d->init(this);
setText(text);
}
KMessageWidget::~KMessageWidget()
{
delete d;
}
QString KMessageWidget::text() const
{
return d->textLabel->text();
}
void KMessageWidget::setText(const QString &text)
{
d->textLabel->setText(text);
updateGeometry();
}
KMessageWidget::MessageType KMessageWidget::messageType() const
{
return d->messageType;
}
void KMessageWidget::setMessageType(KMessageWidget::MessageType type)
{
d->messageType = type;
d->applyStyleSheet();
}
QSize KMessageWidget::sizeHint() const
{
ensurePolished();
return d->content->sizeHint();
}
QSize KMessageWidget::minimumSizeHint() const
{
ensurePolished();
return d->content->minimumSizeHint();
}
bool KMessageWidget::event(QEvent *event)
{
if (event->type() == QEvent::Polish && !d->content->layout()) {
d->createLayout();
} else if (event->type() == QEvent::PaletteChange) {
d->applyStyleSheet();
} else if (event->type() == QEvent::Show && !d->ignoreShowEventDoingAnimatedShow) {
if ((height() != d->content->height()) || (d->content->pos().y() != 0)) {
d->content->move(0, 0);
setFixedHeight(d->content->height());
}
}
return QFrame::event(event);
}
void KMessageWidget::resizeEvent(QResizeEvent *event)
{
QFrame::resizeEvent(event);
if (d->timeLine->state() == QTimeLine::NotRunning) {
d->content->resize(width(), d->bestContentHeight());
}
}
int KMessageWidget::heightForWidth(int width) const
{
ensurePolished();
return d->content->heightForWidth(width);
}
void KMessageWidget::paintEvent(QPaintEvent *event)
{
QFrame::paintEvent(event);
if (d->timeLine->state() == QTimeLine::Running) {
QPainter painter(this);
painter.setOpacity(d->timeLine->currentValue() * d->timeLine->currentValue());
painter.drawPixmap(0, 0, d->contentSnapShot);
}
}
bool KMessageWidget::wordWrap() const
{
return d->wordWrap;
}
void KMessageWidget::setWordWrap(bool wordWrap)
{
d->wordWrap = wordWrap;
d->textLabel->setWordWrap(wordWrap);
QSizePolicy policy = sizePolicy();
policy.setHeightForWidth(wordWrap);
setSizePolicy(policy);
d->updateLayout();
// Without this, when user does wordWrap -> !wordWrap -> wordWrap, a minimum
// height is set, causing the widget to be too high.
// Mostly visible in test programs.
if (wordWrap) {
setMinimumHeight(0);
}
}
bool KMessageWidget::isCloseButtonVisible() const
{
return d->closeButton->isVisible();
}
void KMessageWidget::setCloseButtonVisible(bool show)
{
d->closeButton->setVisible(show);
updateGeometry();
}
void KMessageWidget::addAction(QAction *action)
{
QFrame::addAction(action);
d->updateLayout();
}
void KMessageWidget::removeAction(QAction *action)
{
QFrame::removeAction(action);
d->updateLayout();
}
void KMessageWidget::animatedShow()
{
// Test before styleHint, as there might have been a style change while animation was running
if (isHideAnimationRunning()) {
d->timeLine->stop();
emit hideAnimationFinished();
}
if (!style()->styleHint(QStyle::SH_Widget_Animate, nullptr, this)
|| (parentWidget() && !parentWidget()->isVisible())) {
show();
emit showAnimationFinished();
return;
}
if (isVisible() && (d->timeLine->state() == QTimeLine::NotRunning) && (height() == d->bestContentHeight()) && (d->content->pos().y() == 0)) {
emit showAnimationFinished();
return;
}
d->ignoreShowEventDoingAnimatedShow = true;
show();
d->ignoreShowEventDoingAnimatedShow = false;
setFixedHeight(0);
int wantedHeight = d->bestContentHeight();
d->content->setGeometry(0, -wantedHeight, width(), wantedHeight);
d->updateSnapShot();
d->timeLine->setDirection(QTimeLine::Forward);
if (d->timeLine->state() == QTimeLine::NotRunning) {
d->timeLine->start();
}
}
void KMessageWidget::animatedHide()
{
// test this before isVisible, as animatedShow might have been called directly before,
// so the first timeline event is not yet done and the widget is still hidden
// And before styleHint, as there might have been a style change while animation was running
if (isShowAnimationRunning()) {
d->timeLine->stop();
emit showAnimationFinished();
}
if (!style()->styleHint(QStyle::SH_Widget_Animate, nullptr, this)) {
hide();
emit hideAnimationFinished();
return;
}
if (!isVisible()) {
// explicitly hide it, so it stays hidden in case it is only not visible due to the parents
hide();
emit hideAnimationFinished();
return;
}
d->content->move(0, -d->content->height());
d->updateSnapShot();
d->timeLine->setDirection(QTimeLine::Backward);
if (d->timeLine->state() == QTimeLine::NotRunning) {
d->timeLine->start();
}
}
bool KMessageWidget::isHideAnimationRunning() const
{
return (d->timeLine->direction() == QTimeLine::Backward)
&& (d->timeLine->state() == QTimeLine::Running);
}
bool KMessageWidget::isShowAnimationRunning() const
{
return (d->timeLine->direction() == QTimeLine::Forward)
&& (d->timeLine->state() == QTimeLine::Running);
}
QIcon KMessageWidget::icon() const
{
return d->icon;
}
void KMessageWidget::setIcon(const QIcon &icon)
{
d->icon = icon;
if (d->icon.isNull()) {
d->iconLabel->hide();
} else {
const int size = style()->pixelMetric(QStyle::PM_ToolBarIconSize);
d->iconLabel->setPixmap(d->icon.pixmap(size));
d->iconLabel->show();
}
}
#include "moc_kmessagewidget.cpp"

View File

@@ -0,0 +1,346 @@
/* This file is part of the KDE libraries
*
* Copyright (c) 2011 Aurélien Gâteau <agateau@kde.org>
* Copyright (c) 2014 Dominik Haumann <dhaumann@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#ifndef KMESSAGEWIDGET_H
#define KMESSAGEWIDGET_H
#include <QFrame>
class KMessageWidgetPrivate;
/**
* @class KMessageWidget kmessagewidget.h KMessageWidget
*
* @short A widget to provide feedback or propose opportunistic interactions.
*
* KMessageWidget can be used to provide inline positive or negative
* feedback, or to implement opportunistic interactions.
*
* As a feedback widget, KMessageWidget provides a less intrusive alternative
* to "OK Only" message boxes. If you want to avoid a modal KMessageBox,
* consider using KMessageWidget instead.
*
* Examples of KMessageWidget look as follows, all of them having an icon set
* with setIcon(), and the first three show a close button:
*
* \image html kmessagewidget.png "KMessageWidget with different message types"
*
* <b>Negative feedback</b>
*
* The KMessageWidget can be used as a secondary indicator of failure: the
* first indicator is usually the fact the action the user expected to happen
* did not happen.
*
* Example: User fills a form, clicks "Submit".
*
* @li Expected feedback: form closes
* @li First indicator of failure: form stays there
* @li Second indicator of failure: a KMessageWidget appears on top of the
* form, explaining the error condition
*
* When used to provide negative feedback, KMessageWidget should be placed
* close to its context. In the case of a form, it should appear on top of the
* form entries.
*
* KMessageWidget should get inserted in the existing layout. Space should not
* be reserved for it, otherwise it becomes "dead space", ignored by the user.
* KMessageWidget should also not appear as an overlay to prevent blocking
* access to elements the user needs to interact with to fix the failure.
*
* <b>Positive feedback</b>
*
* KMessageWidget can be used for positive feedback but it shouldn't be
* overused. It is often enough to provide feedback by simply showing the
* results of an action.
*
* Examples of acceptable uses:
*
* @li Confirm success of "critical" transactions
* @li Indicate completion of background tasks
*
* Example of unadapted uses:
*
* @li Indicate successful saving of a file
* @li Indicate a file has been successfully removed
*
* <b>Opportunistic interaction</b>
*
* Opportunistic interaction is the situation where the application suggests to
* the user an action he could be interested in perform, either based on an
* action the user just triggered or an event which the application noticed.
*
* Example of acceptable uses:
*
* @li A browser can propose remembering a recently entered password
* @li A music collection can propose ripping a CD which just got inserted
* @li A chat application may notify the user a "special friend" just connected
*
* @author Aurélien Gâteau <agateau@kde.org>
* @since 4.7
*/
class KMessageWidget : public QFrame
{
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText)
Q_PROPERTY(bool wordWrap READ wordWrap WRITE setWordWrap)
Q_PROPERTY(bool closeButtonVisible READ isCloseButtonVisible WRITE setCloseButtonVisible)
Q_PROPERTY(MessageType messageType READ messageType WRITE setMessageType)
Q_PROPERTY(QIcon icon READ icon WRITE setIcon)
public:
/**
* Available message types.
* The background colors are chosen depending on the message type.
*/
enum MessageType {
Positive,
Information,
Warning,
Error
};
Q_ENUM(MessageType)
/**
* Constructs a KMessageWidget with the specified @p parent.
*/
explicit KMessageWidget(QWidget *parent = nullptr);
/**
* Constructs a KMessageWidget with the specified @p parent and
* contents @p text.
*/
explicit KMessageWidget(const QString &text, QWidget *parent = nullptr);
/**
* Destructor.
*/
~KMessageWidget() override;
/**
* Get the text of this message widget.
* @see setText()
*/
QString text() const;
/**
* Check whether word wrap is enabled.
*
* If word wrap is enabled, the message widget wraps the displayed text
* as required to the available width of the widget. This is useful to
* avoid breaking widget layouts.
*
* @see setWordWrap()
*/
bool wordWrap() const;
/**
* Check whether the close button is visible.
*
* @see setCloseButtonVisible()
*/
bool isCloseButtonVisible() const;
/**
* Get the type of this message.
* By default, the type is set to KMessageWidget::Information.
*
* @see KMessageWidget::MessageType, setMessageType()
*/
MessageType messageType() const;
/**
* Add @p action to the message widget.
* For each action a button is added to the message widget in the
* order the actions were added.
*
* @param action the action to add
* @see removeAction(), QWidget::actions()
*/
void addAction(QAction *action);
/**
* Remove @p action from the message widget.
*
* @param action the action to remove
* @see KMessageWidget::MessageType, addAction(), setMessageType()
*/
void removeAction(QAction *action);
/**
* Returns the preferred size of the message widget.
*/
QSize sizeHint() const override;
/**
* Returns the minimum size of the message widget.
*/
QSize minimumSizeHint() const override;
/**
* Returns the required height for @p width.
* @param width the width in pixels
*/
int heightForWidth(int width) const override;
/**
* The icon shown on the left of the text. By default, no icon is shown.
* @since 4.11
*/
QIcon icon() const;
/**
* Check whether the hide animation started by calling animatedHide()
* is still running. If animations are disabled, this function always
* returns @e false.
*
* @see animatedHide(), hideAnimationFinished()
* @since 5.0
*/
bool isHideAnimationRunning() const;
/**
* Check whether the show animation started by calling animatedShow()
* is still running. If animations are disabled, this function always
* returns @e false.
*
* @see animatedShow(), showAnimationFinished()
* @since 5.0
*/
bool isShowAnimationRunning() const;
public Q_SLOTS:
/**
* Set the text of the message widget to @p text.
* If the message widget is already visible, the text changes on the fly.
*
* @param text the text to display, rich text is allowed
* @see text()
*/
void setText(const QString &text);
/**
* Set word wrap to @p wordWrap. If word wrap is enabled, the text()
* of the message widget is wrapped to fit the available width.
* If word wrap is disabled, the message widget's minimum size is
* such that the entire text fits.
*
* @param wordWrap disable/enable word wrap
* @see wordWrap()
*/
void setWordWrap(bool wordWrap);
/**
* Set the visibility of the close button. If @p visible is @e true,
* a close button is shown that calls animatedHide() if clicked.
*
* @see closeButtonVisible(), animatedHide()
*/
void setCloseButtonVisible(bool visible);
/**
* Set the message type to @p type.
* By default, the message type is set to KMessageWidget::Information.
* Appropriate colors are chosen to mimic the appearance of Kirigami's
* InlineMessage.
*
* @see messageType(), KMessageWidget::MessageType
*/
void setMessageType(KMessageWidget::MessageType type);
/**
* Show the widget using an animation.
*/
void animatedShow();
/**
* Hide the widget using an animation.
*/
void animatedHide();
/**
* Define an icon to be shown on the left of the text
* @since 4.11
*/
void setIcon(const QIcon &icon);
Q_SIGNALS:
/**
* This signal is emitted when the user clicks a link in the text label.
* The URL referred to by the href anchor is passed in contents.
* @param contents text of the href anchor
* @see QLabel::linkActivated()
* @since 4.10
*/
void linkActivated(const QString &contents);
/**
* This signal is emitted when the user hovers over a link in the text label.
* The URL referred to by the href anchor is passed in contents.
* @param contents text of the href anchor
* @see QLabel::linkHovered()
* @since 4.11
*/
void linkHovered(const QString &contents);
/**
* This signal is emitted when the hide animation is finished, started by
* calling animatedHide(). If animations are disabled, this signal is
* emitted immediately after the message widget got hidden.
*
* @note This signal is @e not emitted if the widget was hidden by
* calling hide(), so this signal is only useful in conjunction
* with animatedHide().
*
* @see animatedHide()
* @since 5.0
*/
void hideAnimationFinished();
/**
* This signal is emitted when the show animation is finished, started by
* calling animatedShow(). If animations are disabled, this signal is
* emitted immediately after the message widget got shown.
*
* @note This signal is @e not emitted if the widget was shown by
* calling show(), so this signal is only useful in conjunction
* with animatedShow().
*
* @see animatedShow()
* @since 5.0
*/
void showAnimationFinished();
protected:
void paintEvent(QPaintEvent *event) override;
bool event(QEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
private:
KMessageWidgetPrivate *const d;
friend class KMessageWidgetPrivate;
Q_PRIVATE_SLOT(d, void slotTimeLineChanged(qreal))
Q_PRIVATE_SLOT(d, void slotTimeLineFinished())
};
#endif /* KMESSAGEWIDGET_H */

View File

@@ -87,9 +87,36 @@ void setLaunchOnStartup_private(const QString &appName, const QString &guiName,
}
}
// TODO: Right now only detection on toggle/startup, not when windows theme is switched while nextcloud is running
static inline bool hasDarkSystray_private()
{
return true;
bool hasDarkSystray = true;
// Open registry key first, continue only on success (may be legitimately absent in earlier windows versions)
HKEY hKey;
LONG lRes = RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", 0, KEY_READ, &hKey);
// classical windows function - preserve buff size for DWORD, call ExW version, store regkey value in nResult
if (lRes == ERROR_SUCCESS) {
DWORD dwBufferSize(sizeof(DWORD));
DWORD nResult(0);
// https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regqueryvalueexw
LONG nError = ::RegQueryValueExW(hKey,
L"SystemUsesLightTheme",
NULL,
NULL,
reinterpret_cast<LPBYTE>(&nResult),
&dwBufferSize);
// if RegQuery returned no error and light theme was found, change systray return value
if (nError == ERROR_SUCCESS && nResult == 1)
hasDarkSystray = false;
return hasDarkSystray;
} else {
// fallback to true if regkey could not be determined
return hasDarkSystray;
}
}
QVariant Utility::registryGetKeyValue(HKEY hRootKey, const QString &subKey, const QString &valueName)

View File

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

View File

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

View File

@@ -124,7 +124,9 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
* because it's a hidden file that should not be synced.
* This code should probably be in csync_exclude, but it does not have the fs parameter.
* Keep it here for now */
if (ctx->ignore_hidden_files && (fs->is_hidden)) {
if (ctx->ignore_hidden_files
&& fs->is_hidden
&& !fs->path.endsWith(".sync-exclude.lst")) {
qCInfo(lcUpdate, "file excluded because it is a hidden file: %s", fs->path.constData());
excluded = CSYNC_FILE_EXCLUDE_HIDDEN;
}

View File

@@ -22,6 +22,7 @@ set(client_UI_SRCS
generalsettings.ui
legalnotice.ui
ignorelisteditor.ui
ignorelisttablewidget.ui
networksettings.ui
activitywidget.ui
synclogdialog.ui
@@ -38,6 +39,8 @@ set(client_UI_SRCS
wizard/owncloudconnectionmethoddialog.ui
wizard/owncloudhttpcredspage.ui
wizard/owncloudoauthcredspage.ui
wizard/flow2authcredspage.ui
wizard/flow2authwidget.ui
wizard/owncloudsetupnocredspage.ui
wizard/owncloudwizardresultpage.ui
wizard/webview.ui
@@ -59,6 +62,7 @@ set(client_SRCS
generalsettings.cpp
legalnotice.cpp
ignorelisteditor.cpp
ignorelisttablewidget.cpp
lockwatcher.cpp
logbrowser.cpp
navigationpanehelper.cpp
@@ -103,6 +107,7 @@ set(client_SRCS
creds/credentialsfactory.cpp
creds/httpcredentialsgui.cpp
creds/oauth.cpp
creds/flow2auth.cpp
creds/webflowcredentials.cpp
creds/webflowcredentialsdialog.cpp
wizard/postfixlineedit.cpp
@@ -111,6 +116,8 @@ set(client_SRCS
wizard/owncloudconnectionmethoddialog.cpp
wizard/owncloudhttpcredspage.cpp
wizard/owncloudoauthcredspage.cpp
wizard/flow2authcredspage.cpp
wizard/flow2authwidget.cpp
wizard/owncloudsetuppage.cpp
wizard/owncloudwizardcommon.cpp
wizard/owncloudwizard.cpp
@@ -165,6 +172,7 @@ set(3rdparty_SRC
../3rdparty/qtsingleapplication/qtlocalpeer.cpp
../3rdparty/qtsingleapplication/qtsingleapplication.cpp
../3rdparty/qtsingleapplication/qtsinglecoreapplication.cpp
../3rdparty/kmessagewidget/kmessagewidget.cpp
)
if (APPLE)
@@ -243,7 +251,7 @@ if(APPLE)
file(GLOB_RECURSE OWNCLOUD_SIDEBAR_ICONS "${theme_dir}/colored/*-${APPLICATION_ICON_NAME}-sidebar*")
MESSAGE(STATUS "OWNCLOUD_SIDEBAR_ICONS: ${APPLICATION_ICON_NAME}: ${OWNCLOUD_SIDEBAR_ICONS}")
endif()
ecm_add_app_icon(final_src ICONS "${OWNCLOUD_ICONS}" SIDEBAR_ICONS "${OWNCLOUD_SIDEBAR_ICONS}" OUTFILE_BASE "${APPLICATION_ICON_NAME}")
ecm_add_app_icon(final_src ICONS "${OWNCLOUD_ICONS}" SIDEBAR_ICONS "${OWNCLOUD_SIDEBAR_ICONS}" OUTFILE_BASENAME "${APPLICATION_ICON_NAME}")
if(UNIX AND NOT APPLE)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIE")
@@ -265,6 +273,9 @@ if(NOT BUILD_OWNCLOUD_OSX_BUNDLE)
endforeach(_file)
install(FILES ${client_I18N} DESTINATION ${SHAREDIR}/${APPLICATION_EXECUTABLE}/i18n)
else()
file(GLOB_RECURSE VISUAL_ELEMENTS "${theme_dir}/colored/*-${APPLICATION_ICON_NAME}-w10startmenu*")
install(FILES ${VISUAL_ELEMENTS} DESTINATION bin/visualelements)
install(FILES "${theme_dir}/${APPLICATION_EXECUTABLE}.VisualElementsManifest.xml" DESTINATION bin)
install(FILES ${client_I18N} DESTINATION i18n)
endif()
@@ -329,6 +340,7 @@ target_include_directories(${APPLICATION_EXECUTABLE} PRIVATE
${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}
)

View File

@@ -342,9 +342,6 @@ AccountPtr AccountManager::createAccount()
connect(acc.data(), &Account::proxyAuthenticationRequired,
ProxyAuthHandler::instance(), &ProxyAuthHandler::handleProxyAuthenticationRequired);
connect(acc.data()->e2e(), &ClientSideEncryption::mnemonicGenerated,
&AccountManager::displayMnemonic);
return acc;
}
@@ -364,6 +361,7 @@ void AccountManager::displayMnemonic(const QString& mnemonic)
widget->exec();
widget->resize(widget->sizeHint());
}
void AccountManager::shutdown()
{
auto accountsCopy = _accounts;

View File

@@ -35,10 +35,12 @@
#include "filesystem.h"
#include "clientsideencryptionjobs.h"
#include "syncresult.h"
#include "ignorelisttablewidget.h"
#include <math.h>
#include <QDesktopServices>
#include <QDialogButtonBox>
#include <QDir>
#include <QListWidgetItem>
#include <QMessageBox>
@@ -194,6 +196,14 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
// Connect E2E stuff
connect(this, &AccountSettings::requesetMnemonic, _accountState->account()->e2e(), &ClientSideEncryption::slotRequestMnemonic);
connect(_accountState->account()->e2e(), &ClientSideEncryption::showMnemonic, this, &AccountSettings::slotShowMnemonic);
connect(_accountState->account()->e2e(), &ClientSideEncryption::mnemonicGenerated, this, &AccountSettings::slotNewMnemonicGenerated);
if (_accountState->account()->e2e()->newMnemonicGenerated())
{
slotNewMnemonicGenerated();
} else {
ui->encryptionMessage->hide();
}
}
@@ -222,6 +232,19 @@ void AccountSettings::createAccountToolbox()
slotAccountAdded(_accountState);
}
void AccountSettings::slotNewMnemonicGenerated()
{
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);
ui->encryptionMessage->addAction(mnemonic);
ui->encryptionMessage->show();
}
void AccountSettings::slotMenuBeforeShow() {
if (_menuShown) {
return;
@@ -401,7 +424,7 @@ bool AccountSettings::canEncryptOrDecrypt (const FolderStatusModel::SubFolderInf
return true;
}
void AccountSettings::slotMarkSubfolderEncrpted(const FolderStatusModel::SubFolderInfo* folderInfo)
void AccountSettings::slotMarkSubfolderEncrypted(const FolderStatusModel::SubFolderInfo* folderInfo)
{
if (!canEncryptOrDecrypt(folderInfo)) {
return;
@@ -518,6 +541,51 @@ void AccountSettings::slotLockForDecryptionError(const QByteArray& fileId, int h
qDebug() << "Error Locking for decryption";
}
void AccountSettings::slotEditCurrentIgnoredFiles()
{
Folder *f = FolderMan::instance()->folder(selectedFolderAlias());
if (f == nullptr)
return;
openIgnoredFilesDialog(f->path());
}
void AccountSettings::slotEditCurrentLocalIgnoredFiles()
{
QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
if (!selected.isValid() || _model->classify(selected) != FolderStatusModel::SubFolder)
return;
QString fileName = _model->data(selected, FolderStatusDelegate::FolderPathRole).toString();
openIgnoredFilesDialog(fileName);
}
void AccountSettings::openIgnoredFilesDialog(const QString & absFolderPath)
{
Q_ASSERT(absFolderPath.startsWith('/'));
Q_ASSERT(absFolderPath.endsWith('/'));
const QString ignoreFile = absFolderPath + ".sync-exclude.lst";
auto layout = new QVBoxLayout();
auto ignoreListWidget = new IgnoreListTableWidget(this);
ignoreListWidget->readIgnoreFile(ignoreFile);
layout->addWidget(ignoreListWidget);
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
layout->addWidget(buttonBox);
auto dialog = new QDialog();
dialog->setLayout(layout);
connect(buttonBox, &QDialogButtonBox::clicked, [=](QAbstractButton * button) {
if (buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole)
ignoreListWidget->slotWriteIgnoreFile(ignoreFile);
dialog->close();
});
connect(buttonBox, &QDialogButtonBox::rejected,
dialog, &QDialog::close);
dialog->open();
}
void AccountSettings::slotSubfolderContextMenuRequested(const QModelIndex& index, const QPoint& pos)
{
Q_UNUSED(pos);
@@ -540,12 +608,16 @@ void AccountSettings::slotSubfolderContextMenuRequested(const QModelIndex& index
if (!isEncrypted) {
ac = menu.addAction(tr("Encrypt"));
connect(ac, &QAction::triggered, [this, &info] { slotMarkSubfolderEncrpted(info); });
connect(ac, &QAction::triggered, [this, &info] { slotMarkSubfolderEncrypted(info); });
} else {
// Ingore decrypting for now since it only works with an empty folder
// connect(ac, &QAction::triggered, [this, &info] { slotMarkSubfolderDecrypted(info); });
}
}
ac = menu.addAction(tr("Edit Ignored Files"));
connect(ac, &QAction::triggered, this, &AccountSettings::slotEditCurrentLocalIgnoredFiles);
menu.exec(QCursor::pos());
}
@@ -579,6 +651,9 @@ void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
QAction *ac = menu->addAction(tr("Open folder"));
connect(ac, &QAction::triggered, this, &AccountSettings::slotOpenCurrentFolder);
ac = menu->addAction(tr("Edit Ignored Files"));
connect(ac, &QAction::triggered, this, &AccountSettings::slotEditCurrentIgnoredFiles);
if (!ui->_folderList->isExpanded(index)) {
ac = menu->addAction(tr("Choose what to sync"));
ac->setEnabled(folderConnected);

View File

@@ -80,6 +80,8 @@ protected slots:
void slotRemoveCurrentFolder();
void slotOpenCurrentFolder(); // sync folder
void slotOpenCurrentLocalSubFolder(); // selected subfolder in sync folder
void slotEditCurrentIgnoredFiles();
void slotEditCurrentLocalIgnoredFiles();
void slotFolderWizardAccepted();
void slotFolderWizardRejected();
void slotDeleteAccount();
@@ -87,7 +89,7 @@ protected slots:
void slotOpenAccountWizard();
void slotAccountAdded(AccountState *);
void refreshSelectiveSyncStatus();
void slotMarkSubfolderEncrpted(const FolderStatusModel::SubFolderInfo* folderInfo);
void slotMarkSubfolderEncrypted(const FolderStatusModel::SubFolderInfo* folderInfo);
void slotMarkSubfolderDecrypted(const FolderStatusModel::SubFolderInfo* folderInfo);
void slotSubfolderContextMenuRequested(const QModelIndex& idx, const QPoint& point);
void slotCustomContextMenuRequested(const QPoint &);
@@ -99,6 +101,7 @@ protected slots:
// Encryption Related Stuff.
void slotShowMnemonic(const QString &mnemonic);
void slotNewMnemonicGenerated();
void slotEncryptionFlagSuccess(const QByteArray &folderId);
void slotEncryptionFlagError(const QByteArray &folderId, int httpReturnCode);
@@ -109,7 +112,7 @@ protected slots:
void slotUploadMetadataSuccess(const QByteArray& folderId);
void slotUpdateMetadataError(const QByteArray& folderId, int httpReturnCode);
// Remove Encryotion Bit.
// Remove Encryption Bit.
void slotLockForDecryptionSuccess(const QByteArray& folderId, const QByteArray& token);
void slotLockForDecryptionError(const QByteArray& folderId, int httpReturnCode);
void slotDeleteMetadataSuccess(const QByteArray& folderId);
@@ -124,6 +127,7 @@ private:
QStringList errors = QStringList());
bool event(QEvent *) override;
void createAccountToolbox();
void openIgnoredFilesDialog(const QString & absFolderPath);
/// Returns the alias of the selected folder, empty string if none
QString selectedFolderAlias() const;

View File

@@ -6,136 +6,15 @@
<rect>
<x>0</x>
<y>0</y>
<width>575</width>
<width>582</width>
<height>557</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
<string notr="true">Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QWidget" name="accountStatus" native="true">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="SslButton" name="sslButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="connectLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Connected with &lt;server&gt; as &lt;user&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QToolButton" name="_accountToolbox">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="storageGroupBox">
<item>
<widget class="QLabel" name="quotaInfoLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string/>
</property>
<property name="text">
<string>Storage space: ...</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="quotaProgressBar">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>7</height>
</size>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>-1</number>
</property>
<property name="textVisible">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="OCC::FolderStatusView" name="_folderList">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>5</verstretch>
</sizepolicy>
</property>
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="animated">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<item row="4" column="0">
<widget class="QWidget" name="selectiveSyncStatus" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
@@ -267,6 +146,130 @@
</layout>
</widget>
</item>
<item row="0" column="0">
<widget class="QWidget" name="accountStatus" native="true">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="SslButton" name="sslButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="connectLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Connected with &lt;server&gt; as &lt;user&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QToolButton" name="_accountToolbox">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<layout class="QHBoxLayout" name="storageGroupBox">
<item>
<widget class="QLabel" name="quotaInfoLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string/>
</property>
<property name="text">
<string>Storage space: ...</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="quotaProgressBar">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>7</height>
</size>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>-1</number>
</property>
<property name="textVisible">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0">
<widget class="OCC::FolderStatusView" name="_folderList">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>5</verstretch>
</sizepolicy>
</property>
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="animated">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="KMessageWidget" name="encryptionMessage" native="true"/>
</item>
</layout>
</widget>
<customwidgets>
@@ -280,6 +283,12 @@
<extends>QTreeView</extends>
<header>folderstatusview.h</header>
</customwidget>
<customwidget>
<class>KMessageWidget</class>
<extends>QWidget</extends>
<header>kmessagewidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>

View File

@@ -103,10 +103,12 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
|| a._status == SyncFileItem::BlacklistedError) {
return QIcon(QLatin1String(":/client/resources/state-error.svg"));
} else if(a._status == SyncFileItem::SoftError
|| a._status == SyncFileItem::FileIgnored
|| a._status == SyncFileItem::Conflict
|| a._status == SyncFileItem::Restoration){
|| 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"));
}

View File

@@ -121,6 +121,11 @@ void ActivityWidget::slotProgressInfo(const QString &folder, const ProgressInfo
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);

View File

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

View File

@@ -24,7 +24,7 @@ extern "C" {
CloudProvidersProviderExporter *_providerExporter;
void on_bus_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data)
void on_name_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data)
{
Q_UNUSED(name);
CloudProviderManager *self;
@@ -34,6 +34,14 @@ void on_bus_acquired (GDBusConnection *connection, const gchar *name, gpointer u
self->registerSignals();
}
void on_name_lost (GDBusConnection *connection, const gchar *name, gpointer user_data)
{
Q_UNUSED(connection);
Q_UNUSED(name);
Q_UNUSED(user_data);
g_clear_object (&_providerExporter);
}
void CloudProviderManager::registerSignals()
{
OCC::FolderMan *folderManager = OCC::FolderMan::instance();
@@ -45,7 +53,7 @@ CloudProviderManager::CloudProviderManager(QObject *parent) : QObject(parent)
{
_map = new QMap<QString, CloudProviderWrapper*>();
QString busName = QString(LIBCLOUDPROVIDERS_DBUS_BUS_NAME);
g_bus_own_name (G_BUS_TYPE_SESSION, busName.toAscii().data(), G_BUS_NAME_OWNER_FLAGS_NONE, on_bus_acquired, nullptr, nullptr, this, nullptr);
g_bus_own_name (G_BUS_TYPE_SESSION, busName.toAscii().data(), G_BUS_NAME_OWNER_FLAGS_NONE, nullptr, on_name_acquired, nullptr, this, nullptr);
}
void CloudProviderManager::slotFolderListChanged(const Folder::Map &folderMap)

View File

@@ -33,33 +33,25 @@ using namespace OCC;
GSimpleActionGroup *actionGroup = nullptr;
static
gchar* qstring_to_gchar(const QString &string)
{
QByteArray ba = string.toUtf8();
char* data = ba.data();
return g_strdup(data);
}
CloudProviderWrapper::CloudProviderWrapper(QObject *parent, Folder *folder, CloudProvidersProviderExporter* cloudprovider) : QObject(parent)
, _folder(folder)
{
GMenuModel *model;
GActionGroup *action_group;
_recentlyChanged = new QList<QPair<QString, QString>>();
gchar *accountName = g_strdup_printf ("Account%sFolder%s",
qstring_to_gchar(folder->alias()),
qstring_to_gchar(folder->accountState()->account()->id()));
QString accountName = QString("Account%1Folder%2").arg(folder->alias(), folder->accountState()->account()->id());
_cloudProvider = CLOUD_PROVIDERS_PROVIDER_EXPORTER(cloudprovider);
_cloudProviderAccount = cloud_providers_account_exporter_new(_cloudProvider, accountName);
_cloudProviderAccount = cloud_providers_account_exporter_new(_cloudProvider, accountName.toUtf8().data());
gchar* folderName = qstring_to_gchar(folder->shortGuiLocalPath());
gchar* folderPath = qstring_to_gchar(folder->cleanPath());
cloud_providers_account_exporter_set_name (_cloudProviderAccount, folderName);
cloud_providers_account_exporter_set_name (_cloudProviderAccount, folder->shortGuiLocalPath().toUtf8().data());
cloud_providers_account_exporter_set_icon (_cloudProviderAccount, g_icon_new_for_string(APPLICATION_ICON_NAME, nullptr));
cloud_providers_account_exporter_set_path (_cloudProviderAccount, folderPath);
cloud_providers_account_exporter_set_path (_cloudProviderAccount, folder->cleanPath().toUtf8().data());
cloud_providers_account_exporter_set_status (_cloudProviderAccount, CLOUD_PROVIDERS_ACCOUNT_STATUS_IDLE);
cloud_providers_account_exporter_set_menu_model (_cloudProviderAccount, getMenuModel());
cloud_providers_account_exporter_set_action_group (_cloudProviderAccount, getActionGroup());
model = getMenuModel();
cloud_providers_account_exporter_set_menu_model (_cloudProviderAccount, model);
action_group = getActionGroup();
cloud_providers_account_exporter_set_action_group (_cloudProviderAccount, action_group);
connect(ProgressDispatcher::instance(), SIGNAL(progressInfo(QString, ProgressInfo)), this, SLOT(slotUpdateProgress(QString, ProgressInfo)));
connect(_folder, SIGNAL(syncStarted()), this, SLOT(slotSyncStarted()));
@@ -68,10 +60,8 @@ CloudProviderWrapper::CloudProviderWrapper(QObject *parent, Folder *folder, Clou
_paused = _folder->syncPaused();
updatePauseStatus();
g_free(accountName);
g_free(folderName);
g_free(folderPath);
g_clear_object (&model);
g_clear_object (&action_group);
}
CloudProviderWrapper::~CloudProviderWrapper()
@@ -93,6 +83,16 @@ static bool shouldShowInRecentsMenu(const SyncFileItem &item)
&& item._instruction != CSYNC_INSTRUCTION_NONE;
}
static GMenuItem *menu_item_new(const QString &label, const gchar *detailed_action)
{
return g_menu_item_new(label.toUtf8 ().data(), detailed_action);
}
static GMenuItem *menu_item_new_submenu(const QString &label, GMenuModel *submenu)
{
return g_menu_item_new_submenu(label.toUtf8 ().data(), submenu);
}
void CloudProviderWrapper::slotUpdateProgress(const QString &folder, const ProgressInfo &progress)
{
// Only update progress for the current folder
@@ -154,30 +154,25 @@ void CloudProviderWrapper::slotUpdateProgress(const QString &folder, const Progr
if(!_recentlyChanged->isEmpty()) {
QList<QPair<QString, QString>>::iterator i;
for (i = _recentlyChanged->begin(); i != _recentlyChanged->end(); i++) {
gchar *file;
QString label = i->first;
QString fullPath = i->second;
file = g_strdup(qstring_to_gchar(label));
item = g_menu_item_new(file, "cloudprovider.showfile");
g_menu_item_set_action_and_target_value(item, "cloudprovider.showfile", g_variant_new_string(qstring_to_gchar(fullPath)));
item = menu_item_new(label, "cloudprovider.showfile");
g_menu_item_set_action_and_target_value(item, "cloudprovider.showfile", g_variant_new_string(fullPath.toUtf8().data()));
g_menu_append_item(_recentMenu, item);
g_clear_object (&item);
}
} else {
item = g_menu_item_new("No recently changed files", nullptr);
item = menu_item_new(tr("No recently changed files"), nullptr);
g_menu_append_item(_recentMenu, item);
g_clear_object (&item);
}
}
}
void CloudProviderWrapper::updateStatusText(QString statusText)
{
char* state = qstring_to_gchar(_folder->accountState()->stateString(_folder->accountState()->state()));
char* statusChar = qstring_to_gchar(statusText);
char* status = g_strdup_printf("%s - %s", state, statusChar);
cloud_providers_account_exporter_set_status_details(_cloudProviderAccount, status);
g_free(state);
g_free(statusChar);
g_free(status);
QString status = QString("%1 - %2").arg(_folder->accountState()->stateString(_folder->accountState()->state()), statusText);
cloud_providers_account_exporter_set_status_details(_cloudProviderAccount, status.toUtf8().data());
}
void CloudProviderWrapper::updatePauseStatus()
@@ -217,38 +212,53 @@ GMenuModel* CloudProviderWrapper::getMenuModel() {
GMenu* section;
GMenuItem* item;
QString item_label;
_mainMenu = g_menu_new();
section = g_menu_new();
item = g_menu_item_new("Open website", "cloudprovider.openwebsite");
item = menu_item_new(tr("Open website"), "cloudprovider.openwebsite");
g_menu_append_item(section, item);
g_clear_object (&item);
g_menu_append_section(_mainMenu, nullptr, G_MENU_MODEL(section));
g_clear_object (&section);
_recentMenu = g_menu_new();
item = g_menu_item_new("No recently changed files", nullptr);
item = menu_item_new(tr("No recently changed files"), nullptr);
g_menu_append_item(_recentMenu, item);
section = g_menu_new();
item = g_menu_item_new_submenu("Recently changed", G_MENU_MODEL(_recentMenu));
g_menu_append_item(section, item);
g_menu_append_section(_mainMenu, nullptr, G_MENU_MODEL(section));
g_clear_object (&item);
section = g_menu_new();
item = g_menu_item_new("Pause synchronization", "cloudprovider.pause");
item = menu_item_new_submenu(tr("Recently changed"), G_MENU_MODEL(_recentMenu));
g_menu_append_item(section, item);
g_clear_object (&item);
g_menu_append_section(_mainMenu, nullptr, G_MENU_MODEL(section));
g_clear_object (&section);
section = g_menu_new();
item = g_menu_item_new("Help", "cloudprovider.openhelp");
g_menu_append_item(section, item);
item = g_menu_item_new("Settings", "cloudprovider.opensettings");
g_menu_append_item(section, item);
item = g_menu_item_new("Log out", "cloudprovider.logout");
g_menu_append_item(section, item);
item = g_menu_item_new("Quit sync client", "cloudprovider.quit");
item = menu_item_new(tr("Pause synchronization"), "cloudprovider.pause");
g_menu_append_item(section, item);
g_clear_object (&item);
g_menu_append_section(_mainMenu, nullptr, G_MENU_MODEL(section));
g_clear_object (&section);
section = g_menu_new();
item = menu_item_new(tr("Help"), "cloudprovider.openhelp");
g_menu_append_item(section, item);
g_clear_object (&item);
item = menu_item_new(tr("Settings"), "cloudprovider.opensettings");
g_menu_append_item(section, item);
g_clear_object (&item);
item = menu_item_new(tr("Log out"), "cloudprovider.logout");
g_menu_append_item(section, item);
g_clear_object (&item);
item = menu_item_new(tr("Quit sync client"), "cloudprovider.quit");
g_menu_append_item(section, item);
g_clear_object (&item);
g_menu_append_section(_mainMenu, nullptr, G_MENU_MODEL(section));
g_clear_object (&section);
g_clear_object (&_recentMenu);
return G_MENU_MODEL(_mainMenu);
}
@@ -277,11 +287,10 @@ activate_action_open (GSimpleAction *action, GVariant *parameter, gpointer user_
}
if(g_str_equal(name, "showfile")) {
gchar *path;
g_variant_get (parameter, "s", &path);
g_print("showfile => %s\n", path);
showInFileManager(QString(path));
}
const gchar *path = g_variant_get_string (parameter, NULL);
g_print("showfile => %s\n", path);
showInFileManager(QString(path));
}
if(g_str_equal(name, "logout")) {
self->folder()->accountState()->signOutByUi();
@@ -331,12 +340,13 @@ static GActionEntry actions[] = {
GActionGroup* CloudProviderWrapper::getActionGroup()
{
g_clear_object (&actionGroup);
actionGroup = g_simple_action_group_new ();
g_action_map_add_action_entries (G_ACTION_MAP (actionGroup), actions, G_N_ELEMENTS (actions), this);
bool state = _folder->syncPaused();
GAction *pause = g_action_map_lookup_action(G_ACTION_MAP(actionGroup), "pause");
g_simple_action_set_state(G_SIMPLE_ACTION(pause), g_variant_new_boolean(state));
return G_ACTION_GROUP (actionGroup);
return G_ACTION_GROUP (g_object_ref (actionGroup));
}
void CloudProviderWrapper::slotSyncPausedChanged(Folder *folder, bool state)

176
src/gui/creds/flow2auth.cpp Normal file
View File

@@ -0,0 +1,176 @@
/*
* Copyright (C) by Olivier Goffart <ogoffart@woboq.com>
* Copyright (C) by Michael Schuster <michael@nextcloud.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include <QDesktopServices>
#include <QTimer>
#include <QBuffer>
#include "account.h"
#include "creds/flow2auth.h"
#include <QJsonObject>
#include <QJsonDocument>
#include "theme.h"
#include "networkjobs.h"
#include "configfile.h"
namespace OCC {
Q_LOGGING_CATEGORY(lcFlow2auth, "nextcloud.sync.credentials.flow2auth", QtInfoMsg)
Flow2Auth::~Flow2Auth()
{
}
void Flow2Auth::start()
{
// Note: All startup code is in openBrowser() to allow reinitiate a new request with
// fresh tokens. Opening the same pollEndpoint link twice triggers an expiration
// message by the server (security, intended design).
openBrowser();
}
QUrl Flow2Auth::authorisationLink() const
{
return _loginUrl;
}
void Flow2Auth::openBrowser()
{
_pollTimer.stop();
// Step 1: Initiate a login, do an anonymous POST request
QUrl url = Utility::concatUrlPath(_account->url().toString(), QLatin1String("/index.php/login/v2"));
auto job = _account->sendRequest("POST", url);
job->setTimeout(qMin(30 * 1000ll, job->timeoutMsec()));
QObject::connect(job, &SimpleNetworkJob::finishedSignal, this, [this](QNetworkReply *reply) {
auto jsonData = reply->readAll();
QJsonParseError jsonParseError;
QJsonObject json = QJsonDocument::fromJson(jsonData, &jsonParseError).object();
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()) {
QString errorReason;
QString errorFromJson = json["error"].toString();
if (!errorFromJson.isEmpty()) {
errorReason = tr("Error returned from the server: <em>%1</em>")
.arg(errorFromJson.toHtmlEscaped());
} else if (reply->error() != QNetworkReply::NoError) {
errorReason = tr("There was an error accessing the 'token' endpoint: <br><em>%1</em>")
.arg(reply->errorString().toHtmlEscaped());
} else if (jsonParseError.error != QJsonParseError::NoError) {
errorReason = tr("Could not parse the JSON returned from the server: <br><em>%1</em>")
.arg(jsonParseError.errorString());
} else {
errorReason = tr("The reply from the server did not contain all expected fields");
}
qCWarning(lcFlow2auth) << "Error when getting the loginUrl" << json << errorReason;
emit result(Error);
return;
}
_loginUrl = loginUrl;
_pollToken = pollToken;
_pollEndpoint = pollEndpoint;
// Start polling
ConfigFile cfg;
std::chrono::milliseconds polltime = cfg.remotePollInterval();
qCInfo(lcFlow2auth) << "setting remote poll timer interval to" << polltime.count() << "msec";
_pollTimer.setInterval(polltime.count());
QObject::connect(&_pollTimer, &QTimer::timeout, this, &Flow2Auth::slotPollTimerTimeout);
_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());
}
});
}
void Flow2Auth::slotPollTimerTimeout()
{
_pollTimer.stop();
// Step 2: Poll
QNetworkRequest req;
req.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
auto requestBody = new QBuffer;
QUrlQuery arguments(QString("token=%1").arg(_pollToken));
requestBody->setData(arguments.query(QUrl::FullyEncoded).toLatin1());
auto job = _account->sendRequest("POST", _pollEndpoint, req, requestBody);
job->setTimeout(qMin(30 * 1000ll, job->timeoutMsec()));
QObject::connect(job, &SimpleNetworkJob::finishedSignal, this, [this](QNetworkReply *reply) {
auto jsonData = reply->readAll();
QJsonParseError jsonParseError;
QJsonObject json = QJsonDocument::fromJson(jsonData, &jsonParseError).object();
QUrl serverUrl = 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()) {
QString errorReason;
QString errorFromJson = json["error"].toString();
if (!errorFromJson.isEmpty()) {
errorReason = tr("Error returned from the server: <em>%1</em>")
.arg(errorFromJson.toHtmlEscaped());
} else if (reply->error() != QNetworkReply::NoError) {
errorReason = tr("There was an error accessing the 'token' endpoint: <br><em>%1</em>")
.arg(reply->errorString().toHtmlEscaped());
} else if (jsonParseError.error != QJsonParseError::NoError) {
errorReason = tr("Could not parse the JSON returned from the server: <br><em>%1</em>")
.arg(jsonParseError.errorString());
} else {
errorReason = tr("The reply from the server did not contain all expected fields");
}
qCDebug(lcFlow2auth) << "Error when polling for the appPassword" << json << errorReason;
// Forget sensitive data
appPassword.clear();
loginName.clear();
// Failed: poll again
_pollTimer.start();
return;
}
// Success
qCInfo(lcFlow2auth) << "Success getting the appPassword for user: " << loginName << ", server: " << serverUrl.toString();
_account->setUrl(serverUrl);
emit result(LoggedIn, loginName, appPassword);
// Forget sensitive data
appPassword.clear();
loginName.clear();
});
}
} // namespace OCC

68
src/gui/creds/flow2auth.h Normal file
View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) by Olivier Goffart <ogoffart@woboq.com>
* Copyright (C) by Michael Schuster <michael@nextcloud.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#pragma once
#include <QPointer>
#include <QUrl>
#include <QTimer>
#include "accountfwd.h"
namespace OCC {
/**
* Job that does the authorization, grants and fetches the access token via Login Flow v2
*
* See: https://docs.nextcloud.com/server/latest/developer_manual/client_apis/LoginFlow/index.html#login-flow-v2
*
*/
class Flow2Auth : public QObject
{
Q_OBJECT
public:
Flow2Auth(Account *account, QObject *parent)
: QObject(parent)
, _account(account)
{
}
~Flow2Auth();
enum Result { NotSupported,
LoggedIn,
Error };
Q_ENUM(Result);
void start();
void openBrowser();
QUrl authorisationLink() const;
signals:
/**
* The state has changed.
* when logged in, appPassword has the value of the app password.
*/
void result(Flow2Auth::Result result, const QString &user = QString(), const QString &appPassword = QString());
private slots:
void slotPollTimerTimeout();
private:
Account *_account;
QUrl _loginUrl;
QString _pollToken;
QString _pollEndpoint;
QTimer _pollTimer;
};
} // namespace OCC

View File

@@ -148,7 +148,7 @@ QString HttpCredentialsGui::requestAppPasswordText(const Account *account)
} else if (version >= Account::makeServerVersion(12, 0, 0)) {
url += QLatin1String("/index.php/settings/personal#security");
} else if (version >= Account::makeServerVersion(11, 0, 0)) {
url += QLatin1String("/index.php/settings/personal#apppasswords");
url += QLatin1String("/index.php/settings/user/security#security");
} else {
return QString();
}

View File

@@ -90,7 +90,7 @@ ShibbolethWebView::ShibbolethWebView(AccountPtr account, QWidget *parent)
QWebView *debugView = new QWebView(this);
debugView->setPage(debugPage);
QMainWindow *window = new QMainWindow(this);
window->setWindowTitle(tr("SSL Chipher Debug View"));
window->setWindowTitle(tr("SSL Cipher Debug View"));
window->setCentralWidget(debugView);
window->show();
}

View File

@@ -14,6 +14,7 @@
#include "accessmanager.h"
#include "account.h"
#include "configfile.h"
#include "theme.h"
#include "wizard/webview.h"
#include "webflowcredentialsdialog.h"
@@ -24,6 +25,13 @@ namespace OCC {
Q_LOGGING_CATEGORY(lcWebFlowCredentials, "sync.credentials.webflow", QtInfoMsg)
namespace {
const char userC[] = "user";
const char clientCertificatePEMC[] = "_clientCertificatePEM";
const char clientKeyPEMC[] = "_clientKeyPEM";
const char clientCaCertificatePEMC[] = "_clientCaCertificatePEM";
} // ns
class WebFlowCredentialsAccessManager : public AccessManager
{
public:
@@ -37,13 +45,27 @@ protected:
QNetworkReply *createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData) override
{
QNetworkRequest req(request);
if (!req.attribute(HttpCredentials::DontAddCredentialsAttribute).toBool()) {
if (!req.attribute(WebFlowCredentials::DontAddCredentialsAttribute).toBool()) {
if (_cred && !_cred->password().isEmpty()) {
QByteArray credHash = QByteArray(_cred->user().toUtf8() + ":" + _cred->password().toUtf8()).toBase64();
req.setRawHeader("Authorization", "Basic " + credHash);
}
}
if (_cred && !_cred->_clientSslKey.isNull() && !_cred->_clientSslCertificate.isNull()) {
// SSL configuration
QSslConfiguration sslConfiguration = req.sslConfiguration();
sslConfiguration.setLocalCertificate(_cred->_clientSslCertificate);
sslConfiguration.setPrivateKey(_cred->_clientSslKey);
// Merge client side CA with system CA
auto ca = sslConfiguration.systemCaCertificates();
ca.append(_cred->_clientSslCaCertificates);
sslConfiguration.setCaCertificates(ca);
req.setSslConfiguration(sslConfiguration);
}
return AccessManager::createRequest(op, req, outgoingData);
}
@@ -53,22 +75,33 @@ private:
QPointer<const WebFlowCredentials> _cred;
};
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());
}
WebFlowCredentials::WebFlowCredentials()
: _ready(false)
, _credentialsValid(false)
, _keychainMigration(false)
, _retryOnKeyChainError(false)
{
}
WebFlowCredentials::WebFlowCredentials(const QString &user, const QString &password, const QSslCertificate &certificate, const QSslKey &key)
WebFlowCredentials::WebFlowCredentials(const QString &user, const QString &password, const QSslCertificate &certificate, const QSslKey &key, const QList<QSslCertificate> &caCertificates)
: _user(user)
, _password(password)
, _clientSslKey(key)
, _clientSslCertificate(certificate)
, _clientSslCaCertificates(caCertificates)
, _ready(true)
, _credentialsValid(true)
, _keychainMigration(false)
, _retryOnKeyChainError(false)
{
}
@@ -102,24 +135,29 @@ bool WebFlowCredentials::ready() const {
void WebFlowCredentials::fetchFromKeychain() {
_wasFetched = true;
// Make sure we get the user fromt he config file
// Make sure we get the user from the config file
fetchUser();
if (ready()) {
emit fetched();
} else {
qCInfo(lcWebFlowCredentials()) << "Fetch from keyhchain!";
qCInfo(lcWebFlowCredentials()) << "Fetch from keychain!";
fetchFromKeychainHelper();
}
}
void WebFlowCredentials::askFromUser() {
_askDialog = new WebFlowCredentialsDialog();
// LoginFlowV2 > WebViewFlow > OAuth > Shib > Basic
bool useFlow2 = (_account->serverVersionInt() >= Account::makeServerVersion(16, 0, 0));
QUrl url = _account->url();
QString path = url.path() + "/index.php/login/flow";
url.setPath(path);
_askDialog->setUrl(url);
_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);
}
QString msg = tr("You have been logged out of %1 as user %2. Please login again")
.arg(_account->displayName(), _user);
@@ -129,11 +167,11 @@ void WebFlowCredentials::askFromUser() {
connect(_askDialog, &WebFlowCredentialsDialog::urlCatched, this, &WebFlowCredentials::slotAskFromUserCredentialsProvided);
qCWarning(lcWebFlowCredentials()) << "User needs to reauth!";
qCDebug(lcWebFlowCredentials()) << "User needs to reauth!";
}
void WebFlowCredentials::slotAskFromUserCredentialsProvided(const QString &user, const QString &pass, const QString &host) {
Q_UNUSED(host);
Q_UNUSED(host)
if (_user != user) {
qCInfo(lcWebFlowCredentials()) << "Authed with the wrong user!";
@@ -142,10 +180,12 @@ void WebFlowCredentials::slotAskFromUserCredentialsProvided(const QString &user,
.arg(_user);
_askDialog->setError(msg);
QUrl url = _account->url();
QString path = url.path() + "/index.php/login/flow";
url.setPath(path);
_askDialog->setUrl(url);
if (!_askDialog->isUsingFlow2()) {
QUrl url = _account->url();
QString path = url.path() + "/index.php/login/flow";
url.setPath(path);
_askDialog->setUrl(url);
}
return;
}
@@ -165,9 +205,10 @@ void WebFlowCredentials::slotAskFromUserCredentialsProvided(const QString &user,
bool WebFlowCredentials::stillValid(QNetworkReply *reply) {
qCWarning(lcWebFlowCredentials()) << "Still valid?";
qCWarning(lcWebFlowCredentials()) << reply->error();
qCWarning(lcWebFlowCredentials()) << reply->errorString();
if (reply->error() != QNetworkReply::NoError) {
qCWarning(lcWebFlowCredentials()) << reply->error();
qCWarning(lcWebFlowCredentials()) << reply->errorString();
}
return (reply->error() != QNetworkReply::AuthenticationRequiredError);
}
@@ -177,17 +218,128 @@ void WebFlowCredentials::persist() {
return;
}
_account->setCredentialSetting("user", _user);
_account->setCredentialSetting(userC, _user);
_account->wantsAccountSaved(_account);
//TODO: Add ssl cert and key storing
// write cert if there is one
if (!_clientSslCertificate.isNull()) {
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();
}
}
void WebFlowCredentials::slotWriteClientCertPEMJobDone()
{
// write ssl key if there is one
if (!_clientSslKey.isNull()) {
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();
}
}
void WebFlowCredentials::writeSingleClientCaCertPEM()
{
// write a ca cert if there is any in the queue
if (!_clientSslCaCertificatesWriteQueue.isEmpty()) {
// grab and remove the first cert from the queue
auto cert = _clientSslCaCertificatesWriteQueue.dequeue();
auto index = (_clientSslCaCertificates.count() - _clientSslCaCertificatesWriteQueue.count()) - 1;
// keep the limit
if (index > (_clientSslCaCertificatesMaxCount - 1)) {
qCWarning(lcWebFlowCredentials) << "Maximum client CA cert count exceeded while writing slot" << QString::number(index) << "), cutting off after " << QString::number(_clientSslCaCertificatesMaxCount) << "certs";
_clientSslCaCertificatesWriteQueue.clear();
slotWriteClientCaCertsPEMJobDone(nullptr);
return;
}
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()
{
_clientSslCaCertificatesWriteQueue.clear();
// write ca certs if there are any
if (!_clientSslCaCertificates.isEmpty()) {
// queue the certs to avoid trouble on Windows (Workaround for CredWriteW used by QtKeychain)
_clientSslCaCertificatesWriteQueue.append(_clientSslCaCertificates);
// first ca cert
writeSingleClientCaCertPEM();
} else {
slotWriteClientCaCertsPEMJobDone(nullptr);
}
}
void WebFlowCredentials::slotWriteClientCaCertsPEMJobDone(QKeychain::Job *incomingJob)
{
// errors / next ca cert?
if (incomingJob && !_clientSslCaCertificates.isEmpty()) {
WritePasswordJob *writeJob = static_cast<WritePasswordJob *>(incomingJob);
if (writeJob->error() != NoError) {
qCWarning(lcWebFlowCredentials) << "Error while writing client CA cert" << writeJob->errorString();
}
if (!_clientSslCaCertificatesWriteQueue.isEmpty()) {
// next ca cert
writeSingleClientCaCertPEM();
return;
}
}
// done storing ca certs, time for the password
WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
addSettingsToJob(_account, job);
job->setInsecureFallback(false);
connect(job, &Job::finished, this, &WebFlowCredentials::slotWriteJobDone);
job->setKey(keychainKey(_account->url().toString(), _user, _account->id()));
job->setTextData(_password);
job->start();
}
void WebFlowCredentials::slotWriteJobDone(QKeychain::Job *job)
{
delete job->settings();
switch (job->error()) {
case NoError:
break;
default:
qCWarning(lcWebFlowCredentials) << "Error while writing password" << job->errorString();
}
WritePasswordJob *wjob = qobject_cast<WritePasswordJob *>(job);
wjob->deleteLater();
}
void WebFlowCredentials::invalidateToken() {
// clear the session cookie.
_account->clearCookieJar();
@@ -200,7 +352,7 @@ void WebFlowCredentials::invalidateToken() {
QTimer::singleShot(0, _account, &Account::clearQNAMCache);
}
void WebFlowCredentials::forgetSensitiveData(){
void WebFlowCredentials::forgetSensitiveData() {
_password = QString();
_ready = false;
@@ -208,7 +360,7 @@ void WebFlowCredentials::forgetSensitiveData(){
const QString kck = keychainKey(_account->url().toString(), _user, _account->id());
if (kck.isEmpty()) {
qCWarning(lcWebFlowCredentials()) << "InvalidateToken: User is empty, bailing out!";
qCDebug(lcWebFlowCredentials()) << "InvalidateToken: User is empty, bailing out!";
return;
}
@@ -218,6 +370,15 @@ void WebFlowCredentials::forgetSensitiveData(){
job->start();
invalidateToken();
/* 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) {
@@ -228,12 +389,12 @@ void WebFlowCredentials::setAccount(Account *account) {
}
QString WebFlowCredentials::fetchUser() {
_user = _account->credentialSetting("user").toString();
_user = _account->credentialSetting(userC).toString();
return _user;
}
void WebFlowCredentials::slotAuthentication(QNetworkReply *reply, QAuthenticator *authenticator) {
Q_UNUSED(reply);
Q_UNUSED(reply)
if (!_ready) {
return;
@@ -243,7 +404,7 @@ void WebFlowCredentials::slotAuthentication(QNetworkReply *reply, QAuthenticator
return;
}
qCWarning(lcWebFlowCredentials()) << "Requires authentication";
qCDebug(lcWebFlowCredentials()) << "Requires authentication";
authenticator->setUser(_user);
authenticator->setPassword(_password);
@@ -259,12 +420,139 @@ void WebFlowCredentials::slotFinished(QNetworkReply *reply) {
}
void WebFlowCredentials::fetchFromKeychainHelper() {
// Read client cert from keychain
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(QKeychain::Job *incomingJob)
{
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
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." << incomingJob->errorString();
QTimer::singleShot(10000, this, &WebFlowCredentials::fetchFromKeychainHelper);
_retryOnKeyChainError = false;
return;
}
_retryOnKeyChainError = false;
#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) {
_clientSslCertificate = sslCertificateList.at(0);
}
}
// Load key too
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(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
// load whatever we have. So we try until it works.
_clientSslKey = QSslKey(clientKeyPEM, QSsl::Rsa);
if (_clientSslKey.isNull()) {
_clientSslKey = QSslKey(clientKeyPEM, QSsl::Dsa);
}
if (_clientSslKey.isNull()) {
_clientSslKey = QSslKey(clientKeyPEM, QSsl::Ec);
}
if (_clientSslKey.isNull()) {
qCWarning(lcWebFlowCredentials) << "Could not load SSL key into Qt!";
}
}
// Start fetching client CA certs
_clientSslCaCertificates.clear();
readSingleClientCaCertPEM();
}
void WebFlowCredentials::readSingleClientCaCertPEM()
{
// try to fetch a client ca cert
if (_clientSslCaCertificates.count() < _clientSslCaCertificatesMaxCount) {
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;
slotReadClientCaCertsPEMJobDone(nullptr);
}
}
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);
if (sslCertificateList.length() >= 1) {
_clientSslCaCertificates.append(sslCertificateList.at(0));
}
// 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();
}
}
}
// Now fetch the actual server password
const QString kck = keychainKey(
_account->url().toString(),
_user,
_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::slotReadPasswordJobDone);
@@ -282,6 +570,10 @@ void WebFlowCredentials::slotReadPasswordJobDone(Job *incomingJob) {
return;
}
if (_user.isEmpty()) {
qCWarning(lcWebFlowCredentials) << "Strange: User is empty!";
}
if (error == QKeychain::NoError) {
_password = job->textData();
_ready = true;
@@ -295,16 +587,29 @@ void WebFlowCredentials::slotReadPasswordJobDone(Job *incomingJob) {
if (_keychainMigration && _ready) {
_keychainMigration = false;
persist();
deleteOldKeychainEntries();
qCWarning(lcWebFlowCredentials) << "Migrated old keychain entries";
deleteKeychainEntries(true); // true: delete old entries
qCInfo(lcWebFlowCredentials) << "Migrated old keychain entries";
}
}
void WebFlowCredentials::deleteOldKeychainEntries() {
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
job->setInsecureFallback(false);
job->setKey(keychainKey(_account->url().toString(), _user, QString()));
job->start();
void WebFlowCredentials::deleteKeychainEntries(bool oldKeychainEntries) {
auto startDeleteJob = [this, oldKeychainEntries](QString user) {
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
addSettingsToJob(_account, job);
job->setInsecureFallback(true);
job->setKey(keychainKey(_account->url().toString(),
user,
oldKeychainEntries ? QString() : _account->id()));
job->start();
};
startDeleteJob(_user);
startDeleteJob(_user + clientKeyPEMC);
startDeleteJob(_user + clientCertificatePEMC);
for (auto i = 0; i < _clientSslCaCertificates.count(); i++) {
startDeleteJob(_user + clientCaCertificatePEMC + QString::number(i));
}
}
}

View File

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

View File

@@ -3,13 +3,21 @@
#include <QVBoxLayout>
#include <QLabel>
#include "theme.h"
#include "wizard/owncloudwizardcommon.h"
#include "wizard/webview.h"
#include "wizard/flow2authwidget.h"
namespace OCC {
WebFlowCredentialsDialog::WebFlowCredentialsDialog(QWidget *parent)
: QDialog(parent)
WebFlowCredentialsDialog::WebFlowCredentialsDialog(Account *account, bool useFlow2, QWidget *parent)
: QDialog(parent),
_useFlow2(useFlow2),
_flow2AuthWidget(nullptr),
_webView(nullptr)
{
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
_layout = new QVBoxLayout(this);
//QString msg = tr("You have been logged out of %1 as user %2, please login again")
@@ -17,28 +25,43 @@ WebFlowCredentialsDialog::WebFlowCredentialsDialog(QWidget *parent)
_infoLabel = new QLabel();
_layout->addWidget(_infoLabel);
_webView = new WebView();
_layout->addWidget(_webView);
if (_useFlow2) {
_flow2AuthWidget = new Flow2AuthWidget(account);
_layout->addWidget(_flow2AuthWidget);
connect(_flow2AuthWidget, &Flow2AuthWidget::urlCatched, this, &WebFlowCredentialsDialog::urlCatched);
} else {
_webView = new WebView();
_layout->addWidget(_webView);
connect(_webView, &WebView::urlCatched, this, &WebFlowCredentialsDialog::urlCatched);
}
_errorLabel = new QLabel();
_errorLabel->hide();
_layout->addWidget(_errorLabel);
setLayout(_layout);
WizardCommon::initErrorLabel(_errorLabel);
connect(_webView, &WebView::urlCatched, this, &WebFlowCredentialsDialog::urlCatched);
setLayout(_layout);
}
void WebFlowCredentialsDialog::closeEvent(QCloseEvent* e) {
Q_UNUSED(e);
Q_UNUSED(e)
// Force calling WebView::~WebView() earlier so that _profile and _page are
// deleted in the correct order.
delete _webView;
if (_webView) {
// Force calling WebView::~WebView() earlier so that _profile and _page are
// deleted in the correct order.
delete _webView;
}
if (_flow2AuthWidget)
delete _flow2AuthWidget;
}
void WebFlowCredentialsDialog::setUrl(const QUrl &url) {
_webView->setUrl(url);
if (_webView)
_webView->setUrl(url);
}
void WebFlowCredentialsDialog::setInfo(const QString &msg) {
@@ -46,6 +69,11 @@ void WebFlowCredentialsDialog::setInfo(const QString &msg) {
}
void WebFlowCredentialsDialog::setError(const QString &error) {
if (_useFlow2 && _flow2AuthWidget) {
_flow2AuthWidget->setError(error);
return;
}
if (error.isEmpty()) {
_errorLabel->hide();
} else {

View File

@@ -4,23 +4,30 @@
#include <QDialog>
#include <QUrl>
#include "accountfwd.h"
class QLabel;
class QVBoxLayout;
namespace OCC {
class WebView;
class Flow2AuthWidget;
class WebFlowCredentialsDialog : public QDialog
{
Q_OBJECT
public:
WebFlowCredentialsDialog(QWidget *parent = nullptr);
WebFlowCredentialsDialog(Account *account, bool useFlow2, QWidget *parent = nullptr);
void setUrl(const QUrl &url);
void setInfo(const QString &msg);
void setError(const QString &error);
bool isUsingFlow2() const {
return _useFlow2;
}
protected:
void closeEvent(QCloseEvent * e) override;
@@ -28,7 +35,11 @@ signals:
void urlCatched(const QString user, const QString pass, const QString host);
private:
bool _useFlow2;
Flow2AuthWidget *_flow2AuthWidget;
WebView *_webView;
QLabel *_errorLabel;
QLabel *_infoLabel;
QVBoxLayout *_layout;

View File

@@ -351,6 +351,10 @@ void Folder::showSyncResultPopup()
createGuiLog(_syncResult.firstItemError()->_file, LogStatusError, errorCount);
}
if (int lockedCount = _syncResult.numLockedItems()) {
createGuiLog(_syncResult.firstItemLocked()->_file, LogStatusFileLocked, lockedCount);
}
qCInfo(lcFolder) << "Folder sync result: " << int(_syncResult.status());
}
@@ -1071,17 +1075,17 @@ void Folder::slotAboutToRemoveAllFiles(SyncFileItem::Direction dir, bool *cancel
QString msg = dir == SyncFileItem::Down ? tr("All files in the sync folder '%1' folder were deleted on the server.\n"
"These deletes will be synchronized to your local sync folder, making such files "
"unavailable unless you have a right to restore. \n"
"If you decide to keep the files, they will be re-synced with the server if you have rights to do so.\n"
"If you decide to restore the files, they will be re-synced with the server if you have rights to do so.\n"
"If you decide to delete the files, they will be unavailable to you, unless you are the owner.")
: tr("All the files in your local sync folder '%1' were deleted. These deletes will be "
"synchronized with your server, making such files unavailable unless restored.\n"
"Are you sure you want to sync those actions with the server?\n"
"If this was an accident and you decide to keep your files, they will be re-synced from the server.");
QMessageBox msgBox(QMessageBox::Warning, tr("Download new files?"),
: tr("All files got deleted from your local sync folder '%1'.\n"
"These files will be deleted from the server and will not be available on your other devices if they "
"will not be restored.\n"
"If this action was unintended you can restore the lost data now.");
QMessageBox msgBox(QMessageBox::Warning, tr("Delete all files?"),
msg.arg(shortGuiLocalPath()));
msgBox.setWindowFlags(msgBox.windowFlags() | Qt::WindowStaysOnTopHint);
msgBox.addButton(tr("Download new files"), QMessageBox::DestructiveRole);
QPushButton *keepBtn = msgBox.addButton(tr("Keep local files"), QMessageBox::AcceptRole);
msgBox.addButton(tr("Delete all files"), QMessageBox::DestructiveRole);
QPushButton *keepBtn = msgBox.addButton(tr("Restore deleted files"), QMessageBox::AcceptRole);
if (msgBox.exec() == -1) {
*cancel = true;
return;

View File

@@ -344,7 +344,8 @@ private:
LogStatusNew,
LogStatusError,
LogStatusConflict,
LogStatusUpdated
LogStatusUpdated,
LogStatusFileLocked
};
void createGuiLog(const QString &filename, LogStatus status, int count,

View File

@@ -75,9 +75,27 @@ bool FolderWatcher::isReliable() const
return _isReliable;
}
void FolderWatcher::appendSubPaths(QDir dir, QStringList& subPaths) {
QStringList newSubPaths = dir.entryList(QDir::NoDotAndDotDot | QDir::Dirs | QDir::Files);
for (int i = 0; i < newSubPaths.size(); i++) {
QString path = dir.path() + "/" + newSubPaths[i];
QFileInfo fileInfo(path);
subPaths.append(path);
if (fileInfo.isDir()) {
QDir dir(path);
appendSubPaths(dir, subPaths);
}
}
}
void FolderWatcher::changeDetected(const QString &path)
{
QFileInfo fileInfo(path);
QStringList paths(path);
if (fileInfo.isDir()) {
QDir dir(path);
appendSubPaths(dir, paths);
}
changeDetected(paths);
}

View File

@@ -26,6 +26,7 @@
#include <QHash>
#include <QScopedPointer>
#include <QSet>
#include <QDir>
class QTimer;
@@ -120,6 +121,8 @@ private:
Folder *_folder;
bool _isReliable = true;
void appendSubPaths(QDir dir, QStringList& subPaths);
friend class FolderWatcherPrivate;
};
}

View File

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

View File

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

View File

@@ -184,6 +184,7 @@ void GeneralSettings::slotShowInExplorerNavigationPane(bool checked)
void GeneralSettings::slotIgnoreFilesEditor()
{
if (_ignoreEditor.isNull()) {
ConfigFile cfgFile;
_ignoreEditor = new IgnoreListEditor(this);
_ignoreEditor->setAttribute(Qt::WA_DeleteOnClose, true);
_ignoreEditor->open();

View File

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

View File

@@ -14,8 +14,9 @@
#include "configfile.h"
#include "ignorelisteditor.h"
#include "folderman.h"
#include "generalsettings.h"
#include "ignorelisteditor.h"
#include "ui_ignorelisteditor.h"
#include <QFile>
@@ -27,10 +28,6 @@
namespace OCC {
static int patternCol = 0;
static int deletableCol = 1;
static int readOnlyRows = 3;
IgnoreListEditor::IgnoreListEditor(QWidget *parent)
: QDialog(parent)
, ui(new Ui::IgnoreListEditor)
@@ -39,28 +36,28 @@ IgnoreListEditor::IgnoreListEditor(QWidget *parent)
ui->setupUi(this);
ConfigFile cfgFile;
ui->descriptionLabel->setText(tr("Files or folders matching a pattern will not be synchronized.\n\n"
"Items where deletion is allowed will be deleted if they prevent a "
"directory from being removed. "
"This is useful for meta data."));
//FIXME This is not true. The entries are hardcoded below in setupTableReadOnlyItems
readOnlyTooltip = tr("This entry is provided by the system at '%1' "
"and cannot be modified in this view.")
.arg(QDir::toNativeSeparators(cfgFile.excludeFile(ConfigFile::SystemScope)));
setupTableReadOnlyItems();
readIgnoreFile(cfgFile.excludeFile(ConfigFile::UserScope), false);
const auto userConfig = cfgFile.excludeFile(ConfigFile::Scope::UserScope);
ui->ignoreTableWidget->readIgnoreFile(userConfig);
connect(this, &QDialog::accepted, this, &IgnoreListEditor::slotUpdateLocalIgnoreList);
ui->removePushButton->setEnabled(false);
connect(ui->tableWidget, &QTableWidget::itemSelectionChanged, this, &IgnoreListEditor::slotItemSelectionChanged);
connect(ui->removePushButton, &QAbstractButton::clicked, this, &IgnoreListEditor::slotRemoveCurrentItem);
connect(ui->addPushButton, &QAbstractButton::clicked, this, &IgnoreListEditor::slotAddPattern);
connect(ui->removeAllPushButton, &QAbstractButton::clicked, this, &IgnoreListEditor::slotRemoveAllItems);
connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &IgnoreListEditor::slotRestoreDefaults);
connect(this, &QDialog::accepted, [=]() {
ui->ignoreTableWidget->slotWriteIgnoreFile(userConfig);
/* handle the hidden file checkbox */
ui->tableWidget->resizeColumnsToContents();
ui->tableWidget->horizontalHeader()->setSectionResizeMode(patternCol, QHeaderView::Stretch);
ui->tableWidget->verticalHeader()->setVisible(false);
/* the ignoreHiddenFiles flag is a folder specific setting, but for now, it is
* handled globally. Save it to every folder that is defined.
* TODO this can now be fixed, simply attach this IgnoreListEditor to top-level account
* settings
*/
FolderMan::instance()->setIgnoreHiddenFiles(ignoreHiddenFiles());
});
connect(ui->buttonBox, &QDialogButtonBox::clicked,
this, &IgnoreListEditor::slotRestoreDefaults);
ui->syncHiddenFilesCheckBox->setChecked(!FolderMan::instance()->ignoreHiddenFiles());
}
@@ -70,12 +67,11 @@ IgnoreListEditor::~IgnoreListEditor()
delete ui;
}
void IgnoreListEditor::setupTableReadOnlyItems(){
ui->tableWidget->setRowCount(0);
addPattern(".csync_journal.db*", /*deletable=*/false, /*readonly=*/true);
addPattern("._sync_*.db*", /*deletable=*/false, /*readonly=*/true);
addPattern(".sync_*.db*", /*deletable=*/false, /*readonly=*/true);
ui->removeAllPushButton->setEnabled(false);
void IgnoreListEditor::setupTableReadOnlyItems()
{
ui->ignoreTableWidget->addPattern(".csync_journal.db*", /*deletable=*/false, /*readonly=*/true);
ui->ignoreTableWidget->addPattern("._sync_*.db*", /*deletable=*/false, /*readonly=*/true);
ui->ignoreTableWidget->addPattern(".sync_*.db*", /*deletable=*/false, /*readonly=*/true);
}
bool IgnoreListEditor::ignoreHiddenFiles()
@@ -83,140 +79,16 @@ bool IgnoreListEditor::ignoreHiddenFiles()
return !ui->syncHiddenFilesCheckBox->isChecked();
}
void IgnoreListEditor::slotItemSelectionChanged()
void IgnoreListEditor::slotRestoreDefaults(QAbstractButton *button)
{
QTableWidgetItem *item = ui->tableWidget->currentItem();
if (!item) {
ui->removePushButton->setEnabled(false);
if(ui->buttonBox->buttonRole(button) != QDialogButtonBox::ResetRole)
return;
}
bool enable = item->flags() & Qt::ItemIsEnabled;
ui->removePushButton->setEnabled(enable);
}
ui->ignoreTableWidget->slotRemoveAllItems();
void IgnoreListEditor::slotRemoveCurrentItem()
{
ui->tableWidget->removeRow(ui->tableWidget->currentRow());
if(ui->tableWidget->rowCount() == readOnlyRows)
ui->removeAllPushButton->setEnabled(false);
}
void IgnoreListEditor::slotRemoveAllItems()
{
ui->tableWidget->clearContents();
setupTableReadOnlyItems();
}
void IgnoreListEditor::slotUpdateLocalIgnoreList()
{
ConfigFile cfgFile;
QString ignoreFile = cfgFile.excludeFile(ConfigFile::UserScope);
QFile ignores(ignoreFile);
if (ignores.open(QIODevice::WriteOnly)) {
// rewrites the whole file since now the user can also remove system patterns
QFile::resize(ignoreFile, 0);
for (int row = 0; row < ui->tableWidget->rowCount(); ++row) {
QTableWidgetItem *patternItem = ui->tableWidget->item(row, patternCol);
QTableWidgetItem *deletableItem = ui->tableWidget->item(row, deletableCol);
if (patternItem->flags() & Qt::ItemIsEnabled) {
QByteArray prepend;
if (deletableItem->checkState() == Qt::Checked) {
prepend = "]";
} else if (patternItem->text().startsWith('#')) {
prepend = "\\";
}
ignores.write(prepend + patternItem->text().toUtf8() + '\n');
}
}
} else {
QMessageBox::warning(this, tr("Could not open file"),
tr("Cannot write changes to '%1'.").arg(ignoreFile));
}
ignores.close(); //close the file before reloading stuff.
FolderMan *folderMan = FolderMan::instance();
/* handle the hidden file checkbox */
/* the ignoreHiddenFiles flag is a folder specific setting, but for now, it is
* handled globally. Save it to every folder that is defined.
*/
folderMan->setIgnoreHiddenFiles(ignoreHiddenFiles());
// We need to force a remote discovery after a change of the ignore list.
// Otherwise we would not download the files/directories that are no longer
// ignored (because the remote etag did not change) (issue #3172)
foreach (Folder *folder, folderMan->map()) {
folder->journalDb()->forceRemoteDiscoveryNextSync();
folderMan->scheduleFolder(folder);
}
}
void IgnoreListEditor::slotAddPattern()
{
bool okClicked;
QString pattern = QInputDialog::getText(this, tr("Add Ignore Pattern"),
tr("Add a new ignore pattern:"),
QLineEdit::Normal, QString(), &okClicked);
if (!okClicked || pattern.isEmpty())
return;
addPattern(pattern, false, false);
ui->tableWidget->scrollToBottom();
}
void IgnoreListEditor::slotRestoreDefaults(QAbstractButton *button){
if(ui->buttonBox->buttonRole(button) == QDialogButtonBox::ResetRole){
ConfigFile cfgFile;
setupTableReadOnlyItems();
readIgnoreFile(cfgFile.excludeFile(ConfigFile::SystemScope), false);
}
}
void IgnoreListEditor::readIgnoreFile(const QString &file, bool readOnly)
{
QFile ignores(file);
if (ignores.open(QIODevice::ReadOnly)) {
while (!ignores.atEnd()) {
QString line = QString::fromUtf8(ignores.readLine());
line.chop(1);
if (!line.isEmpty() && !line.startsWith("#")) {
bool deletable = false;
if (line.startsWith(']')) {
deletable = true;
line = line.mid(1);
}
addPattern(line, deletable, readOnly);
}
}
}
}
int IgnoreListEditor::addPattern(const QString &pattern, bool deletable, bool readOnly)
{
int newRow = ui->tableWidget->rowCount();
ui->tableWidget->setRowCount(newRow + 1);
QTableWidgetItem *patternItem = new QTableWidgetItem;
patternItem->setText(pattern);
ui->tableWidget->setItem(newRow, patternCol, patternItem);
QTableWidgetItem *deletableItem = new QTableWidgetItem;
deletableItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
deletableItem->setCheckState(deletable ? Qt::Checked : Qt::Unchecked);
ui->tableWidget->setItem(newRow, deletableCol, deletableItem);
if (readOnly) {
patternItem->setFlags(patternItem->flags() ^ Qt::ItemIsEnabled);
patternItem->setToolTip(readOnlyTooltip);
deletableItem->setFlags(deletableItem->flags() ^ Qt::ItemIsEnabled);
}
ui->removeAllPushButton->setEnabled(true);
return newRow;
setupTableReadOnlyItems();
ui->ignoreTableWidget->readIgnoreFile(cfgFile.excludeFile(ConfigFile::SystemScope), false);
}
} // namespace OCC

View File

@@ -35,23 +35,16 @@ class IgnoreListEditor : public QDialog
Q_OBJECT
public:
explicit IgnoreListEditor(QWidget *parent = nullptr);
IgnoreListEditor(QWidget *parent = nullptr);
~IgnoreListEditor();
bool ignoreHiddenFiles();
private slots:
void slotItemSelectionChanged();
void slotRemoveCurrentItem();
void slotUpdateLocalIgnoreList();
void slotAddPattern();
void slotRestoreDefaults(QAbstractButton *button);
void slotRemoveAllItems();
private:
void readIgnoreFile(const QString &file, bool readOnly);
void setupTableReadOnlyItems();
int addPattern(const QString &pattern, bool deletable, bool readOnly);
QString readOnlyTooltip;
Ui::IgnoreListEditor *ui;
};

View File

@@ -36,96 +36,8 @@
<string>Files Ignored by Patterns</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="4" column="0" colspan="2">
<widget class="QLabel" name="descriptionLabel">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="1">
<spacer name="verticalSpacer">
<property name="enabled">
<bool>true</bool>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>213</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0" rowspan="4">
<widget class="QTableWidget" name="tableWidget">
<property name="enabled">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="columnCount">
<number>2</number>
</property>
<column>
<property name="text">
<string>Pattern</string>
</property>
</column>
<column>
<property name="text">
<string>Allow Deletion</string>
</property>
</column>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="removePushButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="addPushButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="removeAllPushButton">
<property name="text">
<string>Remove all</string>
</property>
</widget>
<item row="0" column="0">
<widget class="IgnoreListTableWidget" name="ignoreTableWidget" native="true"/>
</item>
</layout>
</widget>
@@ -139,6 +51,14 @@
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>IgnoreListTableWidget</class>
<extends>QWidget</extends>
<header>ignorelisttablewidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>

View File

@@ -0,0 +1,167 @@
#include "ignorelisttablewidget.h"
#include "ui_ignorelisttablewidget.h"
#include "folderman.h"
#include <QFile>
#include <QInputDialog>
#include <QLineEdit>
#include <QMessageBox>
namespace OCC {
static constexpr int patternCol = 0;
static constexpr int deletableCol = 1;
static constexpr int readOnlyRows = 3;
IgnoreListTableWidget::IgnoreListTableWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::IgnoreListTableWidget)
{
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
ui->setupUi(this);
ui->descriptionLabel->setText(tr("Files or folders matching a pattern will not be synchronized.\n\n"
"Items where deletion is allowed will be deleted if they prevent a "
"directory from being removed. "
"This is useful for meta data."));
ui->removePushButton->setEnabled(false);
connect(ui->tableWidget, &QTableWidget::itemSelectionChanged,
this, &IgnoreListTableWidget::slotItemSelectionChanged);
connect(ui->removePushButton, &QAbstractButton::clicked,
this, &IgnoreListTableWidget::slotRemoveCurrentItem);
connect(ui->addPushButton, &QAbstractButton::clicked,
this, &IgnoreListTableWidget::slotAddPattern);
connect(ui->removeAllPushButton, &QAbstractButton::clicked,
this, &IgnoreListTableWidget::slotRemoveAllItems);
ui->tableWidget->resizeColumnsToContents();
ui->tableWidget->horizontalHeader()->setSectionResizeMode(patternCol, QHeaderView::Stretch);
ui->tableWidget->verticalHeader()->setVisible(false);
}
IgnoreListTableWidget::~IgnoreListTableWidget()
{
delete ui;
}
void IgnoreListTableWidget::slotItemSelectionChanged()
{
QTableWidgetItem *item = ui->tableWidget->currentItem();
if (!item) {
ui->removePushButton->setEnabled(false);
return;
}
bool enable = item->flags() & Qt::ItemIsEnabled;
ui->removePushButton->setEnabled(enable);
}
void IgnoreListTableWidget::slotRemoveCurrentItem()
{
ui->tableWidget->removeRow(ui->tableWidget->currentRow());
if(ui->tableWidget->rowCount() == readOnlyRows)
ui->removeAllPushButton->setEnabled(false);
}
void IgnoreListTableWidget::slotRemoveAllItems()
{
ui->tableWidget->setRowCount(0);
}
void IgnoreListTableWidget::slotWriteIgnoreFile(const QString & file)
{
QFile ignores(file);
if (ignores.open(QIODevice::WriteOnly)) {
// rewrites the whole file since now the user can also remove system patterns
QFile::resize(file, 0);
for (int row = 0; row < ui->tableWidget->rowCount(); ++row) {
QTableWidgetItem *patternItem = ui->tableWidget->item(row, patternCol);
QTableWidgetItem *deletableItem = ui->tableWidget->item(row, deletableCol);
if (patternItem->flags() & Qt::ItemIsEnabled) {
QByteArray prepend;
if (deletableItem->checkState() == Qt::Checked) {
prepend = "]";
} else if (patternItem->text().startsWith('#')) {
prepend = "\\";
}
ignores.write(prepend + patternItem->text().toUtf8() + '\n');
}
}
} else {
QMessageBox::warning(this, tr("Could not open file"),
tr("Cannot write changes to '%1'.").arg(file));
}
ignores.close(); //close the file before reloading stuff.
FolderMan *folderMan = FolderMan::instance();
// We need to force a remote discovery after a change of the ignore list.
// Otherwise we would not download the files/directories that are no longer
// ignored (because the remote etag did not change) (issue #3172)
foreach (Folder *folder, folderMan->map()) {
folder->journalDb()->forceRemoteDiscoveryNextSync();
folderMan->scheduleFolder(folder);
}
}
void IgnoreListTableWidget::slotAddPattern()
{
bool okClicked;
QString pattern = QInputDialog::getText(this, tr("Add Ignore Pattern"),
tr("Add a new ignore pattern:"),
QLineEdit::Normal, QString(), &okClicked);
if (!okClicked || pattern.isEmpty())
return;
addPattern(pattern, false, false);
ui->tableWidget->scrollToBottom();
}
void IgnoreListTableWidget::readIgnoreFile(const QString &file, bool readOnly)
{
QFile ignores(file);
if (ignores.open(QIODevice::ReadOnly)) {
while (!ignores.atEnd()) {
QString line = QString::fromUtf8(ignores.readLine());
line.chop(1);
if (!line.isEmpty() && !line.startsWith("#")) {
bool deletable = false;
if (line.startsWith(']')) {
deletable = true;
line = line.mid(1);
}
addPattern(line, deletable, readOnly);
}
}
}
}
int IgnoreListTableWidget::addPattern(const QString &pattern, bool deletable, bool readOnly)
{
int newRow = ui->tableWidget->rowCount();
ui->tableWidget->setRowCount(newRow + 1);
QTableWidgetItem *patternItem = new QTableWidgetItem;
patternItem->setText(pattern);
ui->tableWidget->setItem(newRow, patternCol, patternItem);
QTableWidgetItem *deletableItem = new QTableWidgetItem;
deletableItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
deletableItem->setCheckState(deletable ? Qt::Checked : Qt::Unchecked);
ui->tableWidget->setItem(newRow, deletableCol, deletableItem);
if (readOnly) {
patternItem->setFlags(patternItem->flags() ^ Qt::ItemIsEnabled);
patternItem->setToolTip(readOnlyTooltip);
deletableItem->setFlags(deletableItem->flags() ^ Qt::ItemIsEnabled);
}
ui->removeAllPushButton->setEnabled(true);
return newRow;
}
} // namespace OCC

View File

@@ -0,0 +1,38 @@
#pragma once
#include <QWidget>
class QAbstractButton;
namespace OCC {
namespace Ui {
class IgnoreListTableWidget;
}
class IgnoreListTableWidget : public QWidget
{
Q_OBJECT
public:
IgnoreListTableWidget(QWidget *parent = nullptr);
~IgnoreListTableWidget();
void readIgnoreFile(const QString &file, bool readOnly = false);
int addPattern(const QString &pattern, bool deletable, bool readOnly);
public slots:
void slotRemoveAllItems();
void slotWriteIgnoreFile(const QString & file);
private slots:
void slotItemSelectionChanged();
void slotRemoveCurrentItem();
void slotAddPattern();
private:
void setupTableReadOnlyItems();
QString readOnlyTooltip;
Ui::IgnoreListTableWidget *ui;
};
} // namespace OCC

View File

@@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OCC::IgnoreListTableWidget</class>
<widget class="QWidget" name="OCC::IgnoreListTableWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>342</width>
<height>378</height>
</rect>
</property>
<property name="windowTitle">
<string>IgnoreListTableWidget</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0" rowspan="4">
<widget class="QTableWidget" name="tableWidget">
<property name="enabled">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="columnCount">
<number>2</number>
</property>
<column>
<property name="text">
<string>Pattern</string>
</property>
</column>
<column>
<property name="text">
<string>Allow Deletion</string>
</property>
</column>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="addPushButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="removePushButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="removeAllPushButton">
<property name="text">
<string>Remove all</string>
</property>
</widget>
</item>
<item row="3" column="1">
<spacer name="verticalSpacer">
<property name="enabled">
<bool>true</bool>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>322</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="0" colspan="2">
<widget class="QLabel" name="descriptionLabel">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -11,7 +11,7 @@
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
<string notr="true">Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
@@ -33,7 +33,7 @@
<item>
<widget class="QLabel" name="notice">
<property name="text">
<string>TextLabel</string>
<string notr="true">TextLabel</string>
</property>
</widget>
</item>

View File

@@ -20,7 +20,7 @@
</sizepolicy>
</property>
<property name="windowTitle">
<string>Dialog</string>
<string notr="true">Dialog</string>
</property>
<property name="sizeGripEnabled">
<bool>false</bool>

View File

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

View File

@@ -21,7 +21,7 @@ namespace OCC {
OcsShareeJob::OcsShareeJob(AccountPtr account)
: OcsJob(account)
{
setPath("ocs/v1.php/apps/files_sharing/api/v1/sharees");
setPath("ocs/v2.php/apps/files_sharing/api/v1/sharees");
connect(this, &OcsJob::jobFinished, this, &OcsShareeJob::jobDone);
}

View File

@@ -24,7 +24,7 @@ namespace OCC {
OcsShareJob::OcsShareJob(AccountPtr account)
: OcsJob(account)
{
setPath("ocs/v1.php/apps/files_sharing/api/v1/shares");
setPath("ocs/v2.php/apps/files_sharing/api/v1/shares");
connect(this, &OcsJob::jobFinished, this, &OcsShareJob::jobDone);
}
@@ -33,6 +33,7 @@ void OcsShareJob::getShares(const QString &path)
setVerb("GET");
addParam(QString::fromLatin1("path"), path);
addParam(QString::fromLatin1("reshares"), QString("true"));
addPassStatusCode(404);
start();

View File

@@ -779,7 +779,7 @@ void ownCloudGui::setupActions()
_actionStatus->setEnabled(false);
_actionSettings = new QAction(tr("Settings..."), this);
_actionNewAccountWizard = new QAction(tr("New account..."), this);
_actionRecent = new QAction(tr("Details..."), this);
_actionRecent = new QAction(tr("View more activity..."), this);
_actionRecent->setEnabled(true);
QObject::connect(_actionRecent, &QAction::triggered, this, &ownCloudGui::slotShowSyncProtocol);

View File

@@ -408,7 +408,7 @@ void OwncloudSetupWizard::slotAuthError()
}
_ocWizard->show();
if (_ocWizard->currentId() == WizardCommon::Page_ShibbolethCreds || _ocWizard->currentId() == WizardCommon::Page_OAuthCreds) {
if (_ocWizard->currentId() == WizardCommon::Page_ShibbolethCreds || _ocWizard->currentId() == WizardCommon::Page_OAuthCreds || _ocWizard->currentId() == WizardCommon::Page_Flow2AuthCreds) {
_ocWizard->back();
}
_ocWizard->displayError(errorMsg, _ocWizard->currentId() == WizardCommon::Page_ServerSetup && checkDowngradeAdvised(reply));

View File

@@ -18,6 +18,8 @@
#include "sharelinkwidget.h"
#include "shareusergroupwidget.h"
#include "sharemanager.h"
#include "account.h"
#include "accountstate.h"
#include "configfile.h"
@@ -49,7 +51,8 @@ ShareDialog::ShareDialog(QPointer<AccountState> accountState,
, _maxSharingPermissions(maxSharingPermissions)
, _privateLinkUrl(accountState->account()->deprecatedPrivateLinkUrl(numericFileId).toString(QUrl::FullyEncoded))
, _startPage(startPage)
, _linkWidget(nullptr)
, _linkWidgetList({})
, _emptyShareLinkWidget(nullptr)
, _userGroupWidget(nullptr)
, _progressIndicator(nullptr)
{
@@ -101,11 +104,6 @@ ShareDialog::ShareDialog(QPointer<AccountState> accountState,
this->setWindowTitle(tr("%1 Sharing").arg(Theme::instance()->appNameGUI()));
if (!accountState->account()->capabilities().shareAPI()) {
// TODO do we want to display it?
//auto label = new QLabel(tr("The server does not allow sharing"));
//label->setWordWrap(true);
//label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
//layout()->replaceWidget(_ui->shareWidgets, label);
return;
}
@@ -115,14 +113,6 @@ ShareDialog::ShareDialog(QPointer<AccountState> accountState,
job->start();
}
//TODO Progress Indicator where should it go?
// _progressIndicator = new QProgressIndicator(this);
// _progressIndicator->startAnimation();
// _progressIndicator->setToolTip(tr("Retrieving maximum possible sharing permissions from server..."));
// _ui->buttonBoxLayout->insertWidget(0, _progressIndicator);
// Server versions >= 9.1 support the "share-permissions" property
// older versions will just return share-permissions: ""
auto job = new PropfindJob(accountState->account(), _sharePath);
job->setProperties(
QList<QByteArray>()
@@ -133,10 +123,103 @@ ShareDialog::ShareDialog(QPointer<AccountState> accountState,
connect(job, &PropfindJob::result, this, &ShareDialog::slotPropfindReceived);
connect(job, &PropfindJob::finishedWithError, this, &ShareDialog::slotPropfindError);
job->start();
bool sharingPossible = true;
if (!accountState->account()->capabilities().sharePublicLink()) {
qCWarning(lcSharing) << "Link shares have been disabled";
sharingPossible = false;
} else if (!(maxSharingPermissions & SharePermissionShare)) {
qCWarning(lcSharing) << "The file can not be shared because it was shared without sharing permission.";
sharingPossible = false;
}
if (sharingPossible) {
_manager = new ShareManager(accountState->account(), this);
connect(_manager, &ShareManager::sharesFetched, this, &ShareDialog::slotSharesFetched);
connect(_manager, &ShareManager::linkShareCreated, this, &ShareDialog::slotAddLinkShareWidget);
}
}
void ShareDialog::addLinkShareWidget(const QSharedPointer<LinkShare> &linkShare){
_linkWidgetList.append(new ShareLinkWidget(_accountState->account(), _sharePath, _localPath, _maxSharingPermissions, this));
int index = _linkWidgetList.size()-1;
_linkWidgetList.at(index)->setLinkShare(linkShare);
connect(linkShare.data(), &Share::serverError, _linkWidgetList.at(index), &ShareLinkWidget::slotServerError);
connect(linkShare.data(), &Share::shareDeleted, _linkWidgetList.at(index), &ShareLinkWidget::slotDeleteShareFetched);
connect(_manager, &ShareManager::linkShareRequiresPassword, _linkWidgetList.at(index), &ShareLinkWidget::slotCreateShareRequiresPassword);
connect(_manager, &ShareManager::serverError, _linkWidgetList.at(index), &ShareLinkWidget::slotServerError);
// Connect all shares signals to gui slots
connect(this, &ShareDialog::toggleAnimation, _linkWidgetList.at(index), &ShareLinkWidget::slotToggleAnimation);
connect(_linkWidgetList.at(index), &ShareLinkWidget::createLinkShare, this, &ShareDialog::slotCreateLinkShare);
connect(_linkWidgetList.at(index), &ShareLinkWidget::deleteLinkShare, this, &ShareDialog::slotDeleteShare);
//connect(_linkWidgetList.at(index), &ShareLinkWidget::resizeRequested, this, &ShareDialog::slotAdjustScrollWidgetSize);
_ui->verticalLayout->insertWidget(_linkWidgetList.size()+1, _linkWidgetList.at(index));
_linkWidgetList.at(index)->setupUiOptions();
}
void ShareDialog::initLinkShareWidget(){
if(_linkWidgetList.size() == 0){
_emptyShareLinkWidget = new ShareLinkWidget(_accountState->account(), _sharePath, _localPath, _maxSharingPermissions, this);
_linkWidgetList.append(_emptyShareLinkWidget);
// connect(_emptyShareLinkWidget, &ShareLinkWidget::resizeRequested, this, &ShareDialog::slotAdjustScrollWidgetSize);
// connect(this, &ShareDialog::toggleAnimation, _emptyShareLinkWidget, &ShareLinkWidget::slotToggleAnimation);
connect(_emptyShareLinkWidget, &ShareLinkWidget::createLinkShare, this, &ShareDialog::slotCreateLinkShare);
_ui->verticalLayout->insertWidget(_linkWidgetList.size()+1, _emptyShareLinkWidget);
_emptyShareLinkWidget->show();
} else if(_emptyShareLinkWidget) {
_emptyShareLinkWidget->hide();
_ui->verticalLayout->removeWidget(_emptyShareLinkWidget);
_linkWidgetList.removeAll(_emptyShareLinkWidget);
_emptyShareLinkWidget = nullptr;
}
}
void ShareDialog::slotAddLinkShareWidget(const QSharedPointer<LinkShare> &linkShare){
emit toggleAnimation(true);
addLinkShareWidget(linkShare);
initLinkShareWidget();
emit toggleAnimation(false);
}
void ShareDialog::slotSharesFetched(const QList<QSharedPointer<Share>> &shares)
{
emit toggleAnimation(true);
const QString versionString = _accountState->account()->serverVersion();
qCInfo(lcSharing) << versionString << "Fetched" << shares.count() << "shares";
foreach (auto share, shares) {
if (share->getShareType() != Share::TypeLink || share->getUidOwner() != share->account()->davUser()) {
continue;
}
QSharedPointer<LinkShare> linkShare = qSharedPointerDynamicCast<LinkShare>(share);
addLinkShareWidget(linkShare);
}
initLinkShareWidget();
emit toggleAnimation(false);
}
// TODO
void ShareDialog::slotAdjustScrollWidgetSize()
{
int count = this->findChildren<ShareLinkWidget *>().count();
_ui->scrollArea->setVisible(count > 0);
if (count > 0 && count <= 3) {
_ui->scrollArea->setFixedHeight(_ui->scrollArea->widget()->sizeHint().height());
}
_ui->scrollArea->setFrameShape(count > 3 ? QFrame::StyledPanel : QFrame::NoFrame);
}
ShareDialog::~ShareDialog()
{
_linkWidgetList.clear();
delete _ui;
}
@@ -178,8 +261,6 @@ void ShareDialog::slotPropfindError()
void ShareDialog::showSharingUi()
{
//_progressIndicator->stopAnimation();
auto theme = Theme::instance();
// There's no difference between being unable to reshare and
@@ -190,6 +271,7 @@ void ShareDialog::showSharingUi()
auto label = new QLabel(this);
label->setText(tr("The file can not be shared because it was shared without sharing permission."));
label->setWordWrap(true);
_ui->verticalLayout->insertWidget(1, label);
return;
}
@@ -205,15 +287,25 @@ void ShareDialog::showSharingUi()
}
if (theme->linkSharing()) {
_linkWidget = new ShareLinkWidget(_accountState->account(), _sharePath, _localPath, _maxSharingPermissions, this);
_ui->verticalLayout->insertWidget(2, _linkWidget);
_linkWidget->getShares();
if (_startPage == ShareDialogStartPage::PublicLinks)
_ui->verticalLayout->insertWidget(3, _linkWidget);
_manager->fetchShares(_sharePath);
}
}
void ShareDialog::slotCreateLinkShare()
{
_manager->createLinkShare(_sharePath, QString(), QString());
}
void ShareDialog::slotDeleteShare()
{
auto sharelinkWidget = dynamic_cast<ShareLinkWidget*>(sender());
sharelinkWidget->hide();
_ui->verticalLayout->removeWidget(sharelinkWidget);
_linkWidgetList.removeAll(sharelinkWidget);
initLinkShareWidget();
}
void ShareDialog::slotThumbnailFetched(const int &statusCode, const QByteArray &reply)
{
if (statusCode != 200) {
@@ -237,8 +329,10 @@ void ShareDialog::slotAccountStateChanged(int state)
_userGroupWidget->setEnabled(enabled);
}
if (_linkWidget != nullptr) {
_linkWidget->setEnabled(enabled);
if(_linkWidgetList.size() > 0){
foreach(ShareLinkWidget *widget, _linkWidgetList){
widget->setEnabled(state);
}
}
}
}

View File

@@ -19,6 +19,7 @@
#include "sharepermissions.h"
#include "owncloudgui.h"
#include <QSharedPointer>
#include <QPointer>
#include <QString>
#include <QDialog>
@@ -34,6 +35,9 @@ namespace Ui {
class ShareLinkWidget;
class ShareUserGroupWidget;
class ShareManager;
class LinkShare;
class Share;
class ShareDialog : public QDialog
{
@@ -56,8 +60,19 @@ private slots:
void slotThumbnailFetched(const int &statusCode, const QByteArray &reply);
void slotAccountStateChanged(int state);
void slotSharesFetched(const QList<QSharedPointer<Share>> &shares);
void slotAddLinkShareWidget(const QSharedPointer<LinkShare> &linkShare);
void slotDeleteShare();
void slotCreateLinkShare();
void slotAdjustScrollWidgetSize();
signals:
void toggleAnimation(bool);
private:
void showSharingUi();
void addLinkShareWidget(const QSharedPointer<LinkShare> &linkShare);
void initLinkShareWidget();
Ui::ShareDialog *_ui;
@@ -68,8 +83,10 @@ private:
QByteArray _numericFileId;
QString _privateLinkUrl;
ShareDialogStartPage _startPage;
ShareManager *_manager;
ShareLinkWidget *_linkWidget;
QList<ShareLinkWidget*> _linkWidgetList;
ShareLinkWidget* _emptyShareLinkWidget;
ShareUserGroupWidget *_userGroupWidget;
QProgressIndicator *_progressIndicator;
};

View File

@@ -17,9 +17,8 @@
#include "sharelinkwidget.h"
#include "account.h"
#include "capabilities.h"
#include "sharemanager.h"
#include "guiutility.h"
#include "sharemanager.h"
#include "QProgressIndicator.h"
#include <QBuffer>
@@ -29,9 +28,12 @@
#include <QMessageBox>
#include <QMenu>
#include <QToolButton>
#include <QPropertyAnimation>
namespace OCC {
Q_LOGGING_CATEGORY(lcShareLink, "nextcloud.gui.sharelink", QtInfoMsg)
ShareLinkWidget::ShareLinkWidget(AccountPtr account,
const QString &sharePath,
const QString &localPath,
@@ -42,13 +44,11 @@ ShareLinkWidget::ShareLinkWidget(AccountPtr account,
, _account(account)
, _sharePath(sharePath)
, _localPath(localPath)
, _manager(nullptr)
, _linkShare(nullptr)
, _passwordRequired(false)
, _expiryRequired(false)
, _namesSupported(true)
, _linkContextMenu(nullptr)
, _copyLinkAction(nullptr)
, _readOnlyLinkAction(nullptr)
, _allowEditingLinkAction(nullptr)
, _allowUploadEditingLinkAction(nullptr)
@@ -68,26 +68,26 @@ ShareLinkWidget::ShareLinkWidget(AccountPtr account,
QFileInfo fi(localPath);
_isFile = fi.isFile();
connect(_ui->enableShareLink, &QCheckBox::toggled, this, &ShareLinkWidget::slotCreateOrDeleteShareLink);
connect(_ui->enableShareLink, &QPushButton::clicked, this, &ShareLinkWidget::slotCreateShareLink);
connect(_ui->lineEdit_password, &QLineEdit::returnPressed, this, &ShareLinkWidget::slotCreatePassword);
connect(_ui->confirmPassword, &QAbstractButton::clicked, this, &ShareLinkWidget::slotCreatePassword);
connect(_ui->confirmExpirationDate, &QAbstractButton::clicked, this, &ShareLinkWidget::slotSetExpireDate);
connect(_ui->calendar, &QDateTimeEdit::dateChanged, this, &ShareLinkWidget::slotExpireDateChanged);
connect(_ui->calendar, &QDateTimeEdit::dateChanged, this, &ShareLinkWidget::slotSetExpireDate);
_ui->errorLabel->hide();
bool sharingPossible = true;
if (!_account->capabilities().sharePublicLink()) {
qCWarning(lcSharing) << "Link shares have been disabled";
qCWarning(lcShareLink) << "Link shares have been disabled";
sharingPossible = false;
} else if (!(maxSharingPermissions & SharePermissionShare)) {
qCWarning(lcSharing) << "The file can not be shared because it was shared without sharing permission.";
qCWarning(lcShareLink) << "The file can not be shared because it was shared without sharing permission.";
sharingPossible = false;
}
_ui->createShareButton->setVisible(sharingPossible);
_ui->enableShareLink->setVisible(sharingPossible);
_ui->shareLinkToolButton->setVisible(sharingPossible);
_ui->enableShareLink->setChecked(false);
_ui->shareLinkToolButton->setEnabled(false);
_ui->shareLinkToolButton->hide();
// Older servers don't support multiple public link shares
if (!_account->capabilities().sharePublicLinkMultiple()) {
@@ -100,34 +100,9 @@ ShareLinkWidget::ShareLinkWidget(AccountPtr account,
// check if the file is already inside of a synced folder
if (sharePath.isEmpty()) {
// The file is not yet in an ownCloud synced folder. We could automatically
// copy it over, but that is skipped as not all questions can be answered that
// are involved in that, see https://github.com/owncloud/client/issues/2732
//
// _ui->checkBox_shareLink->setEnabled(false);
// uploadExternalFile();
qCWarning(lcSharing) << "Unable to share files not in a sync folder.";
qCWarning(lcShareLink) << "Unable to share files not in a sync folder.";
return;
}
// TODO File Drop
// File can't have public upload set; we also hide it if the capability isn't there
// _ui->widget_editing->setVisible(
// !_isFile && _account->capabilities().sharePublicLinkAllowUpload());
//_ui->radio_uploadOnly->setVisible(
//_account->capabilities().sharePublicLinkSupportsUploadOnly());
/*
* Create the share manager and connect it properly
*/
if (sharingPossible) {
_manager = new ShareManager(_account, this);
connect(_manager, &ShareManager::sharesFetched, this, &ShareLinkWidget::slotSharesFetched);
connect(_manager, &ShareManager::linkShareCreated, this, &ShareLinkWidget::slotCreateShareFetched);
connect(_manager, &ShareManager::linkShareRequiresPassword, this, &ShareLinkWidget::slotCreateShareRequiresPassword);
connect(_manager, &ShareManager::serverError, this, &ShareLinkWidget::slotServerError);
}
}
ShareLinkWidget::~ShareLinkWidget()
@@ -135,7 +110,7 @@ ShareLinkWidget::~ShareLinkWidget()
delete _ui;
}
void ShareLinkWidget::toggleAnimation(bool start){
void ShareLinkWidget::slotToggleAnimation(bool start){
if (start) {
if (!_ui->progressIndicator->isAnimated())
_ui->progressIndicator->startAnimation();
@@ -144,218 +119,170 @@ void ShareLinkWidget::toggleAnimation(bool start){
}
}
void ShareLinkWidget::getShares()
{
if (_manager) {
toggleAnimation(true);
_manager->fetchShares(_sharePath);
}
void ShareLinkWidget::setLinkShare(QSharedPointer<LinkShare> linkShare){
_linkShare = linkShare;
}
void ShareLinkWidget::slotSharesFetched(const QList<QSharedPointer<Share>> &shares)
{
const QString versionString = _account->serverVersion();
qCInfo(lcSharing) << versionString << "Fetched" << shares.count() << "shares";
foreach (auto share, shares) {
if (share->getShareType() != Share::TypeLink) {
continue;
}
_linkShare = qSharedPointerDynamicCast<LinkShare>(share);
// Connect all shares signals to gui slots
connect(share.data(), &Share::serverError, this, &ShareLinkWidget::slotServerError);
connect(share.data(), &Share::shareDeleted, this, &ShareLinkWidget::slotDeleteShareFetched);
connect(_linkShare.data(), &LinkShare::expireDateSet, this, &ShareLinkWidget::slotExpireDateSet);
connect(_linkShare.data(), &LinkShare::passwordSet, this, &ShareLinkWidget::slotPasswordSet);
connect(_linkShare.data(), &LinkShare::passwordSetError, this, &ShareLinkWidget::slotPasswordSetError);
// Prepare permissions check and create group action
bool checked = false;
SharePermissions perm = _linkShare->getPermissions();
QActionGroup *permissionsGroup = new QActionGroup(this);
// Prepare sharing menu
_linkContextMenu = new QMenu(this);
// radio button style
permissionsGroup->setExclusive(true);
if(_isFile){
checked = perm & (SharePermissionRead & SharePermissionUpdate);
_allowEditingLinkAction = _linkContextMenu->addAction(tr("Allow Editing"));
_allowEditingLinkAction->setCheckable(true);
_allowEditingLinkAction->setChecked(checked);
} else {
checked = perm & SharePermissionRead;
_readOnlyLinkAction = permissionsGroup->addAction(tr("Read only"));
_readOnlyLinkAction->setCheckable(true);
_readOnlyLinkAction->setChecked(checked);
checked = perm & (SharePermissionRead &
SharePermissionCreate &
SharePermissionUpdate &
SharePermissionDelete);
_allowUploadEditingLinkAction = permissionsGroup->addAction(tr("Allow Upload && Editing"));
_allowUploadEditingLinkAction->setCheckable(true);
_allowUploadEditingLinkAction->setChecked(checked);
checked = perm & SharePermissionCreate;
_allowUploadLinkAction = permissionsGroup->addAction(tr("File Drop (Upload Only)"));
_allowUploadLinkAction->setCheckable(true);
_allowUploadLinkAction->setChecked(checked);
}
// Add copy action (icon only)
_copyLinkAction = _linkContextMenu->addAction(QIcon(":/client/resources/copy.svg"),
tr("Copy link"));
// Adds permissions actions (radio button style)
if(_isFile){
_linkContextMenu->addAction(_allowEditingLinkAction);
} else {
_linkContextMenu->addAction(_readOnlyLinkAction);
_linkContextMenu->addAction(_allowUploadEditingLinkAction);
_linkContextMenu->addAction(_allowUploadLinkAction);
}
// Adds action to display password widget (check box)
_passwordProtectLinkAction = _linkContextMenu->addAction(tr("Password Protect"));
_passwordProtectLinkAction->setCheckable(true);
if(_linkShare->isPasswordSet()){
_passwordProtectLinkAction->setChecked(true);
_ui->lineEdit_password->setPlaceholderText("********");
showPasswordOptions(true);
}
// If password is enforced then don't allow users to disable it
if (_account->capabilities().sharePublicLinkEnforcePassword()) {
_passwordProtectLinkAction->setChecked(true);
_passwordProtectLinkAction->setEnabled(false);
_passwordRequired = true;
}
// Adds action to display expiration date widget (check box)
_expirationDateLinkAction = _linkContextMenu->addAction(tr("Expiration Date"));
_expirationDateLinkAction->setCheckable(true);
if(_linkShare->getExpireDate().isValid()){
_ui->calendar->setDate(_linkShare->getExpireDate());
_expirationDateLinkAction->setChecked(true);
showExpireDateOptions(true);
}
// If expiredate is enforced do not allow disable and set max days
if (_account->capabilities().sharePublicLinkEnforceExpireDate()) {
_ui->calendar->setMaximumDate(QDate::currentDate().addDays(
_account->capabilities().sharePublicLinkExpireDateDays()));
_expirationDateLinkAction->setChecked(true);
_expirationDateLinkAction->setEnabled(false);
_expiryRequired = true;
}
// Adds action to unshare widget (check box)
_unshareLinkAction = _linkContextMenu->addAction(QIcon(":/client/resources/delete.png"),
tr("Unshare"));
connect(_linkContextMenu, &QMenu::triggered,
this, &ShareLinkWidget::slotLinkContextMenuActionTriggered);
_ui->shareLinkToolButton->setMenu(_linkContextMenu);
_ui->shareLinkToolButton->setEnabled(true);
_ui->enableShareLink->setEnabled(true);
_ui->enableShareLink->setChecked(true);
// show sharing options
_ui->shareLinkToolButton->show();
}
toggleAnimation(false);
QSharedPointer<LinkShare> ShareLinkWidget::getLinkShare(){
return _linkShare;
}
void ShareLinkWidget::setExpireDate(const QDate &date)
{
if (_linkShare) {
toggleAnimation(true);
_ui->errorLabel->hide();
_linkShare->setExpireDate(date);
void ShareLinkWidget::setupUiOptions(){
connect(_linkShare.data(), &LinkShare::expireDateSet, this, &ShareLinkWidget::slotExpireDateSet);
connect(_linkShare.data(), &LinkShare::passwordSet, this, &ShareLinkWidget::slotPasswordSet);
connect(_linkShare.data(), &LinkShare::passwordSetError, this, &ShareLinkWidget::slotPasswordSetError);
// Prepare permissions check and create group action
const QDate expireDate = _linkShare.data()->getExpireDate().isValid()? _linkShare.data()->getExpireDate() : QDate();
const SharePermissions perm = _linkShare.data()->getPermissions();
bool checked = false;
QActionGroup *permissionsGroup = new QActionGroup(this);
// Prepare sharing menu
_linkContextMenu = new QMenu(this);
// radio button style
permissionsGroup->setExclusive(true);
if(_isFile){
checked = perm & (SharePermissionRead & SharePermissionUpdate);
_allowEditingLinkAction = _linkContextMenu->addAction(tr("Allow Editing"));
_allowEditingLinkAction->setCheckable(true);
_allowEditingLinkAction->setChecked(checked);
} else {
checked = perm & SharePermissionRead;
_readOnlyLinkAction = permissionsGroup->addAction(tr("Read only"));
_readOnlyLinkAction->setCheckable(true);
_readOnlyLinkAction->setChecked(checked);
checked = perm & (SharePermissionRead &
SharePermissionCreate &
SharePermissionUpdate &
SharePermissionDelete);
_allowUploadEditingLinkAction = permissionsGroup->addAction(tr("Allow Upload && Editing"));
_allowUploadEditingLinkAction->setCheckable(true);
_allowUploadEditingLinkAction->setChecked(checked);
checked = perm & SharePermissionCreate;
_allowUploadLinkAction = permissionsGroup->addAction(tr("File Drop (Upload Only)"));
_allowUploadLinkAction->setCheckable(true);
_allowUploadLinkAction->setChecked(checked);
}
// Adds permissions actions (radio button style)
if(_isFile){
_linkContextMenu->addAction(_allowEditingLinkAction);
} else {
_linkContextMenu->addAction(_readOnlyLinkAction);
_linkContextMenu->addAction(_allowUploadEditingLinkAction);
_linkContextMenu->addAction(_allowUploadLinkAction);
}
// Adds action to display password widget (check box)
_passwordProtectLinkAction = _linkContextMenu->addAction(tr("Password Protect"));
_passwordProtectLinkAction->setCheckable(true);
if(_linkShare.data()->isPasswordSet()){
_passwordProtectLinkAction->setChecked(true);
_ui->lineEdit_password->setPlaceholderText("********");
showPasswordOptions(true);
}
// If password is enforced then don't allow users to disable it
if (_account->capabilities().sharePublicLinkEnforcePassword()) {
_passwordProtectLinkAction->setChecked(true);
_passwordProtectLinkAction->setEnabled(false);
_passwordRequired = true;
}
// Adds action to display expiration date widget (check box)
_expirationDateLinkAction = _linkContextMenu->addAction(tr("Expiration Date"));
_expirationDateLinkAction->setCheckable(true);
if(!expireDate.isNull()){
_ui->calendar->setDate(expireDate);
_expirationDateLinkAction->setChecked(true);
showExpireDateOptions(true);
}
// If expiredate is enforced do not allow disable and set max days
if (_account->capabilities().sharePublicLinkEnforceExpireDate()) {
_ui->calendar->setMaximumDate(QDate::currentDate().addDays(
_account->capabilities().sharePublicLinkExpireDateDays()));
_expirationDateLinkAction->setChecked(true);
_expirationDateLinkAction->setEnabled(false);
_expiryRequired = true;
}
// Adds action to unshare widget (check box)
_unshareLinkAction = _linkContextMenu->addAction(QIcon(":/client/resources/delete.png"),
tr("Unshare"));
_linkContextMenu->addSeparator();
_addAnotherLinkAction = _linkContextMenu->addAction(QIcon(":/client/resources/add.png"),
tr("Add another link"));
_ui->enableShareLink->setIcon(QIcon(":/client/resources/copy.svg"));
disconnect(_ui->enableShareLink, &QPushButton::clicked, this, &ShareLinkWidget::slotCreateShareLink);
connect(_ui->enableShareLink, &QPushButton::clicked, this, &ShareLinkWidget::slotCopyLinkShare);
connect(_linkContextMenu, &QMenu::triggered,
this, &ShareLinkWidget::slotLinkContextMenuActionTriggered);
_ui->shareLinkToolButton->setMenu(_linkContextMenu);
_ui->shareLinkToolButton->setEnabled(true);
_ui->enableShareLink->setEnabled(true);
_ui->enableShareLink->setChecked(true);
// show sharing options
_ui->shareLinkToolButton->show();
//TO DO
//startAnimation(0, height());
}
void ShareLinkWidget::slotCopyLinkShare(bool clicked){
Q_UNUSED(clicked);
QApplication::clipboard()->setText(_linkShare->getLink().toString());
}
void ShareLinkWidget::slotExpireDateSet()
{
toggleAnimation(false);
}
void ShareLinkWidget::slotExpireDateChanged(const QDate &date)
{
setExpireDate(date);
slotToggleAnimation(false);
}
void ShareLinkWidget::slotSetExpireDate()
{
slotExpireDateChanged(_ui->calendar->date());
if(!_linkShare){
return;
}
slotToggleAnimation(true);
_ui->errorLabel->hide();
_linkShare->setExpireDate(_ui->calendar->date());
}
void ShareLinkWidget::slotCreatePassword()
{
if (!_manager) {
return;
}
toggleAnimation(true);
if (!_linkShare) {
// If share creation requires a password, we'll be in this case
if (_ui->lineEdit_password->text().isEmpty()) {
_ui->lineEdit_password->setFocus();
return;
}
_manager->createLinkShare(_sharePath, QString(), _ui->lineEdit_password->text());
} else {
setPassword(_ui->lineEdit_password->text());
}
}
void ShareLinkWidget::slotCreateOrDeleteShareLink(bool checked)
{
if (!_manager) {
qCWarning(lcSharing) << "No share manager set.";
return;
}
toggleAnimation(true);
if(checked){
_manager->createLinkShare(_sharePath, QString(), QString());
} else {
if (!_linkShare) {
qCWarning(lcSharing) << "No public link set.";
return;
}
confirmAndDeleteShare();
}
slotToggleAnimation(true);
_ui->errorLabel->hide();
_linkShare->setPassword(_ui->lineEdit_password->text());
}
void ShareLinkWidget::setPassword(const QString &password)
void ShareLinkWidget::slotCreateShareLink(bool clicked)
{
if (_linkShare) {
toggleAnimation(true);
_ui->errorLabel->hide();
_linkShare->setPassword(password);
}
slotToggleAnimation(true);
emit createLinkShare();
}
void ShareLinkWidget::slotPasswordSet()
{
if (!_linkShare)
return;
_ui->lineEdit_password->setText(QString());
if (_linkShare->isPasswordSet()) {
_ui->lineEdit_password->setPlaceholderText("********");
@@ -364,39 +291,55 @@ void ShareLinkWidget::slotPasswordSet()
_ui->lineEdit_password->setPlaceholderText(QString());
}
toggleAnimation(false);
slotToggleAnimation(false);
}
/*
* When setting/deleting a password from a share the old share is
* deleted and a new one is created. So we need to refetch the shares
* at this point.
*
* NOTE: I don't see this happening with oC > 10
*/
getShares();
void ShareLinkWidget::startAnimation(const int start, const int end){
QPropertyAnimation *animation = new QPropertyAnimation(this, "maximumHeight", this);
animation->setDuration(500);
animation->setStartValue(start);
animation->setEndValue(end);
connect(animation, &QAbstractAnimation::finished, this, &ShareLinkWidget::slotAnimationFinished);
if(end < start) // that is to remove the widget, not to show it
connect(animation, &QAbstractAnimation::finished, this, &ShareLinkWidget::slotDeleteAnimationFinished);
connect(animation, &QVariantAnimation::valueChanged, this, &ShareLinkWidget::resizeRequested);
animation->start();
}
void ShareLinkWidget::slotDeleteShareFetched()
{
toggleAnimation(true);
slotToggleAnimation(false);
// TODO
//startAnimation(height(), 0);
_linkShare.clear();
_ui->enableShareLink->setChecked(false);
_ui->shareLinkToolButton->setEnabled(false);
_ui->shareLinkToolButton->hide();
togglePasswordOptions(false);
toggleExpireDateOptions(false);
getShares();
emit deleteLinkShare();
}
void ShareLinkWidget::slotCreateShareFetched()
void ShareLinkWidget::slotAnimationFinished()
{
toggleAnimation(true);
getShares();
emit resizeRequested();
deleteLater();
}
void ShareLinkWidget::slotDeleteAnimationFinished()
{
// There is a painting bug where a small line of this widget isn't
// properly cleared. This explicit repaint() call makes sure any trace of
// the share widget is removed once it's destroyed. #4189
connect(this, SIGNAL(destroyed(QObject *)), parentWidget(), SLOT(repaint()));
}
void ShareLinkWidget::slotCreateShareRequiresPassword(const QString &message)
{
toggleAnimation(true);
slotToggleAnimation(true);
showPasswordOptions(true);
if (!message.isEmpty()) {
@@ -468,8 +411,10 @@ void ShareLinkWidget::confirmAndDeleteShare()
connect(messageBox, &QMessageBox::finished, this,
[messageBox, yesButton, this]() {
if (messageBox->clickedButton() == yesButton)
if (messageBox->clickedButton() == yesButton) {
this->slotToggleAnimation(true);
this->_linkShare->deleteShare();
}
});
messageBox->open();
}
@@ -495,8 +440,8 @@ void ShareLinkWidget::slotLinkContextMenuActionTriggered(QAction *action)
bool state = action->isChecked();
SharePermissions perm = SharePermissionRead;
if (action == _copyLinkAction) {
QApplication::clipboard()->setText(_linkShare->getLink().toString());
if(action == _addAnotherLinkAction){
emit createLinkShare();
} else if (action == _readOnlyLinkAction && state) {
_linkShare->setPermissions(perm);
@@ -520,13 +465,13 @@ void ShareLinkWidget::slotLinkContextMenuActionTriggered(QAction *action)
toggleExpireDateOptions(state);
} else if (action == _unshareLinkAction) {
slotCreateOrDeleteShareLink(state);
confirmAndDeleteShare();
}
}
void ShareLinkWidget::slotServerError(int code, const QString &message)
{
toggleAnimation(false);
slotToggleAnimation(false);
qCWarning(lcSharing) << "Error from server" << code << message;
displayError(message);

View File

@@ -37,7 +37,6 @@ class QuotaInfo;
class SyncResult;
class LinkShare;
class Share;
class ShareManager;
/**
* @brief The ShareDialog class
@@ -54,44 +53,51 @@ public:
SharePermissions maxSharingPermissions,
QWidget *parent = nullptr);
~ShareLinkWidget();
void getShares();
void toggleButton(bool show);
void setupUiOptions();
void setLinkShare(QSharedPointer<LinkShare> linkShare);
QSharedPointer<LinkShare> getLinkShare();
public slots:
void slotDeleteShareFetched();
void slotToggleAnimation(bool start);
void slotServerError(int code, const QString &message);
void slotCreateShareRequiresPassword(const QString &message);
private slots:
void slotSharesFetched(const QList<QSharedPointer<Share>> &shares);
//void slotShareSelectionChanged();
void slotCreateShareLink(bool clicked);
void slotCreateOrDeleteShareLink(bool checked);
void slotCreatePassword();
void slotPasswordSet();
void slotPasswordSetError(int code, const QString &message);
void slotExpireDateChanged(const QDate &date);
void slotSetExpireDate();
void slotExpireDateSet();
void slotContextMenuButtonClicked();
void slotLinkContextMenuActionTriggered(QAction *action);
void slotDeleteShareFetched();
void slotCreateShareFetched();
void slotCreateShareRequiresPassword(const QString &message);
void slotDeleteAnimationFinished();
void slotAnimationFinished();
void slotPasswordSet();
void slotExpireDateSet();
void slotServerError(int code, const QString &message);
void slotPasswordSetError(int code, const QString &message);
signals:
void createLinkShare();
void deleteLinkShare();
void resizeRequested();
void visualDeletionDone();
private:
void displayError(const QString &errMsg);
void showPasswordOptions(bool show);
void togglePasswordOptions(bool enable);
void setPassword(const QString &password);
void showExpireDateOptions(bool show);
void toggleExpireDateOptions(bool enable);
void setExpireDate(const QDate &date);
void copyShareLink(const QUrl &url);
void slotCopyLinkShare(bool clicked);
/** Confirm with the user and then delete the share */
void confirmAndDeleteShare();
@@ -99,7 +105,7 @@ private:
/** Retrieve a share's name, accounting for _namesSupported */
QString shareName() const;
void toggleAnimation(bool start);
void startAnimation(const int start, const int end);
Ui::ShareLinkWidget *_ui;
AccountPtr _account;
@@ -107,7 +113,6 @@ private:
QString _localPath;
QString _shareUrl;
ShareManager *_manager;
QSharedPointer<LinkShare> _linkShare;
bool _isFile;
@@ -116,7 +121,6 @@ private:
bool _namesSupported;
QMenu *_linkContextMenu;
QAction *_copyLinkAction;
QAction *_readOnlyLinkAction;
QAction *_allowEditingLinkAction;
QAction *_allowUploadEditingLinkAction;
@@ -124,6 +128,7 @@ private:
QAction *_passwordProtectLinkAction;
QAction *_expirationDateLinkAction;
QAction *_unshareLinkAction;
QAction *_addAnotherLinkAction;
};
}

View File

@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>350</width>
<height>126</height>
<height>160</height>
</rect>
</property>
<property name="sizePolicy">
@@ -29,80 +29,13 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0" colspan="3">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="createShareButton">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">text-align: left</string>
</property>
<property name="text">
<string>&amp;Share link</string>
</property>
<property name="icon">
<iconset resource="../../client.qrc">
<normaloff>:/client/resources/public.svg</normaloff>:/client/resources/public.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>25</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QProgressIndicator" name="progressIndicator"/>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>25</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="enableShareLink">
<property name="text">
<string>Enable</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="3">
<widget class="QToolButton" name="shareLinkToolButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset resource="../../client.qrc">
<normaloff>:/client/resources/more.svg</normaloff>:/client/resources/more.svg</iconset>
</property>
<property name="popupMode">
<enum>QToolButton::InstantPopup</enum>
<item row="2" column="1">
<widget class="QDateEdit" name="calendar">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
@@ -122,64 +55,17 @@
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEdit_password">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<item row="0" column="3">
<widget class="QToolButton" name="shareLinkToolButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QToolButton" name="confirmPassword">
<property name="icon">
<iconset resource="../../client.qrc">
<normaloff>:/client/resources/confirm.svg</normaloff>:/client/resources/confirm.svg</iconset>
<normaloff>:/client/resources/more.svg</normaloff>:/client/resources/more.svg</iconset>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="expirationLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Expiration date:</string>
</property>
<property name="indent">
<number>20</number>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDateEdit" name="calendar">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QToolButton" name="confirmExpirationDate">
<property name="icon">
<iconset resource="../../client.qrc">
<normaloff>:/client/resources/confirm.svg</normaloff>:/client/resources/confirm.svg</iconset>
</property>
<property name="autoRaise">
<bool>true</bool>
<property name="popupMode">
<enum>QToolButton::InstantPopup</enum>
</property>
</widget>
</item>
@@ -233,6 +119,130 @@
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEdit_password">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QToolButton" name="confirmPassword">
<property name="icon">
<iconset resource="../../client.qrc">
<normaloff>:/client/resources/confirm.svg</normaloff>:/client/resources/confirm.svg</iconset>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QToolButton" name="confirmExpirationDate">
<property name="icon">
<iconset resource="../../client.qrc">
<normaloff>:/client/resources/confirm.svg</normaloff>:/client/resources/confirm.svg</iconset>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0" colspan="3">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="createShareButton">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">text-align: left</string>
</property>
<property name="text">
<string>&amp;Share link</string>
</property>
<property name="icon">
<iconset resource="../../client.qrc">
<normaloff>:/client/resources/public.svg</normaloff>:/client/resources/public.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>25</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QProgressIndicator" name="progressIndicator" native="true"/>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>25</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="enableShareLink">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../client.qrc">
<normaloff>:/client/resources/add.png</normaloff>:/client/resources/add.png</iconset>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="expirationLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Expiration date:</string>
</property>
<property name="indent">
<number>20</number>
</property>
</widget>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>

View File

@@ -52,12 +52,16 @@ static void updateFolder(const AccountPtr &account, const QString &path)
Share::Share(AccountPtr account,
const QString &id,
const QString &uidowner,
const QString &ownerDisplayName,
const QString &path,
const ShareType shareType,
const Permissions permissions,
const QSharedPointer<Sharee> shareWith)
: _account(account)
, _id(id)
, _uidowner(uidowner)
, _ownerDisplayName(ownerDisplayName)
, _path(path)
, _shareType(shareType)
, _permissions(permissions)
@@ -80,6 +84,16 @@ QString Share::getId() const
return _id;
}
QString Share::getUidOwner() const
{
return _uidowner;
}
QString Share::getOwnerDisplayName() const
{
return _ownerDisplayName;
}
Share::ShareType Share::getShareType() const
{
return _shareType;
@@ -153,6 +167,8 @@ bool LinkShare::isPasswordSet() const
LinkShare::LinkShare(AccountPtr account,
const QString &id,
const QString &uidowner,
const QString &ownerDisplayName,
const QString &path,
const QString &name,
const QString &token,
@@ -160,7 +176,7 @@ LinkShare::LinkShare(AccountPtr account,
bool passwordSet,
const QUrl &url,
const QDate &expireDate)
: Share(account, id, path, Share::TypeLink, permissions)
: Share(account, id, uidowner, ownerDisplayName, path, Share::TypeLink, permissions)
, _name(name)
, _token(token)
, _passwordSet(passwordSet)
@@ -393,6 +409,8 @@ QSharedPointer<LinkShare> ShareManager::parseLinkShare(const QJsonObject &data)
return QSharedPointer<LinkShare>(new LinkShare(_account,
data.value("id").toVariant().toString(), // "id" used to be an integer, support both
data.value("uid_owner").toString(),
data.value("displayname_owner").toString(),
data.value("path").toString(),
data.value("name").toString(),
data.value("token").toString(),
@@ -410,6 +428,8 @@ QSharedPointer<Share> ShareManager::parseShare(const QJsonObject &data)
return QSharedPointer<Share>(new Share(_account,
data.value("id").toVariant().toString(), // "id" used to be an integer, support both
data.value("uid_owner").toVariant().toString(),
data.value("displayname_owner").toVariant().toString(),
data.value("path").toString(),
(Share::ShareType)data.value("share_type").toInt(),
(Share::Permissions)data.value("permissions").toInt(),

View File

@@ -54,6 +54,8 @@ public:
*/
explicit Share(AccountPtr account,
const QString &id,
const QString &owner,
const QString &ownerDisplayName,
const QString &path,
const ShareType shareType,
const Permissions permissions = SharePermissionDefault,
@@ -71,6 +73,16 @@ public:
*/
QString getId() const;
/*
* Get the uid_owner
*/
QString getUidOwner() const;
/*
* Get the owner display name
*/
QString getOwnerDisplayName() const;
/*
* Get the shareType
*/
@@ -110,6 +122,8 @@ signals:
protected:
AccountPtr _account;
QString _id;
QString _uidowner;
QString _ownerDisplayName;
QString _path;
ShareType _shareType;
Permissions _permissions;
@@ -134,6 +148,8 @@ class LinkShare : public Share
public:
explicit LinkShare(AccountPtr account,
const QString &id,
const QString &uidowner,
const QString &ownerDisplayName,
const QString &path,
const QString &name,
const QString &token,

View File

@@ -190,13 +190,23 @@ void ShareUserGroupWidget::slotSharesFetched(const QList<QSharedPointer<Share>>
layout->setContentsMargins(0, 0, 0, 0);
int x = 0;
int height = 0;
QList<QString> linkOwners({});
foreach (const auto &share, shares) {
// We don't handle link shares, only TypeUser or TypeGroup
if (share->getShareType() == Share::TypeLink) {
if(!share->getUidOwner().isEmpty() &&
share->getUidOwner() != share->account()->davUser()){
linkOwners.append(share->getOwnerDisplayName());
}
continue;
}
// the owner of the file that shared it first
if(x == 0 && !share->getUidOwner().isEmpty()){
_ui->mainOwnerLabel->setText(QString("Shared with you by ").append(share->getOwnerDisplayName()));
}
ShareUserLine *s = new ShareUserLine(share, _maxSharingPermissions, _isFile, _parentScrollArea);
connect(s, &ShareUserLine::resizeRequested, this, &ShareUserGroupWidget::slotAdjustScrollWidgetSize);
connect(s, &ShareUserLine::visualDeletionDone, this, &ShareUserGroupWidget::getShares);
@@ -209,7 +219,18 @@ void ShareUserGroupWidget::slotSharesFetched(const QList<QSharedPointer<Share>>
}
}
scrollArea->setFrameShape(x > 3 ? QFrame::StyledPanel : QFrame::NoFrame);
foreach (const QString &owner, linkOwners) {
auto ownerLabel = new QLabel(QString(owner + " shared via link"));
layout->addWidget(ownerLabel);
ownerLabel->setVisible(true);
x++;
if (x <= 6) {
height = newViewPort->sizeHint().height();
}
}
scrollArea->setFrameShape(x > 6 ? QFrame::StyledPanel : QFrame::NoFrame);
scrollArea->setVisible(!shares.isEmpty());
scrollArea->setFixedHeight(height);
scrollArea->setWidget(newViewPort);
@@ -367,6 +388,15 @@ ShareUserLine::ShareUserLine(QSharedPointer<Share> share,
menu->addAction(_permissionReshare);
connect(_permissionReshare, &QAction::triggered, this, &ShareUserLine::slotPermissionsChanged);
menu->addSeparator();
// Adds action to delete share widget
QIcon deleteicon = QIcon::fromTheme(QLatin1String("user-trash"),QIcon(QLatin1String(":/client/resources/delete.png")));
_deleteShareButton= new QAction(deleteicon,tr("Unshare"), this);
menu->addAction(_deleteShareButton);
connect(_deleteShareButton, &QAction::triggered, this, &ShareUserLine::on_deleteShareButton_clicked);
/*
* Files can't have create or delete permissions
*/
@@ -413,8 +443,8 @@ ShareUserLine::ShareUserLine(QSharedPointer<Share> share,
connect(share.data(), &Share::permissionsSet, this, &ShareUserLine::slotPermissionsSet);
connect(share.data(), &Share::shareDeleted, this, &ShareUserLine::slotShareDeleted);
_ui->deleteShareButton->setIcon(QIcon::fromTheme(QLatin1String("user-trash"),
QIcon(QLatin1String(":/client/resources/delete.png"))));
// _ui->deleteShareButton->setIcon(QIcon::fromTheme(QLatin1String("user-trash"),
// QIcon(QLatin1String(":/client/resources/delete.png"))));
if (!share->account()->capabilities().shareResharing()) {
_permissionReshare->setVisible(false);

View File

@@ -148,6 +148,7 @@ private:
// _permissionEdit is a checkbox
QAction *_permissionReshare;
QAction *_deleteShareButton;
QAction *_permissionCreate;
QAction *_permissionChange;
QAction *_permissionDelete;

View File

@@ -32,6 +32,13 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="mainOwnerLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="shareeHorizontalLayout">
<property name="leftMargin">

View File

@@ -91,15 +91,7 @@
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="deleteShareButton">
<property name="icon">
<iconset theme="user-trash">
<normaloff>.</normaloff>.</iconset>
</property>
</widget>
</item>
</layout>
</layout>
</widget>
<customwidgets>
<customwidget>

View File

@@ -622,8 +622,8 @@ void OCC::SocketApi::openPrivateLink(const QString &link)
void SocketApi::command_GET_STRINGS(const QString &argument, SocketListener *listener)
{
static std::array<std::pair<const char *, QString>, 5> strings { {
{ "SHARE_MENU_TITLE", tr("Share...") },
{ "CONTEXT_MENU_TITLE", Theme::instance()->appNameGUI() },
{ "SHARE_MENU_TITLE", tr("Share options") },
{ "CONTEXT_MENU_TITLE", tr("Share via ") + Theme::instance()->appNameGUI()},
{ "COPY_PRIVATE_LINK_MENU_TITLE", tr("Copy private link to clipboard") },
{ "EMAIL_PRIVATE_LINK_MENU_TITLE", tr("Send private link by email...") },
} };
@@ -652,7 +652,7 @@ void SocketApi::sendSharingContextMenuOptions(const FileData &fileData, SocketLi
if (isOnTheServer && !record._remotePerm.isNull() && !record._remotePerm.hasPermission(RemotePermissions::CanReshare)) {
listener->sendMessage(QLatin1String("MENU_ITEM:DISABLED:d:") + tr("Resharing this file is not allowed"));
} else {
listener->sendMessage(QLatin1String("MENU_ITEM:SHARE") + flagString + tr("Share..."));
listener->sendMessage(QLatin1String("MENU_ITEM:SHARE") + flagString + tr("Share options"));
// Do we have public links?
bool publicLinksEnabled = theme->linkSharing() && capabilities.sharePublicLink();
@@ -663,13 +663,13 @@ void SocketApi::sendSharingContextMenuOptions(const FileData &fileData, SocketLi
&& !capabilities.sharePublicLinkEnforcePassword();
if (canCreateDefaultPublicLink) {
listener->sendMessage(QLatin1String("MENU_ITEM:COPY_PUBLIC_LINK") + flagString + tr("Copy public link to clipboard"));
listener->sendMessage(QLatin1String("MENU_ITEM:COPY_PUBLIC_LINK") + flagString + tr("Copy public link"));
} else if (publicLinksEnabled) {
listener->sendMessage(QLatin1String("MENU_ITEM:MANAGE_PUBLIC_LINKS") + flagString + tr("Copy public link to clipboard"));
listener->sendMessage(QLatin1String("MENU_ITEM:MANAGE_PUBLIC_LINKS") + flagString + tr("Copy public link"));
}
}
listener->sendMessage(QLatin1String("MENU_ITEM:COPY_PRIVATE_LINK") + flagString + tr("Copy private link to clipboard"));
listener->sendMessage(QLatin1String("MENU_ITEM:COPY_PRIVATE_LINK") + flagString + tr("Copy internal link"));
// Disabled: only providing email option for private links would look odd,
// and the copy option is more general.

View File

@@ -184,10 +184,15 @@ QString SslErrorDialog::certDiv(QSslCertificate cert) const
msg += QL("<p>");
QString md5sum = Utility::formatFingerprint(cert.digest(QCryptographicHash::Md5).toHex());
QString sha1sum = Utility::formatFingerprint(cert.digest(QCryptographicHash::Sha1).toHex());
msg += tr("Fingerprint (MD5): <tt>%1</tt>").arg(md5sum) + QL("<br/>");
msg += tr("Fingerprint (SHA1): <tt>%1</tt>").arg(sha1sum) + QL("<br/>");
if (cert.effectiveDate() < QDateTime(QDate(2016, 1, 1), QTime(), Qt::UTC)) {
QString sha1sum = Utility::formatFingerprint(cert.digest(QCryptographicHash::Sha1).toHex());
msg += tr("Fingerprint (SHA1): <tt>%1</tt>").arg(sha1sum) + QL("<br/>");
}
QString sha256sum = Utility::formatFingerprint(cert.digest(QCryptographicHash::Sha256).toHex());
QString sha512sum = Utility::formatFingerprint(cert.digest(QCryptographicHash::Sha512).toHex());
msg += tr("Fingerprint (SHA-256): <tt>%1</tt>").arg(sha256sum) + QL("<br/>");
msg += tr("Fingerprint (SHA-512): <tt>%1</tt>").arg(sha512sum) + QL("<br/>");
msg += QL("<br/>");
msg += tr("Effective Date: %1").arg(cert.effectiveDate().toString()) + QL("<br/>");
msg += tr("Expiration Date: %1").arg(cert.expiryDate().toString()) + QL("</p>");

View File

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

View File

@@ -0,0 +1,151 @@
/*
* Copyright (C) by Olivier Goffart <ogoffart@woboq.com>
* Copyright (C) by Michael Schuster <michael@nextcloud.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include <QVariant>
#include <QMenu>
#include <QClipboard>
#include "wizard/flow2authcredspage.h"
#include "theme.h"
#include "account.h"
#include "cookiejar.h"
#include "wizard/owncloudwizardcommon.h"
#include "wizard/owncloudwizard.h"
#include "creds/credentialsfactory.h"
#include "creds/webflowcredentials.h"
namespace OCC {
Flow2AuthCredsPage::Flow2AuthCredsPage()
: AbstractCredentialsWizardPage()
{
_ui.setupUi(this);
Theme *theme = Theme::instance();
_ui.topLabel->hide();
_ui.bottomLabel->hide();
QVariant variant = theme->customMedia(Theme::oCSetupTop);
WizardCommon::setupCustomMedia(variant, _ui.topLabel);
variant = theme->customMedia(Theme::oCSetupBottom);
WizardCommon::setupCustomMedia(variant, _ui.bottomLabel);
WizardCommon::initErrorLabel(_ui.errorLabel);
setTitle(WizardCommon::titleTemplate().arg(tr("Connect to %1").arg(Theme::instance()->appNameGUI())));
setSubTitle(WizardCommon::subTitleTemplate().arg(tr("Login in your browser (Login Flow v2)")));
connect(_ui.openLinkButton, &QCommandLinkButton::clicked, this, &Flow2AuthCredsPage::slotOpenBrowser);
connect(_ui.copyLinkButton, &QCommandLinkButton::clicked, this, &Flow2AuthCredsPage::slotCopyLinkToClipboard);
}
void Flow2AuthCredsPage::initializePage()
{
OwncloudWizard *ocWizard = qobject_cast<OwncloudWizard *>(wizard());
Q_ASSERT(ocWizard);
ocWizard->account()->setCredentials(CredentialsFactory::create("http"));
_asyncAuth.reset(new Flow2Auth(ocWizard->account().data(), this));
connect(_asyncAuth.data(), &Flow2Auth::result, this, &Flow2AuthCredsPage::asyncAuthResult, Qt::QueuedConnection);
_asyncAuth->start();
// Don't hide the wizard (avoid user confusion)!
//wizard()->hide();
}
void OCC::Flow2AuthCredsPage::cleanupPage()
{
// The next or back button was activated, show the wizard again
wizard()->show();
_asyncAuth.reset();
// Forget sensitive data
_appPassword.clear();
_user.clear();
}
void Flow2AuthCredsPage::asyncAuthResult(Flow2Auth::Result r, const QString &user,
const QString &appPassword)
{
switch (r) {
case Flow2Auth::NotSupported: {
/* Flow2Auth not supported (can't open browser) */
_ui.errorLabel->setText(tr("Unable to open the Browser, please copy the link to your Browser."));
_ui.errorLabel->show();
/* Don't fallback to HTTP credentials */
/*OwncloudWizard *ocWizard = qobject_cast<OwncloudWizard *>(wizard());
ocWizard->back();
ocWizard->setAuthType(DetermineAuthTypeJob::Basic);*/
break;
}
case Flow2Auth::Error:
/* Error while getting the access token. (Timeout, or the server did not accept our client credentials */
_ui.errorLabel->show();
wizard()->show();
break;
case Flow2Auth::LoggedIn: {
_user = user;
_appPassword = appPassword;
OwncloudWizard *ocWizard = qobject_cast<OwncloudWizard *>(wizard());
Q_ASSERT(ocWizard);
emit connectToOCUrl(ocWizard->account()->url().toString());
break;
}
}
}
int Flow2AuthCredsPage::nextId() const
{
return WizardCommon::Page_AdvancedSetup;
}
void Flow2AuthCredsPage::setConnected()
{
wizard()->show();
}
AbstractCredentials *Flow2AuthCredsPage::getCredentials() const
{
OwncloudWizard *ocWizard = qobject_cast<OwncloudWizard *>(wizard());
Q_ASSERT(ocWizard);
return new WebFlowCredentials(
_user,
_appPassword,
ocWizard->_clientSslCertificate,
ocWizard->_clientSslKey,
ocWizard->_clientSslCaCertificates
);
}
bool Flow2AuthCredsPage::isComplete() const
{
return false; /* We can never go forward manually */
}
void Flow2AuthCredsPage::slotOpenBrowser()
{
if (_ui.errorLabel)
_ui.errorLabel->hide();
if (_asyncAuth)
_asyncAuth->openBrowser();
}
void Flow2AuthCredsPage::slotCopyLinkToClipboard()
{
if (_asyncAuth)
QApplication::clipboard()->setText(_asyncAuth->authorisationLink().toString(QUrl::FullyEncoded));
}
} // namespace OCC

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) by Olivier Goffart <ogoffart@woboq.com>
* Copyright (C) by Michael Schuster <michael@nextcloud.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#pragma once
#include <QList>
#include <QMap>
#include <QNetworkCookie>
#include <QUrl>
#include <QPointer>
#include "wizard/abstractcredswizardpage.h"
#include "accountfwd.h"
#include "creds/flow2auth.h"
#include "ui_flow2authcredspage.h"
namespace OCC {
class Flow2AuthCredsPage : public AbstractCredentialsWizardPage
{
Q_OBJECT
public:
Flow2AuthCredsPage();
AbstractCredentials *getCredentials() const override;
void initializePage() override;
void cleanupPage() override;
int nextId() const override;
void setConnected();
bool isComplete() const override;
public Q_SLOTS:
void asyncAuthResult(Flow2Auth::Result, const QString &user, const QString &appPassword);
signals:
void connectToOCUrl(const QString &);
public:
QString _user;
QString _appPassword;
QScopedPointer<Flow2Auth> _asyncAuth;
Ui_Flow2AuthCredsPage _ui;
protected slots:
void slotOpenBrowser();
void slotCopyLinkToClipboard();
};
} // namespace OCC

View File

@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Flow2AuthCredsPage</class>
<widget class="QWidget" name="Flow2AuthCredsPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>424</width>
<height>373</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="topLabel">
<property name="text">
<string notr="true">TextLabel</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Please switch to your browser to proceed.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="errorLabel">
<property name="text">
<string>An error occurred while connecting. Please try again.</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
</widget>
</item>
<item>
<widget class="QCommandLinkButton" name="openLinkButton">
<property name="text">
<string>Re-open Browser</string>
</property>
</widget>
</item>
<item>
<widget class="QCommandLinkButton" name="copyLinkButton">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>Copy link</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>127</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="bottomLabel">
<property name="text">
<string notr="true">TextLabel</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,115 @@
/*
* 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 "flow2authwidget.h"
#include <QDesktopServices>
#include <QProgressBar>
#include <QLoggingCategory>
#include <QLocale>
#include <QMessageBox>
#include <QMenu>
#include <QClipboard>
#include "common/utility.h"
#include "account.h"
#include "theme.h"
#include "wizard/owncloudwizardcommon.h"
namespace OCC {
Q_LOGGING_CATEGORY(lcFlow2AuthWidget, "gui.wizard.flow2authwidget", QtInfoMsg)
Flow2AuthWidget::Flow2AuthWidget(Account *account, QWidget *parent)
: QWidget(parent),
_account(account),
_ui()
{
_ui.setupUi(this);
Theme *theme = Theme::instance();
_ui.topLabel->hide();
_ui.bottomLabel->hide();
QVariant variant = theme->customMedia(Theme::oCSetupTop);
WizardCommon::setupCustomMedia(variant, _ui.topLabel);
variant = theme->customMedia(Theme::oCSetupBottom);
WizardCommon::setupCustomMedia(variant, _ui.bottomLabel);
WizardCommon::initErrorLabel(_ui.errorLabel);
connect(_ui.openLinkButton, &QCommandLinkButton::clicked, this, &Flow2AuthWidget::slotOpenBrowser);
connect(_ui.copyLinkButton, &QCommandLinkButton::clicked, this, &Flow2AuthWidget::slotCopyLinkToClipboard);
_asyncAuth.reset(new Flow2Auth(_account, this));
connect(_asyncAuth.data(), &Flow2Auth::result, this, &Flow2AuthWidget::asyncAuthResult, Qt::QueuedConnection);
_asyncAuth->start();
}
void Flow2AuthWidget::asyncAuthResult(Flow2Auth::Result r, const QString &user,
const QString &appPassword)
{
switch (r) {
case Flow2Auth::NotSupported:
/* Flow2Auth can't open browser */
_ui.errorLabel->setText(tr("Unable to open the Browser, please copy the link to your Browser."));
_ui.errorLabel->show();
break;
case Flow2Auth::Error:
/* Error while getting the access token. (Timeout, or the server did not accept our client credentials */
_ui.errorLabel->show();
break;
case Flow2Auth::LoggedIn: {
_user = user;
_appPassword = appPassword;
emit urlCatched(_user, _appPassword, QString());
break;
}
}
}
void Flow2AuthWidget::setError(const QString &error) {
if (error.isEmpty()) {
_ui.errorLabel->hide();
} else {
_ui.errorLabel->setText(error);
_ui.errorLabel->show();
}
}
Flow2AuthWidget::~Flow2AuthWidget() {
_asyncAuth.reset();
// Forget sensitive data
_appPassword.clear();
_user.clear();
}
void Flow2AuthWidget::slotOpenBrowser()
{
if (_ui.errorLabel)
_ui.errorLabel->hide();
if (_asyncAuth)
_asyncAuth->openBrowser();
}
void Flow2AuthWidget::slotCopyLinkToClipboard()
{
if (_asyncAuth)
QApplication::clipboard()->setText(_asyncAuth->authorisationLink().toString(QUrl::FullyEncoded));
}
} // namespace OCC

View File

@@ -0,0 +1,56 @@
/*
* 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.
*/
#ifndef FLOW2AUTHWIDGET_H
#define FLOW2AUTHWIDGET_H
#include <QUrl>
#include <QWidget>
#include "creds/flow2auth.h"
#include "ui_flow2authwidget.h"
namespace OCC {
class Flow2AuthWidget : public QWidget
{
Q_OBJECT
public:
Flow2AuthWidget(Account *account, QWidget *parent = nullptr);
virtual ~Flow2AuthWidget();
void setError(const QString &error);
public Q_SLOTS:
void asyncAuthResult(Flow2Auth::Result, const QString &user, const QString &appPassword);
signals:
void urlCatched(const QString user, const QString pass, const QString host);
private:
Account *_account;
QString _user;
QString _appPassword;
QScopedPointer<Flow2Auth> _asyncAuth;
Ui_Flow2AuthWidget _ui;
protected slots:
void slotOpenBrowser();
void slotCopyLinkToClipboard();
};
}
#endif // FLOW2AUTHWIDGET_H

View File

@@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Flow2AuthWidget</class>
<widget class="QWidget" name="Flow2AuthWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<height>280</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>500</width>
<height>280</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="topLabel">
<property name="text">
<string notr="true">TextLabel</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Please switch to your browser to proceed.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="errorLabel">
<property name="text">
<string>An error occurred while connecting. Please try again.</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
</widget>
</item>
<item>
<widget class="QCommandLinkButton" name="openLinkButton">
<property name="text">
<string>Re-open Browser</string>
</property>
</widget>
</item>
<item>
<widget class="QCommandLinkButton" name="copyLinkButton">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>Copy link</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>127</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="bottomLabel">
<property name="text">
<string notr="true">TextLabel</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -382,7 +382,7 @@ qint64 OwncloudAdvancedSetupPage::availableLocalSpace() const
QString OwncloudAdvancedSetupPage::checkLocalSpace(qint64 remoteSize) const
{
return (availableLocalSpace()>remoteSize) ? QString() : tr("There is no enough free space in the local folder!");
return (availableLocalSpace()>remoteSize) ? QString() : tr("There isn't enough free space in the local folder!");
}
} // namespace OCC

View File

@@ -17,7 +17,7 @@
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
<string notr="true">Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
@@ -29,7 +29,7 @@
</sizepolicy>
</property>
<property name="text">
<string>TextLabel</string>
<string notr="true">TextLabel</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
@@ -97,7 +97,7 @@
<item>
<widget class="QLabel" name="lSyncEverythingSizeLabel">
<property name="text">
<string>TextLabel</string>
<string notr="true">TextLabel</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
@@ -202,7 +202,7 @@
<item>
<widget class="QLabel" name="lSelectiveSyncSizeLabel">
<property name="text">
<string>TextLabel</string>
<string notr="true">TextLabel</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
@@ -232,7 +232,7 @@
<item>
<widget class="QLabel" name="lLocalIcon">
<property name="text">
<string>TextLabel</string>
<string notr="true">TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
@@ -282,7 +282,7 @@
</sizepolicy>
</property>
<property name="text">
<string>pbSelectLocalFolder</string>
<string notr="true">pbSelectLocalFolder</string>
</property>
</widget>
</item>
@@ -291,7 +291,7 @@
<item>
<widget class="QLabel" name="lServerIcon">
<property name="text">
<string>TextLabel</string>
<string notr="true">TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
@@ -383,7 +383,7 @@
<item row="4" column="1">
<widget class="QLabel" name="lFreeSpace">
<property name="text">
<string>TextLabel</string>
<string notr="true">TextLabel</string>
</property>
</widget>
</item>
@@ -392,7 +392,7 @@
<item>
<widget class="QLabel" name="errorLabel">
<property name="text">
<string>TextLabel</string>
<string notr="true">TextLabel</string>
</property>
</widget>
</item>
@@ -418,7 +418,7 @@
<item>
<widget class="QLabel" name="bottomLabel">
<property name="text">
<string>TextLabel</string>
<string notr="true">TextLabel</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>

View File

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

View File

@@ -45,21 +45,8 @@ OwncloudOAuthCredsPage::OwncloudOAuthCredsPage()
setTitle(WizardCommon::titleTemplate().arg(tr("Connect to %1").arg(Theme::instance()->appNameGUI())));
setSubTitle(WizardCommon::subTitleTemplate().arg(tr("Login in your browser")));
connect(_ui.openLinkButton, &QCommandLinkButton::clicked, [this] {
_ui.errorLabel->hide();
if (_asyncAuth)
_asyncAuth->openBrowser();
});
_ui.openLinkButton->setContextMenuPolicy(Qt::CustomContextMenu);
QObject::connect(_ui.openLinkButton, &QWidget::customContextMenuRequested, [this](const QPoint &pos) {
auto menu = new QMenu(_ui.openLinkButton);
menu->addAction(tr("Copy link to clipboard"), this, [this] {
if (_asyncAuth)
QApplication::clipboard()->setText(_asyncAuth->authorisationLink().toString(QUrl::FullyEncoded));
});
menu->setAttribute(Qt::WA_DeleteOnClose);
menu->popup(_ui.openLinkButton->mapToGlobal(pos));
});
connect(_ui.openLinkButton, &QCommandLinkButton::clicked, this, &OwncloudOAuthCredsPage::slotOpenBrowser);
connect(_ui.copyLinkButton, &QCommandLinkButton::clicked, this, &OwncloudOAuthCredsPage::slotCopyLinkToClipboard);
}
void OwncloudOAuthCredsPage::initializePage()
@@ -70,7 +57,9 @@ void OwncloudOAuthCredsPage::initializePage()
_asyncAuth.reset(new OAuth(ocWizard->account().data(), this));
connect(_asyncAuth.data(), &OAuth::result, this, &OwncloudOAuthCredsPage::asyncAuthResult, Qt::QueuedConnection);
_asyncAuth->start();
wizard()->hide();
// Don't hide the wizard (avoid user confusion)!
//wizard()->hide();
}
void OCC::OwncloudOAuthCredsPage::cleanupPage()
@@ -131,4 +120,19 @@ bool OwncloudOAuthCredsPage::isComplete() const
return false; /* We can never go forward manually */
}
void OwncloudOAuthCredsPage::slotOpenBrowser()
{
if (_ui.errorLabel)
_ui.errorLabel->hide();
if (_asyncAuth)
_asyncAuth->openBrowser();
}
void OwncloudOAuthCredsPage::slotCopyLinkToClipboard()
{
if (_asyncAuth)
QApplication::clipboard()->setText(_asyncAuth->authorisationLink().toString(QUrl::FullyEncoded));
}
} // namespace OCC

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