mirror of
https://github.com/chylex/Nextcloud-Desktop.git
synced 2026-04-11 11:57:41 +02:00
Compare commits
160 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b45f5fd1a9 | ||
|
|
a4317ca50d | ||
|
|
6c6eeab479 | ||
|
|
7fdbc72991 | ||
|
|
2187c6f4fc | ||
|
|
f3b825f333 | ||
|
|
676d4e1308 | ||
|
|
e03309a05e | ||
|
|
de15f20006 | ||
|
|
7ed1062314 | ||
|
|
80138b1cbf | ||
|
|
93547ced6d | ||
|
|
0aefa58644 | ||
|
|
992bcf56f5 | ||
|
|
35de69f7bc | ||
|
|
1c44909254 | ||
|
|
c3ccb72f2b | ||
|
|
890bd09ce9 | ||
|
|
b5309c47fc | ||
|
|
d24a09e47f | ||
|
|
bdae4a9be7 | ||
|
|
d9ce9814da | ||
|
|
f6d019af21 | ||
|
|
423ee61817 | ||
|
|
4871776a4b | ||
|
|
f4e83891e1 | ||
|
|
d589b524e0 | ||
|
|
de0158e2bb | ||
|
|
62856f9001 | ||
|
|
1313bc573f | ||
|
|
c2838fb8a8 | ||
|
|
ba71bf13f4 | ||
|
|
7e36c7ba59 | ||
|
|
e9641a3b94 | ||
|
|
7ff1911492 | ||
|
|
57f8b866a5 | ||
|
|
883df2f4fa | ||
|
|
5880c4954e | ||
|
|
b02bd066a9 | ||
|
|
6abec7cea9 | ||
|
|
cc4e6b236a | ||
|
|
821946ad94 | ||
|
|
94c3e19ede | ||
|
|
feba6910ce | ||
|
|
4f37249750 | ||
|
|
fda8c406f6 | ||
|
|
a804c4650a | ||
|
|
e1f4963973 | ||
|
|
6ae761c43c | ||
|
|
504bb34d26 | ||
|
|
1136cee383 | ||
|
|
591d4c812b | ||
|
|
a105e3f758 | ||
|
|
a88687bfe3 | ||
|
|
879ed544e1 | ||
|
|
13aaffc46b | ||
|
|
62fc12fe40 | ||
|
|
f4543e0c79 | ||
|
|
d35f466773 | ||
|
|
e3cb3b28ff | ||
|
|
36049afbc4 | ||
|
|
59c165aa1d | ||
|
|
071b4abeeb | ||
|
|
93e04fc72b | ||
|
|
a9915c4b46 | ||
|
|
a265ff52e7 | ||
|
|
85b4965d7f | ||
|
|
963beec760 | ||
|
|
2f812063ac | ||
|
|
a485120a34 | ||
|
|
6337116de7 | ||
|
|
a56eb2e95e | ||
|
|
ac3246f9f2 | ||
|
|
aa9849c112 | ||
|
|
a89e49ef84 | ||
|
|
1d745535f7 | ||
|
|
3184aeed43 | ||
|
|
3faf010b55 | ||
|
|
163c80e203 | ||
|
|
73462e97aa | ||
|
|
6f4144a464 | ||
|
|
9820464545 | ||
|
|
3ecd7823f9 | ||
|
|
9b504eaddd | ||
|
|
838ca6cba0 | ||
|
|
7e5e40d5c4 | ||
|
|
abd8d1fda1 | ||
|
|
0a3491f332 | ||
|
|
15d9ca2b00 | ||
|
|
7682749415 | ||
|
|
a18fc5b5c9 | ||
|
|
96783a9b80 | ||
|
|
258c2cee2e | ||
|
|
65ff3c0de1 | ||
|
|
6879c9f1c9 | ||
|
|
fade8465e4 | ||
|
|
76b5c6b6d4 | ||
|
|
bdd0cc4dc3 | ||
|
|
876b1e239e | ||
|
|
da2007c7f6 | ||
|
|
7a33cb97cd | ||
|
|
7a18a58fae | ||
|
|
fcc9d02bcc | ||
|
|
98238669fe | ||
|
|
a80de38517 | ||
|
|
4cb75d91f9 | ||
|
|
55e4dceeb7 | ||
|
|
0006b35abf | ||
|
|
b153391cbf | ||
|
|
c0659a3124 | ||
|
|
de8a7aa680 | ||
|
|
03b11693a3 | ||
|
|
23f2d79f70 | ||
|
|
f6db365391 | ||
|
|
95f74ceb1f | ||
|
|
91be4961d7 | ||
|
|
b3eb16bfd3 | ||
|
|
52d91ce198 | ||
|
|
c49efcc137 | ||
|
|
c14556153f | ||
|
|
5e1a2a423f | ||
|
|
3ac1ba079a | ||
|
|
b7a9cd1d45 | ||
|
|
c7b0ce036d | ||
|
|
f5afa07b0a | ||
|
|
1b550b976f | ||
|
|
68f0b9e0aa | ||
|
|
8945ef2652 | ||
|
|
0cc74d21a2 | ||
|
|
f4c79c8f68 | ||
|
|
b6ef8edb12 | ||
|
|
06ffd3e841 | ||
|
|
a3596b80e9 | ||
|
|
8ac1d92161 | ||
|
|
d55f1d5c06 | ||
|
|
854c637c73 | ||
|
|
92ad0c4a43 | ||
|
|
fb8facb787 | ||
|
|
30832cc427 | ||
|
|
ab7f29c0b2 | ||
|
|
6500442217 | ||
|
|
0e6a9fd0c1 | ||
|
|
ed7c489722 | ||
|
|
d4b9b5129a | ||
|
|
4a79e24db2 | ||
|
|
215d185fbe | ||
|
|
6f273bf7dd | ||
|
|
f2bc25c9ca | ||
|
|
ca6de6128f | ||
|
|
a1e5e6ca40 | ||
|
|
cf8bb1c5bc | ||
|
|
ebe1cef357 | ||
|
|
922d14f016 | ||
|
|
7a0c6a2f8f | ||
|
|
f35a2c0b2c | ||
|
|
c6b22089e3 | ||
|
|
cf0082f92f | ||
|
|
f4e129d4e2 | ||
|
|
ec6eaf2121 | ||
|
|
1dc1f91620 |
31
.drone.yml
31
.drone.yml
@@ -237,13 +237,17 @@ name: qt-5.12
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: build and test
|
- name: build and test
|
||||||
image: nextcloudci/client-5.12:client-5.12-2
|
image: nextcloudci/client-5.12:client-5.12-5
|
||||||
commands:
|
commands:
|
||||||
# Install QtKeyChain
|
# Install QtKeyChain
|
||||||
- /bin/bash -c "
|
- /bin/bash -c "
|
||||||
export CC=gcc-7 &&
|
export CC=gcc-7 &&
|
||||||
export CXX=g++-7 &&
|
export CXX=g++-7 &&
|
||||||
source /opt/qt512/bin/qt512-env.sh &&
|
export QT_BASE_DIR=/opt/qt5.12.5 &&
|
||||||
|
export QTDIR=\$QT_BASE_DIR &&
|
||||||
|
export PATH=\$QT_BASE_DIR/bin:\$PATH &&
|
||||||
|
export LD_LIBRARY_PATH=\$QT_BASE_DIR/lib/x86_64-linux-gnu:\$QT_BASE_DIR/lib:/usr/local/lib:\$LD_LIBRARY_PATH &&
|
||||||
|
export PKG_CONFIG_PATH=\$QT_BASE_DIR/lib/pkgconfig:\$PKG_CONFIG_PATH &&
|
||||||
cd /tmp &&
|
cd /tmp &&
|
||||||
git clone https://github.com/frankosterfeld/qtkeychain.git &&
|
git clone https://github.com/frankosterfeld/qtkeychain.git &&
|
||||||
cd qtkeychain &&
|
cd qtkeychain &&
|
||||||
@@ -257,7 +261,11 @@ steps:
|
|||||||
- /bin/bash -c "
|
- /bin/bash -c "
|
||||||
export CC=gcc-7 &&
|
export CC=gcc-7 &&
|
||||||
export CXX=g++-7 &&
|
export CXX=g++-7 &&
|
||||||
source /opt/qt512/bin/qt512-env.sh &&
|
export QT_BASE_DIR=/opt/qt5.12.5 &&
|
||||||
|
export QTDIR=\$QT_BASE_DIR &&
|
||||||
|
export PATH=\$QT_BASE_DIR/bin:\$PATH &&
|
||||||
|
export LD_LIBRARY_PATH=\$QT_BASE_DIR/lib/x86_64-linux-gnu:\$QT_BASE_DIR/lib:/usr/local/lib:\$LD_LIBRARY_PATH &&
|
||||||
|
export PKG_CONFIG_PATH=\$QT_BASE_DIR/lib/pkgconfig:\$PKG_CONFIG_PATH &&
|
||||||
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 ../ &&
|
||||||
@@ -278,13 +286,17 @@ name: qt-5.12-clang
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: build and test
|
- name: build and test
|
||||||
image: nextcloudci/client-5.12:client-5.12-2
|
image: nextcloudci/client-5.12:client-5.12-5
|
||||||
commands:
|
commands:
|
||||||
# Install QtKeyChain
|
# Install QtKeyChain
|
||||||
- /bin/bash -c "
|
- /bin/bash -c "
|
||||||
export CC=clang-6.0 &&
|
export CC=clang-6.0 &&
|
||||||
export CXX=clang++-6.0 &&
|
export CXX=clang++-6.0 &&
|
||||||
source /opt/qt512/bin/qt512-env.sh &&
|
export QT_BASE_DIR=/opt/qt5.12.5 &&
|
||||||
|
export QTDIR=\$QT_BASE_DIR &&
|
||||||
|
export PATH=\$QT_BASE_DIR/bin:\$PATH &&
|
||||||
|
export LD_LIBRARY_PATH=\$QT_BASE_DIR/lib/x86_64-linux-gnu:\$QT_BASE_DIR/lib:/usr/local/lib:\$LD_LIBRARY_PATH &&
|
||||||
|
export PKG_CONFIG_PATH=\$QT_BASE_DIR/lib/pkgconfig:\$PKG_CONFIG_PATH &&
|
||||||
cd /tmp &&
|
cd /tmp &&
|
||||||
git clone https://github.com/frankosterfeld/qtkeychain.git &&
|
git clone https://github.com/frankosterfeld/qtkeychain.git &&
|
||||||
cd qtkeychain &&
|
cd qtkeychain &&
|
||||||
@@ -298,7 +310,11 @@ steps:
|
|||||||
- /bin/bash -c "
|
- /bin/bash -c "
|
||||||
export CC=clang-6.0 &&
|
export CC=clang-6.0 &&
|
||||||
export CXX=clang++-6.0 &&
|
export CXX=clang++-6.0 &&
|
||||||
source /opt/qt512/bin/qt512-env.sh &&
|
export QT_BASE_DIR=/opt/qt5.12.5 &&
|
||||||
|
export QTDIR=\$QT_BASE_DIR &&
|
||||||
|
export PATH=\$QT_BASE_DIR/bin:\$PATH &&
|
||||||
|
export LD_LIBRARY_PATH=\$QT_BASE_DIR/lib/x86_64-linux-gnu:\$QT_BASE_DIR/lib:/usr/local/lib:\$LD_LIBRARY_PATH &&
|
||||||
|
export PKG_CONFIG_PATH=\$QT_BASE_DIR/lib/pkgconfig:\$PKG_CONFIG_PATH &&
|
||||||
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 ../ &&
|
||||||
@@ -319,9 +335,10 @@ name: AppImage
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: build
|
- name: build
|
||||||
image: nextcloudci/client-5.12:client-5.12-2
|
image: nextcloudci/client-5.12:client-5.12-5
|
||||||
commands:
|
commands:
|
||||||
- /bin/bash -c "./admin/linux/build-appimage.sh"
|
- /bin/bash -c "./admin/linux/build-appimage.sh"
|
||||||
|
- /bin/bash -c "./admin/linux/upload-appimage.sh"
|
||||||
trigger:
|
trigger:
|
||||||
branch:
|
branch:
|
||||||
- master
|
- master
|
||||||
|
|||||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,6 +1,3 @@
|
|||||||
[submodule "src/3rdparty/qtmacgoodies"]
|
|
||||||
path = src/3rdparty/qtmacgoodies
|
|
||||||
url = https://github.com/camilasan/qtmacgoodies.git
|
|
||||||
[submodule "binary"]
|
[submodule "binary"]
|
||||||
path = binary
|
path = binary
|
||||||
url = git://github.com/owncloud/owncloud-client-binary.git
|
url = git://github.com/owncloud/owncloud-client-binary.git
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ X-GNOME-Autostart-Delay=3
|
|||||||
|
|
||||||
|
|
||||||
# Translations
|
# Translations
|
||||||
Icon[cs_CZ]=@NAZEV_IKONY_APLIKACE@
|
Icon[cs_CZ]=@APPLICATION_ICON_NAME@
|
||||||
Name[cs_CZ]=@APPLICATION_NAME@ synchronizační klient pro desktop
|
Name[cs_CZ]=@APPLICATION_NAME@ synchronizační klient pro desktop
|
||||||
Comment[cs_CZ]=@APPLICATION_NAME@ synchronizační klient pro desktop
|
Comment[cs_CZ]=@APPLICATION_NAME@ synchronizační klient pro desktop
|
||||||
GenericName[cs_CZ]=Synchronizace složek
|
GenericName[cs_CZ]=Synchronizace složek
|
||||||
|
|||||||
204
.tx/nextcloud.client-desktop/cy_GB_translation
Normal file
204
.tx/nextcloud.client-desktop/cy_GB_translation
Normal 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[cy_GB]=@APPLICATION_ICON_NAME@
|
||||||
|
Name[cy_GB]=@APPLICATION_NAME@ cleient cydweddu bwrdd gwaith
|
||||||
|
Comment[cy_GB]=@APPLICATION_NAME@ cleient cydweddu bwrdd gwaith
|
||||||
|
GenericName[cy_GB]=Cydweddu Ffolder
|
||||||
@@ -199,6 +199,6 @@ X-GNOME-Autostart-Delay=3
|
|||||||
|
|
||||||
# Translations
|
# Translations
|
||||||
Icon[de_DE]=@APPLICATION_ICON_NAME@
|
Icon[de_DE]=@APPLICATION_ICON_NAME@
|
||||||
Name[de_DE]=@APPLICATION_NAME@ Client zur Desktop-Synchronisation
|
Name[de_DE]=@APPLICATION_NAME@ Client zur Desktop-Synchronisierung
|
||||||
Comment[de_DE]=@APPLICATION_NAME@ Client zur Desktop-Synchronisation
|
Comment[de_DE]=@APPLICATION_NAME@ Client zur Desktop-Synchronisierung
|
||||||
GenericName[de_DE]=Synchronisationsordner
|
GenericName[de_DE]=Synchronisierungsordner
|
||||||
|
|||||||
@@ -198,4 +198,7 @@ X-GNOME-Autostart-Delay=3
|
|||||||
|
|
||||||
|
|
||||||
# Translations
|
# Translations
|
||||||
|
Icon[el]=@APPLICATION_ICON_NAME@
|
||||||
|
Name[el]=@APPLICATION_NAME@ πρόγραμμα συγχρονισμού
|
||||||
|
Comment[el]=@APPLICATION_NAME@ πρόγραμμα συγχρονισμού
|
||||||
GenericName[el]=Συγχρονισμός φακέλου
|
GenericName[el]=Συγχρονισμός φακέλου
|
||||||
|
|||||||
204
.tx/nextcloud.client-desktop/es_AR_translation
Normal file
204
.tx/nextcloud.client-desktop/es_AR_translation
Normal 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[es_AR]=@APPLICATION_ICON_NAME@
|
||||||
|
Name[es_AR]=@APPLICATION_NAME@ cliente de sincronización de escritorio
|
||||||
|
Comment[es_AR]=@APPLICATION_NAME@ cliente de sincronización de escritorio
|
||||||
|
GenericName[es_AR]=Sincronización de carpetas
|
||||||
201
.tx/nextcloud.client-desktop/fa_translation
Normal file
201
.tx/nextcloud.client-desktop/fa_translation
Normal 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
|
||||||
|
Comment[fa]=@ APPLICATION_NAME @ مشتری هماهنگ سازی دسکتاپ
|
||||||
@@ -201,4 +201,4 @@ X-GNOME-Autostart-Delay=3
|
|||||||
Icon[ro]=@APPLICATION_ICON_NAME@
|
Icon[ro]=@APPLICATION_ICON_NAME@
|
||||||
Name[ro]=@APPLICATION_NAME@ client de sincronizare pentru desktop
|
Name[ro]=@APPLICATION_NAME@ client de sincronizare pentru desktop
|
||||||
Comment[ro]=@APPLICATION_NAME@ client de sincronizare pentru desktop
|
Comment[ro]=@APPLICATION_NAME@ client de sincronizare pentru desktop
|
||||||
GenericName[ro]=Sincronizare dosare
|
GenericName[ro]=Sincronizare director
|
||||||
|
|||||||
@@ -201,4 +201,4 @@ X-GNOME-Autostart-Delay=3
|
|||||||
Icon[sk_SK]=@APPLICATION_ICON_NAME@
|
Icon[sk_SK]=@APPLICATION_ICON_NAME@
|
||||||
Name[sk_SK]=@APPLICATION_NAME@ synchronizačný klient pre PC
|
Name[sk_SK]=@APPLICATION_NAME@ synchronizačný klient pre PC
|
||||||
Comment[sk_SK]=@APPLICATION_NAME@ synchronizačný klient pre PC
|
Comment[sk_SK]=@APPLICATION_NAME@ synchronizačný klient pre PC
|
||||||
GenericName[sk_SK]=Synchnonizácia priečinkov
|
GenericName[sk_SK]=Synchronizácia priečinkov
|
||||||
|
|||||||
@@ -199,6 +199,6 @@ X-GNOME-Autostart-Delay=3
|
|||||||
|
|
||||||
# Translations
|
# Translations
|
||||||
Icon[sl]=@APPLICATION_ICON_NAME@
|
Icon[sl]=@APPLICATION_ICON_NAME@
|
||||||
Name[sl]=@APPLICATION_NAME@ odjemalec za usklajevanje
|
Name[sl]=@APPLICATION_NAME@ program za usklajevanje
|
||||||
Comment[sl]=@APPLICATION_NAME@ odjemalec za usklajevanje
|
Comment[sl]=@APPLICATION_NAME@ program za usklajevanje
|
||||||
GenericName[sl]=Usklajevanje map
|
GenericName[sl]=Usklajevanje map
|
||||||
|
|||||||
@@ -199,4 +199,6 @@ X-GNOME-Autostart-Delay=3
|
|||||||
|
|
||||||
# Translations
|
# Translations
|
||||||
Icon[sv]=@APPLICATION_ICON_NAME@
|
Icon[sv]=@APPLICATION_ICON_NAME@
|
||||||
|
Name[sv]=@APPLICATION_NAME@ desktopssynkklient
|
||||||
|
Comment[sv]=@APPLICATION_NAME@ desktopssynkroniseringsklient
|
||||||
GenericName[sv]=Mappsynkronisering
|
GenericName[sv]=Mappsynkronisering
|
||||||
|
|||||||
204
.tx/nextcloud.client-desktop/sw_translation
Normal file
204
.tx/nextcloud.client-desktop/sw_translation
Normal 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[sw]=@APPLICATION_ICON_NAME@
|
||||||
|
Name[sw]=Teja ya @APPLICATION_NAME@ ya kufanana faili kwa seva na faili ziko hapa
|
||||||
|
Comment[sw]=Teja ya @APPLICATION_NAME@ ya kufanana faili kwa seva na faili ziko hapa
|
||||||
|
GenericName[sw]=Fanana Kabrasha
|
||||||
@@ -198,6 +198,7 @@ X-GNOME-Autostart-Delay=3
|
|||||||
|
|
||||||
|
|
||||||
# Translations
|
# Translations
|
||||||
|
Icon[zh_TW]=@APPLICATION_ICON_NAME@
|
||||||
Name[zh_TW]=@APPLICATION_NAME@ 桌面同步客戶端
|
Name[zh_TW]=@APPLICATION_NAME@ 桌面同步客戶端
|
||||||
Comment[zh_TW]=@APPLICATION_NAME@ 桌面同步客戶端
|
Comment[zh_TW]=@APPLICATION_NAME@ 桌面同步客戶端
|
||||||
GenericName[zh_TW]=資料夾同步
|
GenericName[zh_TW]=資料夾同步
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
set( MIRALL_VERSION_MAJOR 2 )
|
set( MIRALL_VERSION_MAJOR 2 )
|
||||||
set( MIRALL_VERSION_MINOR 6 )
|
set( MIRALL_VERSION_MINOR 6 )
|
||||||
set( MIRALL_VERSION_PATCH 1 )
|
set( MIRALL_VERSION_PATCH 4 )
|
||||||
set( MIRALL_VERSION_YEAR 2019 )
|
set( MIRALL_VERSION_YEAR 2020 )
|
||||||
set( MIRALL_SOVERSION 0 )
|
set( MIRALL_SOVERSION 0 )
|
||||||
|
|
||||||
if ( NOT DEFINED MIRALL_VERSION_SUFFIX )
|
if ( NOT DEFINED MIRALL_VERSION_SUFFIX )
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ RUN apt-get update -q && DEBIAN_FRONTEND=noninteractive apt-get install -q -y --
|
|||||||
libsqlite3-dev \
|
libsqlite3-dev \
|
||||||
libssl-dev \
|
libssl-dev \
|
||||||
libcmocka-dev \
|
libcmocka-dev \
|
||||||
|
libcloudproviders-dev \
|
||||||
qt5-default \
|
qt5-default \
|
||||||
qttools5-dev-tools \
|
qttools5-dev-tools \
|
||||||
libqt5webkit5-dev \
|
libqt5webkit5-dev \
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ mkdir /app
|
|||||||
mkdir /build
|
mkdir /build
|
||||||
|
|
||||||
#Set Qt-5.12
|
#Set Qt-5.12
|
||||||
export QT_BASE_DIR=/opt/qt512
|
export QT_BASE_DIR=/opt/qt5.12.5
|
||||||
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
|
||||||
@@ -18,11 +18,11 @@ if [ $SUFFIX != "master" ]; then
|
|||||||
SUFFIX="PR-$SUFFIX"
|
SUFFIX="PR-$SUFFIX"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#QtKeyChain 0.9.1
|
#QtKeyChain master
|
||||||
cd /build
|
cd /build
|
||||||
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 master
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake -D CMAKE_INSTALL_PREFIX=/usr ../
|
cmake -D CMAKE_INSTALL_PREFIX=/usr ../
|
||||||
@@ -66,7 +66,8 @@ rm -rf ./usr/share/nemo-python/
|
|||||||
mv ./etc/Nextcloud/sync-exclude.lst ./usr/bin/
|
mv ./etc/Nextcloud/sync-exclude.lst ./usr/bin/
|
||||||
rm -rf ./etc
|
rm -rf ./etc
|
||||||
|
|
||||||
sed -i -e 's|Icon=nextcloud|Icon=Nextcloud|g' usr/share/applications/nextcloud.desktop # Bug in desktop file?
|
DESKTOP_FILE=/app/usr/share/applications/${LINUX_APPLICATION_ID}.desktop
|
||||||
|
sed -i -e 's|Icon=nextcloud|Icon=Nextcloud|g' ${DESKTOP_FILE} # Bug in desktop file?
|
||||||
cp ./usr/share/icons/hicolor/512x512/apps/Nextcloud.png . # Workaround for linuxeployqt bug, FIXME
|
cp ./usr/share/icons/hicolor/512x512/apps/Nextcloud.png . # Workaround for linuxeployqt bug, FIXME
|
||||||
|
|
||||||
|
|
||||||
@@ -87,17 +88,12 @@ chmod a+x linuxdeployqt*.AppImage
|
|||||||
rm ./linuxdeployqt-continuous-x86_64.AppImage
|
rm ./linuxdeployqt-continuous-x86_64.AppImage
|
||||||
unset QTDIR; unset QT_PLUGIN_PATH ; unset LD_LIBRARY_PATH
|
unset QTDIR; unset QT_PLUGIN_PATH ; unset LD_LIBRARY_PATH
|
||||||
export LD_LIBRARY_PATH=/app/usr/lib/
|
export LD_LIBRARY_PATH=/app/usr/lib/
|
||||||
./squashfs-root/AppRun /app/usr/share/applications/nextcloud.desktop -bundle-non-qt-libs
|
./squashfs-root/AppRun ${DESKTOP_FILE} -bundle-non-qt-libs
|
||||||
|
|
||||||
# Set origin
|
# Set origin
|
||||||
./squashfs-root/usr/bin/patchelf --set-rpath '$ORIGIN/' /app/usr/lib/libnextcloudsync.so.0
|
./squashfs-root/usr/bin/patchelf --set-rpath '$ORIGIN/' /app/usr/lib/libnextcloudsync.so.0
|
||||||
|
|
||||||
# Build AppImage
|
# Build AppImage
|
||||||
./squashfs-root/AppRun /app/usr/share/applications/nextcloud.desktop -appimage
|
./squashfs-root/AppRun ${DESKTOP_FILE} -appimage
|
||||||
|
|
||||||
mv Nextcloud*.AppImage Nextcloud-${SUFFIX}-${DRONE_COMMIT}-x86_64.AppImage
|
mv Nextcloud*.AppImage Nextcloud-${SUFFIX}-${DRONE_COMMIT}-x86_64.AppImage
|
||||||
|
|
||||||
curl --upload-file $(readlink -f ./Nextcloud*.AppImage) https://transfer.sh/Nextcloud-${SUFFIX}-${DRONE_COMMIT}-x86_64.AppImage
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "Get the AppImage at the link above!"
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ Build-Depends: cmake,
|
|||||||
extra-cmake-modules (>= 5.16),
|
extra-cmake-modules (>= 5.16),
|
||||||
libkf5kio-dev,
|
libkf5kio-dev,
|
||||||
libcmocka-dev,
|
libcmocka-dev,
|
||||||
|
libcloudproviders-dev,
|
||||||
|
libdbus-1-dev,
|
||||||
libhttp-dav-perl,
|
libhttp-dav-perl,
|
||||||
libinotify-dev [kfreebsd-any],
|
libinotify-dev [kfreebsd-any],
|
||||||
libqt5svg5-dev,
|
libqt5svg5-dev,
|
||||||
|
|||||||
48
admin/linux/debian/debian.oldstable/changelog
Normal file
48
admin/linux/debian/debian.oldstable/changelog
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
nextcloud-client (2.3.3-1.0~oldstable1) oldstable; urgency=medium
|
||||||
|
|
||||||
|
* Debian build support for the forked client.
|
||||||
|
|
||||||
|
-- István Váradi <ivaradi@varadiistvan.hu> Mon, 6 Nov 2017 20:20:04 +0100
|
||||||
|
|
||||||
|
nextcloud-client (2.3.1-1.0) oldstable; urgency=medium
|
||||||
|
|
||||||
|
* New upstream version
|
||||||
|
|
||||||
|
-- István Váradi <ivaradi@varadiistvan.hu> Thu, 23 Mar 2017 19:07:36 +0100
|
||||||
|
|
||||||
|
nextcloud-client (2.3.0-1.0) oldstable; urgency=medium
|
||||||
|
|
||||||
|
* New upstream version
|
||||||
|
|
||||||
|
-- István Váradi <ivaradi@varadiistvan.hu> Tue, 21 Mar 2017 19:34:13 +0100
|
||||||
|
|
||||||
|
nextcloud-client (2.2.4-1.4) oldstable; urgency=medium
|
||||||
|
|
||||||
|
* The locale-specific icon names are correct too
|
||||||
|
|
||||||
|
-- István Váradi <ivaradi@varadiistvan.hu> Tue, 7 Feb 2017 19:55:40 +0100
|
||||||
|
|
||||||
|
nextcloud-client (2.2.4-1.3) oldstable; urgency=medium
|
||||||
|
|
||||||
|
* Caja syncstate plugin is built.
|
||||||
|
* The syncstate plugin has application-specific name
|
||||||
|
|
||||||
|
-- István Váradi <ivaradi@varadiistvan.hu> Fri, 27 Jan 2017 19:34:18 +0100
|
||||||
|
|
||||||
|
nextcloud-client (2.2.4-1.2) oldstable; urgency=medium
|
||||||
|
|
||||||
|
* Fixed appname in the Nemo syncstate extension.
|
||||||
|
|
||||||
|
-- István Váradi <ivaradi@varadiistvan.hu> Thu, 19 Jan 2017 16:46:50 +0100
|
||||||
|
|
||||||
|
nextcloud-client (2.2.4-1.1) oldstable; urgency=medium
|
||||||
|
|
||||||
|
* Added Nautilus and Nemo syncstate extensions.
|
||||||
|
|
||||||
|
-- István Váradi <ivaradi@varadiistvan.hu> Tue, 17 Jan 2017 19:55:32 +0100
|
||||||
|
|
||||||
|
nextcloud-client (2.2.4-1.0) oldstable; urgency=medium
|
||||||
|
|
||||||
|
* Initial release.
|
||||||
|
|
||||||
|
-- István Váradi <ivaradi@varadiistvan.hu> Wed, 14 Dec 2016 20:07:46 +0100
|
||||||
84
admin/linux/debian/debian.oldstable/control
Normal file
84
admin/linux/debian/debian.oldstable/control
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
Source: nextcloud-client
|
||||||
|
Section: contrib/devel
|
||||||
|
Priority: optional
|
||||||
|
Maintainer: István Váradi <ivaradi@varadiistvan.hu>
|
||||||
|
Build-Depends: cmake,
|
||||||
|
debhelper,
|
||||||
|
cdbs,
|
||||||
|
dh-python,
|
||||||
|
extra-cmake-modules (>= 5.16),
|
||||||
|
kdelibs5-dev,
|
||||||
|
kio-dev,
|
||||||
|
libcmocka-dev,
|
||||||
|
libdbus-1-dev,
|
||||||
|
libhttp-dav-perl,
|
||||||
|
libinotify-dev [kfreebsd-any],
|
||||||
|
libqt5webkit5-dev,
|
||||||
|
libqt5svg5-dev,
|
||||||
|
libsqlite3-dev,
|
||||||
|
libssl-dev (>= 1.1.0),
|
||||||
|
zlib1g-dev,
|
||||||
|
optipng,
|
||||||
|
pkg-kde-tools,
|
||||||
|
python-sphinx | python3-sphinx,
|
||||||
|
python3-all,
|
||||||
|
qt5keychain-dev,
|
||||||
|
qtwebengine5-dev,
|
||||||
|
qtdeclarative5-dev,
|
||||||
|
qttools5-dev,
|
||||||
|
qttools5-dev-tools,
|
||||||
|
xvfb
|
||||||
|
Standards-Version: 3.9.8
|
||||||
|
Homepage: https://github.com/nextcloud/client_theming
|
||||||
|
#Vcs-Git: git://anonscm.debian.org/collab-maint/nextcloud-client.git
|
||||||
|
#Vcs-Browser: https://anonscm.debian.org/cgit/collab-maint/nextcloud-client.git
|
||||||
|
|
||||||
|
Package: nextcloud-client
|
||||||
|
Architecture: any
|
||||||
|
Depends: libnextcloudsync0 (=${binary:Version}), ${shlibs:Depends}, ${misc:Depends}, nextcloud-client-l10n
|
||||||
|
Recommends: libgnome-keyring0
|
||||||
|
Description: Nextcloud desktop sync client
|
||||||
|
Use the desktop client to keep your files synchronized
|
||||||
|
between your Nextcloud server and your desktop. Select
|
||||||
|
one or more directories on your local machine and always
|
||||||
|
have access to your latest files wherever you are.
|
||||||
|
|
||||||
|
Package: libnextcloudsync0
|
||||||
|
Architecture: any
|
||||||
|
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||||
|
Description: Nextcloud sync library
|
||||||
|
Used by the Nextcloud desktop client as the synchronization engine.
|
||||||
|
|
||||||
|
Package: libnextcloudsync-dev
|
||||||
|
Architecture: any
|
||||||
|
Section: contrib/libdevel
|
||||||
|
Depends: libnextcloudsync0 (=${binary:Version}), ${misc:Depends}
|
||||||
|
Description: Nextcloud sync library development files
|
||||||
|
The headers and development library for the Nextcloud sync library.
|
||||||
|
|
||||||
|
Package: nextcloud-client-l10n
|
||||||
|
Architecture: all
|
||||||
|
Depends: ${misc:Depends}
|
||||||
|
Description: Nextcloud client internatialization files
|
||||||
|
The translation files.
|
||||||
|
|
||||||
|
Package: nextcloud-client-nautilus
|
||||||
|
Architecture: all
|
||||||
|
Depends: nextcloud-client (>=${binary:Version}), libnextcloudsync0, python-nautilus, nautilus, ${misc:Depends}
|
||||||
|
Description: Nautilus plugin for Nextcloud
|
||||||
|
This package contains a Nautilus plugin to display
|
||||||
|
synchronization status icons for Nextcloud files.
|
||||||
|
|
||||||
|
Package: nextcloud-client-nemo
|
||||||
|
Architecture: all
|
||||||
|
Depends: nextcloud-client (>=${binary:Version}), libnextcloudsync0, python-nemo, nemo, ${misc:Depends}
|
||||||
|
Description: Nemo plugin for Nextcloud
|
||||||
|
This package contains a Nemo plugin to display
|
||||||
|
synchronization status icons for Nextcloud files.
|
||||||
|
|
||||||
|
Package: nextcloud-client-caja
|
||||||
|
Architecture: all
|
||||||
|
Depends: nextcloud-client (>=${binary:Version}), libnextcloudsync0, python-caja, caja, ${misc:Depends}
|
||||||
|
Description: Caja plugin for Nextcloud
|
||||||
|
This package contains a Caja plugin to display
|
||||||
|
synchronization status icons for Nextcloud files.
|
||||||
@@ -9,7 +9,10 @@ Build-Depends: cmake,
|
|||||||
extra-cmake-modules (>= 5.16),
|
extra-cmake-modules (>= 5.16),
|
||||||
kdelibs5-dev,
|
kdelibs5-dev,
|
||||||
kio-dev,
|
kio-dev,
|
||||||
|
libavcodec58,
|
||||||
libcmocka-dev,
|
libcmocka-dev,
|
||||||
|
libcloudproviders-dev,
|
||||||
|
libdbus-1-dev,
|
||||||
libhttp-dav-perl,
|
libhttp-dav-perl,
|
||||||
libinotify-dev [kfreebsd-any],
|
libinotify-dev [kfreebsd-any],
|
||||||
libqt5webkit5-dev,
|
libqt5webkit5-dev,
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
usr/bin
|
||||||
|
usr/share/applications
|
||||||
|
usr/share/icons
|
||||||
|
debian/101-sync-inotify.conf etc/sysctl.d
|
||||||
@@ -10,6 +10,8 @@ Build-Depends: cmake,
|
|||||||
kdelibs5-dev,
|
kdelibs5-dev,
|
||||||
libkf5kio-dev,
|
libkf5kio-dev,
|
||||||
libcmocka-dev,
|
libcmocka-dev,
|
||||||
|
libcloudproviders-dev,
|
||||||
|
libdbus-1-dev,
|
||||||
libhttp-dav-perl,
|
libhttp-dav-perl,
|
||||||
libinotify-dev [kfreebsd-any],
|
libinotify-dev [kfreebsd-any],
|
||||||
libqt5svg5-dev,
|
libqt5svg5-dev,
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
usr/bin
|
usr/bin
|
||||||
usr/share/applications
|
usr/share/applications
|
||||||
|
usr/share/cloud-providers/
|
||||||
|
usr/share/dbus-1/services/
|
||||||
usr/share/icons
|
usr/share/icons
|
||||||
debian/101-sync-inotify.conf etc/sysctl.d
|
debian/101-sync-inotify.conf etc/sysctl.d
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ if ! wget http://ppa.launchpad.net/${repo}/ubuntu/pool/main/n/nextcloud-client/n
|
|||||||
origsourceopt="-sa"
|
origsourceopt="-sa"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for distribution in xenial bionic disco eoan stable; do
|
for distribution in xenial bionic disco eoan stable oldstable; do
|
||||||
rm -rf nextcloud-client_${basever}
|
rm -rf nextcloud-client_${basever}
|
||||||
cp -a ${DRONE_WORKSPACE} nextcloud-client_${basever}
|
cp -a ${DRONE_WORKSPACE} nextcloud-client_${basever}
|
||||||
|
|
||||||
@@ -98,26 +98,46 @@ if test "${pull_request}" = "master"; then
|
|||||||
PPA=$PPA_BETA
|
PPA=$PPA_BETA
|
||||||
OBS_PROJECT=$OBS_PROJECT_BETA
|
OBS_PROJECT=$OBS_PROJECT_BETA
|
||||||
fi
|
fi
|
||||||
OBS_SUBDIR="${OBS_PROJECT}/${OBS_PACKAGE}"
|
|
||||||
|
|
||||||
if test -f ~/.has_ppa_keys; then
|
if test -f ~/.has_ppa_keys; then
|
||||||
for changes in nextcloud-client_*~+([a-z])1_source.changes; do
|
for changes in nextcloud-client_*~+([a-z])1_source.changes; do
|
||||||
dput $PPA $changes > /dev/null
|
case "${changes}" in
|
||||||
|
*oldstable1*)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
dput $PPA $changes > /dev/null
|
||||||
|
;;
|
||||||
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
mkdir osc
|
for distribution in stable oldstable; do
|
||||||
cd osc
|
if test "${distribution}" = "oldstable"; then
|
||||||
osc co ${OBS_PROJECT} ${OBS_PACKAGE}
|
pkgsuffix=".${distribution}"
|
||||||
if test "$(ls ${OBS_SUBDIR})"; then
|
pkgvertag="~${distribution}1"
|
||||||
osc delete ${OBS_SUBDIR}/*
|
else
|
||||||
fi
|
pkgsuffix=""
|
||||||
cp ../nextcloud-client*.orig.tar.* ${OBS_SUBDIR}/
|
pkgvertag=""
|
||||||
cp ../nextcloud-client_*[0-9.][0-9].dsc ${OBS_SUBDIR}/
|
fi
|
||||||
cp ../nextcloud-client_*[0-9.][0-9].debian.tar* ${OBS_SUBDIR}/
|
|
||||||
cp ../nextcloud-client_*[0-9.][0-9]_source.changes ${OBS_SUBDIR}/
|
|
||||||
osc add ${OBS_SUBDIR}/*
|
|
||||||
|
|
||||||
cd ${OBS_SUBDIR}
|
package="${OBS_PACKAGE}${pkgsuffix}"
|
||||||
osc commit -m "Travis update"
|
OBS_SUBDIR="${OBS_PROJECT}/${package}"
|
||||||
|
|
||||||
|
mkdir -p osc
|
||||||
|
pushd osc
|
||||||
|
osc co ${OBS_PROJECT} ${package}
|
||||||
|
if test "$(ls ${OBS_SUBDIR})"; then
|
||||||
|
osc delete ${OBS_SUBDIR}/*
|
||||||
|
fi
|
||||||
|
|
||||||
|
cp ../nextcloud-client*.orig.tar.* ${OBS_SUBDIR}/
|
||||||
|
cp ../nextcloud-client_*[0-9.][0-9]${pkgvertag}.dsc ${OBS_SUBDIR}/
|
||||||
|
cp ../nextcloud-client_*[0-9.][0-9]${pkgvertag}.debian.tar* ${OBS_SUBDIR}/
|
||||||
|
cp ../nextcloud-client_*[0-9.][0-9]${pkgvertag}_source.changes ${OBS_SUBDIR}/
|
||||||
|
osc add ${OBS_SUBDIR}/*
|
||||||
|
|
||||||
|
cd ${OBS_SUBDIR}
|
||||||
|
osc commit -m "Travis update"
|
||||||
|
popd
|
||||||
|
done
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
21
admin/linux/upload-appimage.sh
Executable file
21
admin/linux/upload-appimage.sh
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
set -xe
|
||||||
|
|
||||||
|
cd /build
|
||||||
|
|
||||||
|
# Upload AppImage
|
||||||
|
APPIMAGE=$(readlink -f ./Nextcloud*.AppImage)
|
||||||
|
BASENAME=$(basename ${APPIMAGE})
|
||||||
|
|
||||||
|
if curl --max-time 900 --upload-file ${APPIMAGE} https://transfer.sh/${BASENAME}
|
||||||
|
then
|
||||||
|
echo
|
||||||
|
echo "Get the AppImage at the link above!"
|
||||||
|
else
|
||||||
|
echo
|
||||||
|
echo "Upload failed, however this is an optional step."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Don't let the Drone build fail
|
||||||
|
exit 0
|
||||||
@@ -49,7 +49,7 @@ fi
|
|||||||
if [ ! -z "$identity" ]; then
|
if [ ! -z "$identity" ]; then
|
||||||
echo "Will try to sign the installer"
|
echo "Will try to sign the installer"
|
||||||
pushd $install_path
|
pushd $install_path
|
||||||
productsign --sign "$identity" "$installer_file" "$installer_file.new"
|
productsign --timestamp --sign "$identity" "$installer_file" "$installer_file.new"
|
||||||
mv "$installer_file".new "$installer_file"
|
mv "$installer_file".new "$installer_file"
|
||||||
popd
|
popd
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>@MIRALL_VERSION_STRING@</string>
|
<string>@MIRALL_VERSION_STRING@</string>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string>(C) 2014-2018 @APPLICATION_VENDOR@</string>
|
<string>(C) 2014-2020 @APPLICATION_VENDOR@</string>
|
||||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>SUShowReleaseNotes</key>
|
<key>SUShowReleaseNotes</key>
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ To prevent automatic updates and disallow manual overrides:
|
|||||||
|
|
||||||
1. Edit this Registry key:
|
1. Edit this Registry key:
|
||||||
|
|
||||||
``HKEY_LOCAL_MACHINE\Software\Policies\Nextcloud\Nextcloud``
|
``HKEY_LOCAL_MACHINE\Software\Policies\Nextcloud GmbH\Nextcloud``
|
||||||
|
|
||||||
2. Add the key ``skipUpdateCheck`` (of type DWORD).
|
2. Add the key ``skipUpdateCheck`` (of type DWORD).
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ download page.
|
|||||||
System Requirements
|
System Requirements
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
|
||||||
- Windows 7+
|
- Windows 8.1+
|
||||||
- macOS 10.7+ (**64-bit only**)
|
- macOS 10.7+ (**64-bit only**)
|
||||||
- CentOS 6 & 7 (64-bit only)
|
- CentOS 6 & 7 (64-bit only)
|
||||||
- Debian 8.0 & 9.0
|
- Debian 8.0 & 9.0
|
||||||
@@ -36,8 +36,7 @@ System Requirements
|
|||||||
- openSUSE Leap 42.2 & 42.3
|
- openSUSE Leap 42.2 & 42.3
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
For Linux distributions, we support, if technically feasible, the latest 2 versions per platform and the previous `LTS`_.
|
For Linux distributions, we support, if technically feasible, the latest 2 versions per platform and the previous LTS.
|
||||||
>>>>>>> b2da03441... update supported linux platforms
|
|
||||||
|
|
||||||
Installation Wizard
|
Installation Wizard
|
||||||
-------------------
|
-------------------
|
||||||
|
|||||||
@@ -467,6 +467,7 @@
|
|||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = FinderSyncExt/FinderSyncExt.entitlements;
|
CODE_SIGN_ENTITLEMENTS = FinderSyncExt/FinderSyncExt.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "-";
|
CODE_SIGN_IDENTITY = "-";
|
||||||
|
CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
|||||||
@@ -35,11 +35,11 @@ public:
|
|||||||
|
|
||||||
QString contextMenuTitle() const
|
QString contextMenuTitle() const
|
||||||
{
|
{
|
||||||
return _strings.value("CONTEXT_MENU_TITLE", "ownCloud");
|
return _strings.value("CONTEXT_MENU_TITLE", "Nextcloud");
|
||||||
}
|
}
|
||||||
QString shareActionTitle() const
|
QString shareActionTitle() const
|
||||||
{
|
{
|
||||||
return _strings.value("SHARE_MENU_TITLE", "Share...");
|
return _strings.value("SHARE_MENU_TITLE", "Share …");
|
||||||
}
|
}
|
||||||
|
|
||||||
QString copyPrivateLinkTitle() const { return _strings["COPY_PRIVATE_LINK_MENU_TITLE"]; }
|
QString copyPrivateLinkTitle() const { return _strings["COPY_PRIVATE_LINK_MENU_TITLE"]; }
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[D-BUS Service]
|
[D-BUS Service]
|
||||||
Name=@LIBCLOUDPROVIDERS_DBUS_BUS_NAME@
|
Name=@LIBCLOUDPROVIDERS_DBUS_BUS_NAME@
|
||||||
Exec=@APPLICATION_EXECUTABLE@
|
Exec=@APPLICATION_EXECUTABLE@ --background
|
||||||
|
|
||||||
|
|||||||
@@ -182,20 +182,38 @@ IFACEMETHODIMP OCContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
|
|||||||
{
|
{
|
||||||
std::wstring command;
|
std::wstring command;
|
||||||
|
|
||||||
|
CMINVOKECOMMANDINFOEX *piciEx = nullptr;
|
||||||
|
if (pici->cbSize == sizeof(CMINVOKECOMMANDINFOEX))
|
||||||
|
piciEx = (CMINVOKECOMMANDINFOEX*)pici;
|
||||||
|
|
||||||
// For the Unicode case, if the high-order word is not zero, the
|
// For the Unicode case, if the high-order word is not zero, the
|
||||||
// command's verb string is in lpcmi->lpVerbW.
|
// command's verb string is in lpcmi->lpVerbW.
|
||||||
if (HIWORD(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW))
|
if (piciEx
|
||||||
{
|
&& (piciEx->fMask & CMIC_MASK_UNICODE)
|
||||||
command = ((CMINVOKECOMMANDINFOEX *)pici)->lpVerbW;
|
&& HIWORD(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW)) {
|
||||||
} else {
|
|
||||||
|
command = piciEx->lpVerbW;
|
||||||
|
|
||||||
|
// Verify that we handle the verb
|
||||||
|
bool handled = false;
|
||||||
|
for (auto &item : m_info.menuItems) {
|
||||||
|
if (item.command == command) {
|
||||||
|
handled = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!handled)
|
||||||
|
return E_FAIL;
|
||||||
|
} else if (IS_INTRESOURCE(pici->lpVerb)) {
|
||||||
// If the command cannot be identified through the verb string, then
|
// If the command cannot be identified through the verb string, then
|
||||||
// check the identifier offset.
|
// check the identifier offset.
|
||||||
|
|
||||||
auto offset = LOWORD(pici->lpVerb);
|
auto offset = LOWORD(pici->lpVerb);
|
||||||
if (offset >= m_info.menuItems.size())
|
if (offset >= m_info.menuItems.size())
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
|
|
||||||
command = m_info.menuItems[offset].command;
|
command = m_info.menuItems[offset].command;
|
||||||
|
} else {
|
||||||
|
return E_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
OCClientInterface::SendRequest(command.data(), m_selectedFiles);
|
OCClientInterface::SendRequest(command.data(), m_selectedFiles);
|
||||||
|
|||||||
1
src/3rdparty/qtmacgoodies
vendored
1
src/3rdparty/qtmacgoodies
vendored
Submodule src/3rdparty/qtmacgoodies deleted from b59d091b3e
@@ -29,6 +29,11 @@ if(NOT MSVC)
|
|||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_FORTIFY_SOURCE=2")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_FORTIFY_SOURCE=2")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_FORTIFY_SOURCE=2")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_FORTIFY_SOURCE=2")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Calling Qt's qCWarning(category, ...) with no params for "..." is a GNU
|
||||||
|
# extension (C++11 §16.3/4 forbids them). Silence clang's warnings.
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-gnu-zero-variadic-macro-arguments")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-gnu-zero-variadic-macro-arguments")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
|
|||||||
@@ -288,7 +288,11 @@ void ExcludedFiles::addManualExclude(const QByteArray &expr)
|
|||||||
|
|
||||||
void ExcludedFiles::addManualExclude(const QByteArray &expr, const QByteArray &basePath)
|
void ExcludedFiles::addManualExclude(const QByteArray &expr, const QByteArray &basePath)
|
||||||
{
|
{
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
Q_ASSERT(basePath.size() >= 2 && basePath.at(1) == ':');
|
||||||
|
#else
|
||||||
Q_ASSERT(basePath.startsWith('/'));
|
Q_ASSERT(basePath.startsWith('/'));
|
||||||
|
#endif
|
||||||
Q_ASSERT(basePath.endsWith('/'));
|
Q_ASSERT(basePath.endsWith('/'));
|
||||||
|
|
||||||
auto key = basePath;
|
auto key = basePath;
|
||||||
|
|||||||
@@ -35,12 +35,11 @@ set(client_UI_SRCS
|
|||||||
addcertificatedialog.ui
|
addcertificatedialog.ui
|
||||||
proxyauthdialog.ui
|
proxyauthdialog.ui
|
||||||
mnemonicdialog.ui
|
mnemonicdialog.ui
|
||||||
|
wizard/flow2authwidget.ui
|
||||||
wizard/owncloudadvancedsetuppage.ui
|
wizard/owncloudadvancedsetuppage.ui
|
||||||
wizard/owncloudconnectionmethoddialog.ui
|
wizard/owncloudconnectionmethoddialog.ui
|
||||||
wizard/owncloudhttpcredspage.ui
|
wizard/owncloudhttpcredspage.ui
|
||||||
wizard/owncloudoauthcredspage.ui
|
wizard/owncloudoauthcredspage.ui
|
||||||
wizard/flow2authcredspage.ui
|
|
||||||
wizard/flow2authwidget.ui
|
|
||||||
wizard/owncloudsetupnocredspage.ui
|
wizard/owncloudsetupnocredspage.ui
|
||||||
wizard/owncloudwizardresultpage.ui
|
wizard/owncloudwizardresultpage.ui
|
||||||
wizard/webview.ui
|
wizard/webview.ui
|
||||||
@@ -91,7 +90,7 @@ set(client_SRCS
|
|||||||
syncrunfilelog.cpp
|
syncrunfilelog.cpp
|
||||||
systray.cpp
|
systray.cpp
|
||||||
thumbnailjob.cpp
|
thumbnailjob.cpp
|
||||||
quotainfo.cpp
|
userinfo.cpp
|
||||||
accountstate.cpp
|
accountstate.cpp
|
||||||
addcertificatedialog.cpp
|
addcertificatedialog.cpp
|
||||||
authenticationdialog.cpp
|
authenticationdialog.cpp
|
||||||
@@ -103,12 +102,14 @@ set(client_SRCS
|
|||||||
servernotificationhandler.cpp
|
servernotificationhandler.cpp
|
||||||
guiutility.cpp
|
guiutility.cpp
|
||||||
elidedlabel.cpp
|
elidedlabel.cpp
|
||||||
|
headerbanner.cpp
|
||||||
iconjob.cpp
|
iconjob.cpp
|
||||||
remotewipe.cpp
|
remotewipe.cpp
|
||||||
creds/credentialsfactory.cpp
|
creds/credentialsfactory.cpp
|
||||||
creds/httpcredentialsgui.cpp
|
creds/httpcredentialsgui.cpp
|
||||||
creds/oauth.cpp
|
creds/oauth.cpp
|
||||||
creds/flow2auth.cpp
|
creds/flow2auth.cpp
|
||||||
|
creds/keychainchunk.cpp
|
||||||
creds/webflowcredentials.cpp
|
creds/webflowcredentials.cpp
|
||||||
creds/webflowcredentialsdialog.cpp
|
creds/webflowcredentialsdialog.cpp
|
||||||
wizard/postfixlineedit.cpp
|
wizard/postfixlineedit.cpp
|
||||||
@@ -145,8 +146,6 @@ set(updater_SRCS
|
|||||||
|
|
||||||
IF( APPLE )
|
IF( APPLE )
|
||||||
list(APPEND client_SRCS cocoainitializer_mac.mm)
|
list(APPEND client_SRCS cocoainitializer_mac.mm)
|
||||||
list(APPEND client_SRCS settingsdialogmac.cpp)
|
|
||||||
list(REMOVE_ITEM client_SRCS settingsdialog.cpp)
|
|
||||||
list(APPEND client_SRCS socketapisocket_mac.mm)
|
list(APPEND client_SRCS socketapisocket_mac.mm)
|
||||||
list(APPEND client_SRCS systray.mm)
|
list(APPEND client_SRCS systray.mm)
|
||||||
|
|
||||||
@@ -176,17 +175,6 @@ set(3rdparty_SRC
|
|||||||
../3rdparty/kmessagewidget/kmessagewidget.cpp
|
../3rdparty/kmessagewidget/kmessagewidget.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if (APPLE)
|
|
||||||
list(APPEND 3rdparty_SRC
|
|
||||||
../3rdparty/qtmacgoodies/src/macpreferenceswindow.mm
|
|
||||||
../3rdparty/qtmacgoodies/src/macstandardicon.mm
|
|
||||||
../3rdparty/qtmacgoodies/src/macwindow.mm
|
|
||||||
)
|
|
||||||
# We want to access Cocoa specific structures in the code above
|
|
||||||
# and need the platform plugin interface for that - which is private.
|
|
||||||
include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT WIN32)
|
if(NOT WIN32)
|
||||||
list(APPEND 3rdparty_SRC ../3rdparty/qtlockedfile/qtlockedfile_unix.cpp)
|
list(APPEND 3rdparty_SRC ../3rdparty/qtlockedfile/qtlockedfile_unix.cpp)
|
||||||
else()
|
else()
|
||||||
@@ -342,7 +330,6 @@ ENDIF()
|
|||||||
target_include_directories(${APPLICATION_EXECUTABLE} PRIVATE
|
target_include_directories(${APPLICATION_EXECUTABLE} PRIVATE
|
||||||
${CMAKE_SOURCE_DIR}/src/3rdparty/QProgressIndicator
|
${CMAKE_SOURCE_DIR}/src/3rdparty/QProgressIndicator
|
||||||
${CMAKE_SOURCE_DIR}/src/3rdparty/qtlockedfile
|
${CMAKE_SOURCE_DIR}/src/3rdparty/qtlockedfile
|
||||||
${CMAKE_SOURCE_DIR}/src/3rdparty/qtmacgoodies/src
|
|
||||||
${CMAKE_SOURCE_DIR}/src/3rdparty/qtsingleapplication
|
${CMAKE_SOURCE_DIR}/src/3rdparty/qtsingleapplication
|
||||||
${CMAKE_SOURCE_DIR}/src/3rdparty/kmessagewidget
|
${CMAKE_SOURCE_DIR}/src/3rdparty/kmessagewidget
|
||||||
${CMAKE_CURRENT_BINARY_DIR}
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
#include "configfile.h"
|
#include "configfile.h"
|
||||||
#include "account.h"
|
#include "account.h"
|
||||||
#include "accountstate.h"
|
#include "accountstate.h"
|
||||||
#include "quotainfo.h"
|
#include "userinfo.h"
|
||||||
#include "accountmanager.h"
|
#include "accountmanager.h"
|
||||||
#include "owncloudsetupwizard.h"
|
#include "owncloudsetupwizard.h"
|
||||||
#include "creds/abstractcredentials.h"
|
#include "creds/abstractcredentials.h"
|
||||||
@@ -57,10 +57,6 @@
|
|||||||
|
|
||||||
#include "account.h"
|
#include "account.h"
|
||||||
|
|
||||||
#ifdef Q_OS_MAC
|
|
||||||
#include "settingsdialogmac.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace OCC {
|
namespace OCC {
|
||||||
|
|
||||||
Q_LOGGING_CATEGORY(lcAccountSettings, "nextcloud.gui.account.settings", QtInfoMsg)
|
Q_LOGGING_CATEGORY(lcAccountSettings, "nextcloud.gui.account.settings", QtInfoMsg)
|
||||||
@@ -113,13 +109,13 @@ protected:
|
|||||||
|
|
||||||
AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
|
AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
, ui(new Ui::AccountSettings)
|
, _ui(new Ui::AccountSettings)
|
||||||
, _wasDisabledBefore(false)
|
, _wasDisabledBefore(false)
|
||||||
, _accountState(accountState)
|
, _accountState(accountState)
|
||||||
, _quotaInfo(accountState)
|
, _userInfo(accountState, false, true)
|
||||||
, _menuShown(false)
|
, _menuShown(false)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
_ui->setupUi(this);
|
||||||
|
|
||||||
_model = new FolderStatusModel;
|
_model = new FolderStatusModel;
|
||||||
_model->setAccountState(_accountState);
|
_model->setAccountState(_accountState);
|
||||||
@@ -127,37 +123,40 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
|
|||||||
FolderStatusDelegate *delegate = new FolderStatusDelegate;
|
FolderStatusDelegate *delegate = new FolderStatusDelegate;
|
||||||
delegate->setParent(this);
|
delegate->setParent(this);
|
||||||
|
|
||||||
ui->_folderList->header()->hide();
|
// Connect styleChanged events to our widgets, so they can adapt (Dark-/Light-Mode switching)
|
||||||
ui->_folderList->setItemDelegate(delegate);
|
connect(this, &AccountSettings::styleChanged, delegate, &FolderStatusDelegate::slotStyleChanged);
|
||||||
ui->_folderList->setModel(_model);
|
|
||||||
|
_ui->_folderList->header()->hide();
|
||||||
|
_ui->_folderList->setItemDelegate(delegate);
|
||||||
|
_ui->_folderList->setModel(_model);
|
||||||
#if defined(Q_OS_MAC)
|
#if defined(Q_OS_MAC)
|
||||||
ui->_folderList->setMinimumWidth(400);
|
_ui->_folderList->setMinimumWidth(400);
|
||||||
#else
|
#else
|
||||||
ui->_folderList->setMinimumWidth(300);
|
_ui->_folderList->setMinimumWidth(300);
|
||||||
#endif
|
#endif
|
||||||
new ToolTipUpdater(ui->_folderList);
|
new ToolTipUpdater(_ui->_folderList);
|
||||||
|
|
||||||
auto mouseCursorChanger = new MouseCursorChanger(this);
|
auto mouseCursorChanger = new MouseCursorChanger(this);
|
||||||
mouseCursorChanger->folderList = ui->_folderList;
|
mouseCursorChanger->folderList = _ui->_folderList;
|
||||||
mouseCursorChanger->model = _model;
|
mouseCursorChanger->model = _model;
|
||||||
ui->_folderList->setMouseTracking(true);
|
_ui->_folderList->setMouseTracking(true);
|
||||||
ui->_folderList->setAttribute(Qt::WA_Hover, true);
|
_ui->_folderList->setAttribute(Qt::WA_Hover, true);
|
||||||
ui->_folderList->installEventFilter(mouseCursorChanger);
|
_ui->_folderList->installEventFilter(mouseCursorChanger);
|
||||||
|
|
||||||
createAccountToolbox();
|
createAccountToolbox();
|
||||||
connect(AccountManager::instance(), &AccountManager::accountAdded,
|
connect(AccountManager::instance(), &AccountManager::accountAdded,
|
||||||
this, &AccountSettings::slotAccountAdded);
|
this, &AccountSettings::slotAccountAdded);
|
||||||
connect(this, &AccountSettings::removeAccountFolders,
|
connect(this, &AccountSettings::removeAccountFolders,
|
||||||
AccountManager::instance(), &AccountManager::removeAccountFolders);
|
AccountManager::instance(), &AccountManager::removeAccountFolders);
|
||||||
connect(ui->_folderList, &QWidget::customContextMenuRequested,
|
connect(_ui->_folderList, &QWidget::customContextMenuRequested,
|
||||||
this, &AccountSettings::slotCustomContextMenuRequested);
|
this, &AccountSettings::slotCustomContextMenuRequested);
|
||||||
connect(ui->_folderList, &QAbstractItemView::clicked,
|
connect(_ui->_folderList, &QAbstractItemView::clicked,
|
||||||
this, &AccountSettings::slotFolderListClicked);
|
this, &AccountSettings::slotFolderListClicked);
|
||||||
connect(ui->_folderList, &QTreeView::expanded, this, &AccountSettings::refreshSelectiveSyncStatus);
|
connect(_ui->_folderList, &QTreeView::expanded, this, &AccountSettings::refreshSelectiveSyncStatus);
|
||||||
connect(ui->_folderList, &QTreeView::collapsed, this, &AccountSettings::refreshSelectiveSyncStatus);
|
connect(_ui->_folderList, &QTreeView::collapsed, this, &AccountSettings::refreshSelectiveSyncStatus);
|
||||||
connect(ui->selectiveSyncNotification, &QLabel::linkActivated,
|
connect(_ui->selectiveSyncNotification, &QLabel::linkActivated,
|
||||||
this, &AccountSettings::slotLinkActivated);
|
this, &AccountSettings::slotLinkActivated);
|
||||||
connect(_model, &FolderStatusModel::suggestExpand, ui->_folderList, &QTreeView::expand);
|
connect(_model, &FolderStatusModel::suggestExpand, _ui->_folderList, &QTreeView::expand);
|
||||||
connect(_model, &FolderStatusModel::dirtyChanged, this, &AccountSettings::refreshSelectiveSyncStatus);
|
connect(_model, &FolderStatusModel::dirtyChanged, this, &AccountSettings::refreshSelectiveSyncStatus);
|
||||||
refreshSelectiveSyncStatus();
|
refreshSelectiveSyncStatus();
|
||||||
connect(_model, &QAbstractItemModel::rowsInserted,
|
connect(_model, &QAbstractItemModel::rowsInserted,
|
||||||
@@ -174,25 +173,26 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
|
|||||||
addAction(syncNowWithRemoteDiscovery);
|
addAction(syncNowWithRemoteDiscovery);
|
||||||
|
|
||||||
|
|
||||||
connect(ui->selectiveSyncApply, &QAbstractButton::clicked, _model, &FolderStatusModel::slotApplySelectiveSync);
|
connect(_ui->selectiveSyncApply, &QAbstractButton::clicked, _model, &FolderStatusModel::slotApplySelectiveSync);
|
||||||
connect(ui->selectiveSyncCancel, &QAbstractButton::clicked, _model, &FolderStatusModel::resetFolders);
|
connect(_ui->selectiveSyncCancel, &QAbstractButton::clicked, _model, &FolderStatusModel::resetFolders);
|
||||||
connect(ui->bigFolderApply, &QAbstractButton::clicked, _model, &FolderStatusModel::slotApplySelectiveSync);
|
connect(_ui->bigFolderApply, &QAbstractButton::clicked, _model, &FolderStatusModel::slotApplySelectiveSync);
|
||||||
connect(ui->bigFolderSyncAll, &QAbstractButton::clicked, _model, &FolderStatusModel::slotSyncAllPendingBigFolders);
|
connect(_ui->bigFolderSyncAll, &QAbstractButton::clicked, _model, &FolderStatusModel::slotSyncAllPendingBigFolders);
|
||||||
connect(ui->bigFolderSyncNone, &QAbstractButton::clicked, _model, &FolderStatusModel::slotSyncNoPendingBigFolders);
|
connect(_ui->bigFolderSyncNone, &QAbstractButton::clicked, _model, &FolderStatusModel::slotSyncNoPendingBigFolders);
|
||||||
|
|
||||||
connect(FolderMan::instance(), &FolderMan::folderListChanged, _model, &FolderStatusModel::resetFolders);
|
connect(FolderMan::instance(), &FolderMan::folderListChanged, _model, &FolderStatusModel::resetFolders);
|
||||||
connect(this, &AccountSettings::folderChanged, _model, &FolderStatusModel::resetFolders);
|
connect(this, &AccountSettings::folderChanged, _model, &FolderStatusModel::resetFolders);
|
||||||
|
|
||||||
|
|
||||||
QColor color = palette().highlight().color();
|
// quotaProgressBar style now set in customizeStyle()
|
||||||
ui->quotaProgressBar->setStyleSheet(QString::fromLatin1(progressBarStyleC).arg(color.name()));
|
/*QColor color = palette().highlight().color();
|
||||||
|
_ui->quotaProgressBar->setStyleSheet(QString::fromLatin1(progressBarStyleC).arg(color.name()));*/
|
||||||
|
|
||||||
ui->connectLabel->setText(tr("No account configured."));
|
_ui->connectLabel->setText(tr("No account configured."));
|
||||||
|
|
||||||
connect(_accountState, &AccountState::stateChanged, this, &AccountSettings::slotAccountStateChanged);
|
connect(_accountState, &AccountState::stateChanged, this, &AccountSettings::slotAccountStateChanged);
|
||||||
slotAccountStateChanged();
|
slotAccountStateChanged();
|
||||||
|
|
||||||
connect(&_quotaInfo, &QuotaInfo::quotaUpdated,
|
connect(&_userInfo, &UserInfo::quotaUpdated,
|
||||||
this, &AccountSettings::slotUpdateQuota);
|
this, &AccountSettings::slotUpdateQuota);
|
||||||
|
|
||||||
// Connect E2E stuff
|
// Connect E2E stuff
|
||||||
@@ -204,8 +204,10 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
|
|||||||
{
|
{
|
||||||
slotNewMnemonicGenerated();
|
slotNewMnemonicGenerated();
|
||||||
} else {
|
} else {
|
||||||
ui->encryptionMessage->hide();
|
_ui->encryptionMessage->hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customizeStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -227,9 +229,9 @@ void AccountSettings::createAccountToolbox()
|
|||||||
menu->addAction(action);
|
menu->addAction(action);
|
||||||
connect(action, &QAction::triggered, this, &AccountSettings::slotDeleteAccount);
|
connect(action, &QAction::triggered, this, &AccountSettings::slotDeleteAccount);
|
||||||
|
|
||||||
ui->_accountToolbox->setText(tr("Account") + QLatin1Char(' '));
|
_ui->_accountToolbox->setText(tr("Account") + QLatin1Char(' '));
|
||||||
ui->_accountToolbox->setMenu(menu);
|
_ui->_accountToolbox->setMenu(menu);
|
||||||
ui->_accountToolbox->setPopupMode(QToolButton::InstantPopup);
|
_ui->_accountToolbox->setPopupMode(QToolButton::InstantPopup);
|
||||||
|
|
||||||
slotAccountAdded(_accountState);
|
slotAccountAdded(_accountState);
|
||||||
}
|
}
|
||||||
@@ -237,14 +239,14 @@ void AccountSettings::createAccountToolbox()
|
|||||||
|
|
||||||
void AccountSettings::slotNewMnemonicGenerated()
|
void AccountSettings::slotNewMnemonicGenerated()
|
||||||
{
|
{
|
||||||
ui->encryptionMessage->setText(tr("This account supports end-to-end encryption"));
|
_ui->encryptionMessage->setText(tr("This account supports end-to-end encryption"));
|
||||||
|
|
||||||
QAction *mnemonic = new QAction(tr("Enable encryption"), this);
|
QAction *mnemonic = new QAction(tr("Enable encryption"), this);
|
||||||
connect(mnemonic, &QAction::triggered, this, &AccountSettings::requesetMnemonic);
|
connect(mnemonic, &QAction::triggered, this, &AccountSettings::requesetMnemonic);
|
||||||
connect(mnemonic, &QAction::triggered, ui->encryptionMessage, &KMessageWidget::hide);
|
connect(mnemonic, &QAction::triggered, _ui->encryptionMessage, &KMessageWidget::hide);
|
||||||
|
|
||||||
ui->encryptionMessage->addAction(mnemonic);
|
_ui->encryptionMessage->addAction(mnemonic);
|
||||||
ui->encryptionMessage->show();
|
_ui->encryptionMessage->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AccountSettings::slotMenuBeforeShow() {
|
void AccountSettings::slotMenuBeforeShow() {
|
||||||
@@ -252,7 +254,7 @@ void AccountSettings::slotMenuBeforeShow() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto menu = ui->_accountToolbox->menu();
|
auto menu = _ui->_accountToolbox->menu();
|
||||||
|
|
||||||
// We can't check this during the initial creation as there is no account yet then
|
// We can't check this during the initial creation as there is no account yet then
|
||||||
if (_accountState->account()->capabilities().clientSideEncryptionAvaliable()) {
|
if (_accountState->account()->capabilities().clientSideEncryptionAvaliable()) {
|
||||||
@@ -267,7 +269,7 @@ void AccountSettings::slotMenuBeforeShow() {
|
|||||||
|
|
||||||
QString AccountSettings::selectedFolderAlias() const
|
QString AccountSettings::selectedFolderAlias() const
|
||||||
{
|
{
|
||||||
QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
|
QModelIndex selected = _ui->_folderList->selectionModel()->currentIndex();
|
||||||
if (!selected.isValid())
|
if (!selected.isValid())
|
||||||
return "";
|
return "";
|
||||||
return _model->data(selected, FolderStatusDelegate::FolderAliasRole).toString();
|
return _model->data(selected, FolderStatusDelegate::FolderAliasRole).toString();
|
||||||
@@ -280,16 +282,7 @@ void AccountSettings::slotOpenAccountWizard()
|
|||||||
if (qgetenv("QT_QPA_PLATFORMTHEME") == "appmenu-qt5" || QSystemTrayIcon::isSystemTrayAvailable()) {
|
if (qgetenv("QT_QPA_PLATFORMTHEME") == "appmenu-qt5" || QSystemTrayIcon::isSystemTrayAvailable()) {
|
||||||
topLevelWidget()->close();
|
topLevelWidget()->close();
|
||||||
}
|
}
|
||||||
#ifdef Q_OS_MAC
|
|
||||||
qCDebug(lcAccountSettings) << parent() << topLevelWidget();
|
|
||||||
SettingsDialogMac *sd = qobject_cast<SettingsDialogMac *>(topLevelWidget());
|
|
||||||
|
|
||||||
if (sd) {
|
|
||||||
sd->showActivityPage();
|
|
||||||
} else {
|
|
||||||
qFatal("nope");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
OwncloudSetupWizard::runWizard(qApp, SLOT(slotownCloudWizardDone(int)), nullptr);
|
OwncloudSetupWizard::runWizard(qApp, SLOT(slotownCloudWizardDone(int)), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,7 +298,7 @@ void AccountSettings::slotToggleSignInState()
|
|||||||
|
|
||||||
void AccountSettings::doExpand()
|
void AccountSettings::doExpand()
|
||||||
{
|
{
|
||||||
ui->_folderList->expandToDepth(0);
|
_ui->_folderList->expandToDepth(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AccountSettings::slotShowMnemonic(const QString &mnemonic) {
|
void AccountSettings::slotShowMnemonic(const QString &mnemonic) {
|
||||||
@@ -553,7 +546,7 @@ void AccountSettings::slotEditCurrentIgnoredFiles()
|
|||||||
|
|
||||||
void AccountSettings::slotEditCurrentLocalIgnoredFiles()
|
void AccountSettings::slotEditCurrentLocalIgnoredFiles()
|
||||||
{
|
{
|
||||||
QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
|
QModelIndex selected = _ui->_folderList->selectionModel()->currentIndex();
|
||||||
if (!selected.isValid() || _model->classify(selected) != FolderStatusModel::SubFolder)
|
if (!selected.isValid() || _model->classify(selected) != FolderStatusModel::SubFolder)
|
||||||
return;
|
return;
|
||||||
QString fileName = _model->data(selected, FolderStatusDelegate::FolderPathRole).toString();
|
QString fileName = _model->data(selected, FolderStatusDelegate::FolderPathRole).toString();
|
||||||
@@ -625,7 +618,7 @@ void AccountSettings::slotSubfolderContextMenuRequested(const QModelIndex& index
|
|||||||
|
|
||||||
void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
|
void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
|
||||||
{
|
{
|
||||||
QTreeView *tv = ui->_folderList;
|
QTreeView *tv = _ui->_folderList;
|
||||||
QModelIndex index = tv->indexAt(pos);
|
QModelIndex index = tv->indexAt(pos);
|
||||||
if (!index.isValid()) {
|
if (!index.isValid()) {
|
||||||
return;
|
return;
|
||||||
@@ -656,7 +649,7 @@ void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
|
|||||||
ac = menu->addAction(tr("Edit Ignored Files"));
|
ac = menu->addAction(tr("Edit Ignored Files"));
|
||||||
connect(ac, &QAction::triggered, this, &AccountSettings::slotEditCurrentIgnoredFiles);
|
connect(ac, &QAction::triggered, this, &AccountSettings::slotEditCurrentIgnoredFiles);
|
||||||
|
|
||||||
if (!ui->_folderList->isExpanded(index)) {
|
if (!_ui->_folderList->isExpanded(index)) {
|
||||||
ac = menu->addAction(tr("Choose what to sync"));
|
ac = menu->addAction(tr("Choose what to sync"));
|
||||||
ac->setEnabled(folderConnected);
|
ac->setEnabled(folderConnected);
|
||||||
connect(ac, &QAction::triggered, this, &AccountSettings::doExpand);
|
connect(ac, &QAction::triggered, this, &AccountSettings::doExpand);
|
||||||
@@ -695,7 +688,7 @@ void AccountSettings::slotFolderListClicked(const QModelIndex &indx)
|
|||||||
}
|
}
|
||||||
if (_model->classify(indx) == FolderStatusModel::RootFolder) {
|
if (_model->classify(indx) == FolderStatusModel::RootFolder) {
|
||||||
// tries to find if we clicked on the '...' button.
|
// tries to find if we clicked on the '...' button.
|
||||||
QTreeView *tv = ui->_folderList;
|
QTreeView *tv = _ui->_folderList;
|
||||||
auto pos = tv->mapFromGlobal(QCursor::pos());
|
auto pos = tv->mapFromGlobal(QCursor::pos());
|
||||||
if (FolderStatusDelegate::optionsButtonRect(tv->visualRect(indx), layoutDirection()).contains(pos)) {
|
if (FolderStatusDelegate::optionsButtonRect(tv->visualRect(indx), layoutDirection()).contains(pos)) {
|
||||||
slotCustomContextMenuRequested(pos);
|
slotCustomContextMenuRequested(pos);
|
||||||
@@ -708,8 +701,8 @@ void AccountSettings::slotFolderListClicked(const QModelIndex &indx)
|
|||||||
|
|
||||||
// Expand root items on single click
|
// Expand root items on single click
|
||||||
if (_accountState && _accountState->state() == AccountState::Connected) {
|
if (_accountState && _accountState->state() == AccountState::Connected) {
|
||||||
bool expanded = !(ui->_folderList->isExpanded(indx));
|
bool expanded = !(_ui->_folderList->isExpanded(indx));
|
||||||
ui->_folderList->setExpanded(indx, expanded);
|
_ui->_folderList->setExpanded(indx, expanded);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -746,7 +739,7 @@ void AccountSettings::slotFolderWizardAccepted()
|
|||||||
qCInfo(lcAccountSettings) << "Creating folder" << definition.localPath;
|
qCInfo(lcAccountSettings) << "Creating folder" << definition.localPath;
|
||||||
if (!dir.mkpath(".")) {
|
if (!dir.mkpath(".")) {
|
||||||
QMessageBox::warning(this, tr("Folder creation failed"),
|
QMessageBox::warning(this, tr("Folder creation failed"),
|
||||||
tr("<p>Could not create local folder <i>%1</i>.")
|
tr("<p>Could not create local folder <i>%1</i>.</p>")
|
||||||
.arg(QDir::toNativeSeparators(definition.localPath)));
|
.arg(QDir::toNativeSeparators(definition.localPath)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -791,7 +784,7 @@ void AccountSettings::slotRemoveCurrentFolder()
|
|||||||
{
|
{
|
||||||
FolderMan *folderMan = FolderMan::instance();
|
FolderMan *folderMan = FolderMan::instance();
|
||||||
auto folder = folderMan->folder(selectedFolderAlias());
|
auto folder = folderMan->folder(selectedFolderAlias());
|
||||||
QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
|
QModelIndex selected = _ui->_folderList->selectionModel()->currentIndex();
|
||||||
if (selected.isValid() && folder) {
|
if (selected.isValid() && folder) {
|
||||||
int row = selected.row();
|
int row = selected.row();
|
||||||
|
|
||||||
@@ -833,7 +826,7 @@ void AccountSettings::slotOpenCurrentFolder()
|
|||||||
|
|
||||||
void AccountSettings::slotOpenCurrentLocalSubFolder()
|
void AccountSettings::slotOpenCurrentLocalSubFolder()
|
||||||
{
|
{
|
||||||
QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
|
QModelIndex selected = _ui->_folderList->selectionModel()->currentIndex();
|
||||||
if (!selected.isValid() || _model->classify(selected) != FolderStatusModel::SubFolder)
|
if (!selected.isValid() || _model->classify(selected) != FolderStatusModel::SubFolder)
|
||||||
return;
|
return;
|
||||||
QString fileName = _model->data(selected, FolderStatusDelegate::FolderPathRole).toString();
|
QString fileName = _model->data(selected, FolderStatusDelegate::FolderPathRole).toString();
|
||||||
@@ -847,18 +840,21 @@ void AccountSettings::showConnectionLabel(const QString &message, QStringList er
|
|||||||
"border-width: 1px; border-style: solid; border-color: #aaaaaa;"
|
"border-width: 1px; border-style: solid; border-color: #aaaaaa;"
|
||||||
"border-radius:5px;");
|
"border-radius:5px;");
|
||||||
if (errors.isEmpty()) {
|
if (errors.isEmpty()) {
|
||||||
ui->connectLabel->setText(message);
|
QString msg = message;
|
||||||
ui->connectLabel->setToolTip(QString());
|
Theme::replaceLinkColorStringBackgroundAware(msg);
|
||||||
ui->connectLabel->setStyleSheet(QString());
|
_ui->connectLabel->setText(msg);
|
||||||
|
_ui->connectLabel->setToolTip(QString());
|
||||||
|
_ui->connectLabel->setStyleSheet(QString());
|
||||||
} else {
|
} else {
|
||||||
errors.prepend(message);
|
errors.prepend(message);
|
||||||
const QString msg = errors.join(QLatin1String("\n"));
|
QString msg = errors.join(QLatin1String("\n"));
|
||||||
qCDebug(lcAccountSettings) << msg;
|
qCDebug(lcAccountSettings) << msg;
|
||||||
ui->connectLabel->setText(msg);
|
Theme::replaceLinkColorString(msg, QColor("#c1c8e6"));
|
||||||
ui->connectLabel->setToolTip(QString());
|
_ui->connectLabel->setText(msg);
|
||||||
ui->connectLabel->setStyleSheet(errStyle);
|
_ui->connectLabel->setToolTip(QString());
|
||||||
|
_ui->connectLabel->setStyleSheet(errStyle);
|
||||||
}
|
}
|
||||||
ui->accountStatus->setVisible(!message.isEmpty());
|
_ui->accountStatus->setVisible(!message.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AccountSettings::slotEnableCurrentFolder()
|
void AccountSettings::slotEnableCurrentFolder()
|
||||||
@@ -956,29 +952,29 @@ void AccountSettings::slotOpenOC()
|
|||||||
void AccountSettings::slotUpdateQuota(qint64 total, qint64 used)
|
void AccountSettings::slotUpdateQuota(qint64 total, qint64 used)
|
||||||
{
|
{
|
||||||
if (total > 0) {
|
if (total > 0) {
|
||||||
ui->quotaProgressBar->setVisible(true);
|
_ui->quotaProgressBar->setVisible(true);
|
||||||
ui->quotaProgressBar->setEnabled(true);
|
_ui->quotaProgressBar->setEnabled(true);
|
||||||
// workaround the label only accepting ints (which may be only 32 bit wide)
|
// workaround the label only accepting ints (which may be only 32 bit wide)
|
||||||
const double percent = used / (double)total * 100;
|
const double percent = used / (double)total * 100;
|
||||||
const int percentInt = qMin(qRound(percent), 100);
|
const int percentInt = qMin(qRound(percent), 100);
|
||||||
ui->quotaProgressBar->setValue(percentInt);
|
_ui->quotaProgressBar->setValue(percentInt);
|
||||||
QString usedStr = Utility::octetsToString(used);
|
QString usedStr = Utility::octetsToString(used);
|
||||||
QString totalStr = Utility::octetsToString(total);
|
QString totalStr = Utility::octetsToString(total);
|
||||||
QString percentStr = Utility::compactFormatDouble(percent, 1);
|
QString percentStr = Utility::compactFormatDouble(percent, 1);
|
||||||
QString toolTip = tr("%1 (%3%) of %2 in use. Some folders, including network mounted or shared folders, might have different limits.").arg(usedStr, totalStr, percentStr);
|
QString toolTip = tr("%1 (%3%) of %2 in use. Some folders, including network mounted or shared folders, might have different limits.").arg(usedStr, totalStr, percentStr);
|
||||||
ui->quotaInfoLabel->setText(tr("%1 of %2 in use").arg(usedStr, totalStr));
|
_ui->quotaInfoLabel->setText(tr("%1 of %2 in use").arg(usedStr, totalStr));
|
||||||
ui->quotaInfoLabel->setToolTip(toolTip);
|
_ui->quotaInfoLabel->setToolTip(toolTip);
|
||||||
ui->quotaProgressBar->setToolTip(toolTip);
|
_ui->quotaProgressBar->setToolTip(toolTip);
|
||||||
} else {
|
} else {
|
||||||
ui->quotaProgressBar->setVisible(false);
|
_ui->quotaProgressBar->setVisible(false);
|
||||||
ui->quotaInfoLabel->setToolTip(QString());
|
_ui->quotaInfoLabel->setToolTip(QString());
|
||||||
|
|
||||||
/* -1 means not computed; -2 means unknown; -3 means unlimited (#3940)*/
|
/* -1 means not computed; -2 means unknown; -3 means unlimited (#3940)*/
|
||||||
if (total == 0 || total == -1) {
|
if (total == 0 || total == -1) {
|
||||||
ui->quotaInfoLabel->setText(tr("Currently there is no storage usage information available."));
|
_ui->quotaInfoLabel->setText(tr("Currently there is no storage usage information available."));
|
||||||
} else {
|
} else {
|
||||||
QString usedStr = Utility::octetsToString(used);
|
QString usedStr = Utility::octetsToString(used);
|
||||||
ui->quotaInfoLabel->setText(tr("%1 in use").arg(usedStr));
|
_ui->quotaInfoLabel->setText(tr("%1 in use").arg(usedStr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -987,7 +983,7 @@ void AccountSettings::slotAccountStateChanged()
|
|||||||
{
|
{
|
||||||
int state = _accountState ? _accountState->state() : AccountState::Disconnected;
|
int state = _accountState ? _accountState->state() : AccountState::Disconnected;
|
||||||
if (_accountState) {
|
if (_accountState) {
|
||||||
ui->sslButton->updateAccountState(_accountState);
|
_ui->sslButton->updateAccountState(_accountState);
|
||||||
AccountPtr account = _accountState->account();
|
AccountPtr account = _accountState->account();
|
||||||
QUrl safeUrl(account->url());
|
QUrl safeUrl(account->url());
|
||||||
safeUrl.setPassword(QString()); // Remove the password from the URL to avoid showing it in the UI
|
safeUrl.setPassword(QString()); // Remove the password from the URL to avoid showing it in the UI
|
||||||
@@ -1032,7 +1028,7 @@ void AccountSettings::slotAccountStateChanged()
|
|||||||
"<a href='%1'>Click here</a> to re-open the browser.")
|
"<a href='%1'>Click here</a> to re-open the browser.")
|
||||||
.arg(url.toString(QUrl::FullyEncoded)));
|
.arg(url.toString(QUrl::FullyEncoded)));
|
||||||
} else {
|
} else {
|
||||||
showConnectionLabel(tr("Connecting to %1...").arg(serverWithUser));
|
showConnectionLabel(tr("Connecting to %1 …").arg(serverWithUser));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
showConnectionLabel(tr("No connection to %1 at %2.")
|
showConnectionLabel(tr("No connection to %1 at %2.")
|
||||||
@@ -1046,14 +1042,14 @@ void AccountSettings::slotAccountStateChanged()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Allow to expand the item if the account is connected. */
|
/* Allow to expand the item if the account is connected. */
|
||||||
ui->_folderList->setItemsExpandable(state == AccountState::Connected);
|
_ui->_folderList->setItemsExpandable(state == AccountState::Connected);
|
||||||
|
|
||||||
if (state != AccountState::Connected) {
|
if (state != AccountState::Connected) {
|
||||||
/* check if there are expanded root items, if so, close them */
|
/* check if there are expanded root items, if so, close them */
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < _model->rowCount(); ++i) {
|
for (i = 0; i < _model->rowCount(); ++i) {
|
||||||
if (ui->_folderList->isExpanded(_model->index(i)))
|
if (_ui->_folderList->isExpanded(_model->index(i)))
|
||||||
ui->_folderList->setExpanded(_model->index(i), false);
|
_ui->_folderList->setExpanded(_model->index(i), false);
|
||||||
}
|
}
|
||||||
} else if (_model->isDirty()) {
|
} else if (_model->isDirty()) {
|
||||||
// If we connect and have pending changes, show the list.
|
// If we connect and have pending changes, show the list.
|
||||||
@@ -1097,21 +1093,21 @@ void AccountSettings::slotLinkActivated(const QString &link)
|
|||||||
// Make sure the folder itself is expanded
|
// Make sure the folder itself is expanded
|
||||||
Folder *f = FolderMan::instance()->folder(alias);
|
Folder *f = FolderMan::instance()->folder(alias);
|
||||||
QModelIndex folderIndx = _model->indexForPath(f, QString());
|
QModelIndex folderIndx = _model->indexForPath(f, QString());
|
||||||
if (!ui->_folderList->isExpanded(folderIndx)) {
|
if (!_ui->_folderList->isExpanded(folderIndx)) {
|
||||||
ui->_folderList->setExpanded(folderIndx, true);
|
_ui->_folderList->setExpanded(folderIndx, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndex indx = _model->indexForPath(f, myFolder);
|
QModelIndex indx = _model->indexForPath(f, myFolder);
|
||||||
if (indx.isValid()) {
|
if (indx.isValid()) {
|
||||||
// make sure all the parents are expanded
|
// make sure all the parents are expanded
|
||||||
for (auto i = indx.parent(); i.isValid(); i = i.parent()) {
|
for (auto i = indx.parent(); i.isValid(); i = i.parent()) {
|
||||||
if (!ui->_folderList->isExpanded(i)) {
|
if (!_ui->_folderList->isExpanded(i)) {
|
||||||
ui->_folderList->setExpanded(i, true);
|
_ui->_folderList->setExpanded(i, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ui->_folderList->setSelectionMode(QAbstractItemView::SingleSelection);
|
_ui->_folderList->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||||
ui->_folderList->setCurrentIndex(indx);
|
_ui->_folderList->setCurrentIndex(indx);
|
||||||
ui->_folderList->scrollTo(indx);
|
_ui->_folderList->scrollTo(indx);
|
||||||
} else {
|
} else {
|
||||||
qCWarning(lcAccountSettings) << "Unable to find a valid index for " << myFolder;
|
qCWarning(lcAccountSettings) << "Unable to find a valid index for " << myFolder;
|
||||||
}
|
}
|
||||||
@@ -1120,7 +1116,7 @@ void AccountSettings::slotLinkActivated(const QString &link)
|
|||||||
|
|
||||||
AccountSettings::~AccountSettings()
|
AccountSettings::~AccountSettings()
|
||||||
{
|
{
|
||||||
delete ui;
|
delete _ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AccountSettings::refreshSelectiveSyncStatus()
|
void AccountSettings::refreshSelectiveSyncStatus()
|
||||||
@@ -1158,8 +1154,8 @@ void AccountSettings::refreshSelectiveSyncStatus()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (msg.isEmpty()) {
|
if (msg.isEmpty()) {
|
||||||
ui->selectiveSyncButtons->setVisible(true);
|
_ui->selectiveSyncButtons->setVisible(true);
|
||||||
ui->bigFolderUi->setVisible(false);
|
_ui->bigFolderUi->setVisible(false);
|
||||||
} else {
|
} else {
|
||||||
ConfigFile cfg;
|
ConfigFile cfg;
|
||||||
QString info = !cfg.confirmExternalStorage()
|
QString info = !cfg.confirmExternalStorage()
|
||||||
@@ -1168,27 +1164,27 @@ void AccountSettings::refreshSelectiveSyncStatus()
|
|||||||
? tr("There are folders that were not synchronized because they are external storages: ")
|
? tr("There are folders that were not synchronized because they are external storages: ")
|
||||||
: tr("There are folders that were not synchronized because they are too big or external storages: ");
|
: tr("There are folders that were not synchronized because they are too big or external storages: ");
|
||||||
|
|
||||||
ui->selectiveSyncNotification->setText(info + msg);
|
_ui->selectiveSyncNotification->setText(info + msg);
|
||||||
ui->selectiveSyncButtons->setVisible(false);
|
_ui->selectiveSyncButtons->setVisible(false);
|
||||||
ui->bigFolderUi->setVisible(true);
|
_ui->bigFolderUi->setVisible(true);
|
||||||
shouldBeVisible = true;
|
shouldBeVisible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ui->selectiveSyncApply->setEnabled(_model->isDirty() || !msg.isEmpty());
|
_ui->selectiveSyncApply->setEnabled(_model->isDirty() || !msg.isEmpty());
|
||||||
bool wasVisible = !ui->selectiveSyncStatus->isHidden();
|
bool wasVisible = !_ui->selectiveSyncStatus->isHidden();
|
||||||
if (wasVisible != shouldBeVisible) {
|
if (wasVisible != shouldBeVisible) {
|
||||||
QSize hint = ui->selectiveSyncStatus->sizeHint();
|
QSize hint = _ui->selectiveSyncStatus->sizeHint();
|
||||||
if (shouldBeVisible) {
|
if (shouldBeVisible) {
|
||||||
ui->selectiveSyncStatus->setMaximumHeight(0);
|
_ui->selectiveSyncStatus->setMaximumHeight(0);
|
||||||
ui->selectiveSyncStatus->setVisible(true);
|
_ui->selectiveSyncStatus->setVisible(true);
|
||||||
}
|
}
|
||||||
auto anim = new QPropertyAnimation(ui->selectiveSyncStatus, "maximumHeight", ui->selectiveSyncStatus);
|
auto anim = new QPropertyAnimation(_ui->selectiveSyncStatus, "maximumHeight", _ui->selectiveSyncStatus);
|
||||||
anim->setEndValue(shouldBeVisible ? hint.height() : 0);
|
anim->setEndValue(shouldBeVisible ? hint.height() : 0);
|
||||||
anim->start(QAbstractAnimation::DeleteWhenStopped);
|
anim->start(QAbstractAnimation::DeleteWhenStopped);
|
||||||
connect(anim, &QPropertyAnimation::finished, [this, shouldBeVisible]() {
|
connect(anim, &QPropertyAnimation::finished, [this, shouldBeVisible]() {
|
||||||
ui->selectiveSyncStatus->setMaximumHeight(QWIDGETSIZE_MAX);
|
_ui->selectiveSyncStatus->setMaximumHeight(QWIDGETSIZE_MAX);
|
||||||
if (!shouldBeVisible) {
|
if (!shouldBeVisible) {
|
||||||
ui->selectiveSyncStatus->hide();
|
_ui->selectiveSyncStatus->hide();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1242,18 +1238,36 @@ void AccountSettings::slotDeleteAccount()
|
|||||||
bool AccountSettings::event(QEvent *e)
|
bool AccountSettings::event(QEvent *e)
|
||||||
{
|
{
|
||||||
if (e->type() == QEvent::Hide || e->type() == QEvent::Show) {
|
if (e->type() == QEvent::Hide || e->type() == QEvent::Show) {
|
||||||
_quotaInfo.setActive(isVisible());
|
_userInfo.setActive(isVisible());
|
||||||
}
|
}
|
||||||
if (e->type() == QEvent::Show) {
|
if (e->type() == QEvent::Show) {
|
||||||
// Expand the folder automatically only if there's only one, see #4283
|
// Expand the folder automatically only if there's only one, see #4283
|
||||||
// The 2 is 1 folder + 1 'add folder' button
|
// The 2 is 1 folder + 1 'add folder' button
|
||||||
if (_model->rowCount() <= 2) {
|
if (_model->rowCount() <= 2) {
|
||||||
ui->_folderList->setExpanded(_model->index(0, 0), true);
|
_ui->_folderList->setExpanded(_model->index(0, 0), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return QWidget::event(e);
|
return QWidget::event(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AccountSettings::slotStyleChanged()
|
||||||
|
{
|
||||||
|
customizeStyle();
|
||||||
|
|
||||||
|
// Notify the other widgets (Dark-/Light-Mode switching)
|
||||||
|
emit styleChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AccountSettings::customizeStyle()
|
||||||
|
{
|
||||||
|
QString msg = _ui->connectLabel->text();
|
||||||
|
Theme::replaceLinkColorStringBackgroundAware(msg);
|
||||||
|
_ui->connectLabel->setText(msg);
|
||||||
|
|
||||||
|
QColor color = palette().highlight().color();
|
||||||
|
_ui->quotaProgressBar->setStyleSheet(QString::fromLatin1(progressBarStyleC).arg(color.name()));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace OCC
|
} // namespace OCC
|
||||||
|
|
||||||
#include "accountsettings.moc"
|
#include "accountsettings.moc"
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
#include "folder.h"
|
#include "folder.h"
|
||||||
#include "quotainfo.h"
|
#include "userinfo.h"
|
||||||
#include "progressdispatcher.h"
|
#include "progressdispatcher.h"
|
||||||
#include "owncloudgui.h"
|
#include "owncloudgui.h"
|
||||||
#include "folderstatusmodel.h"
|
#include "folderstatusmodel.h"
|
||||||
@@ -64,11 +64,13 @@ signals:
|
|||||||
void showIssuesList(AccountState *account);
|
void showIssuesList(AccountState *account);
|
||||||
void requesetMnemonic();
|
void requesetMnemonic();
|
||||||
void removeAccountFolders(AccountState *account);
|
void removeAccountFolders(AccountState *account);
|
||||||
|
void styleChanged();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void slotOpenOC();
|
void slotOpenOC();
|
||||||
void slotUpdateQuota(qint64, qint64);
|
void slotUpdateQuota(qint64, qint64);
|
||||||
void slotAccountStateChanged();
|
void slotAccountStateChanged();
|
||||||
|
void slotStyleChanged();
|
||||||
|
|
||||||
AccountState *accountsState() { return _accountState; }
|
AccountState *accountsState() { return _accountState; }
|
||||||
|
|
||||||
@@ -129,17 +131,18 @@ private:
|
|||||||
bool event(QEvent *) override;
|
bool event(QEvent *) override;
|
||||||
void createAccountToolbox();
|
void createAccountToolbox();
|
||||||
void openIgnoredFilesDialog(const QString & absFolderPath);
|
void openIgnoredFilesDialog(const QString & absFolderPath);
|
||||||
|
void customizeStyle();
|
||||||
|
|
||||||
/// Returns the alias of the selected folder, empty string if none
|
/// Returns the alias of the selected folder, empty string if none
|
||||||
QString selectedFolderAlias() const;
|
QString selectedFolderAlias() const;
|
||||||
|
|
||||||
Ui::AccountSettings *ui;
|
Ui::AccountSettings *_ui;
|
||||||
|
|
||||||
FolderStatusModel *_model;
|
FolderStatusModel *_model;
|
||||||
QUrl _OCUrl;
|
QUrl _OCUrl;
|
||||||
bool _wasDisabledBefore;
|
bool _wasDisabledBefore;
|
||||||
AccountState *_accountState;
|
AccountState *_accountState;
|
||||||
QuotaInfo _quotaInfo;
|
UserInfo _userInfo;
|
||||||
QAction *_toggleSignInOutAction;
|
QAction *_toggleSignInOutAction;
|
||||||
QAction *_addAccountAction;
|
QAction *_addAccountAction;
|
||||||
|
|
||||||
|
|||||||
@@ -208,7 +208,7 @@
|
|||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Storage space: ...</string>
|
<string>Storage space: …</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="textFormat">
|
<property name="textFormat">
|
||||||
<enum>Qt::PlainText</enum>
|
<enum>Qt::PlainText</enum>
|
||||||
|
|||||||
@@ -229,7 +229,7 @@ void AccountState::checkConnectivity()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectionValidator *conValidator = new ConnectionValidator(account());
|
ConnectionValidator *conValidator = new ConnectionValidator(AccountStatePtr(this));
|
||||||
_connectionValidator = conValidator;
|
_connectionValidator = conValidator;
|
||||||
connect(conValidator, &ConnectionValidator::connectionResult,
|
connect(conValidator, &ConnectionValidator::connectionResult,
|
||||||
this, &AccountState::slotConnectionValidatorResult);
|
this, &AccountState::slotConnectionValidatorResult);
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace OCC {
|
|||||||
|
|
||||||
bool operator<(const Activity &rhs, const Activity &lhs)
|
bool operator<(const Activity &rhs, const Activity &lhs)
|
||||||
{
|
{
|
||||||
return rhs._dateTime.toMSecsSinceEpoch() > lhs._dateTime.toMSecsSinceEpoch();
|
return rhs._dateTime > lhs._dateTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const Activity &rhs, const Activity &lhs)
|
bool operator==(const Activity &rhs, const Activity &lhs)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "activityitemdelegate.h"
|
#include "activityitemdelegate.h"
|
||||||
|
#include "activitylistmodel.h"
|
||||||
#include "folderstatusmodel.h"
|
#include "folderstatusmodel.h"
|
||||||
#include "folderman.h"
|
#include "folderman.h"
|
||||||
#include "accountstate.h"
|
#include "accountstate.h"
|
||||||
@@ -26,6 +27,12 @@
|
|||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
|
||||||
|
#define FIXME_USE_HIGH_DPI_RATIO
|
||||||
|
#ifdef FIXME_USE_HIGH_DPI_RATIO
|
||||||
|
// FIXME: Find a better way to calculate the text width on high-dpi displays (Retina).
|
||||||
|
#include <QDesktopWidget>
|
||||||
|
#endif
|
||||||
|
|
||||||
#define HASQT5_11 (QT_VERSION >= QT_VERSION_CHECK(5,11,0))
|
#define HASQT5_11 (QT_VERSION >= QT_VERSION_CHECK(5,11,0))
|
||||||
|
|
||||||
namespace OCC {
|
namespace OCC {
|
||||||
@@ -40,6 +47,12 @@ int ActivityItemDelegate::_buttonHeight = 0;
|
|||||||
const QString ActivityItemDelegate::_remote_share("remote_share");
|
const QString ActivityItemDelegate::_remote_share("remote_share");
|
||||||
const QString ActivityItemDelegate::_call("call");
|
const QString ActivityItemDelegate::_call("call");
|
||||||
|
|
||||||
|
ActivityItemDelegate::ActivityItemDelegate()
|
||||||
|
: QStyledItemDelegate()
|
||||||
|
{
|
||||||
|
customizeStyle();
|
||||||
|
}
|
||||||
|
|
||||||
int ActivityItemDelegate::iconHeight()
|
int ActivityItemDelegate::iconHeight()
|
||||||
{
|
{
|
||||||
if (_iconHeight == 0) {
|
if (_iconHeight == 0) {
|
||||||
@@ -89,10 +102,26 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
|||||||
int iconSize = 16;
|
int iconSize = 16;
|
||||||
int iconOffset = qRound(fm.height() / 4.0 * 7.0);
|
int iconOffset = qRound(fm.height() / 4.0 * 7.0);
|
||||||
int offset = 4;
|
int offset = 4;
|
||||||
|
const bool isSelected = (option.state & QStyle::State_Selected);
|
||||||
|
#ifdef FIXME_USE_HIGH_DPI_RATIO
|
||||||
|
// FIXME: Find a better way to calculate the text width on high-dpi displays (Retina).
|
||||||
|
const int device_pixel_ration = QApplication::desktop()->devicePixelRatio();
|
||||||
|
int pixel_ratio = (device_pixel_ration > 1 ? device_pixel_ration : 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
// get the data
|
// get the data
|
||||||
Activity::Type activityType = qvariant_cast<Activity::Type>(index.data(ActionRole));
|
Activity::Type activityType = qvariant_cast<Activity::Type>(index.data(ActionRole));
|
||||||
QIcon actionIcon = qvariant_cast<QIcon>(index.data(ActionIconRole));
|
QIcon actionIcon;
|
||||||
|
const ActivityListModel::ActionIcon icn = qvariant_cast<ActivityListModel::ActionIcon>(index.data(ActionIconRole));
|
||||||
|
switch(icn.iconType) {
|
||||||
|
case ActivityListModel::ActivityIconType::iconUseCached: actionIcon = icn.cachedIcon; break;
|
||||||
|
case ActivityListModel::ActivityIconType::iconActivity: actionIcon = (isSelected ? _iconActivity_sel : _iconActivity); break;
|
||||||
|
case ActivityListModel::ActivityIconType::iconBell: actionIcon = (isSelected ? _iconBell_sel : _iconBell); break;
|
||||||
|
case ActivityListModel::ActivityIconType::iconStateError: actionIcon = _iconStateError; break;
|
||||||
|
case ActivityListModel::ActivityIconType::iconStateWarning: actionIcon = _iconStateWarning; break;
|
||||||
|
case ActivityListModel::ActivityIconType::iconStateInfo: actionIcon = _iconStateInfo; break;
|
||||||
|
case ActivityListModel::ActivityIconType::iconStateSync: actionIcon = _iconStateSync; break;
|
||||||
|
}
|
||||||
QString objectType = qvariant_cast<QString>(index.data(ObjectTypeRole));
|
QString objectType = qvariant_cast<QString>(index.data(ObjectTypeRole));
|
||||||
QString actionText = qvariant_cast<QString>(index.data(ActionTextRole));
|
QString actionText = qvariant_cast<QString>(index.data(ActionTextRole));
|
||||||
QString messageText = qvariant_cast<QString>(index.data(MessageRole));
|
QString messageText = qvariant_cast<QString>(index.data(MessageRole));
|
||||||
@@ -116,6 +145,10 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
|||||||
actionTextBox.setTop(option.rect.top() + margin + offset/2);
|
actionTextBox.setTop(option.rect.top() + margin + offset/2);
|
||||||
actionTextBox.setHeight(fm.height());
|
actionTextBox.setHeight(fm.height());
|
||||||
actionTextBox.setLeft(actionIconRect.right() + margin);
|
actionTextBox.setLeft(actionIconRect.right() + margin);
|
||||||
|
#ifdef FIXME_USE_HIGH_DPI_RATIO
|
||||||
|
// FIXME: Find a better way to calculate the text width on high-dpi displays (Retina).
|
||||||
|
actionTextBoxWidth *= pixel_ratio;
|
||||||
|
#endif
|
||||||
actionTextBox.setRight(actionTextBox.left() + actionTextBoxWidth + margin);
|
actionTextBox.setRight(actionTextBox.left() + actionTextBoxWidth + margin);
|
||||||
|
|
||||||
// message text rect
|
// message text rect
|
||||||
@@ -138,11 +171,10 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
|||||||
|
|
||||||
// time box rect
|
// time box rect
|
||||||
QRect timeBox = messageTextBox;
|
QRect timeBox = messageTextBox;
|
||||||
QString timeStr = tr("%1").arg(timeText);
|
|
||||||
#if (HASQT5_11)
|
#if (HASQT5_11)
|
||||||
int timeTextWidth = fm.horizontalAdvance(timeStr);
|
int timeTextWidth = fm.horizontalAdvance(timeText);
|
||||||
#else
|
#else
|
||||||
int timeTextWidth = fm.width(timeStr);
|
int timeTextWidth = fm.width(timeText);
|
||||||
#endif
|
#endif
|
||||||
int timeTop = option.rect.top() + fm.height() + fm.height() + margin + offset/2;
|
int timeTop = option.rect.top() + fm.height() + fm.height() + margin + offset/2;
|
||||||
if(messageText.isEmpty() || actionText.isEmpty())
|
if(messageText.isEmpty() || actionText.isEmpty())
|
||||||
@@ -150,6 +182,10 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
|||||||
timeBox.setTop(timeTop);
|
timeBox.setTop(timeTop);
|
||||||
timeBox.setHeight(fm.height());
|
timeBox.setHeight(fm.height());
|
||||||
timeBox.setBottom(timeBox.top() + fm.height());
|
timeBox.setBottom(timeBox.top() + fm.height());
|
||||||
|
#ifdef FIXME_USE_HIGH_DPI_RATIO
|
||||||
|
// FIXME: Find a better way to calculate the text width on high-dpi displays (Retina).
|
||||||
|
timeTextWidth *= pixel_ratio;
|
||||||
|
#endif
|
||||||
timeBox.setRight(timeBox.left() + timeTextWidth + margin);
|
timeBox.setRight(timeBox.left() + timeTextWidth + margin);
|
||||||
|
|
||||||
// buttons - default values
|
// buttons - default values
|
||||||
@@ -184,9 +220,9 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
|||||||
if(activityType == Activity::Type::NotificationType){
|
if(activityType == Activity::Type::NotificationType){
|
||||||
|
|
||||||
// Secondary will be 'Dismiss' or '...' multiple options button
|
// Secondary will be 'Dismiss' or '...' multiple options button
|
||||||
secondaryButton.icon = QIcon(QLatin1String(":/client/resources/close.svg"));
|
secondaryButton.icon = (isSelected ? _iconClose_sel : _iconClose);
|
||||||
if(customList.size() > 1)
|
if(customList.size() > 1)
|
||||||
secondaryButton.icon = QIcon(QLatin1String(":/client/resources/more.svg"));
|
secondaryButton.icon = (isSelected ? _iconMore_sel : _iconMore);
|
||||||
secondaryButton.iconSize = QSize(iconSize, iconSize);
|
secondaryButton.iconSize = QSize(iconSize, iconSize);
|
||||||
|
|
||||||
// Primary button will be 'More Information' or 'Accept'
|
// Primary button will be 'More Information' or 'Accept'
|
||||||
@@ -209,7 +245,7 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
|||||||
} else if(activityType == Activity::SyncResultType){
|
} else if(activityType == Activity::SyncResultType){
|
||||||
|
|
||||||
// Secondary will be 'open file manager' with the folder icon
|
// Secondary will be 'open file manager' with the folder icon
|
||||||
secondaryButton.icon = QIcon(QLatin1String(":/client/resources/folder.svg"));
|
secondaryButton.icon = _iconFolder;
|
||||||
secondaryButton.iconSize = QSize(iconSize, iconSize);
|
secondaryButton.iconSize = QSize(iconSize, iconSize);
|
||||||
|
|
||||||
// Primary button will be 'open browser'
|
// Primary button will be 'open browser'
|
||||||
@@ -230,7 +266,7 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
|||||||
} else if(activityType == Activity::SyncFileItemType){
|
} else if(activityType == Activity::SyncFileItemType){
|
||||||
|
|
||||||
// Secondary will be 'open file manager' with the folder icon
|
// Secondary will be 'open file manager' with the folder icon
|
||||||
secondaryButton.icon = QIcon(QLatin1String(":/client/resources/folder.svg"));
|
secondaryButton.icon = _iconFolder;
|
||||||
secondaryButton.iconSize = QSize(iconSize, iconSize);
|
secondaryButton.iconSize = QSize(iconSize, iconSize);
|
||||||
|
|
||||||
// No primary button on this case
|
// No primary button on this case
|
||||||
@@ -255,7 +291,7 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
|||||||
p.setCurrentColorGroup(QPalette::Disabled);
|
p.setCurrentColorGroup(QPalette::Disabled);
|
||||||
|
|
||||||
// change pen color if the line is selected
|
// change pen color if the line is selected
|
||||||
if (option.state & QStyle::State_Selected)
|
if (isSelected)
|
||||||
painter->setPen(p.color(QPalette::HighlightedText));
|
painter->setPen(p.color(QPalette::HighlightedText));
|
||||||
else
|
else
|
||||||
painter->setPen(p.color(QPalette::Text));
|
painter->setPen(p.color(QPalette::Text));
|
||||||
@@ -269,8 +305,15 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
|||||||
painter->drawText(actionTextBox, elidedAction);
|
painter->drawText(actionTextBox, elidedAction);
|
||||||
|
|
||||||
// draw the buttons
|
// draw the buttons
|
||||||
if(activityType == Activity::Type::NotificationType || activityType == Activity::Type::SyncResultType)
|
if(activityType == Activity::Type::NotificationType || activityType == Activity::Type::SyncResultType) {
|
||||||
|
primaryButton.palette = p;
|
||||||
|
if (isSelected)
|
||||||
|
primaryButton.palette.setColor(QPalette::ButtonText, p.color(QPalette::HighlightedText));
|
||||||
|
else
|
||||||
|
primaryButton.palette.setColor(QPalette::ButtonText, p.color(QPalette::Text));
|
||||||
|
|
||||||
QApplication::style()->drawControl(QStyle::CE_PushButton, &primaryButton, painter);
|
QApplication::style()->drawControl(QStyle::CE_PushButton, &primaryButton, painter);
|
||||||
|
}
|
||||||
|
|
||||||
// Since they are errors on local syncing, there is nothing to do in the server
|
// Since they are errors on local syncing, there is nothing to do in the server
|
||||||
if(activityType != Activity::Type::ActivityType)
|
if(activityType != Activity::Type::ActivityType)
|
||||||
@@ -284,13 +327,13 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
|||||||
}
|
}
|
||||||
|
|
||||||
// change pen color for the time
|
// change pen color for the time
|
||||||
if (option.state & QStyle::State_Selected)
|
if (isSelected)
|
||||||
painter->setPen(p.color(QPalette::Disabled, QPalette::HighlightedText));
|
painter->setPen(p.color(QPalette::Disabled, QPalette::HighlightedText));
|
||||||
else
|
else
|
||||||
painter->setPen(p.color(QPalette::Disabled, QPalette::Text));
|
painter->setPen(p.color(QPalette::Disabled, QPalette::Text));
|
||||||
|
|
||||||
// draw the time
|
// draw the time
|
||||||
const QString elidedTime = fm.elidedText(timeStr, Qt::ElideRight, spaceLeftForText);
|
const QString elidedTime = fm.elidedText(timeText, Qt::ElideRight, spaceLeftForText);
|
||||||
painter->drawText(timeBox, elidedTime);
|
painter->drawText(timeBox, elidedTime);
|
||||||
|
|
||||||
painter->restore();
|
painter->restore();
|
||||||
@@ -333,4 +376,32 @@ bool ActivityItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
|
|||||||
return QStyledItemDelegate::editorEvent(event, model, option, index);
|
return QStyledItemDelegate::editorEvent(event, model, option, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ActivityItemDelegate::slotStyleChanged()
|
||||||
|
{
|
||||||
|
customizeStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActivityItemDelegate::customizeStyle()
|
||||||
|
{
|
||||||
|
QPalette pal;
|
||||||
|
pal.setColor(QPalette::Base, QColor(0,0,0)); // use dark background colour to invert icons
|
||||||
|
|
||||||
|
_iconClose = Theme::createColorAwareIcon(QLatin1String(":/client/resources/close.svg"));
|
||||||
|
_iconClose_sel = Theme::createColorAwareIcon(QLatin1String(":/client/resources/close.svg"), pal);
|
||||||
|
_iconMore = Theme::createColorAwareIcon(QLatin1String(":/client/resources/more.svg"));
|
||||||
|
_iconMore_sel = Theme::createColorAwareIcon(QLatin1String(":/client/resources/more.svg"), pal);
|
||||||
|
|
||||||
|
_iconFolder = QIcon(QLatin1String(":/client/resources/folder.svg"));
|
||||||
|
|
||||||
|
_iconActivity = Theme::createColorAwareIcon(QLatin1String(":/client/resources/activity.png"));
|
||||||
|
_iconActivity_sel = Theme::createColorAwareIcon(QLatin1String(":/client/resources/activity.png"), pal);
|
||||||
|
_iconBell = Theme::createColorAwareIcon(QLatin1String(":/client/resources/bell.svg"));
|
||||||
|
_iconBell_sel = Theme::createColorAwareIcon(QLatin1String(":/client/resources/bell.svg"), pal);
|
||||||
|
|
||||||
|
_iconStateError = QIcon(QLatin1String(":/client/resources/state-error.svg"));
|
||||||
|
_iconStateWarning = QIcon(QLatin1String(":/client/resources/state-warning.svg"));
|
||||||
|
_iconStateInfo = QIcon(QLatin1String(":/client/resources/state-info.svg"));
|
||||||
|
_iconStateSync = QIcon(QLatin1String(":/client/resources/state-sync.svg"));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace OCC
|
} // namespace OCC
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ public:
|
|||||||
AccountConnectedRole,
|
AccountConnectedRole,
|
||||||
SyncFileStatusRole };
|
SyncFileStatusRole };
|
||||||
|
|
||||||
|
ActivityItemDelegate();
|
||||||
|
|
||||||
void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const override;
|
void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const override;
|
||||||
QSize sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const override;
|
QSize sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const override;
|
||||||
bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option,
|
bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option,
|
||||||
@@ -51,11 +53,16 @@ public:
|
|||||||
static int rowHeight();
|
static int rowHeight();
|
||||||
static int iconHeight();
|
static int iconHeight();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void slotStyleChanged();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void primaryButtonClickedOnItemView(const QModelIndex &index);
|
void primaryButtonClickedOnItemView(const QModelIndex &index);
|
||||||
void secondaryButtonClickedOnItemView(const QModelIndex &index);
|
void secondaryButtonClickedOnItemView(const QModelIndex &index);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void customizeStyle();
|
||||||
|
|
||||||
static int _margin;
|
static int _margin;
|
||||||
static int _iconHeight;
|
static int _iconHeight;
|
||||||
static int _primaryButtonWidth;
|
static int _primaryButtonWidth;
|
||||||
@@ -65,6 +72,23 @@ private:
|
|||||||
static int _buttonHeight;
|
static int _buttonHeight;
|
||||||
static const QString _remote_share;
|
static const QString _remote_share;
|
||||||
static const QString _call;
|
static const QString _call;
|
||||||
|
|
||||||
|
QIcon _iconClose;
|
||||||
|
QIcon _iconClose_sel;
|
||||||
|
QIcon _iconMore;
|
||||||
|
QIcon _iconMore_sel;
|
||||||
|
|
||||||
|
QIcon _iconFolder;
|
||||||
|
|
||||||
|
QIcon _iconActivity;
|
||||||
|
QIcon _iconActivity_sel;
|
||||||
|
QIcon _iconBell;
|
||||||
|
QIcon _iconBell_sel;
|
||||||
|
|
||||||
|
QIcon _iconStateError;
|
||||||
|
QIcon _iconStateWarning;
|
||||||
|
QIcon _iconStateInfo;
|
||||||
|
QIcon _iconStateSync;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace OCC
|
} // namespace OCC
|
||||||
|
|||||||
@@ -71,13 +71,14 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
|
|||||||
return QVariant(list.at(0));
|
return QVariant(list.at(0));
|
||||||
}
|
}
|
||||||
// File does not exist anymore? Let's try to open its path
|
// File does not exist anymore? Let's try to open its path
|
||||||
list = FolderMan::instance()->findFileInLocalFolders(QFileInfo(relPath).path(), ast->account());
|
if(QFileInfo(relPath).exists()) {
|
||||||
if (list.count() > 0) {
|
list = FolderMan::instance()->findFileInLocalFolders(QFileInfo(relPath).path(), ast->account());
|
||||||
return QVariant(list.at(0));
|
if (list.count() > 0) {
|
||||||
|
return QVariant(list.at(0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return QVariant();
|
return QVariant();
|
||||||
break;
|
|
||||||
case ActivityItemDelegate::ActionsLinksRole:{
|
case ActivityItemDelegate::ActionsLinksRole:{
|
||||||
QList<QVariant> customList;
|
QList<QVariant> customList;
|
||||||
foreach (ActivityLink customItem, a._links) {
|
foreach (ActivityLink customItem, a._links) {
|
||||||
@@ -86,61 +87,63 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
|
|||||||
customList << customVariant;
|
customList << customVariant;
|
||||||
}
|
}
|
||||||
return customList;
|
return customList;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case ActivityItemDelegate::ActionIconRole:
|
case ActivityItemDelegate::ActionIconRole:{
|
||||||
|
ActionIcon actionIcon;
|
||||||
if(a._type == Activity::NotificationType){
|
if(a._type == Activity::NotificationType){
|
||||||
QIcon cachedIcon = ServerNotificationHandler::iconCache.value(a._id);
|
QIcon cachedIcon = ServerNotificationHandler::iconCache.value(a._id);
|
||||||
if(!cachedIcon.isNull())
|
if(!cachedIcon.isNull()) {
|
||||||
return cachedIcon;
|
actionIcon.iconType = ActivityIconType::iconUseCached;
|
||||||
else return QIcon(QLatin1String(":/client/resources/bell.svg"));
|
actionIcon.cachedIcon = cachedIcon;
|
||||||
|
} else {
|
||||||
|
actionIcon.iconType = ActivityIconType::iconBell;
|
||||||
|
}
|
||||||
} else if(a._type == Activity::SyncResultType){
|
} else if(a._type == Activity::SyncResultType){
|
||||||
return QIcon(QLatin1String(":/client/resources/state-error.svg"));
|
actionIcon.iconType = ActivityIconType::iconStateError;
|
||||||
} else if(a._type == Activity::SyncFileItemType){
|
} else if(a._type == Activity::SyncFileItemType){
|
||||||
if(a._status == SyncFileItem::NormalError
|
if(a._status == SyncFileItem::NormalError
|
||||||
|| a._status == SyncFileItem::FatalError
|
|| a._status == SyncFileItem::FatalError
|
||||||
|| a._status == SyncFileItem::DetailError
|
|| a._status == SyncFileItem::DetailError
|
||||||
|| a._status == SyncFileItem::BlacklistedError) {
|
|| a._status == SyncFileItem::BlacklistedError) {
|
||||||
return QIcon(QLatin1String(":/client/resources/state-error.svg"));
|
actionIcon.iconType = ActivityIconType::iconStateError;
|
||||||
} else if(a._status == SyncFileItem::SoftError
|
} else if(a._status == SyncFileItem::SoftError
|
||||||
|| a._status == SyncFileItem::Conflict
|
|| a._status == SyncFileItem::Conflict
|
||||||
|| a._status == SyncFileItem::Restoration
|
|| a._status == SyncFileItem::Restoration
|
||||||
|| a._status == SyncFileItem::FileLocked){
|
|| a._status == SyncFileItem::FileLocked){
|
||||||
return QIcon(QLatin1String(":/client/resources/state-warning.svg"));
|
actionIcon.iconType = ActivityIconType::iconStateWarning;
|
||||||
} else if(a._status == SyncFileItem::FileIgnored){
|
} else if(a._status == SyncFileItem::FileIgnored){
|
||||||
return QIcon(QLatin1String(":/client/resources/state-info.svg"));
|
actionIcon.iconType = ActivityIconType::iconStateInfo;
|
||||||
|
} else {
|
||||||
|
actionIcon.iconType = ActivityIconType::iconStateSync;
|
||||||
}
|
}
|
||||||
return QIcon(QLatin1String(":/client/resources/state-sync.svg"));
|
} else {
|
||||||
|
actionIcon.iconType = ActivityIconType::iconActivity;
|
||||||
}
|
}
|
||||||
return QIcon(QLatin1String(":/client/resources/activity.png"));
|
QVariant icn;
|
||||||
break;
|
icn.setValue(actionIcon);
|
||||||
|
return icn;
|
||||||
|
}
|
||||||
case ActivityItemDelegate::ObjectTypeRole:
|
case ActivityItemDelegate::ObjectTypeRole:
|
||||||
return a._objectType;
|
return a._objectType;
|
||||||
break;
|
|
||||||
case ActivityItemDelegate::ActionRole:{
|
case ActivityItemDelegate::ActionRole:{
|
||||||
QVariant type;
|
QVariant type;
|
||||||
type.setValue(a._type);
|
type.setValue(a._type);
|
||||||
return type;
|
return type;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case ActivityItemDelegate::ActionTextRole:
|
case ActivityItemDelegate::ActionTextRole:
|
||||||
return a._subject;
|
return a._subject;
|
||||||
break;
|
|
||||||
case ActivityItemDelegate::MessageRole:
|
case ActivityItemDelegate::MessageRole:
|
||||||
return a._message;
|
return a._message;
|
||||||
break;
|
|
||||||
case ActivityItemDelegate::LinkRole:
|
case ActivityItemDelegate::LinkRole:
|
||||||
return a._link;
|
return a._link;
|
||||||
break;
|
|
||||||
case ActivityItemDelegate::AccountRole:
|
case ActivityItemDelegate::AccountRole:
|
||||||
return a._accName;
|
return a._accName;
|
||||||
break;
|
|
||||||
case ActivityItemDelegate::PointInTimeRole:
|
case ActivityItemDelegate::PointInTimeRole:
|
||||||
return Utility::timeAgoInWords(a._dateTime);
|
return Utility::timeAgoInWords(a._dateTime.toLocalTime());
|
||||||
break;
|
case Qt::ToolTipRole:
|
||||||
|
return a._dateTime.toLocalTime().toString(Qt::DefaultLocaleShortDate);
|
||||||
case ActivityItemDelegate::AccountConnectedRole:
|
case ActivityItemDelegate::AccountConnectedRole:
|
||||||
return (ast && ast->isConnected());
|
return (ast && ast->isConnected());
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
@@ -229,6 +232,29 @@ void ActivityListModel::addErrorToActivityList(Activity activity) {
|
|||||||
combineActivityLists();
|
combineActivityLists();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ActivityListModel::addIgnoredFileToList(Activity newActivity) {
|
||||||
|
qCInfo(lcActivity) << "First checking for duplicates then add file to the notification list of ignored files: " << newActivity._file;
|
||||||
|
|
||||||
|
bool duplicate = false;
|
||||||
|
if(_listOfIgnoredFiles.size() == 0){
|
||||||
|
_notificationIgnoredFiles = newActivity;
|
||||||
|
_notificationIgnoredFiles._subject = tr("Files from the ignore list as well as symbolic links are not synced. This includes:");
|
||||||
|
_listOfIgnoredFiles.append(newActivity);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(Activity activity, _listOfIgnoredFiles){
|
||||||
|
if(activity._file == newActivity._file){
|
||||||
|
duplicate = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!duplicate){
|
||||||
|
_notificationIgnoredFiles._message.append(", " + newActivity._file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ActivityListModel::addNotificationToActivityList(Activity activity) {
|
void ActivityListModel::addNotificationToActivityList(Activity activity) {
|
||||||
qCInfo(lcActivity) << "Notification successfully added to the notification list: " << activity._subject;
|
qCInfo(lcActivity) << "Notification successfully added to the notification list: " << activity._subject;
|
||||||
_notificationLists.prepend(activity);
|
_notificationLists.prepend(activity);
|
||||||
@@ -242,7 +268,7 @@ void ActivityListModel::clearNotifications() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ActivityListModel::removeActivityFromActivityList(int row) {
|
void ActivityListModel::removeActivityFromActivityList(int row) {
|
||||||
Activity activity = _finalList.at(row);
|
Activity activity = _finalList.at(row);
|
||||||
removeActivityFromActivityList(activity);
|
removeActivityFromActivityList(activity);
|
||||||
combineActivityLists();
|
combineActivityLists();
|
||||||
}
|
}
|
||||||
@@ -276,22 +302,31 @@ void ActivityListModel::removeActivityFromActivityList(Activity activity) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ActivityListModel::combineActivityLists()
|
void ActivityListModel::combineActivityLists()
|
||||||
{
|
{
|
||||||
ActivityList resultList;
|
ActivityList resultList;
|
||||||
|
|
||||||
std::sort(_notificationErrorsLists.begin(), _notificationErrorsLists.end());
|
if(_notificationErrorsLists.count() > 0) {
|
||||||
resultList.append(_notificationErrorsLists);
|
std::sort(_notificationErrorsLists.begin(), _notificationErrorsLists.end());
|
||||||
|
resultList.append(_notificationErrorsLists);
|
||||||
|
}
|
||||||
|
if(_listOfIgnoredFiles.size() > 0)
|
||||||
|
resultList.append(_notificationIgnoredFiles);
|
||||||
|
|
||||||
std::sort(_notificationLists.begin(), _notificationLists.end());
|
if(_notificationLists.count() > 0) {
|
||||||
resultList.append(_notificationLists);
|
std::sort(_notificationLists.begin(), _notificationLists.end());
|
||||||
|
resultList.append(_notificationLists);
|
||||||
|
}
|
||||||
|
|
||||||
std::sort(_syncFileItemLists.begin(), _syncFileItemLists.end());
|
if(_syncFileItemLists.count() > 0) {
|
||||||
resultList.append(_syncFileItemLists);
|
std::sort(_syncFileItemLists.begin(), _syncFileItemLists.end());
|
||||||
|
resultList.append(_syncFileItemLists);
|
||||||
|
}
|
||||||
|
|
||||||
std::sort(_activityLists.begin(), _activityLists.end());
|
if(_activityLists.count() > 0) {
|
||||||
resultList.append(_activityLists);
|
std::sort(_activityLists.begin(), _activityLists.end());
|
||||||
|
resultList.append(_activityLists);
|
||||||
|
}
|
||||||
|
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
_finalList.clear();
|
_finalList.clear();
|
||||||
|
|||||||
@@ -38,6 +38,20 @@ class ActivityListModel : public QAbstractListModel
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
enum ActivityIconType {
|
||||||
|
iconUseCached = 0,
|
||||||
|
iconActivity,
|
||||||
|
iconBell,
|
||||||
|
iconStateError,
|
||||||
|
iconStateWarning,
|
||||||
|
iconStateInfo,
|
||||||
|
iconStateSync
|
||||||
|
};
|
||||||
|
struct ActionIcon {
|
||||||
|
ActivityIconType iconType;
|
||||||
|
QIcon cachedIcon;
|
||||||
|
};
|
||||||
|
|
||||||
explicit ActivityListModel(AccountState *accountState, QWidget *parent = nullptr);
|
explicit ActivityListModel(AccountState *accountState, QWidget *parent = nullptr);
|
||||||
|
|
||||||
QVariant data(const QModelIndex &index, int role) const override;
|
QVariant data(const QModelIndex &index, int role) const override;
|
||||||
@@ -51,6 +65,7 @@ public:
|
|||||||
void addNotificationToActivityList(Activity activity);
|
void addNotificationToActivityList(Activity activity);
|
||||||
void clearNotifications();
|
void clearNotifications();
|
||||||
void addErrorToActivityList(Activity activity);
|
void addErrorToActivityList(Activity activity);
|
||||||
|
void addIgnoredFileToList(Activity newActivity);
|
||||||
void addSyncFileItemToActivityList(Activity activity);
|
void addSyncFileItemToActivityList(Activity activity);
|
||||||
void removeActivityFromActivityList(int row);
|
void removeActivityFromActivityList(int row);
|
||||||
void removeActivityFromActivityList(Activity activity);
|
void removeActivityFromActivityList(Activity activity);
|
||||||
@@ -73,6 +88,8 @@ private:
|
|||||||
ActivityList _activityLists;
|
ActivityList _activityLists;
|
||||||
ActivityList _syncFileItemLists;
|
ActivityList _syncFileItemLists;
|
||||||
ActivityList _notificationLists;
|
ActivityList _notificationLists;
|
||||||
|
ActivityList _listOfIgnoredFiles;
|
||||||
|
Activity _notificationIgnoredFiles;
|
||||||
ActivityList _notificationErrorsLists;
|
ActivityList _notificationErrorsLists;
|
||||||
ActivityList _finalList;
|
ActivityList _finalList;
|
||||||
AccountState *_accountState;
|
AccountState *_accountState;
|
||||||
@@ -81,4 +98,7 @@ private:
|
|||||||
int _currentItem = 0;
|
int _currentItem = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(OCC::ActivityListModel::ActionIcon)
|
||||||
|
|
||||||
#endif // ACTIVITYLISTMODEL_H
|
#endif // ACTIVITYLISTMODEL_H
|
||||||
|
|||||||
@@ -89,6 +89,9 @@ ActivityWidget::ActivityWidget(AccountState *accountState, QWidget *parent)
|
|||||||
this, &ActivityWidget::addError);
|
this, &ActivityWidget::addError);
|
||||||
|
|
||||||
_removeTimer.setInterval(1000);
|
_removeTimer.setInterval(1000);
|
||||||
|
|
||||||
|
// Connect styleChanged events to our widgets, so they can adapt (Dark-/Light-Mode switching)
|
||||||
|
connect(this, &ActivityWidget::styleChanged, delegate, &ActivityItemDelegate::slotStyleChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
ActivityWidget::~ActivityWidget()
|
ActivityWidget::~ActivityWidget()
|
||||||
@@ -127,11 +130,12 @@ void ActivityWidget::slotProgressInfo(const QString &folder, const ProgressInfo
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if(activity._status == SyncFileItem::FileIgnored && !QFileInfo(f->path() + activity._file).exists()){
|
if(activity._status == SyncFileItem::FileIgnored && !QFileInfo(f->path() + activity._file).exists()) {
|
||||||
_model->removeActivityFromActivityList(activity);
|
_model->removeActivityFromActivityList(activity);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if(!QFileInfo(f->path() + activity._file).exists()){
|
if(!QFileInfo(f->path() + activity._file).exists()){
|
||||||
_model->removeActivityFromActivityList(activity);
|
_model->removeActivityFromActivityList(activity);
|
||||||
continue;
|
continue;
|
||||||
@@ -175,7 +179,7 @@ void ActivityWidget::slotItemCompleted(const QString &folder, const SyncFileItem
|
|||||||
Activity activity;
|
Activity activity;
|
||||||
activity._type = Activity::SyncFileItemType; //client activity
|
activity._type = Activity::SyncFileItemType; //client activity
|
||||||
activity._status = item->_status;
|
activity._status = item->_status;
|
||||||
activity._dateTime = QDateTime::fromString(QDateTime::currentDateTime().toString(), Qt::ISODate);
|
activity._dateTime = QDateTime::currentDateTime();
|
||||||
activity._message = item->_originalFile;
|
activity._message = item->_originalFile;
|
||||||
activity._link = folderInstance->accountState()->account()->url();
|
activity._link = folderInstance->accountState()->account()->url();
|
||||||
activity._accName = folderInstance->accountState()->account()->displayName();
|
activity._accName = folderInstance->accountState()->account()->displayName();
|
||||||
@@ -191,8 +195,12 @@ void ActivityWidget::slotItemCompleted(const QString &folder, const SyncFileItem
|
|||||||
qCWarning(lcActivity) << "Item " << item->_file << " retrieved resulted in error " << item->_errorString;
|
qCWarning(lcActivity) << "Item " << item->_file << " retrieved resulted in error " << item->_errorString;
|
||||||
activity._subject = item->_errorString;
|
activity._subject = item->_errorString;
|
||||||
|
|
||||||
// add 'protocol error' to activity list
|
if(item->_status == SyncFileItem::Status::FileIgnored) {
|
||||||
_model->addErrorToActivityList(activity);
|
_model->addIgnoredFileToList(activity);
|
||||||
|
} else {
|
||||||
|
// add 'protocol error' to activity list
|
||||||
|
_model->addErrorToActivityList(activity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -543,6 +551,12 @@ void ActivityWidget::slotNotifyServerFinished(const QString &reply, int replyCod
|
|||||||
qCInfo(lcActivity) << "Server Notification reply code" << replyCode << reply;
|
qCInfo(lcActivity) << "Server Notification reply code" << replyCode << reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ActivityWidget::slotStyleChanged()
|
||||||
|
{
|
||||||
|
// Notify the other widgets (Dark-/Light-Mode switching)
|
||||||
|
emit styleChanged();
|
||||||
|
}
|
||||||
|
|
||||||
/* ==================================================================== */
|
/* ==================================================================== */
|
||||||
|
|
||||||
ActivitySettings::ActivitySettings(AccountState *accountState, QWidget *parent)
|
ActivitySettings::ActivitySettings(AccountState *accountState, QWidget *parent)
|
||||||
@@ -565,6 +579,9 @@ ActivitySettings::ActivitySettings(AccountState *accountState, QWidget *parent)
|
|||||||
// connect a model signal to stop the animation
|
// connect a model signal to stop the animation
|
||||||
connect(_activityWidget, &ActivityWidget::rowsInserted, _progressIndicator, &QProgressIndicator::stopAnimation);
|
connect(_activityWidget, &ActivityWidget::rowsInserted, _progressIndicator, &QProgressIndicator::stopAnimation);
|
||||||
connect(_activityWidget, &ActivityWidget::rowsInserted, this, &ActivitySettings::slotDisplayActivities);
|
connect(_activityWidget, &ActivityWidget::rowsInserted, this, &ActivitySettings::slotDisplayActivities);
|
||||||
|
|
||||||
|
// Connect styleChanged events to our widgets, so they can adapt (Dark-/Light-Mode switching)
|
||||||
|
connect(this, &ActivitySettings::styleChanged, _activityWidget, &ActivityWidget::slotStyleChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActivitySettings::slotDisplayActivities(){
|
void ActivitySettings::slotDisplayActivities(){
|
||||||
@@ -623,4 +640,14 @@ bool ActivitySettings::event(QEvent *e)
|
|||||||
ActivitySettings::~ActivitySettings()
|
ActivitySettings::~ActivitySettings()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ActivitySettings::slotStyleChanged()
|
||||||
|
{
|
||||||
|
if(_progressIndicator)
|
||||||
|
_progressIndicator->setColor(QGuiApplication::palette().color(QPalette::Text));
|
||||||
|
|
||||||
|
// Notify the other widgets (Dark-/Light-Mode switching)
|
||||||
|
emit styleChanged();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,12 +78,14 @@ public slots:
|
|||||||
void addError(const QString &folderAlias, const QString &message, ErrorCategory category);
|
void addError(const QString &folderAlias, const QString &message, ErrorCategory category);
|
||||||
void slotProgressInfo(const QString &folder, const ProgressInfo &progress);
|
void slotProgressInfo(const QString &folder, const ProgressInfo &progress);
|
||||||
void slotItemCompleted(const QString &folder, const SyncFileItemPtr &item);
|
void slotItemCompleted(const QString &folder, const SyncFileItemPtr &item);
|
||||||
|
void slotStyleChanged();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void guiLog(const QString &, const QString &);
|
void guiLog(const QString &, const QString &);
|
||||||
void rowsInserted();
|
void rowsInserted();
|
||||||
void hideActivityTab(bool);
|
void hideActivityTab(bool);
|
||||||
void sendNotificationRequest(const QString &accountName, const QString &link, const QByteArray &verb, int row);
|
void sendNotificationRequest(const QString &accountName, const QString &link, const QByteArray &verb, int row);
|
||||||
|
void styleChanged();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void slotBuildNotificationDisplay(const ActivityList &list);
|
void slotBuildNotificationDisplay(const ActivityList &list);
|
||||||
@@ -96,6 +98,7 @@ private slots:
|
|||||||
void slotSecondaryButtonClickedOnListView(const QModelIndex &index);
|
void slotSecondaryButtonClickedOnListView(const QModelIndex &index);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void customizeStyle();
|
||||||
void showLabels();
|
void showLabels();
|
||||||
QString timeString(QDateTime dt, QLocale::FormatType format) const;
|
QString timeString(QDateTime dt, QLocale::FormatType format) const;
|
||||||
Ui::ActivityWidget *_ui;
|
Ui::ActivityWidget *_ui;
|
||||||
@@ -137,6 +140,7 @@ public slots:
|
|||||||
void slotRefresh();
|
void slotRefresh();
|
||||||
void slotRemoveAccount();
|
void slotRemoveAccount();
|
||||||
void setNotificationRefreshInterval(std::chrono::milliseconds interval);
|
void setNotificationRefreshInterval(std::chrono::milliseconds interval);
|
||||||
|
void slotStyleChanged();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void slotRegularNotificationCheck();
|
void slotRegularNotificationCheck();
|
||||||
@@ -144,6 +148,7 @@ private slots:
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void guiLog(const QString &, const QString &);
|
void guiLog(const QString &, const QString &);
|
||||||
|
void styleChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool event(QEvent *e) override;
|
bool event(QEvent *e) override;
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="pushButtonBrowseCertificate">
|
<widget class="QPushButton" name="pushButtonBrowseCertificate">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Browse...</string>
|
<string>Browse …</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="labelPWDCertificate">
|
<widget class="QLabel" name="labelPWDCertificate">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Certificate password :</string>
|
<string>Certificate password:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@@ -254,6 +254,9 @@ Application::Application(int &argc, char **argv)
|
|||||||
|
|
||||||
// Cleanup at Quit.
|
// Cleanup at Quit.
|
||||||
connect(this, &QCoreApplication::aboutToQuit, this, &Application::slotCleanup);
|
connect(this, &QCoreApplication::aboutToQuit, this, &Application::slotCleanup);
|
||||||
|
|
||||||
|
// Allow other classes to hook into isShowingSettingsDialog() signals (re-auth widgets, for example)
|
||||||
|
connect(_gui.data(), &ownCloudGui::isShowingSettingsDialog, this, &Application::slotGuiIsShowingSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::~Application()
|
Application::~Application()
|
||||||
@@ -648,5 +651,9 @@ void Application::showSettingsDialog()
|
|||||||
_gui->slotShowSettings();
|
_gui->slotShowSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Application::slotGuiIsShowingSettings()
|
||||||
|
{
|
||||||
|
emit isShowingSettingsDialog();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace OCC
|
} // namespace OCC
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ protected:
|
|||||||
signals:
|
signals:
|
||||||
void folderRemoved();
|
void folderRemoved();
|
||||||
void folderStateChanged(Folder *);
|
void folderStateChanged(Folder *);
|
||||||
|
void isShowingSettingsDialog();
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void slotParseMessage(const QString &, QObject *);
|
void slotParseMessage(const QString &, QObject *);
|
||||||
@@ -91,6 +92,7 @@ protected slots:
|
|||||||
void slotAccountStateAdded(AccountState *accountState);
|
void slotAccountStateAdded(AccountState *accountState);
|
||||||
void slotAccountStateRemoved(AccountState *accountState);
|
void slotAccountStateRemoved(AccountState *accountState);
|
||||||
void slotSystemOnlineConfigurationChanged(QNetworkConfiguration);
|
void slotSystemOnlineConfigurationChanged(QNetworkConfiguration);
|
||||||
|
void slotGuiIsShowingSettings();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setHelp();
|
void setHelp();
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ bool ClientProxy::isUsingSystemDefault()
|
|||||||
return cfg.proxyType() == QNetworkProxy::DefaultProxy;
|
return cfg.proxyType() == QNetworkProxy::DefaultProxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString printQNetworkProxy(const QNetworkProxy &proxy)
|
QString printQNetworkProxy(const QNetworkProxy &proxy)
|
||||||
|
|||||||
@@ -52,8 +52,8 @@ void CloudProviderManager::registerSignals()
|
|||||||
CloudProviderManager::CloudProviderManager(QObject *parent) : QObject(parent)
|
CloudProviderManager::CloudProviderManager(QObject *parent) : QObject(parent)
|
||||||
{
|
{
|
||||||
_map = new QMap<QString, CloudProviderWrapper*>();
|
_map = new QMap<QString, CloudProviderWrapper*>();
|
||||||
QString busName = QString(LIBCLOUDPROVIDERS_DBUS_BUS_NAME);
|
_folder_index = 0;
|
||||||
g_bus_own_name (G_BUS_TYPE_SESSION, busName.toAscii().data(), G_BUS_NAME_OWNER_FLAGS_NONE, nullptr, on_name_acquired, nullptr, this, nullptr);
|
g_bus_own_name (G_BUS_TYPE_SESSION, LIBCLOUDPROVIDERS_DBUS_BUS_NAME, 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)
|
||||||
@@ -72,7 +72,7 @@ void CloudProviderManager::slotFolderListChanged(const Folder::Map &folderMap)
|
|||||||
while (j.hasNext()) {
|
while (j.hasNext()) {
|
||||||
j.next();
|
j.next();
|
||||||
if (!_map->contains(j.key())) {
|
if (!_map->contains(j.key())) {
|
||||||
auto *cpo = new CloudProviderWrapper(this, j.value(), _providerExporter);
|
auto *cpo = new CloudProviderWrapper(this, j.value(), _folder_index++, _providerExporter);
|
||||||
_map->insert(j.key(), cpo);
|
_map->insert(j.key(), cpo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ public slots:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
QMap<QString, CloudProviderWrapper*> *_map;
|
QMap<QString, CloudProviderWrapper*> *_map;
|
||||||
|
unsigned int _folder_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CLOUDPROVIDERMANAGER_H
|
#endif // CLOUDPROVIDERMANAGER_H
|
||||||
|
|||||||
@@ -33,13 +33,13 @@ using namespace OCC;
|
|||||||
|
|
||||||
GSimpleActionGroup *actionGroup = nullptr;
|
GSimpleActionGroup *actionGroup = nullptr;
|
||||||
|
|
||||||
CloudProviderWrapper::CloudProviderWrapper(QObject *parent, Folder *folder, CloudProvidersProviderExporter* cloudprovider) : QObject(parent)
|
CloudProviderWrapper::CloudProviderWrapper(QObject *parent, Folder *folder, int folderId, CloudProvidersProviderExporter* cloudprovider) : QObject(parent)
|
||||||
, _folder(folder)
|
, _folder(folder)
|
||||||
{
|
{
|
||||||
GMenuModel *model;
|
GMenuModel *model;
|
||||||
GActionGroup *action_group;
|
GActionGroup *action_group;
|
||||||
_recentlyChanged = new QList<QPair<QString, QString>>();
|
_recentlyChanged = new QList<QPair<QString, QString>>();
|
||||||
QString accountName = QString("Account%1Folder%2").arg(folder->alias(), folder->accountState()->account()->id());
|
QString accountName = QString("Folder/%1").arg(folderId);
|
||||||
|
|
||||||
_cloudProvider = CLOUD_PROVIDERS_PROVIDER_EXPORTER(cloudprovider);
|
_cloudProvider = CLOUD_PROVIDERS_PROVIDER_EXPORTER(cloudprovider);
|
||||||
_cloudProviderAccount = cloud_providers_account_exporter_new(_cloudProvider, accountName.toUtf8().data());
|
_cloudProviderAccount = cloud_providers_account_exporter_new(_cloudProvider, accountName.toUtf8().data());
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class CloudProviderWrapper : public QObject
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit CloudProviderWrapper(QObject *parent = nullptr, Folder *folder = nullptr, CloudProvidersProviderExporter* cloudprovider = nullptr);
|
explicit CloudProviderWrapper(QObject *parent = nullptr, Folder *folder = nullptr, int folderId = 0, CloudProvidersProviderExporter* cloudprovider = nullptr);
|
||||||
~CloudProviderWrapper();
|
~CloudProviderWrapper();
|
||||||
CloudProvidersAccountExporter* accountExporter();
|
CloudProvidersAccountExporter* accountExporter();
|
||||||
Folder* folder();
|
Folder* folder();
|
||||||
|
|||||||
@@ -22,6 +22,8 @@
|
|||||||
|
|
||||||
#include "connectionvalidator.h"
|
#include "connectionvalidator.h"
|
||||||
#include "account.h"
|
#include "account.h"
|
||||||
|
#include "accountstate.h"
|
||||||
|
#include "userinfo.h"
|
||||||
#include "networkjobs.h"
|
#include "networkjobs.h"
|
||||||
#include "clientproxy.h"
|
#include "clientproxy.h"
|
||||||
#include <creds/abstractcredentials.h>
|
#include <creds/abstractcredentials.h>
|
||||||
@@ -34,9 +36,10 @@ Q_LOGGING_CATEGORY(lcConnectionValidator, "nextcloud.sync.connectionvalidator",
|
|||||||
// This makes sure we get tried often enough without "ConnectionValidator already running"
|
// This makes sure we get tried often enough without "ConnectionValidator already running"
|
||||||
static qint64 timeoutToUseMsec = qMax(1000, ConnectionValidator::DefaultCallingIntervalMsec - 5 * 1000);
|
static qint64 timeoutToUseMsec = qMax(1000, ConnectionValidator::DefaultCallingIntervalMsec - 5 * 1000);
|
||||||
|
|
||||||
ConnectionValidator::ConnectionValidator(AccountPtr account, QObject *parent)
|
ConnectionValidator::ConnectionValidator(AccountStatePtr accountState, QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, _account(account)
|
, _accountState(accountState)
|
||||||
|
, _account(accountState->account())
|
||||||
, _isCheckingServerAndAuth(false)
|
, _isCheckingServerAndAuth(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -44,7 +47,7 @@ ConnectionValidator::ConnectionValidator(AccountPtr account, QObject *parent)
|
|||||||
void ConnectionValidator::checkServerAndAuth()
|
void ConnectionValidator::checkServerAndAuth()
|
||||||
{
|
{
|
||||||
if (!_account) {
|
if (!_account) {
|
||||||
_errors << tr("No ownCloud account configured");
|
_errors << tr("No Nextcloud account configured");
|
||||||
reportResult(NotConfigured);
|
reportResult(NotConfigured);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -265,10 +268,9 @@ void ConnectionValidator::ocsConfigReceived(const QJsonDocument &json, AccountPt
|
|||||||
|
|
||||||
void ConnectionValidator::fetchUser()
|
void ConnectionValidator::fetchUser()
|
||||||
{
|
{
|
||||||
JsonApiJob *job = new JsonApiJob(_account, QLatin1String("ocs/v1.php/cloud/user"), this);
|
UserInfo *userInfo = new UserInfo(_accountState.data(), true, true, this);
|
||||||
job->setTimeout(timeoutToUseMsec);
|
QObject::connect(userInfo, &UserInfo::fetchedLastInfo, this, &ConnectionValidator::slotUserFetched);
|
||||||
QObject::connect(job, &JsonApiJob::jsonReceived, this, &ConnectionValidator::slotUserFetched);
|
userInfo->setActive(true);
|
||||||
job->start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConnectionValidator::setAndCheckServerVersion(const QString &version)
|
bool ConnectionValidator::setAndCheckServerVersion(const QString &version)
|
||||||
@@ -300,34 +302,22 @@ bool ConnectionValidator::setAndCheckServerVersion(const QString &version)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionValidator::slotUserFetched(const QJsonDocument &json)
|
void ConnectionValidator::slotUserFetched(UserInfo *userInfo)
|
||||||
{
|
{
|
||||||
QString user = json.object().value("ocs").toObject().value("data").toObject().value("id").toString();
|
if(userInfo) {
|
||||||
if (!user.isEmpty()) {
|
userInfo->setActive(false);
|
||||||
_account->setDavUser(user);
|
userInfo->deleteLater();
|
||||||
}
|
|
||||||
QString displayName = json.object().value("ocs").toObject().value("data").toObject().value("display-name").toString();
|
|
||||||
if (!displayName.isEmpty()) {
|
|
||||||
_account->setDavDisplayName(displayName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef TOKEN_AUTH_ONLY
|
#ifndef TOKEN_AUTH_ONLY
|
||||||
AvatarJob *job = new AvatarJob(_account, _account->davUser(), 128, this);
|
connect(_account->e2e(), &ClientSideEncryption::initializationFinished, this, &ConnectionValidator::reportConnected);
|
||||||
job->setTimeout(20 * 1000);
|
_account->e2e()->initialize();
|
||||||
QObject::connect(job, &AvatarJob::avatarPixmap, this, &ConnectionValidator::slotAvatarImage);
|
|
||||||
job->start();
|
|
||||||
#else
|
#else
|
||||||
reportResult(Connected);
|
reportResult(Connected);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef TOKEN_AUTH_ONLY
|
#ifndef TOKEN_AUTH_ONLY
|
||||||
void ConnectionValidator::slotAvatarImage(const QImage &img)
|
|
||||||
{
|
|
||||||
_account->setAvatar(img);
|
|
||||||
connect(_account->e2e(), &ClientSideEncryption::initializationFinished, this, &ConnectionValidator::reportConnected);
|
|
||||||
_account->e2e()->initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConnectionValidator::reportConnected() {
|
void ConnectionValidator::reportConnected() {
|
||||||
reportResult(Connected);
|
reportResult(Connected);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,23 +67,20 @@ namespace OCC {
|
|||||||
+---------------------------------+
|
+---------------------------------+
|
||||||
|
|
|
|
||||||
fetchUser
|
fetchUser
|
||||||
PropfindJob
|
Utilizes the UserInfo class to fetch the user and avatar image
|
||||||
|
|
|
||||||
+-> slotUserFetched
|
|
||||||
AvatarJob
|
|
||||||
|
|
|
||||||
+-> slotAvatarImage -->
|
|
||||||
+-----------------------------------+
|
+-----------------------------------+
|
||||||
|
|
|
|
||||||
+-> Client Side Encryption Checks --+ --reportResult()
|
+-> Client Side Encryption Checks --+ --reportResult()
|
||||||
\endcode
|
\endcode
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
class UserInfo;
|
||||||
|
|
||||||
class ConnectionValidator : public QObject
|
class ConnectionValidator : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit ConnectionValidator(AccountPtr account, QObject *parent = nullptr);
|
explicit ConnectionValidator(AccountStatePtr accountState, QObject *parent = nullptr);
|
||||||
|
|
||||||
enum Status {
|
enum Status {
|
||||||
Undefined,
|
Undefined,
|
||||||
@@ -125,13 +122,12 @@ protected slots:
|
|||||||
void slotAuthSuccess();
|
void slotAuthSuccess();
|
||||||
|
|
||||||
void slotCapabilitiesRecieved(const QJsonDocument &);
|
void slotCapabilitiesRecieved(const QJsonDocument &);
|
||||||
void slotUserFetched(const QJsonDocument &);
|
void slotUserFetched(UserInfo *userInfo);
|
||||||
#ifndef TOKEN_AUTH_ONLY
|
|
||||||
void slotAvatarImage(const QImage &img);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
#ifndef TOKEN_AUTH_ONLY
|
||||||
void reportConnected();
|
void reportConnected();
|
||||||
|
#endif
|
||||||
void reportResult(Status status);
|
void reportResult(Status status);
|
||||||
void checkServerCapabilities();
|
void checkServerCapabilities();
|
||||||
void fetchUser();
|
void fetchUser();
|
||||||
@@ -144,6 +140,7 @@ private:
|
|||||||
bool setAndCheckServerVersion(const QString &version);
|
bool setAndCheckServerVersion(const QString &version);
|
||||||
|
|
||||||
QStringList _errors;
|
QStringList _errors;
|
||||||
|
AccountStatePtr _accountState;
|
||||||
AccountPtr _account;
|
AccountPtr _account;
|
||||||
bool _isCheckingServerAndAuth;
|
bool _isCheckingServerAndAuth;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,10 +14,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QClipboard>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
#include "account.h"
|
#include "account.h"
|
||||||
#include "creds/flow2auth.h"
|
#include "flow2auth.h"
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include "theme.h"
|
#include "theme.h"
|
||||||
@@ -28,6 +30,17 @@ namespace OCC {
|
|||||||
|
|
||||||
Q_LOGGING_CATEGORY(lcFlow2auth, "nextcloud.sync.credentials.flow2auth", QtInfoMsg)
|
Q_LOGGING_CATEGORY(lcFlow2auth, "nextcloud.sync.credentials.flow2auth", QtInfoMsg)
|
||||||
|
|
||||||
|
|
||||||
|
Flow2Auth::Flow2Auth(Account *account, QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, _account(account)
|
||||||
|
, _isBusy(false)
|
||||||
|
, _hasToken(false)
|
||||||
|
{
|
||||||
|
_pollTimer.setInterval(1000);
|
||||||
|
QObject::connect(&_pollTimer, &QTimer::timeout, this, &Flow2Auth::slotPollTimerTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
Flow2Auth::~Flow2Auth()
|
Flow2Auth::~Flow2Auth()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -47,7 +60,23 @@ QUrl Flow2Auth::authorisationLink() const
|
|||||||
|
|
||||||
void Flow2Auth::openBrowser()
|
void Flow2Auth::openBrowser()
|
||||||
{
|
{
|
||||||
_pollTimer.stop();
|
fetchNewToken(TokenAction::actionOpenBrowser);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Flow2Auth::copyLinkToClipboard()
|
||||||
|
{
|
||||||
|
fetchNewToken(TokenAction::actionCopyLinkToClipboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Flow2Auth::fetchNewToken(const TokenAction action)
|
||||||
|
{
|
||||||
|
if(_isBusy)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_isBusy = true;
|
||||||
|
_hasToken = false;
|
||||||
|
|
||||||
|
emit statusChanged(PollStatus::statusFetchToken, 0);
|
||||||
|
|
||||||
// Step 1: Initiate a login, do an anonymous POST request
|
// Step 1: Initiate a login, do an anonymous POST request
|
||||||
QUrl url = Utility::concatUrlPath(_account->url().toString(), QLatin1String("/index.php/login/v2"));
|
QUrl url = Utility::concatUrlPath(_account->url().toString(), QLatin1String("/index.php/login/v2"));
|
||||||
@@ -59,14 +88,18 @@ void Flow2Auth::openBrowser()
|
|||||||
auto job = _account->sendRequest("POST", url, req);
|
auto job = _account->sendRequest("POST", url, req);
|
||||||
job->setTimeout(qMin(30 * 1000ll, job->timeoutMsec()));
|
job->setTimeout(qMin(30 * 1000ll, job->timeoutMsec()));
|
||||||
|
|
||||||
QObject::connect(job, &SimpleNetworkJob::finishedSignal, this, [this](QNetworkReply *reply) {
|
QObject::connect(job, &SimpleNetworkJob::finishedSignal, this, [this, action](QNetworkReply *reply) {
|
||||||
auto jsonData = reply->readAll();
|
auto jsonData = reply->readAll();
|
||||||
QJsonParseError jsonParseError;
|
QJsonParseError jsonParseError;
|
||||||
QJsonObject json = QJsonDocument::fromJson(jsonData, &jsonParseError).object();
|
QJsonObject json = QJsonDocument::fromJson(jsonData, &jsonParseError).object();
|
||||||
|
QString pollToken, pollEndpoint, loginUrl;
|
||||||
|
|
||||||
QString pollToken = json.value("poll").toObject().value("token").toString();
|
if (reply->error() == QNetworkReply::NoError && jsonParseError.error == QJsonParseError::NoError
|
||||||
QString pollEndpoint = json.value("poll").toObject().value("endpoint").toString();
|
&& !json.isEmpty()) {
|
||||||
QUrl loginUrl = json["login"].toString();
|
pollToken = json.value("poll").toObject().value("token").toString();
|
||||||
|
pollEndpoint = json.value("poll").toObject().value("endpoint").toString();
|
||||||
|
loginUrl = json["login"].toString();
|
||||||
|
}
|
||||||
|
|
||||||
if (reply->error() != QNetworkReply::NoError || jsonParseError.error != QJsonParseError::NoError
|
if (reply->error() != QNetworkReply::NoError || jsonParseError.error != QJsonParseError::NoError
|
||||||
|| json.isEmpty() || pollToken.isEmpty() || pollEndpoint.isEmpty() || loginUrl.isEmpty()) {
|
|| json.isEmpty() || pollToken.isEmpty() || pollEndpoint.isEmpty() || loginUrl.isEmpty()) {
|
||||||
@@ -85,7 +118,9 @@ void Flow2Auth::openBrowser()
|
|||||||
errorReason = tr("The reply from the server did not contain all expected fields");
|
errorReason = tr("The reply from the server did not contain all expected fields");
|
||||||
}
|
}
|
||||||
qCWarning(lcFlow2auth) << "Error when getting the loginUrl" << json << errorReason;
|
qCWarning(lcFlow2auth) << "Error when getting the loginUrl" << json << errorReason;
|
||||||
emit result(Error);
|
emit result(Error, errorReason);
|
||||||
|
_pollTimer.stop();
|
||||||
|
_isBusy = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,23 +134,50 @@ void Flow2Auth::openBrowser()
|
|||||||
ConfigFile cfg;
|
ConfigFile cfg;
|
||||||
std::chrono::milliseconds polltime = cfg.remotePollInterval();
|
std::chrono::milliseconds polltime = cfg.remotePollInterval();
|
||||||
qCInfo(lcFlow2auth) << "setting remote poll timer interval to" << polltime.count() << "msec";
|
qCInfo(lcFlow2auth) << "setting remote poll timer interval to" << polltime.count() << "msec";
|
||||||
_pollTimer.setInterval(polltime.count());
|
_secondsInterval = (polltime.count() / 1000);
|
||||||
QObject::connect(&_pollTimer, &QTimer::timeout, this, &Flow2Auth::slotPollTimerTimeout);
|
_secondsLeft = _secondsInterval;
|
||||||
_pollTimer.start();
|
emit statusChanged(PollStatus::statusPollCountdown, _secondsLeft);
|
||||||
|
|
||||||
|
if(!_pollTimer.isActive()) {
|
||||||
// Try to open Browser
|
_pollTimer.start();
|
||||||
if (!QDesktopServices::openUrl(authorisationLink())) {
|
|
||||||
// We cannot open the browser, then we claim we don't support Flow2Auth.
|
|
||||||
// Our UI callee should ask the user to copy and open the link.
|
|
||||||
emit result(NotSupported, QString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
switch(action)
|
||||||
|
{
|
||||||
|
case actionOpenBrowser:
|
||||||
|
// Try to open Browser
|
||||||
|
if (!QDesktopServices::openUrl(authorisationLink())) {
|
||||||
|
// We cannot open the browser, then we claim we don't support Flow2Auth.
|
||||||
|
// Our UI callee will ask the user to copy and open the link.
|
||||||
|
emit result(NotSupported);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case actionCopyLinkToClipboard:
|
||||||
|
QApplication::clipboard()->setText(authorisationLink().toString(QUrl::FullyEncoded));
|
||||||
|
emit statusChanged(PollStatus::statusCopyLinkToClipboard, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isBusy = false;
|
||||||
|
_hasToken = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Flow2Auth::slotPollTimerTimeout()
|
void Flow2Auth::slotPollTimerTimeout()
|
||||||
{
|
{
|
||||||
_pollTimer.stop();
|
if(_isBusy || !_hasToken)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_isBusy = true;
|
||||||
|
|
||||||
|
_secondsLeft--;
|
||||||
|
if(_secondsLeft > 0) {
|
||||||
|
emit statusChanged(PollStatus::statusPollCountdown, _secondsLeft);
|
||||||
|
_isBusy = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit statusChanged(PollStatus::statusPollNow, 0);
|
||||||
|
|
||||||
// Step 2: Poll
|
// Step 2: Poll
|
||||||
QNetworkRequest req;
|
QNetworkRequest req;
|
||||||
@@ -132,10 +194,15 @@ void Flow2Auth::slotPollTimerTimeout()
|
|||||||
auto jsonData = reply->readAll();
|
auto jsonData = reply->readAll();
|
||||||
QJsonParseError jsonParseError;
|
QJsonParseError jsonParseError;
|
||||||
QJsonObject json = QJsonDocument::fromJson(jsonData, &jsonParseError).object();
|
QJsonObject json = QJsonDocument::fromJson(jsonData, &jsonParseError).object();
|
||||||
|
QUrl serverUrl;
|
||||||
|
QString loginName, appPassword;
|
||||||
|
|
||||||
QUrl serverUrl = json["server"].toString();
|
if (reply->error() == QNetworkReply::NoError && jsonParseError.error == QJsonParseError::NoError
|
||||||
QString loginName = json["loginName"].toString();
|
&& !json.isEmpty()) {
|
||||||
QString appPassword = json["appPassword"].toString();
|
serverUrl = json["server"].toString();
|
||||||
|
loginName = json["loginName"].toString();
|
||||||
|
appPassword = json["appPassword"].toString();
|
||||||
|
}
|
||||||
|
|
||||||
if (reply->error() != QNetworkReply::NoError || jsonParseError.error != QJsonParseError::NoError
|
if (reply->error() != QNetworkReply::NoError || jsonParseError.error != QJsonParseError::NoError
|
||||||
|| json.isEmpty() || serverUrl.isEmpty() || loginName.isEmpty() || appPassword.isEmpty()) {
|
|| json.isEmpty() || serverUrl.isEmpty() || loginName.isEmpty() || appPassword.isEmpty()) {
|
||||||
@@ -155,26 +222,50 @@ void Flow2Auth::slotPollTimerTimeout()
|
|||||||
}
|
}
|
||||||
qCDebug(lcFlow2auth) << "Error when polling for the appPassword" << json << errorReason;
|
qCDebug(lcFlow2auth) << "Error when polling for the appPassword" << json << errorReason;
|
||||||
|
|
||||||
|
// We get a 404 until authentication is done, so don't show this error in the GUI.
|
||||||
|
if(reply->error() != QNetworkReply::ContentNotFoundError)
|
||||||
|
emit result(Error, errorReason);
|
||||||
|
|
||||||
// Forget sensitive data
|
// Forget sensitive data
|
||||||
appPassword.clear();
|
appPassword.clear();
|
||||||
loginName.clear();
|
loginName.clear();
|
||||||
|
|
||||||
// Failed: poll again
|
// Failed: poll again
|
||||||
_pollTimer.start();
|
_secondsLeft = _secondsInterval;
|
||||||
|
_isBusy = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_pollTimer.stop();
|
||||||
|
|
||||||
// Success
|
// Success
|
||||||
qCInfo(lcFlow2auth) << "Success getting the appPassword for user: " << loginName << ", server: " << serverUrl.toString();
|
qCInfo(lcFlow2auth) << "Success getting the appPassword for user: " << loginName << ", server: " << serverUrl.toString();
|
||||||
|
|
||||||
_account->setUrl(serverUrl);
|
_account->setUrl(serverUrl);
|
||||||
|
|
||||||
emit result(LoggedIn, loginName, appPassword);
|
emit result(LoggedIn, QString(), loginName, appPassword);
|
||||||
|
|
||||||
// Forget sensitive data
|
// Forget sensitive data
|
||||||
appPassword.clear();
|
appPassword.clear();
|
||||||
loginName.clear();
|
loginName.clear();
|
||||||
|
|
||||||
|
_loginUrl.clear();
|
||||||
|
_pollToken.clear();
|
||||||
|
_pollEndpoint.clear();
|
||||||
|
|
||||||
|
_isBusy = false;
|
||||||
|
_hasToken = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Flow2Auth::slotPollNow()
|
||||||
|
{
|
||||||
|
// poll now if we're not already doing so
|
||||||
|
if(_isBusy || !_hasToken)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_secondsLeft = 1;
|
||||||
|
slotPollTimerTimeout();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace OCC
|
} // namespace OCC
|
||||||
|
|||||||
@@ -25,17 +25,23 @@ namespace OCC {
|
|||||||
* Job that does the authorization, grants and fetches the access token via Login Flow v2
|
* Job that does the authorization, grants and fetches the access token via Login Flow v2
|
||||||
*
|
*
|
||||||
* See: https://docs.nextcloud.com/server/latest/developer_manual/client_apis/LoginFlow/index.html#login-flow-v2
|
* See: https://docs.nextcloud.com/server/latest/developer_manual/client_apis/LoginFlow/index.html#login-flow-v2
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
class Flow2Auth : public QObject
|
class Flow2Auth : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
Flow2Auth(Account *account, QObject *parent)
|
enum TokenAction {
|
||||||
: QObject(parent)
|
actionOpenBrowser = 1,
|
||||||
, _account(account)
|
actionCopyLinkToClipboard
|
||||||
{
|
};
|
||||||
}
|
enum PollStatus {
|
||||||
|
statusPollCountdown = 1,
|
||||||
|
statusPollNow,
|
||||||
|
statusFetchToken,
|
||||||
|
statusCopyLinkToClipboard
|
||||||
|
};
|
||||||
|
|
||||||
|
Flow2Auth(Account *account, QObject *parent);
|
||||||
~Flow2Auth();
|
~Flow2Auth();
|
||||||
|
|
||||||
enum Result { NotSupported,
|
enum Result { NotSupported,
|
||||||
@@ -44,6 +50,7 @@ public:
|
|||||||
Q_ENUM(Result);
|
Q_ENUM(Result);
|
||||||
void start();
|
void start();
|
||||||
void openBrowser();
|
void openBrowser();
|
||||||
|
void copyLinkToClipboard();
|
||||||
QUrl authorisationLink() const;
|
QUrl authorisationLink() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
@@ -51,18 +58,29 @@ signals:
|
|||||||
* The state has changed.
|
* The state has changed.
|
||||||
* when logged in, appPassword has the value of the app password.
|
* when logged in, appPassword has the value of the app password.
|
||||||
*/
|
*/
|
||||||
void result(Flow2Auth::Result result, const QString &user = QString(), const QString &appPassword = QString());
|
void result(Flow2Auth::Result result, const QString &errorString = QString(),
|
||||||
|
const QString &user = QString(), const QString &appPassword = QString());
|
||||||
|
|
||||||
|
void statusChanged(const PollStatus status, int secondsLeft);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void slotPollNow();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void slotPollTimerTimeout();
|
void slotPollTimerTimeout();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void fetchNewToken(const TokenAction action);
|
||||||
|
|
||||||
Account *_account;
|
Account *_account;
|
||||||
QUrl _loginUrl;
|
QUrl _loginUrl;
|
||||||
QString _pollToken;
|
QString _pollToken;
|
||||||
QString _pollEndpoint;
|
QString _pollEndpoint;
|
||||||
QTimer _pollTimer;
|
QTimer _pollTimer;
|
||||||
|
int _secondsLeft;
|
||||||
|
int _secondsInterval;
|
||||||
|
bool _isBusy;
|
||||||
|
bool _hasToken;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace OCC
|
} // namespace OCC
|
||||||
|
|||||||
221
src/gui/creds/keychainchunk.cpp
Normal file
221
src/gui/creds/keychainchunk.cpp
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) by Michael Schuster <michael@nextcloud.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "account.h"
|
||||||
|
#include "keychainchunk.h"
|
||||||
|
#include "theme.h"
|
||||||
|
#include "networkjobs.h"
|
||||||
|
#include "configfile.h"
|
||||||
|
#include "creds/abstractcredentials.h"
|
||||||
|
|
||||||
|
using namespace QKeychain;
|
||||||
|
|
||||||
|
namespace OCC {
|
||||||
|
|
||||||
|
Q_LOGGING_CATEGORY(lcKeychainChunk, "nextcloud.sync.credentials.keychainchunk", QtInfoMsg)
|
||||||
|
|
||||||
|
namespace KeychainChunk {
|
||||||
|
|
||||||
|
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
|
||||||
|
static void addSettingsToJob(Account *account, QKeychain::Job *job)
|
||||||
|
{
|
||||||
|
Q_UNUSED(account)
|
||||||
|
auto settings = ConfigFile::settingsWithGroup(Theme::instance()->appName());
|
||||||
|
settings->setParent(job); // make the job parent to make setting deleted properly
|
||||||
|
job->setSettings(settings.release());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Job
|
||||||
|
*/
|
||||||
|
Job::Job(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
_serviceName = Theme::instance()->appName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WriteJob
|
||||||
|
*/
|
||||||
|
WriteJob::WriteJob(Account *account, const QString &key, const QByteArray &data, QObject *parent)
|
||||||
|
: Job(parent)
|
||||||
|
{
|
||||||
|
_account = account;
|
||||||
|
_key = key;
|
||||||
|
|
||||||
|
// Windows workaround: Split the private key into chunks of 2048 bytes,
|
||||||
|
// to allow 4k (4096 bit) keys to be saved (obey Windows's limits)
|
||||||
|
_chunkBuffer = data;
|
||||||
|
_chunkCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteJob::start()
|
||||||
|
{
|
||||||
|
slotWriteJobDone(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteJob::slotWriteJobDone(QKeychain::Job *incomingJob)
|
||||||
|
{
|
||||||
|
QKeychain::WritePasswordJob *writeJob = static_cast<QKeychain::WritePasswordJob *>(incomingJob);
|
||||||
|
|
||||||
|
// errors?
|
||||||
|
if (writeJob) {
|
||||||
|
_error = writeJob->error();
|
||||||
|
_errorString = writeJob->errorString();
|
||||||
|
|
||||||
|
if (writeJob->error() != NoError) {
|
||||||
|
qCWarning(lcKeychainChunk) << "Error while writing" << writeJob->key() << "chunk" << writeJob->errorString();
|
||||||
|
_chunkBuffer.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write a chunk if there is any in the buffer
|
||||||
|
if (!_chunkBuffer.isEmpty()) {
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
// Windows workaround: Split the data into chunks of 2048 bytes,
|
||||||
|
// to allow 4k (4096 bit) keys to be saved (obey Windows's limits)
|
||||||
|
auto chunk = _chunkBuffer.left(KeychainChunk::ChunkSize);
|
||||||
|
|
||||||
|
_chunkBuffer = _chunkBuffer.right(_chunkBuffer.size() - chunk.size());
|
||||||
|
#else
|
||||||
|
// write full data in one chunk on non-Windows, as usual
|
||||||
|
auto chunk = _chunkBuffer;
|
||||||
|
|
||||||
|
_chunkBuffer.clear();
|
||||||
|
#endif
|
||||||
|
auto index = (_chunkCount++);
|
||||||
|
|
||||||
|
// keep the limit
|
||||||
|
if (_chunkCount > KeychainChunk::MaxChunks) {
|
||||||
|
qCWarning(lcKeychainChunk) << "Maximum chunk count exceeded while writing" << writeJob->key() << "chunk" << QString::number(index) << "cutting off after" << QString::number(KeychainChunk::MaxChunks) << "chunks";
|
||||||
|
|
||||||
|
writeJob->deleteLater();
|
||||||
|
|
||||||
|
_chunkBuffer.clear();
|
||||||
|
|
||||||
|
emit finished(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString kck = AbstractCredentials::keychainKey(
|
||||||
|
_account->url().toString(),
|
||||||
|
_key + (index > 0 ? (QString(".") + QString::number(index)) : QString()),
|
||||||
|
_account->id());
|
||||||
|
|
||||||
|
QKeychain::WritePasswordJob *job = new QKeychain::WritePasswordJob(_serviceName);
|
||||||
|
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
|
||||||
|
addSettingsToJob(_account, job);
|
||||||
|
#endif
|
||||||
|
job->setInsecureFallback(_insecureFallback);
|
||||||
|
connect(job, &QKeychain::Job::finished, this, &KeychainChunk::WriteJob::slotWriteJobDone);
|
||||||
|
// only add the key's (sub)"index" after the first element, to stay compatible with older versions and non-Windows
|
||||||
|
job->setKey(kck);
|
||||||
|
job->setBinaryData(chunk);
|
||||||
|
job->start();
|
||||||
|
|
||||||
|
chunk.clear();
|
||||||
|
} else {
|
||||||
|
emit finished(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeJob->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ReadJob
|
||||||
|
*/
|
||||||
|
ReadJob::ReadJob(Account *account, const QString &key, const bool &keychainMigration, QObject *parent)
|
||||||
|
: Job(parent)
|
||||||
|
{
|
||||||
|
_account = account;
|
||||||
|
_key = key;
|
||||||
|
|
||||||
|
_keychainMigration = keychainMigration;
|
||||||
|
|
||||||
|
_chunkCount = 0;
|
||||||
|
_chunkBuffer.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadJob::start()
|
||||||
|
{
|
||||||
|
_chunkCount = 0;
|
||||||
|
_chunkBuffer.clear();
|
||||||
|
|
||||||
|
const QString kck = AbstractCredentials::keychainKey(
|
||||||
|
_account->url().toString(),
|
||||||
|
_key,
|
||||||
|
_keychainMigration ? QString() : _account->id());
|
||||||
|
|
||||||
|
QKeychain::ReadPasswordJob *job = new QKeychain::ReadPasswordJob(_serviceName);
|
||||||
|
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
|
||||||
|
addSettingsToJob(_account, job);
|
||||||
|
#endif
|
||||||
|
job->setInsecureFallback(_insecureFallback);
|
||||||
|
job->setKey(kck);
|
||||||
|
connect(job, &QKeychain::Job::finished, this, &KeychainChunk::ReadJob::slotReadJobDone);
|
||||||
|
job->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadJob::slotReadJobDone(QKeychain::Job *incomingJob)
|
||||||
|
{
|
||||||
|
// Errors or next chunk?
|
||||||
|
QKeychain::ReadPasswordJob *readJob = static_cast<QKeychain::ReadPasswordJob *>(incomingJob);
|
||||||
|
|
||||||
|
if (readJob) {
|
||||||
|
if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
|
||||||
|
_chunkBuffer.append(readJob->binaryData());
|
||||||
|
_chunkCount++;
|
||||||
|
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
// try to fetch next chunk
|
||||||
|
if (_chunkCount < KeychainChunk::MaxChunks) {
|
||||||
|
const QString kck = AbstractCredentials::keychainKey(
|
||||||
|
_account->url().toString(),
|
||||||
|
_key + QString(".") + QString::number(_chunkCount),
|
||||||
|
_keychainMigration ? QString() : _account->id());
|
||||||
|
|
||||||
|
QKeychain::ReadPasswordJob *job = new QKeychain::ReadPasswordJob(_serviceName);
|
||||||
|
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
|
||||||
|
addSettingsToJob(_account, job);
|
||||||
|
#endif
|
||||||
|
job->setInsecureFallback(_insecureFallback);
|
||||||
|
job->setKey(kck);
|
||||||
|
connect(job, &QKeychain::Job::finished, this, &KeychainChunk::ReadJob::slotReadJobDone);
|
||||||
|
job->start();
|
||||||
|
|
||||||
|
readJob->deleteLater();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
qCWarning(lcKeychainChunk) << "Maximum chunk count for" << readJob->key() << "reached, ignoring after" << KeychainChunk::MaxChunks;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
if (readJob->error() != QKeychain::Error::EntryNotFound ||
|
||||||
|
((readJob->error() == QKeychain::Error::EntryNotFound) && _chunkCount == 0)) {
|
||||||
|
_error = readJob->error();
|
||||||
|
_errorString = readJob->errorString();
|
||||||
|
qCWarning(lcKeychainChunk) << "Unable to read" << readJob->key() << "chunk" << QString::number(_chunkCount) << readJob->errorString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readJob->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
emit finished(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace KeychainChunk
|
||||||
|
|
||||||
|
} // namespace OCC
|
||||||
120
src/gui/creds/keychainchunk.h
Normal file
120
src/gui/creds/keychainchunk.h
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) by Michael Schuster <michael@nextcloud.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifndef KEYCHAINCHUNK_H
|
||||||
|
#define KEYCHAINCHUNK_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <keychain.h>
|
||||||
|
#include "accountfwd.h"
|
||||||
|
|
||||||
|
// We don't support insecure fallback
|
||||||
|
// #define KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK
|
||||||
|
|
||||||
|
namespace OCC {
|
||||||
|
|
||||||
|
namespace KeychainChunk {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Workaround for Windows:
|
||||||
|
*
|
||||||
|
* Split the keychain entry's data into chunks of 2048 bytes,
|
||||||
|
* to allow 4k (4096 bit) keys / large certs to be saved (see limits in webflowcredentials.h)
|
||||||
|
*/
|
||||||
|
static constexpr int ChunkSize = 2048;
|
||||||
|
static constexpr int MaxChunks = 10;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief: Abstract base class for KeychainChunk jobs.
|
||||||
|
*/
|
||||||
|
class Job : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
Job(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
const QKeychain::Error error() const {
|
||||||
|
return _error;
|
||||||
|
}
|
||||||
|
const QString errorString() const {
|
||||||
|
return _errorString;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray binaryData() const {
|
||||||
|
return _chunkBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool insecureFallback() const {
|
||||||
|
return _insecureFallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we use it but don't support insecure fallback, give us nice compilation errors ;p
|
||||||
|
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
|
||||||
|
void setInsecureFallback(const bool &insecureFallback)
|
||||||
|
{
|
||||||
|
_insecureFallback = insecureFallback;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QString _serviceName;
|
||||||
|
Account *_account;
|
||||||
|
QString _key;
|
||||||
|
bool _insecureFallback = false;
|
||||||
|
bool _keychainMigration = false;
|
||||||
|
|
||||||
|
QKeychain::Error _error = QKeychain::NoError;
|
||||||
|
QString _errorString;
|
||||||
|
|
||||||
|
int _chunkCount = 0;
|
||||||
|
QByteArray _chunkBuffer;
|
||||||
|
}; // class Job
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief: Simple wrapper class for QKeychain::WritePasswordJob, splits too large keychain entry's data into chunks on Windows
|
||||||
|
*/
|
||||||
|
class WriteJob : public KeychainChunk::Job {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
WriteJob(Account *account, const QString &key, const QByteArray &data, QObject *parent = nullptr);
|
||||||
|
void start();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void finished(KeychainChunk::WriteJob *incomingJob);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void slotWriteJobDone(QKeychain::Job *incomingJob);
|
||||||
|
}; // class WriteJob
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief: Simple wrapper class for QKeychain::ReadPasswordJob, splits too large keychain entry's data into chunks on Windows
|
||||||
|
*/
|
||||||
|
class ReadJob : public KeychainChunk::Job {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
ReadJob(Account *account, const QString &key, const bool &keychainMigration, QObject *parent = nullptr);
|
||||||
|
void start();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void finished(KeychainChunk::ReadJob *incomingJob);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void slotReadJobDone(QKeychain::Job *incomingJob);
|
||||||
|
}; // class ReadJob
|
||||||
|
|
||||||
|
} // namespace KeychainChunk
|
||||||
|
|
||||||
|
} // namespace OCC
|
||||||
|
|
||||||
|
#endif // KEYCHAINCHUNK_H
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
#include "theme.h"
|
#include "theme.h"
|
||||||
#include "wizard/webview.h"
|
#include "wizard/webview.h"
|
||||||
#include "webflowcredentialsdialog.h"
|
#include "webflowcredentialsdialog.h"
|
||||||
|
#include "keychainchunk.h"
|
||||||
|
|
||||||
using namespace QKeychain;
|
using namespace QKeychain;
|
||||||
|
|
||||||
@@ -75,6 +76,7 @@ private:
|
|||||||
QPointer<const WebFlowCredentials> _cred;
|
QPointer<const WebFlowCredentials> _cred;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
|
||||||
static void addSettingsToJob(Account *account, QKeychain::Job *job)
|
static void addSettingsToJob(Account *account, QKeychain::Job *job)
|
||||||
{
|
{
|
||||||
Q_UNUSED(account)
|
Q_UNUSED(account)
|
||||||
@@ -82,6 +84,7 @@ static void addSettingsToJob(Account *account, QKeychain::Job *job)
|
|||||||
settings->setParent(job); // make the job parent to make setting deleted properly
|
settings->setParent(job); // make the job parent to make setting deleted properly
|
||||||
job->setSettings(settings.release());
|
job->setSettings(settings.release());
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
WebFlowCredentials::WebFlowCredentials()
|
WebFlowCredentials::WebFlowCredentials()
|
||||||
: _ready(false)
|
: _ready(false)
|
||||||
@@ -147,25 +150,32 @@ void WebFlowCredentials::fetchFromKeychain() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WebFlowCredentials::askFromUser() {
|
void WebFlowCredentials::askFromUser() {
|
||||||
// LoginFlowV2 > WebViewFlow > OAuth > Shib > Basic
|
// Determine if the old flow has to be used (GS for now)
|
||||||
bool useFlow2 = (_account->serverVersionInt() >= Account::makeServerVersion(16, 0, 0));
|
// Do a DetermineAuthTypeJob to make sure that the server is still using Flow2
|
||||||
|
DetermineAuthTypeJob *job = new DetermineAuthTypeJob(_account->sharedFromThis(), this);
|
||||||
|
connect(job, &DetermineAuthTypeJob::authType, [this](DetermineAuthTypeJob::AuthType type) {
|
||||||
|
// LoginFlowV2 > WebViewFlow > OAuth > Shib > Basic
|
||||||
|
bool useFlow2 = (type != DetermineAuthTypeJob::WebViewFlow);
|
||||||
|
|
||||||
_askDialog = new WebFlowCredentialsDialog(_account, useFlow2);
|
_askDialog = new WebFlowCredentialsDialog(_account, useFlow2);
|
||||||
|
|
||||||
if (!useFlow2) {
|
if (!useFlow2) {
|
||||||
QUrl url = _account->url();
|
QUrl url = _account->url();
|
||||||
QString path = url.path() + "/index.php/login/flow";
|
QString path = url.path() + "/index.php/login/flow";
|
||||||
url.setPath(path);
|
url.setPath(path);
|
||||||
_askDialog->setUrl(url);
|
_askDialog->setUrl(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString msg = tr("You have been logged out of %1 as user %2. Please login again")
|
QString msg = tr("You have been logged out of %1 as user %2. Please login again")
|
||||||
.arg(_account->displayName(), _user);
|
.arg(_account->displayName(), _user);
|
||||||
_askDialog->setInfo(msg);
|
_askDialog->setInfo(msg);
|
||||||
|
|
||||||
_askDialog->show();
|
_askDialog->show();
|
||||||
|
|
||||||
connect(_askDialog, &WebFlowCredentialsDialog::urlCatched, this, &WebFlowCredentials::slotAskFromUserCredentialsProvided);
|
connect(_askDialog, &WebFlowCredentialsDialog::urlCatched, this, &WebFlowCredentials::slotAskFromUserCredentialsProvided);
|
||||||
|
connect(_askDialog, &WebFlowCredentialsDialog::onClose, this, &WebFlowCredentials::slotAskFromUserCancelled);
|
||||||
|
});
|
||||||
|
job->start();
|
||||||
|
|
||||||
qCDebug(lcWebFlowCredentials()) << "User needs to reauth!";
|
qCDebug(lcWebFlowCredentials()) << "User needs to reauth!";
|
||||||
}
|
}
|
||||||
@@ -173,7 +183,11 @@ void WebFlowCredentials::askFromUser() {
|
|||||||
void WebFlowCredentials::slotAskFromUserCredentialsProvided(const QString &user, const QString &pass, const QString &host) {
|
void WebFlowCredentials::slotAskFromUserCredentialsProvided(const QString &user, const QString &pass, const QString &host) {
|
||||||
Q_UNUSED(host)
|
Q_UNUSED(host)
|
||||||
|
|
||||||
if (_user != user) {
|
// Compare the re-entered username case-insensitive and save the new value (avoid breaking the account)
|
||||||
|
// See issue: https://github.com/nextcloud/desktop/issues/1741
|
||||||
|
if (QString::compare(_user, user, Qt::CaseInsensitive) == 0) {
|
||||||
|
_user = user;
|
||||||
|
} else {
|
||||||
qCInfo(lcWebFlowCredentials()) << "Authed with the wrong user!";
|
qCInfo(lcWebFlowCredentials()) << "Authed with the wrong user!";
|
||||||
|
|
||||||
QString msg = tr("Please login with the user: %1")
|
QString msg = tr("Please login with the user: %1")
|
||||||
@@ -199,10 +213,18 @@ void WebFlowCredentials::slotAskFromUserCredentialsProvided(const QString &user,
|
|||||||
emit asked();
|
emit asked();
|
||||||
|
|
||||||
_askDialog->close();
|
_askDialog->close();
|
||||||
delete _askDialog;
|
_askDialog->deleteLater();
|
||||||
_askDialog = nullptr;
|
_askDialog = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WebFlowCredentials::slotAskFromUserCancelled() {
|
||||||
|
qCDebug(lcWebFlowCredentials()) << "User cancelled reauth!";
|
||||||
|
|
||||||
|
emit asked();
|
||||||
|
|
||||||
|
_askDialog->deleteLater();
|
||||||
|
_askDialog = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
bool WebFlowCredentials::stillValid(QNetworkReply *reply) {
|
bool WebFlowCredentials::stillValid(QNetworkReply *reply) {
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
@@ -223,33 +245,32 @@ void WebFlowCredentials::persist() {
|
|||||||
|
|
||||||
// write cert if there is one
|
// write cert if there is one
|
||||||
if (!_clientSslCertificate.isNull()) {
|
if (!_clientSslCertificate.isNull()) {
|
||||||
WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
|
auto *job = new KeychainChunk::WriteJob(_account,
|
||||||
addSettingsToJob(_account, job);
|
_user + clientCertificatePEMC,
|
||||||
job->setInsecureFallback(false);
|
_clientSslCertificate.toPem());
|
||||||
connect(job, &Job::finished, this, &WebFlowCredentials::slotWriteClientCertPEMJobDone);
|
connect(job, &KeychainChunk::WriteJob::finished, this, &WebFlowCredentials::slotWriteClientCertPEMJobDone);
|
||||||
job->setKey(keychainKey(_account->url().toString(), _user + clientCertificatePEMC, _account->id()));
|
|
||||||
job->setBinaryData(_clientSslCertificate.toPem());
|
|
||||||
job->start();
|
job->start();
|
||||||
} else {
|
} else {
|
||||||
// no cert, just write credentials
|
// no cert, just write credentials
|
||||||
slotWriteClientCertPEMJobDone();
|
slotWriteClientCertPEMJobDone(nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebFlowCredentials::slotWriteClientCertPEMJobDone()
|
void WebFlowCredentials::slotWriteClientCertPEMJobDone(KeychainChunk::WriteJob *writeJob)
|
||||||
{
|
{
|
||||||
|
if(writeJob)
|
||||||
|
writeJob->deleteLater();
|
||||||
|
|
||||||
// write ssl key if there is one
|
// write ssl key if there is one
|
||||||
if (!_clientSslKey.isNull()) {
|
if (!_clientSslKey.isNull()) {
|
||||||
WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
|
auto *job = new KeychainChunk::WriteJob(_account,
|
||||||
addSettingsToJob(_account, job);
|
_user + clientKeyPEMC,
|
||||||
job->setInsecureFallback(false);
|
_clientSslKey.toPem());
|
||||||
connect(job, &Job::finished, this, &WebFlowCredentials::slotWriteClientKeyPEMJobDone);
|
connect(job, &KeychainChunk::WriteJob::finished, this, &WebFlowCredentials::slotWriteClientKeyPEMJobDone);
|
||||||
job->setKey(keychainKey(_account->url().toString(), _user + clientKeyPEMC, _account->id()));
|
|
||||||
job->setBinaryData(_clientSslKey.toPem());
|
|
||||||
job->start();
|
job->start();
|
||||||
} else {
|
} else {
|
||||||
// no key, just write credentials
|
// no key, just write credentials
|
||||||
slotWriteClientKeyPEMJobDone();
|
slotWriteClientKeyPEMJobDone(nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,7 +285,7 @@ void WebFlowCredentials::writeSingleClientCaCertPEM()
|
|||||||
|
|
||||||
// keep the limit
|
// keep the limit
|
||||||
if (index > (_clientSslCaCertificatesMaxCount - 1)) {
|
if (index > (_clientSslCaCertificatesMaxCount - 1)) {
|
||||||
qCWarning(lcWebFlowCredentials) << "Maximum client CA cert count exceeded while writing slot" << QString::number(index) << "), cutting off after " << QString::number(_clientSslCaCertificatesMaxCount) << "certs";
|
qCWarning(lcWebFlowCredentials) << "Maximum client CA cert count exceeded while writing slot" << QString::number(index) << "cutting off after" << QString::number(_clientSslCaCertificatesMaxCount) << "certs";
|
||||||
|
|
||||||
_clientSslCaCertificatesWriteQueue.clear();
|
_clientSslCaCertificatesWriteQueue.clear();
|
||||||
|
|
||||||
@@ -272,20 +293,21 @@ void WebFlowCredentials::writeSingleClientCaCertPEM()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
|
auto *job = new KeychainChunk::WriteJob(_account,
|
||||||
addSettingsToJob(_account, job);
|
_user + clientCaCertificatePEMC + QString::number(index),
|
||||||
job->setInsecureFallback(false);
|
cert.toPem());
|
||||||
connect(job, &Job::finished, this, &WebFlowCredentials::slotWriteClientCaCertsPEMJobDone);
|
connect(job, &KeychainChunk::WriteJob::finished, this, &WebFlowCredentials::slotWriteClientCaCertsPEMJobDone);
|
||||||
job->setKey(keychainKey(_account->url().toString(), _user + clientCaCertificatePEMC + QString::number(index), _account->id()));
|
|
||||||
job->setBinaryData(cert.toPem());
|
|
||||||
job->start();
|
job->start();
|
||||||
} else {
|
} else {
|
||||||
slotWriteClientCaCertsPEMJobDone(nullptr);
|
slotWriteClientCaCertsPEMJobDone(nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebFlowCredentials::slotWriteClientKeyPEMJobDone()
|
void WebFlowCredentials::slotWriteClientKeyPEMJobDone(KeychainChunk::WriteJob *writeJob)
|
||||||
{
|
{
|
||||||
|
if(writeJob)
|
||||||
|
writeJob->deleteLater();
|
||||||
|
|
||||||
_clientSslCaCertificatesWriteQueue.clear();
|
_clientSslCaCertificatesWriteQueue.clear();
|
||||||
|
|
||||||
// write ca certs if there are any
|
// write ca certs if there are any
|
||||||
@@ -300,16 +322,16 @@ void WebFlowCredentials::slotWriteClientKeyPEMJobDone()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebFlowCredentials::slotWriteClientCaCertsPEMJobDone(QKeychain::Job *incomingJob)
|
void WebFlowCredentials::slotWriteClientCaCertsPEMJobDone(KeychainChunk::WriteJob *writeJob)
|
||||||
{
|
{
|
||||||
// errors / next ca cert?
|
// errors / next ca cert?
|
||||||
if (incomingJob && !_clientSslCaCertificates.isEmpty()) {
|
if (writeJob && !_clientSslCaCertificates.isEmpty()) {
|
||||||
WritePasswordJob *writeJob = static_cast<WritePasswordJob *>(incomingJob);
|
|
||||||
|
|
||||||
if (writeJob->error() != NoError) {
|
if (writeJob->error() != NoError) {
|
||||||
qCWarning(lcWebFlowCredentials) << "Error while writing client CA cert" << writeJob->errorString();
|
qCWarning(lcWebFlowCredentials) << "Error while writing client CA cert" << writeJob->errorString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
writeJob->deleteLater();
|
||||||
|
|
||||||
if (!_clientSslCaCertificatesWriteQueue.isEmpty()) {
|
if (!_clientSslCaCertificatesWriteQueue.isEmpty()) {
|
||||||
// next ca cert
|
// next ca cert
|
||||||
writeSingleClientCaCertPEM();
|
writeSingleClientCaCertPEM();
|
||||||
@@ -319,7 +341,9 @@ void WebFlowCredentials::slotWriteClientCaCertsPEMJobDone(QKeychain::Job *incomi
|
|||||||
|
|
||||||
// done storing ca certs, time for the password
|
// done storing ca certs, time for the password
|
||||||
WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
|
WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
|
||||||
|
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
|
||||||
addSettingsToJob(_account, job);
|
addSettingsToJob(_account, job);
|
||||||
|
#endif
|
||||||
job->setInsecureFallback(false);
|
job->setInsecureFallback(false);
|
||||||
connect(job, &Job::finished, this, &WebFlowCredentials::slotWriteJobDone);
|
connect(job, &Job::finished, this, &WebFlowCredentials::slotWriteJobDone);
|
||||||
job->setKey(keychainKey(_account->url().toString(), _user, _account->id()));
|
job->setKey(keychainKey(_account->url().toString(), _user, _account->id()));
|
||||||
@@ -369,18 +393,15 @@ void WebFlowCredentials::forgetSensitiveData() {
|
|||||||
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
|
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
|
||||||
job->setInsecureFallback(false);
|
job->setInsecureFallback(false);
|
||||||
job->setKey(kck);
|
job->setKey(kck);
|
||||||
|
connect(job, &Job::finished, this, [](QKeychain::Job *job) {
|
||||||
|
DeletePasswordJob *djob = qobject_cast<DeletePasswordJob *>(job);
|
||||||
|
djob->deleteLater();
|
||||||
|
});
|
||||||
job->start();
|
job->start();
|
||||||
|
|
||||||
invalidateToken();
|
invalidateToken();
|
||||||
|
|
||||||
/* IMPORTANT
|
deleteKeychainEntries();
|
||||||
* TODO: For "Log out" & "Remove account": Remove client CA certs and KEY!
|
|
||||||
*
|
|
||||||
* Disabled as long as selecting another cert is not supported by the UI.
|
|
||||||
*
|
|
||||||
* Being able to specify a new certificate is important anyway: expiry etc.
|
|
||||||
*/
|
|
||||||
//deleteKeychainEntries();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebFlowCredentials::setAccount(Account *account) {
|
void WebFlowCredentials::setAccount(Account *account) {
|
||||||
@@ -420,35 +441,29 @@ void WebFlowCredentials::slotFinished(QNetworkReply *reply) {
|
|||||||
_credentialsValid = true;
|
_credentialsValid = true;
|
||||||
|
|
||||||
/// Used later for remote wipe
|
/// Used later for remote wipe
|
||||||
_account->setAppPassword(_password);
|
_account->writeAppPasswordOnce(_password);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebFlowCredentials::fetchFromKeychainHelper() {
|
void WebFlowCredentials::fetchFromKeychainHelper() {
|
||||||
// Read client cert from keychain
|
// Read client cert from keychain
|
||||||
const QString kck = keychainKey(
|
auto *job = new KeychainChunk::ReadJob(_account,
|
||||||
_account->url().toString(),
|
_user + clientCertificatePEMC,
|
||||||
_user + clientCertificatePEMC,
|
_keychainMigration);
|
||||||
_keychainMigration ? QString() : _account->id());
|
connect(job, &KeychainChunk::ReadJob::finished, this, &WebFlowCredentials::slotReadClientCertPEMJobDone);
|
||||||
|
|
||||||
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
|
|
||||||
addSettingsToJob(_account, job);
|
|
||||||
job->setInsecureFallback(false);
|
|
||||||
job->setKey(kck);
|
|
||||||
connect(job, &Job::finished, this, &WebFlowCredentials::slotReadClientCertPEMJobDone);
|
|
||||||
job->start();
|
job->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebFlowCredentials::slotReadClientCertPEMJobDone(QKeychain::Job *incomingJob)
|
void WebFlowCredentials::slotReadClientCertPEMJobDone(KeychainChunk::ReadJob *readJob)
|
||||||
{
|
{
|
||||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
|
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
|
||||||
Q_ASSERT(!incomingJob->insecureFallback()); // If insecureFallback is set, the next test would be pointless
|
Q_ASSERT(!readJob->insecureFallback()); // If insecureFallback is set, the next test would be pointless
|
||||||
if (_retryOnKeyChainError && (incomingJob->error() == QKeychain::NoBackendAvailable
|
if (_retryOnKeyChainError && (readJob->error() == QKeychain::NoBackendAvailable
|
||||||
|| incomingJob->error() == QKeychain::OtherError)) {
|
|| readJob->error() == QKeychain::OtherError)) {
|
||||||
// Could be that the backend was not yet available. Wait some extra seconds.
|
// Could be that the backend was not yet available. Wait some extra seconds.
|
||||||
// (Issues #4274 and #6522)
|
// (Issues #4274 and #6522)
|
||||||
// (For kwallet, the error is OtherError instead of NoBackendAvailable, maybe a bug in QtKeychain)
|
// (For kwallet, the error is OtherError instead of NoBackendAvailable, maybe a bug in QtKeychain)
|
||||||
qCInfo(lcWebFlowCredentials) << "Backend unavailable (yet?) Retrying in a few seconds." << incomingJob->errorString();
|
qCInfo(lcWebFlowCredentials) << "Backend unavailable (yet?) Retrying in a few seconds." << readJob->errorString();
|
||||||
QTimer::singleShot(10000, this, &WebFlowCredentials::fetchFromKeychainHelper);
|
QTimer::singleShot(10000, this, &WebFlowCredentials::fetchFromKeychainHelper);
|
||||||
_retryOnKeyChainError = false;
|
_retryOnKeyChainError = false;
|
||||||
return;
|
return;
|
||||||
@@ -457,7 +472,6 @@ void WebFlowCredentials::slotReadClientCertPEMJobDone(QKeychain::Job *incomingJo
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Store PEM in memory
|
// Store PEM in memory
|
||||||
ReadPasswordJob *readJob = static_cast<ReadPasswordJob *>(incomingJob);
|
|
||||||
if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
|
if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
|
||||||
QList<QSslCertificate> sslCertificateList = QSslCertificate::fromData(readJob->binaryData(), QSsl::Pem);
|
QList<QSslCertificate> sslCertificateList = QSslCertificate::fromData(readJob->binaryData(), QSsl::Pem);
|
||||||
if (sslCertificateList.length() >= 1) {
|
if (sslCertificateList.length() >= 1) {
|
||||||
@@ -465,25 +479,19 @@ void WebFlowCredentials::slotReadClientCertPEMJobDone(QKeychain::Job *incomingJo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load key too
|
readJob->deleteLater();
|
||||||
const QString kck = keychainKey(
|
|
||||||
_account->url().toString(),
|
|
||||||
_user + clientKeyPEMC,
|
|
||||||
_keychainMigration ? QString() : _account->id());
|
|
||||||
|
|
||||||
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
|
// Load key too
|
||||||
addSettingsToJob(_account, job);
|
auto *job = new KeychainChunk::ReadJob(_account,
|
||||||
job->setInsecureFallback(false);
|
_user + clientKeyPEMC,
|
||||||
job->setKey(kck);
|
_keychainMigration);
|
||||||
connect(job, &Job::finished, this, &WebFlowCredentials::slotReadClientKeyPEMJobDone);
|
connect(job, &KeychainChunk::ReadJob::finished, this, &WebFlowCredentials::slotReadClientKeyPEMJobDone);
|
||||||
job->start();
|
job->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebFlowCredentials::slotReadClientKeyPEMJobDone(QKeychain::Job *incomingJob)
|
void WebFlowCredentials::slotReadClientKeyPEMJobDone(KeychainChunk::ReadJob *readJob)
|
||||||
{
|
{
|
||||||
// Store key in memory
|
// Store key in memory
|
||||||
ReadPasswordJob *readJob = static_cast<ReadPasswordJob *>(incomingJob);
|
|
||||||
|
|
||||||
if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
|
if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
|
||||||
QByteArray clientKeyPEM = readJob->binaryData();
|
QByteArray clientKeyPEM = readJob->binaryData();
|
||||||
// FIXME Unfortunately Qt has a bug and we can't just use QSsl::Opaque to let it
|
// FIXME Unfortunately Qt has a bug and we can't just use QSsl::Opaque to let it
|
||||||
@@ -498,8 +506,13 @@ void WebFlowCredentials::slotReadClientKeyPEMJobDone(QKeychain::Job *incomingJob
|
|||||||
if (_clientSslKey.isNull()) {
|
if (_clientSslKey.isNull()) {
|
||||||
qCWarning(lcWebFlowCredentials) << "Could not load SSL key into Qt!";
|
qCWarning(lcWebFlowCredentials) << "Could not load SSL key into Qt!";
|
||||||
}
|
}
|
||||||
|
clientKeyPEM.clear();
|
||||||
|
} else {
|
||||||
|
qCWarning(lcWebFlowCredentials) << "Unable to read client key" << readJob->errorString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readJob->deleteLater();
|
||||||
|
|
||||||
// Start fetching client CA certs
|
// Start fetching client CA certs
|
||||||
_clientSslCaCertificates.clear();
|
_clientSslCaCertificates.clear();
|
||||||
|
|
||||||
@@ -510,28 +523,20 @@ void WebFlowCredentials::readSingleClientCaCertPEM()
|
|||||||
{
|
{
|
||||||
// try to fetch a client ca cert
|
// try to fetch a client ca cert
|
||||||
if (_clientSslCaCertificates.count() < _clientSslCaCertificatesMaxCount) {
|
if (_clientSslCaCertificates.count() < _clientSslCaCertificatesMaxCount) {
|
||||||
const QString kck = keychainKey(
|
auto *job = new KeychainChunk::ReadJob(_account,
|
||||||
_account->url().toString(),
|
_user + clientCaCertificatePEMC + QString::number(_clientSslCaCertificates.count()),
|
||||||
_user + clientCaCertificatePEMC + QString::number(_clientSslCaCertificates.count()),
|
_keychainMigration);
|
||||||
_keychainMigration ? QString() : _account->id());
|
connect(job, &KeychainChunk::ReadJob::finished, this, &WebFlowCredentials::slotReadClientCaCertsPEMJobDone);
|
||||||
|
|
||||||
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
|
|
||||||
addSettingsToJob(_account, job);
|
|
||||||
job->setInsecureFallback(false);
|
|
||||||
job->setKey(kck);
|
|
||||||
connect(job, &Job::finished, this, &WebFlowCredentials::slotReadClientCaCertsPEMJobDone);
|
|
||||||
job->start();
|
job->start();
|
||||||
} else {
|
} else {
|
||||||
qCWarning(lcWebFlowCredentials) << "Maximum client CA cert count exceeded while reading, ignoring after " << _clientSslCaCertificatesMaxCount;
|
qCWarning(lcWebFlowCredentials) << "Maximum client CA cert count exceeded while reading, ignoring after" << _clientSslCaCertificatesMaxCount;
|
||||||
|
|
||||||
slotReadClientCaCertsPEMJobDone(nullptr);
|
slotReadClientCaCertsPEMJobDone(nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebFlowCredentials::slotReadClientCaCertsPEMJobDone(QKeychain::Job *incomingJob) {
|
void WebFlowCredentials::slotReadClientCaCertsPEMJobDone(KeychainChunk::ReadJob *readJob) {
|
||||||
// Store key in memory
|
// Store cert in memory
|
||||||
ReadPasswordJob *readJob = static_cast<ReadPasswordJob *>(incomingJob);
|
|
||||||
|
|
||||||
if (readJob) {
|
if (readJob) {
|
||||||
if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
|
if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
|
||||||
QList<QSslCertificate> sslCertificateList = QSslCertificate::fromData(readJob->binaryData(), QSsl::Pem);
|
QList<QSslCertificate> sslCertificateList = QSslCertificate::fromData(readJob->binaryData(), QSsl::Pem);
|
||||||
@@ -539,15 +544,19 @@ void WebFlowCredentials::slotReadClientCaCertsPEMJobDone(QKeychain::Job *incomin
|
|||||||
_clientSslCaCertificates.append(sslCertificateList.at(0));
|
_clientSslCaCertificates.append(sslCertificateList.at(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readJob->deleteLater();
|
||||||
|
|
||||||
// try next cert
|
// try next cert
|
||||||
readSingleClientCaCertPEM();
|
readSingleClientCaCertPEM();
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
if (readJob->error() != QKeychain::Error::EntryNotFound ||
|
if (readJob->error() != QKeychain::Error::EntryNotFound ||
|
||||||
((readJob->error() == QKeychain::Error::EntryNotFound) && _clientSslCaCertificates.count() == 0)) {
|
((readJob->error() == QKeychain::Error::EntryNotFound) && _clientSslCaCertificates.count() == 0)) {
|
||||||
qCWarning(lcWebFlowCredentials) << "Unable to read client CA cert slot " << QString::number(_clientSslCaCertificates.count()) << readJob->errorString();
|
qCWarning(lcWebFlowCredentials) << "Unable to read client CA cert slot" << QString::number(_clientSslCaCertificates.count()) << readJob->errorString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readJob->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now fetch the actual server password
|
// Now fetch the actual server password
|
||||||
@@ -557,7 +566,9 @@ void WebFlowCredentials::slotReadClientCaCertsPEMJobDone(QKeychain::Job *incomin
|
|||||||
_keychainMigration ? QString() : _account->id());
|
_keychainMigration ? QString() : _account->id());
|
||||||
|
|
||||||
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
|
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
|
||||||
|
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
|
||||||
addSettingsToJob(_account, job);
|
addSettingsToJob(_account, job);
|
||||||
|
#endif
|
||||||
job->setInsecureFallback(false);
|
job->setInsecureFallback(false);
|
||||||
job->setKey(kck);
|
job->setKey(kck);
|
||||||
connect(job, &Job::finished, this, &WebFlowCredentials::slotReadPasswordJobDone);
|
connect(job, &Job::finished, this, &WebFlowCredentials::slotReadPasswordJobDone);
|
||||||
@@ -565,7 +576,7 @@ void WebFlowCredentials::slotReadClientCaCertsPEMJobDone(QKeychain::Job *incomin
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WebFlowCredentials::slotReadPasswordJobDone(Job *incomingJob) {
|
void WebFlowCredentials::slotReadPasswordJobDone(Job *incomingJob) {
|
||||||
QKeychain::ReadPasswordJob *job = static_cast<ReadPasswordJob *>(incomingJob);
|
QKeychain::ReadPasswordJob *job = qobject_cast<ReadPasswordJob *>(incomingJob);
|
||||||
QKeychain::Error error = job->error();
|
QKeychain::Error error = job->error();
|
||||||
|
|
||||||
// If we could not find the entry try the old entries
|
// If we could not find the entry try the old entries
|
||||||
@@ -588,6 +599,8 @@ void WebFlowCredentials::slotReadPasswordJobDone(Job *incomingJob) {
|
|||||||
}
|
}
|
||||||
emit fetched();
|
emit fetched();
|
||||||
|
|
||||||
|
job->deleteLater();
|
||||||
|
|
||||||
// If keychain data was read from legacy location, wipe these entries and store new ones
|
// If keychain data was read from legacy location, wipe these entries and store new ones
|
||||||
if (_keychainMigration && _ready) {
|
if (_keychainMigration && _ready) {
|
||||||
_keychainMigration = false;
|
_keychainMigration = false;
|
||||||
@@ -598,23 +611,60 @@ void WebFlowCredentials::slotReadPasswordJobDone(Job *incomingJob) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WebFlowCredentials::deleteKeychainEntries(bool oldKeychainEntries) {
|
void WebFlowCredentials::deleteKeychainEntries(bool oldKeychainEntries) {
|
||||||
auto startDeleteJob = [this, oldKeychainEntries](QString user) {
|
auto startDeleteJob = [this, oldKeychainEntries](QString key) {
|
||||||
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
|
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
|
||||||
|
#if defined(KEYCHAINCHUNK_ENABLE_INSECURE_FALLBACK)
|
||||||
addSettingsToJob(_account, job);
|
addSettingsToJob(_account, job);
|
||||||
job->setInsecureFallback(true);
|
#endif
|
||||||
|
job->setInsecureFallback(false);
|
||||||
job->setKey(keychainKey(_account->url().toString(),
|
job->setKey(keychainKey(_account->url().toString(),
|
||||||
user,
|
key,
|
||||||
oldKeychainEntries ? QString() : _account->id()));
|
oldKeychainEntries ? QString() : _account->id()));
|
||||||
|
|
||||||
|
connect(job, &Job::finished, this, [](QKeychain::Job *job) {
|
||||||
|
DeletePasswordJob *djob = qobject_cast<DeletePasswordJob *>(job);
|
||||||
|
djob->deleteLater();
|
||||||
|
});
|
||||||
job->start();
|
job->start();
|
||||||
};
|
};
|
||||||
|
|
||||||
startDeleteJob(_user);
|
startDeleteJob(_user);
|
||||||
startDeleteJob(_user + clientKeyPEMC);
|
|
||||||
startDeleteJob(_user + clientCertificatePEMC);
|
|
||||||
|
|
||||||
for (auto i = 0; i < _clientSslCaCertificates.count(); i++) {
|
/* IMPORTANT - remove later - FIXME MS@2019-12-07 -->
|
||||||
startDeleteJob(_user + clientCaCertificatePEMC + QString::number(i));
|
* TODO: For "Log out" & "Remove account": Remove client CA certs and KEY!
|
||||||
|
*
|
||||||
|
* Disabled as long as selecting another cert is not supported by the UI.
|
||||||
|
*
|
||||||
|
* Being able to specify a new certificate is important anyway: expiry etc.
|
||||||
|
*
|
||||||
|
* We introduce this dirty hack here, to allow deleting them upon Remote Wipe.
|
||||||
|
*/
|
||||||
|
if(_account->isRemoteWipeRequested_HACK()) {
|
||||||
|
// <-- FIXME MS@2019-12-07
|
||||||
|
startDeleteJob(_user + clientKeyPEMC);
|
||||||
|
startDeleteJob(_user + clientCertificatePEMC);
|
||||||
|
|
||||||
|
for (auto i = 0; i < _clientSslCaCertificates.count(); i++) {
|
||||||
|
startDeleteJob(_user + clientCaCertificatePEMC + QString::number(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
// Also delete key / cert sub-chunks (Windows workaround)
|
||||||
|
// The first chunk (0) has no suffix, to stay compatible with older versions and non-Windows
|
||||||
|
for (auto chunk = 1; chunk < KeychainChunk::MaxChunks; chunk++) {
|
||||||
|
const QString strChunkSuffix = QString(".") + QString::number(chunk);
|
||||||
|
|
||||||
|
startDeleteJob(_user + clientKeyPEMC + strChunkSuffix);
|
||||||
|
startDeleteJob(_user + clientCertificatePEMC + strChunkSuffix);
|
||||||
|
|
||||||
|
for (auto i = 0; i < _clientSslCaCertificates.count(); i++) {
|
||||||
|
startDeleteJob(_user + clientCaCertificatePEMC + QString::number(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// FIXME MS@2019-12-07 -->
|
||||||
}
|
}
|
||||||
|
// <-- FIXME MS@2019-12-07
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // namespace OCC
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ namespace QKeychain {
|
|||||||
|
|
||||||
namespace OCC {
|
namespace OCC {
|
||||||
|
|
||||||
|
namespace KeychainChunk {
|
||||||
|
class ReadJob;
|
||||||
|
class WriteJob;
|
||||||
|
}
|
||||||
|
|
||||||
class WebFlowCredentialsDialog;
|
class WebFlowCredentialsDialog;
|
||||||
|
|
||||||
class WebFlowCredentials : public AbstractCredentials
|
class WebFlowCredentials : public AbstractCredentials
|
||||||
@@ -61,15 +66,16 @@ private slots:
|
|||||||
void slotFinished(QNetworkReply *reply);
|
void slotFinished(QNetworkReply *reply);
|
||||||
|
|
||||||
void slotAskFromUserCredentialsProvided(const QString &user, const QString &pass, const QString &host);
|
void slotAskFromUserCredentialsProvided(const QString &user, const QString &pass, const QString &host);
|
||||||
|
void slotAskFromUserCancelled();
|
||||||
|
|
||||||
void slotReadClientCertPEMJobDone(QKeychain::Job *incomingJob);
|
void slotReadClientCertPEMJobDone(KeychainChunk::ReadJob *readJob);
|
||||||
void slotReadClientKeyPEMJobDone(QKeychain::Job *incomingJob);
|
void slotReadClientKeyPEMJobDone(KeychainChunk::ReadJob *readJob);
|
||||||
void slotReadClientCaCertsPEMJobDone(QKeychain::Job *incommingJob);
|
void slotReadClientCaCertsPEMJobDone(KeychainChunk::ReadJob *readJob);
|
||||||
void slotReadPasswordJobDone(QKeychain::Job *incomingJob);
|
void slotReadPasswordJobDone(QKeychain::Job *incomingJob);
|
||||||
|
|
||||||
void slotWriteClientCertPEMJobDone();
|
void slotWriteClientCertPEMJobDone(KeychainChunk::WriteJob *writeJob);
|
||||||
void slotWriteClientKeyPEMJobDone();
|
void slotWriteClientKeyPEMJobDone(KeychainChunk::WriteJob *writeJob);
|
||||||
void slotWriteClientCaCertsPEMJobDone(QKeychain::Job *incomingJob);
|
void slotWriteClientCaCertsPEMJobDone(KeychainChunk::WriteJob *writeJob);
|
||||||
void slotWriteJobDone(QKeychain::Job *);
|
void slotWriteJobDone(QKeychain::Job *);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -83,7 +89,7 @@ private:
|
|||||||
void writeSingleClientCaCertPEM();
|
void writeSingleClientCaCertPEM();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since we're limited by Windows limits we just create our own
|
* Since we're limited by Windows limits, we just create our own
|
||||||
* limit to avoid evil things happening by endless recursion
|
* limit to avoid evil things happening by endless recursion
|
||||||
*
|
*
|
||||||
* Better than storing the count and relying on maybe-hacked values
|
* Better than storing the count and relying on maybe-hacked values
|
||||||
@@ -121,6 +127,6 @@ protected:
|
|||||||
WebFlowCredentialsDialog *_askDialog;
|
WebFlowCredentialsDialog *_askDialog;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
} // namespace OCC
|
||||||
|
|
||||||
#endif // WEBFLOWCREDENTIALS_H
|
#endif // WEBFLOWCREDENTIALS_H
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
|
||||||
#include "theme.h"
|
#include "theme.h"
|
||||||
|
#include "application.h"
|
||||||
|
#include "owncloudgui.h"
|
||||||
|
#include "headerbanner.h"
|
||||||
#include "wizard/owncloudwizardcommon.h"
|
#include "wizard/owncloudwizardcommon.h"
|
||||||
#include "wizard/webview.h"
|
#include "wizard/webview.h"
|
||||||
#include "wizard/flow2authwidget.h"
|
#include "wizard/flow2authwidget.h"
|
||||||
@@ -19,31 +22,59 @@ WebFlowCredentialsDialog::WebFlowCredentialsDialog(Account *account, bool useFlo
|
|||||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||||
|
|
||||||
_layout = new QVBoxLayout(this);
|
_layout = new QVBoxLayout(this);
|
||||||
|
int spacing = _layout->spacing();
|
||||||
|
int margin = _layout->margin();
|
||||||
|
_layout->setSpacing(0);
|
||||||
|
_layout->setMargin(0);
|
||||||
|
|
||||||
|
if(_useFlow2) {
|
||||||
|
_headerBanner = new HeaderBanner(this);
|
||||||
|
_layout->addWidget(_headerBanner);
|
||||||
|
Theme *theme = Theme::instance();
|
||||||
|
_headerBanner->setup(tr("Log in"), theme->wizardHeaderLogo(), theme->wizardHeaderBanner(),
|
||||||
|
Qt::AutoText, QString::fromLatin1("color:#fff;"));
|
||||||
|
}
|
||||||
|
|
||||||
|
_containerLayout = new QVBoxLayout(this);
|
||||||
|
_containerLayout->setSpacing(spacing);
|
||||||
|
_containerLayout->setMargin(margin);
|
||||||
|
|
||||||
//QString msg = tr("You have been logged out of %1 as user %2, please login again")
|
|
||||||
// .arg(_account->displayName(), _user);
|
|
||||||
_infoLabel = new QLabel();
|
_infoLabel = new QLabel();
|
||||||
_layout->addWidget(_infoLabel);
|
_containerLayout->addWidget(_infoLabel);
|
||||||
|
|
||||||
if (_useFlow2) {
|
if (_useFlow2) {
|
||||||
_flow2AuthWidget = new Flow2AuthWidget(account);
|
_flow2AuthWidget = new Flow2AuthWidget();
|
||||||
_layout->addWidget(_flow2AuthWidget);
|
_containerLayout->addWidget(_flow2AuthWidget);
|
||||||
|
|
||||||
connect(_flow2AuthWidget, &Flow2AuthWidget::urlCatched, this, &WebFlowCredentialsDialog::urlCatched);
|
connect(_flow2AuthWidget, &Flow2AuthWidget::authResult, this, &WebFlowCredentialsDialog::slotFlow2AuthResult);
|
||||||
|
|
||||||
|
// Connect styleChanged events to our widgets, so they can adapt (Dark-/Light-Mode switching)
|
||||||
|
connect(this, &WebFlowCredentialsDialog::styleChanged, _flow2AuthWidget, &Flow2AuthWidget::slotStyleChanged);
|
||||||
|
|
||||||
|
// allow Flow2 page to poll on window activation
|
||||||
|
connect(this, &WebFlowCredentialsDialog::onActivate, _flow2AuthWidget, &Flow2AuthWidget::slotPollNow);
|
||||||
|
|
||||||
|
_flow2AuthWidget->startAuth(account);
|
||||||
} else {
|
} else {
|
||||||
_webView = new WebView();
|
_webView = new WebView();
|
||||||
_layout->addWidget(_webView);
|
_containerLayout->addWidget(_webView);
|
||||||
|
|
||||||
connect(_webView, &WebView::urlCatched, this, &WebFlowCredentialsDialog::urlCatched);
|
connect(_webView, &WebView::urlCatched, this, &WebFlowCredentialsDialog::urlCatched);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto app = static_cast<Application *>(qApp);
|
||||||
|
connect(app, &Application::isShowingSettingsDialog, this, &WebFlowCredentialsDialog::slotShowSettingsDialog);
|
||||||
|
|
||||||
_errorLabel = new QLabel();
|
_errorLabel = new QLabel();
|
||||||
_errorLabel->hide();
|
_errorLabel->hide();
|
||||||
_layout->addWidget(_errorLabel);
|
_containerLayout->addWidget(_errorLabel);
|
||||||
|
|
||||||
WizardCommon::initErrorLabel(_errorLabel);
|
WizardCommon::initErrorLabel(_errorLabel);
|
||||||
|
|
||||||
|
_layout->addLayout(_containerLayout);
|
||||||
setLayout(_layout);
|
setLayout(_layout);
|
||||||
|
|
||||||
|
customizeStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebFlowCredentialsDialog::closeEvent(QCloseEvent* e) {
|
void WebFlowCredentialsDialog::closeEvent(QCloseEvent* e) {
|
||||||
@@ -52,11 +83,17 @@ void WebFlowCredentialsDialog::closeEvent(QCloseEvent* e) {
|
|||||||
if (_webView) {
|
if (_webView) {
|
||||||
// Force calling WebView::~WebView() earlier so that _profile and _page are
|
// Force calling WebView::~WebView() earlier so that _profile and _page are
|
||||||
// deleted in the correct order.
|
// deleted in the correct order.
|
||||||
delete _webView;
|
_webView->deleteLater();
|
||||||
|
_webView = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_flow2AuthWidget)
|
if (_flow2AuthWidget) {
|
||||||
delete _flow2AuthWidget;
|
_flow2AuthWidget->resetAuth();
|
||||||
|
_flow2AuthWidget->deleteLater();
|
||||||
|
_flow2AuthWidget = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebFlowCredentialsDialog::setUrl(const QUrl &url) {
|
void WebFlowCredentialsDialog::setUrl(const QUrl &url) {
|
||||||
@@ -69,6 +106,9 @@ void WebFlowCredentialsDialog::setInfo(const QString &msg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WebFlowCredentialsDialog::setError(const QString &error) {
|
void WebFlowCredentialsDialog::setError(const QString &error) {
|
||||||
|
// bring window to top
|
||||||
|
slotShowSettingsDialog();
|
||||||
|
|
||||||
if (_useFlow2 && _flow2AuthWidget) {
|
if (_useFlow2 && _flow2AuthWidget) {
|
||||||
_flow2AuthWidget->setError(error);
|
_flow2AuthWidget->setError(error);
|
||||||
return;
|
return;
|
||||||
@@ -82,4 +122,49 @@ void WebFlowCredentialsDialog::setError(const QString &error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WebFlowCredentialsDialog::changeEvent(QEvent *e)
|
||||||
|
{
|
||||||
|
switch (e->type()) {
|
||||||
|
case QEvent::StyleChange:
|
||||||
|
case QEvent::PaletteChange:
|
||||||
|
case QEvent::ThemeChange:
|
||||||
|
customizeStyle();
|
||||||
|
|
||||||
|
// Notify the other widgets (Dark-/Light-Mode switching)
|
||||||
|
emit styleChanged();
|
||||||
|
break;
|
||||||
|
case QEvent::ActivationChange:
|
||||||
|
if(isActiveWindow())
|
||||||
|
emit onActivate();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDialog::changeEvent(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WebFlowCredentialsDialog::customizeStyle()
|
||||||
|
{
|
||||||
|
// HINT: Customize dialog's own style here, if necessary in the future (Dark-/Light-Mode switching)
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebFlowCredentialsDialog::slotShowSettingsDialog()
|
||||||
|
{
|
||||||
|
// bring window to top but slightly delay, to avoid being hidden behind the SettingsDialog
|
||||||
|
QTimer::singleShot(100, this, [this] {
|
||||||
|
ownCloudGui::raiseDialog(this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebFlowCredentialsDialog::slotFlow2AuthResult(Flow2Auth::Result r, const QString &errorString, const QString &user, const QString &appPassword)
|
||||||
|
{
|
||||||
|
if(r == Flow2Auth::LoggedIn) {
|
||||||
|
emit urlCatched(user, appPassword, QString());
|
||||||
|
} else {
|
||||||
|
// bring window to top
|
||||||
|
slotShowSettingsDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace OCC
|
||||||
|
|||||||
@@ -5,12 +5,14 @@
|
|||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
#include "accountfwd.h"
|
#include "accountfwd.h"
|
||||||
|
#include "creds/flow2auth.h"
|
||||||
|
|
||||||
class QLabel;
|
class QLabel;
|
||||||
class QVBoxLayout;
|
class QVBoxLayout;
|
||||||
|
|
||||||
namespace OCC {
|
namespace OCC {
|
||||||
|
|
||||||
|
class HeaderBanner;
|
||||||
class WebView;
|
class WebView;
|
||||||
class Flow2AuthWidget;
|
class Flow2AuthWidget;
|
||||||
|
|
||||||
@@ -30,11 +32,21 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void closeEvent(QCloseEvent * e) override;
|
void closeEvent(QCloseEvent * e) override;
|
||||||
|
void changeEvent(QEvent *) override;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void slotFlow2AuthResult(Flow2Auth::Result, const QString &errorString, const QString &user, const QString &appPassword);
|
||||||
|
void slotShowSettingsDialog();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void urlCatched(const QString user, const QString pass, const QString host);
|
void urlCatched(const QString user, const QString pass, const QString host);
|
||||||
|
void styleChanged();
|
||||||
|
void onActivate();
|
||||||
|
void onClose();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void customizeStyle();
|
||||||
|
|
||||||
bool _useFlow2;
|
bool _useFlow2;
|
||||||
|
|
||||||
Flow2AuthWidget *_flow2AuthWidget;
|
Flow2AuthWidget *_flow2AuthWidget;
|
||||||
@@ -43,8 +55,10 @@ private:
|
|||||||
QLabel *_errorLabel;
|
QLabel *_errorLabel;
|
||||||
QLabel *_infoLabel;
|
QLabel *_infoLabel;
|
||||||
QVBoxLayout *_layout;
|
QVBoxLayout *_layout;
|
||||||
|
QVBoxLayout *_containerLayout;
|
||||||
|
HeaderBanner *_headerBanner;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
} // namespace OCC
|
||||||
|
|
||||||
#endif // WEBFLOWCREDENTIALSDIALOG_H
|
#endif // WEBFLOWCREDENTIALSDIALOG_H
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ namespace OCC {
|
|||||||
FolderStatusDelegate::FolderStatusDelegate()
|
FolderStatusDelegate::FolderStatusDelegate()
|
||||||
: QStyledItemDelegate()
|
: QStyledItemDelegate()
|
||||||
{
|
{
|
||||||
m_moreIcon = QIcon(QLatin1String(":/client/resources/more.svg"));
|
customizeStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString FolderStatusDelegate::addFolderText()
|
QString FolderStatusDelegate::addFolderText()
|
||||||
@@ -273,6 +273,11 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
|||||||
rect.setHeight(texts.count() * subFm.height() + 2 * margin);
|
rect.setHeight(texts.count() * subFm.height() + 2 * margin);
|
||||||
rect.setRight(option.rect.right() - margin);
|
rect.setRight(option.rect.right() - margin);
|
||||||
|
|
||||||
|
// save previous state to not mess up colours with the background (fixes issue: https://github.com/nextcloud/desktop/issues/1237)
|
||||||
|
auto oldBrush = painter->brush();
|
||||||
|
auto oldPen = painter->pen();
|
||||||
|
auto oldFont = painter->font();
|
||||||
|
|
||||||
painter->setBrush(color);
|
painter->setBrush(color);
|
||||||
painter->setPen(QColor(0xaa, 0xaa, 0xaa));
|
painter->setPen(QColor(0xaa, 0xaa, 0xaa));
|
||||||
painter->drawRoundedRect(QStyle::visualRect(option.direction, option.rect, rect),
|
painter->drawRoundedRect(QStyle::visualRect(option.direction, option.rect, rect),
|
||||||
@@ -290,6 +295,11 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
|||||||
textRect.translate(0, textRect.height());
|
textRect.translate(0, textRect.height());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// restore previous state
|
||||||
|
painter->setBrush(oldBrush);
|
||||||
|
painter->setPen(oldPen);
|
||||||
|
painter->setFont(oldFont);
|
||||||
|
|
||||||
h = rect.bottom() + margin;
|
h = rect.bottom() + margin;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -349,7 +359,7 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
|||||||
btnOpt.arrowType = Qt::NoArrow;
|
btnOpt.arrowType = Qt::NoArrow;
|
||||||
btnOpt.subControls = QStyle::SC_ToolButton;
|
btnOpt.subControls = QStyle::SC_ToolButton;
|
||||||
btnOpt.rect = optionsButtonVisualRect;
|
btnOpt.rect = optionsButtonVisualRect;
|
||||||
btnOpt.icon = m_moreIcon;
|
btnOpt.icon = _iconMore;
|
||||||
int e = QApplication::style()->pixelMetric(QStyle::PM_ButtonIconSize);
|
int e = QApplication::style()->pixelMetric(QStyle::PM_ButtonIconSize);
|
||||||
btnOpt.iconSize = QSize(e,e);
|
btnOpt.iconSize = QSize(e,e);
|
||||||
QApplication::style()->drawComplexControl(QStyle::CC_ToolButton, &btnOpt, painter);
|
QApplication::style()->drawComplexControl(QStyle::CC_ToolButton, &btnOpt, painter);
|
||||||
@@ -423,5 +433,14 @@ QRect FolderStatusDelegate::errorsListRect(QRect within)
|
|||||||
return within;
|
return within;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FolderStatusDelegate::slotStyleChanged()
|
||||||
|
{
|
||||||
|
customizeStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FolderStatusDelegate::customizeStyle()
|
||||||
|
{
|
||||||
|
_iconMore = Theme::createColorAwareIcon(QLatin1String(":/client/resources/more.svg"));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace OCC
|
} // namespace OCC
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ class FolderStatusDelegate : public QStyledItemDelegate
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
QIcon m_moreIcon;
|
|
||||||
FolderStatusDelegate();
|
FolderStatusDelegate();
|
||||||
|
|
||||||
enum datarole { FolderAliasRole = Qt::UserRole + 100,
|
enum datarole { FolderAliasRole = Qt::UserRole + 100,
|
||||||
@@ -62,9 +61,16 @@ public:
|
|||||||
static QRect errorsListRect(QRect within);
|
static QRect errorsListRect(QRect within);
|
||||||
static int rootFolderHeightWithoutErrors(const QFontMetrics &fm, const QFontMetrics &aliasFm);
|
static int rootFolderHeightWithoutErrors(const QFontMetrics &fm, const QFontMetrics &aliasFm);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void slotStyleChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void customizeStyle();
|
||||||
|
|
||||||
static QString addFolderText();
|
static QString addFolderText();
|
||||||
QPersistentModelIndex _pressedIndex;
|
QPersistentModelIndex _pressedIndex;
|
||||||
|
|
||||||
|
QIcon _iconMore;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace OCC
|
} // namespace OCC
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ QVariant FolderStatusModel::data(const QModelIndex &index, int role) const
|
|||||||
return QVariant(tr("Error while loading the list of folders from the server.")
|
return QVariant(tr("Error while loading the list of folders from the server.")
|
||||||
+ QString("\n") + x->_lastErrorString);
|
+ QString("\n") + x->_lastErrorString);
|
||||||
} else {
|
} else {
|
||||||
return tr("Fetching folder list from server...");
|
return tr("Fetching folder list from server …");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -1099,15 +1099,15 @@ void FolderStatusModel::slotFolderSyncStateChange(Folder *f)
|
|||||||
}
|
}
|
||||||
QString message;
|
QString message;
|
||||||
if (pos <= 0) {
|
if (pos <= 0) {
|
||||||
message = tr("Waiting...");
|
message = tr("Waiting …");
|
||||||
} else {
|
} else {
|
||||||
message = tr("Waiting for %n other folder(s)...", "", pos);
|
message = tr("Waiting for %n other folder(s) …", "", pos);
|
||||||
}
|
}
|
||||||
pi = SubFolderInfo::Progress();
|
pi = SubFolderInfo::Progress();
|
||||||
pi._overallSyncString = message;
|
pi._overallSyncString = message;
|
||||||
} else if (state == SyncResult::SyncPrepare) {
|
} else if (state == SyncResult::SyncPrepare) {
|
||||||
pi = SubFolderInfo::Progress();
|
pi = SubFolderInfo::Progress();
|
||||||
pi._overallSyncString = tr("Preparing to sync...");
|
pi._overallSyncString = tr("Preparing to sync …");
|
||||||
} else if (state == SyncResult::Problem || state == SyncResult::Success) {
|
} else if (state == SyncResult::Problem || state == SyncResult::Success) {
|
||||||
// Reset the progress info after a sync.
|
// Reset the progress info after a sync.
|
||||||
pi = SubFolderInfo::Progress();
|
pi = SubFolderInfo::Progress();
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="localFolderChooseBtn">
|
<widget class="QPushButton" name="localFolderChooseBtn">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Choose...</string>
|
<string>&Choose …</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@@ -140,7 +140,7 @@
|
|||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QPushButton" name="addFolderButton">
|
<widget class="QPushButton" name="addFolderButton">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Create Folder</string>
|
<string>Create folder</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@@ -57,6 +57,13 @@ GeneralSettings::GeneralSettings(QWidget *parent)
|
|||||||
|
|
||||||
connect(_ui->showInExplorerNavigationPaneCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::slotShowInExplorerNavigationPane);
|
connect(_ui->showInExplorerNavigationPaneCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::slotShowInExplorerNavigationPane);
|
||||||
|
|
||||||
|
// Rename 'Explorer' appropriately on non-Windows
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
QString txt = _ui->showInExplorerNavigationPaneCheckBox->text();
|
||||||
|
txt.replace(QString::fromLatin1("Explorer"), QString::fromLatin1("Finder"));
|
||||||
|
_ui->showInExplorerNavigationPaneCheckBox->setText(txt);
|
||||||
|
#endif
|
||||||
|
|
||||||
_ui->autostartCheckBox->setChecked(Utility::hasLaunchOnStartup(Theme::instance()->appName()));
|
_ui->autostartCheckBox->setChecked(Utility::hasLaunchOnStartup(Theme::instance()->appName()));
|
||||||
connect(_ui->autostartCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::slotToggleLaunchOnStartup);
|
connect(_ui->autostartCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::slotToggleLaunchOnStartup);
|
||||||
|
|
||||||
@@ -70,7 +77,8 @@ GeneralSettings::GeneralSettings(QWidget *parent)
|
|||||||
connect(_ui->legalNoticeButton, &QPushButton::clicked, this, &GeneralSettings::slotShowLegalNotice);
|
connect(_ui->legalNoticeButton, &QPushButton::clicked, this, &GeneralSettings::slotShowLegalNotice);
|
||||||
|
|
||||||
loadMiscSettings();
|
loadMiscSettings();
|
||||||
slotUpdateInfo();
|
// updater info now set in: customizeStyle
|
||||||
|
//slotUpdateInfo();
|
||||||
|
|
||||||
// misc
|
// misc
|
||||||
connect(_ui->monoIconsCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::saveMiscSettings);
|
connect(_ui->monoIconsCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::saveMiscSettings);
|
||||||
@@ -92,6 +100,9 @@ GeneralSettings::GeneralSettings(QWidget *parent)
|
|||||||
if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows10)
|
if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows10)
|
||||||
#endif
|
#endif
|
||||||
_ui->showInExplorerNavigationPaneCheckBox->setVisible(false);
|
_ui->showInExplorerNavigationPaneCheckBox->setVisible(false);
|
||||||
|
#else
|
||||||
|
// Hide on non-Windows
|
||||||
|
_ui->showInExplorerNavigationPaneCheckBox->setVisible(false);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Set the left contents margin of the layout to zero to make the checkboxes
|
/* Set the left contents margin of the layout to zero to make the checkboxes
|
||||||
@@ -109,6 +120,8 @@ GeneralSettings::GeneralSettings(QWidget *parent)
|
|||||||
|
|
||||||
// accountAdded means the wizard was finished and the wizard might change some options.
|
// accountAdded means the wizard was finished and the wizard might change some options.
|
||||||
connect(AccountManager::instance(), &AccountManager::accountAdded, this, &GeneralSettings::loadMiscSettings);
|
connect(AccountManager::instance(), &AccountManager::accountAdded, this, &GeneralSettings::loadMiscSettings);
|
||||||
|
|
||||||
|
customizeStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
GeneralSettings::~GeneralSettings()
|
GeneralSettings::~GeneralSettings()
|
||||||
@@ -149,7 +162,11 @@ void GeneralSettings::slotUpdateInfo()
|
|||||||
connect(updater, &OCUpdater::downloadStateChanged, this, &GeneralSettings::slotUpdateInfo, Qt::UniqueConnection);
|
connect(updater, &OCUpdater::downloadStateChanged, this, &GeneralSettings::slotUpdateInfo, Qt::UniqueConnection);
|
||||||
connect(_ui->restartButton, &QAbstractButton::clicked, updater, &OCUpdater::slotStartInstaller, Qt::UniqueConnection);
|
connect(_ui->restartButton, &QAbstractButton::clicked, updater, &OCUpdater::slotStartInstaller, Qt::UniqueConnection);
|
||||||
connect(_ui->restartButton, &QAbstractButton::clicked, qApp, &QApplication::quit, Qt::UniqueConnection);
|
connect(_ui->restartButton, &QAbstractButton::clicked, qApp, &QApplication::quit, Qt::UniqueConnection);
|
||||||
_ui->updateStateLabel->setText(updater->statusString());
|
|
||||||
|
QString status = updater->statusString();
|
||||||
|
Theme::replaceLinkColorStringBackgroundAware(status);
|
||||||
|
_ui->updateStateLabel->setText(status);
|
||||||
|
|
||||||
_ui->restartButton->setVisible(updater->downloadState() == OCUpdater::DownloadComplete);
|
_ui->restartButton->setVisible(updater->downloadState() == OCUpdater::DownloadComplete);
|
||||||
} else {
|
} else {
|
||||||
// can't have those infos from sparkle currently
|
// can't have those infos from sparkle currently
|
||||||
@@ -211,4 +228,20 @@ void GeneralSettings::slotShowLegalNotice()
|
|||||||
delete notice;
|
delete notice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GeneralSettings::slotStyleChanged()
|
||||||
|
{
|
||||||
|
customizeStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GeneralSettings::customizeStyle()
|
||||||
|
{
|
||||||
|
// setup about section
|
||||||
|
QString about = Theme::instance()->about();
|
||||||
|
Theme::replaceLinkColorStringBackgroundAware(about);
|
||||||
|
_ui->aboutLabel->setText(about);
|
||||||
|
|
||||||
|
// updater info
|
||||||
|
slotUpdateInfo();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace OCC
|
} // namespace OCC
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ public:
|
|||||||
~GeneralSettings();
|
~GeneralSettings();
|
||||||
QSize sizeHint() const override;
|
QSize sizeHint() const override;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void slotStyleChanged();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void saveMiscSettings();
|
void saveMiscSettings();
|
||||||
void slotToggleLaunchOnStartup(bool);
|
void slotToggleLaunchOnStartup(bool);
|
||||||
@@ -50,6 +53,8 @@ private slots:
|
|||||||
void slotShowLegalNotice();
|
void slotShowLegalNotice();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void customizeStyle();
|
||||||
|
|
||||||
Ui::GeneralSettings *_ui;
|
Ui::GeneralSettings *_ui;
|
||||||
QPointer<IgnoreListEditor> _ignoreEditor;
|
QPointer<IgnoreListEditor> _ignoreEditor;
|
||||||
QPointer<SyncLogDialog> _syncLogDialog;
|
QPointer<SyncLogDialog> _syncLogDialog;
|
||||||
|
|||||||
146
src/gui/headerbanner.cpp
Normal file
146
src/gui/headerbanner.cpp
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) by Michael Schuster <michael@nextcloud.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Based on Qt sourcecode:
|
||||||
|
** qt5/qtbase/src/widgets/dialogs/qwizard.cpp
|
||||||
|
**
|
||||||
|
** https://code.qt.io/cgit/qt/qtbase.git/tree/src/widgets/dialogs/qwizard.cpp?h=v5.13.0
|
||||||
|
**
|
||||||
|
** Original license:
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of the QtWidgets module of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:LGPL$
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 3 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 2.0 or (at your option) the GNU General
|
||||||
|
** Public license version 3 or any later version approved by the KDE Free
|
||||||
|
** Qt Foundation. The licenses are as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||||
|
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "headerbanner.h"
|
||||||
|
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QStyle>
|
||||||
|
#include <QGuiApplication>
|
||||||
|
|
||||||
|
namespace OCC {
|
||||||
|
|
||||||
|
// These fudge terms were needed a few places to obtain pixel-perfect results
|
||||||
|
const int GapBetweenLogoAndRightEdge = 5;
|
||||||
|
const int ModernHeaderTopMargin = 2;
|
||||||
|
|
||||||
|
HeaderBanner::HeaderBanner(QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
{
|
||||||
|
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||||
|
setBackgroundRole(QPalette::Base);
|
||||||
|
titleLabel = new QLabel(this);
|
||||||
|
titleLabel->setBackgroundRole(QPalette::Base);
|
||||||
|
logoLabel = new QLabel(this);
|
||||||
|
QFont font = titleLabel->font();
|
||||||
|
font.setBold(true);
|
||||||
|
titleLabel->setFont(font);
|
||||||
|
layout = new QGridLayout(this);
|
||||||
|
layout->setContentsMargins(QMargins());
|
||||||
|
layout->setSpacing(0);
|
||||||
|
layout->setRowMinimumHeight(3, 1);
|
||||||
|
layout->setRowStretch(4, 1);
|
||||||
|
layout->setColumnStretch(2, 1);
|
||||||
|
layout->setColumnMinimumWidth(4, 2 * GapBetweenLogoAndRightEdge);
|
||||||
|
layout->setColumnMinimumWidth(6, GapBetweenLogoAndRightEdge);
|
||||||
|
layout->addWidget(titleLabel, 1, 1, 5, 1);
|
||||||
|
layout->addWidget(logoLabel, 1, 5, 5, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HeaderBanner::setup(const QString &title, const QPixmap &logo, const QPixmap &banner,
|
||||||
|
const Qt::TextFormat titleFormat, const QString &styleSheet)
|
||||||
|
{
|
||||||
|
QStyle *style = parentWidget()->style();
|
||||||
|
//const int layoutHorizontalSpacing = style->pixelMetric(QStyle::PM_LayoutHorizontalSpacing);
|
||||||
|
int topLevelMarginLeft = style->pixelMetric(QStyle::PM_LayoutLeftMargin, 0, parentWidget());
|
||||||
|
int topLevelMarginRight = style->pixelMetric(QStyle::PM_LayoutRightMargin, 0, parentWidget());
|
||||||
|
int topLevelMarginTop = style->pixelMetric(QStyle::PM_LayoutTopMargin, 0, parentWidget());
|
||||||
|
//int topLevelMarginBottom = style->pixelMetric(QStyle::PM_LayoutBottomMargin, 0, parentWidget());
|
||||||
|
|
||||||
|
layout->setRowMinimumHeight(0, ModernHeaderTopMargin);
|
||||||
|
layout->setRowMinimumHeight(1, topLevelMarginTop - ModernHeaderTopMargin - 1);
|
||||||
|
layout->setRowMinimumHeight(6, 3);
|
||||||
|
int minColumnWidth0 = topLevelMarginLeft + topLevelMarginRight;
|
||||||
|
int minColumnWidth1 = topLevelMarginLeft + topLevelMarginRight + 1;
|
||||||
|
layout->setColumnMinimumWidth(0, minColumnWidth0);
|
||||||
|
layout->setColumnMinimumWidth(1, minColumnWidth1);
|
||||||
|
titleLabel->setTextFormat(titleFormat);
|
||||||
|
titleLabel->setText(title);
|
||||||
|
if(!styleSheet.isEmpty())
|
||||||
|
titleLabel->setStyleSheet(styleSheet);
|
||||||
|
logoLabel->setPixmap(logo);
|
||||||
|
bannerPixmap = banner;
|
||||||
|
if (bannerPixmap.isNull()) {
|
||||||
|
QSize size = layout->totalMinimumSize();
|
||||||
|
setMinimumSize(size);
|
||||||
|
setMaximumSize(QWIDGETSIZE_MAX, size.height());
|
||||||
|
} else {
|
||||||
|
setFixedHeight(banner.height() + 2);
|
||||||
|
}
|
||||||
|
updateGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HeaderBanner::paintEvent(QPaintEvent * /* event */)
|
||||||
|
{
|
||||||
|
QPainter painter(this);
|
||||||
|
painter.drawPixmap(0, 0, width(), bannerPixmap.height(), bannerPixmap);
|
||||||
|
int x = width() - 2;
|
||||||
|
int y = height() - 2;
|
||||||
|
const QPalette &pal = QGuiApplication::palette();
|
||||||
|
painter.setPen(pal.mid().color());
|
||||||
|
painter.drawLine(0, y, x, y);
|
||||||
|
painter.setPen(pal.base().color());
|
||||||
|
painter.drawPoint(x + 1, y);
|
||||||
|
painter.drawLine(0, y + 1, x + 1, y + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace OCC
|
||||||
93
src/gui/headerbanner.h
Normal file
93
src/gui/headerbanner.h
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) by Michael Schuster <michael@nextcloud.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Based on Qt sourcecode:
|
||||||
|
** qt5/qtbase/src/widgets/dialogs/qwizard.cpp
|
||||||
|
**
|
||||||
|
** https://code.qt.io/cgit/qt/qtbase.git/tree/src/widgets/dialogs/qwizard.cpp?h=v5.13.0
|
||||||
|
**
|
||||||
|
** Original license:
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of the QtWidgets module of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:LGPL$
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 3 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 2.0 or (at your option) the GNU General
|
||||||
|
** Public license version 3 or any later version approved by the KDE Free
|
||||||
|
** Qt Foundation. The licenses are as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||||
|
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef HEADERBANNER_H
|
||||||
|
#define HEADERBANNER_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
class QLabel;
|
||||||
|
class QGridLayout;
|
||||||
|
class QPixmap;
|
||||||
|
|
||||||
|
namespace OCC {
|
||||||
|
|
||||||
|
class HeaderBanner : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
HeaderBanner(QWidget *parent = 0);
|
||||||
|
|
||||||
|
void setup(const QString &title, const QPixmap &logo, const QPixmap &banner,
|
||||||
|
const Qt::TextFormat titleFormat, const QString &styleSheet);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QLabel *titleLabel;
|
||||||
|
QLabel *logoLabel;
|
||||||
|
QGridLayout *layout;
|
||||||
|
QPixmap bannerPixmap;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace OCC
|
||||||
|
|
||||||
|
#endif // HEADERBANNER_H
|
||||||
@@ -24,7 +24,7 @@ LegalNotice::LegalNotice(QDialog *parent)
|
|||||||
{
|
{
|
||||||
_ui->setupUi(this);
|
_ui->setupUi(this);
|
||||||
|
|
||||||
QString notice = tr("<p>Copyright 2017-2018 Nextcloud GmbH<br />"
|
QString notice = tr("<p>Copyright 2017-2020 Nextcloud GmbH<br />"
|
||||||
"Copyright 2012-2018 ownCloud GmbH</p>");
|
"Copyright 2012-2018 ownCloud GmbH</p>");
|
||||||
|
|
||||||
notice += tr("<p>Licensed under the GNU General Public License (GPL) Version 2.0 or any later version.</p>");
|
notice += tr("<p>Licensed under the GNU General Public License (GPL) Version 2.0 or any later version.</p>");
|
||||||
|
|||||||
@@ -32,6 +32,11 @@ NavigationPaneHelper::NavigationPaneHelper(FolderMan *folderMan)
|
|||||||
|
|
||||||
_updateCloudStorageRegistryTimer.setSingleShot(true);
|
_updateCloudStorageRegistryTimer.setSingleShot(true);
|
||||||
connect(&_updateCloudStorageRegistryTimer, &QTimer::timeout, this, &NavigationPaneHelper::updateCloudStorageRegistry);
|
connect(&_updateCloudStorageRegistryTimer, &QTimer::timeout, this, &NavigationPaneHelper::updateCloudStorageRegistry);
|
||||||
|
|
||||||
|
// Ensure that the folder integration stays persistent in Explorer,
|
||||||
|
// the uninstaller removes the folder upon updating the client.
|
||||||
|
_showInExplorerNavigationPane = !_showInExplorerNavigationPane;
|
||||||
|
setShowInExplorerNavigationPane(!_showInExplorerNavigationPane);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavigationPaneHelper::setShowInExplorerNavigationPane(bool show)
|
void NavigationPaneHelper::setShowInExplorerNavigationPane(bool show)
|
||||||
@@ -74,73 +79,78 @@ void NavigationPaneHelper::updateCloudStorageRegistry()
|
|||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Then re-save every folder that has a valid navigationPaneClsid to the registry.
|
// Only save folder entries if the option is enabled.
|
||||||
// We currently don't distinguish between new and existing CLSIDs, if it's there we just
|
if (_showInExplorerNavigationPane) {
|
||||||
// save over it. We at least need to update the tile in case we are suddently using multiple accounts.
|
// Then re-save every folder that has a valid navigationPaneClsid to the registry.
|
||||||
foreach (Folder *folder, _folderMan->map()) {
|
// We currently don't distinguish between new and existing CLSIDs, if it's there we just
|
||||||
if (!folder->navigationPaneClsid().isNull()) {
|
// save over it. We at least need to update the tile in case we are suddently using multiple accounts.
|
||||||
// If it already exists, unmark it for removal, this is a valid sync root.
|
foreach (Folder *folder, _folderMan->map()) {
|
||||||
entriesToRemove.removeOne(folder->navigationPaneClsid());
|
if (!folder->navigationPaneClsid().isNull()) {
|
||||||
|
// If it already exists, unmark it for removal, this is a valid sync root.
|
||||||
|
entriesToRemove.removeOne(folder->navigationPaneClsid());
|
||||||
|
|
||||||
QString clsidStr = folder->navigationPaneClsid().toString();
|
QString clsidStr = folder->navigationPaneClsid().toString();
|
||||||
QString clsidPath = QString() % "Software\\Classes\\CLSID\\" % clsidStr;
|
QString clsidPath = QString() % "Software\\Classes\\CLSID\\" % clsidStr;
|
||||||
QString clsidPathWow64 = QString() % "Software\\Classes\\Wow6432Node\\CLSID\\" % clsidStr;
|
QString clsidPathWow64 = QString() % "Software\\Classes\\Wow6432Node\\CLSID\\" % clsidStr;
|
||||||
QString namespacePath = QString() % "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\NameSpace\\" % clsidStr;
|
QString namespacePath = QString() % "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\NameSpace\\" % clsidStr;
|
||||||
|
|
||||||
QString title = folder->shortGuiRemotePathOrAppName();
|
QString title = folder->shortGuiRemotePathOrAppName();
|
||||||
// Write the account name in the sidebar only when using more than one account.
|
// Write the account name in the sidebar only when using more than one account.
|
||||||
if (AccountManager::instance()->accounts().size() > 1)
|
if (AccountManager::instance()->accounts().size() > 1)
|
||||||
title = title % " - " % folder->accountState()->account()->displayName();
|
title = title % " - " % folder->accountState()->account()->displayName();
|
||||||
QString iconPath = QDir::toNativeSeparators(qApp->applicationFilePath());
|
QString iconPath = QDir::toNativeSeparators(qApp->applicationFilePath());
|
||||||
QString targetFolderPath = QDir::toNativeSeparators(folder->cleanPath());
|
QString targetFolderPath = QDir::toNativeSeparators(folder->cleanPath());
|
||||||
|
|
||||||
qCInfo(lcNavPane) << "Explorer Cloud storage provider: saving path" << targetFolderPath << "to CLSID" << clsidStr;
|
qCInfo(lcNavPane) << "Explorer Cloud storage provider: saving path" << targetFolderPath << "to CLSID" << clsidStr;
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
// Steps taken from: https://msdn.microsoft.com/en-us/library/windows/desktop/dn889934%28v=vs.85%29.aspx
|
// Steps taken from: https://msdn.microsoft.com/en-us/library/windows/desktop/dn889934%28v=vs.85%29.aspx
|
||||||
// Step 1: Add your CLSID and name your extension
|
// Step 1: Add your CLSID and name your extension
|
||||||
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath, QString(), REG_SZ, title);
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath, QString(), REG_SZ, title);
|
||||||
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64, QString(), REG_SZ, title);
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64, QString(), REG_SZ, title);
|
||||||
// Step 2: Set the image for your icon
|
// Step 2: Set the image for your icon
|
||||||
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\DefaultIcon"), QString(), REG_SZ, iconPath);
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\DefaultIcon"), QString(), REG_SZ, iconPath);
|
||||||
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64 + QStringLiteral("\\DefaultIcon"), QString(), REG_SZ, iconPath);
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64 + QStringLiteral("\\DefaultIcon"), QString(), REG_SZ, iconPath);
|
||||||
// Step 3: Add your extension to the Navigation Pane and make it visible
|
// Step 3: Add your extension to the Navigation Pane and make it visible
|
||||||
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath, QStringLiteral("System.IsPinnedToNameSpaceTree"), REG_DWORD, 0x1);
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath, QStringLiteral("System.IsPinnedToNameSpaceTree"), REG_DWORD, 0x1);
|
||||||
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64, QStringLiteral("System.IsPinnedToNameSpaceTree"), REG_DWORD, 0x1);
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64, QStringLiteral("System.IsPinnedToNameSpaceTree"), REG_DWORD, 0x1);
|
||||||
// Step 4: Set the location for your extension in the Navigation Pane
|
// Step 4: Set the location for your extension in the Navigation Pane
|
||||||
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath, QStringLiteral("SortOrderIndex"), REG_DWORD, 0x41);
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath, QStringLiteral("SortOrderIndex"), REG_DWORD, 0x41);
|
||||||
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64, QStringLiteral("SortOrderIndex"), REG_DWORD, 0x41);
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64, QStringLiteral("SortOrderIndex"), REG_DWORD, 0x41);
|
||||||
// Step 5: Provide the dll that hosts your extension.
|
// Step 5: Provide the dll that hosts your extension.
|
||||||
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\InProcServer32"), QString(), REG_EXPAND_SZ, QStringLiteral("%systemroot%\\system32\\shell32.dll"));
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\InProcServer32"), QString(), REG_EXPAND_SZ, QStringLiteral("%systemroot%\\system32\\shell32.dll"));
|
||||||
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64 + QStringLiteral("\\InProcServer32"), QString(), REG_EXPAND_SZ, QStringLiteral("%systemroot%\\system32\\shell32.dll"));
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64 + QStringLiteral("\\InProcServer32"), QString(), REG_EXPAND_SZ, QStringLiteral("%systemroot%\\system32\\shell32.dll"));
|
||||||
// Step 6: Define the instance object
|
// Step 6: Define the instance object
|
||||||
// Indicate that your namespace extension should function like other file folder structures in File Explorer.
|
// Indicate that your namespace extension should function like other file folder structures in File Explorer.
|
||||||
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\Instance"), QStringLiteral("CLSID"), REG_SZ, QStringLiteral("{0E5AAE11-A475-4c5b-AB00-C66DE400274E}"));
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\Instance"), QStringLiteral("CLSID"), REG_SZ, QStringLiteral("{0E5AAE11-A475-4c5b-AB00-C66DE400274E}"));
|
||||||
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64 + QStringLiteral("\\Instance"), QStringLiteral("CLSID"), REG_SZ, QStringLiteral("{0E5AAE11-A475-4c5b-AB00-C66DE400274E}"));
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64 + QStringLiteral("\\Instance"), QStringLiteral("CLSID"), REG_SZ, QStringLiteral("{0E5AAE11-A475-4c5b-AB00-C66DE400274E}"));
|
||||||
// Step 7: Provide the file system attributes of the target folder
|
// Step 7: Provide the file system attributes of the target folder
|
||||||
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\Instance\\InitPropertyBag"), QStringLiteral("Attributes"), REG_DWORD, 0x11);
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\Instance\\InitPropertyBag"), QStringLiteral("Attributes"), REG_DWORD, 0x11);
|
||||||
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64 + QStringLiteral("\\Instance\\InitPropertyBag"), QStringLiteral("Attributes"), REG_DWORD, 0x11);
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64 + QStringLiteral("\\Instance\\InitPropertyBag"), QStringLiteral("Attributes"), REG_DWORD, 0x11);
|
||||||
// Step 8: Set the path for the sync root
|
// Step 8: Set the path for the sync root
|
||||||
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\Instance\\InitPropertyBag"), QStringLiteral("TargetFolderPath"), REG_SZ, targetFolderPath);
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\Instance\\InitPropertyBag"), QStringLiteral("TargetFolderPath"), REG_SZ, targetFolderPath);
|
||||||
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64 + QStringLiteral("\\Instance\\InitPropertyBag"), QStringLiteral("TargetFolderPath"), REG_SZ, targetFolderPath);
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64 + QStringLiteral("\\Instance\\InitPropertyBag"), QStringLiteral("TargetFolderPath"), REG_SZ, targetFolderPath);
|
||||||
// Step 9: Set appropriate shell flags
|
// Step 9: Set appropriate shell flags
|
||||||
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\ShellFolder"), QStringLiteral("FolderValueFlags"), REG_DWORD, 0x28);
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\ShellFolder"), QStringLiteral("FolderValueFlags"), REG_DWORD, 0x28);
|
||||||
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64 + QStringLiteral("\\ShellFolder"), QStringLiteral("FolderValueFlags"), REG_DWORD, 0x28);
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64 + QStringLiteral("\\ShellFolder"), QStringLiteral("FolderValueFlags"), REG_DWORD, 0x28);
|
||||||
// Step 10: Set the appropriate flags to control your shell behavior
|
// Step 10: Set the appropriate flags to control your shell behavior
|
||||||
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\ShellFolder"), QStringLiteral("Attributes"), REG_DWORD, 0xF080004D);
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\ShellFolder"), QStringLiteral("Attributes"), REG_DWORD, 0xF080004D);
|
||||||
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64 + QStringLiteral("\\ShellFolder"), QStringLiteral("Attributes"), REG_DWORD, 0xF080004D);
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64 + QStringLiteral("\\ShellFolder"), QStringLiteral("Attributes"), REG_DWORD, 0xF080004D);
|
||||||
// Step 11: Register your extension in the namespace root
|
// Step 11: Register your extension in the namespace root
|
||||||
Utility::registrySetKeyValue(HKEY_CURRENT_USER, namespacePath, QString(), REG_SZ, title);
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, namespacePath, QString(), REG_SZ, title);
|
||||||
// Step 12: Hide your extension from the Desktop
|
// Step 12: Hide your extension from the Desktop
|
||||||
Utility::registrySetKeyValue(HKEY_CURRENT_USER, QStringLiteral("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\NewStartPanel"), clsidStr, REG_DWORD, 0x1);
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, QStringLiteral("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\NewStartPanel"), clsidStr, REG_DWORD, 0x1);
|
||||||
|
|
||||||
// For us, to later be able to iterate and find our own namespace entries and associated CLSID.
|
// For us, to later be able to iterate and find our own namespace entries and associated CLSID.
|
||||||
// Use the macro instead of the theme to make sure it matches with the uninstaller.
|
// Use the macro instead of the theme to make sure it matches with the uninstaller.
|
||||||
Utility::registrySetKeyValue(HKEY_CURRENT_USER, namespacePath, QStringLiteral("ApplicationName"), REG_SZ, QLatin1String(APPLICATION_NAME));
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, namespacePath, QStringLiteral("ApplicationName"), REG_SZ, QLatin1String(APPLICATION_NAME));
|
||||||
#else
|
#else
|
||||||
// This code path should only occur on Windows (the config will be false, and the checkbox invisible on other platforms).
|
// This code path should only occur on Windows (the config will be false, and the checkbox invisible on other platforms).
|
||||||
// Add runtime checks rather than #ifdefing out the whole code to help catch breakages when developing on other platforms.
|
// Add runtime checks rather than #ifdefing out the whole code to help catch breakages when developing on other platforms.
|
||||||
Q_ASSERT(false);
|
|
||||||
|
// Don't crash, by any means!
|
||||||
|
// Q_ASSERT(false);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -97,7 +97,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QLabel" name="label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>:</string>
|
<string notr="true">:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@@ -20,12 +20,7 @@
|
|||||||
#include "progressdispatcher.h"
|
#include "progressdispatcher.h"
|
||||||
#include "owncloudsetupwizard.h"
|
#include "owncloudsetupwizard.h"
|
||||||
#include "sharedialog.h"
|
#include "sharedialog.h"
|
||||||
#if defined(Q_OS_MAC)
|
|
||||||
#include "settingsdialogmac.h"
|
|
||||||
#include "macwindow.h" // qtmacgoodies
|
|
||||||
#else
|
|
||||||
#include "settingsdialog.h"
|
#include "settingsdialog.h"
|
||||||
#endif
|
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "logbrowser.h"
|
#include "logbrowser.h"
|
||||||
#include "account.h"
|
#include "account.h"
|
||||||
@@ -62,11 +57,7 @@ const char propertyAccountC[] = "oc_account";
|
|||||||
ownCloudGui::ownCloudGui(Application *parent)
|
ownCloudGui::ownCloudGui(Application *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, _tray(nullptr)
|
, _tray(nullptr)
|
||||||
#if defined(Q_OS_MAC)
|
|
||||||
, _settingsDialog(new SettingsDialogMac(this))
|
|
||||||
#else
|
|
||||||
, _settingsDialog(new SettingsDialog(this))
|
, _settingsDialog(new SettingsDialog(this))
|
||||||
#endif
|
|
||||||
, _logBrowser(nullptr)
|
, _logBrowser(nullptr)
|
||||||
#ifdef WITH_LIBCLOUDPROVIDERS
|
#ifdef WITH_LIBCLOUDPROVIDERS
|
||||||
, _bus(QDBusConnection::sessionBus())
|
, _bus(QDBusConnection::sessionBus())
|
||||||
@@ -421,7 +412,7 @@ void ownCloudGui::addAccountContextMenu(AccountStatePtr accountState, QMenu *men
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (accountState->isSignedOut()) {
|
if (accountState->isSignedOut()) {
|
||||||
QAction *signin = menu->addAction(tr("Log in..."));
|
QAction *signin = menu->addAction(tr("Log in …"));
|
||||||
signin->setProperty(propertyAccountC, QVariant::fromValue(accountState));
|
signin->setProperty(propertyAccountC, QVariant::fromValue(accountState));
|
||||||
connect(signin, &QAction::triggered, this, &ownCloudGui::slotLogin);
|
connect(signin, &QAction::triggered, this, &ownCloudGui::slotLogin);
|
||||||
} else {
|
} else {
|
||||||
@@ -786,9 +777,9 @@ void ownCloudGui::setupActions()
|
|||||||
_actionStatus->setEnabled(false);
|
_actionStatus->setEnabled(false);
|
||||||
_navLinksMenu = new QMenu(tr("Apps"));
|
_navLinksMenu = new QMenu(tr("Apps"));
|
||||||
_navLinksMenu->setEnabled(false);
|
_navLinksMenu->setEnabled(false);
|
||||||
_actionSettings = new QAction(tr("Settings..."), this);
|
_actionSettings = new QAction(tr("Settings …"), this);
|
||||||
_actionNewAccountWizard = new QAction(tr("New account..."), this);
|
_actionNewAccountWizard = new QAction(tr("New account …"), this);
|
||||||
_actionRecent = new QAction(tr("View more activity..."), this);
|
_actionRecent = new QAction(tr("View more activity …"), this);
|
||||||
_actionRecent->setEnabled(true);
|
_actionRecent->setEnabled(true);
|
||||||
|
|
||||||
QObject::connect(_actionRecent, &QAction::triggered, this, &ownCloudGui::slotShowSyncProtocol);
|
QObject::connect(_actionRecent, &QAction::triggered, this, &ownCloudGui::slotShowSyncProtocol);
|
||||||
@@ -943,7 +934,7 @@ void ownCloudGui::slotUpdateProgress(const QString &folder, const ProgressInfo &
|
|||||||
quint64 totalFileCount = qMax(progress.totalFiles(), currentFile);
|
quint64 totalFileCount = qMax(progress.totalFiles(), currentFile);
|
||||||
QString msg;
|
QString msg;
|
||||||
if (progress.trustEta()) {
|
if (progress.trustEta()) {
|
||||||
msg = tr("Syncing %1 of %2 (%3 left)")
|
msg = tr("Syncing %1 of %2 (%3 left)")
|
||||||
.arg(currentFile)
|
.arg(currentFile)
|
||||||
.arg(totalFileCount)
|
.arg(totalFileCount)
|
||||||
.arg(Utility::durationToDescriptiveString2(progress.totalProgress().estimatedEta));
|
.arg(Utility::durationToDescriptiveString2(progress.totalProgress().estimatedEta));
|
||||||
@@ -1077,18 +1068,18 @@ void ownCloudGui::slotShowGuiMessage(const QString &title, const QString &messag
|
|||||||
void ownCloudGui::slotShowSettings()
|
void ownCloudGui::slotShowSettings()
|
||||||
{
|
{
|
||||||
if (_settingsDialog.isNull()) {
|
if (_settingsDialog.isNull()) {
|
||||||
_settingsDialog =
|
_settingsDialog = new SettingsDialog(this);
|
||||||
#if defined(Q_OS_MAC)
|
|
||||||
new SettingsDialogMac(this);
|
|
||||||
#else
|
|
||||||
new SettingsDialog(this);
|
|
||||||
#endif
|
|
||||||
_settingsDialog->setAttribute(Qt::WA_DeleteOnClose, true);
|
_settingsDialog->setAttribute(Qt::WA_DeleteOnClose, true);
|
||||||
_settingsDialog->show();
|
_settingsDialog->show();
|
||||||
}
|
}
|
||||||
raiseDialog(_settingsDialog.data());
|
raiseDialog(_settingsDialog.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ownCloudGui::slotSettingsDialogActivated()
|
||||||
|
{
|
||||||
|
emit isShowingSettingsDialog();
|
||||||
|
}
|
||||||
|
|
||||||
void ownCloudGui::slotShowSyncProtocol()
|
void ownCloudGui::slotShowSyncProtocol()
|
||||||
{
|
{
|
||||||
slotShowSettings();
|
slotShowSettings();
|
||||||
@@ -1143,10 +1134,6 @@ void ownCloudGui::raiseDialog(QWidget *raiseWidget)
|
|||||||
raiseWidget->raise();
|
raiseWidget->raise();
|
||||||
raiseWidget->activateWindow();
|
raiseWidget->activateWindow();
|
||||||
|
|
||||||
#if defined(Q_OS_MAC)
|
|
||||||
// viel hilft viel ;-)
|
|
||||||
MacWindow::bringToFront(raiseWidget);
|
|
||||||
#endif
|
|
||||||
#if defined(Q_OS_X11)
|
#if defined(Q_OS_X11)
|
||||||
WId wid = widget->winId();
|
WId wid = widget->winId();
|
||||||
NETWM::init();
|
NETWM::init();
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ namespace OCC {
|
|||||||
class Folder;
|
class Folder;
|
||||||
|
|
||||||
class SettingsDialog;
|
class SettingsDialog;
|
||||||
class SettingsDialogMac;
|
|
||||||
class ShareDialog;
|
class ShareDialog;
|
||||||
class Application;
|
class Application;
|
||||||
class LogBrowser;
|
class LogBrowser;
|
||||||
@@ -71,6 +70,7 @@ public:
|
|||||||
signals:
|
signals:
|
||||||
void setupProxy();
|
void setupProxy();
|
||||||
void serverError(int code, const QString &message);
|
void serverError(int code, const QString &message);
|
||||||
|
void isShowingSettingsDialog();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setupContextMenu();
|
void setupContextMenu();
|
||||||
@@ -94,6 +94,7 @@ public slots:
|
|||||||
void slotToggleLogBrowser();
|
void slotToggleLogBrowser();
|
||||||
void slotOpenOwnCloud();
|
void slotOpenOwnCloud();
|
||||||
void slotOpenSettingsDialog();
|
void slotOpenSettingsDialog();
|
||||||
|
void slotSettingsDialogActivated();
|
||||||
void slotHelp();
|
void slotHelp();
|
||||||
void slotOpenPath(const QString &path);
|
void slotOpenPath(const QString &path);
|
||||||
void slotAccountStateChanged();
|
void slotAccountStateChanged();
|
||||||
@@ -131,11 +132,7 @@ private:
|
|||||||
void buildNavigationAppsMenu(AccountStatePtr account, QMenu *accountMenu);
|
void buildNavigationAppsMenu(AccountStatePtr account, QMenu *accountMenu);
|
||||||
|
|
||||||
QPointer<Systray> _tray;
|
QPointer<Systray> _tray;
|
||||||
#if defined(Q_OS_MAC)
|
|
||||||
QPointer<SettingsDialogMac> _settingsDialog;
|
|
||||||
#else
|
|
||||||
QPointer<SettingsDialog> _settingsDialog;
|
QPointer<SettingsDialog> _settingsDialog;
|
||||||
#endif
|
|
||||||
QPointer<LogBrowser> _logBrowser;
|
QPointer<LogBrowser> _logBrowser;
|
||||||
// tray's menu
|
// tray's menu
|
||||||
QScopedPointer<QMenu> _contextMenu;
|
QScopedPointer<QMenu> _contextMenu;
|
||||||
|
|||||||
@@ -331,7 +331,7 @@ void OwncloudSetupWizard::slotConnectToOCUrl(const QString &url)
|
|||||||
AbstractCredentials *creds = _ocWizard->getCredentials();
|
AbstractCredentials *creds = _ocWizard->getCredentials();
|
||||||
_ocWizard->account()->setCredentials(creds);
|
_ocWizard->account()->setCredentials(creds);
|
||||||
_ocWizard->setField(QLatin1String("OCUrl"), url);
|
_ocWizard->setField(QLatin1String("OCUrl"), url);
|
||||||
_ocWizard->appendToConfigurationLog(tr("Trying to connect to %1 at %2...")
|
_ocWizard->appendToConfigurationLog(tr("Trying to connect to %1 at %2 …")
|
||||||
.arg(Theme::instance()->appNameGUI())
|
.arg(Theme::instance()->appNameGUI())
|
||||||
.arg(url));
|
.arg(url));
|
||||||
|
|
||||||
@@ -404,10 +404,11 @@ void OwncloudSetupWizard::slotAuthError()
|
|||||||
|
|
||||||
// Something else went wrong, maybe the response was 200 but with invalid data.
|
// Something else went wrong, maybe the response was 200 but with invalid data.
|
||||||
} else {
|
} else {
|
||||||
errorMsg = tr("There was an invalid response to an authenticated webdav request");
|
errorMsg = tr("There was an invalid response to an authenticated WebDAV request");
|
||||||
}
|
}
|
||||||
|
|
||||||
_ocWizard->show();
|
// bring wizard to top
|
||||||
|
_ocWizard->bringToTop();
|
||||||
if (_ocWizard->currentId() == WizardCommon::Page_ShibbolethCreds || _ocWizard->currentId() == WizardCommon::Page_OAuthCreds || _ocWizard->currentId() == WizardCommon::Page_Flow2AuthCreds) {
|
if (_ocWizard->currentId() == WizardCommon::Page_ShibbolethCreds || _ocWizard->currentId() == WizardCommon::Page_OAuthCreds || _ocWizard->currentId() == WizardCommon::Page_Flow2AuthCreds) {
|
||||||
_ocWizard->back();
|
_ocWizard->back();
|
||||||
}
|
}
|
||||||
@@ -452,7 +453,7 @@ void OwncloudSetupWizard::slotCreateLocalAndRemoteFolders(const QString &localFo
|
|||||||
tr("Local sync folder %1 already exists, setting it up for sync.<br/><br/>")
|
tr("Local sync folder %1 already exists, setting it up for sync.<br/><br/>")
|
||||||
.arg(Utility::escape(localFolder)));
|
.arg(Utility::escape(localFolder)));
|
||||||
} else {
|
} else {
|
||||||
QString res = tr("Creating local sync folder %1...").arg(localFolder);
|
QString res = tr("Creating local sync folder %1 …").arg(localFolder);
|
||||||
if (fi.mkpath(localFolder)) {
|
if (fi.mkpath(localFolder)) {
|
||||||
FileSystem::setFolderMinimumPermissions(localFolder);
|
FileSystem::setFolderMinimumPermissions(localFolder);
|
||||||
Utility::setupFavLink(localFolder);
|
Utility::setupFavLink(localFolder);
|
||||||
|
|||||||
@@ -1,121 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) by Daniel Molkentin <danimo@owncloud.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "quotainfo.h"
|
|
||||||
#include "account.h"
|
|
||||||
#include "accountstate.h"
|
|
||||||
#include "networkjobs.h"
|
|
||||||
#include "folderman.h"
|
|
||||||
#include "creds/abstractcredentials.h"
|
|
||||||
#include <theme.h>
|
|
||||||
|
|
||||||
#include <QTimer>
|
|
||||||
|
|
||||||
namespace OCC {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
static const int defaultIntervalT = 30 * 1000;
|
|
||||||
static const int failIntervalT = 5 * 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
QuotaInfo::QuotaInfo(AccountState *accountState, QObject *parent)
|
|
||||||
: QObject(parent)
|
|
||||||
, _accountState(accountState)
|
|
||||||
, _lastQuotaTotalBytes(0)
|
|
||||||
, _lastQuotaUsedBytes(0)
|
|
||||||
, _active(false)
|
|
||||||
{
|
|
||||||
connect(accountState, &AccountState::stateChanged,
|
|
||||||
this, &QuotaInfo::slotAccountStateChanged);
|
|
||||||
connect(&_jobRestartTimer, &QTimer::timeout, this, &QuotaInfo::slotCheckQuota);
|
|
||||||
_jobRestartTimer.setSingleShot(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuotaInfo::setActive(bool active)
|
|
||||||
{
|
|
||||||
_active = active;
|
|
||||||
slotAccountStateChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void QuotaInfo::slotAccountStateChanged()
|
|
||||||
{
|
|
||||||
if (canGetQuota()) {
|
|
||||||
auto elapsed = _lastQuotaRecieved.msecsTo(QDateTime::currentDateTime());
|
|
||||||
if (_lastQuotaRecieved.isNull() || elapsed >= defaultIntervalT) {
|
|
||||||
slotCheckQuota();
|
|
||||||
} else {
|
|
||||||
_jobRestartTimer.start(defaultIntervalT - elapsed);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_jobRestartTimer.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuotaInfo::slotRequestFailed()
|
|
||||||
{
|
|
||||||
_lastQuotaTotalBytes = 0;
|
|
||||||
_lastQuotaUsedBytes = 0;
|
|
||||||
_jobRestartTimer.start(failIntervalT);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QuotaInfo::canGetQuota() const
|
|
||||||
{
|
|
||||||
if (!_accountState || !_active) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
AccountPtr account = _accountState->account();
|
|
||||||
return _accountState->isConnected()
|
|
||||||
&& account->credentials()
|
|
||||||
&& account->credentials()->ready();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString QuotaInfo::quotaBaseFolder() const
|
|
||||||
{
|
|
||||||
return Theme::instance()->quotaBaseFolder();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuotaInfo::slotCheckQuota()
|
|
||||||
{
|
|
||||||
if (!canGetQuota()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_job) {
|
|
||||||
// The previous job was not finished? Then we cancel it!
|
|
||||||
_job->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
AccountPtr account = _accountState->account();
|
|
||||||
_job = new PropfindJob(account, quotaBaseFolder(), this);
|
|
||||||
_job->setProperties(QList<QByteArray>() << "quota-available-bytes"
|
|
||||||
<< "quota-used-bytes");
|
|
||||||
connect(_job.data(), &PropfindJob::result, this, &QuotaInfo::slotUpdateLastQuota);
|
|
||||||
connect(_job.data(), &AbstractNetworkJob::networkError, this, &QuotaInfo::slotRequestFailed);
|
|
||||||
_job->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuotaInfo::slotUpdateLastQuota(const QVariantMap &result)
|
|
||||||
{
|
|
||||||
// The server can return fractional bytes (#1374)
|
|
||||||
// <d:quota-available-bytes>1374532061.2</d:quota-available-bytes>
|
|
||||||
qint64 avail = result["quota-available-bytes"].toDouble();
|
|
||||||
_lastQuotaUsedBytes = result["quota-used-bytes"].toDouble();
|
|
||||||
// negative value of the available quota have special meaning (#3940)
|
|
||||||
_lastQuotaTotalBytes = avail >= 0 ? _lastQuotaUsedBytes + avail : avail;
|
|
||||||
emit quotaUpdated(_lastQuotaTotalBytes, _lastQuotaUsedBytes);
|
|
||||||
_jobRestartTimer.start(defaultIntervalT);
|
|
||||||
_lastQuotaRecieved = QDateTime::currentDateTime();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -100,6 +100,18 @@ void RemoteWipe::checkJobSlot()
|
|||||||
auto accountState = manager->account(_account->displayName()).data();
|
auto accountState = manager->account(_account->displayName()).data();
|
||||||
|
|
||||||
if(wipe){
|
if(wipe){
|
||||||
|
/* IMPORTANT - remove later - FIXME MS@2019-12-07 -->
|
||||||
|
* TODO: For "Log out" & "Remove account": Remove client CA certs and KEY!
|
||||||
|
*
|
||||||
|
* Disabled as long as selecting another cert is not supported by the UI.
|
||||||
|
*
|
||||||
|
* Being able to specify a new certificate is important anyway: expiry etc.
|
||||||
|
*
|
||||||
|
* We introduce this dirty hack here, to allow deleting them upon Remote Wipe.
|
||||||
|
*/
|
||||||
|
_account->setRemoteWipeRequested_HACK();
|
||||||
|
// <-- FIXME MS@2019-12-07
|
||||||
|
|
||||||
// delete account
|
// delete account
|
||||||
manager->deleteAccount(accountState);
|
manager->deleteAccount(accountState);
|
||||||
manager->save();
|
manager->save();
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ SelectiveSyncWidget::SelectiveSyncWidget(AccountPtr account, QWidget *parent)
|
|||||||
, _inserting(false)
|
, _inserting(false)
|
||||||
, _folderTree(new QTreeWidget(this))
|
, _folderTree(new QTreeWidget(this))
|
||||||
{
|
{
|
||||||
_loading = new QLabel(tr("Loading ..."), _folderTree);
|
_loading = new QLabel(tr("Loading …"), _folderTree);
|
||||||
|
|
||||||
auto layout = new QVBoxLayout(this);
|
auto layout = new QVBoxLayout(this);
|
||||||
layout->setContentsMargins(0, 0, 0, 0);
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|||||||
@@ -56,10 +56,6 @@ namespace OCC {
|
|||||||
|
|
||||||
#include "settingsdialogcommon.cpp"
|
#include "settingsdialogcommon.cpp"
|
||||||
|
|
||||||
//
|
|
||||||
// Whenever you change something here check both settingsdialog.cpp and settingsdialogmac.cpp !
|
|
||||||
//
|
|
||||||
|
|
||||||
SettingsDialog::SettingsDialog(ownCloudGui *gui, QWidget *parent)
|
SettingsDialog::SettingsDialog(ownCloudGui *gui, QWidget *parent)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
, _ui(new Ui::SettingsDialog)
|
, _ui(new Ui::SettingsDialog)
|
||||||
@@ -108,6 +104,9 @@ SettingsDialog::SettingsDialog(ownCloudGui *gui, QWidget *parent)
|
|||||||
GeneralSettings *generalSettings = new GeneralSettings;
|
GeneralSettings *generalSettings = new GeneralSettings;
|
||||||
_ui->stack->addWidget(generalSettings);
|
_ui->stack->addWidget(generalSettings);
|
||||||
|
|
||||||
|
// Connect styleChanged events to our widgets, so they can adapt (Dark-/Light-Mode switching)
|
||||||
|
connect(this, &SettingsDialog::styleChanged, generalSettings, &GeneralSettings::slotStyleChanged);
|
||||||
|
|
||||||
QAction *networkAction = createColorAwareAction(QLatin1String(":/client/resources/network.png"), tr("Network"));
|
QAction *networkAction = createColorAwareAction(QLatin1String(":/client/resources/network.png"), tr("Network"));
|
||||||
_actionGroup->addAction(networkAction);
|
_actionGroup->addAction(networkAction);
|
||||||
_toolBar->addAction(networkAction);
|
_toolBar->addAction(networkAction);
|
||||||
@@ -128,6 +127,8 @@ SettingsDialog::SettingsDialog(ownCloudGui *gui, QWidget *parent)
|
|||||||
connect(showLogWindow, &QAction::triggered, gui, &ownCloudGui::slotToggleLogBrowser);
|
connect(showLogWindow, &QAction::triggered, gui, &ownCloudGui::slotToggleLogBrowser);
|
||||||
addAction(showLogWindow);
|
addAction(showLogWindow);
|
||||||
|
|
||||||
|
connect(this, &SettingsDialog::onActivate, gui, &ownCloudGui::slotSettingsDialogActivated);
|
||||||
|
|
||||||
customizeStyle();
|
customizeStyle();
|
||||||
|
|
||||||
cfg.restoreGeometry(this);
|
cfg.restoreGeometry(this);
|
||||||
@@ -160,6 +161,13 @@ void SettingsDialog::changeEvent(QEvent *e)
|
|||||||
case QEvent::PaletteChange:
|
case QEvent::PaletteChange:
|
||||||
case QEvent::ThemeChange:
|
case QEvent::ThemeChange:
|
||||||
customizeStyle();
|
customizeStyle();
|
||||||
|
|
||||||
|
// Notify the other widgets (Dark-/Light-Mode switching)
|
||||||
|
emit styleChanged();
|
||||||
|
break;
|
||||||
|
case QEvent::ActivationChange:
|
||||||
|
if(isActiveWindow())
|
||||||
|
emit onActivate();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -263,6 +271,10 @@ void SettingsDialog::accountAdded(AccountState *s)
|
|||||||
// Refresh immediatly when getting online
|
// Refresh immediatly when getting online
|
||||||
connect(s, &AccountState::isConnectedChanged, this, &SettingsDialog::slotRefreshActivityAccountStateSender);
|
connect(s, &AccountState::isConnectedChanged, this, &SettingsDialog::slotRefreshActivityAccountStateSender);
|
||||||
|
|
||||||
|
// Connect styleChanged event, to adapt (Dark-/Light-Mode switching)
|
||||||
|
connect(this, &SettingsDialog::styleChanged, accountSettings, &AccountSettings::slotStyleChanged);
|
||||||
|
connect(this, &SettingsDialog::styleChanged, _activitySettings[s], &ActivitySettings::slotStyleChanged);
|
||||||
|
|
||||||
activityAdded(s);
|
activityAdded(s);
|
||||||
slotRefreshActivity(s);
|
slotRefreshActivity(s);
|
||||||
}
|
}
|
||||||
@@ -344,13 +356,13 @@ void SettingsDialog::accountRemoved(AccountState *s)
|
|||||||
void SettingsDialog::customizeStyle()
|
void SettingsDialog::customizeStyle()
|
||||||
{
|
{
|
||||||
QString highlightColor(palette().highlight().color().name());
|
QString highlightColor(palette().highlight().color().name());
|
||||||
QString altBase(palette().alternateBase().color().name());
|
QString highlightTextColor(palette().highlightedText().color().name());
|
||||||
QString dark(palette().dark().color().name());
|
QString dark(palette().dark().color().name());
|
||||||
QString background(palette().base().color().name());
|
QString background(palette().base().color().name());
|
||||||
_toolBar->setStyleSheet(QString::fromLatin1(TOOLBAR_CSS).arg(background, dark, highlightColor, altBase));
|
_toolBar->setStyleSheet(QString::fromLatin1(TOOLBAR_CSS).arg(background, dark, highlightColor, highlightTextColor));
|
||||||
|
|
||||||
Q_FOREACH (QAction *a, _actionGroup->actions()) {
|
Q_FOREACH (QAction *a, _actionGroup->actions()) {
|
||||||
QIcon icon = createColorAwareIcon(a->property("iconPath").toString());
|
QIcon icon = Theme::createColorAwareIcon(a->property("iconPath").toString(), palette());
|
||||||
a->setIcon(icon);
|
a->setIcon(icon);
|
||||||
QToolButton *btn = qobject_cast<QToolButton *>(_toolBar->widgetForAction(a));
|
QToolButton *btn = qobject_cast<QToolButton *>(_toolBar->widgetForAction(a));
|
||||||
if (btn)
|
if (btn)
|
||||||
@@ -358,34 +370,6 @@ void SettingsDialog::customizeStyle()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isDarkColor(const QColor &color)
|
|
||||||
{
|
|
||||||
// account for different sensitivity of the human eye to certain colors
|
|
||||||
double treshold = 1.0 - (0.299 * color.red() + 0.587 * color.green() + 0.114 * color.blue()) / 255.0;
|
|
||||||
return treshold > 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
QIcon SettingsDialog::createColorAwareIcon(const QString &name)
|
|
||||||
{
|
|
||||||
QPalette pal = palette();
|
|
||||||
QImage img(name);
|
|
||||||
QImage inverted(img);
|
|
||||||
inverted.invertPixels(QImage::InvertRgb);
|
|
||||||
|
|
||||||
QIcon icon;
|
|
||||||
if (isDarkColor(pal.color(QPalette::Base))) {
|
|
||||||
icon.addPixmap(QPixmap::fromImage(inverted));
|
|
||||||
} else {
|
|
||||||
icon.addPixmap(QPixmap::fromImage(img));
|
|
||||||
}
|
|
||||||
if (isDarkColor(pal.color(QPalette::HighlightedText))) {
|
|
||||||
icon.addPixmap(QPixmap::fromImage(img), QIcon::Normal, QIcon::On);
|
|
||||||
} else {
|
|
||||||
icon.addPixmap(QPixmap::fromImage(inverted), QIcon::Normal, QIcon::On);
|
|
||||||
}
|
|
||||||
return icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
class ToolButtonAction : public QWidgetAction
|
class ToolButtonAction : public QWidgetAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -426,7 +410,7 @@ QAction *SettingsDialog::createActionWithIcon(const QIcon &icon, const QString &
|
|||||||
QAction *SettingsDialog::createColorAwareAction(const QString &iconPath, const QString &text)
|
QAction *SettingsDialog::createColorAwareAction(const QString &iconPath, const QString &text)
|
||||||
{
|
{
|
||||||
// all buttons must have the same size in order to keep a good layout
|
// all buttons must have the same size in order to keep a good layout
|
||||||
QIcon coloredIcon = createColorAwareIcon(iconPath);
|
QIcon coloredIcon = Theme::createColorAwareIcon(iconPath, palette());
|
||||||
return createActionWithIcon(coloredIcon, text, iconPath);
|
return createActionWithIcon(coloredIcon, text, iconPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,10 @@ public slots:
|
|||||||
void slotAccountAvatarChanged();
|
void slotAccountAvatarChanged();
|
||||||
void slotAccountDisplayNameChanged();
|
void slotAccountDisplayNameChanged();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void styleChanged();
|
||||||
|
void onActivate();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void reject() override;
|
void reject() override;
|
||||||
void accept() override;
|
void accept() override;
|
||||||
@@ -76,7 +80,6 @@ private:
|
|||||||
void customizeStyle();
|
void customizeStyle();
|
||||||
void activityAdded(AccountState *);
|
void activityAdded(AccountState *);
|
||||||
|
|
||||||
QIcon createColorAwareIcon(const QString &name);
|
|
||||||
QAction *createColorAwareAction(const QString &iconName, const QString &fileName);
|
QAction *createColorAwareAction(const QString &iconName, const QString &fileName);
|
||||||
QAction *createActionWithIcon(const QIcon &icon, const QString &text, const QString &iconPath = QString());
|
QAction *createActionWithIcon(const QIcon &icon, const QString &text, const QString &iconPath = QString());
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ namespace SettingsDialogCommon
|
|||||||
{
|
{
|
||||||
|
|
||||||
/** display name with two lines that is displayed in the settings
|
/** display name with two lines that is displayed in the settings
|
||||||
* If width is bigger than 0, the string will be ellided so it does not exceed that width
|
* If width is bigger than 0, the string will be elided so it does not exceed that width
|
||||||
*/
|
*/
|
||||||
QString shortDisplayNameForSettings(Account* account, int width)
|
QString shortDisplayNameForSettings(Account* account, int width)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,239 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) by Denis Dzyubenko
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "settingsdialogmac.h"
|
|
||||||
|
|
||||||
#include "macstandardicon.h"
|
|
||||||
|
|
||||||
#include "folderman.h"
|
|
||||||
#include "theme.h"
|
|
||||||
#include "generalsettings.h"
|
|
||||||
#include "networksettings.h"
|
|
||||||
#include "accountsettings.h"
|
|
||||||
#include "accountstate.h"
|
|
||||||
#include "creds/abstractcredentials.h"
|
|
||||||
#include "configfile.h"
|
|
||||||
#include "progressdispatcher.h"
|
|
||||||
#include "owncloudgui.h"
|
|
||||||
#include "activitywidget.h"
|
|
||||||
#include "accountmanager.h"
|
|
||||||
|
|
||||||
#include <QLabel>
|
|
||||||
#include <QStandardItemModel>
|
|
||||||
#include <QPushButton>
|
|
||||||
#include <QSettings>
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QPainterPath>
|
|
||||||
|
|
||||||
namespace OCC {
|
|
||||||
|
|
||||||
#include "settingsdialogcommon.cpp"
|
|
||||||
|
|
||||||
|
|
||||||
// Duplicate in settingsdialog.cpp
|
|
||||||
static QIcon circleMask(const QImage &avatar)
|
|
||||||
{
|
|
||||||
int dim = avatar.width();
|
|
||||||
|
|
||||||
QPixmap fixedImage(dim, dim);
|
|
||||||
fixedImage.fill(Qt::transparent);
|
|
||||||
|
|
||||||
QPainter imgPainter(&fixedImage);
|
|
||||||
QPainterPath clip;
|
|
||||||
clip.addEllipse(0, 0, dim, dim);
|
|
||||||
imgPainter.setClipPath(clip);
|
|
||||||
imgPainter.drawImage(0, 0, avatar);
|
|
||||||
imgPainter.end();
|
|
||||||
|
|
||||||
return QIcon(fixedImage);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Whenever you change something here check both settingsdialog.cpp and settingsdialogmac.cpp !
|
|
||||||
//
|
|
||||||
SettingsDialogMac::SettingsDialogMac(ownCloudGui *gui, QWidget *parent)
|
|
||||||
: MacPreferencesWindow(parent)
|
|
||||||
, _gui(gui)
|
|
||||||
{
|
|
||||||
// do not show minimize button. There is no use, and restoring the
|
|
||||||
// dialog from minimize is broken in MacPreferencesWindow
|
|
||||||
setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint | Qt::WindowMaximizeButtonHint);
|
|
||||||
|
|
||||||
// Emulate dialog behavior: Escape means close
|
|
||||||
QAction *closeDialogAction = new QAction(this);
|
|
||||||
closeDialogAction->setShortcut(QKeySequence(Qt::Key_Escape));
|
|
||||||
connect(closeDialogAction, &QAction::triggered, this, &SettingsDialogMac::close);
|
|
||||||
addAction(closeDialogAction);
|
|
||||||
// People perceive this as a Window, so also make Ctrl+W work
|
|
||||||
QAction *closeWindowAction = new QAction(this);
|
|
||||||
closeWindowAction->setShortcut(QKeySequence("Ctrl+W"));
|
|
||||||
connect(closeWindowAction, &QAction::triggered, this, &SettingsDialogMac::close);
|
|
||||||
addAction(closeWindowAction);
|
|
||||||
// People perceive this as a Window, so also make Ctrl+H work
|
|
||||||
QAction *hideWindowAction = new QAction(this);
|
|
||||||
hideWindowAction->setShortcut(QKeySequence("Ctrl+H"));
|
|
||||||
connect(hideWindowAction, &QAction::triggered, this, &SettingsDialogMac::hide);
|
|
||||||
addAction(hideWindowAction);
|
|
||||||
|
|
||||||
setObjectName("SettingsMac"); // required as group for saveGeometry call
|
|
||||||
|
|
||||||
setWindowTitle(tr("%1").arg(Theme::instance()->appNameGUI()));
|
|
||||||
|
|
||||||
connect(AccountManager::instance(), &AccountManager::accountAdded,
|
|
||||||
this, &SettingsDialogMac::accountAdded);
|
|
||||||
connect(AccountManager::instance(), &AccountManager::accountRemoved,
|
|
||||||
this, &SettingsDialogMac::accountRemoved);
|
|
||||||
|
|
||||||
_actionsIdx = -1;
|
|
||||||
foreach (auto ai, AccountManager::instance()->accounts()) {
|
|
||||||
accountAdded(ai.data());
|
|
||||||
}
|
|
||||||
|
|
||||||
QIcon generalIcon = MacStandardIcon::icon(MacStandardIcon::PreferencesGeneral);
|
|
||||||
GeneralSettings *generalSettings = new GeneralSettings;
|
|
||||||
addPreferencesPanel(generalIcon, tr("General"), generalSettings);
|
|
||||||
|
|
||||||
QIcon networkIcon = MacStandardIcon::icon(MacStandardIcon::Network);
|
|
||||||
NetworkSettings *networkSettings = new NetworkSettings;
|
|
||||||
addPreferencesPanel(networkIcon, tr("Network"), networkSettings);
|
|
||||||
|
|
||||||
QAction *showLogWindow = new QAction(this);
|
|
||||||
showLogWindow->setShortcut(QKeySequence("F12"));
|
|
||||||
connect(showLogWindow, &QAction::triggered, gui, &ownCloudGui::slotToggleLogBrowser);
|
|
||||||
addAction(showLogWindow);
|
|
||||||
|
|
||||||
ConfigFile cfg;
|
|
||||||
cfg.restoreGeometry(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SettingsDialogMac::closeEvent(QCloseEvent *event)
|
|
||||||
{
|
|
||||||
ConfigFile cfg;
|
|
||||||
cfg.saveGeometry(this);
|
|
||||||
MacPreferencesWindow::closeEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SettingsDialogMac::showActivityPage()
|
|
||||||
{
|
|
||||||
// Count backwards (0-based) from the last panel (multiple accounts can be on the left)
|
|
||||||
setCurrentPanelIndex(preferencePanelCount() - 1 - 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SettingsDialogMac::accountAdded(AccountState *s)
|
|
||||||
{
|
|
||||||
QIcon accountIcon = MacStandardIcon::icon(MacStandardIcon::UserAccounts);
|
|
||||||
auto accountSettings = new AccountSettings(s, this);
|
|
||||||
QString displayName = Theme::instance()->multiAccount() ? SettingsDialogCommon::shortDisplayNameForSettings(s->account().data(), 0) : tr("Account");
|
|
||||||
|
|
||||||
// this adds the panel - nothing to add here just to fix the order
|
|
||||||
insertPreferencesPanel(++_actionsIdx, accountIcon, displayName, accountSettings);
|
|
||||||
|
|
||||||
connect(accountSettings, &AccountSettings::folderChanged, _gui, &ownCloudGui::slotFoldersChanged);
|
|
||||||
connect(accountSettings, &AccountSettings::openFolderAlias, _gui, &ownCloudGui::slotFolderOpenAction);
|
|
||||||
|
|
||||||
connect(s->account().data(), &Account::accountChangedAvatar, this, &SettingsDialogMac::slotAccountAvatarChanged);
|
|
||||||
connect(s->account().data(), &Account::accountChangedDisplayName, this, &SettingsDialogMac::slotAccountDisplayNameChanged);
|
|
||||||
|
|
||||||
// Refresh immediatly when getting online
|
|
||||||
connect(s, &AccountState::isConnectedChanged, this, &SettingsDialogMac::slotRefreshActivityAccountStateSender);
|
|
||||||
|
|
||||||
// Add activity panel
|
|
||||||
QIcon activityIcon(QLatin1String(":/client/resources/activity.png"));
|
|
||||||
_activitySettings[s] = new ActivitySettings(s, this);
|
|
||||||
insertPreferencesPanel(++_actionsIdx, activityIcon, tr("Activity"), _activitySettings[s]);
|
|
||||||
connect(_activitySettings[s], SIGNAL(guiLog(QString, QString)), _gui,
|
|
||||||
SLOT(slotShowOptionalTrayMessage(QString, QString)));
|
|
||||||
|
|
||||||
// if this is not the first account, add separator 2 positions before int the toolbar
|
|
||||||
if(AccountManager::instance()->accounts().first().data() != s &&
|
|
||||||
AccountManager::instance()->accounts().size() >= 1){
|
|
||||||
_separators[s] = insertSeparator(_actionsIdx - 1);
|
|
||||||
++_actionsIdx; //we have one more item in the toolbar
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigFile cfg;
|
|
||||||
_activitySettings[s]->setNotificationRefreshInterval(cfg.notificationRefreshInterval());
|
|
||||||
|
|
||||||
slotRefreshActivity(s);
|
|
||||||
setCurrentPanelIndex(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SettingsDialogMac::accountRemoved(AccountState *s)
|
|
||||||
{
|
|
||||||
auto list = findChildren<AccountSettings *>(QString());
|
|
||||||
foreach (auto p, list) {
|
|
||||||
if (p->accountsState() == s) {
|
|
||||||
removePreferencesPanel(p);
|
|
||||||
|
|
||||||
// remove settings panel
|
|
||||||
if(_activitySettings.contains(s))
|
|
||||||
removePreferencesPanel(_activitySettings[s]);
|
|
||||||
|
|
||||||
// remove separator if there is any
|
|
||||||
if(_separators.contains(s)){
|
|
||||||
removeSeparator(_separators[s]);
|
|
||||||
_separators.remove(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SettingsDialogMac::slotRefreshActivityAccountStateSender()
|
|
||||||
{
|
|
||||||
slotRefreshActivity(qobject_cast<AccountState*>(sender()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SettingsDialogMac::slotRefreshActivity(AccountState *accountState)
|
|
||||||
{
|
|
||||||
if (accountState) {
|
|
||||||
_activitySettings[accountState]->slotRefresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SettingsDialogMac::slotAccountAvatarChanged()
|
|
||||||
{
|
|
||||||
Account *account = static_cast<Account *>(sender());
|
|
||||||
auto list = findChildren<AccountSettings *>(QString());
|
|
||||||
foreach (auto p, list) {
|
|
||||||
if (p->accountsState()->account() == account) {
|
|
||||||
int idx = indexForPanel(p);
|
|
||||||
QImage pix = account->avatar();
|
|
||||||
if (!pix.isNull()) {
|
|
||||||
setPreferencesPanelIcon(idx, circleMask(pix));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SettingsDialogMac::slotAccountDisplayNameChanged()
|
|
||||||
{
|
|
||||||
Account *account = static_cast<Account *>(sender());
|
|
||||||
auto list = findChildren<AccountSettings *>(QString());
|
|
||||||
foreach (auto p, list) {
|
|
||||||
if (p->accountsState()->account() == account) {
|
|
||||||
int idx = indexForPanel(p);
|
|
||||||
QString displayName = account->displayName();
|
|
||||||
if (!displayName.isNull()) {
|
|
||||||
displayName = Theme::instance()->multiAccount()
|
|
||||||
? SettingsDialogCommon::shortDisplayNameForSettings(account, 0)
|
|
||||||
: tr("Account");
|
|
||||||
setPreferencesPanelTitle(idx, displayName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) by Denis Dzyubenko
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef SETTINGSDIALOGMAC_H
|
|
||||||
#define SETTINGSDIALOGMAC_H
|
|
||||||
|
|
||||||
#include "progressdispatcher.h"
|
|
||||||
#include "macpreferenceswindow.h"
|
|
||||||
#include "owncloudgui.h"
|
|
||||||
|
|
||||||
class QStandardItemModel;
|
|
||||||
class QListWidgetItem;
|
|
||||||
|
|
||||||
namespace OCC {
|
|
||||||
|
|
||||||
class AccountSettings;
|
|
||||||
class Application;
|
|
||||||
class FolderMan;
|
|
||||||
class ownCloudGui;
|
|
||||||
class Folder;
|
|
||||||
class AccountState;
|
|
||||||
class ActivitySettings;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The SettingsDialogMac class
|
|
||||||
* @ingroup gui
|
|
||||||
*/
|
|
||||||
class SettingsDialogMac : public MacPreferencesWindow
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit SettingsDialogMac(ownCloudGui *gui, QWidget *parent = 0);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void showActivityPage();
|
|
||||||
void slotRefreshActivity(AccountState *accountState);
|
|
||||||
void slotRefreshActivityAccountStateSender();
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void accountAdded(AccountState *);
|
|
||||||
void accountRemoved(AccountState *);
|
|
||||||
void slotAccountAvatarChanged();
|
|
||||||
void slotAccountDisplayNameChanged();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void closeEvent(QCloseEvent *event);
|
|
||||||
|
|
||||||
QAction *_actionBefore;
|
|
||||||
int _actionsIdx;
|
|
||||||
QMap<AccountState *, QAction *> _separators;
|
|
||||||
|
|
||||||
QMap<AccountState *, ActivitySettings *> _activitySettings;
|
|
||||||
ownCloudGui *_gui;
|
|
||||||
|
|
||||||
int _protocolIdx;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // SETTINGSDIALOGMAC_H
|
|
||||||
;
|
|
||||||
@@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QFileIconProvider>
|
#include <QFileIconProvider>
|
||||||
|
#include <QInputDialog>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QFrame>
|
#include <QFrame>
|
||||||
@@ -137,6 +138,7 @@ ShareDialog::ShareDialog(QPointer<AccountState> accountState,
|
|||||||
_manager = new ShareManager(accountState->account(), this);
|
_manager = new ShareManager(accountState->account(), this);
|
||||||
connect(_manager, &ShareManager::sharesFetched, this, &ShareDialog::slotSharesFetched);
|
connect(_manager, &ShareManager::sharesFetched, this, &ShareDialog::slotSharesFetched);
|
||||||
connect(_manager, &ShareManager::linkShareCreated, this, &ShareDialog::slotAddLinkShareWidget);
|
connect(_manager, &ShareManager::linkShareCreated, this, &ShareDialog::slotAddLinkShareWidget);
|
||||||
|
connect(_manager, &ShareManager::linkShareRequiresPassword, this, &ShareDialog::slotLinkShareRequiresPassword);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,6 +158,9 @@ void ShareDialog::addLinkShareWidget(const QSharedPointer<LinkShare> &linkShare)
|
|||||||
connect(_linkWidgetList.at(index), &ShareLinkWidget::deleteLinkShare, this, &ShareDialog::slotDeleteShare);
|
connect(_linkWidgetList.at(index), &ShareLinkWidget::deleteLinkShare, this, &ShareDialog::slotDeleteShare);
|
||||||
//connect(_linkWidgetList.at(index), &ShareLinkWidget::resizeRequested, this, &ShareDialog::slotAdjustScrollWidgetSize);
|
//connect(_linkWidgetList.at(index), &ShareLinkWidget::resizeRequested, this, &ShareDialog::slotAdjustScrollWidgetSize);
|
||||||
|
|
||||||
|
// Connect styleChanged events to our widget, so it can adapt (Dark-/Light-Mode switching)
|
||||||
|
connect(this, &ShareDialog::styleChanged, _linkWidgetList.at(index), &ShareLinkWidget::slotStyleChanged);
|
||||||
|
|
||||||
_ui->verticalLayout->insertWidget(_linkWidgetList.size()+1, _linkWidgetList.at(index));
|
_ui->verticalLayout->insertWidget(_linkWidgetList.size()+1, _linkWidgetList.at(index));
|
||||||
_linkWidgetList.at(index)->setupUiOptions();
|
_linkWidgetList.at(index)->setupUiOptions();
|
||||||
}
|
}
|
||||||
@@ -282,6 +287,10 @@ void ShareDialog::showSharingUi()
|
|||||||
|
|
||||||
if (userGroupSharing) {
|
if (userGroupSharing) {
|
||||||
_userGroupWidget = new ShareUserGroupWidget(_accountState->account(), _sharePath, _localPath, _maxSharingPermissions, _privateLinkUrl, this);
|
_userGroupWidget = new ShareUserGroupWidget(_accountState->account(), _sharePath, _localPath, _maxSharingPermissions, _privateLinkUrl, this);
|
||||||
|
|
||||||
|
// Connect styleChanged events to our widget, so it can adapt (Dark-/Light-Mode switching)
|
||||||
|
connect(this, &ShareDialog::styleChanged, _userGroupWidget, &ShareUserGroupWidget::slotStyleChanged);
|
||||||
|
|
||||||
_ui->verticalLayout->insertWidget(1, _userGroupWidget);
|
_ui->verticalLayout->insertWidget(1, _userGroupWidget);
|
||||||
_userGroupWidget->getShares();
|
_userGroupWidget->getShares();
|
||||||
}
|
}
|
||||||
@@ -296,6 +305,24 @@ void ShareDialog::slotCreateLinkShare()
|
|||||||
_manager->createLinkShare(_sharePath, QString(), QString());
|
_manager->createLinkShare(_sharePath, QString(), QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShareDialog::slotLinkShareRequiresPassword()
|
||||||
|
{
|
||||||
|
bool ok;
|
||||||
|
QString password = QInputDialog::getText(this,
|
||||||
|
tr("Password for share required"),
|
||||||
|
tr("Please enter a password for your link share:"),
|
||||||
|
QLineEdit::Normal,
|
||||||
|
QString(),
|
||||||
|
&ok);
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
// The dialog was canceled so no need to do anything
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to create the link share again with the newly entered password
|
||||||
|
_manager->createLinkShare(_sharePath, QString(), password);
|
||||||
|
}
|
||||||
|
|
||||||
void ShareDialog::slotDeleteShare()
|
void ShareDialog::slotDeleteShare()
|
||||||
{
|
{
|
||||||
@@ -335,4 +362,21 @@ void ShareDialog::slotAccountStateChanged(int state)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShareDialog::changeEvent(QEvent *e)
|
||||||
|
{
|
||||||
|
switch (e->type()) {
|
||||||
|
case QEvent::StyleChange:
|
||||||
|
case QEvent::PaletteChange:
|
||||||
|
case QEvent::ThemeChange:
|
||||||
|
// Notify the other widgets (Dark-/Light-Mode switching)
|
||||||
|
emit styleChanged();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDialog::changeEvent(e);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,10 +64,15 @@ private slots:
|
|||||||
void slotAddLinkShareWidget(const QSharedPointer<LinkShare> &linkShare);
|
void slotAddLinkShareWidget(const QSharedPointer<LinkShare> &linkShare);
|
||||||
void slotDeleteShare();
|
void slotDeleteShare();
|
||||||
void slotCreateLinkShare();
|
void slotCreateLinkShare();
|
||||||
|
void slotLinkShareRequiresPassword();
|
||||||
void slotAdjustScrollWidgetSize();
|
void slotAdjustScrollWidgetSize();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void toggleAnimation(bool);
|
void toggleAnimation(bool);
|
||||||
|
void styleChanged();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void changeEvent(QEvent *) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void showSharingUi();
|
void showSharingUi();
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include "capabilities.h"
|
#include "capabilities.h"
|
||||||
#include "guiutility.h"
|
#include "guiutility.h"
|
||||||
#include "sharemanager.h"
|
#include "sharemanager.h"
|
||||||
|
#include "theme.h"
|
||||||
|
|
||||||
#include "QProgressIndicator.h"
|
#include "QProgressIndicator.h"
|
||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
@@ -240,6 +241,8 @@ void ShareLinkWidget::setupUiOptions(){
|
|||||||
|
|
||||||
//TO DO
|
//TO DO
|
||||||
//startAnimation(0, height());
|
//startAnimation(0, height());
|
||||||
|
|
||||||
|
customizeStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShareLinkWidget::slotCopyLinkShare(bool clicked){
|
void ShareLinkWidget::slotCopyLinkShare(bool clicked){
|
||||||
@@ -489,4 +492,32 @@ void ShareLinkWidget::displayError(const QString &errMsg)
|
|||||||
_ui->errorLabel->setText(errMsg);
|
_ui->errorLabel->setText(errMsg);
|
||||||
_ui->errorLabel->show();
|
_ui->errorLabel->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShareLinkWidget::slotStyleChanged()
|
||||||
|
{
|
||||||
|
customizeStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShareLinkWidget::customizeStyle()
|
||||||
|
{
|
||||||
|
_unshareLinkAction->setIcon(Theme::createColorAwareIcon(":/client/resources/delete.png"));
|
||||||
|
|
||||||
|
_addAnotherLinkAction->setIcon(Theme::createColorAwareIcon(":/client/resources/add.png"));
|
||||||
|
|
||||||
|
_ui->enableShareLink->setIcon(Theme::createColorAwareIcon(":/client/resources/copy.svg"));
|
||||||
|
|
||||||
|
// only on master, not in stable-2.6 yet
|
||||||
|
// _ui->shareLinkIconLabel->setPixmap(Theme::createColorAwarePixmap(":/client/resources/public.svg"));
|
||||||
|
_ui->createShareButton->setIcon(Theme::createColorAwareIcon(":/client/resources/public.svg"));
|
||||||
|
|
||||||
|
_ui->shareLinkToolButton->setIcon(Theme::createColorAwareIcon(":/client/resources/more.svg"));
|
||||||
|
|
||||||
|
// only on master, not in stable-2.6 yet
|
||||||
|
// _ui->confirmNote->setIcon(Theme::createColorAwareIcon(":/client/resources/confirm.svg"));
|
||||||
|
_ui->confirmPassword->setIcon(Theme::createColorAwareIcon(":/client/resources/confirm.svg"));
|
||||||
|
_ui->confirmExpirationDate->setIcon(Theme::createColorAwareIcon(":/client/resources/confirm.svg"));
|
||||||
|
|
||||||
|
_ui->progressIndicator->setColor(QGuiApplication::palette().color(QPalette::Text));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ namespace Ui {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class AbstractCredentials;
|
class AbstractCredentials;
|
||||||
class QuotaInfo;
|
|
||||||
class SyncResult;
|
class SyncResult;
|
||||||
class LinkShare;
|
class LinkShare;
|
||||||
class Share;
|
class Share;
|
||||||
@@ -65,6 +64,7 @@ public slots:
|
|||||||
void slotToggleAnimation(bool start);
|
void slotToggleAnimation(bool start);
|
||||||
void slotServerError(int code, const QString &message);
|
void slotServerError(int code, const QString &message);
|
||||||
void slotCreateShareRequiresPassword(const QString &message);
|
void slotCreateShareRequiresPassword(const QString &message);
|
||||||
|
void slotStyleChanged();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void slotCreateShareLink(bool clicked);
|
void slotCreateShareLink(bool clicked);
|
||||||
@@ -107,6 +107,8 @@ private:
|
|||||||
|
|
||||||
void startAnimation(const int start, const int end);
|
void startAnimation(const int start, const int end);
|
||||||
|
|
||||||
|
void customizeStyle();
|
||||||
|
|
||||||
Ui::ShareLinkWidget *_ui;
|
Ui::ShareLinkWidget *_ui;
|
||||||
AccountPtr _account;
|
AccountPtr _account;
|
||||||
QString _sharePath;
|
QString _sharePath;
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#include "thumbnailjob.h"
|
#include "thumbnailjob.h"
|
||||||
#include "sharee.h"
|
#include "sharee.h"
|
||||||
#include "sharemanager.h"
|
#include "sharemanager.h"
|
||||||
|
#include "theme.h"
|
||||||
|
|
||||||
#include "QProgressIndicator.h"
|
#include "QProgressIndicator.h"
|
||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
@@ -112,6 +113,8 @@ ShareUserGroupWidget::ShareUserGroupWidget(AccountPtr account,
|
|||||||
//_ui->shareeHorizontalLayout->addWidget(&_pi_sharee);
|
//_ui->shareeHorizontalLayout->addWidget(&_pi_sharee);
|
||||||
|
|
||||||
_parentScrollArea = parentWidget()->findChild<QScrollArea*>("scrollArea");
|
_parentScrollArea = parentWidget()->findChild<QScrollArea*>("scrollArea");
|
||||||
|
|
||||||
|
customizeStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
ShareUserGroupWidget::~ShareUserGroupWidget()
|
ShareUserGroupWidget::~ShareUserGroupWidget()
|
||||||
@@ -211,6 +214,10 @@ void ShareUserGroupWidget::slotSharesFetched(const QList<QSharedPointer<Share>>
|
|||||||
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);
|
||||||
|
|
||||||
|
// Connect styleChanged events to our widget, so it can adapt (Dark-/Light-Mode switching)
|
||||||
|
connect(this, &ShareUserGroupWidget::styleChanged, s, &ShareUserLine::slotStyleChanged);
|
||||||
|
|
||||||
layout->addWidget(s);
|
layout->addWidget(s);
|
||||||
|
|
||||||
x++;
|
x++;
|
||||||
@@ -255,7 +262,8 @@ void ShareUserGroupWidget::slotPrivateLinkShare()
|
|||||||
auto menu = new QMenu(this);
|
auto menu = new QMenu(this);
|
||||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
|
||||||
menu->addAction(QIcon(":/client/resources/copy.svg"),
|
// this icon is not handled by slotStyleChanged() -> customizeStyle but we can live with that
|
||||||
|
menu->addAction(Theme::createColorAwareIcon(":/client/resources/copy.svg"),
|
||||||
tr("Copy link"),
|
tr("Copy link"),
|
||||||
this, SLOT(slotPrivateLinkCopy()));
|
this, SLOT(slotPrivateLinkCopy()));
|
||||||
|
|
||||||
@@ -358,6 +366,25 @@ void ShareUserGroupWidget::slotPrivateLinkEmail()
|
|||||||
this);
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShareUserGroupWidget::slotStyleChanged()
|
||||||
|
{
|
||||||
|
customizeStyle();
|
||||||
|
|
||||||
|
// Notify the other widgets (ShareUserLine in this case, Dark-/Light-Mode switching)
|
||||||
|
emit styleChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShareUserGroupWidget::customizeStyle()
|
||||||
|
{
|
||||||
|
_ui->confirmShare->setIcon(Theme::createColorAwareIcon(":/client/resources/confirm.svg"));
|
||||||
|
|
||||||
|
_pi_sharee.setColor(QGuiApplication::palette().color(QPalette::Text));
|
||||||
|
|
||||||
|
foreach (auto pi, _parentScrollArea->findChildren<QProgressIndicator *>()) {
|
||||||
|
pi->setColor(QGuiApplication::palette().color(QPalette::Text));;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ShareUserLine::ShareUserLine(QSharedPointer<Share> share,
|
ShareUserLine::ShareUserLine(QSharedPointer<Share> share,
|
||||||
SharePermissions maxSharingPermissions,
|
SharePermissions maxSharingPermissions,
|
||||||
bool isFile,
|
bool isFile,
|
||||||
@@ -423,8 +450,9 @@ ShareUserLine::ShareUserLine(QSharedPointer<Share> share,
|
|||||||
_ui->permissionToolButton->setMenu(menu);
|
_ui->permissionToolButton->setMenu(menu);
|
||||||
_ui->permissionToolButton->setPopupMode(QToolButton::InstantPopup);
|
_ui->permissionToolButton->setPopupMode(QToolButton::InstantPopup);
|
||||||
|
|
||||||
QIcon icon(QLatin1String(":/client/resources/more.svg"));
|
// icon now set in: customizeStyle
|
||||||
_ui->permissionToolButton->setIcon(icon);
|
/*QIcon icon(QLatin1String(":/client/resources/more.svg"));
|
||||||
|
_ui->permissionToolButton->setIcon(icon);*/
|
||||||
|
|
||||||
// Set the permissions checkboxes
|
// Set the permissions checkboxes
|
||||||
displayPermissions();
|
displayPermissions();
|
||||||
@@ -451,6 +479,8 @@ ShareUserLine::ShareUserLine(QSharedPointer<Share> share,
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadAvatar();
|
loadAvatar();
|
||||||
|
|
||||||
|
customizeStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShareUserLine::loadAvatar()
|
void ShareUserLine::loadAvatar()
|
||||||
@@ -645,4 +675,18 @@ void ShareUserLine::displayPermissions()
|
|||||||
_permissionDelete->setChecked(perm & SharePermissionDelete);
|
_permissionDelete->setChecked(perm & SharePermissionDelete);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShareUserLine::slotStyleChanged()
|
||||||
|
{
|
||||||
|
customizeStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShareUserLine::customizeStyle()
|
||||||
|
{
|
||||||
|
_ui->permissionToolButton->setIcon(Theme::createColorAwareIcon(":/client/resources/more.svg"));
|
||||||
|
|
||||||
|
QIcon deleteicon = QIcon::fromTheme(QLatin1String("user-trash"),Theme::createColorAwareIcon(QLatin1String(":/client/resources/delete.png")));
|
||||||
|
_deleteShareButton->setIcon(deleteicon);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ namespace Ui {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class AbstractCredentials;
|
class AbstractCredentials;
|
||||||
class QuotaInfo;
|
|
||||||
class SyncResult;
|
class SyncResult;
|
||||||
class Share;
|
class Share;
|
||||||
class Sharee;
|
class Sharee;
|
||||||
@@ -64,9 +63,11 @@ public:
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void togglePublicLinkShare(bool);
|
void togglePublicLinkShare(bool);
|
||||||
|
void styleChanged();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void getShares();
|
void getShares();
|
||||||
|
void slotStyleChanged();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void slotSharesFetched(const QList<QSharedPointer<Share>> &shares);
|
void slotSharesFetched(const QList<QSharedPointer<Share>> &shares);
|
||||||
@@ -88,6 +89,8 @@ private slots:
|
|||||||
void slotPrivateLinkEmail();
|
void slotPrivateLinkEmail();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void customizeStyle();
|
||||||
|
|
||||||
Ui::ShareUserGroupWidget *_ui;
|
Ui::ShareUserGroupWidget *_ui;
|
||||||
QScrollArea *_parentScrollArea;
|
QScrollArea *_parentScrollArea;
|
||||||
AccountPtr _account;
|
AccountPtr _account;
|
||||||
@@ -127,6 +130,9 @@ signals:
|
|||||||
void visualDeletionDone();
|
void visualDeletionDone();
|
||||||
void resizeRequested();
|
void resizeRequested();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void slotStyleChanged();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void on_deleteShareButton_clicked();
|
void on_deleteShareButton_clicked();
|
||||||
void slotPermissionsChanged();
|
void slotPermissionsChanged();
|
||||||
@@ -141,6 +147,7 @@ private slots:
|
|||||||
private:
|
private:
|
||||||
void displayPermissions();
|
void displayPermissions();
|
||||||
void loadAvatar();
|
void loadAvatar();
|
||||||
|
void customizeStyle();
|
||||||
|
|
||||||
Ui::ShareUserLine *_ui;
|
Ui::ShareUserLine *_ui;
|
||||||
QSharedPointer<Share> _share;
|
QSharedPointer<Share> _share;
|
||||||
|
|||||||
@@ -56,7 +56,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="shareeLineEdit">
|
<widget class="QLineEdit" name="shareeLineEdit">
|
||||||
<property name="placeholderText">
|
<property name="placeholderText">
|
||||||
<string>Share with users or groups ...</string>
|
<string>Share with users or groups …</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@@ -63,7 +63,7 @@
|
|||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>User name</string>
|
<string>Username</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="textFormat">
|
<property name="textFormat">
|
||||||
<enum>Qt::PlainText</enum>
|
<enum>Qt::PlainText</enum>
|
||||||
|
|||||||
@@ -49,8 +49,10 @@
|
|||||||
#include <QLocalSocket>
|
#include <QLocalSocket>
|
||||||
#include <QStringBuilder>
|
#include <QStringBuilder>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QInputDialog>
|
||||||
|
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
|
#include <QDesktopServices>
|
||||||
|
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
|
||||||
@@ -290,6 +292,12 @@ void SocketApi::slotReadSocket()
|
|||||||
int indexOfMethod = staticMetaObject.indexOfMethod(functionWithArguments);
|
int indexOfMethod = staticMetaObject.indexOfMethod(functionWithArguments);
|
||||||
|
|
||||||
QString argument = line.remove(0, command.length() + 1);
|
QString argument = line.remove(0, command.length() + 1);
|
||||||
|
if (indexOfMethod == -1) {
|
||||||
|
// Fallback: Try upper-case command
|
||||||
|
functionWithArguments = "command_" + command.toUpper() + "(QString,SocketListener*)";
|
||||||
|
indexOfMethod = staticMetaObject.indexOfMethod(functionWithArguments);
|
||||||
|
}
|
||||||
|
|
||||||
if (indexOfMethod != -1) {
|
if (indexOfMethod != -1) {
|
||||||
staticMetaObject.method(indexOfMethod).invoke(this, Q_ARG(QString, argument), Q_ARG(SocketListener *, listener));
|
staticMetaObject.method(indexOfMethod).invoke(this, Q_ARG(QString, argument), Q_ARG(SocketListener *, listener));
|
||||||
} else {
|
} else {
|
||||||
@@ -477,6 +485,8 @@ public:
|
|||||||
this, &GetOrCreatePublicLinkShare::linkShareCreated);
|
this, &GetOrCreatePublicLinkShare::linkShareCreated);
|
||||||
connect(&_shareManager, &ShareManager::serverError,
|
connect(&_shareManager, &ShareManager::serverError,
|
||||||
this, &GetOrCreatePublicLinkShare::serverError);
|
this, &GetOrCreatePublicLinkShare::serverError);
|
||||||
|
connect(&_shareManager, &ShareManager::linkShareRequiresPassword,
|
||||||
|
this, &GetOrCreatePublicLinkShare::passwordRequired);
|
||||||
}
|
}
|
||||||
|
|
||||||
void run()
|
void run()
|
||||||
@@ -512,6 +522,24 @@ private slots:
|
|||||||
success(share->getLink().toString());
|
success(share->getLink().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void passwordRequired() {
|
||||||
|
bool ok;
|
||||||
|
QString password = QInputDialog::getText(nullptr,
|
||||||
|
tr("Password for share required"),
|
||||||
|
tr("Please enter a password for your link share:"),
|
||||||
|
QLineEdit::Normal,
|
||||||
|
QString(),
|
||||||
|
&ok);
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
// The dialog was canceled so no need to do anything
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to create the link share again with the newly entered password
|
||||||
|
_shareManager.createLinkShare(_localFile, QString(), password);
|
||||||
|
}
|
||||||
|
|
||||||
void serverError(int code, const QString &message)
|
void serverError(int code, const QString &message)
|
||||||
{
|
{
|
||||||
qCWarning(lcPublicLink) << "Share fetch/create error" << code << message;
|
qCWarning(lcPublicLink) << "Share fetch/create error" << code << message;
|
||||||
@@ -565,6 +593,24 @@ void SocketApi::command_COPY_PUBLIC_LINK(const QString &localFile, SocketListene
|
|||||||
job->run();
|
job->run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Windows Shell / Explorer pinning fallbacks, see issue: https://github.com/nextcloud/desktop/issues/1599
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
void SocketApi::command_COPYASPATH(const QString &localFile, SocketListener *)
|
||||||
|
{
|
||||||
|
QApplication::clipboard()->setText(localFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketApi::command_OPENNEWWINDOW(const QString &localFile, SocketListener *)
|
||||||
|
{
|
||||||
|
QDesktopServices::openUrl(QUrl::fromLocalFile(localFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketApi::command_OPEN(const QString &localFile, SocketListener *socketListener)
|
||||||
|
{
|
||||||
|
command_OPENNEWWINDOW(localFile, socketListener);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Fetches the private link url asynchronously and then calls the target slot
|
// Fetches the private link url asynchronously and then calls the target slot
|
||||||
void SocketApi::fetchPrivateLinkUrlHelper(const QString &localFile, const std::function<void(const QString &url)> &targetFun)
|
void SocketApi::fetchPrivateLinkUrlHelper(const QString &localFile, const std::function<void(const QString &url)> &targetFun)
|
||||||
{
|
{
|
||||||
@@ -623,9 +669,9 @@ void SocketApi::command_GET_STRINGS(const QString &argument, SocketListener *lis
|
|||||||
{
|
{
|
||||||
static std::array<std::pair<const char *, QString>, 5> strings { {
|
static std::array<std::pair<const char *, QString>, 5> strings { {
|
||||||
{ "SHARE_MENU_TITLE", tr("Share options") },
|
{ "SHARE_MENU_TITLE", tr("Share options") },
|
||||||
{ "CONTEXT_MENU_TITLE", tr("Share via ") + Theme::instance()->appNameGUI()},
|
{ "CONTEXT_MENU_TITLE", tr("Share via %1").arg(Theme::instance()->appNameGUI())},
|
||||||
{ "COPY_PRIVATE_LINK_MENU_TITLE", tr("Copy private link to clipboard") },
|
{ "COPY_PRIVATE_LINK_MENU_TITLE", tr("Copy private link to clipboard") },
|
||||||
{ "EMAIL_PRIVATE_LINK_MENU_TITLE", tr("Send private link by email...") },
|
{ "EMAIL_PRIVATE_LINK_MENU_TITLE", tr("Send private link by email …") },
|
||||||
} };
|
} };
|
||||||
listener->sendMessage(QString("GET_STRINGS:BEGIN"));
|
listener->sendMessage(QString("GET_STRINGS:BEGIN"));
|
||||||
for (const auto& key_value : strings) {
|
for (const auto& key_value : strings) {
|
||||||
@@ -673,7 +719,7 @@ void SocketApi::sendSharingContextMenuOptions(const FileData &fileData, SocketLi
|
|||||||
|
|
||||||
// Disabled: only providing email option for private links would look odd,
|
// Disabled: only providing email option for private links would look odd,
|
||||||
// and the copy option is more general.
|
// and the copy option is more general.
|
||||||
//listener->sendMessage(QLatin1String("MENU_ITEM:EMAIL_PRIVATE_LINK") + flagString + tr("Send private link by email..."));
|
//listener->sendMessage(QLatin1String("MENU_ITEM:EMAIL_PRIVATE_LINK") + flagString + tr("Send private link by email …"));
|
||||||
}
|
}
|
||||||
|
|
||||||
SocketApi::FileData SocketApi::FileData::get(const QString &localFile)
|
SocketApi::FileData SocketApi::FileData::get(const QString &localFile)
|
||||||
|
|||||||
@@ -105,6 +105,13 @@ private:
|
|||||||
Q_INVOKABLE void command_EMAIL_PRIVATE_LINK(const QString &localFile, SocketListener *listener);
|
Q_INVOKABLE void command_EMAIL_PRIVATE_LINK(const QString &localFile, SocketListener *listener);
|
||||||
Q_INVOKABLE void command_OPEN_PRIVATE_LINK(const QString &localFile, SocketListener *listener);
|
Q_INVOKABLE void command_OPEN_PRIVATE_LINK(const QString &localFile, SocketListener *listener);
|
||||||
|
|
||||||
|
// Windows Shell / Explorer pinning fallbacks, see issue: https://github.com/nextcloud/desktop/issues/1599
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
Q_INVOKABLE void command_COPYASPATH(const QString &localFile, SocketListener *listener);
|
||||||
|
Q_INVOKABLE void command_OPENNEWWINDOW(const QString &localFile, SocketListener *listener);
|
||||||
|
Q_INVOKABLE void command_OPEN(const QString &localFile, SocketListener *listener);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Fetch the private link and call targetFun
|
// Fetch the private link and call targetFun
|
||||||
void fetchPrivateLinkUrlHelper(const QString &localFile, const std::function<void(const QString &url)> &targetFun);
|
void fetchPrivateLinkUrlHelper(const QString &localFile, const std::function<void(const QString &url)> &targetFun);
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user