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

Compare commits

...

163 Commits

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

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

Signed-off-by: Camila San <hello@camila.codes>
2019-07-24 17:08:31 +00:00
Nextcloud bot
50be2e067c [tx-robot] updated from transifex 2019-07-22 03:06:58 +00: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
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
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
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
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
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
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
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
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
112 changed files with 27900 additions and 11569 deletions

View File

@@ -1,228 +1,347 @@
clone: kind: pipeline
git: name: qt-5.7
image: plugins/git
tags: true
depth: 1
pipeline: steps:
qt-5.7: - name: build and test
image: nextcloudci/client-5.7:client-5.7-4 image: nextcloudci/client-5.7:client-5.7-4
commands: commands:
# Install QtKeyChain # Install QtKeyChain
- /bin/bash -c " - /bin/bash -c "
source /opt/qt57/bin/qt57-env.sh && source /opt/qt57/bin/qt57-env.sh &&
cd /tmp && cd /tmp &&
git clone https://github.com/frankosterfeld/qtkeychain.git && git clone https://github.com/frankosterfeld/qtkeychain.git &&
cd qtkeychain && cd qtkeychain &&
git checkout v0.9.1 && git checkout v0.9.1 &&
mkdir build && mkdir build &&
cd build && cd build &&
cmake ../ && cmake ../ &&
make && make &&
make install" make install"
# Build client # Build client
- /bin/bash -c " - /bin/bash -c "
source /opt/qt57/bin/qt57-env.sh && source /opt/qt57/bin/qt57-env.sh &&
mkdir build && mkdir build &&
cd build && cd build &&
cmake -D NO_SHIBBOLETH=1 -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=1 ../ && cmake -D NO_SHIBBOLETH=1 -DCMAKE_BUILD_TYPE=Debug -DUNIT_TESTING=1 ../ &&
make && make &&
useradd -m -s /bin/bash test && useradd -m -s /bin/bash test &&
chown -R test:test . && chown -R test:test . &&
su -c 'ctest --output-on-failure' test" su -c 'ctest --output-on-failure' test"
when: trigger:
matrix: branch:
TESTS: qt-5.7 - master
qt-5.8: event:
image: nextcloudci/client-5.8:client-5.8-4 - pull_request
commands: - push
# 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
qt-5.11-clang: ---
image: nextcloudci/client-5.11:client-5.11-3 kind: pipeline
commands: name: qt-5.8
# 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
AppImage: steps:
image: nextcloudci/client-5.11:client-5.11-3 - name: build and test
commands: image: nextcloudci/client-5.8:client-5.8-4
- /bin/bash -c "./admin/linux/build-appimage.sh" commands:
when: # Install QtKeyChain
matrix: - /bin/bash -c "
BUILD: AppImage 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 kind: pipeline
commands: name: qt-5.9
- /bin/bash -c "./admin/linux/debian/drone-build.sh"
secrets: [ DEBIAN_SECRET_KEY, DEBIAN_SECRET_IV ]
when:
matrix:
BUILD: Debian
documentation: steps:
image: nextcloudci/documentation:documentation-5 - name: build and test
commands: image: nextcloudci/client-5.9:client-5.9-5
- cd doc commands:
- make html # Install QtKeyChain
when: - /bin/bash -c "
matrix: source /opt/qt59/bin/qt59-env.sh &&
TESTS: documentation 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: kind: pipeline
- TESTS: qt-5.7 name: qt-5.10
- 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
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: 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 Dear user,
The latest version can be seen by checking https://download.nextcloud.com/desktop/ Please understand that at the moment, we are very busy with customer issues
For support try our forums: https://help.nextcloud.com 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 ### Expected behaviour
Tell us what should happen Tell us what should happen
@@ -18,6 +23,11 @@ Tell us what happens instead
### Client configuration ### Client configuration
Client version: 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: Operating system:
@@ -34,15 +44,6 @@ Installation path of client:
<!--- <!---
Optional section. It depends on the issue. Optional section. It depends on the issue.
---> --->
Operating system:
Web server:
Database:
PHP version:
Nextcloud version: Nextcloud version:
Storage backend (external storage): 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 Please use Gist (https://gist.github.com/) or a similar code paster for longer
logs. logs.
```Template for output < 10 lines```
1. Client logfile: Output of `nextcloud --logwindow` or `nextcloud --logfile log.txt` 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) (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) (See also https://docs.nextcloud.com/desktop/2.3/troubleshooting.html#log-files)

1
.gitignore vendored
View File

@@ -171,3 +171,4 @@ CPackConfig.cmake
CPackOptions.cmake CPackOptions.cmake
CPackSourceConfig.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 # Translations
Icon[ca]=@APPLICATION_ICON_NAME@ 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 Comment[ca]=@APPLICATION_NAME@ client de sincronització d'escriptori
GenericName[ca]=Directori de sincronització GenericName[ca]=Directori de sincronització

View File

@@ -201,4 +201,4 @@ X-GNOME-Autostart-Delay=3
Icon[da]=@APPLICATION_ICON_NAME@ Icon[da]=@APPLICATION_ICON_NAME@
Name[da]=@APPLICATION_NAME@ Arbejdsstationsssynkroniseringsklient Name[da]=@APPLICATION_NAME@ Arbejdsstationsssynkroniseringsklient
Comment[da]=@APPLICATION_NAME@ Arbejdsstationsssynkroniseringsklient Comment[da]=@APPLICATION_NAME@ Arbejdsstationsssynkroniseringsklient
GenericName[da]=Mappe sync GenericName[da]=Mappesynkronisering

View File

@@ -198,7 +198,7 @@ X-GNOME-Autostart-Delay=3
# Translations # Translations
Icon[de_DE]=@APPLICATION_ICON_NAME@ Icon[de]=@APPLICATION_ICON_NAME@
Name[de_DE]=@APPLICATION_NAME@ Client zur Desktop-Synchronisation Name[de]=@APPLICATION_NAME@ Client zur Desktop-Synchronisation
Comment[de_DE]=@APPLICATION_NAME@ Client zur Desktop-Synchronisation Comment[de]=@APPLICATION_NAME@ Client zur Desktop-Synchronisation
GenericName[de_DE]=Synchronisationsordner GenericName[de]=Synchronisationsordner

View File

@@ -201,4 +201,4 @@ X-GNOME-Autostart-Delay=3
Icon[pt_BR]=@APPLICATION_ICON_NAME@ Icon[pt_BR]=@APPLICATION_ICON_NAME@
Name[pt_BR]=@APPLICATION_NAME@ cliente de sincronização desktop Name[pt_BR]=@APPLICATION_NAME@ cliente de sincronização desktop
Comment[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

@@ -199,6 +199,6 @@ X-GNOME-Autostart-Delay=3
# Translations # Translations
Icon[sk_SK]=@APPLICATION_ICON_NAME@ Icon[sk_SK]=@APPLICATION_ICON_NAME@
Name[sk_SK]=@APPLICATION_NAME@ Synchronizačný klient pre PC Name[sk_SK]=@APPLICATION_NAME@ synchronizačný klient pre PC
Comment[sk_SK]=@APPLICATION_NAME@ Synchronizačný klient pre PC Comment[sk_SK]=@APPLICATION_NAME@ synchronizačný klient pre PC
GenericName[sk_SK]=Synchnonizácia priečinka GenericName[sk_SK]=Synchnonizácia priečinkov

View File

@@ -24,33 +24,27 @@ $ cd build
``` ```
##### Compile and install ##### Compile and install
For development reasons it is better to install the client on user space :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:
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/`.
##### 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 $ make install
``` ```
##### Windows ##### 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 $ cmake --build . --config Debug --target install
``` ```
##### Mac OS :information_source: More detailed instructions can be found at the [Desktop Client Wiki](https://github.com/nextcloud/desktop/wiki).
```
$ 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).
### :inbox_tray: Where to find binaries to download ### :inbox_tray: Where to find binaries to download

View File

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

View File

@@ -5,8 +5,8 @@ set -xe
mkdir /app mkdir /app
mkdir /build mkdir /build
#Set Qt-5.11 #Set Qt-5.12
export QT_BASE_DIR=/opt/qt511 export QT_BASE_DIR=/opt/qt512
export QTDIR=$QT_BASE_DIR export QTDIR=$QT_BASE_DIR
export PATH=$QT_BASE_DIR/bin:$PATH export PATH=$QT_BASE_DIR/bin:$PATH
export LD_LIBRARY_PATH=$QT_BASE_DIR/lib/x86_64-linux-gnu:$QT_BASE_DIR/lib:$LD_LIBRARY_PATH export LD_LIBRARY_PATH=$QT_BASE_DIR/lib/x86_64-linux-gnu:$QT_BASE_DIR/lib:$LD_LIBRARY_PATH

View File

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

BIN
resources/add.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

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

@@ -165,6 +165,7 @@ set(3rdparty_SRC
../3rdparty/qtsingleapplication/qtlocalpeer.cpp ../3rdparty/qtsingleapplication/qtlocalpeer.cpp
../3rdparty/qtsingleapplication/qtsingleapplication.cpp ../3rdparty/qtsingleapplication/qtsingleapplication.cpp
../3rdparty/qtsingleapplication/qtsinglecoreapplication.cpp ../3rdparty/qtsingleapplication/qtsinglecoreapplication.cpp
../3rdparty/kmessagewidget/kmessagewidget.cpp
) )
if (APPLE) if (APPLE)
@@ -329,6 +330,7 @@ target_include_directories(${APPLICATION_EXECUTABLE} PRIVATE
${CMAKE_SOURCE_DIR}/src/3rdparty/qtlockedfile ${CMAKE_SOURCE_DIR}/src/3rdparty/qtlockedfile
${CMAKE_SOURCE_DIR}/src/3rdparty/qtmacgoodies/src ${CMAKE_SOURCE_DIR}/src/3rdparty/qtmacgoodies/src
${CMAKE_SOURCE_DIR}/src/3rdparty/qtsingleapplication ${CMAKE_SOURCE_DIR}/src/3rdparty/qtsingleapplication
${CMAKE_SOURCE_DIR}/src/3rdparty/kmessagewidget
${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}
) )

View File

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

View File

@@ -194,6 +194,14 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
// Connect E2E stuff // Connect E2E stuff
connect(this, &AccountSettings::requesetMnemonic, _accountState->account()->e2e(), &ClientSideEncryption::slotRequestMnemonic); connect(this, &AccountSettings::requesetMnemonic, _accountState->account()->e2e(), &ClientSideEncryption::slotRequestMnemonic);
connect(_accountState->account()->e2e(), &ClientSideEncryption::showMnemonic, this, &AccountSettings::slotShowMnemonic); 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 +230,19 @@ void AccountSettings::createAccountToolbox()
slotAccountAdded(_accountState); 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() { void AccountSettings::slotMenuBeforeShow() {
if (_menuShown) { if (_menuShown) {
return; return;

View File

@@ -99,6 +99,7 @@ protected slots:
// Encryption Related Stuff. // Encryption Related Stuff.
void slotShowMnemonic(const QString &mnemonic); void slotShowMnemonic(const QString &mnemonic);
void slotNewMnemonicGenerated();
void slotEncryptionFlagSuccess(const QByteArray &folderId); void slotEncryptionFlagSuccess(const QByteArray &folderId);
void slotEncryptionFlagError(const QByteArray &folderId, int httpReturnCode); void slotEncryptionFlagError(const QByteArray &folderId, int httpReturnCode);

View File

@@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>575</width> <width>582</width>
<height>557</height> <height>557</height>
</rect> </rect>
</property> </property>
@@ -14,128 +14,7 @@
<string>Form</string> <string>Form</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="0" column="0"> <item row="4" 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">
<widget class="QWidget" name="selectiveSyncStatus" native="true"> <widget class="QWidget" name="selectiveSyncStatus" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_3"> <layout class="QHBoxLayout" name="horizontalLayout_3">
<item> <item>
@@ -267,6 +146,130 @@
</layout> </layout>
</widget> </widget>
</item> </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> </layout>
</widget> </widget>
<customwidgets> <customwidgets>
@@ -280,6 +283,12 @@
<extends>QTreeView</extends> <extends>QTreeView</extends>
<header>folderstatusview.h</header> <header>folderstatusview.h</header>
</customwidget> </customwidget>
<customwidget>
<class>KMessageWidget</class>
<extends>QWidget</extends>
<header>kmessagewidget.h</header>
<container>1</container>
</customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>
<connections/> <connections/>

View File

@@ -105,7 +105,8 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
} else if(a._status == SyncFileItem::SoftError } else if(a._status == SyncFileItem::SoftError
|| a._status == SyncFileItem::FileIgnored || a._status == SyncFileItem::FileIgnored
|| a._status == SyncFileItem::Conflict || a._status == SyncFileItem::Conflict
|| a._status == SyncFileItem::Restoration){ || a._status == SyncFileItem::Restoration
|| a._status == SyncFileItem::FileLocked){
return QIcon(QLatin1String(":/client/resources/state-warning.svg")); return QIcon(QLatin1String(":/client/resources/state-warning.svg"));
} }
return QIcon(QLatin1String(":/client/resources/state-sync.svg")); return QIcon(QLatin1String(":/client/resources/state-sync.svg"));

View File

@@ -121,6 +121,11 @@ void ActivityWidget::slotProgressInfo(const QString &folder, const ProgressInfo
continue; 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()){ if(activity._status == SyncFileItem::FileIgnored && !QFileInfo(f->path() + activity._file).exists()){
_model->removeActivityFromActivityList(activity); _model->removeActivityFromActivityList(activity);

View File

@@ -24,7 +24,7 @@ extern "C" {
CloudProvidersProviderExporter *_providerExporter; 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); Q_UNUSED(name);
CloudProviderManager *self; CloudProviderManager *self;
@@ -34,6 +34,14 @@ void on_bus_acquired (GDBusConnection *connection, const gchar *name, gpointer u
self->registerSignals(); 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() void CloudProviderManager::registerSignals()
{ {
OCC::FolderMan *folderManager = OCC::FolderMan::instance(); OCC::FolderMan *folderManager = OCC::FolderMan::instance();
@@ -45,7 +53,7 @@ CloudProviderManager::CloudProviderManager(QObject *parent) : QObject(parent)
{ {
_map = new QMap<QString, CloudProviderWrapper*>(); _map = new QMap<QString, CloudProviderWrapper*>();
QString busName = QString(LIBCLOUDPROVIDERS_DBUS_BUS_NAME); 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) void CloudProviderManager::slotFolderListChanged(const Folder::Map &folderMap)

View File

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

View File

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

View File

@@ -351,6 +351,10 @@ void Folder::showSyncResultPopup()
createGuiLog(_syncResult.firstItemError()->_file, LogStatusError, errorCount); 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()); qCInfo(lcFolder) << "Folder sync result: " << int(_syncResult.status());
} }

View File

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

View File

@@ -75,9 +75,27 @@ bool FolderWatcher::isReliable() const
return _isReliable; 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) void FolderWatcher::changeDetected(const QString &path)
{ {
QFileInfo fileInfo(path);
QStringList paths(path); QStringList paths(path);
if (fileInfo.isDir()) {
QDir dir(path);
appendSubPaths(dir, paths);
}
changeDetected(paths); changeDetected(paths);
} }

View File

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

View File

@@ -21,7 +21,7 @@ namespace OCC {
OcsShareeJob::OcsShareeJob(AccountPtr account) OcsShareeJob::OcsShareeJob(AccountPtr account)
: OcsJob(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); connect(this, &OcsJob::jobFinished, this, &OcsShareeJob::jobDone);
} }

View File

@@ -24,7 +24,7 @@ namespace OCC {
OcsShareJob::OcsShareJob(AccountPtr account) OcsShareJob::OcsShareJob(AccountPtr account)
: OcsJob(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); connect(this, &OcsJob::jobFinished, this, &OcsShareJob::jobDone);
} }
@@ -33,6 +33,7 @@ void OcsShareJob::getShares(const QString &path)
setVerb("GET"); setVerb("GET");
addParam(QString::fromLatin1("path"), path); addParam(QString::fromLatin1("path"), path);
addParam(QString::fromLatin1("reshares"), QString("true"));
addPassStatusCode(404); addPassStatusCode(404);
start(); start();

View File

@@ -18,6 +18,8 @@
#include "sharelinkwidget.h" #include "sharelinkwidget.h"
#include "shareusergroupwidget.h" #include "shareusergroupwidget.h"
#include "sharemanager.h"
#include "account.h" #include "account.h"
#include "accountstate.h" #include "accountstate.h"
#include "configfile.h" #include "configfile.h"
@@ -49,7 +51,8 @@ ShareDialog::ShareDialog(QPointer<AccountState> accountState,
, _maxSharingPermissions(maxSharingPermissions) , _maxSharingPermissions(maxSharingPermissions)
, _privateLinkUrl(accountState->account()->deprecatedPrivateLinkUrl(numericFileId).toString(QUrl::FullyEncoded)) , _privateLinkUrl(accountState->account()->deprecatedPrivateLinkUrl(numericFileId).toString(QUrl::FullyEncoded))
, _startPage(startPage) , _startPage(startPage)
, _linkWidget(nullptr) , _linkWidgetList({})
, _emptyShareLinkWidget(nullptr)
, _userGroupWidget(nullptr) , _userGroupWidget(nullptr)
, _progressIndicator(nullptr) , _progressIndicator(nullptr)
{ {
@@ -101,11 +104,6 @@ ShareDialog::ShareDialog(QPointer<AccountState> accountState,
this->setWindowTitle(tr("%1 Sharing").arg(Theme::instance()->appNameGUI())); this->setWindowTitle(tr("%1 Sharing").arg(Theme::instance()->appNameGUI()));
if (!accountState->account()->capabilities().shareAPI()) { 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; return;
} }
@@ -115,14 +113,6 @@ ShareDialog::ShareDialog(QPointer<AccountState> accountState,
job->start(); 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); auto job = new PropfindJob(accountState->account(), _sharePath);
job->setProperties( job->setProperties(
QList<QByteArray>() QList<QByteArray>()
@@ -133,10 +123,103 @@ ShareDialog::ShareDialog(QPointer<AccountState> accountState,
connect(job, &PropfindJob::result, this, &ShareDialog::slotPropfindReceived); connect(job, &PropfindJob::result, this, &ShareDialog::slotPropfindReceived);
connect(job, &PropfindJob::finishedWithError, this, &ShareDialog::slotPropfindError); connect(job, &PropfindJob::finishedWithError, this, &ShareDialog::slotPropfindError);
job->start(); 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() ShareDialog::~ShareDialog()
{ {
_linkWidgetList.clear();
delete _ui; delete _ui;
} }
@@ -178,8 +261,6 @@ void ShareDialog::slotPropfindError()
void ShareDialog::showSharingUi() void ShareDialog::showSharingUi()
{ {
//_progressIndicator->stopAnimation();
auto theme = Theme::instance(); auto theme = Theme::instance();
// There's no difference between being unable to reshare and // There's no difference between being unable to reshare and
@@ -190,6 +271,7 @@ void ShareDialog::showSharingUi()
auto label = new QLabel(this); auto label = new QLabel(this);
label->setText(tr("The file can not be shared because it was shared without sharing permission.")); label->setText(tr("The file can not be shared because it was shared without sharing permission."));
label->setWordWrap(true); label->setWordWrap(true);
_ui->verticalLayout->insertWidget(1, label);
return; return;
} }
@@ -205,15 +287,25 @@ void ShareDialog::showSharingUi()
} }
if (theme->linkSharing()) { if (theme->linkSharing()) {
_linkWidget = new ShareLinkWidget(_accountState->account(), _sharePath, _localPath, _maxSharingPermissions, this); _manager->fetchShares(_sharePath);
_ui->verticalLayout->insertWidget(2, _linkWidget);
_linkWidget->getShares();
if (_startPage == ShareDialogStartPage::PublicLinks)
_ui->verticalLayout->insertWidget(3, _linkWidget);
} }
} }
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) void ShareDialog::slotThumbnailFetched(const int &statusCode, const QByteArray &reply)
{ {
if (statusCode != 200) { if (statusCode != 200) {
@@ -237,8 +329,10 @@ void ShareDialog::slotAccountStateChanged(int state)
_userGroupWidget->setEnabled(enabled); _userGroupWidget->setEnabled(enabled);
} }
if (_linkWidget != nullptr) { if(_linkWidgetList.size() > 0){
_linkWidget->setEnabled(enabled); foreach(ShareLinkWidget *widget, _linkWidgetList){
widget->setEnabled(state);
}
} }
} }
} }

View File

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

View File

@@ -17,9 +17,8 @@
#include "sharelinkwidget.h" #include "sharelinkwidget.h"
#include "account.h" #include "account.h"
#include "capabilities.h" #include "capabilities.h"
#include "sharemanager.h"
#include "guiutility.h" #include "guiutility.h"
#include "sharemanager.h"
#include "QProgressIndicator.h" #include "QProgressIndicator.h"
#include <QBuffer> #include <QBuffer>
@@ -29,9 +28,12 @@
#include <QMessageBox> #include <QMessageBox>
#include <QMenu> #include <QMenu>
#include <QToolButton> #include <QToolButton>
#include <QPropertyAnimation>
namespace OCC { namespace OCC {
Q_LOGGING_CATEGORY(lcShareLink, "nextcloud.gui.sharelink", QtInfoMsg)
ShareLinkWidget::ShareLinkWidget(AccountPtr account, ShareLinkWidget::ShareLinkWidget(AccountPtr account,
const QString &sharePath, const QString &sharePath,
const QString &localPath, const QString &localPath,
@@ -42,13 +44,11 @@ ShareLinkWidget::ShareLinkWidget(AccountPtr account,
, _account(account) , _account(account)
, _sharePath(sharePath) , _sharePath(sharePath)
, _localPath(localPath) , _localPath(localPath)
, _manager(nullptr)
, _linkShare(nullptr) , _linkShare(nullptr)
, _passwordRequired(false) , _passwordRequired(false)
, _expiryRequired(false) , _expiryRequired(false)
, _namesSupported(true) , _namesSupported(true)
, _linkContextMenu(nullptr) , _linkContextMenu(nullptr)
, _copyLinkAction(nullptr)
, _readOnlyLinkAction(nullptr) , _readOnlyLinkAction(nullptr)
, _allowEditingLinkAction(nullptr) , _allowEditingLinkAction(nullptr)
, _allowUploadEditingLinkAction(nullptr) , _allowUploadEditingLinkAction(nullptr)
@@ -68,26 +68,26 @@ ShareLinkWidget::ShareLinkWidget(AccountPtr account,
QFileInfo fi(localPath); QFileInfo fi(localPath);
_isFile = fi.isFile(); _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->lineEdit_password, &QLineEdit::returnPressed, this, &ShareLinkWidget::slotCreatePassword);
connect(_ui->confirmPassword, &QAbstractButton::clicked, this, &ShareLinkWidget::slotCreatePassword); connect(_ui->confirmPassword, &QAbstractButton::clicked, this, &ShareLinkWidget::slotCreatePassword);
connect(_ui->confirmExpirationDate, &QAbstractButton::clicked, this, &ShareLinkWidget::slotSetExpireDate); 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(); _ui->errorLabel->hide();
bool sharingPossible = true; bool sharingPossible = true;
if (!_account->capabilities().sharePublicLink()) { if (!_account->capabilities().sharePublicLink()) {
qCWarning(lcSharing) << "Link shares have been disabled"; qCWarning(lcShareLink) << "Link shares have been disabled";
sharingPossible = false; sharingPossible = false;
} else if (!(maxSharingPermissions & SharePermissionShare)) { } 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; sharingPossible = false;
} }
_ui->createShareButton->setVisible(sharingPossible); _ui->enableShareLink->setChecked(false);
_ui->enableShareLink->setVisible(sharingPossible); _ui->shareLinkToolButton->setEnabled(false);
_ui->shareLinkToolButton->setVisible(sharingPossible); _ui->shareLinkToolButton->hide();
// Older servers don't support multiple public link shares // Older servers don't support multiple public link shares
if (!_account->capabilities().sharePublicLinkMultiple()) { if (!_account->capabilities().sharePublicLinkMultiple()) {
@@ -100,34 +100,9 @@ ShareLinkWidget::ShareLinkWidget(AccountPtr account,
// check if the file is already inside of a synced folder // check if the file is already inside of a synced folder
if (sharePath.isEmpty()) { if (sharePath.isEmpty()) {
// The file is not yet in an ownCloud synced folder. We could automatically qCWarning(lcShareLink) << "Unable to share files not in a sync folder.";
// 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.";
return; 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() ShareLinkWidget::~ShareLinkWidget()
@@ -135,7 +110,7 @@ ShareLinkWidget::~ShareLinkWidget()
delete _ui; delete _ui;
} }
void ShareLinkWidget::toggleAnimation(bool start){ void ShareLinkWidget::slotToggleAnimation(bool start){
if (start) { if (start) {
if (!_ui->progressIndicator->isAnimated()) if (!_ui->progressIndicator->isAnimated())
_ui->progressIndicator->startAnimation(); _ui->progressIndicator->startAnimation();
@@ -144,218 +119,170 @@ void ShareLinkWidget::toggleAnimation(bool start){
} }
} }
void ShareLinkWidget::getShares() void ShareLinkWidget::setLinkShare(QSharedPointer<LinkShare> linkShare){
{ _linkShare = linkShare;
if (_manager) {
toggleAnimation(true);
_manager->fetchShares(_sharePath);
}
} }
void ShareLinkWidget::slotSharesFetched(const QList<QSharedPointer<Share>> &shares) QSharedPointer<LinkShare> ShareLinkWidget::getLinkShare(){
{ return _linkShare;
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);
} }
void ShareLinkWidget::setExpireDate(const QDate &date) void ShareLinkWidget::setupUiOptions(){
{ connect(_linkShare.data(), &LinkShare::expireDateSet, this, &ShareLinkWidget::slotExpireDateSet);
if (_linkShare) { connect(_linkShare.data(), &LinkShare::passwordSet, this, &ShareLinkWidget::slotPasswordSet);
toggleAnimation(true); connect(_linkShare.data(), &LinkShare::passwordSetError, this, &ShareLinkWidget::slotPasswordSetError);
_ui->errorLabel->hide();
_linkShare->setExpireDate(date); // 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() void ShareLinkWidget::slotExpireDateSet()
{ {
toggleAnimation(false); slotToggleAnimation(false);
}
void ShareLinkWidget::slotExpireDateChanged(const QDate &date)
{
setExpireDate(date);
} }
void ShareLinkWidget::slotSetExpireDate() void ShareLinkWidget::slotSetExpireDate()
{ {
slotExpireDateChanged(_ui->calendar->date()); if(!_linkShare){
return;
}
slotToggleAnimation(true);
_ui->errorLabel->hide();
_linkShare->setExpireDate(_ui->calendar->date());
} }
void ShareLinkWidget::slotCreatePassword() void ShareLinkWidget::slotCreatePassword()
{ {
if (!_manager) {
return;
}
toggleAnimation(true);
if (!_linkShare) { 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; return;
} }
toggleAnimation(true); slotToggleAnimation(true);
_ui->errorLabel->hide();
if(checked){ _linkShare->setPassword(_ui->lineEdit_password->text());
_manager->createLinkShare(_sharePath, QString(), QString());
} else {
if (!_linkShare) {
qCWarning(lcSharing) << "No public link set.";
return;
}
confirmAndDeleteShare();
}
} }
void ShareLinkWidget::setPassword(const QString &password) void ShareLinkWidget::slotCreateShareLink(bool clicked)
{ {
if (_linkShare) { slotToggleAnimation(true);
toggleAnimation(true); emit createLinkShare();
_ui->errorLabel->hide();
_linkShare->setPassword(password);
}
} }
void ShareLinkWidget::slotPasswordSet() void ShareLinkWidget::slotPasswordSet()
{ {
if (!_linkShare)
return;
_ui->lineEdit_password->setText(QString()); _ui->lineEdit_password->setText(QString());
if (_linkShare->isPasswordSet()) { if (_linkShare->isPasswordSet()) {
_ui->lineEdit_password->setPlaceholderText("********"); _ui->lineEdit_password->setPlaceholderText("********");
@@ -364,39 +291,55 @@ void ShareLinkWidget::slotPasswordSet()
_ui->lineEdit_password->setPlaceholderText(QString()); _ui->lineEdit_password->setPlaceholderText(QString());
} }
toggleAnimation(false); slotToggleAnimation(false);
}
/* void ShareLinkWidget::startAnimation(const int start, const int end){
* 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 QPropertyAnimation *animation = new QPropertyAnimation(this, "maximumHeight", this);
* at this point.
* animation->setDuration(500);
* NOTE: I don't see this happening with oC > 10 animation->setStartValue(start);
*/ animation->setEndValue(end);
getShares();
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() void ShareLinkWidget::slotDeleteShareFetched()
{ {
toggleAnimation(true); slotToggleAnimation(false);
// TODO
//startAnimation(height(), 0);
_linkShare.clear(); _linkShare.clear();
_ui->enableShareLink->setChecked(false);
_ui->shareLinkToolButton->setEnabled(false);
_ui->shareLinkToolButton->hide();
togglePasswordOptions(false); togglePasswordOptions(false);
toggleExpireDateOptions(false); toggleExpireDateOptions(false);
getShares(); emit deleteLinkShare();
} }
void ShareLinkWidget::slotCreateShareFetched() void ShareLinkWidget::slotAnimationFinished()
{ {
toggleAnimation(true); emit resizeRequested();
getShares(); 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) void ShareLinkWidget::slotCreateShareRequiresPassword(const QString &message)
{ {
toggleAnimation(true); slotToggleAnimation(true);
showPasswordOptions(true); showPasswordOptions(true);
if (!message.isEmpty()) { if (!message.isEmpty()) {
@@ -468,8 +411,10 @@ void ShareLinkWidget::confirmAndDeleteShare()
connect(messageBox, &QMessageBox::finished, this, connect(messageBox, &QMessageBox::finished, this,
[messageBox, yesButton, this]() { [messageBox, yesButton, this]() {
if (messageBox->clickedButton() == yesButton) if (messageBox->clickedButton() == yesButton) {
this->slotToggleAnimation(true);
this->_linkShare->deleteShare(); this->_linkShare->deleteShare();
}
}); });
messageBox->open(); messageBox->open();
} }
@@ -495,8 +440,8 @@ void ShareLinkWidget::slotLinkContextMenuActionTriggered(QAction *action)
bool state = action->isChecked(); bool state = action->isChecked();
SharePermissions perm = SharePermissionRead; SharePermissions perm = SharePermissionRead;
if (action == _copyLinkAction) { if(action == _addAnotherLinkAction){
QApplication::clipboard()->setText(_linkShare->getLink().toString()); emit createLinkShare();
} else if (action == _readOnlyLinkAction && state) { } else if (action == _readOnlyLinkAction && state) {
_linkShare->setPermissions(perm); _linkShare->setPermissions(perm);
@@ -520,13 +465,13 @@ void ShareLinkWidget::slotLinkContextMenuActionTriggered(QAction *action)
toggleExpireDateOptions(state); toggleExpireDateOptions(state);
} else if (action == _unshareLinkAction) { } else if (action == _unshareLinkAction) {
slotCreateOrDeleteShareLink(state); confirmAndDeleteShare();
} }
} }
void ShareLinkWidget::slotServerError(int code, const QString &message) void ShareLinkWidget::slotServerError(int code, const QString &message)
{ {
toggleAnimation(false); slotToggleAnimation(false);
qCWarning(lcSharing) << "Error from server" << code << message; qCWarning(lcSharing) << "Error from server" << code << message;
displayError(message); displayError(message);

View File

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

View File

@@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>350</width> <width>350</width>
<height>126</height> <height>160</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@@ -29,80 +29,13 @@
<property name="bottomMargin"> <property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item row="0" column="0" colspan="3"> <item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout"> <widget class="QDateEdit" name="calendar">
<item> <property name="sizePolicy">
<widget class="QPushButton" name="createShareButton"> <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<property name="sizePolicy"> <horstretch>1</horstretch>
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> <verstretch>0</verstretch>
<horstretch>0</horstretch> </sizepolicy>
<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>
</property> </property>
</widget> </widget>
</item> </item>
@@ -122,64 +55,17 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="0" column="3">
<widget class="QLineEdit" name="lineEdit_password"> <widget class="QToolButton" name="shareLinkToolButton">
<property name="sizePolicy"> <property name="enabled">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed"> <bool>false</bool>
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QToolButton" name="confirmPassword">
<property name="icon"> <property name="icon">
<iconset resource="../../client.qrc"> <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>
<property name="autoRaise"> <property name="popupMode">
<bool>true</bool> <enum>QToolButton::InstantPopup</enum>
</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> </property>
</widget> </widget>
</item> </item>
@@ -233,6 +119,130 @@
</property> </property>
</widget> </widget>
</item> </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> </layout>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>

View File

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

View File

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

View File

@@ -190,18 +190,29 @@ void ShareUserGroupWidget::slotSharesFetched(const QList<QSharedPointer<Share>>
layout->setContentsMargins(0, 0, 0, 0); layout->setContentsMargins(0, 0, 0, 0);
int x = 0; int x = 0;
int height = 0; int height = 0;
QList<QString> linkOwners({});
foreach (const auto &share, shares) { foreach (const auto &share, shares) {
// We don't handle link shares, only TypeUser or TypeGroup // We don't handle link shares, only TypeUser or TypeGroup
if (share->getShareType() == Share::TypeLink) { if (share->getShareType() == Share::TypeLink) {
if(!share->getUidOwner().isEmpty() &&
share->getUidOwner() != share->account()->davUser()){
linkOwners.append(share->getOwnerDisplayName());
}
continue; 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); ShareUserLine *s = new ShareUserLine(share, _maxSharingPermissions, _isFile, _parentScrollArea);
connect(s, &ShareUserLine::resizeRequested, this, &ShareUserGroupWidget::slotAdjustScrollWidgetSize); connect(s, &ShareUserLine::resizeRequested, this, &ShareUserGroupWidget::slotAdjustScrollWidgetSize);
connect(s, &ShareUserLine::visualDeletionDone, this, &ShareUserGroupWidget::getShares); connect(s, &ShareUserLine::visualDeletionDone, this, &ShareUserGroupWidget::getShares);
s->setBackgroundRole(layout->count() % 2 == 0 ? QPalette::Base : QPalette::AlternateBase); s->setBackgroundRole(layout->count() % 2 == 0 ? QPalette::Base : QPalette::AlternateBase);
layout->addWidget(s); layout->addWidget(s);
s->setVisible(true);
x++; x++;
if (x <= 3) { if (x <= 3) {
@@ -209,7 +220,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->setVisible(!shares.isEmpty());
scrollArea->setFixedHeight(height); scrollArea->setFixedHeight(height);
scrollArea->setWidget(newViewPort); scrollArea->setWidget(newViewPort);
@@ -420,6 +442,13 @@ ShareUserLine::ShareUserLine(QSharedPointer<Share> share,
_permissionReshare->setVisible(false); _permissionReshare->setVisible(false);
} }
//If the initiator is not you. And the recipient is not you. Show it without any options.
if(share->account()->id() != share->getId() && share->account()->davUser() != share->getShareWith()->shareWith()){
_ui->permissionsEdit->hide();
_ui->permissionToolButton->hide();
_ui->deleteShareButton->hide();
}
loadAvatar(); loadAvatar();
} }

View File

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

View File

@@ -382,7 +382,7 @@ qint64 OwncloudAdvancedSetupPage::availableLocalSpace() const
QString OwncloudAdvancedSetupPage::checkLocalSpace(qint64 remoteSize) 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 } // namespace OCC

View File

@@ -136,7 +136,7 @@ WebViewPageUrlSchemeHandler::WebViewPageUrlSchemeHandler(QObject *parent)
void WebViewPageUrlSchemeHandler::requestStarted(QWebEngineUrlRequestJob *request) { void WebViewPageUrlSchemeHandler::requestStarted(QWebEngineUrlRequestJob *request) {
QUrl url = request->requestUrl(); QUrl url = request->requestUrl();
QString path = url.path().mid(1); QString path = url.path(0).mid(1); // get undecoded path
QStringList parts = path.split("&"); QStringList parts = path.split("&");
QString server; QString server;
@@ -153,12 +153,14 @@ void WebViewPageUrlSchemeHandler::requestStarted(QWebEngineUrlRequestJob *reques
} }
} }
user = QUrl::fromPercentEncoding(user.toUtf8()); qCDebug(lcWizardWebiew()) << "Got raw user from request path: " << user;
password = QUrl::fromPercentEncoding(password.toUtf8());
user = user.replace(QChar('+'), QChar(' ')); user = user.replace(QChar('+'), QChar(' '));
password = password.replace(QChar('+'), QChar(' ')); password = password.replace(QChar('+'), QChar(' '));
user = QUrl::fromPercentEncoding(user.toUtf8());
password = QUrl::fromPercentEncoding(password.toUtf8());
if (!server.startsWith("http://") && !server.startsWith("https://")) { if (!server.startsWith("http://") && !server.startsWith("https://")) {
server = "https://" + server; server = "https://" + server;
} }
@@ -197,7 +199,7 @@ bool WebEnginePage::certificateError(const QWebEngineCertificateError &certifica
*/ */
QMessageBox messageBox; QMessageBox messageBox;
messageBox.setText(tr("Invalid certificate detected")); messageBox.setText(tr("Invalid certificate detected"));
messageBox.setInformativeText(tr("The host \"%1\" provided an invalid certitiface. Continue?").arg(certificateError.url().host())); messageBox.setInformativeText(tr("The host \"%1\" provided an invalid certificate. Continue?").arg(certificateError.url().host()));
messageBox.setIcon(QMessageBox::Warning); messageBox.setIcon(QMessageBox::Warning);
messageBox.setStandardButtons(QMessageBox::Yes|QMessageBox::No); messageBox.setStandardButtons(QMessageBox::Yes|QMessageBox::No);
messageBox.setDefaultButton(QMessageBox::No); messageBox.setDefaultButton(QMessageBox::No);

View File

@@ -958,6 +958,7 @@ void ClientSideEncryption::encryptPrivateKey()
{ {
QStringList list = WordList::getRandomWords(12); QStringList list = WordList::getRandomWords(12);
_mnemonic = list.join(' '); _mnemonic = list.join(' ');
_newMnemonicGenerated = true;
qCInfo(lcCse()) << "mnemonic Generated:" << _mnemonic; qCInfo(lcCse()) << "mnemonic Generated:" << _mnemonic;
emit mnemonicGenerated(_mnemonic); emit mnemonicGenerated(_mnemonic);
@@ -989,6 +990,11 @@ void ClientSideEncryption::encryptPrivateKey()
job->start(); job->start();
} }
bool ClientSideEncryption::newMnemonicGenerated() const
{
return _newMnemonicGenerated;
}
void ClientSideEncryption::decryptPrivateKey(const QByteArray &key) { void ClientSideEncryption::decryptPrivateKey(const QByteArray &key) {
QString msg = tr("Please enter your end to end encryption passphrase:<br>" QString msg = tr("Please enter your end to end encryption passphrase:<br>"
"<br>" "<br>"

View File

@@ -87,6 +87,8 @@ public:
void forgetSensitiveData(); void forgetSensitiveData();
bool newMnemonicGenerated() const;
public slots: public slots:
void slotRequestMnemonic(); void slotRequestMnemonic();
@@ -127,6 +129,7 @@ public:
QSslKey _publicKey; QSslKey _publicKey;
QSslCertificate _certificate; QSslCertificate _certificate;
QString _mnemonic; QString _mnemonic;
bool _newMnemonicGenerated = false;
}; };
/* Generates the Metadata for the folder */ /* Generates the Metadata for the folder */

View File

@@ -37,7 +37,7 @@
#include <QNetworkProxy> #include <QNetworkProxy>
#include <QStandardPaths> #include <QStandardPaths>
#define DEFAULT_REMOTE_POLL_INTERVAL 30000 // default remote poll time in milliseconds #define DEFAULT_REMOTE_POLL_INTERVAL 5000 // default remote poll time in milliseconds
#define DEFAULT_MAX_LOG_LINES 20000 #define DEFAULT_MAX_LOG_LINES 20000
namespace OCC { namespace OCC {
@@ -750,7 +750,7 @@ void ConfigFile::setMoveToTrash(bool isChecked)
bool ConfigFile::promptDeleteFiles() const bool ConfigFile::promptDeleteFiles() const
{ {
QSettings settings(configFile(), QSettings::IniFormat); QSettings settings(configFile(), QSettings::IniFormat);
return settings.value(QLatin1String(promptDeleteC), true).toBool(); return settings.value(QLatin1String(promptDeleteC), false).toBool();
} }
void ConfigFile::setPromptDeleteFiles(bool promptDeleteFiles) void ConfigFile::setPromptDeleteFiles(bool promptDeleteFiles)

View File

@@ -269,6 +269,7 @@ void PropagateItemJob::done(SyncFileItem::Status statusArg, const QString &error
case SyncFileItem::FileIgnored: case SyncFileItem::FileIgnored:
case SyncFileItem::NoStatus: case SyncFileItem::NoStatus:
case SyncFileItem::BlacklistedError: case SyncFileItem::BlacklistedError:
case SyncFileItem::FileLocked:
// nothing // nothing
break; break;
} }

View File

@@ -93,7 +93,7 @@ inline SyncFileItem::Status classifyError(QNetworkReply::NetworkError nerror,
if (anotherSyncNeeded) { if (anotherSyncNeeded) {
*anotherSyncNeeded = true; *anotherSyncNeeded = true;
} }
return SyncFileItem::SoftError; return SyncFileItem::FileLocked;
} }
return SyncFileItem::NormalError; return SyncFileItem::NormalError;

View File

@@ -91,7 +91,8 @@ bool Progress::isWarningKind(SyncFileItem::Status kind)
return kind == SyncFileItem::SoftError || kind == SyncFileItem::NormalError return kind == SyncFileItem::SoftError || kind == SyncFileItem::NormalError
|| kind == SyncFileItem::FatalError || kind == SyncFileItem::FileIgnored || kind == SyncFileItem::FatalError || kind == SyncFileItem::FileIgnored
|| kind == SyncFileItem::Conflict || kind == SyncFileItem::Restoration || kind == SyncFileItem::Conflict || kind == SyncFileItem::Restoration
|| kind == SyncFileItem::DetailError || kind == SyncFileItem::BlacklistedError; || kind == SyncFileItem::DetailError || kind == SyncFileItem::BlacklistedError
|| kind == SyncFileItem::FileLocked;
} }
bool Progress::isIgnoredKind(SyncFileItem::Status kind) bool Progress::isIgnoredKind(SyncFileItem::Status kind)

View File

@@ -571,6 +571,12 @@ void PropagateDownloadFile::slotGetFinished()
qCWarning(lcPropagateDownload) << "server replied 404, assuming file was deleted"; qCWarning(lcPropagateDownload) << "server replied 404, assuming file was deleted";
} }
// Getting a 423 means that the file is locked
const bool fileLocked = _item->_httpErrorCode == 423;
if (fileLocked) {
qCWarning(lcPropagateDownload) << "server replied 423, file is Locked";
}
// Don't keep the temporary file if it is empty or we // Don't keep the temporary file if it is empty or we
// used a bad range header or the file's not on the server anymore. // used a bad range header or the file's not on the server anymore.
if (_tmpFile.size() == 0 || badRangeHeader || fileNotFound) { if (_tmpFile.size() == 0 || badRangeHeader || fileNotFound) {
@@ -659,7 +665,7 @@ void PropagateDownloadFile::slotGetFinished()
if (_tmpFile.size() == 0 && _item->_size > 0) { if (_tmpFile.size() == 0 && _item->_size > 0) {
FileSystem::remove(_tmpFile.fileName()); FileSystem::remove(_tmpFile.fileName());
done(SyncFileItem::NormalError, done(SyncFileItem::NormalError,
tr("The downloaded file is empty despite the server announced it should have been %1.") tr("The downloaded file is empty despite that the server announced it should have been %1.")
.arg(Utility::octetsToString(_item->_size))); .arg(Utility::octetsToString(_item->_size)));
return; return;
} }

View File

@@ -92,19 +92,19 @@ private:
public: public:
// Takes ownership of the device // Takes ownership of the device
explicit PUTFileJob(AccountPtr account, const QString &path, QIODevice *device, explicit PUTFileJob(AccountPtr account, const QString &path, std::unique_ptr<QIODevice> device,
const QMap<QByteArray, QByteArray> &headers, int chunk, QObject *parent = nullptr) const QMap<QByteArray, QByteArray> &headers, int chunk, QObject *parent = nullptr)
: AbstractNetworkJob(account, path, parent) : AbstractNetworkJob(account, path, parent)
, _device(device) , _device(device.release())
, _headers(headers) , _headers(headers)
, _chunk(chunk) , _chunk(chunk)
{ {
_device->setParent(this); _device->setParent(this);
} }
explicit PUTFileJob(AccountPtr account, const QUrl &url, QIODevice *device, explicit PUTFileJob(AccountPtr account, const QUrl &url, std::unique_ptr<QIODevice> device,
const QMap<QByteArray, QByteArray> &headers, int chunk, QObject *parent = nullptr) const QMap<QByteArray, QByteArray> &headers, int chunk, QObject *parent = nullptr)
: AbstractNetworkJob(account, QString(), parent) : AbstractNetworkJob(account, QString(), parent)
, _device(device) , _device(device.release())
, _headers(headers) , _headers(headers)
, _url(url) , _url(url)
, _chunk(chunk) , _chunk(chunk)

View File

@@ -305,7 +305,7 @@ void PropagateUploadFileNG::startNextChunk()
return; return;
} }
auto device = new UploadDevice(&propagator()->_bandwidthManager); auto device = std::make_unique<UploadDevice>(&propagator()->_bandwidthManager);
const QString fileName = _fileToUpload._path; const QString fileName = _fileToUpload._path;
if (!device->prepareAndOpen(fileName, _sent, _currentChunkSize)) { if (!device->prepareAndOpen(fileName, _sent, _currentChunkSize)) {
@@ -328,13 +328,14 @@ void PropagateUploadFileNG::startNextChunk()
QUrl url = chunkUrl(_currentChunk); QUrl url = chunkUrl(_currentChunk);
// job takes ownership of device via a QScopedPointer. Job deletes itself when finishing // job takes ownership of device via a QScopedPointer. Job deletes itself when finishing
PUTFileJob *job = new PUTFileJob(propagator()->account(), url, device, headers, _currentChunk, this); auto devicePtr = device.get(); // for connections later
PUTFileJob *job = new PUTFileJob(propagator()->account(), url, std::move(device), headers, _currentChunk, this);
_jobs.append(job); _jobs.append(job);
connect(job, &PUTFileJob::finishedSignal, this, &PropagateUploadFileNG::slotPutFinished); connect(job, &PUTFileJob::finishedSignal, this, &PropagateUploadFileNG::slotPutFinished);
connect(job, &PUTFileJob::uploadProgress, connect(job, &PUTFileJob::uploadProgress,
this, &PropagateUploadFileNG::slotUploadProgress); this, &PropagateUploadFileNG::slotUploadProgress);
connect(job, &PUTFileJob::uploadProgress, connect(job, &PUTFileJob::uploadProgress,
device, &UploadDevice::slotJobUploadProgress); devicePtr, &UploadDevice::slotJobUploadProgress);
connect(job, &QObject::destroyed, this, &PropagateUploadFileCommon::slotJobDestroyed); connect(job, &QObject::destroyed, this, &PropagateUploadFileCommon::slotJobDestroyed);
job->start(); job->start();
propagator()->_activeJobList.append(this); propagator()->_activeJobList.append(this);

View File

@@ -89,7 +89,7 @@ void PropagateUploadFileV1::startNextChunk()
QString path = _fileToUpload._file; QString path = _fileToUpload._file;
UploadDevice *device = new UploadDevice(&propagator()->_bandwidthManager); auto device = std::make_unique<UploadDevice>(&propagator()->_bandwidthManager);
qint64 chunkStart = 0; qint64 chunkStart = 0;
qint64 currentChunkSize = fileSize; qint64 currentChunkSize = fileSize;
bool isFinalChunk = false; bool isFinalChunk = false;
@@ -134,16 +134,16 @@ void PropagateUploadFileV1::startNextChunk()
} }
// Soft error because this is likely caused by the user modifying his files while syncing // Soft error because this is likely caused by the user modifying his files while syncing
abortWithError(SyncFileItem::SoftError, device->errorString()); abortWithError(SyncFileItem::SoftError, device->errorString());
delete device;
return; return;
} }
// job takes ownership of device via a QScopedPointer. Job deletes itself when finishing // job takes ownership of device via a QScopedPointer. Job deletes itself when finishing
PUTFileJob *job = new PUTFileJob(propagator()->account(), propagator()->_remoteFolder + path, device, headers, _currentChunk, this); auto devicePtr = device.get(); // for connections later
PUTFileJob *job = new PUTFileJob(propagator()->account(), propagator()->_remoteFolder + path, std::move(device), headers, _currentChunk, this);
_jobs.append(job); _jobs.append(job);
connect(job, &PUTFileJob::finishedSignal, this, &PropagateUploadFileV1::slotPutFinished); connect(job, &PUTFileJob::finishedSignal, this, &PropagateUploadFileV1::slotPutFinished);
connect(job, &PUTFileJob::uploadProgress, this, &PropagateUploadFileV1::slotUploadProgress); connect(job, &PUTFileJob::uploadProgress, this, &PropagateUploadFileV1::slotUploadProgress);
connect(job, &PUTFileJob::uploadProgress, device, &UploadDevice::slotJobUploadProgress); connect(job, &PUTFileJob::uploadProgress, devicePtr, &UploadDevice::slotJobUploadProgress);
connect(job, &QObject::destroyed, this, &PropagateUploadFileCommon::slotJobDestroyed); connect(job, &QObject::destroyed, this, &PropagateUploadFileCommon::slotJobDestroyed);
if (isFinalChunk) if (isFinalChunk)
adjustLastJobTimeout(job, fileSize); adjustLastJobTimeout(job, fileSize);

View File

@@ -26,6 +26,8 @@
#include "propagateremotedelete.h" #include "propagateremotedelete.h"
#include "propagatedownload.h" #include "propagatedownload.h"
#include "common/asserts.h" #include "common/asserts.h"
#include "configfile.h"
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include <windows.h> #include <windows.h>
@@ -1035,7 +1037,8 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
} }
} }
if (!_hasNoneFiles && _hasRemoveFile) { ConfigFile cfgFile;
if (!_hasNoneFiles && _hasRemoveFile && cfgFile.promptDeleteFiles()) {
qCInfo(lcEngine) << "All the files are going to be changed, asking the user"; qCInfo(lcEngine) << "All the files are going to be changed, asking the user";
bool cancel = false; bool cancel = false;
emit aboutToRemoveAllFiles(syncItems.first()->_direction, &cancel); emit aboutToRemoveAllFiles(syncItems.first()->_direction, &cancel);

View File

@@ -59,6 +59,7 @@ public:
Conflict, Conflict,
FileIgnored, ///< The file is in the ignored list (or blacklisted with no retries left) FileIgnored, ///< The file is in the ignored list (or blacklisted with no retries left)
FileLocked, ///< The file is locked
Restoration, ///< The file was restored because what should have been done was not allowed Restoration, ///< The file was restored because what should have been done was not allowed
/** For errors that should only appear in the error view. /** For errors that should only appear in the error view.

View File

@@ -105,7 +105,8 @@ static inline bool showWarningInSocketApi(const SyncFileItem &item)
return item._instruction == CSYNC_INSTRUCTION_IGNORE return item._instruction == CSYNC_INSTRUCTION_IGNORE
|| status == SyncFileItem::FileIgnored || status == SyncFileItem::FileIgnored
|| status == SyncFileItem::Conflict || status == SyncFileItem::Conflict
|| status == SyncFileItem::Restoration; || status == SyncFileItem::Restoration
|| status == SyncFileItem::FileLocked;
} }
SyncFileStatusTracker::SyncFileStatusTracker(SyncEngine *syncEngine) SyncFileStatusTracker::SyncFileStatusTracker(SyncEngine *syncEngine)

View File

@@ -28,6 +28,7 @@ SyncResult::SyncResult()
, _numNewConflictItems(0) , _numNewConflictItems(0)
, _numOldConflictItems(0) , _numOldConflictItems(0)
, _numErrorItems(0) , _numErrorItems(0)
, _numLockedItems(0)
{ {
} }
@@ -139,6 +140,13 @@ void SyncResult::processCompletedItem(const SyncFileItemPtr &item)
_folderStructureWasChanged = true; _folderStructureWasChanged = true;
} }
if(item->_status == SyncFileItem::FileLocked){
_numLockedItems++;
if (!_firstItemLocked) {
_firstItemLocked = item;
}
}
// Process the item to the gui // Process the item to the gui
if (item->_status == SyncFileItem::FatalError || item->_status == SyncFileItem::NormalError) { if (item->_status == SyncFileItem::FatalError || item->_status == SyncFileItem::NormalError) {
//: this displays an error string (%2) for a file %1 //: this displays an error string (%2) for a file %1

View File

@@ -72,12 +72,16 @@ public:
int numErrorItems() const { return _numErrorItems; } int numErrorItems() const { return _numErrorItems; }
bool hasUnresolvedConflicts() const { return _numNewConflictItems + _numOldConflictItems > 0; } bool hasUnresolvedConflicts() const { return _numNewConflictItems + _numOldConflictItems > 0; }
int numLockedItems() const { return _numLockedItems; }
bool hasLockedFiles() const { return _numLockedItems > 0; }
const SyncFileItemPtr &firstItemNew() const { return _firstItemNew; } const SyncFileItemPtr &firstItemNew() const { return _firstItemNew; }
const SyncFileItemPtr &firstItemDeleted() const { return _firstItemDeleted; } const SyncFileItemPtr &firstItemDeleted() const { return _firstItemDeleted; }
const SyncFileItemPtr &firstItemUpdated() const { return _firstItemUpdated; } const SyncFileItemPtr &firstItemUpdated() const { return _firstItemUpdated; }
const SyncFileItemPtr &firstItemRenamed() const { return _firstItemRenamed; } const SyncFileItemPtr &firstItemRenamed() const { return _firstItemRenamed; }
const SyncFileItemPtr &firstNewConflictItem() const { return _firstNewConflictItem; } const SyncFileItemPtr &firstNewConflictItem() const { return _firstNewConflictItem; }
const SyncFileItemPtr &firstItemError() const { return _firstItemError; } const SyncFileItemPtr &firstItemError() const { return _firstItemError; }
const SyncFileItemPtr &firstItemLocked() const { return _firstItemLocked; }
void processCompletedItem(const SyncFileItemPtr &item); void processCompletedItem(const SyncFileItemPtr &item);
@@ -101,6 +105,7 @@ private:
int _numNewConflictItems; int _numNewConflictItems;
int _numOldConflictItems; int _numOldConflictItems;
int _numErrorItems; int _numErrorItems;
int _numLockedItems;
SyncFileItemPtr _firstItemNew; SyncFileItemPtr _firstItemNew;
SyncFileItemPtr _firstItemDeleted; SyncFileItemPtr _firstItemDeleted;
@@ -108,6 +113,7 @@ private:
SyncFileItemPtr _firstItemRenamed; SyncFileItemPtr _firstItemRenamed;
SyncFileItemPtr _firstNewConflictItem; SyncFileItemPtr _firstNewConflictItem;
SyncFileItemPtr _firstItemError; SyncFileItemPtr _firstItemError;
SyncFileItemPtr _firstItemLocked;
}; };
} }

View File

@@ -341,10 +341,9 @@ QString Theme::gitSHA1() const
QString Theme::about() const QString Theme::about() const
{ {
QString devString; QString devString;
devString = tr("<p>Version %1. For more information please visit <a href='%2'>%3</a>.</p>") devString = tr("<p>Version %1. For more information please click <a href='%2'>here</a>.</p>")
.arg(MIRALL_VERSION_STRING) .arg(MIRALL_VERSION_STRING)
.arg("http://" MIRALL_STRINGIFY(APPLICATION_DOMAIN)) .arg(helpUrl());
.arg(MIRALL_STRINGIFY(APPLICATION_DOMAIN));
devString += tr("<p>This release was supplied by %1</p>") devString += tr("<p>This release was supplied by %1</p>")
.arg(APPLICATION_VENDOR); .arg(APPLICATION_VENDOR);

View File

@@ -8,6 +8,7 @@
#include <QtTest> #include <QtTest>
#include "syncenginetestutils.h" #include "syncenginetestutils.h"
#include <syncengine.h> #include <syncengine.h>
#include <configfile.h>
using namespace OCC; using namespace OCC;
@@ -48,6 +49,8 @@ private slots:
{ {
QFETCH(bool, deleteOnRemote); QFETCH(bool, deleteOnRemote);
FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()}; FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
ConfigFile config;
config.setPromptDeleteFiles(true);
//Just set a blacklist so we can check it is still there. This directory does not exists but //Just set a blacklist so we can check it is still there. This directory does not exists but
// that does not matter for our purposes. // that does not matter for our purposes.

View File

@@ -140,6 +140,17 @@ private slots:
QVERIFY(waitForPathChanged(file)); QVERIFY(waitForPathChanged(file));
} }
void testMove3LevelDirWithFile() {
QString file(_rootPath + "/a0/b/c/empty.txt");
mkdir(_rootPath + "/a0");
mkdir(_rootPath + "/a0/b");
mkdir(_rootPath + "/a0/b/c");
touch(file);
mv(_rootPath + "/a0 ", _rootPath + "/a");
QVERIFY(waitForPathChanged(_rootPath + "/a/b/c/empty.txt"));
}
void testCreateADir() { void testCreateADir() {
QString file(_rootPath+"/a1/b1/new_dir"); QString file(_rootPath+"/a1/b1/new_dir");
mkdir(file); mkdir(file);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

4170
translations/client_da.ts Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

4172
translations/client_he.ts Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

4190
translations/client_id.ts Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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