From 5e4aaab91be3a079ab4c2febc2d62c74318549a9 Mon Sep 17 00:00:00 2001 From: Jenkins for ownCloud Date: Fri, 23 Jun 2017 02:22:09 +0200 Subject: [PATCH 001/107] [tx-robot] updated from transifex --- mirall.desktop.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mirall.desktop.in b/mirall.desktop.in index b3a21737b9..0e11ce6b78 100644 --- a/mirall.desktop.in +++ b/mirall.desktop.in @@ -48,6 +48,9 @@ X-GNOME-Autostart-Delay=3 # Translations +# Translations + + # Translations Comment[oc]=@APPLICATION_NAME@ sincronizacion del client GenericName[oc]=Dorsièr de Sincronizacion From 0edc29dc72ea1a55fd559ebbd597cfd6dddf0d80 Mon Sep 17 00:00:00 2001 From: Jenkins for ownCloud Date: Sat, 24 Jun 2017 02:22:16 +0200 Subject: [PATCH 002/107] [tx-robot] updated from transifex --- mirall.desktop.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mirall.desktop.in b/mirall.desktop.in index 0e11ce6b78..f8bf5025a3 100644 --- a/mirall.desktop.in +++ b/mirall.desktop.in @@ -51,6 +51,9 @@ X-GNOME-Autostart-Delay=3 # Translations +# Translations + + # Translations Comment[oc]=@APPLICATION_NAME@ sincronizacion del client GenericName[oc]=Dorsièr de Sincronizacion From 5979db12709a1238f342c03654163e43024cb9c2 Mon Sep 17 00:00:00 2001 From: Jenkins for ownCloud Date: Sun, 25 Jun 2017 02:22:09 +0200 Subject: [PATCH 003/107] [tx-robot] updated from transifex --- mirall.desktop.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mirall.desktop.in b/mirall.desktop.in index f8bf5025a3..889d5da05c 100644 --- a/mirall.desktop.in +++ b/mirall.desktop.in @@ -54,6 +54,9 @@ X-GNOME-Autostart-Delay=3 # Translations +# Translations + + # Translations Comment[oc]=@APPLICATION_NAME@ sincronizacion del client GenericName[oc]=Dorsièr de Sincronizacion From c70cdd61cabfa18aba71f02df2457961da633011 Mon Sep 17 00:00:00 2001 From: Jenkins for ownCloud Date: Mon, 26 Jun 2017 02:22:09 +0200 Subject: [PATCH 004/107] [tx-robot] updated from transifex --- mirall.desktop.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mirall.desktop.in b/mirall.desktop.in index 889d5da05c..8613fda373 100644 --- a/mirall.desktop.in +++ b/mirall.desktop.in @@ -57,6 +57,9 @@ X-GNOME-Autostart-Delay=3 # Translations +# Translations + + # Translations Comment[oc]=@APPLICATION_NAME@ sincronizacion del client GenericName[oc]=Dorsièr de Sincronizacion From 2a52b087fd8ea60d4f86fab0209868dfe498397c Mon Sep 17 00:00:00 2001 From: Jenkins for ownCloud Date: Tue, 27 Jun 2017 02:22:10 +0200 Subject: [PATCH 005/107] [tx-robot] updated from transifex --- mirall.desktop.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mirall.desktop.in b/mirall.desktop.in index 8613fda373..828dc390a2 100644 --- a/mirall.desktop.in +++ b/mirall.desktop.in @@ -60,6 +60,9 @@ X-GNOME-Autostart-Delay=3 # Translations +# Translations + + # Translations Comment[oc]=@APPLICATION_NAME@ sincronizacion del client GenericName[oc]=Dorsièr de Sincronizacion From fd0b8f35839c927b023b1e7552ba9335dc0d1f5b Mon Sep 17 00:00:00 2001 From: Jenkins for ownCloud Date: Wed, 28 Jun 2017 02:22:09 +0200 Subject: [PATCH 006/107] [tx-robot] updated from transifex --- mirall.desktop.in | 3 +++ translations/client_ja.ts | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/mirall.desktop.in b/mirall.desktop.in index 828dc390a2..4648897373 100644 --- a/mirall.desktop.in +++ b/mirall.desktop.in @@ -63,6 +63,9 @@ X-GNOME-Autostart-Delay=3 # Translations +# Translations + + # Translations Comment[oc]=@APPLICATION_NAME@ sincronizacion del client GenericName[oc]=Dorsièr de Sincronizacion diff --git a/translations/client_ja.ts b/translations/client_ja.ts index d750ca1f24..056c1f3586 100644 --- a/translations/client_ja.ts +++ b/translations/client_ja.ts @@ -2553,7 +2553,7 @@ It is not advisable to use it. Anyone with the link has access to the file/folder - + リンクを知っている人はファイル/フォルダにアクセスできます From e8f95743cc256ebf67ec65fddde859617de6b93a Mon Sep 17 00:00:00 2001 From: Jenkins for ownCloud Date: Thu, 29 Jun 2017 02:22:16 +0200 Subject: [PATCH 007/107] [tx-robot] updated from transifex --- mirall.desktop.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mirall.desktop.in b/mirall.desktop.in index 4648897373..7f8590c267 100644 --- a/mirall.desktop.in +++ b/mirall.desktop.in @@ -66,6 +66,9 @@ X-GNOME-Autostart-Delay=3 # Translations +# Translations + + # Translations Comment[oc]=@APPLICATION_NAME@ sincronizacion del client GenericName[oc]=Dorsièr de Sincronizacion From 0f70bd7913eece083cdf71689440d105985249a2 Mon Sep 17 00:00:00 2001 From: Samuel Alfageme Date: Wed, 28 Jun 2017 12:28:07 +0200 Subject: [PATCH 008/107] Update central.owncloud.org link Desktop client category was renamed, old link was 404 - see: https://central.owncloud.org/t/new-central-categories-you-decide --- .github/issue_template.md | 2 +- src/libsync/owncloudtheme.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/issue_template.md b/.github/issue_template.md index 834d89e543..7681eab04a 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -2,7 +2,7 @@ Please try to only report a bug if it happens with the latest version The latest version can be seen by checking the ChangeLog: https://owncloud.org/changelog/desktop/ -For support try: https://central.owncloud.org/c/help/desktop-file-sync +For support try: https://central.owncloud.org/c/desktop-client ---> diff --git a/src/libsync/owncloudtheme.cpp b/src/libsync/owncloudtheme.cpp index 5528fe92d3..4f30571b24 100644 --- a/src/libsync/owncloudtheme.cpp +++ b/src/libsync/owncloudtheme.cpp @@ -44,7 +44,7 @@ QString ownCloudTheme::about() const { QString devString; devString = trUtf8("

Version %2. For more information visit https://%4

" - "

For known issues and help, please visit: https://central.owncloud.org

" + "

For known issues and help, please visit: https://central.owncloud.org

" "

By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, " " Jan-Christoph Borchardt, and others.

" "

Copyright ownCloud GmbH

" From aeea27b57b8496e7dc375b341f1bd0b22c8297a9 Mon Sep 17 00:00:00 2001 From: Samuel Alfageme Date: Wed, 28 Jun 2017 12:28:07 +0200 Subject: [PATCH 009/107] Update central.owncloud.org link Desktop client category was renamed, old link was 404 - see: https://central.owncloud.org/t/new-central-categories-you-decide --- issue_template.md | 2 +- src/libsync/owncloudtheme.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/issue_template.md b/issue_template.md index 834d89e543..7681eab04a 100644 --- a/issue_template.md +++ b/issue_template.md @@ -2,7 +2,7 @@ Please try to only report a bug if it happens with the latest version The latest version can be seen by checking the ChangeLog: https://owncloud.org/changelog/desktop/ -For support try: https://central.owncloud.org/c/help/desktop-file-sync +For support try: https://central.owncloud.org/c/desktop-client ---> diff --git a/src/libsync/owncloudtheme.cpp b/src/libsync/owncloudtheme.cpp index 7b38057074..c3108bab3d 100644 --- a/src/libsync/owncloudtheme.cpp +++ b/src/libsync/owncloudtheme.cpp @@ -46,7 +46,7 @@ QString ownCloudTheme::about() const { QString devString; devString = trUtf8("

Version %2. For more information visit https://%4

" - "

For known issues and help, please visit: https://central.owncloud.org

" + "

For known issues and help, please visit: https://central.owncloud.org

" "

By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, " " Jan-Christoph Borchardt, and others.

" "

Copyright ownCloud GmbH

" From e6984f4058f4519423134c0ea9e4d57def5b8689 Mon Sep 17 00:00:00 2001 From: Jenkins for ownCloud Date: Fri, 30 Jun 2017 02:18:46 +0200 Subject: [PATCH 010/107] [tx-robot] updated from transifex --- mirall.desktop.in | 3 +++ translations/client_ca.ts | 4 ++-- translations/client_cs.ts | 4 ++-- translations/client_de.ts | 4 ++-- translations/client_el.ts | 4 ++-- translations/client_en.ts | 2 +- translations/client_es.ts | 4 ++-- translations/client_es_AR.ts | 2 +- translations/client_et.ts | 2 +- translations/client_eu.ts | 2 +- translations/client_fa.ts | 2 +- translations/client_fi.ts | 2 +- translations/client_fr.ts | 4 ++-- translations/client_gl.ts | 2 +- translations/client_hu.ts | 2 +- translations/client_it.ts | 4 ++-- translations/client_ja.ts | 4 ++-- translations/client_nb_NO.ts | 4 ++-- translations/client_nl.ts | 4 ++-- translations/client_pl.ts | 2 +- translations/client_pt.ts | 4 ++-- translations/client_pt_BR.ts | 5 ++--- translations/client_ru.ts | 4 ++-- translations/client_sk.ts | 2 +- translations/client_sl.ts | 4 ++-- translations/client_sr.ts | 2 +- translations/client_sv.ts | 2 +- translations/client_th.ts | 4 ++-- translations/client_tr.ts | 2 +- translations/client_uk.ts | 2 +- translations/client_zh_CN.ts | 2 +- translations/client_zh_TW.ts | 2 +- 32 files changed, 49 insertions(+), 47 deletions(-) diff --git a/mirall.desktop.in b/mirall.desktop.in index 7f8590c267..69e57a3043 100644 --- a/mirall.desktop.in +++ b/mirall.desktop.in @@ -69,6 +69,9 @@ X-GNOME-Autostart-Delay=3 # Translations +# Translations + + # Translations Comment[oc]=@APPLICATION_NAME@ sincronizacion del client GenericName[oc]=Dorsièr de Sincronizacion diff --git a/translations/client_ca.ts b/translations/client_ca.ts index dce4d38608..873b089dbe 100644 --- a/translations/client_ca.ts +++ b/translations/client_ca.ts @@ -3453,8 +3453,8 @@ No és aconsellada usar-la. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> - <p>Versió %2. Per més informació visiteu <a href="%3">https://%4</a></p><p>Per errors coneguts i ajuda, visiteu: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>Per Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt i altres.</small></p><p>Copyright ownCloud GmbH</p><p>amb llicència GNU General Public License (GPL) versió 2.0<br/>ownCloud i el logo d'ownCloud són marques registrades d'ownCloud GmbH als Estats Units, altres països, o ambdós.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + diff --git a/translations/client_cs.ts b/translations/client_cs.ts index 3c2eb0519f..120bf368ed 100644 --- a/translations/client_cs.ts +++ b/translations/client_cs.ts @@ -3456,8 +3456,8 @@ Nedoporučuje se jí používat. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> - <p>Verze %2. Pro další informace navštivte <a href="%3">https://%4</a></p><p>Informace o známých chybách a pomoc hledejte na: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt a další.</small></p><p>Copyright ownCloud GmbH</p><p>Licencováno pod GNU General Public License (GPL) Verze 2.0<br/>ownCloud a ownCloud logo jsou registrované obchodní známky ownCloud GmbH ve Spojených státech, ostatních zemích nebo obojí.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + diff --git a/translations/client_de.ts b/translations/client_de.ts index 5b3b13befd..8c1e8d6a25 100644 --- a/translations/client_de.ts +++ b/translations/client_de.ts @@ -3457,8 +3457,8 @@ Es ist nicht ratsam, diese zu benutzen. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> - <p>Version %2. Weitere Informationen unter <a href="%3">https://%4</a></p><p>Für bekannte Fehler und die Hilfe, besuchen Sie bitte: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>Von Klaas Freitag, Daniel Molkentin, Jan-Christoph Borchardt, Olivier Goffart, Markus Götz und anderen.</small></p><p>Copyright ownCloud GmbH</p><p>Lizenziert unter den Bedingungen der GNU General Public License (GPL) Version 2.0<br/>ownCloud und das ownCloud Logo sind eingetragene Warenzeichen der ownCloud Inc. in den USA, anderen Ländern, oder beidem.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + diff --git a/translations/client_el.ts b/translations/client_el.ts index bac0620b61..8eb0f845b6 100644 --- a/translations/client_el.ts +++ b/translations/client_el.ts @@ -3458,8 +3458,8 @@ It is not advisable to use it. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> - <p>Έκδοση% 2.Για περισσότερες πληροφορίες επισκεφτείτε<a href="%3"> επίσημη ιστοσελίδα://%4</a></p><p> Για γνωστά ζητήματα και βοήθεια, παρακαλώ επισκεφτείτε: <a href=επίσημη ιστοσελίδα://central.owncloud.org/c/help/desktop-file-sync">επίσημη ιστοσελίδα://central.owncloud.org</a></p><p><small> Από τον Klaas Freitag, τον Daniel Molkentin, τον Olivier Goffart, τον Markus Götz , Jan-Christoph Borchardt και άλλους.</small></p><p> Κατοχυρωμένα υπό το ownCloud GmbH </ p> <p>Άδεια χρήσης υπό την GNU General Public License (GPL) Έκδοση 2.0 <br/> ownCloud και το ownCloud Τα λογότυπα είναι εμπορικά σήματα κατατεθέντα της ownCloud GmbH στις Ηνωμένες Πολιτείες, σε άλλες χώρες ή και στα δύο. </ P> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + diff --git a/translations/client_en.ts b/translations/client_en.ts index b6a2146eae..1d3fb917c7 100644 --- a/translations/client_en.ts +++ b/translations/client_en.ts @@ -3475,7 +3475,7 @@ It is not advisable to use it. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> diff --git a/translations/client_es.ts b/translations/client_es.ts index 97b7e559ba..436cd6a6f1 100644 --- a/translations/client_es.ts +++ b/translations/client_es.ts @@ -3457,8 +3457,8 @@ No se recomienda usarla. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> - <p>Versiòn %2. Para más información visita:<a href="%3">https://%4</a></p><p>Para ayuda y fallos conocidos, visita: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + diff --git a/translations/client_es_AR.ts b/translations/client_es_AR.ts index f0839b5d42..71656d1119 100644 --- a/translations/client_es_AR.ts +++ b/translations/client_es_AR.ts @@ -3443,7 +3443,7 @@ It is not advisable to use it. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> diff --git a/translations/client_et.ts b/translations/client_et.ts index 1e194a6058..09e06e1bec 100644 --- a/translations/client_et.ts +++ b/translations/client_et.ts @@ -3446,7 +3446,7 @@ Selle kasutamine pole soovitatav. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> diff --git a/translations/client_eu.ts b/translations/client_eu.ts index 73e5d44a24..541746ea9e 100644 --- a/translations/client_eu.ts +++ b/translations/client_eu.ts @@ -3446,7 +3446,7 @@ Ez da gomendagarria erabltzea. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> diff --git a/translations/client_fa.ts b/translations/client_fa.ts index cafd7cfadc..b91ad56023 100644 --- a/translations/client_fa.ts +++ b/translations/client_fa.ts @@ -3443,7 +3443,7 @@ It is not advisable to use it. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> diff --git a/translations/client_fi.ts b/translations/client_fi.ts index 08064209e8..6df95d381d 100644 --- a/translations/client_fi.ts +++ b/translations/client_fi.ts @@ -3448,7 +3448,7 @@ Osoitteen käyttäminen ei ole suositeltavaa. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> diff --git a/translations/client_fr.ts b/translations/client_fr.ts index c570e49b41..de5e127870 100644 --- a/translations/client_fr.ts +++ b/translations/client_fr.ts @@ -3459,8 +3459,8 @@ Il est déconseillé de l'utiliser. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> - <p>Version %2. Pour plus d'informations, consultez <a href="%3">https://%4</a>.</p><p>Pour consulter les problèmes connus et obtenir de l'aide, merci de visiter : <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a>.</p><p><small>Par Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt et d'autres personnes.</small></p><p>Copyright ownCloud GmbH</p><p>Sous licence GNU General Public License (GPL) Version 2.0.<br/>ownCloud et le logo ownCloud sont des marques déposées de ownCloud GmbH aux États-Unis, dans d'autres pays ou dans les deux.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + diff --git a/translations/client_gl.ts b/translations/client_gl.ts index 84643b388b..c19c71dc11 100644 --- a/translations/client_gl.ts +++ b/translations/client_gl.ts @@ -3447,7 +3447,7 @@ Recomendámoslle que non o use. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> diff --git a/translations/client_hu.ts b/translations/client_hu.ts index 192694b89b..0e3954d0a4 100644 --- a/translations/client_hu.ts +++ b/translations/client_hu.ts @@ -3444,7 +3444,7 @@ It is not advisable to use it. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> diff --git a/translations/client_it.ts b/translations/client_it.ts index fc949e14a5..bc9ec84578 100644 --- a/translations/client_it.ts +++ b/translations/client_it.ts @@ -3453,8 +3453,8 @@ Non è consigliabile utilizzarlo. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> - <p>Versione %2. Per ulteriori informazioni, visita <a href="%3">https://%4</a></p><p>Per problemi noti e aiuto, visita:<a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>Di Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, e altri.</small></p><p>Copyright ownCloud GmbH</p><p>Sotto licenza GNU General Public License (GPL) versione 2.0<br/>ownCloud e il logo di ownCloud sono marchi registrati di ownCloud, Inc. negli Stati Uniti, in altri paesi o entrambi.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + diff --git a/translations/client_ja.ts b/translations/client_ja.ts index 056c1f3586..d9cd070e62 100644 --- a/translations/client_ja.ts +++ b/translations/client_ja.ts @@ -3454,8 +3454,8 @@ It is not advisable to use it. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> - <p>バージョン %2 詳細については、<a href="%3">https://%4</a>をご覧ください。</p><p>既知の問題とヘルプは、<a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a>をご覧ください。By Klaas Freitag, Daniel Molkentin, Jan-Christoph Borchardt, Olivier Goffart, Markus Götz and others.</small></p><p>著作権 ownCloud, Inc.<p><p>がGNU General Public License (GPL) バージョン2.0 でライセンスされています。<br/>ownCloud 及び ownCloud のロゴはアメリカ合衆国またはその他の国、あるいはその両方における<br> ownCloud, Inc.の登録商標です。</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + diff --git a/translations/client_nb_NO.ts b/translations/client_nb_NO.ts index 76bd6db10e..3b3af38528 100644 --- a/translations/client_nb_NO.ts +++ b/translations/client_nb_NO.ts @@ -3457,8 +3457,8 @@ Det er ikke tilrådelig å bruke den. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> - <p>Versjon %2. For mer informasjon gå til <a href="%3">https://%4</a></p><p>For kjente problemer og hjelp, gå til: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>Av Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt med flere.</small></p><p>Copyright ownCloud GmbH</p><p>Lisensiert under GNU General Public License (GPL) Version 2.0<br/>ownCloud og ownCloud-logo er registrerte varemerker for ownCloud GmbH i USA, andre land eller begge deler.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + diff --git a/translations/client_nl.ts b/translations/client_nl.ts index d2e03afd6e..adee3cfaf3 100644 --- a/translations/client_nl.ts +++ b/translations/client_nl.ts @@ -3462,8 +3462,8 @@ We adviseren deze site niet te gebruiken. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> - <p>Versie %2. Voor meer informatie bezoek <a href="%3">https://%4</a></p><p>Voor bekende problemen en hulp, bezoek: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a>.</p><p><small>Door Klaas Freitag, Daniel Molkentin, Jan-Christoph Borchardt, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt en anderen.</small></p><p>Copyright ownCloud, GmbH</p><p>Gelicenseerd onder de GNU General Public License (GPL) Versie 2.0<br>ownCloud en het ownCloud logo zijn geregistreerde handelsmerken van ownCloud GmbH in de Verenigde Staten, andere landen, of beide.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + diff --git a/translations/client_pl.ts b/translations/client_pl.ts index e773f7c385..8d6b90d178 100644 --- a/translations/client_pl.ts +++ b/translations/client_pl.ts @@ -3451,7 +3451,7 @@ Niezalecane jest jego użycie. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> diff --git a/translations/client_pt.ts b/translations/client_pt.ts index 5bdf1dd69a..237aaf6ae8 100644 --- a/translations/client_pt.ts +++ b/translations/client_pt.ts @@ -3458,8 +3458,8 @@ Não é aconselhada a sua utilização. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> - <p>Versão %2. Para mais informação visite <a href="%3">https://%4</a></p><p>Para problemas conhecidos e melhoramento, por favor visite: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>Por Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, entre outros.</small></p><p>Direitos autorais ownCloud GmbH</p><p>Licenciado sob a GNU General Public License (GPL) Versão 2.0<br/>ownCloud e o logótipo ownCloud são marcas registadas de ownCloud GmbH nos Estados Unidos, outros países, ou ambos.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + diff --git a/translations/client_pt_BR.ts b/translations/client_pt_BR.ts index 9bb8a78516..3a7a6d8422 100644 --- a/translations/client_pt_BR.ts +++ b/translations/client_pt_BR.ts @@ -3455,9 +3455,8 @@ It is not advisable to use it. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> - <p>Versão %2. Para mais informações visite <a href="%3">https://%4</a></p><p> -Para problemas conhecidos e ajuda, visite: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>Por Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, e outros.</small></p><p>Copyright ownCloud GmbH</p><p>Licenciado sob o GNU General Public License (GPL) Versão 2.0<br/>ownCloud e o ownCloud Logo são marcas registradas da ownCloud GmbH nos United States, outros países, ou ambos.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + diff --git a/translations/client_ru.ts b/translations/client_ru.ts index 75d0152827..fb2d9ae8df 100644 --- a/translations/client_ru.ts +++ b/translations/client_ru.ts @@ -3455,8 +3455,8 @@ It is not advisable to use it. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> - <p>Версия %2. За известными проблемами и помощью обращайтесь по ссылке <a href="%3">https://%4</a></p><p><small>Авторы: Клаас Фрейтаг, Дэниель Молкентен, Оливье Гоффар, Маркус Гётц, Жан-Кристоф Бошар, и другие.</small></p><p>Авторские права принадлежат ownCloud GmbH</p><p>Лицензировано под GNU General Public License (GPL) версии 2.0<br/>ownCloud и логотип ownCloud — зарегистрированные торговые марки ownCloud GmbH в Соединённых Штатах, и/или других странах.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + diff --git a/translations/client_sk.ts b/translations/client_sk.ts index e27219054b..23441bf53d 100644 --- a/translations/client_sk.ts +++ b/translations/client_sk.ts @@ -3447,7 +3447,7 @@ Nie je vhodné ju používať. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> diff --git a/translations/client_sl.ts b/translations/client_sl.ts index eaec8fcec7..9178317d98 100644 --- a/translations/client_sl.ts +++ b/translations/client_sl.ts @@ -3458,8 +3458,8 @@ Uporaba ni priporočljiva. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> - <p>Različica %2. Več podrobnosti je mogoče najti na <a href="%3">https://%4</a></p><p>Znane težave in pomoč je na voljo na povezavi <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a>.</p><p><small>Avtorstvo: Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt in drugi.</small></p><p>Avtorske pravice ownCloud GmbH</p><p>Programski paket je objavljen z dovoljenjem GNU General Public License (GPL), različice 2.0.<br/>Znamka in logotip ownCloud sta blagovni znamki družbe ownCloud GmbH v Združenih državah, drugih državah ali obojih.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + diff --git a/translations/client_sr.ts b/translations/client_sr.ts index 6ab459366c..32a73ea25d 100644 --- a/translations/client_sr.ts +++ b/translations/client_sr.ts @@ -3447,7 +3447,7 @@ It is not advisable to use it. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> diff --git a/translations/client_sv.ts b/translations/client_sv.ts index 804934345f..c2e8c380bf 100644 --- a/translations/client_sv.ts +++ b/translations/client_sv.ts @@ -3451,7 +3451,7 @@ Det är inte lämpligt använda den. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> diff --git a/translations/client_th.ts b/translations/client_th.ts index 376816964a..fbae87c830 100644 --- a/translations/client_th.ts +++ b/translations/client_th.ts @@ -3457,8 +3457,8 @@ It is not advisable to use it. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> - <p>เวอร์ชัน %2 สำหรับข้อมูลเพิ่มเติมสามารถอ่านได้ที่ <a href="%3">https://%4</a></p><p>สำหรับปัญหาที่พบ, และอ่านข้อมูลเพิ่มเติมที่: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>โดย Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt และคนอื่นๆ</small></p><p>ลิขสิทธิ์ ownCloud GmbH</p><p>ภายใต้สัญญาอนุญาต GNU General Public (GPL) เวอร์ชั่น 2.0 <br/> ownCloud และโลโก้ ownCloud เป็นเครื่องหมายการค้าจดทะเบียนของ ownCloud GmbH ในสหรัฐอเมริกาและประเทศอื่นๆ</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + diff --git a/translations/client_tr.ts b/translations/client_tr.ts index 99944a8804..260fe62c6e 100644 --- a/translations/client_tr.ts +++ b/translations/client_tr.ts @@ -3448,7 +3448,7 @@ Kullanmanız önerilmez. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> diff --git a/translations/client_uk.ts b/translations/client_uk.ts index 5f4a399af1..aaa7ad4656 100644 --- a/translations/client_uk.ts +++ b/translations/client_uk.ts @@ -3446,7 +3446,7 @@ It is not advisable to use it. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> diff --git a/translations/client_zh_CN.ts b/translations/client_zh_CN.ts index 57fbe2b663..a99d1383e3 100644 --- a/translations/client_zh_CN.ts +++ b/translations/client_zh_CN.ts @@ -3448,7 +3448,7 @@ It is not advisable to use it. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> diff --git a/translations/client_zh_TW.ts b/translations/client_zh_TW.ts index 460511e108..ff436c0e51 100644 --- a/translations/client_zh_TW.ts +++ b/translations/client_zh_TW.ts @@ -3449,7 +3449,7 @@ It is not advisable to use it. OCC::ownCloudTheme - <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/help/desktop-file-sync">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> + <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> From 2326ea77e62cc7a58f308c214849c9581213be39 Mon Sep 17 00:00:00 2001 From: Jenkins for ownCloud Date: Sat, 1 Jul 2017 02:18:28 +0200 Subject: [PATCH 011/107] [tx-robot] updated from transifex --- mirall.desktop.in | 3 +++ translations/client_de.ts | 2 +- translations/client_pt_BR.ts | 2 +- translations/client_th.ts | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/mirall.desktop.in b/mirall.desktop.in index 69e57a3043..feea2f630b 100644 --- a/mirall.desktop.in +++ b/mirall.desktop.in @@ -72,6 +72,9 @@ X-GNOME-Autostart-Delay=3 # Translations +# Translations + + # Translations Comment[oc]=@APPLICATION_NAME@ sincronizacion del client GenericName[oc]=Dorsièr de Sincronizacion diff --git a/translations/client_de.ts b/translations/client_de.ts index 8c1e8d6a25..38feacb3d5 100644 --- a/translations/client_de.ts +++ b/translations/client_de.ts @@ -3458,7 +3458,7 @@ Es ist nicht ratsam, diese zu benutzen. <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> - + <p>Version %2. Weitere Informationen unter <a href="%3">https://%4</a></p><p>Für bekannte Fehler und die Hilfe, besuchen Sie bitte: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>Von Klaas Freitag, Daniel Molkentin, Jan-Christoph Borchardt, Olivier Goffart, Markus Götz und anderen.</small></p><p>Copyright ownCloud GmbH</p><p>Lizenziert unter den Bedingungen der GNU General Public License (GPL) Version 2.0<br/>ownCloud und das ownCloud Logo sind eingetragene Warenzeichen der ownCloud Inc. in den USA, anderen Ländern, oder beidem.</p> diff --git a/translations/client_pt_BR.ts b/translations/client_pt_BR.ts index 3a7a6d8422..d0d7240eee 100644 --- a/translations/client_pt_BR.ts +++ b/translations/client_pt_BR.ts @@ -3456,7 +3456,7 @@ It is not advisable to use it. <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> - + <p>Versão %2. Para mais informações visite <a href="%3">https://%4</a></p><p>Para saber sobre problemas e ajuda, por favor visite: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>Por Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, e outros.</small></p><p>Direitos Autorais ownCloud GmbH</p><p>Licenciado sob a GNU General Public License (GPL) Versão 2.0<br/>ownCloud e o Logo ownCloud são marcas registradas da ownCloud GmbH nos United States, e outros países, ou ambos.</p> diff --git a/translations/client_th.ts b/translations/client_th.ts index fbae87c830..d5c880363c 100644 --- a/translations/client_th.ts +++ b/translations/client_th.ts @@ -3458,7 +3458,7 @@ It is not advisable to use it. <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> - + <p>รุ่น %2 สำหรับข้อมูลเพิ่มเดิมไปดูได้ที่ <a href="%3">https://%4</a></p><p> หรือแจ้งปัญหาที่ทราบไปที่: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>โดย Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt และอีกหลายท่าน</small></p><p>ลิขสิทธิ์ ownCloud GmbH</p><p>ได้รับอนุญาตภายใต้ GNU General Public License (GPL) เวอร์ชัน 2.0<br/>ownCloud และโลโก้ OwnCloud เป็นเครื่องหมายจดทะเบียนการค้าของ ownCloud GmbH ในประเทศสหรัฐอเมริกาหรือประเทศอื่นๆ</p> From 2c0e820d9a11db4dac29da5e73e85229b57dfc26 Mon Sep 17 00:00:00 2001 From: Jenkins for ownCloud Date: Sun, 2 Jul 2017 02:18:29 +0200 Subject: [PATCH 012/107] [tx-robot] updated from transifex --- mirall.desktop.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mirall.desktop.in b/mirall.desktop.in index feea2f630b..1df12c80cc 100644 --- a/mirall.desktop.in +++ b/mirall.desktop.in @@ -75,6 +75,9 @@ X-GNOME-Autostart-Delay=3 # Translations +# Translations + + # Translations Comment[oc]=@APPLICATION_NAME@ sincronizacion del client GenericName[oc]=Dorsièr de Sincronizacion From 2a410217e21639e479f4f5b2643fb09647c8b963 Mon Sep 17 00:00:00 2001 From: Jenkins for ownCloud Date: Mon, 3 Jul 2017 02:18:29 +0200 Subject: [PATCH 013/107] [tx-robot] updated from transifex --- mirall.desktop.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mirall.desktop.in b/mirall.desktop.in index 1df12c80cc..84bba57fcb 100644 --- a/mirall.desktop.in +++ b/mirall.desktop.in @@ -78,6 +78,9 @@ X-GNOME-Autostart-Delay=3 # Translations +# Translations + + # Translations Comment[oc]=@APPLICATION_NAME@ sincronizacion del client GenericName[oc]=Dorsièr de Sincronizacion From bdb8a4a0cb6959bbc7e5e483f82bf295ea3c163f Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Mon, 3 Jul 2017 11:48:04 +0200 Subject: [PATCH 014/107] csync tests: Fix for new owncloudcmd return codes These were introduced in 4af45394f9f61fbc68c9f0a4d2e5387d14052e3b --- csync/tests/ownCloud/ownCloud/Test.pm | 11 +++++----- csync/tests/ownCloud/t4.pl | 3 ++- csync/tests/ownCloud/t7.pl | 30 +++++++++++++++++---------- csync/tests/ownCloud/t8.pl | 17 +++++++++------ 4 files changed, 37 insertions(+), 24 deletions(-) diff --git a/csync/tests/ownCloud/ownCloud/Test.pm b/csync/tests/ownCloud/ownCloud/Test.pm index ee3d557294..0034b0e6bf 100644 --- a/csync/tests/ownCloud/ownCloud/Test.pm +++ b/csync/tests/ownCloud/ownCloud/Test.pm @@ -294,15 +294,13 @@ sub localCleanup($) system( "rm -rf $dir" ); } -# parameter: An optional full url to the owncloud sync dir. +# parameter: the expected return code sub csync( ;$ ) { - my ($aurl) = @_; + my $expected = $_[0] // 0; + print "EXPECTED $expected\n"; my $url = testDirUrl(); - if( $aurl ) { - $url = $aurl; - } if( $url =~ /^https:/ ) { $url =~ s#^https://##; # Remove the leading http:// $url = "ownclouds://$user:$passwd@". $url; @@ -317,7 +315,8 @@ sub csync( ;$ ) my $cmd = "LD_LIBRARY_PATH=$ld_libpath $csync $args $localDir $url"; print "Starting: $cmd\n"; - system( $cmd ) == 0 or die("CSync died!\n"); + my $result = system( $cmd ); + $result == ($expected << 8) or die("Wrong csync return code or crash! $result\n"); } # diff --git a/csync/tests/ownCloud/t4.pl b/csync/tests/ownCloud/t4.pl index e862abebb0..aeccd2a767 100755 --- a/csync/tests/ownCloud/t4.pl +++ b/csync/tests/ownCloud/t4.pl @@ -79,7 +79,8 @@ printInfo("Add a file in a read only directory"); system( "echo \"Hello World\" >> /tmp/kernelcrash.txt" ); put_to_dir( '/tmp/kernelcrash.txt', 'test_stat' ); -csync(); +# Sync failed, can't download file to readonly dir +csync(1); assert( ! -e localDir().'test_stat/kernelcrash' ); diff --git a/csync/tests/ownCloud/t7.pl b/csync/tests/ownCloud/t7.pl index 117b998fe8..6b68a06efa 100755 --- a/csync/tests/ownCloud/t7.pl +++ b/csync/tests/ownCloud/t7.pl @@ -102,10 +102,6 @@ system("echo '__modified' > ". localDir() . "normalDirectory_PERM_CKDNV_/canBeMo system("echo '__modified_' > ". localDir() . "readonlyDirectory_PERM_M_/canBeModified_PERM_W_.data"); #5. Create a new file in a read only folder -# (they should not be uploaded) -createLocalFile( localDir() . "readonlyDirectory_PERM_M_/newFile_PERM_WDNV_.data", 105 ); - -#6. Create a new file in a read only folder # (should be uploaded) createLocalFile( localDir() . "normalDirectory_PERM_CKDNV_/newFile_PERM_WDNV_.data", 106 ); @@ -113,7 +109,6 @@ createLocalFile( localDir() . "normalDirectory_PERM_CKDNV_/newFile_PERM_WDNV_.da csync(); assertCsyncJournalOk(localDir()); - #1. # File should be recovered assert( -e localDir(). 'normalDirectory_PERM_CKDNV_/cannotBeRemoved_PERM_WVN_.data' ); @@ -139,6 +134,23 @@ system("rm " . localDir().'readonlyDirectory_PERM_M_/canotBeModified_PERM_DVN__c #4. File should be updated, that's tested by assertLocalAndRemoteDir #5. +# the file should be in the server and local +assert( -e localDir() . "normalDirectory_PERM_CKDNV_/newFile_PERM_WDNV_.data" ); + +### Both side should still be the same +assertLocalAndRemoteDir( '', 0); + +# Next test + +#6. Create a new file in a read only folder +# (they should not be uploaded) +createLocalFile( localDir() . "readonlyDirectory_PERM_M_/newFile_PERM_WDNV_.data", 105 ); + +# error: can't upload to readonly +csync(1); +assertCsyncJournalOk(localDir()); + +#6. # The file should not exist on the remote # TODO: test that the file is NOT on the server # but still be there @@ -146,11 +158,6 @@ assert( -e localDir() . "readonlyDirectory_PERM_M_/newFile_PERM_WDNV_.data" ); # remove it so assertLocalAndRemoteDir succeed. unlink(localDir() . "readonlyDirectory_PERM_M_/newFile_PERM_WDNV_.data"); -#6. -# the file should be in the server and local -assert( -e localDir() . "normalDirectory_PERM_CKDNV_/newFile_PERM_WDNV_.data" ); - - ### Both side should still be the same assertLocalAndRemoteDir( '', 0); @@ -207,7 +214,8 @@ system("mv " . localDir().'readonlyDirectory_PERM_M_/subdir_PERM_CK_ ' . localDi #2. move a directory from read to read only (move the directory from previous step) system("mv " . localDir().'normalDirectory_PERM_CKDNV_/subdir_PERM_CKDNV_ ' . localDir().'readonlyDirectory_PERM_M_/moved_PERM_CK_' ); -csync(); +# error: can't upload to readonly! +csync(1); assertCsyncJournalOk(localDir()); #1. diff --git a/csync/tests/ownCloud/t8.pl b/csync/tests/ownCloud/t8.pl index 1a0557d7fe..4628344da0 100755 --- a/csync/tests/ownCloud/t8.pl +++ b/csync/tests/ownCloud/t8.pl @@ -54,7 +54,8 @@ createLocalFile( $tmpdir . "test.dat", 170 ); createRemoteDir( "dir" ); glob_put( "$tmpdir/*", "dir" ); -csync(); +# can't download these +csync(1); # Check that only one of the two file was synced. # The one that exist here is undefined, the current implementation will take the @@ -76,7 +77,8 @@ printInfo( "Renaming one file to the same name as another one with different cas moveRemoteFile( 'dir/Hello.dat', 'dir/NORMAL.dat'); moveRemoteFile( 'dir/test.dat', 'dir/TEST.dat'); -csync(); +# can't download these +csync(1); # Hello -> NORMAL should not have do the move since the case conflict assert( -e localDir() . 'dir/Hello.dat' ); @@ -87,13 +89,16 @@ assert( -e localDir() . 'dir/Normal.dat' ); assert( -e localDir() . 'dir/TEST.dat' ); assert( !-e localDir() . 'dir/test.dat' ); +# undo the change that causes the sync fail +moveRemoteFile( 'dir/NORMAL.dat', 'dir/Hello.dat'); printInfo( "Another directory with the same name but different casing is created" ); createRemoteDir( "DIR" ); -glob_put( "$tmpdir/*", "DIR" ); +glob_put( "$tmpdir/HELLO.dat", "DIR" ); -csync(); +# can't download dirs +csync(1); assert( !-e localDir() . 'DIR' ); @@ -107,7 +112,6 @@ csync(); # now DIR was fetched assert( -e localDir() . 'DIR' ); assert( -e localDir() . 'DIR/HELLO.dat' ); -assert( !-e localDir() . 'DIR/Hello.dat' ); assert( !-e localDir() . 'dir' ); # dir/NORMAL.dat is still on the server @@ -125,7 +129,8 @@ createLocalFile( $tmpdir2 . "file.dat", 33 ); createRemoteDir( "parallel" ); glob_put( "$tmpdir2/*", "parallel" ); -csync(); +# again, can't download both +csync(1); # only one file must exist assert( (!-e localDir() . 'parallel/FILE.dat' ) or (!-e localDir() . 'parallel/file.dat') ); From 851a3128e4cd80024430e50904859276a1b662d1 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Wed, 28 Jun 2017 11:15:22 +0200 Subject: [PATCH 015/107] SyncEngine: Add unittest for SyncFileItem properties #5855 Checks instruction, direction, size, modtime for three common cases. --- test/testsyncengine.cpp | 75 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/test/testsyncengine.cpp b/test/testsyncengine.cpp index 1f48931da1..1a9ac42d11 100644 --- a/test/testsyncengine.cpp +++ b/test/testsyncengine.cpp @@ -385,6 +385,81 @@ private slots: QVERIFY(fakeFolder.syncOnce()); QCOMPARE(nGET, 1); } + + /** + * Checks whether SyncFileItems have the expected properties before start + * of propagation. + */ + void testSyncFileItemProperties() + { + FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() }; + + auto initialMtime = fakeFolder.currentLocalState().find("A/a1")->lastModified; + auto changedMtime = QDateTime::currentDateTime().addDays(-4); + auto changedMtime2 = QDateTime::currentDateTime().addDays(-3); + + // Base mtime with no ms content (filesystem is seconds only) + initialMtime.setMSecsSinceEpoch(initialMtime.toMSecsSinceEpoch() / 1000 * 1000); + changedMtime.setMSecsSinceEpoch(changedMtime.toMSecsSinceEpoch() / 1000 * 1000); + changedMtime2.setMSecsSinceEpoch(changedMtime2.toMSecsSinceEpoch() / 1000 * 1000); + + // upload a + fakeFolder.localModifier().appendByte("A/a1"); + fakeFolder.localModifier().setModTime("A/a1", changedMtime); + // download b + fakeFolder.remoteModifier().appendByte("B/b1"); + fakeFolder.remoteModifier().setModTime("B/b1", changedMtime); + // conflict c + fakeFolder.localModifier().appendByte("C/c1"); + fakeFolder.localModifier().appendByte("C/c1"); + fakeFolder.localModifier().setModTime("C/c1", changedMtime); + fakeFolder.remoteModifier().appendByte("C/c1"); + fakeFolder.remoteModifier().setModTime("C/c1", changedMtime2); + + connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToPropagate, [&](SyncFileItemVector &items) { + SyncFileItemPtr a1, b1, c1; + for (auto &item : items) { + if (item->_file == "A/a1") + a1 = item; + if (item->_file == "B/b1") + b1 = item; + if (item->_file == "C/c1") + c1 = item; + } + + // a1: should have local size and modtime + QVERIFY(a1); + QCOMPARE(a1->_instruction, CSYNC_INSTRUCTION_SYNC); + QCOMPARE(a1->_direction, SyncFileItem::Up); + + // NOTE: This is currently a bug! #5855 + //QCOMPARE(a1->_size, quint64(5)); + + QCOMPARE(Utility::qDateTimeFromTime_t(a1->_modtime), changedMtime); + QCOMPARE(a1->log._other_size, quint64(4)); + QCOMPARE(Utility::qDateTimeFromTime_t(a1->log._other_modtime), initialMtime); + + // b2: should have remote size and modtime + QVERIFY(b1); + QCOMPARE(b1->_instruction, CSYNC_INSTRUCTION_SYNC); + QCOMPARE(b1->_direction, SyncFileItem::Down); + QCOMPARE(b1->_size, quint64(17)); + QCOMPARE(Utility::qDateTimeFromTime_t(b1->_modtime), changedMtime); + QCOMPARE(b1->log._other_size, quint64(16)); + QCOMPARE(Utility::qDateTimeFromTime_t(b1->log._other_modtime), initialMtime); + + // c1: conflicts are downloads, so remote size and modtime + QVERIFY(c1); + QCOMPARE(c1->_instruction, CSYNC_INSTRUCTION_CONFLICT); + QCOMPARE(c1->_direction, SyncFileItem::None); + QCOMPARE(c1->_size, quint64(25)); + QCOMPARE(Utility::qDateTimeFromTime_t(c1->_modtime), changedMtime2); + QCOMPARE(c1->log._other_size, quint64(26)); + QCOMPARE(Utility::qDateTimeFromTime_t(c1->log._other_modtime), changedMtime); + }); + + QVERIFY(fakeFolder.syncOnce()); + } }; QTEST_GUILESS_MAIN(TestSyncEngine) From d018d460e3968ea67352a9dad9f3bdbe8a97306e Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Mon, 3 Jul 2017 13:16:29 +0200 Subject: [PATCH 016/107] csync tests: remove stray 'print' --- csync/tests/ownCloud/ownCloud/Test.pm | 1 - 1 file changed, 1 deletion(-) diff --git a/csync/tests/ownCloud/ownCloud/Test.pm b/csync/tests/ownCloud/ownCloud/Test.pm index 0034b0e6bf..251164e2c1 100644 --- a/csync/tests/ownCloud/ownCloud/Test.pm +++ b/csync/tests/ownCloud/ownCloud/Test.pm @@ -298,7 +298,6 @@ sub localCleanup($) sub csync( ;$ ) { my $expected = $_[0] // 0; - print "EXPECTED $expected\n"; my $url = testDirUrl(); if( $url =~ /^https:/ ) { From f49985697bbda412d5ad12534f405ec2ac3ab037 Mon Sep 17 00:00:00 2001 From: Markus Goetz Date: Mon, 3 Jul 2017 14:08:10 +0200 Subject: [PATCH 017/107] TestSyncJournalDB: Fix concurrent usage on CI --- test/testsyncjournaldb.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/testsyncjournaldb.cpp b/test/testsyncjournaldb.cpp index fa6021acc4..3787eb4c14 100644 --- a/test/testsyncjournaldb.cpp +++ b/test/testsyncjournaldb.cpp @@ -17,10 +17,11 @@ class TestSyncJournalDB : public QObject { Q_OBJECT + QTemporaryDir _tempDir; public: - TestSyncJournalDB() - : _db("/tmp/csync-test.db") + TestSyncJournalDB() : _db((_tempDir.path() + "/sync.db")) { + QVERIFY(_tempDir.isValid()); } QDateTime dropMsecs(QDateTime time) From d1e00099dc502fe22a1dd4e5c0d8d42e7de868a7 Mon Sep 17 00:00:00 2001 From: Piotr Mrowczynski Date: Tue, 20 Jun 2017 20:27:26 +0200 Subject: [PATCH 018/107] Classify chunked items correctly. Issue #5850 --- src/libsync/discoveryphase.h | 5 +++-- src/libsync/owncloudpropagator.cpp | 3 ++- src/libsync/propagateupload.h | 6 +++++- src/libsync/propagateuploadng.cpp | 1 + 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/libsync/discoveryphase.h b/src/libsync/discoveryphase.h index 9603c39ab0..b7d252c675 100644 --- a/src/libsync/discoveryphase.h +++ b/src/libsync/discoveryphase.h @@ -53,9 +53,10 @@ struct SyncOptions /** If a confirmation should be asked for external storages */ bool _confirmExternalStorage; - /** The initial un-adjusted chunk size in bytes for chunked uploads + /** The initial un-adjusted chunk size in bytes for chunked uploads, both + * for old and new chunking algorithm, which classifies the item to be chunked * - * When dynamic chunk size adjustments are done, this is the + * In chunkingNG, when dynamic chunk size adjustments are done, this is the * starting value and is then gradually adjusted within the * minChunkSize / maxChunkSize bounds. */ diff --git a/src/libsync/owncloudpropagator.cpp b/src/libsync/owncloudpropagator.cpp index 62f12f0232..f77dfd860e 100644 --- a/src/libsync/owncloudpropagator.cpp +++ b/src/libsync/owncloudpropagator.cpp @@ -379,7 +379,8 @@ PropagateItemJob *OwncloudPropagator::createJob(const SyncFileItemPtr &item) return job; } else { PropagateUploadFileCommon *job = 0; - if (item->_size > _chunkSize && account()->capabilities().chunkingNg()) { + if (item->_size > syncOptions()._initialChunkSize && account()->capabilities().chunkingNg()) { + // Item is above _initialChunkSize, thus will be classified as to be chunked job = new PropagateUploadFileNG(this, item); } else { job = new PropagateUploadFileV1(this, item); diff --git a/src/libsync/propagateupload.h b/src/libsync/propagateupload.h index 5e16b5ff98..030c43020a 100644 --- a/src/libsync/propagateupload.h +++ b/src/libsync/propagateupload.h @@ -307,7 +307,11 @@ private: int _chunkCount; /// Total number of chunks for this file int _transferId; /// transfer id (part of the url) - quint64 chunkSize() const { return propagator()->syncOptions()._initialChunkSize; } + quint64 chunkSize() const { + // Old chunking does not use dynamic chunking algorithm, and does not adjusts the chunk size respectively, + // thus this value should be used as the one classifing item to be chunked + return propagator()->syncOptions()._initialChunkSize; + } public: diff --git a/src/libsync/propagateuploadng.cpp b/src/libsync/propagateuploadng.cpp index 45768102da..dd918e80b9 100644 --- a/src/libsync/propagateuploadng.cpp +++ b/src/libsync/propagateuploadng.cpp @@ -400,6 +400,7 @@ void PropagateUploadFileNG::slotPutFinished() // the chunk sizes a bit. quint64 targetSize = (propagator()->_chunkSize + predictedGoodSize) / 2; + // Adjust the dynamic chunk size _chunkSize used for sizing of the item's chunks to be send propagator()->_chunkSize = qBound( propagator()->syncOptions()._minChunkSize, targetSize, From 946c6967e9c866b7865955c3f92d01a412ac4616 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Mon, 3 Jul 2017 16:42:31 +0200 Subject: [PATCH 019/107] Fix check_csync_exclude test Commit 4697f0274f282cafd28cbad07066efb397dbf21b remove .htaccess from the sync-exclude.lst, so use another known excluded pattern --- csync/tests/csync_tests/check_csync_exclude.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/csync/tests/csync_tests/check_csync_exclude.c b/csync/tests/csync_tests/check_csync_exclude.c index bd4813c5ed..ea9ca0e2f6 100644 --- a/csync/tests/csync_tests/check_csync_exclude.c +++ b/csync/tests/csync_tests/check_csync_exclude.c @@ -113,9 +113,9 @@ static void check_csync_excluded(void **state) assert_int_equal(rc, CSYNC_NOT_EXCLUDED); rc = csync_excluded_no_ctx(csync->excludes, ".kde/share/config/kwin.eventsrc", CSYNC_FTW_TYPE_FILE); assert_int_equal(rc, CSYNC_NOT_EXCLUDED); - rc = csync_excluded_no_ctx(csync->excludes, ".htaccess/cache-maximegalon/cache1.txt", CSYNC_FTW_TYPE_FILE); + rc = csync_excluded_no_ctx(csync->excludes, ".directory/cache-maximegalon/cache1.txt", CSYNC_FTW_TYPE_FILE); assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); - rc = csync_excluded_no_ctx(csync->excludes, "mozilla/.htaccess", CSYNC_FTW_TYPE_DIR); + rc = csync_excluded_no_ctx(csync->excludes, "mozilla/.directory", CSYNC_FTW_TYPE_DIR); assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST); /* From 30095a0c3f54195016f35954e0ca29eec1c03a82 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Mon, 3 Jul 2017 17:24:56 +0200 Subject: [PATCH 020/107] Fix check_csync_update Commit 816096311027bac0f391ea97d0b61b4175e9b8f9 changed the SQL requests from csync to read from the checksumtype table. So we need to add this table in the fake DB as well --- csync/tests/csync_tests/check_csync_update.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/csync/tests/csync_tests/check_csync_update.c b/csync/tests/csync_tests/check_csync_update.c index 6bd731947c..695616b2ca 100644 --- a/csync/tests/csync_tests/check_csync_update.c +++ b/csync/tests/csync_tests/check_csync_update.c @@ -52,6 +52,13 @@ static void statedb_create_metadata_table(sqlite3 *db) rc = sqlite3_exec(db, sql, NULL, NULL, NULL); //const char *msg = sqlite3_errmsg(db); assert_int_equal( rc, SQLITE_OK ); + + sql = "CREATE TABLE IF NOT EXISTS checksumtype(" + "id INTEGER PRIMARY KEY," + "name TEXT UNIQUE" + ");"; + rc = sqlite3_exec(db, sql, NULL, NULL, NULL); + assert_int_equal( rc, SQLITE_OK ); } } From 99f5580c37b7ddf5479cc7e6956a7c43ecffc189 Mon Sep 17 00:00:00 2001 From: Jenkins for ownCloud Date: Tue, 4 Jul 2017 02:18:29 +0200 Subject: [PATCH 021/107] [tx-robot] updated from transifex --- mirall.desktop.in | 3 +++ translations/client_es.ts | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/mirall.desktop.in b/mirall.desktop.in index 84bba57fcb..94a45f3f67 100644 --- a/mirall.desktop.in +++ b/mirall.desktop.in @@ -81,6 +81,9 @@ X-GNOME-Autostart-Delay=3 # Translations +# Translations + + # Translations Comment[oc]=@APPLICATION_NAME@ sincronizacion del client GenericName[oc]=Dorsièr de Sincronizacion diff --git a/translations/client_es.ts b/translations/client_es.ts index 436cd6a6f1..eabc6fa573 100644 --- a/translations/client_es.ts +++ b/translations/client_es.ts @@ -3458,7 +3458,7 @@ No se recomienda usarla. <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> - + <p>Version %2. Para mas información visite: <a href="%3">https://%4</a></p><p>Para ayuda y asistencia , visite: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licenciado bajo la GNU General Public License (GPL) Version 2.0<br/>ownCloud y el logo de ownCloud son marcas registradas de ownCloud GmbH en los Estados Unidos, en otros paises o en ambos.</p> From 0db095e02a3cc311a1121ab1b427c92c59687db7 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Tue, 4 Jul 2017 09:57:22 +0200 Subject: [PATCH 022/107] SyncEngineTest: Fix test reliability There was a rounding issue in the mtimes which sometimes resulted in an off-by-one error. Caused by storing a full QDateTime in the FileInfo but the mtime saved to the disk being truncated to seconds. --- test/testsyncengine.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/test/testsyncengine.cpp b/test/testsyncengine.cpp index 1a9ac42d11..8835bd575b 100644 --- a/test/testsyncengine.cpp +++ b/test/testsyncengine.cpp @@ -392,9 +392,7 @@ private slots: */ void testSyncFileItemProperties() { - FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() }; - - auto initialMtime = fakeFolder.currentLocalState().find("A/a1")->lastModified; + auto initialMtime = QDateTime::currentDateTime().addDays(-7); auto changedMtime = QDateTime::currentDateTime().addDays(-4); auto changedMtime2 = QDateTime::currentDateTime().addDays(-3); @@ -403,6 +401,15 @@ private slots: changedMtime.setMSecsSinceEpoch(changedMtime.toMSecsSinceEpoch() / 1000 * 1000); changedMtime2.setMSecsSinceEpoch(changedMtime2.toMSecsSinceEpoch() / 1000 * 1000); + // Ensure the initial mtimes are as expected + auto initialFileInfo = FileInfo::A12_B12_C12_S12(); + initialFileInfo.setModTime("A/a1", initialMtime); + initialFileInfo.setModTime("B/b1", initialMtime); + initialFileInfo.setModTime("C/c1", initialMtime); + + FakeFolder fakeFolder{ initialFileInfo }; + + // upload a fakeFolder.localModifier().appendByte("A/a1"); fakeFolder.localModifier().setModTime("A/a1", changedMtime); From f4d1a07b7247ec4ab9e6afe1b59f3eb15a40af06 Mon Sep 17 00:00:00 2001 From: Markus Goetz Date: Fri, 23 Jun 2017 16:53:23 +0200 Subject: [PATCH 023/107] SyncEngine: Keep local size in SyncItem #5855 --- src/libsync/syncengine.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index 5aa2376b0a..5efee80afa 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -386,8 +386,10 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote ) if (item->_instruction == CSYNC_INSTRUCTION_NONE || (item->_instruction == CSYNC_INSTRUCTION_IGNORE && instruction != CSYNC_INSTRUCTION_NONE)) { + // Take values from side (local/remote) where instruction is not _NONE item->_instruction = instruction; item->_modtime = file->modtime; + item->_size = file->size; } else { if (instruction != CSYNC_INSTRUCTION_NONE) { qWarning() << "ERROR: Instruction" << item->_instruction << "vs" << instruction << "for" << fileUtf8; @@ -517,7 +519,7 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote ) if (file->etag && file->etag[0]) { item->_etag = file->etag; } - item->_size = file->size; + if (!item->_inode) { item->_inode = file->inode; From 34e75f80bcb1032c80defc70e1812ce00b8e502a Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Tue, 4 Jul 2017 10:55:41 +0200 Subject: [PATCH 024/107] main.cpp: Fix a compiler warning src/gui/main.cpp:112:9: warning: bool literal returned from 'main' [-Wmain] Used 1 to keep previous behaviour. I supposed the code was meant to return success (0), but it does not really matter anyway. --- src/gui/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/main.cpp b/src/gui/main.cpp index 8f77b50bc2..4c5970a579 100644 --- a/src/gui/main.cpp +++ b/src/gui/main.cpp @@ -109,7 +109,7 @@ int main(int argc, char **argv) // the updater is triggered Updater *updater = Updater::instance(); if (updater && updater->handleStartup()) { - return true; + return 1; } // if the application is already running, notify it. From b1aaf055b10720fcd2ce93acd869d71526f2a121 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Tue, 27 Jun 2017 14:17:26 +0200 Subject: [PATCH 025/107] Blacklist: Don't let errors become warnings #5516 Before, blacklisted errors were set to FileIgnored status and hence displayed as warnings. Now, they have their own BlacklistedError category which allows them to appear as errors in the issues list and in the shell integration icons. --- src/gui/protocolwidget.cpp | 3 ++- src/libsync/owncloudpropagator.cpp | 4 ++-- src/libsync/progressdispatcher.cpp | 3 ++- src/libsync/syncengine.cpp | 16 ++++++++++++---- src/libsync/syncfileitem.h | 13 +++++++++++-- src/libsync/syncfilestatustracker.cpp | 1 + 6 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/gui/protocolwidget.cpp b/src/gui/protocolwidget.cpp index 9d7058dd8b..9803e796bd 100644 --- a/src/gui/protocolwidget.cpp +++ b/src/gui/protocolwidget.cpp @@ -186,7 +186,8 @@ QTreeWidgetItem *ProtocolWidget::createCompletedTreewidgetItem(const QString &fo QIcon icon; if (item._status == SyncFileItem::NormalError - || item._status == SyncFileItem::FatalError) { + || item._status == SyncFileItem::FatalError + || item._status == SyncFileItem::BlacklistedError) { icon = Theme::instance()->syncStateIcon(SyncResult::Error); } else if (Progress::isWarningKind(item._status)) { icon = Theme::instance()->syncStateIcon(SyncResult::Problem); diff --git a/src/libsync/owncloudpropagator.cpp b/src/libsync/owncloudpropagator.cpp index f77dfd860e..48ba29c0a1 100644 --- a/src/libsync/owncloudpropagator.cpp +++ b/src/libsync/owncloudpropagator.cpp @@ -195,8 +195,7 @@ static void blacklistUpdate(SyncJournalDb *journal, SyncFileItem &item) // An ignoreDuration of 0 mean we're tracking the error, but not actively // suppressing it. if (item._hasBlacklistEntry && newEntry._ignoreDuration > 0) { - item._status = SyncFileItem::FileIgnored; - item._errorString.prepend(PropagateItemJob::tr("Continue blacklisting:") + " "); + item._status = SyncFileItem::BlacklistedError; qCInfo(lcPropagator) << "blacklisting " << item._file << " for " << newEntry._ignoreDuration @@ -260,6 +259,7 @@ void PropagateItemJob::done(SyncFileItem::Status statusArg, const QString &error case SyncFileItem::Conflict: case SyncFileItem::FileIgnored: case SyncFileItem::NoStatus: + case SyncFileItem::BlacklistedError: // nothing break; } diff --git a/src/libsync/progressdispatcher.cpp b/src/libsync/progressdispatcher.cpp index 8473bc3ada..2f86888ae4 100644 --- a/src/libsync/progressdispatcher.cpp +++ b/src/libsync/progressdispatcher.cpp @@ -90,7 +90,8 @@ bool Progress::isWarningKind(SyncFileItem::Status kind) { return kind == SyncFileItem::SoftError || kind == SyncFileItem::NormalError || kind == SyncFileItem::FatalError || kind == SyncFileItem::FileIgnored - || kind == SyncFileItem::Conflict || kind == SyncFileItem::Restoration; + || kind == SyncFileItem::Conflict || kind == SyncFileItem::Restoration + || kind == SyncFileItem::BlacklistedError; } bool Progress::isIgnoredKind(SyncFileItem::Status kind) diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index faf003fbf6..5c1f1f10c7 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -256,12 +256,20 @@ bool SyncEngine::checkErrorBlacklisting(SyncFileItem &item) } } + int waitSeconds = entry._lastTryTime + entry._ignoreDuration - now; qCInfo(lcEngine) << "Item is on blacklist: " << entry._file << "retries:" << entry._retryCount - << "for another" << (entry._lastTryTime + entry._ignoreDuration - now) << "s"; - item._instruction = CSYNC_INSTRUCTION_ERROR; - item._status = SyncFileItem::FileIgnored; - item._errorString = tr("The item is not synced because of previous errors: %1").arg(entry._errorString); + << "for another" << waitSeconds << "s"; + + // We need to indicate that we skip this file due to blacklisting + // for reporting and for making sure we don't update the blacklist + // entry yet. + // Classification is this _instruction and _status + item._instruction = CSYNC_INSTRUCTION_IGNORE; + item._status = SyncFileItem::BlacklistedError; + + auto waitSecondsStr = Utility::durationToDescriptiveString1(1000 * waitSeconds); + item._errorString = tr("%1 (skipped due to earlier error, trying again in %2)").arg(entry._errorString, waitSecondsStr); return true; } diff --git a/src/libsync/syncfileitem.h b/src/libsync/syncfileitem.h index 3eefac5cee..b84c9ae33a 100644 --- a/src/libsync/syncfileitem.h +++ b/src/libsync/syncfileitem.h @@ -53,7 +53,7 @@ public: SoftLink = CSYNC_FTW_TYPE_SLINK }; - enum Status { + enum Status { // stored in 4 bits NoStatus, FatalError, ///< Error that causes the sync to stop @@ -63,7 +63,16 @@ public: Success, ///< The file was properly synced Conflict, ///< The file was properly synced, but a conflict was created FileIgnored, ///< The file is in the ignored list (or blacklisted with no retries left) - Restoration ///< The file was restored because what should have been done was not allowed + Restoration, ///< The file was restored because what should have been done was not allowed + + /** For files whose errors were blacklisted. + * + * If _instruction == IGNORE, the file wasn't even reattempted. + * + * These errors should usually be shown as NormalErrors, but not be as loud + * as them. + */ + BlacklistedError }; SyncFileItem() diff --git a/src/libsync/syncfilestatustracker.cpp b/src/libsync/syncfilestatustracker.cpp index db1f778c20..e643099b29 100644 --- a/src/libsync/syncfilestatustracker.cpp +++ b/src/libsync/syncfilestatustracker.cpp @@ -65,6 +65,7 @@ static inline bool showErrorInSocketApi(const SyncFileItem &item) return item._instruction == CSYNC_INSTRUCTION_ERROR || status == SyncFileItem::NormalError || status == SyncFileItem::FatalError + || status == SyncFileItem::BlacklistedError || item._hasBlacklistEntry; } From 9493e8f42ebf068bc7ac66a187636ca0ce708b8d Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Tue, 4 Jul 2017 12:23:23 +0200 Subject: [PATCH 026/107] AccountState: Add a 1-5min reconnection delay #5872 This only applies when the server was explicitly in maintenance mode or when it was 503-unavailable. --- src/gui/accountstate.cpp | 21 +++++++++++++++++++++ src/gui/accountstate.h | 21 +++++++++++++++++---- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/gui/accountstate.cpp b/src/gui/accountstate.cpp index e7ed89bc29..d2025dde18 100644 --- a/src/gui/accountstate.cpp +++ b/src/gui/accountstate.cpp @@ -20,6 +20,7 @@ #include "configfile.h" #include +#include #include namespace OCC { @@ -32,6 +33,7 @@ AccountState::AccountState(AccountPtr account) , _state(AccountState::Disconnected) , _connectionStatus(ConnectionValidator::Undefined) , _waitingForNewCredentials(false) + , _maintenanceToConnectedDelay(60000 + (qrand() % (4 * 60000))) // 1-5min delay { qRegisterMetaType("AccountState*"); @@ -228,6 +230,23 @@ void AccountState::slotConnectionValidatorResult(ConnectionValidator::Status sta return; } + // Come online gradually from 503 or maintenance mode + if (status == ConnectionValidator::Connected + && (_connectionStatus == ConnectionValidator::ServiceUnavailable + || _connectionStatus == ConnectionValidator::MaintenanceMode)) { + if (!_timeSinceMaintenanceOver.isValid()) { + qCInfo(lcAccountState) << "AccountState reconnection: delaying for" + << _maintenanceToConnectedDelay << "ms"; + _timeSinceMaintenanceOver.start(); + QTimer::singleShot(_maintenanceToConnectedDelay + 100, this, SLOT(checkConnectivity())); + return; + } else if (_timeSinceMaintenanceOver.elapsed() < _maintenanceToConnectedDelay) { + qCInfo(lcAccountState) << "AccountState reconnection: only" + << _timeSinceMaintenanceOver.elapsed() << "ms have passed"; + return; + } + } + if (_connectionStatus != status) { qCInfo(lcAccountState) << "AccountState connection status change: " << connectionStatusString(_connectionStatus) << "->" @@ -263,9 +282,11 @@ void AccountState::slotConnectionValidatorResult(ConnectionValidator::Status sta setState(SignedOut); break; case ConnectionValidator::ServiceUnavailable: + _timeSinceMaintenanceOver.invalidate(); setState(ServiceUnavailable); break; case ConnectionValidator::MaintenanceMode: + _timeSinceMaintenanceOver.invalidate(); setState(MaintenanceMode); break; case ConnectionValidator::Timeout: diff --git a/src/gui/accountstate.h b/src/gui/accountstate.h index 450766bf82..2333bcece8 100644 --- a/src/gui/accountstate.h +++ b/src/gui/accountstate.h @@ -107,10 +107,6 @@ public: bool isConnected() const; - /// Triggers a ping to the server to update state and - /// connection status and errors. - void checkConnectivity(); - /** Returns a new settings object for this account, already in the right groups. */ std::unique_ptr settings(); @@ -127,6 +123,11 @@ public: */ void tagLastSuccessfullETagRequest(); +public slots: + /// Triggers a ping to the server to update state and + /// connection status and errors. + void checkConnectivity(); + private: void setState(State state); @@ -148,6 +149,18 @@ private: bool _waitingForNewCredentials; QElapsedTimer _timeSinceLastETagCheck; QPointer _connectionValidator; + + /** + * Starts counting when the server starts being back up after 503 or + * maintenance mode. The account will only become connected once this + * timer exceeds the _maintenanceToConnectedDelay value. + */ + QElapsedTimer _timeSinceMaintenanceOver; + + /** + * Milliseconds for which to delay reconnection after 503/maintenance. + */ + int _maintenanceToConnectedDelay; }; } From ce8341ca1fd21d946e8c056430025f08d586e380 Mon Sep 17 00:00:00 2001 From: ckamm Date: Tue, 4 Jul 2017 14:08:41 +0200 Subject: [PATCH 027/107] Add a more functional error view #5516 (#5861) * Add a more functional error view #5516 * Allow filtering of ignores and warnings to see only important bits. * Navigate from the folder view to the error view by clicking on the error list with the red background. * Move the error list into its own ui file to allow easier extension. * Fix issue around tab id handling in ActivitySettings. * Rename "Action" column to "Issue". * Change mouse cursor to hand over button and new error list area Several OSX fixes provided by guruz. --- src/gui/CMakeLists.txt | 2 + src/gui/accountsettings.cpp | 49 ++++++ src/gui/accountsettings.h | 1 + src/gui/activitywidget.cpp | 48 ++--- src/gui/activitywidget.h | 5 + src/gui/folderstatusdelegate.cpp | 10 ++ src/gui/folderstatusdelegate.h | 1 + src/gui/issueswidget.cpp | 294 +++++++++++++++++++++++++++++++ src/gui/issueswidget.h | 82 +++++++++ src/gui/issueswidget.ui | 161 +++++++++++++++++ src/gui/protocolwidget.cpp | 107 ++--------- src/gui/protocolwidget.h | 17 +- src/gui/settingsdialog.cpp | 9 + src/gui/settingsdialog.h | 1 + src/gui/settingsdialogmac.cpp | 9 + src/gui/settingsdialogmac.h | 1 + 16 files changed, 665 insertions(+), 132 deletions(-) create mode 100644 src/gui/issueswidget.cpp create mode 100644 src/gui/issueswidget.h create mode 100644 src/gui/issueswidget.ui diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 643bb932cd..e40c030346 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -21,6 +21,7 @@ set(client_UI ignorelisteditor.ui networksettings.ui protocolwidget.ui + issueswidget.ui activitywidget.ui synclogdialog.ui settingsdialog.ui @@ -64,6 +65,7 @@ set(client_SRCS owncloudgui.cpp owncloudsetupwizard.cpp protocolwidget.cpp + issueswidget.cpp activitydata.cpp activitylistmodel.cpp activitywidget.cpp diff --git a/src/gui/accountsettings.cpp b/src/gui/accountsettings.cpp index 2f866502ad..6da34d5904 100644 --- a/src/gui/accountsettings.cpp +++ b/src/gui/accountsettings.cpp @@ -69,6 +69,42 @@ static const char progressBarStyleC[] = "background-color: %1; width: 1px;" "}"; +/** + * Adjusts the mouse cursor based on the region it is on over the folder tree view. + * + * Used to show that one can click the red error list box by changing the cursor + * to the pointing hand. + */ +class MouseCursorChanger : public QObject +{ + Q_OBJECT +public: + MouseCursorChanger(QObject *parent) + : QObject(parent) + { + } + + QTreeView *folderList; + FolderStatusModel *model; + +protected: + bool eventFilter(QObject *watched, QEvent *event) override + { + if (event->type() == QEvent::HoverMove) { + Qt::CursorShape shape = Qt::ArrowCursor; + auto pos = folderList->mapFromGlobal(QCursor::pos()); + auto index = folderList->indexAt(pos); + if (model->classify(index) == FolderStatusModel::RootFolder + && (FolderStatusDelegate::errorsListRect(folderList->visualRect(index)).contains(pos) + || FolderStatusDelegate::optionsButtonRect(folderList->visualRect(index),folderList->layoutDirection()).contains(pos))) { + shape = Qt::PointingHandCursor; + } + folderList->setCursor(shape); + } + return QObject::eventFilter(watched, event); + } +}; + AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent) : QWidget(parent) , ui(new Ui::AccountSettings) @@ -94,6 +130,13 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent) #endif new ToolTipUpdater(ui->_folderList); + auto mouseCursorChanger = new MouseCursorChanger(this); + mouseCursorChanger->folderList = ui->_folderList; + mouseCursorChanger->model = _model; + ui->_folderList->setMouseTracking(true); + ui->_folderList->setAttribute(Qt::WA_Hover, true); + ui->_folderList->installEventFilter(mouseCursorChanger); + createAccountToolbox(); connect(AccountManager::instance(), SIGNAL(accountAdded(AccountState *)), SLOT(slotAccountAdded(AccountState *))); @@ -301,6 +344,10 @@ void AccountSettings::slotFolderListClicked(const QModelIndex &indx) slotCustomContextMenuRequested(pos); return; } + if (FolderStatusDelegate::errorsListRect(tv->visualRect(indx)).contains(pos)) { + emit showIssuesList(_model->data(indx, FolderStatusDelegate::FolderAliasRole).toString()); + return; + } // Expand root items on single click if (_accountState && _accountState->state() == AccountState::Connected) { @@ -808,3 +855,5 @@ bool AccountSettings::event(QEvent *e) } } // namespace OCC + +#include "accountsettings.moc" diff --git a/src/gui/accountsettings.h b/src/gui/accountsettings.h index 9c4a1fc569..3e45d7e4a7 100644 --- a/src/gui/accountsettings.h +++ b/src/gui/accountsettings.h @@ -60,6 +60,7 @@ public: signals: void folderChanged(); void openFolderAlias(const QString &); + void showIssuesList(const QString &folderAlias); public slots: void slotOpenOC(); diff --git a/src/gui/activitywidget.cpp b/src/gui/activitywidget.cpp index 2d40ce8ee4..c4e82884b1 100644 --- a/src/gui/activitywidget.cpp +++ b/src/gui/activitywidget.cpp @@ -33,6 +33,7 @@ #include "accountmanager.h" #include "activityitemdelegate.h" #include "protocolwidget.h" +#include "issueswidget.h" #include "QProgressIndicator.h" #include "notificationwidget.h" #include "notificationconfirmjob.h" @@ -518,33 +519,23 @@ ActivitySettings::ActivitySettings(QWidget *parent) _tab = new QTabWidget(this); hbox->addWidget(_tab); _activityWidget = new ActivityWidget(this); - _activityTabId = _tab->insertTab(0, _activityWidget, Theme::instance()->applicationIcon(), tr("Server Activity")); + _activityTabId = _tab->addTab(_activityWidget, Theme::instance()->applicationIcon(), tr("Server Activity")); connect(_activityWidget, SIGNAL(copyToClipboard()), this, SLOT(slotCopyToClipboard())); connect(_activityWidget, SIGNAL(hideActivityTab(bool)), this, SLOT(setActivityTabHidden(bool))); connect(_activityWidget, SIGNAL(guiLog(QString, QString)), this, SIGNAL(guiLog(QString, QString))); connect(_activityWidget, SIGNAL(newNotification()), SLOT(slotShowActivityTab())); _protocolWidget = new ProtocolWidget(this); - _tab->insertTab(1, _protocolWidget, Theme::instance()->syncStateIcon(SyncResult::Success), tr("Sync Protocol")); + _protocolTabId = _tab->addTab(_protocolWidget, Theme::instance()->syncStateIcon(SyncResult::Success), tr("Sync Protocol")); connect(_protocolWidget, SIGNAL(copyToClipboard()), this, SLOT(slotCopyToClipboard())); - connect(_protocolWidget, SIGNAL(issueItemCountUpdated(int)), - this, SLOT(slotShowIssueItemCount(int))); - // Add the not-synced list into the tab - QWidget *w = new QWidget; - QVBoxLayout *vbox2 = new QVBoxLayout(w); - vbox2->addWidget(new QLabel(tr("List of ignored or erroneous files"), this)); - vbox2->addWidget(_protocolWidget->issueWidget()); - QDialogButtonBox *dlgButtonBox = new QDialogButtonBox(this); - vbox2->addWidget(dlgButtonBox); - QPushButton *_copyBtn = dlgButtonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole); - _copyBtn->setToolTip(tr("Copy the activity list to the clipboard.")); - _copyBtn->setEnabled(true); - connect(_copyBtn, SIGNAL(clicked()), this, SLOT(slotCopyToClipboard())); - - w->setLayout(vbox2); - _syncIssueTabId = _tab->insertTab(2, w, Theme::instance()->syncStateIcon(SyncResult::Problem), QString()); + _issuesWidget = new IssuesWidget(this); + _syncIssueTabId = _tab->addTab(_issuesWidget, Theme::instance()->syncStateIcon(SyncResult::Problem), QString()); slotShowIssueItemCount(0); // to display the label. + connect(_issuesWidget, SIGNAL(issueCountUpdated(int)), + this, SLOT(slotShowIssueItemCount(int))); + connect(_issuesWidget, SIGNAL(copyToClipboard()), + this, SLOT(slotCopyToClipboard())); // Add a progress indicator to spin if the acitivity list is updated. _progressIndicator = new QProgressIndicator(this); @@ -571,10 +562,14 @@ void ActivitySettings::setActivityTabHidden(bool hidden) if (hidden && _activityTabId > -1) { _tab->removeTab(_activityTabId); _activityTabId = -1; + _protocolTabId -= 1; + _syncIssueTabId -= 1; } if (!hidden && _activityTabId == -1) { _activityTabId = _tab->insertTab(0, _activityWidget, Theme::instance()->applicationIcon(), tr("Server Activity")); + _protocolTabId += 1; + _syncIssueTabId += 1; } } @@ -595,6 +590,15 @@ void ActivitySettings::slotShowActivityTab() } } +void ActivitySettings::slotShowIssuesTab(const QString &folderAlias) +{ + if (_syncIssueTabId == -1) + return; + _tab->setCurrentIndex(_syncIssueTabId); + + _issuesWidget->showFolderErrors(folderAlias); +} + void ActivitySettings::slotCopyToClipboard() { QString text; @@ -603,18 +607,18 @@ void ActivitySettings::slotCopyToClipboard() int idx = _tab->currentIndex(); QString message; - if (idx == 0) { + if (idx == _activityTabId) { // the activity widget _activityWidget->storeActivityList(ts); message = tr("The server activity list has been copied to the clipboard."); - } else if (idx == 1) { + } else if (idx == _protocolTabId) { // the protocol widget _protocolWidget->storeSyncActivity(ts); message = tr("The sync activity list has been copied to the clipboard."); - } else if (idx == 2) { + } else if (idx == _syncIssueTabId) { // issues Widget message = tr("The list of unsynced items has been copied to the clipboard."); - _protocolWidget->storeSyncIssues(ts); + _issuesWidget->storeSyncIssues(ts); } QApplication::clipboard()->setText(text); diff --git a/src/gui/activitywidget.h b/src/gui/activitywidget.h index bae9b6ff95..e2fa314e05 100644 --- a/src/gui/activitywidget.h +++ b/src/gui/activitywidget.h @@ -35,6 +35,7 @@ namespace OCC { class Account; class AccountStatusPtr; class ProtocolWidget; +class IssuesWidget; class JsonApiJob; class NotificationWidget; class ActivityListModel; @@ -138,6 +139,8 @@ public slots: void setNotificationRefreshInterval(quint64 interval); + void slotShowIssuesTab(const QString &folderAlias); + private slots: void slotCopyToClipboard(); void setActivityTabHidden(bool hidden); @@ -153,10 +156,12 @@ private: QTabWidget *_tab; int _activityTabId; + int _protocolTabId; int _syncIssueTabId; ActivityWidget *_activityWidget; ProtocolWidget *_protocolWidget; + IssuesWidget *_issuesWidget; QProgressIndicator *_progressIndicator; QTimer _notificationCheckTimer; QHash _timeSinceLastCheck; diff --git a/src/gui/folderstatusdelegate.cpp b/src/gui/folderstatusdelegate.cpp index ddb5038675..1d93893747 100644 --- a/src/gui/folderstatusdelegate.cpp +++ b/src/gui/folderstatusdelegate.cpp @@ -373,5 +373,15 @@ QRect FolderStatusDelegate::optionsButtonRect(QRect within, Qt::LayoutDirection return QStyle::visualRect(direction, within, r); } +QRect FolderStatusDelegate::errorsListRect(QRect within) +{ + QFont font = QFont(); + QFont aliasFont = makeAliasFont(font); + QFontMetrics fm(font); + QFontMetrics aliasFm(aliasFont); + within.setTop(within.top() + FolderStatusDelegate::rootFolderHeightWithoutErrors(fm, aliasFm)); + return within; +} + } // namespace OCC diff --git a/src/gui/folderstatusdelegate.h b/src/gui/folderstatusdelegate.h index b42ae134d0..a3fd5b71d2 100644 --- a/src/gui/folderstatusdelegate.h +++ b/src/gui/folderstatusdelegate.h @@ -56,6 +56,7 @@ public: * return the position of the option button within the item */ static QRect optionsButtonRect(QRect within, Qt::LayoutDirection direction); + static QRect errorsListRect(QRect within); static int rootFolderHeightWithoutErrors(const QFontMetrics &fm, const QFontMetrics &aliasFm); private: diff --git a/src/gui/issueswidget.cpp b/src/gui/issueswidget.cpp new file mode 100644 index 0000000000..eff8a3b454 --- /dev/null +++ b/src/gui/issueswidget.cpp @@ -0,0 +1,294 @@ +/* + * Copyright (C) by Klaas Freitag + * + * 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 +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) +#include +#endif + +#include "issueswidget.h" +#include "configfile.h" +#include "syncresult.h" +#include "logger.h" +#include "utility.h" +#include "theme.h" +#include "folderman.h" +#include "syncfileitem.h" +#include "folder.h" +#include "openfilemanager.h" +#include "activityitemdelegate.h" +#include "protocolwidget.h" +#include "accountstate.h" +#include "account.h" +#include "accountmanager.h" + +#include "ui_issueswidget.h" + +#include + +namespace OCC { + +IssuesWidget::IssuesWidget(QWidget *parent) + : QWidget(parent) + , _ui(new Ui::IssuesWidget) +{ + _ui->setupUi(this); + + connect(ProgressDispatcher::instance(), SIGNAL(progressInfo(QString, ProgressInfo)), + this, SLOT(slotProgressInfo(QString, ProgressInfo))); + connect(ProgressDispatcher::instance(), SIGNAL(itemCompleted(QString, SyncFileItemPtr)), + this, SLOT(slotItemCompleted(QString, SyncFileItemPtr))); + + connect(_ui->_treeWidget, SIGNAL(itemActivated(QTreeWidgetItem *, int)), SLOT(slotOpenFile(QTreeWidgetItem *, int))); + connect(_ui->copyIssuesButton, SIGNAL(clicked()), SIGNAL(copyToClipboard())); + + connect(_ui->showIgnores, SIGNAL(toggled(bool)), SLOT(slotRefreshIssues())); + connect(_ui->showWarnings, SIGNAL(toggled(bool)), SLOT(slotRefreshIssues())); + connect(_ui->filterAccount, SIGNAL(currentIndexChanged(int)), SLOT(slotRefreshIssues())); + connect(_ui->filterAccount, SIGNAL(currentIndexChanged(int)), SLOT(slotUpdateFolderFilters())); + connect(_ui->filterFolder, SIGNAL(currentIndexChanged(int)), SLOT(slotRefreshIssues())); + for (auto account : AccountManager::instance()->accounts()) { + slotAccountAdded(account.data()); + } + connect(AccountManager::instance(), SIGNAL(accountAdded(AccountState *)), + SLOT(slotAccountAdded(AccountState *))); + connect(AccountManager::instance(), SIGNAL(accountRemoved(AccountState *)), + SLOT(slotAccountRemoved(AccountState *))); + + + // Adjust copyToClipboard() when making changes here! + QStringList header; + header << tr("Time"); + header << tr("File"); + header << tr("Folder"); + header << tr("Issue"); + + int timestampColumnExtra = 0; +#ifdef Q_OS_WIN + timestampColumnExtra = 20; // font metrics are broken on Windows, see #4721 +#endif + + _ui->_treeWidget->setHeaderLabels(header); + int timestampColumnWidth = + ActivityItemDelegate::rowHeight() // icon + + _ui->_treeWidget->fontMetrics().width(ProtocolWidget::timeString(QDateTime::currentDateTime())) + + timestampColumnExtra; + _ui->_treeWidget->setColumnWidth(0, timestampColumnWidth); + _ui->_treeWidget->setColumnWidth(1, 180); + _ui->_treeWidget->setColumnCount(4); + _ui->_treeWidget->setRootIsDecorated(false); + _ui->_treeWidget->setTextElideMode(Qt::ElideMiddle); + _ui->_treeWidget->header()->setObjectName("ActivityErrorListHeader"); +#if defined(Q_OS_MAC) + _ui->_treeWidget->setMinimumWidth(400); +#endif +} + +IssuesWidget::~IssuesWidget() +{ + delete _ui; +} + +void IssuesWidget::showEvent(QShowEvent *ev) +{ + ConfigFile cfg; + cfg.restoreGeometryHeader(_ui->_treeWidget->header()); + QWidget::showEvent(ev); +} + +void IssuesWidget::hideEvent(QHideEvent *ev) +{ + ConfigFile cfg; + cfg.saveGeometryHeader(_ui->_treeWidget->header()); + QWidget::hideEvent(ev); +} + +void IssuesWidget::cleanItems(const QString &folder) +{ + // The issue list is a state, clear it and let the next sync fill it + // with ignored files and propagation errors. + int itemCnt = _ui->_treeWidget->topLevelItemCount(); + for (int cnt = itemCnt - 1; cnt >= 0; cnt--) { + QTreeWidgetItem *item = _ui->_treeWidget->topLevelItem(cnt); + QString itemFolder = item->data(2, Qt::UserRole).toString(); + if (itemFolder == folder) { + delete item; + } + } + // update the tabtext + emit(issueCountUpdated(_ui->_treeWidget->topLevelItemCount())); +} + +void IssuesWidget::slotOpenFile(QTreeWidgetItem *item, int) +{ + QString folderName = item->data(2, Qt::UserRole).toString(); + QString fileName = item->text(1); + + Folder *folder = FolderMan::instance()->folder(folderName); + if (folder) { + // folder->path() always comes back with trailing path + QString fullPath = folder->path() + fileName; + if (QFile(fullPath).exists()) { + showInFileManager(fullPath); + } + } +} + +void IssuesWidget::slotProgressInfo(const QString &folder, const ProgressInfo &progress) +{ + if (!progress.isUpdatingEstimates()) { + // The sync is restarting, clean the old items + cleanItems(folder); + } else if (progress.completedFiles() >= progress.totalFiles()) { + //Sync completed + } +} + +void IssuesWidget::slotItemCompleted(const QString &folder, const SyncFileItemPtr &item) +{ + if (!item->hasErrorStatus()) + return; + QTreeWidgetItem *line = ProtocolWidget::createCompletedTreewidgetItem(folder, *item); + if (!line) + return; + + _ui->_treeWidget->insertTopLevelItem(0, line); + line->setHidden(!shouldBeVisible(line, currentAccountFilter(), currentFolderFilter())); + emit issueCountUpdated(_ui->_treeWidget->topLevelItemCount()); +} + +void IssuesWidget::slotRefreshIssues() +{ + auto tree = _ui->_treeWidget; + auto filterFolderAlias = currentFolderFilter(); + auto filterAccount = currentAccountFilter(); + + for (int i = 0; i < tree->topLevelItemCount(); ++i) { + auto item = tree->topLevelItem(i); + item->setHidden(!shouldBeVisible(item, filterAccount, filterFolderAlias)); + } +} + +void IssuesWidget::slotAccountAdded(AccountState *account) +{ + _ui->filterAccount->addItem(account->account()->displayName(), QVariant::fromValue(account)); +} + +void IssuesWidget::slotAccountRemoved(AccountState *account) +{ + for (int i = _ui->filterAccount->count() - 1; i >= 0; --i) { + if (account == _ui->filterAccount->itemData(i).value()) + _ui->filterAccount->removeItem(i); + } +} + +AccountState *IssuesWidget::currentAccountFilter() const +{ + return _ui->filterAccount->currentData().value(); +} + +QString IssuesWidget::currentFolderFilter() const +{ + return _ui->filterFolder->currentData().toString(); +} + +bool IssuesWidget::shouldBeVisible(QTreeWidgetItem *item, AccountState *filterAccount, + const QString &filterFolderAlias) const +{ + bool visible = true; + auto status = item->data(0, Qt::UserRole); + visible &= (_ui->showIgnores->isChecked() || status != SyncFileItem::FileIgnored); + visible &= (_ui->showWarnings->isChecked() + || (status != SyncFileItem::SoftError + && status != SyncFileItem::Conflict + && status != SyncFileItem::Restoration)); + + auto folderalias = item->data(2, Qt::UserRole).toString(); + if (filterAccount) { + auto folder = FolderMan::instance()->folder(folderalias); + visible &= folder && folder->accountState() == filterAccount; + } + visible &= (filterFolderAlias.isEmpty() || filterFolderAlias == folderalias); + + return visible; +} + +void IssuesWidget::slotUpdateFolderFilters() +{ + auto account = _ui->filterAccount->currentData().value(); + + if (!account) { + _ui->filterFolder->setCurrentIndex(0); + } + _ui->filterFolder->setEnabled(account != 0); + + for (int i = _ui->filterFolder->count() - 1; i >= 1; --i) { + _ui->filterFolder->removeItem(i); + } + for (auto folder : FolderMan::instance()->map().values()) { + if (folder->accountState() != account) + continue; + _ui->filterFolder->addItem(folder->shortGuiLocalPath(), folder->alias()); + } +} + +void IssuesWidget::storeSyncIssues(QTextStream &ts) +{ + int topLevelItems = _ui->_treeWidget->topLevelItemCount(); + + for (int i = 0; i < topLevelItems; i++) { + QTreeWidgetItem *child = _ui->_treeWidget->topLevelItem(i); + if (child->isHidden()) + continue; + ts << right + // time stamp + << qSetFieldWidth(20) + << child->data(0, Qt::DisplayRole).toString() + // separator + << qSetFieldWidth(0) << "," + + // file name + << qSetFieldWidth(64) + << child->data(1, Qt::DisplayRole).toString() + // separator + << qSetFieldWidth(0) << "," + + // folder + << qSetFieldWidth(30) + << child->data(2, Qt::DisplayRole).toString() + // separator + << qSetFieldWidth(0) << "," + + // action + << qSetFieldWidth(15) + << child->data(3, Qt::DisplayRole).toString() + << qSetFieldWidth(0) + << endl; + } +} + +void IssuesWidget::showFolderErrors(const QString &folderAlias) +{ + auto folder = FolderMan::instance()->folder(folderAlias); + if (!folder) + return; + + _ui->filterAccount->setCurrentIndex( + qMax(0, _ui->filterAccount->findData(QVariant::fromValue(folder->accountState())))); + _ui->filterFolder->setCurrentIndex( + qMax(0, _ui->filterFolder->findData(folderAlias))); + _ui->showIgnores->setChecked(false); + _ui->showWarnings->setChecked(false); +} +} diff --git a/src/gui/issueswidget.h b/src/gui/issueswidget.h new file mode 100644 index 0000000000..9d31e16e16 --- /dev/null +++ b/src/gui/issueswidget.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) by Klaas Freitag + * + * 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 ISSUESWIDGET_H +#define ISSUESWIDGET_H + +#include +#include +#include + +#include "progressdispatcher.h" +#include "owncloudgui.h" + +#include "ui_issueswidget.h" + +class QPushButton; + +namespace OCC { +class SyncResult; + +namespace Ui { + class ProtocolWidget; +} +class Application; + +/** + * @brief The ProtocolWidget class + * @ingroup gui + */ +class IssuesWidget : public QWidget +{ + Q_OBJECT +public: + explicit IssuesWidget(QWidget *parent = 0); + ~IssuesWidget(); + QSize sizeHint() const { return ownCloudGui::settingsDialogSize(); } + + void storeSyncIssues(QTextStream &ts); + void showFolderErrors(const QString &folderAlias); + +public slots: + void slotProgressInfo(const QString &folder, const ProgressInfo &progress); + void slotItemCompleted(const QString &folder, const SyncFileItemPtr &item); + void slotOpenFile(QTreeWidgetItem *item, int); + +protected: + void showEvent(QShowEvent *); + void hideEvent(QHideEvent *); + +signals: + void copyToClipboard(); + void issueCountUpdated(int); + +private slots: + void slotRefreshIssues(); + void slotUpdateFolderFilters(); + void slotAccountAdded(AccountState *account); + void slotAccountRemoved(AccountState *account); + +private: + AccountState *currentAccountFilter() const; + QString currentFolderFilter() const; + bool shouldBeVisible(QTreeWidgetItem *item, AccountState *filterAccount, + const QString &filterFolderAlias) const; + void cleanItems(const QString &folder); + + Ui::IssuesWidget *_ui; +}; +} + +#endif diff --git a/src/gui/issueswidget.ui b/src/gui/issueswidget.ui new file mode 100644 index 0000000000..eb15d3d8c4 --- /dev/null +++ b/src/gui/issueswidget.ui @@ -0,0 +1,161 @@ + + + OCC::IssuesWidget + + + + 0 + 0 + 580 + 578 + + + + Form + + + + + + List of issues + + + Qt::PlainText + + + + + + + + + + + Account + + + + + + + + <no filter> + + + + + + + + Folder + + + + + + + false + + + + <no filter> + + + + + + + + + + + + Show warnings + + + true + + + + + + + Show ignored files + + + true + + + + + + + + + + + true + + + false + + + true + + + 4 + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Copy the issues list to the clipboard. + + + Copy + + + + + + + + + + diff --git a/src/gui/protocolwidget.cpp b/src/gui/protocolwidget.cpp index 9803e796bd..2f177a1c64 100644 --- a/src/gui/protocolwidget.cpp +++ b/src/gui/protocolwidget.cpp @@ -37,13 +37,10 @@ namespace OCC { ProtocolWidget::ProtocolWidget(QWidget *parent) : QWidget(parent) - , IgnoredIndicatorRole(Qt::UserRole + 1) , _ui(new Ui::ProtocolWidget) { _ui->setupUi(this); - connect(ProgressDispatcher::instance(), SIGNAL(progressInfo(QString, ProgressInfo)), - this, SLOT(slotProgressInfo(QString, ProgressInfo))); connect(ProgressDispatcher::instance(), SIGNAL(itemCompleted(QString, SyncFileItemPtr)), this, SLOT(slotItemCompleted(QString, SyncFileItemPtr))); @@ -81,25 +78,6 @@ ProtocolWidget::ProtocolWidget(QWidget *parent) copyBtn->setToolTip(tr("Copy the activity list to the clipboard.")); copyBtn->setEnabled(true); connect(copyBtn, SIGNAL(clicked()), SIGNAL(copyToClipboard())); - - // this view is used to display all errors such as real errors, soft errors and ignored files - // it is instantiated here, but made accessible via the method issueWidget() so that it can - // be embedded into another gui element. - _issueItemView = new QTreeWidget(this); - header.removeLast(); - _issueItemView->setHeaderLabels(header); - timestampColumnWidth = - ActivityItemDelegate::rowHeight() // icon - + _issueItemView->fontMetrics().width(timeString(QDateTime::currentDateTime())) - + timestampColumnExtra; - _issueItemView->setColumnWidth(0, timestampColumnWidth); - _issueItemView->setColumnWidth(1, 180); - _issueItemView->setColumnCount(4); - _issueItemView->setRootIsDecorated(false); - _issueItemView->setTextElideMode(Qt::ElideMiddle); - _issueItemView->header()->setObjectName("ActivityErrorListHeader"); - connect(_issueItemView, SIGNAL(itemActivated(QTreeWidgetItem *, int)), - SLOT(slotOpenFile(QTreeWidgetItem *, int))); } ProtocolWidget::~ProtocolWidget() @@ -121,23 +99,8 @@ void ProtocolWidget::hideEvent(QHideEvent *ev) QWidget::hideEvent(ev); } -void ProtocolWidget::cleanItems(const QString &folder) -{ - // The issue list is a state, clear it and let the next sync fill it - // with ignored files and propagation errors. - int itemCnt = _issueItemView->topLevelItemCount(); - for (int cnt = itemCnt - 1; cnt >= 0; cnt--) { - QTreeWidgetItem *item = _issueItemView->topLevelItem(cnt); - QString itemFolder = item->data(2, Qt::UserRole).toString(); - if (itemFolder == folder) { - delete item; - } - } - // update the tabtext - emit(issueItemCountUpdated(_issueItemView->topLevelItemCount())); -} -QString ProtocolWidget::timeString(QDateTime dt, QLocale::FormatType format) const +QString ProtocolWidget::timeString(QDateTime dt, QLocale::FormatType format) { const QLocale loc = QLocale::system(); QString dtFormat = loc.dateTimeFormat(format); @@ -198,50 +161,32 @@ QTreeWidgetItem *ProtocolWidget::createCompletedTreewidgetItem(const QString &fo } QTreeWidgetItem *twitem = new QTreeWidgetItem(columns); - if (item._status == SyncFileItem::FileIgnored) { - // Tell that we want to remove it on the next sync. - twitem->setData(0, IgnoredIndicatorRole, true); - } - twitem->setData(0, Qt::SizeHintRole, QSize(0, ActivityItemDelegate::rowHeight())); twitem->setIcon(0, icon); twitem->setToolTip(0, longTimeStr); twitem->setToolTip(1, item._file); twitem->setToolTip(3, message); + twitem->setData(0, Qt::UserRole, item._status); twitem->setData(2, Qt::UserRole, folder); return twitem; } -void ProtocolWidget::slotProgressInfo(const QString &folder, const ProgressInfo &progress) -{ - if (!progress.isUpdatingEstimates()) { - // The sync is restarting, clean the old items - cleanItems(folder); - } else if (progress.completedFiles() >= progress.totalFiles()) { - //Sync completed - } -} - void ProtocolWidget::slotItemCompleted(const QString &folder, const SyncFileItemPtr &item) { + if (item->hasErrorStatus()) + return; QTreeWidgetItem *line = createCompletedTreewidgetItem(folder, *item); if (line) { - if (item->hasErrorStatus()) { - _issueItemView->insertTopLevelItem(0, line); - emit issueItemCountUpdated(_issueItemView->topLevelItemCount()); - } else { - // Limit the number of items - int itemCnt = _ui->_treeWidget->topLevelItemCount(); - while (itemCnt > 2000) { - delete _ui->_treeWidget->takeTopLevelItem(itemCnt - 1); - itemCnt--; - } - _ui->_treeWidget->insertTopLevelItem(0, line); + // Limit the number of items + int itemCnt = _ui->_treeWidget->topLevelItemCount(); + while (itemCnt > 2000) { + delete _ui->_treeWidget->takeTopLevelItem(itemCnt - 1); + itemCnt--; } + _ui->_treeWidget->insertTopLevelItem(0, line); } } - void ProtocolWidget::storeSyncActivity(QTextStream &ts) { int topLevelItems = _ui->_treeWidget->topLevelItemCount(); @@ -281,36 +226,4 @@ void ProtocolWidget::storeSyncActivity(QTextStream &ts) } } -void ProtocolWidget::storeSyncIssues(QTextStream &ts) -{ - int topLevelItems = _issueItemView->topLevelItemCount(); - - for (int i = 0; i < topLevelItems; i++) { - QTreeWidgetItem *child = _issueItemView->topLevelItem(i); - ts << right - // time stamp - << qSetFieldWidth(20) - << child->data(0, Qt::DisplayRole).toString() - // separator - << qSetFieldWidth(0) << "," - - // file name - << qSetFieldWidth(64) - << child->data(1, Qt::DisplayRole).toString() - // separator - << qSetFieldWidth(0) << "," - - // folder - << qSetFieldWidth(30) - << child->data(2, Qt::DisplayRole).toString() - // separator - << qSetFieldWidth(0) << "," - - // action - << qSetFieldWidth(15) - << child->data(3, Qt::DisplayRole).toString() - << qSetFieldWidth(0) - << endl; - } -} } diff --git a/src/gui/protocolwidget.h b/src/gui/protocolwidget.h index 927a23c4b1..7e3a154a5c 100644 --- a/src/gui/protocolwidget.h +++ b/src/gui/protocolwidget.h @@ -46,12 +46,13 @@ public: ~ProtocolWidget(); QSize sizeHint() const { return ownCloudGui::settingsDialogSize(); } - QTreeWidget *issueWidget() { return _issueItemView; } void storeSyncActivity(QTextStream &ts); - void storeSyncIssues(QTextStream &ts); + + // Shared with IssueWidget + static QTreeWidgetItem *createCompletedTreewidgetItem(const QString &folder, const SyncFileItem &item); + static QString timeString(QDateTime dt, QLocale::FormatType format = QLocale::NarrowFormat); public slots: - void slotProgressInfo(const QString &folder, const ProgressInfo &progress); void slotItemCompleted(const QString &folder, const SyncFileItemPtr &item); void slotOpenFile(QTreeWidgetItem *item, int); @@ -61,19 +62,9 @@ protected: signals: void copyToClipboard(); - void issueItemCountUpdated(int); private: - void setSyncResultStatus(const SyncResult &result); - void cleanItems(const QString &folder); - - QTreeWidgetItem *createCompletedTreewidgetItem(const QString &folder, const SyncFileItem &item); - - QString timeString(QDateTime dt, QLocale::FormatType format = QLocale::NarrowFormat) const; - - const int IgnoredIndicatorRole; Ui::ProtocolWidget *_ui; - QTreeWidget *_issueItemView; }; } #endif // PROTOCOLWIDGET_H diff --git a/src/gui/settingsdialog.cpp b/src/gui/settingsdialog.cpp index bb44a080aa..f21ecd5f8f 100644 --- a/src/gui/settingsdialog.cpp +++ b/src/gui/settingsdialog.cpp @@ -211,6 +211,14 @@ void SettingsDialog::showActivityPage() } } +void SettingsDialog::showIssuesList(const QString &folderAlias) +{ + if (!_activityAction) + return; + _activityAction->trigger(); + _activitySettings->slotShowIssuesTab(folderAlias); +} + void SettingsDialog::accountAdded(AccountState *s) { auto height = _toolBar->sizeHint().height(); @@ -242,6 +250,7 @@ void SettingsDialog::accountAdded(AccountState *s) connect(accountSettings, SIGNAL(folderChanged()), _gui, SLOT(slotFoldersChanged())); connect(accountSettings, SIGNAL(openFolderAlias(const QString &)), _gui, SLOT(slotFolderOpenAction(QString))); + connect(accountSettings, SIGNAL(showIssuesList(QString)), SLOT(showIssuesList(QString))); connect(s->account().data(), SIGNAL(accountChangedAvatar()), SLOT(slotAccountAvatarChanged())); slotRefreshActivity(s); diff --git a/src/gui/settingsdialog.h b/src/gui/settingsdialog.h index 9d44599599..15596a00f0 100644 --- a/src/gui/settingsdialog.h +++ b/src/gui/settingsdialog.h @@ -56,6 +56,7 @@ public: public slots: void showFirstPage(); void showActivityPage(); + void showIssuesList(const QString &folderAlias); void slotSwitchPage(QAction *action); void slotRefreshActivity(AccountState *accountState); void slotAccountAvatarChanged(); diff --git a/src/gui/settingsdialogmac.cpp b/src/gui/settingsdialogmac.cpp index 486bc70184..db4fad95fc 100644 --- a/src/gui/settingsdialogmac.cpp +++ b/src/gui/settingsdialogmac.cpp @@ -133,6 +133,14 @@ void SettingsDialogMac::showActivityPage() setCurrentPanelIndex(preferencePanelCount() - 1 - 2); } +void SettingsDialogMac::showIssuesList(const QString &folderAlias) +{ + // Count backwards (0-based) from the last panel (multiple accounts can be on the left) + setCurrentPanelIndex(preferencePanelCount() - 1 - 2); + _activitySettings->slotShowIssuesTab(folderAlias); +} + + void SettingsDialogMac::accountAdded(AccountState *s) { QIcon accountIcon = MacStandardIcon::icon(MacStandardIcon::UserAccounts); @@ -144,6 +152,7 @@ void SettingsDialogMac::accountAdded(AccountState *s) connect(accountSettings, &AccountSettings::folderChanged, _gui, &ownCloudGui::slotFoldersChanged); connect(accountSettings, &AccountSettings::openFolderAlias, _gui, &ownCloudGui::slotFolderOpenAction); + connect(accountSettings, &AccountSettings::showIssuesList, this, &SettingsDialogMac::showIssuesList); connect(s->account().data(), SIGNAL(accountChangedAvatar()), this, SLOT(slotAccountAvatarChanged())); diff --git a/src/gui/settingsdialogmac.h b/src/gui/settingsdialogmac.h index f931f90095..c69ee5f140 100644 --- a/src/gui/settingsdialogmac.h +++ b/src/gui/settingsdialogmac.h @@ -47,6 +47,7 @@ public: public slots: void showActivityPage(); + void showIssuesList(const QString &folderAlias); void slotRefreshActivity(AccountState *accountState); private slots: From 5b0fea195d14da96bbe70e2d98557622013df9ca Mon Sep 17 00:00:00 2001 From: Jenkins for ownCloud Date: Wed, 5 Jul 2017 02:18:35 +0200 Subject: [PATCH 028/107] [tx-robot] updated from transifex --- mirall.desktop.in | 3 ++ translations/client_ca.ts | 60 +++++++++++++++++----------------- translations/client_cs.ts | 60 +++++++++++++++++----------------- translations/client_de.ts | 60 +++++++++++++++++----------------- translations/client_el.ts | 60 +++++++++++++++++----------------- translations/client_en.ts | 60 +++++++++++++++++----------------- translations/client_es.ts | 60 +++++++++++++++++----------------- translations/client_es_AR.ts | 60 +++++++++++++++++----------------- translations/client_et.ts | 60 +++++++++++++++++----------------- translations/client_eu.ts | 60 +++++++++++++++++----------------- translations/client_fa.ts | 60 +++++++++++++++++----------------- translations/client_fi.ts | 60 +++++++++++++++++----------------- translations/client_fr.ts | 60 +++++++++++++++++----------------- translations/client_gl.ts | 60 +++++++++++++++++----------------- translations/client_hu.ts | 60 +++++++++++++++++----------------- translations/client_it.ts | 60 +++++++++++++++++----------------- translations/client_ja.ts | 60 +++++++++++++++++----------------- translations/client_nb_NO.ts | 60 +++++++++++++++++----------------- translations/client_nl.ts | 60 +++++++++++++++++----------------- translations/client_pl.ts | 60 +++++++++++++++++----------------- translations/client_pt.ts | 60 +++++++++++++++++----------------- translations/client_pt_BR.ts | 60 +++++++++++++++++----------------- translations/client_ru.ts | 62 ++++++++++++++++++------------------ translations/client_sk.ts | 60 +++++++++++++++++----------------- translations/client_sl.ts | 60 +++++++++++++++++----------------- translations/client_sr.ts | 60 +++++++++++++++++----------------- translations/client_sv.ts | 60 +++++++++++++++++----------------- translations/client_th.ts | 60 +++++++++++++++++----------------- translations/client_tr.ts | 60 +++++++++++++++++----------------- translations/client_uk.ts | 60 +++++++++++++++++----------------- translations/client_zh_CN.ts | 60 +++++++++++++++++----------------- translations/client_zh_TW.ts | 60 +++++++++++++++++----------------- 32 files changed, 934 insertions(+), 931 deletions(-) diff --git a/mirall.desktop.in b/mirall.desktop.in index 94a45f3f67..def9efb889 100644 --- a/mirall.desktop.in +++ b/mirall.desktop.in @@ -84,6 +84,9 @@ X-GNOME-Autostart-Delay=3 # Translations +# Translations + + # Translations Comment[oc]=@APPLICATION_NAME@ sincronizacion del client GenericName[oc]=Dorsièr de Sincronizacion diff --git a/translations/client_ca.ts b/translations/client_ca.ts index 873b089dbe..d64d4eaf0b 100644 --- a/translations/client_ca.ts +++ b/translations/client_ca.ts @@ -2991,23 +2991,23 @@ No és aconsellada usar-la. Error en llegir la carpeta.
- + File/Folder is ignored because it's hidden. El fitxer/carpeta s'ha ignorat perquè és ocult. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() - + Not allowed because you don't have permission to add parent folder - + Not allowed because you don't have permission to add files in that folder @@ -3067,124 +3067,124 @@ No és aconsellada usar-la. L'element no s'ha sincronitzat degut a errors previs: %1 - + Symbolic links are not supported in syncing. La sincronització d'enllaços simbòlics no està implementada. - + File is listed on the ignore list. El fitxer està a la llista d'ignorats. - + File names ending with a period are not supported on this file system. - + File names containing the character '%1' are not supported on this file system. - + The file name is a reserved name on this file system. - + Filename contains trailing spaces. - + Filename is too long. El nom de fitxer és massa llarg. - + Stat failed. - + Filename encoding is not valid La codificació del nom de fitxer no és vàlida - + Invalid characters, please rename "%1" Caràcters no vàlids. Reanomeneu "%1" - + Unable to initialize a sync journal. No es pot inicialitzar un periòdic de sincronització - + Unable to read the blacklist from the local database - + Unable to read from the sync journal. - + Cannot open the sync journal No es pot obrir el diari de sincronització - + File name contains at least one invalid character El nom del fitxer conté al menys un caràcter invàlid - - + + Ignored because of the "choose what to sync" blacklist S'ignora degut al filtre a «Trieu què sincronitzar» - + Not allowed because you don't have permission to add subfolders to that folder - + Not allowed to upload this file because it is read-only on the server, restoring No es permet pujar aquest fitxer perquè només és de lectura en el servidor, es restaura - - + + Not allowed to remove, restoring No es permet l'eliminació, es restaura - + Local files and share folder removed. Fitxers locals i carpeta compartida esborrats. - + Move not allowed, item restored No es permet moure'l, l'element es restaura - + Move not allowed because %1 is read-only No es permet moure perquè %1 només és de lectura - + the destination el destí - + the source l'origen diff --git a/translations/client_cs.ts b/translations/client_cs.ts index 120bf368ed..c91112bfc6 100644 --- a/translations/client_cs.ts +++ b/translations/client_cs.ts @@ -2994,23 +2994,23 @@ Nedoporučuje se jí používat. Chyba při čtení adresáře. - + File/Folder is ignored because it's hidden. Soubor/adresář je ignorován, protože je skrytý. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() Je dostupných pouze %1, pro spuštění je potřeba alespoň %2 - + Not allowed because you don't have permission to add parent folder Není povoleno, protože nemáte oprávnění vytvořit nadřazený adresář - + Not allowed because you don't have permission to add files in that folder Není povoleno, protože nemáte oprávnění přidávat soubory do tohoto adresáře @@ -3070,124 +3070,124 @@ Nedoporučuje se jí používat. Položka nebyla synchronizována kvůli předchozí chybě: %1 - + Symbolic links are not supported in syncing. Symbolické odkazy nejsou při synchronizaci podporovány. - + File is listed on the ignore list. Soubor se nachází na seznamu ignorovaných. - + File names ending with a period are not supported on this file system. Jména souborů končících tečkou nejsou na tomto systému souborů podporována. - + File names containing the character '%1' are not supported on this file system. Názvy souborů obsahující znak '%1' nejsou na tomto souborovém systému podporovány. - + The file name is a reserved name on this file system. Jméno souboru je na tomto systému souborů rezervovaným jménem. - + Filename contains trailing spaces. Jméno souboru obsahuje mezery na konci řádky. - + Filename is too long. Jméno souboru je příliš dlouhé. - + Stat failed. Stat selhal. - + Filename encoding is not valid Kódování znaků jména soubor je neplatné - + Invalid characters, please rename "%1" Neplatné znaky, prosím přejmenujte "%1" - + Unable to initialize a sync journal. Nemohu inicializovat synchronizační žurnál. - + Unable to read the blacklist from the local database Nelze načíst blacklist z místní databáze - + Unable to read from the sync journal. Nelze číst ze žurnálu synchronizace. - + Cannot open the sync journal Nelze otevřít synchronizační žurnál - + File name contains at least one invalid character Jméno souboru obsahuje alespoň jeden neplatný znak - - + + Ignored because of the "choose what to sync" blacklist Ignorováno podle nastavení "vybrat co synchronizovat" - + Not allowed because you don't have permission to add subfolders to that folder Není povoleno, protože nemáte oprávnění přidávat podadresáře do tohoto adresáře - + Not allowed to upload this file because it is read-only on the server, restoring Není povoleno nahrát tento soubor, protože je na serveru uložen pouze pro čtení, obnovuji - - + + Not allowed to remove, restoring Odstranění není povoleno, obnovuji - + Local files and share folder removed. Místní soubory a sdílený adresář byly odstraněny. - + Move not allowed, item restored Přesun není povolen, položka obnovena - + Move not allowed because %1 is read-only Přesun není povolen, protože %1 je pouze pro čtení - + the destination cílové umístění - + the source zdroj diff --git a/translations/client_de.ts b/translations/client_de.ts index 38feacb3d5..d12002d124 100644 --- a/translations/client_de.ts +++ b/translations/client_de.ts @@ -2995,23 +2995,23 @@ Es ist nicht ratsam, diese zu benutzen. Fehler beim Lesen eines Ordners. - + File/Folder is ignored because it's hidden. Datei wird ignoriert, weil sie versteckt ist. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() Nur %1 sind verfügbar. Zum Beginnen werden mindestens %2 benötigt. - + Not allowed because you don't have permission to add parent folder Nicht erlaubt, da Sie keine Rechte zur Erstellung von Unterordnern haben - + Not allowed because you don't have permission to add files in that folder Nicht erlaubt, da Sie keine Rechte zum Hinzufügen von Dateien in diesen Ordner haben @@ -3071,124 +3071,124 @@ Es ist nicht ratsam, diese zu benutzen. Das Element ist aufgrund vorheriger Fehler nicht synchronisiert: %1 - + Symbolic links are not supported in syncing. Symbolische Verknüpfungen werden bei der Synchronisation nicht unterstützt. - + File is listed on the ignore list. Die Datei ist in der Ignorierliste geführt. - + File names ending with a period are not supported on this file system. Dateinamen enden mit einem Punkt, die in diesem Dateisystem nicht unterstützt wird. - + File names containing the character '%1' are not supported on this file system. Dateinamen beinhalten das Zeichen '%1' und diese werden in diesem Dateisystems nicht unterstützt. - + The file name is a reserved name on this file system. Der Dateiname ist ein reservierter Name in diesem Dateisystem. - + Filename contains trailing spaces. Dateiname endet mit Leerzeichen. - + Filename is too long. Der Dateiname ist zu lang. - + Stat failed. Stat fehlgeschlagen. - + Filename encoding is not valid Dateikodierung ist ungültig - + Invalid characters, please rename "%1" Ungültige Zeichenm bitte benennen Sie "%1" um - + Unable to initialize a sync journal. Synchronisationsbericht konnte nicht initialisiert werden. - + Unable to read the blacklist from the local database Fehler beim Einlesen der Blacklist aus der lokalen Datenbank - + Unable to read from the sync journal. Fehler beim Einlesen des Synchronisierungsprotokolls. - + Cannot open the sync journal Synchronisationsbericht kann nicht geöffnet werden - + File name contains at least one invalid character Der Dateiname enthält mindestens ein ungültiges Zeichen - - + + Ignored because of the "choose what to sync" blacklist Aufgrund der »Zu synchronisierende Elemente auswählen«-Sperrliste ignoriert - + Not allowed because you don't have permission to add subfolders to that folder Nicht erlaubt, da Sie keine Rechte zur Erstellung von Unterordnern haben - + Not allowed to upload this file because it is read-only on the server, restoring Das Hochladen dieser Datei ist nicht erlaubt, da die Datei auf dem Server schreibgeschützt ist, Wiederherstellung - - + + Not allowed to remove, restoring Löschen nicht erlaubt, Wiederherstellung - + Local files and share folder removed. Lokale Dateien und Freigabeordner wurden entfernt. - + Move not allowed, item restored Verschieben nicht erlaubt, Element wiederhergestellt - + Move not allowed because %1 is read-only Verschieben nicht erlaubt, da %1 schreibgeschützt ist - + the destination Das Ziel - + the source Die Quelle diff --git a/translations/client_el.ts b/translations/client_el.ts index 8eb0f845b6..d878522830 100644 --- a/translations/client_el.ts +++ b/translations/client_el.ts @@ -2996,23 +2996,23 @@ It is not advisable to use it. Σφάλμα κατά την ανάγνωση του φακέλου. - + File/Folder is ignored because it's hidden. Το Αρχείο/ο Φάκελος αγνοήθηκε επειδή είναι κρυφό. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() Μόνο %1 είναι διαθέσιμα, απαιτούνται τουλάχιστον %2 για την εκκίνηση - + Not allowed because you don't have permission to add parent folder Δεν επιτρέπεται επειδή δεν έχετε δικαιώματα να προσθέσετε γονικό κατάλογο - + Not allowed because you don't have permission to add files in that folder Δεν επιτρέπεται επειδή δεν έχετε δικαιώματα να προσθέσετε αρχεία σε αυτόν τον φάκελο @@ -3072,124 +3072,124 @@ It is not advisable to use it. Το αντικείμενο δεν είναι συγχρονισμένο λόγω προηγούμενων σφαλμάτων: %1 - + Symbolic links are not supported in syncing. Οι συμβολικού σύνδεσμοι δεν υποστηρίζονται για το συγχρονισμό. - + File is listed on the ignore list. Το αρχείο περιέχεται στη λίστα αρχείων προς αγνόηση. - + File names ending with a period are not supported on this file system. Τα ονόματα αρχείων που διαρκούν μια ορισμένη χρονική περίοδο δεν υποστηρίζονται σε αυτό το σύστημα αρχείων. - + File names containing the character '%1' are not supported on this file system. Τα ονόματα αρχείων που περιέχουν τον χαρακτήρα '% 1' δεν υποστηρίζονται σε αυτό το σύστημα αρχείων. - + The file name is a reserved name on this file system. Το όνομα αρχείου είναι ένα κατοχυρωμένο όνομα σε αυτό το σύστημα αρχείων. - + Filename contains trailing spaces. Το όνομα του αρχείου περιέχει συνεχόμενα κενά. - + Filename is too long. Το όνομα αρχείου είνια πολύ μεγάλο. - + Stat failed. Απέτυχε. - + Filename encoding is not valid Η κωδικοποίηση του ονόματος αρχείου δεν είναι έγκυρη - + Invalid characters, please rename "%1" Μη έγκυροι χαρακτήρες, παρακαλώ μετονομάστε το "%1" - + Unable to initialize a sync journal. Αδυναμία προετοιμασίας αρχείου συγχρονισμού. - + Unable to read the blacklist from the local database Αδυναμία ανάγνωσης της μαύρης λίστας από την τοπική βάση δεδομένων - + Unable to read from the sync journal. Αδυναμία ανάγνωσης από το ημερολόγιο συγχρονισμού. - + Cannot open the sync journal Αδυναμία ανοίγματος του αρχείου συγχρονισμού - + File name contains at least one invalid character Το όνομα αρχείου περιέχει έναν τουλάχιστον μη έγκυρο χαρακτήρα - - + + Ignored because of the "choose what to sync" blacklist Αγνοήθηκε εξαιτίας της μαύρης λίστας "διάλεξε τι να συγχρονιστεί" - + Not allowed because you don't have permission to add subfolders to that folder Δεν επιτρέπεται επειδή δεν έχετε δικαιώματα να προσθέσετε υποφακέλους σε αυτό τον φάκελο - + Not allowed to upload this file because it is read-only on the server, restoring Δεν επιτρέπεται να μεταφορτώσετε αυτό το αρχείο επειδή είναι μόνο για ανάγνωση στο διακομιστή, αποκατάσταση σε εξέλιξη - - + + Not allowed to remove, restoring Δεν επιτρέπεται η αφαίρεση, αποκατάσταση σε εξέλιξη - + Local files and share folder removed. Οι τοπικοί φάκελοι και ο φάκελος κοινής χρήσης αφαιρέθηκαν. - + Move not allowed, item restored Η μετακίνηση δεν επιτρέπεται, το αντικείμενο αποκαταστάθηκε - + Move not allowed because %1 is read-only Η μετακίνηση δεν επιτρέπεται επειδή το %1 είναι μόνο για ανάγνωση - + the destination ο προορισμός - + the source η προέλευση diff --git a/translations/client_en.ts b/translations/client_en.ts index 1d3fb917c7..5425dc5112 100644 --- a/translations/client_en.ts +++ b/translations/client_en.ts @@ -3013,23 +3013,23 @@ It is not advisable to use it. - + File/Folder is ignored because it's hidden. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() - + Not allowed because you don't have permission to add parent folder - + Not allowed because you don't have permission to add files in that folder @@ -3089,124 +3089,124 @@ It is not advisable to use it. - + Symbolic links are not supported in syncing. - + File is listed on the ignore list. - + File names ending with a period are not supported on this file system. - + File names containing the character '%1' are not supported on this file system. - + The file name is a reserved name on this file system. - + Filename contains trailing spaces. - + Filename is too long. - + Stat failed. - + Filename encoding is not valid - + Invalid characters, please rename "%1" - + Unable to initialize a sync journal. - + Unable to read the blacklist from the local database - + Unable to read from the sync journal. - + Cannot open the sync journal - + File name contains at least one invalid character - - + + Ignored because of the "choose what to sync" blacklist - + Not allowed because you don't have permission to add subfolders to that folder - + Not allowed to upload this file because it is read-only on the server, restoring - - + + Not allowed to remove, restoring - + Local files and share folder removed. - + Move not allowed, item restored - + Move not allowed because %1 is read-only - + the destination - + the source diff --git a/translations/client_es.ts b/translations/client_es.ts index eabc6fa573..df6a089405 100644 --- a/translations/client_es.ts +++ b/translations/client_es.ts @@ -2995,23 +2995,23 @@ No se recomienda usarla. Error al leer el directorio. - + File/Folder is ignored because it's hidden. Se ignoran los Archivos/Carpetas ocultos. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() Solo %1 disponible, se necesita por lo menos %2 para comenzar - + Not allowed because you don't have permission to add parent folder No permitido porque no tienes permiso para añadir un directorio padre - + Not allowed because you don't have permission to add files in that folder No permitido porque no tienes permiso para añadir archivos a ese directorio @@ -3071,124 +3071,124 @@ No se recomienda usarla. El elemento no está sincronizado por errores previos: %1 - + Symbolic links are not supported in syncing. No se admiten enlaces simbólicos en la sincronización. - + File is listed on the ignore list. El fichero está en la lista de ignorados - + File names ending with a period are not supported on this file system. Los nombres de archivo que terminan con un punto no son compatibles con este sistema de archivos. - + File names containing the character '%1' are not supported on this file system. Los nombres de archivo que contengan el caracter '%1' no son compatibles con este sistema de archivos. - + The file name is a reserved name on this file system. El nombre del archivo es una palabra reservada del sistema de archivos. - + Filename contains trailing spaces. El nombre del archivo contiene espacios finales. - + Filename is too long. El nombre del archivo es demasiado largo. - + Stat failed. Stat ha fallado. - + Filename encoding is not valid Los caracteres del nombre de fichero no son válidos - + Invalid characters, please rename "%1" Caracteres inválidos, por favor renombre "%1" - + Unable to initialize a sync journal. No se pudo inicializar un registro (journal) de sincronización. - + Unable to read the blacklist from the local database No se pudo leer la lista de bloqueo de la base de datos local - + Unable to read from the sync journal. No se ha podido leer desde el registro de sincronización - + Cannot open the sync journal No es posible abrir el diario de sincronización - + File name contains at least one invalid character Nombre de archivo contiene al menos un caracter no válido - - + + Ignored because of the "choose what to sync" blacklist Ignorado porque se encuentra en la lista negra de "elija qué va a sincronizar" - + Not allowed because you don't have permission to add subfolders to that folder No permitido porque no tienes permiso para añadir subdirectorios a ese directorio - + Not allowed to upload this file because it is read-only on the server, restoring No está permitido subir este archivo porque es de solo lectura en el servidor, restaurando. - - + + Not allowed to remove, restoring No está permitido borrar, restaurando. - + Local files and share folder removed. Se han eliminado los archivos locales y la carpeta compartida. - + Move not allowed, item restored No está permitido mover, elemento restaurado. - + Move not allowed because %1 is read-only No está permitido mover, porque %1 es de sólo lectura. - + the destination destino - + the source origen diff --git a/translations/client_es_AR.ts b/translations/client_es_AR.ts index 71656d1119..f35f41e292 100644 --- a/translations/client_es_AR.ts +++ b/translations/client_es_AR.ts @@ -2981,23 +2981,23 @@ It is not advisable to use it. - + File/Folder is ignored because it's hidden. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() - + Not allowed because you don't have permission to add parent folder - + Not allowed because you don't have permission to add files in that folder @@ -3057,124 +3057,124 @@ It is not advisable to use it. - + Symbolic links are not supported in syncing. Los vínculos simbólicos no está soportados al sincronizar. - + File is listed on the ignore list. El archivo está en la lista de ignorados. - + File names ending with a period are not supported on this file system. - + File names containing the character '%1' are not supported on this file system. - + The file name is a reserved name on this file system. - + Filename contains trailing spaces. - + Filename is too long. - + Stat failed. - + Filename encoding is not valid - + Invalid characters, please rename "%1" - + Unable to initialize a sync journal. Imposible inicializar un diario de sincronización. - + Unable to read the blacklist from the local database - + Unable to read from the sync journal. - + Cannot open the sync journal - + File name contains at least one invalid character - - + + Ignored because of the "choose what to sync" blacklist - + Not allowed because you don't have permission to add subfolders to that folder - + Not allowed to upload this file because it is read-only on the server, restoring - - + + Not allowed to remove, restoring - + Local files and share folder removed. - + Move not allowed, item restored - + Move not allowed because %1 is read-only - + the destination - + the source diff --git a/translations/client_et.ts b/translations/client_et.ts index 09e06e1bec..571e86992f 100644 --- a/translations/client_et.ts +++ b/translations/client_et.ts @@ -2984,23 +2984,23 @@ Selle kasutamine pole soovitatav. - + File/Folder is ignored because it's hidden. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() - + Not allowed because you don't have permission to add parent folder - + Not allowed because you don't have permission to add files in that folder @@ -3060,124 +3060,124 @@ Selle kasutamine pole soovitatav. Üksust ei sünkroniseeritud eelnenud vigade tõttu: %1 - + Symbolic links are not supported in syncing. Sümboolsed lingid ei ole sünkroniseerimisel toetatud. - + File is listed on the ignore list. Fail on märgitud ignoreeritavate nimistus. - + File names ending with a period are not supported on this file system. - + File names containing the character '%1' are not supported on this file system. - + The file name is a reserved name on this file system. - + Filename contains trailing spaces. - + Filename is too long. Faili nimi on liiga pikk. - + Stat failed. - + Filename encoding is not valid Failinime kodeering pole kehtiv - + Invalid characters, please rename "%1" - + Unable to initialize a sync journal. Ei suuda lähtestada sünkroniseeringu zurnaali. - + Unable to read the blacklist from the local database - + Unable to read from the sync journal. - + Cannot open the sync journal Ei suuda avada sünkroniseeringu zurnaali - + File name contains at least one invalid character Faili nimesonvähemalt üks keelatud märk - - + + Ignored because of the "choose what to sync" blacklist "Vali, mida sünkroniseerida" musta nimekirja tõttu vahele jäetud - + Not allowed because you don't have permission to add subfolders to that folder - + Not allowed to upload this file because it is read-only on the server, restoring Pole lubatud üles laadida, kuna tegemist on ainult-loetava serveriga, taastan - - + + Not allowed to remove, restoring Eemaldamine pole lubatud, taastan - + Local files and share folder removed. Kohalikud failid ja jagatud kaustad eemaldatud. - + Move not allowed, item restored Liigutamine pole lubatud, üksus taastatud - + Move not allowed because %1 is read-only Liigutamien pole võimalik kuna %1 on ainult lugemiseks - + the destination sihtkoht - + the source allikas diff --git a/translations/client_eu.ts b/translations/client_eu.ts index 541746ea9e..fae7ce9dbd 100644 --- a/translations/client_eu.ts +++ b/translations/client_eu.ts @@ -2984,23 +2984,23 @@ Ez da gomendagarria erabltzea. - + File/Folder is ignored because it's hidden. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() - + Not allowed because you don't have permission to add parent folder - + Not allowed because you don't have permission to add files in that folder @@ -3060,124 +3060,124 @@ Ez da gomendagarria erabltzea. - + Symbolic links are not supported in syncing. Esteka sinbolikoak ezin dira sinkronizatu. - + File is listed on the ignore list. Fitxategia baztertutakoen zerrendan dago. - + File names ending with a period are not supported on this file system. - + File names containing the character '%1' are not supported on this file system. - + The file name is a reserved name on this file system. - + Filename contains trailing spaces. - + Filename is too long. - + Stat failed. - + Filename encoding is not valid - + Invalid characters, please rename "%1" - + Unable to initialize a sync journal. Ezin izan da sinkronizazio egunerokoa hasieratu. - + Unable to read the blacklist from the local database - + Unable to read from the sync journal. - + Cannot open the sync journal Ezin da sinkronizazio egunerokoa ireki - + File name contains at least one invalid character Fitxategi izenak behintzat baliogabeko karaktere bat du - - + + Ignored because of the "choose what to sync" blacklist - + Not allowed because you don't have permission to add subfolders to that folder - + Not allowed to upload this file because it is read-only on the server, restoring - - + + Not allowed to remove, restoring Ezabatzeko baimenik gabe, berrezartzen - + Local files and share folder removed. - + Move not allowed, item restored Mugitzea ez dago baimenduta, elementua berrezarri da - + Move not allowed because %1 is read-only Mugitzea ez dago baimenduta %1 irakurtzeko bakarrik delako - + the destination helburua - + the source jatorria diff --git a/translations/client_fa.ts b/translations/client_fa.ts index b91ad56023..998bb53edf 100644 --- a/translations/client_fa.ts +++ b/translations/client_fa.ts @@ -2981,23 +2981,23 @@ It is not advisable to use it. خطا در هنگام خواندن پوشه - + File/Folder is ignored because it's hidden. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() - + Not allowed because you don't have permission to add parent folder - + Not allowed because you don't have permission to add files in that folder @@ -3057,124 +3057,124 @@ It is not advisable to use it. - + Symbolic links are not supported in syncing. - + File is listed on the ignore list. - + File names ending with a period are not supported on this file system. - + File names containing the character '%1' are not supported on this file system. - + The file name is a reserved name on this file system. - + Filename contains trailing spaces. - + Filename is too long. نام فایل خیلی طولانی است. - + Stat failed. وضعیت ناموفق - + Filename encoding is not valid رمزگذاری نام فایل معتبر نیست - + Invalid characters, please rename "%1" کاراکتر نامعتبر، لطفا "%1" را تغییر نام دهید - + Unable to initialize a sync journal. - + Unable to read the blacklist from the local database - + Unable to read from the sync journal. - + Cannot open the sync journal - + File name contains at least one invalid character نام فایل دارای حداقل یک کاراکتر نامعتبر است - - + + Ignored because of the "choose what to sync" blacklist - + Not allowed because you don't have permission to add subfolders to that folder با توجه به عدم اجازه‌ی شما به ایجاد زیرپوشه به پوشه مجاز نیست - + Not allowed to upload this file because it is read-only on the server, restoring آپلود این فایل با توجه به فقط-خواندنی بودن آن در سرور مجاز نیست، در حال بازگرداندن - - + + Not allowed to remove, restoring حذف مجاز نیست، در حال بازگردادن - + Local files and share folder removed. فایل‌های محلی و پوشه‌ی اشتراک حذف شد. - + Move not allowed, item restored انتقال مجاز نیست، مورد بازگردانده شد - + Move not allowed because %1 is read-only - + the destination مقصد - + the source مبدا diff --git a/translations/client_fi.ts b/translations/client_fi.ts index 6df95d381d..bb15cff507 100644 --- a/translations/client_fi.ts +++ b/translations/client_fi.ts @@ -2986,23 +2986,23 @@ Osoitteen käyttäminen ei ole suositeltavaa. Kansiota lukiessa tapahtui virhe - + File/Folder is ignored because it's hidden. Tiedosto/kansi ohitetaan, koska se on piilotettu. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() Vain %1 on käytettävissä, käynnistymiseen tarvitaan %2 - + Not allowed because you don't have permission to add parent folder Ei sallittu, koska käyttöoikeutesi eivät riitä ylätason kansion lisäämiseen - + Not allowed because you don't have permission to add files in that folder Ei sallittu, koska käyttöoikeutesi eivät riitä tiedostojen lisäämiseen kyseiseen kansioon @@ -3062,124 +3062,124 @@ Osoitteen käyttäminen ei ole suositeltavaa. Kohdetta ei synkronoitu aiempien virheiden vuoksi: %1 - + Symbolic links are not supported in syncing. Symboliset linkit eivät ole tuettuja synkronoinnissa. - + File is listed on the ignore list. Tiedosto on ohituslistalla. - + File names ending with a period are not supported on this file system. - + File names containing the character '%1' are not supported on this file system. - + The file name is a reserved name on this file system. - + Filename contains trailing spaces. - + Filename is too long. Tiedoston nimi on liian pitkä. - + Stat failed. Stat epäonnistui. - + Filename encoding is not valid Tiedostonimen merkistökoodaus ei ole kelvollista - + Invalid characters, please rename "%1" Virheellisiä merkkejä, anna uusi nimi kohteelle "%1" - + Unable to initialize a sync journal. - + Unable to read the blacklist from the local database - + Unable to read from the sync journal. - + Cannot open the sync journal - + File name contains at least one invalid character Tiedoston nimi sisältää ainakin yhden virheellisen merkin - - + + Ignored because of the "choose what to sync" blacklist - + Not allowed because you don't have permission to add subfolders to that folder Ei sallittu, koska oikeutesi eivät riitä alikansioiden lisäämiseen kyseiseen kansioon - + Not allowed to upload this file because it is read-only on the server, restoring - - + + Not allowed to remove, restoring Poistaminen ei ole sallittua, palautetaan - + Local files and share folder removed. Paikalliset tiedostot ja jakokansio poistettu. - + Move not allowed, item restored Siirtäminen ei ole sallittua, kohde palautettu - + Move not allowed because %1 is read-only Siirto ei ole sallittu, koska %1 on "vain luku"-tilassa - + the destination kohde - + the source lähde diff --git a/translations/client_fr.ts b/translations/client_fr.ts index de5e127870..5c4fcc8bf2 100644 --- a/translations/client_fr.ts +++ b/translations/client_fr.ts @@ -2997,23 +2997,23 @@ Il est déconseillé de l'utiliser. Erreur lors de la lecture du dossier. - + File/Folder is ignored because it's hidden. Le fichier ou dossier a été ignoré car il est masqué. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() Seulement %1 disponibles, il faut au moins %2 pour démarrer - + Not allowed because you don't have permission to add parent folder Non autorisé car vous n'avez pas la permission d'ajouter un dossier parent - + Not allowed because you don't have permission to add files in that folder Non autorisé car vous n'avez pas la permission d'ajouter des fichiers dans ce dossier @@ -3073,124 +3073,124 @@ Il est déconseillé de l'utiliser. Cet élément n'a pas été synchronisé en raison des erreurs précédentes : %1 - + Symbolic links are not supported in syncing. Les liens symboliques ne sont pas pris en charge par la synchronisation. - + File is listed on the ignore list. Le fichier est présent dans la liste des fichiers à exclure. - + File names ending with a period are not supported on this file system. Les noms de fichier se terminant par un point ne sont pas pris en charge sur votre système. - + File names containing the character '%1' are not supported on this file system. Les noms de fichier contenant le caractère '%1' ne sont pas pris en charge sur votre système. - + The file name is a reserved name on this file system. Le nom du fichier est réservé sur votre système. - + Filename contains trailing spaces. Le nom du fichier se fini par des espaces. - + Filename is too long. Le nom de fichier est trop long. - + Stat failed. Stat échoué. - + Filename encoding is not valid L'encodage du nom de fichier n'est pas valide - + Invalid characters, please rename "%1" Caractères non valides. Veuillez renommer "%1" - + Unable to initialize a sync journal. Impossible d'initialiser un journal de synchronisation. - + Unable to read the blacklist from the local database Impossible de lire la liste noire de la base de données locale - + Unable to read from the sync journal. Impossible de lire le journal de synchronisation. - + Cannot open the sync journal Impossible d'ouvrir le journal de synchronisation - + File name contains at least one invalid character Le nom de fichier contient au moins un caractère non valable - - + + Ignored because of the "choose what to sync" blacklist Ignoré en raison de la liste noire "Sélectionner le contenu à synchroniser". - + Not allowed because you don't have permission to add subfolders to that folder Non autorisé car vous n'avez pas la permission d'ajouter des sous-dossiers dans ce dossier - + Not allowed to upload this file because it is read-only on the server, restoring Non autorisé à envoyer ce fichier car il est en lecture seule sur le serveur. Restauration - - + + Not allowed to remove, restoring Non autorisé à supprimer. Restauration - + Local files and share folder removed. Fichiers locaux et dossier partagé supprimés. - + Move not allowed, item restored Déplacement non autorisé, élément restauré - + Move not allowed because %1 is read-only Déplacement non autorisé car %1 est en mode lecture seule - + the destination la destination - + the source la source diff --git a/translations/client_gl.ts b/translations/client_gl.ts index c19c71dc11..77d352a4ec 100644 --- a/translations/client_gl.ts +++ b/translations/client_gl.ts @@ -2985,23 +2985,23 @@ Recomendámoslle que non o use. - + File/Folder is ignored because it's hidden. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() - + Not allowed because you don't have permission to add parent folder - + Not allowed because you don't have permission to add files in that folder @@ -3061,124 +3061,124 @@ Recomendámoslle que non o use. Este elemento non foi sincronizado por mor de erros anteriores: %1 - + Symbolic links are not supported in syncing. As ligazóns simbolicas non son admitidas nas sincronizacións - + File is listed on the ignore list. O ficheiro está na lista de ignorados. - + File names ending with a period are not supported on this file system. - + File names containing the character '%1' are not supported on this file system. - + The file name is a reserved name on this file system. - + Filename contains trailing spaces. - + Filename is too long. O nome de ficheiro é longo de máis. - + Stat failed. Fallou a obtención de estatísticas. - + Filename encoding is not valid O nome de ficheiro codificado non é correcto - + Invalid characters, please rename "%1" Caracteres incorrectos, déalle outro nome a «%1» - + Unable to initialize a sync journal. Non é posíbel preparar un rexistro de sincronización. - + Unable to read the blacklist from the local database - + Unable to read from the sync journal. - + Cannot open the sync journal Non foi posíbel abrir o rexistro de sincronización - + File name contains at least one invalid character O nome de ficheiro contén algún carácter incorrecto - - + + Ignored because of the "choose what to sync" blacklist Ignorado por mor da lista negra de «escolla que sincronizar» - + Not allowed because you don't have permission to add subfolders to that folder - + Not allowed to upload this file because it is read-only on the server, restoring Non está permitido o envío xa que o ficheiro é só de lectura no servidor, restaurando - - + + Not allowed to remove, restoring Non está permitido retiralo, restaurando - + Local files and share folder removed. Retirados os ficheiros locais e o cartafol compartido. - + Move not allowed, item restored Nos está permitido movelo, elemento restaurado - + Move not allowed because %1 is read-only Bon está permitido movelo xa que %1 é só de lectura - + the destination o destino - + the source a orixe diff --git a/translations/client_hu.ts b/translations/client_hu.ts index 0e3954d0a4..c82a752a49 100644 --- a/translations/client_hu.ts +++ b/translations/client_hu.ts @@ -2982,23 +2982,23 @@ It is not advisable to use it. - + File/Folder is ignored because it's hidden. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() - + Not allowed because you don't have permission to add parent folder - + Not allowed because you don't have permission to add files in that folder @@ -3058,124 +3058,124 @@ It is not advisable to use it. - + Symbolic links are not supported in syncing. - + File is listed on the ignore list. Fájl a kizárási listán. - + File names ending with a period are not supported on this file system. - + File names containing the character '%1' are not supported on this file system. - + The file name is a reserved name on this file system. - + Filename contains trailing spaces. - + Filename is too long. Fájlnév túl nagy. - + Stat failed. - + Filename encoding is not valid - + Invalid characters, please rename "%1" Érvénytelen karakterek, kérjük nevezd át: %1 - + Unable to initialize a sync journal. - + Unable to read the blacklist from the local database - + Unable to read from the sync journal. - + Cannot open the sync journal - + File name contains at least one invalid character A fájlnév legalább egy érvénytelen karaktert tartalmaz! - - + + Ignored because of the "choose what to sync" blacklist - + Not allowed because you don't have permission to add subfolders to that folder - + Not allowed to upload this file because it is read-only on the server, restoring - - + + Not allowed to remove, restoring - + Local files and share folder removed. - + Move not allowed, item restored - + Move not allowed because %1 is read-only - + the destination a cél - + the source a forrás diff --git a/translations/client_it.ts b/translations/client_it.ts index bc9ec84578..c254697a97 100644 --- a/translations/client_it.ts +++ b/translations/client_it.ts @@ -2991,23 +2991,23 @@ Non è consigliabile utilizzarlo. Errore durante la lettura della cartella. - + File/Folder is ignored because it's hidden. Il file/cartella è ignorato poiché è nascosto. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() Sono disponibili solo %1, servono almeno %2 per iniziare - + Not allowed because you don't have permission to add parent folder Non consentito poiché non disponi dei permessi per aggiungere la cartella superiore - + Not allowed because you don't have permission to add files in that folder Non consentito poiché non disponi dei permessi per aggiungere file in quella cartella @@ -3067,124 +3067,124 @@ Non è consigliabile utilizzarlo. L'elemento non è sincronizzato a causa dell'errore precedente: %1 - + Symbolic links are not supported in syncing. I collegamenti simbolici non sono supportati dalla sincronizzazione. - + File is listed on the ignore list. Il file è stato aggiunto alla lista ignorati. - + File names ending with a period are not supported on this file system. I nomi del file che terminano con un punto non sono supportati su questo file system. - + File names containing the character '%1' are not supported on this file system. I nomi del file che contengono il carattere '%1' non sono supportati su questo file system. - + The file name is a reserved name on this file system. Il nome del file è un nome riservato su questo file system. - + Filename contains trailing spaces. Il nome del file contiene spazi alla fine. - + Filename is too long. Il nome del file è troppo lungo. - + Stat failed. Stat non riuscita. - + Filename encoding is not valid La codifica del nome del file non è valida - + Invalid characters, please rename "%1" Caratteri non validi, rinomina "%1" - + Unable to initialize a sync journal. Impossibile inizializzare il registro di sincronizzazione. - + Unable to read the blacklist from the local database Impossibile leggere la lista nera dal database locale - + Unable to read from the sync journal. Impossibile leggere dal registro di sincronizzazione. - + Cannot open the sync journal Impossibile aprire il registro di sincronizzazione - + File name contains at least one invalid character Il nome del file contiene almeno un carattere non valido - - + + Ignored because of the "choose what to sync" blacklist Ignorato in base alla lista nera per la scelta di cosa sincronizzare - + Not allowed because you don't have permission to add subfolders to that folder Non consentito poiché non disponi dei permessi per aggiungere sottocartelle in quella cartella - + Not allowed to upload this file because it is read-only on the server, restoring Il caricamento di questo file non è consentito poiché è in sola lettura sul server, ripristino - - + + Not allowed to remove, restoring Rimozione non consentita, ripristino - + Local files and share folder removed. I file locali e la cartella condivisa sono stati rimossi. - + Move not allowed, item restored Spostamento non consentito, elemento ripristinato - + Move not allowed because %1 is read-only Spostamento non consentito poiché %1 è in sola lettura - + the destination la destinazione - + the source l'origine diff --git a/translations/client_ja.ts b/translations/client_ja.ts index d9cd070e62..841ece9575 100644 --- a/translations/client_ja.ts +++ b/translations/client_ja.ts @@ -2992,23 +2992,23 @@ It is not advisable to use it. フォルダーの読み込みエラー - + File/Folder is ignored because it's hidden. 隠しファイル/フォルダーのため無視されました - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() %1 しか空き容量がありません、開始するためには少なくとも %2 は必要です。 - + Not allowed because you don't have permission to add parent folder 親フォルダーを追加する権限がありません - + Not allowed because you don't have permission to add files in that folder そのフォルダーにファイルを追加する権限がありません @@ -3068,124 +3068,124 @@ It is not advisable to use it. このアイテムは以前にエラーが発生したため同期しません: %1 - + Symbolic links are not supported in syncing. 同期機能はシンボリックリンクをサポートしていません。 - + File is listed on the ignore list. ファイルは除外リストに登録されています。 - + File names ending with a period are not supported on this file system. 末尾にピリオドを使うファイル名はサポートされていません - + File names containing the character '%1' are not supported on this file system. ファイル名に使用できない文字列が含まれています: '%1' - + The file name is a reserved name on this file system. ファイル名はこのファイルシステムで予約されている名前です。 - + Filename contains trailing spaces. ファイル名末尾にスペースが含まれます。 - + Filename is too long. ファイル名が長すぎます - + Stat failed. 情報取得エラー - + Filename encoding is not valid ファイル名のエンコーディングが無効です。 - + Invalid characters, please rename "%1" 無効な文字です、"%1" を変更してください。 - + Unable to initialize a sync journal. 同期ジャーナルの初期化ができません。 - + Unable to read the blacklist from the local database ローカルデータベースからブラックリストを読み込みできません - + Unable to read from the sync journal. 同期ジャーナルから読み込みできません - + Cannot open the sync journal 同期ジャーナルを開くことができません - + File name contains at least one invalid character ファイル名に1文字以上の無効な文字が含まれています - - + + Ignored because of the "choose what to sync" blacklist "同期対象先" ブラックリストにより無視されました。 - + Not allowed because you don't have permission to add subfolders to that folder そのフォルダーにサブフォルダーを追加する権限がありません - + Not allowed to upload this file because it is read-only on the server, restoring サーバーでは読み取り専用となっているため、このファイルをアップロードすることはできません、復元しています - - + + Not allowed to remove, restoring 削除できないので復元しています - + Local files and share folder removed. ローカルファイルと共有フォルダーを削除しました。 - + Move not allowed, item restored 移動できないので項目を復元しました - + Move not allowed because %1 is read-only %1 は読み取り専用のため移動できません - + the destination 移動先 - + the source 移動元 diff --git a/translations/client_nb_NO.ts b/translations/client_nb_NO.ts index 3b3af38528..565d04cd5a 100644 --- a/translations/client_nb_NO.ts +++ b/translations/client_nb_NO.ts @@ -2995,23 +2995,23 @@ Det er ikke tilrådelig å bruke den. Feil ved lesing av mappe. - + File/Folder is ignored because it's hidden. Filen/mappen ignoreres fordi den er skjult. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() Bare %1 er tilgjengelig, trenger minst %2 for å begynne - + Not allowed because you don't have permission to add parent folder Ikke tillatt fordi du ikke har lov til å legge til foreldremappe - + Not allowed because you don't have permission to add files in that folder Ikke tillatt fordi du ikke har lov til å opprette filer i den mappen @@ -3071,124 +3071,124 @@ Det er ikke tilrådelig å bruke den. Elementet er ikke synkronisert på grunn av tidligere feil: %1 - + Symbolic links are not supported in syncing. Symbolske lenker støttes ikke i synkronisering. - + File is listed on the ignore list. Filen ligger på ignoreringslisten. - + File names ending with a period are not supported on this file system. Filnavn som slutter med punktum er ikke tillatt på dette filsystemet - + File names containing the character '%1' are not supported on this file system. Filnavn som inneholder '%1' er ikke tillatt på dette filsystemet - + The file name is a reserved name on this file system. Filnavnet er et reservert navn på dette filsystemet. - + Filename contains trailing spaces. Filnavn inneholder blanke på slutten. - + Filename is too long. Filnavn er for langt. - + Stat failed. Stat feilet. - + Filename encoding is not valid Filnavn-koding er ikke gyldig - + Invalid characters, please rename "%1" Ugyldige tegn, gi et annet navn til "%1" - + Unable to initialize a sync journal. Kan ikke initialisere en synkroniseringsjournal. - + Unable to read the blacklist from the local database Kan ikke lese svartelisten fra den lokale databasen - + Unable to read from the sync journal. Kan ikke lese fra synkroniseringsjournalen - + Cannot open the sync journal Kan ikke åpne synkroniseringsjournalen - + File name contains at least one invalid character Filnavnet inneholder minst ett ulovlig tegn - - + + Ignored because of the "choose what to sync" blacklist Ignorert på grunn av svartelisten "velg hva som skal synkroniseres" - + Not allowed because you don't have permission to add subfolders to that folder Ikke tillatt fordi du ikke har lov til å lage undermapper i den mappen - + Not allowed to upload this file because it is read-only on the server, restoring Ikke tillatt å laste opp denne filenfordi den er skrivebeskyttet på serveren, gjenoppretter - - + + Not allowed to remove, restoring Ikke tillatt å fjerne, gjenoppretter - + Local files and share folder removed. Lokale filer og delingsmappe fjernet. - + Move not allowed, item restored Flytting ikke tillatt, element gjenopprettet - + Move not allowed because %1 is read-only Flytting ikke tillatt fordi %1 er skrivebeskyttet - + the destination målet - + the source kilden diff --git a/translations/client_nl.ts b/translations/client_nl.ts index adee3cfaf3..644c5f9b07 100644 --- a/translations/client_nl.ts +++ b/translations/client_nl.ts @@ -3000,23 +3000,23 @@ We adviseren deze site niet te gebruiken. Fout tijdens lezen map. - + File/Folder is ignored because it's hidden. Bestand/Map is genegeerd omdat het verborgen is. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() Slechts %1 beschikbaar, maar heeft minimaal %2 nodig om te starten - + Not allowed because you don't have permission to add parent folder Niet toegestaan omdat u geen rechten hebt om een bovenliggende map toe te voegen - + Not allowed because you don't have permission to add files in that folder Niet toegestaan omdat u geen rechten hebt om bestanden in die map toe te voegen @@ -3076,124 +3076,124 @@ We adviseren deze site niet te gebruiken. Dit onderwerp is niet gesynchroniseerd door eerdere fouten: %1 - + Symbolic links are not supported in syncing. Symbolische links worden niet ondersteund bij het synchroniseren. - + File is listed on the ignore list. Het bestand is opgenomen op de negeerlijst. - + File names ending with a period are not supported on this file system. Bestandsnamen die eindigen met een punt worden niet ondersteund door het bestandssysteem. - + File names containing the character '%1' are not supported on this file system. Bestandsnamen met een '%1' symbool worden niet ondersteund door het bestandssysteem. - + The file name is a reserved name on this file system. De bestandsnaam is een gereserveerde naam op dit bestandssysteem. - + Filename contains trailing spaces. De bestandsnaam bevat spaties achteraan. - + Filename is too long. De bestandsnaam is te lang. - + Stat failed. Stat mislukt. - + Filename encoding is not valid Bestandsnaamcodering is niet geldig - + Invalid characters, please rename "%1" Ongeldige tekens, hernoem "%1" - + Unable to initialize a sync journal. Niet in staat om een synchronisatie transactielog te starten. - + Unable to read the blacklist from the local database Kan de blacklist niet lezen uit de lokale database - + Unable to read from the sync journal. Niet mogelijk om te lezen uit het synchronisatie verslag. - + Cannot open the sync journal Kan het sync transactielog niet openen - + File name contains at least one invalid character De bestandsnaam bevat ten minste één ongeldig teken - - + + Ignored because of the "choose what to sync" blacklist Genegeerd vanwege de "wat synchroniseren" zwarte lijst - + Not allowed because you don't have permission to add subfolders to that folder Niet toegestaan, omdat je geen permissies hebt om submappen aan die map toe te voegen - + Not allowed to upload this file because it is read-only on the server, restoring Niet toegestaan om dit bestand te uploaden, omdat het alleen-lezen is op de server, herstellen - - + + Not allowed to remove, restoring Niet toegestaan om te verwijderen, herstellen - + Local files and share folder removed. Lokale bestanden en share-map verwijderd. - + Move not allowed, item restored Verplaatsen niet toegestaan, object hersteld - + Move not allowed because %1 is read-only Verplaatsen niet toegestaan, omdat %1 alleen-lezen is - + the destination bestemming - + the source bron diff --git a/translations/client_pl.ts b/translations/client_pl.ts index 8d6b90d178..7caff29e64 100644 --- a/translations/client_pl.ts +++ b/translations/client_pl.ts @@ -2989,23 +2989,23 @@ Niezalecane jest jego użycie. Błąd podczas odczytu katalogu. - + File/Folder is ignored because it's hidden. Plik / katalog zostanie zignorowany, ponieważ jest ukryty. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() - + Not allowed because you don't have permission to add parent folder Niedozwolone, ponieważ nie masz uprawnień do dodawania katalogu nadrzędnego - + Not allowed because you don't have permission to add files in that folder Niedozwolone, ponieważ nie masz uprawnień do dodawania plików w tym katalogu @@ -3065,124 +3065,124 @@ Niezalecane jest jego użycie. Ten element nie jest zsynchronizowane z powodu poprzednich błędów: %1 - + Symbolic links are not supported in syncing. Linki symboliczne nie są wspierane przy synchronizacji. - + File is listed on the ignore list. Plik jest na liście plików ignorowanych. - + File names ending with a period are not supported on this file system. - + File names containing the character '%1' are not supported on this file system. Nazwy plików zawierające znaki '%1' nie są wspierane - + The file name is a reserved name on this file system. Nazwa pliku jest zarezerwowana dla plików systemowych - + Filename contains trailing spaces. Nazwa pliku zawiera spacje - + Filename is too long. Nazwa pliku zbyt długa - + Stat failed. Błąd statystyk. - + Filename encoding is not valid - + Invalid characters, please rename "%1" - + Unable to initialize a sync journal. Nie można zainicjować synchronizacji dziennika. - + Unable to read the blacklist from the local database Nie można odczytać czarnej listy z lokalnej bazy danych - + Unable to read from the sync journal. Nie można czytać z dziennika synchronizacji. - + Cannot open the sync journal Nie można otworzyć dziennika synchronizacji - + File name contains at least one invalid character Nazwa pliku zawiera co najmniej jeden nieprawidłowy znak - - + + Ignored because of the "choose what to sync" blacklist - + Not allowed because you don't have permission to add subfolders to that folder Niedozwolone, ponieważ nie masz uprawnień do dodawania podkatalogów w tym katalogu - + Not allowed to upload this file because it is read-only on the server, restoring Wgrywanie niedozwolone, ponieważ plik jest tylko do odczytu na serwerze, przywracanie - - + + Not allowed to remove, restoring Brak uprawnień by usunąć, przywracanie - + Local files and share folder removed. Lokalne pliki i udostępniane foldery zostały usunięte. - + Move not allowed, item restored Przenoszenie niedozwolone, obiekt przywrócony - + Move not allowed because %1 is read-only Przenoszenie niedozwolone, ponieważ %1 jest tylko do odczytu - + the destination docelowy - + the source źródło diff --git a/translations/client_pt.ts b/translations/client_pt.ts index 237aaf6ae8..a205630e8d 100644 --- a/translations/client_pt.ts +++ b/translations/client_pt.ts @@ -2996,23 +2996,23 @@ Não é aconselhada a sua utilização. Erro ao ler o ficheiro. - + File/Folder is ignored because it's hidden. O ficheiro/pasta foi ignorado porque está oculto. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() Apenas %1 estão disponíveis, é preciso um mínimo de %2 para começar - + Not allowed because you don't have permission to add parent folder Não permitido, porque não tem permissão para adicionar a pasta fonte - + Not allowed because you don't have permission to add files in that folder Não permitido, porque não tem permissão para adicionar os ficheiros nessa pasta @@ -3072,124 +3072,124 @@ Não é aconselhada a sua utilização. O item não está sincronizado devido a erros anteriores: %1 - + Symbolic links are not supported in syncing. Hiperligações simbólicas não são suportadas em sincronização. - + File is listed on the ignore list. O ficheiro está na lista de ficheiros a ignorar. - + File names ending with a period are not supported on this file system. Nomes de ficheiros acabados com um ponto final não são suportados neste sistema de ficheiros. - + File names containing the character '%1' are not supported on this file system. Nomes de ficheiros que contêm o caractér '%1' não são suportados neste sistema de ficheiros. - + The file name is a reserved name on this file system. O nome de ficheiro é um nome reservado neste sistema de ficheiros. - + Filename contains trailing spaces. Nome de ficheiro contém espaços em branco seguidos. - + Filename is too long. O nome do ficheiro é muito grande - + Stat failed. Estado falhou. - + Filename encoding is not valid Codificação de nome de ficheiro não é válida - + Invalid characters, please rename "%1" Carateres inválidos, por favor, renomeie "%1" - + Unable to initialize a sync journal. Impossível inicializar sincronização 'journal'. - + Unable to read the blacklist from the local database Não foi possível ler a lista negra a partir da base de dados local - + Unable to read from the sync journal. Não foi possível ler a partir do jornal de sincronização. - + Cannot open the sync journal Impossível abrir o jornal de sincronismo - + File name contains at least one invalid character O nome de ficheiro contém pelo menos um caráter inválido - - + + Ignored because of the "choose what to sync" blacklist Ignorado devido à blacklist de escolha para sincronização - + Not allowed because you don't have permission to add subfolders to that folder Não permitido, porque não tem permissão para adicionar as subpastas nessa pasta - + Not allowed to upload this file because it is read-only on the server, restoring Não é permitido enviar este ficheiro porque este é só de leitura no servidor, a restaurar - - + + Not allowed to remove, restoring Não autorizado para remoção, restaurando - + Local files and share folder removed. Ficheiros locais e pasta partilhada removidos. - + Move not allowed, item restored Mover não foi permitido, item restaurado - + Move not allowed because %1 is read-only Mover não foi autorizado porque %1 é só de leitura - + the destination o destino - + the source a origem diff --git a/translations/client_pt_BR.ts b/translations/client_pt_BR.ts index d0d7240eee..4e51114e10 100644 --- a/translations/client_pt_BR.ts +++ b/translations/client_pt_BR.ts @@ -2993,23 +2993,23 @@ It is not advisable to use it. Erro ao ler pasta. - + File/Folder is ignored because it's hidden. Arquivo/pasta ignorado porque porque está escondido. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() Apenas %1 estão disponíveis, precisamos de pelo menos %2 para começar - + Not allowed because you don't have permission to add parent folder Não permitido porque você não tem permissão para adicionar pasta mãe - + Not allowed because you don't have permission to add files in that folder Não permitido porque você não tem permissão para adicionar arquivos na pasta @@ -3069,124 +3069,124 @@ It is not advisable to use it. O item não está sincronizado devido a erros anteriores: %1 - + Symbolic links are not supported in syncing. Linques simbólicos não são suportados em sincronização. - + File is listed on the ignore list. O arquivo está listado na lista de ignorados. - + File names ending with a period are not supported on this file system. Os nomes de arquivos que terminam com um ponto não são suportados neste sistema de arquivos. - + File names containing the character '%1' are not supported on this file system. Os nomes de arquivos que contêm o caractere '%1' não são suportados neste sistema de arquivos. - + The file name is a reserved name on this file system. O nome do arquivo é um nome reservado neste sistema de arquivos. - + Filename contains trailing spaces. O nome do arquivo contém espaços deixados para trás. - + Filename is too long. O nome do arquivo é muito longo. - + Stat failed. Stat falhou. - + Filename encoding is not valid A codificação do nome do arquivo não é válida - + Invalid characters, please rename "%1" Caracteres inválidos, por favor renomear "%1" - + Unable to initialize a sync journal. Impossibilitado de iniciar a sincronização. - + Unable to read the blacklist from the local database Não é possível ler a lista negra a partir do banco de dados local - + Unable to read from the sync journal. Não é possível ler a partir do relatório de sincronização. - + Cannot open the sync journal Não é possível abrir o arquivo de sincronização - + File name contains at least one invalid character O nome do arquivo contem pelo menos um caractere inválido - - + + Ignored because of the "choose what to sync" blacklist Ignorado por causa da lista negra "escolher o que sincronizar" - + Not allowed because you don't have permission to add subfolders to that folder Não permitido porque você não tem permissão para adicionar subpastas para essa pasta - + Not allowed to upload this file because it is read-only on the server, restoring Não é permitido fazer o upload deste arquivo porque ele é somente leitura no servidor, restaurando - - + + Not allowed to remove, restoring Não é permitido remover, restaurando - + Local files and share folder removed. Arquivos locais e pasta compartilhada removida. - + Move not allowed, item restored Não é permitido mover, item restaurado - + Move not allowed because %1 is read-only Não é permitido mover porque %1 é somente para leitura - + the destination o destino - + the source a fonte diff --git a/translations/client_ru.ts b/translations/client_ru.ts index fb2d9ae8df..dff0342c98 100644 --- a/translations/client_ru.ts +++ b/translations/client_ru.ts @@ -2993,23 +2993,23 @@ It is not advisable to use it. Произошла ошибка во время чтения папки. - + File/Folder is ignored because it's hidden. Файл/папка проигнорированы, так как являются скрытыми. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() Только %1 доступно, нужно как минимум %2 чтобы начать - + Not allowed because you don't have permission to add parent folder Не разрешается, так как у вас нет полномочий на добавление родительской папки - + Not allowed because you don't have permission to add files in that folder Не разрешается, так как у вас нет полномочий на добавление файлов в эту папку @@ -3069,124 +3069,124 @@ It is not advisable to use it. Элемент не синхронизируется из-за произошедших ошибок: %1 - + Symbolic links are not supported in syncing. Синхронизация символических ссылок не поддерживается. - + File is listed on the ignore list. Файл присутствует в списке игнорируемых. - + File names ending with a period are not supported on this file system. Эта файловая система не поддерживает имена файлов, оканчивающиеся на точку. - + File names containing the character '%1' are not supported on this file system. Эта файловая система не поддерживает имена файлов, содержащие символ '%1'. - + The file name is a reserved name on this file system. Данное имя файла зарезервировано в данной файловой системе. - + Filename contains trailing spaces. Имя файла содержит пробелы на конце. - + Filename is too long. Имя файла слишком длинное. - + Stat failed. Не удалось загрузить статистику. - + Filename encoding is not valid Кодировка имени файла не верна - + Invalid characters, please rename "%1" Недопустимые символы, пожалуйста, переименуйте "%1" - + Unable to initialize a sync journal. Невозможно инициализировать журнал синхронизации. - + Unable to read the blacklist from the local database Не удалось прочитать файл чёрного списка из локальной базы данных. - + Unable to read from the sync journal. Не удалось прочитать из журнала синхронизации. - + Cannot open the sync journal Не удаётся открыть журнал синхронизации - + File name contains at least one invalid character Имя файла содержит по крайней мере один некорректный символ - - + + Ignored because of the "choose what to sync" blacklist Игнорируется из-за черного списка в "что синхронизировать" - + Not allowed because you don't have permission to add subfolders to that folder Не разрешается, так как у вас нет полномочий на добавление подпапок в папку. - + Not allowed to upload this file because it is read-only on the server, restoring Не допускается загрузка этого файла, так как на сервере он помечен только для чтения, восстанавливаем - - + + Not allowed to remove, restoring Не допускается удаление, восстанавливаем - + Local files and share folder removed. Локальные файлы и общий каталог удалены. - + Move not allowed, item restored Перемещение не допускается, элемент восстановлен - + Move not allowed because %1 is read-only Перемещение не допускается, поскольку %1 помечен только для чтения - + the destination назначение - + the source источник @@ -3456,7 +3456,7 @@ It is not advisable to use it. <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> - + <p>Версия %2. Для более подробной информации посетите <a href="%3">https://%4</a></p><p>По поводу известных проблем и помощи, пожалуйста посетите: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>Авторы: Клаас Фрейтаг, Дэниель Молкентен, Оливье Гоффар, Маркус Гётц, Жан-Кристоф Бошар, и другие.</small></p><p>Авторские права принадлежат ownCloud GmbH</p><p>Лицензировано под GNU General Public License (GPL) версии 2.0<br/>ownCloud и логотип ownCloud — зарегистрированные торговые марки ownCloud GmbH в Соединённых Штатах, и/или других странах.</p> diff --git a/translations/client_sk.ts b/translations/client_sk.ts index 23441bf53d..bd19c7aacd 100644 --- a/translations/client_sk.ts +++ b/translations/client_sk.ts @@ -2985,23 +2985,23 @@ Nie je vhodné ju používať. Chyba pri čítaní adresára - + File/Folder is ignored because it's hidden. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() - + Not allowed because you don't have permission to add parent folder - + Not allowed because you don't have permission to add files in that folder @@ -3061,124 +3061,124 @@ Nie je vhodné ju používať. Položka nebola synchronizovaná kvôli predchádzajúcej chybe: %1 - + Symbolic links are not supported in syncing. Symbolické odkazy nie sú podporované pri synchronizácii. - + File is listed on the ignore list. Súbor je zapísaný na zozname ignorovaných. - + File names ending with a period are not supported on this file system. - + File names containing the character '%1' are not supported on this file system. - + The file name is a reserved name on this file system. - + Filename contains trailing spaces. - + Filename is too long. Meno súboru je veľmi dlhé. - + Stat failed. - + Filename encoding is not valid Kódovanie znakov názvu súboru je neplatné - + Invalid characters, please rename "%1" Neplatné znaky, premenujte prosím "%1" - + Unable to initialize a sync journal. Nemôžem inicializovať synchronizačný žurnál. - + Unable to read the blacklist from the local database - + Unable to read from the sync journal. Nemožno čítať zo synchronizačného žurnálu - + Cannot open the sync journal Nemožno otvoriť sync žurnál - + File name contains at least one invalid character Názov súboru obsahuje nevhodný znak - - + + Ignored because of the "choose what to sync" blacklist Ignorované podľa nastavenia "vybrať čo synchronizovať" - + Not allowed because you don't have permission to add subfolders to that folder Nie je dovolené, lebo nemáte oprávnenie pridávať podpriečinky do tohto priečinka - + Not allowed to upload this file because it is read-only on the server, restoring Nie je dovolené tento súbor nahrať, pretože je na serveri iba na čítanie. Obnovuje sa. - - + + Not allowed to remove, restoring Nie je dovolené odstrániť. Obnovuje sa. - + Local files and share folder removed. Lokálne súbory a zdieľaný priečinok boli odstránené. - + Move not allowed, item restored Presunutie nie je dovolené. Položka obnovená. - + Move not allowed because %1 is read-only Presunutie nie je dovolené, pretože %1 je na serveri iba na čítanie - + the destination cieľ - + the source zdroj diff --git a/translations/client_sl.ts b/translations/client_sl.ts index 9178317d98..ceac569bab 100644 --- a/translations/client_sl.ts +++ b/translations/client_sl.ts @@ -2996,23 +2996,23 @@ Uporaba ni priporočljiva. Napaka med branjem mape - + File/Folder is ignored because it's hidden. Datoteka/Mapa je prezrta, ker je skrita. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() Le %1 je na voljo, zahtevanih pa je vaj %2 za zagon - + Not allowed because you don't have permission to add parent folder Dejanje ni dovoljeno, ker ni ustreznih dovoljenj za dodajanje starševske mape - + Not allowed because you don't have permission to add files in that folder Dejanje ni dovoljeno, ker ni ustreznih dovoljenj za dodajanje datotek v to mapo @@ -3072,124 +3072,124 @@ Uporaba ni priporočljiva. Predmet ni usklajen zaradi predhodne napake: %1 - + Symbolic links are not supported in syncing. Usklajevanje simbolnih povezav ni podprto. - + File is listed on the ignore list. Datoteka je na seznamu prezrtih datotek. - + File names ending with a period are not supported on this file system. Imena datotek, ki vsebujejo piko, na tem sistemu niso podprta. - + File names containing the character '%1' are not supported on this file system. Imena datotek, ki vsebujejo znak »%1«, na tem sistemu niso podprta. - + The file name is a reserved name on this file system. Ime datoteke je na tem sistemu zadržano za sistemsko datoteko. - + Filename contains trailing spaces. Datoteka vsebuje pripete presledne znake - + Filename is too long. Ime datoteke je predolgo. - + Stat failed. Določanje statističnih podatkov je spodletelo. - + Filename encoding is not valid Kodni zapis imena datoteke ni veljaven. - + Invalid characters, please rename "%1" Uporabljen je neveljaven znak; preimenujte "%1" - + Unable to initialize a sync journal. Dnevnika usklajevanja ni mogoče začeti. - + Unable to read the blacklist from the local database Ni mogoče prebrati črnega seznama iz krajevne mape - + Unable to read from the sync journal. Ni mogoče brati iz dnevnika usklajevanja - + Cannot open the sync journal Ni mogoče odpreti dnevnika usklajevanja - + File name contains at least one invalid character Ime datoteke vsebuje vsaj en neveljaven znak. - - + + Ignored because of the "choose what to sync" blacklist Prezrto, ker je predmet označen na črni listi za usklajevanje - + Not allowed because you don't have permission to add subfolders to that folder Dejanje ni dovoljeno! Ni ustreznih dovoljenj za dodajanje podmap v to mapo. - + Not allowed to upload this file because it is read-only on the server, restoring Ni dovoljeno pošiljati te datoteke, ker ima določena dovoljenja le za branje. Datoteka bo obnovljena na izvorno različico. - - + + Not allowed to remove, restoring Odstranitev ni dovoljena, datoteka bo obnovljena. - + Local files and share folder removed. Krajevne datoteke in mape v souporabi so odstranjene. - + Move not allowed, item restored Premikanje ni dovoljeno, datoteka bo obnovljena. - + Move not allowed because %1 is read-only Premikanje ni dovoljeno, ker je nastavljeno določilo %1 le za branje. - + the destination cilj - + the source vir diff --git a/translations/client_sr.ts b/translations/client_sr.ts index 32a73ea25d..d448d82386 100644 --- a/translations/client_sr.ts +++ b/translations/client_sr.ts @@ -2985,23 +2985,23 @@ It is not advisable to use it. - + File/Folder is ignored because it's hidden. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() - + Not allowed because you don't have permission to add parent folder - + Not allowed because you don't have permission to add files in that folder @@ -3061,124 +3061,124 @@ It is not advisable to use it. Ставка није синхронизована због ранијих грешака: %1 - + Symbolic links are not supported in syncing. Симболичке везе нису подржане у синхронизацији. - + File is listed on the ignore list. Фајл се налази на листи за игнорисање. - + File names ending with a period are not supported on this file system. - + File names containing the character '%1' are not supported on this file system. - + The file name is a reserved name on this file system. - + Filename contains trailing spaces. - + Filename is too long. Назив фајла је предугачак. - + Stat failed. - + Filename encoding is not valid Кодирање назива фајла није исправно - + Invalid characters, please rename "%1" - + Unable to initialize a sync journal. Није могуће покренути у синхронизацију дневника. - + Unable to read the blacklist from the local database - + Unable to read from the sync journal. - + Cannot open the sync journal Не могу да отворим дневник синхронизације - + File name contains at least one invalid character Назив садржи бар један недозвољен карактер - - + + Ignored because of the "choose what to sync" blacklist Игнорисано јер се не налази на листи за синхронизацију - + Not allowed because you don't have permission to add subfolders to that folder - + Not allowed to upload this file because it is read-only on the server, restoring Није могуће отпремити овај фајл јер је на серверу само за читање. Враћам - - + + Not allowed to remove, restoring Није могуће уклањање. Враћам - + Local files and share folder removed. Локални фајлови и дељена фасцикла су уклоњени. - + Move not allowed, item restored Премештање није дозвољено. Ставка је враћена - + Move not allowed because %1 is read-only Премештање није дозвољено јер %1 је само за читање - + the destination одредиште - + the source извор diff --git a/translations/client_sv.ts b/translations/client_sv.ts index c2e8c380bf..b9599b0570 100644 --- a/translations/client_sv.ts +++ b/translations/client_sv.ts @@ -2989,23 +2989,23 @@ Det är inte lämpligt använda den. Fel vid mappinläsning. - + File/Folder is ignored because it's hidden. Filen/Mappen är ignorerad för att den är dold. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() Endast %1 tillgängligt, behöver minst %2 för att starta - + Not allowed because you don't have permission to add parent folder Otillåtet eftersom du inte har rättigheter att lägga till övermappar - + Not allowed because you don't have permission to add files in that folder Otillåtet eftersom du inte har rättigheter att lägga till filer i den mappen. @@ -3065,124 +3065,124 @@ Det är inte lämpligt använda den. Objektet kunde inte synkas på grund av tidigare fel: %1 - + Symbolic links are not supported in syncing. Symboliska länkar stöds ej i synkningen. - + File is listed on the ignore list. Filen är listad i ignorerings listan. - + File names ending with a period are not supported on this file system. - + File names containing the character '%1' are not supported on this file system. - + The file name is a reserved name on this file system. - + Filename contains trailing spaces. Filnamn innehåller mellanslag i slutet. - + Filename is too long. Filnamnet är för långt. - + Stat failed. Stat misslyckades. - + Filename encoding is not valid Filnamnskodning är inte giltig - + Invalid characters, please rename "%1" Otillåtna tecken, var vänlig byt namn på "%1" - + Unable to initialize a sync journal. Kan inte initialisera en synk journal. - + Unable to read the blacklist from the local database Kunde inte läsa svartlistan från den lokala databasen - + Unable to read from the sync journal. Kunde inte läsa från synk-journalen. - + Cannot open the sync journal Kunde inte öppna synk journalen - + File name contains at least one invalid character Filnamnet innehåller minst ett ogiltigt tecken - - + + Ignored because of the "choose what to sync" blacklist Ignorerad eftersom den är svartlistad i "välj vad som ska synkas" - + Not allowed because you don't have permission to add subfolders to that folder Otillåtet eftersom du inte har rättigheter att lägga till undermappar i den mappen. - + Not allowed to upload this file because it is read-only on the server, restoring Inte behörig att ladda upp denna fil då den är skrivskyddad på servern, återställer - - + + Not allowed to remove, restoring Inte behörig att radera, återställer - + Local files and share folder removed. Lokala filer och mappar som är delade är borttagna. - + Move not allowed, item restored Det gick inte att genomföra flytten, objektet återställs - + Move not allowed because %1 is read-only Det gick inte att genomföra flytten då %1 är skrivskyddad - + the destination destinationen - + the source källan diff --git a/translations/client_th.ts b/translations/client_th.ts index d5c880363c..ff11aa8071 100644 --- a/translations/client_th.ts +++ b/translations/client_th.ts @@ -2995,23 +2995,23 @@ It is not advisable to use it. เกิดข้อผิดพลาดขณะกำลังอ่านโฟลเดอร์ - + File/Folder is ignored because it's hidden. ไฟล์/โฟลเดอร์ ที่ซ่อนอยู่จะถูกละเว้น - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() มีเพียง %1 ที่พร้อมใช้งาน คุณจำเป็นต้องมีไม่น้อยกว่า %2 เพื่อเริ่มใช้งาน - + Not allowed because you don't have permission to add parent folder ไม่ได้รับอนุญาต เพราะคุณไม่มีสิทธิ์ที่จะเพิ่มโฟลเดอร์หลัก - + Not allowed because you don't have permission to add files in that folder ไม่ได้รับอนุญาต เพราะคุณไม่มีสิทธิ์ที่จะเพิ่มไฟล์ในโฟลเดอร์นั้น @@ -3071,124 +3071,124 @@ It is not advisable to use it. รายการจะไม่ถูกประสานข้อมูลเนื่องจากเกิดข้อผิดพลาดก่อนหน้านี้: %1 - + Symbolic links are not supported in syncing. ลิงค์สัญลักษณ์จะไม่ได้รับการสนับสนุนในการประสานข้อมูล - + File is listed on the ignore list. ไฟล์อยู่ในรายการที่ละเว้น - + File names ending with a period are not supported on this file system. ชื่อไฟล์ที่ลงท้ายด้วยระยะเวลา ยังไม่ได้รับการสนับสนุนบนระบบไฟล์นี้ - + File names containing the character '%1' are not supported on this file system. ชื่อไฟล์ที่มีตัวอักษร '%1' ยังไม่ได้รับการสนับสนุนบนระบบไฟล์นี้ - + The file name is a reserved name on this file system. ชื่อไฟล์นี้เป็นชื่อที่ถูกสงวนไว้ - + Filename contains trailing spaces. ชื่อไฟล์มีช่องว่างต่อท้าย - + Filename is too long. ชื่อไฟล์ยาวเกินไป - + Stat failed. สถิติความล้มเหลว - + Filename encoding is not valid การเข้ารหัสชื่อไฟล์ไม่ถูกต้อง - + Invalid characters, please rename "%1" ตัวอักษรไม่ถูกต้อง โปรดเปลี่ยนชื่อ "%1" - + Unable to initialize a sync journal. ไม่สามารถเตรียมการประสานข้อมูลเจอร์นัล - + Unable to read the blacklist from the local database ไม่สามารถอ่านบัญชีดำจากฐานข้อมูลต้นทาง - + Unable to read from the sync journal. ไม่สามารถอ่านจากบันทึกการประสานข้อมูล - + Cannot open the sync journal ไม่สามารถเปิดการผสานข้อมูลเจอร์นัล - + File name contains at least one invalid character มีชื่อแฟ้มอย่างน้อยหนึ่งตัวอักษรที่ไม่ถูกต้อง - - + + Ignored because of the "choose what to sync" blacklist ถูกละเว้นเพราะ "ข้อมูลที่เลือกประสาน" ติดบัญชีดำ - + Not allowed because you don't have permission to add subfolders to that folder ไม่อนุญาติเพราะคุณไม่มีสิทธิ์ที่จะเพิ่มโฟลเดอร์ย่อยของโฟลเดอร์นั้น - + Not allowed to upload this file because it is read-only on the server, restoring ไม่อนุญาตให้อัพโหลดไฟล์นี้เพราะมันจะอ่านได้เพียงอย่างเดียวบนเซิร์ฟเวอร์ กำลังฟื้นฟู - - + + Not allowed to remove, restoring ไม่อนุญาตให้ลบเพราะกำลังฟื้นฟู - + Local files and share folder removed. ไฟล์ต้นทางและโฟลเดอร์ที่แชร์ถูกลบออก - + Move not allowed, item restored ไม่ได้รับอนุญาตให้ย้าย เพราะกำลังกู้คืนรายการ - + Move not allowed because %1 is read-only ไม่อนุญาตให้ย้ายเพราะ %1 จะอ่านได้เพียงอย่างเดียว - + the destination ปลายทาง - + the source แหล่งที่มา diff --git a/translations/client_tr.ts b/translations/client_tr.ts index 260fe62c6e..f053594e10 100644 --- a/translations/client_tr.ts +++ b/translations/client_tr.ts @@ -2986,23 +2986,23 @@ Kullanmanız önerilmez. Klasör okunurken hata oluştu. - + File/Folder is ignored because it's hidden. Dosya/Klasör gizli olduğu için yoksayıldı. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() Sadece %1 mevcut, Çalıştırmak için en az %2 gerekmektedir - + Not allowed because you don't have permission to add parent folder Üst dizin ekleme yetkiniz olmadığından izin verilmedi - + Not allowed because you don't have permission to add files in that folder Bu klasöre dosya ekleme yetkiniz olmadığından izin verilmedi @@ -3062,124 +3062,124 @@ Kullanmanız önerilmez. Bu öge, önceki hatalardan dolayı eşitlenemiyor: %1 - + Symbolic links are not supported in syncing. Sembolik bağlantılar eşitlemede desteklenmiyor. - + File is listed on the ignore list. Dosya yoksayma listesinde. - + File names ending with a period are not supported on this file system. - + File names containing the character '%1' are not supported on this file system. - + The file name is a reserved name on this file system. Dosya adı bu dosya sisteminde ayrılmış bir addır. - + Filename contains trailing spaces. - + Filename is too long. Dosya adı çok uzun. - + Stat failed. Durum alma başarısız. - + Filename encoding is not valid Dosya adı kodlaması geçerli değil - + Invalid characters, please rename "%1" Geçersiz karakterler, lütfen "%1" yerine yeni bir isim girin - + Unable to initialize a sync journal. Bir eşitleme günlüğü başlatılamadı. - + Unable to read the blacklist from the local database Yerel veritabanından kara liste okunamadı - + Unable to read from the sync journal. Eşitleme günlüğünden okunamadı. - + Cannot open the sync journal Eşitleme günlüğü açılamıyor - + File name contains at least one invalid character Dosya adı en az bir geçersiz karakter içeriyor - - + + Ignored because of the "choose what to sync" blacklist "Eşitlenecekleri seçin" kara listesinde olduğundan yoksayıldı. - + Not allowed because you don't have permission to add subfolders to that folder Bu dizine alt dizin ekleme yetkiniz olmadığından izin verilmedi - + Not allowed to upload this file because it is read-only on the server, restoring Sunucuda salt okunur olduğundan, bu dosya yüklenemedi, geri alınıyor - - + + Not allowed to remove, restoring Kaldırmaya izin verilmedi, geri alınıyor - + Local files and share folder removed. Yerel dosyalar ve paylaşım klasörü kaldırıldı. - + Move not allowed, item restored Taşımaya izin verilmedi, öge geri alındı - + Move not allowed because %1 is read-only %1 salt okunur olduğundan taşımaya izin verilmedi - + the destination hedef - + the source kaynak diff --git a/translations/client_uk.ts b/translations/client_uk.ts index aaa7ad4656..66f2561d8c 100644 --- a/translations/client_uk.ts +++ b/translations/client_uk.ts @@ -2984,23 +2984,23 @@ It is not advisable to use it. - + File/Folder is ignored because it's hidden. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() Доступно лише %1, для початку необхідно хоча б %2 - + Not allowed because you don't have permission to add parent folder - + Not allowed because you don't have permission to add files in that folder @@ -3060,124 +3060,124 @@ It is not advisable to use it. Шлях не синхронізується через помилки: %1 - + Symbolic links are not supported in syncing. Синхронізація символічних посилань не підтримується. - + File is listed on the ignore list. Файл присутній у списку ігнорованих. - + File names ending with a period are not supported on this file system. - + File names containing the character '%1' are not supported on this file system. - + The file name is a reserved name on this file system. - + Filename contains trailing spaces. - + Filename is too long. Шлях до файлу занадто довгий. - + Stat failed. - + Filename encoding is not valid Кодування файлу не припустиме - + Invalid characters, please rename "%1" - + Unable to initialize a sync journal. Не вдалося ініціалізувати протокол синхронізації. - + Unable to read the blacklist from the local database - + Unable to read from the sync journal. - + Cannot open the sync journal Не вдається відкрити протокол синхронізації - + File name contains at least one invalid character Ім’я файлу містить принаймні один некоректний символ - - + + Ignored because of the "choose what to sync" blacklist Ігнорується через чорний список в "обрати що синхронізувати" - + Not allowed because you don't have permission to add subfolders to that folder Заборонено через відсутність прав додавання підкаталогів в цю теку. - + Not allowed to upload this file because it is read-only on the server, restoring Не дозволено завантажувати цей файл, оскільки він має дозвіл лише на перегляд, відновлюємо - - + + Not allowed to remove, restoring Не дозволено видаляти, відновлюємо - + Local files and share folder removed. Локальні файли та теки в загальному доступі було видалено. - + Move not allowed, item restored Переміщення не дозволено, елемент відновлено - + Move not allowed because %1 is read-only Переміщення не дозволено, оскільки %1 помічений тільки для перегляду - + the destination призначення - + the source джерело diff --git a/translations/client_zh_CN.ts b/translations/client_zh_CN.ts index a99d1383e3..0f0ce5d3e3 100644 --- a/translations/client_zh_CN.ts +++ b/translations/client_zh_CN.ts @@ -2986,23 +2986,23 @@ It is not advisable to use it. 读取目录时出错 - + File/Folder is ignored because it's hidden. 已忽略隐藏的文件和文件夹。 - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() 仅有 %1 有效,至少需要 %2 才能开始 - + Not allowed because you don't have permission to add parent folder 你没有权限增加父目录 - + Not allowed because you don't have permission to add files in that folder 你没有权限增加文件 @@ -3062,124 +3062,124 @@ It is not advisable to use it. 文件没有被同步因为之前的错误: %1 - + Symbolic links are not supported in syncing. 符号链接不被同步支持。 - + File is listed on the ignore list. 文件在忽略列表中。 - + File names ending with a period are not supported on this file system. - + File names containing the character '%1' are not supported on this file system. 此文件系统不支持包含字符 '%1' 的文件名。 - + The file name is a reserved name on this file system. - + Filename contains trailing spaces. 文件名尾部含有空格 - + Filename is too long. 文件名过长。 - + Stat failed. 状态失败。 - + Filename encoding is not valid 文件名编码无效 - + Invalid characters, please rename "%1" 无效的字符,请更改为 “%1” - + Unable to initialize a sync journal. 无法初始化同步日志 - + Unable to read the blacklist from the local database 无法从本地数据库读取黑名单 - + Unable to read from the sync journal. 无法读取同步日志。 - + Cannot open the sync journal 无法打开同步日志 - + File name contains at least one invalid character 文件名中存在至少一个非法字符 - - + + Ignored because of the "choose what to sync" blacklist 已忽略(“选择同步内容”黑名单) - + Not allowed because you don't have permission to add subfolders to that folder 你没有权限增加子目录 - + Not allowed to upload this file because it is read-only on the server, restoring 无法上传文件,因为服务器端此文件为只读,正在回退 - - + + Not allowed to remove, restoring 无法删除,正在回退 - + Local files and share folder removed. 本地文件和共享文件夹已被删除。 - + Move not allowed, item restored 无法移动,正在回退 - + Move not allowed because %1 is read-only 无法移动,%1为是只读的 - + the destination 目标 - + the source diff --git a/translations/client_zh_TW.ts b/translations/client_zh_TW.ts index ff436c0e51..9513b38fb1 100644 --- a/translations/client_zh_TW.ts +++ b/translations/client_zh_TW.ts @@ -2987,23 +2987,23 @@ It is not advisable to use it. 讀取資料夾時發生錯誤。 - + File/Folder is ignored because it's hidden. - + Only %1 are available, need at least %2 to start Placeholders are postfixed with file sizes using Utility::octetsToString() 目前僅有 %1 可以使用,至少需要 %2 才能開始 - + Not allowed because you don't have permission to add parent folder 拒絕此操作,您沒有新增母資料夾的權限。 - + Not allowed because you don't have permission to add files in that folder 拒絕此操作,您沒有新增檔案在此資料夾的權限。 @@ -3063,124 +3063,124 @@ It is not advisable to use it. 因為先前的錯誤: %1 物件沒有同步成功 - + Symbolic links are not supported in syncing. 同步不支援捷徑連結 - + File is listed on the ignore list. 檔案被列在忽略清單。 - + File names ending with a period are not supported on this file system. - + File names containing the character '%1' are not supported on this file system. - + The file name is a reserved name on this file system. - + Filename contains trailing spaces. - + Filename is too long. 檔案名稱太長了。 - + Stat failed. 狀態失敗。 - + Filename encoding is not valid 檔案名稱編碼是無效的 - + Invalid characters, please rename "%1" 無效的字元,請您重新命名 "%1" - + Unable to initialize a sync journal. 同步處理日誌無法初始化 - + Unable to read the blacklist from the local database - + Unable to read from the sync journal. - + Cannot open the sync journal 同步處理日誌無法開啟 - + File name contains at least one invalid character 檔案名稱含有不合法的字元 - - + + Ignored because of the "choose what to sync" blacklist 已忽略。根據 "選擇要同步的項目"的黑名單 - + Not allowed because you don't have permission to add subfolders to that folder 拒絕此操作,您沒有在此新增子資料夾的權限。 - + Not allowed to upload this file because it is read-only on the server, restoring 拒絕上傳此檔案,此檔案在伺服器是唯讀檔,復原中 - - + + Not allowed to remove, restoring 不允許刪除,復原中 - + Local files and share folder removed. 本地端檔案和共享資料夾已被刪除。 - + Move not allowed, item restored 不允許移動,物件復原中 - + Move not allowed because %1 is read-only 不允許移動,因為 %1 是唯讀的 - + the destination 目標 - + the source 來源 From 01b3cde79b1609a9a9325fdab3919365b5721556 Mon Sep 17 00:00:00 2001 From: Jenkins for ownCloud Date: Thu, 6 Jul 2017 02:18:28 +0200 Subject: [PATCH 029/107] [tx-robot] updated from transifex --- mirall.desktop.in | 3 +++ translations/client_sv.ts | 24 ++++++++++++------------ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/mirall.desktop.in b/mirall.desktop.in index def9efb889..f69534a713 100644 --- a/mirall.desktop.in +++ b/mirall.desktop.in @@ -87,6 +87,9 @@ X-GNOME-Autostart-Delay=3 # Translations +# Translations + + # Translations Comment[oc]=@APPLICATION_NAME@ sincronizacion del client GenericName[oc]=Dorsièr de Sincronizacion diff --git a/translations/client_sv.ts b/translations/client_sv.ts index b9599b0570..570a5b8eb4 100644 --- a/translations/client_sv.ts +++ b/translations/client_sv.ts @@ -163,12 +163,12 @@ Force sync now - + Tvinga synkning Restart sync - + Starta om synkning @@ -508,7 +508,7 @@ Certificate & Key (pkcs12) : - + Certifikat och nyckel (pkcs12) : @@ -536,7 +536,7 @@ Error accessing the configuration file - + Kunde inte komma åt konfigurationsfilen @@ -546,7 +546,7 @@ Quit ownCloud - + Avsluta ownCloud @@ -1833,7 +1833,7 @@ Det är inte lämpligt använda den. Invalid URL - + Ogiltig URL @@ -2222,12 +2222,12 @@ Det är inte lämpligt använda den. Missing File ID from server - + Saknar Fil-ID från servern Missing ETag from server - + Saknar ETag från servern @@ -2550,7 +2550,7 @@ Det är inte lämpligt använda den. Anyone with the link has access to the file/folder - + Vem som helst med länken kan komma åt filen eller mappen @@ -3403,7 +3403,7 @@ Det är inte lämpligt använda den. New account... - + Nytt konto... @@ -3628,7 +3628,7 @@ Det är inte lämpligt använda den. Ser&ver Address - + Ser&veradress @@ -3798,7 +3798,7 @@ Det är inte lämpligt använda den. built with %1 - + byggt med %1 From 607287a9d619c31664f12ec9b1f5b081dc8234e8 Mon Sep 17 00:00:00 2001 From: Hefee Date: Thu, 6 Jul 2017 08:41:02 +0200 Subject: [PATCH 030/107] Use OWNCLOUD_BIN_DIR directly in C++ code. No need to do a STR replacement and the macro changing the path. It actually broke test execution for Debian. See https://bugs.debian.org/844937 --- test/owncloud_add_test.cmake | 4 ++-- test/testutility.cpp | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/test/owncloud_add_test.cmake b/test/owncloud_add_test.cmake index bf15ab890b..cefe264eac 100644 --- a/test/owncloud_add_test.cmake +++ b/test/owncloud_add_test.cmake @@ -22,7 +22,7 @@ macro(owncloud_add_test test_class additional_cpp) ) add_definitions(-DOWNCLOUD_TEST) - add_definitions(-DOWNCLOUD_BIN_PATH=${CMAKE_BINARY_DIR}/bin) + add_definitions(-DOWNCLOUD_BIN_PATH="${CMAKE_BINARY_DIR}/bin") add_test(NAME ${OWNCLOUD_TEST_CLASS}Test COMMAND ${OWNCLOUD_TEST_CLASS}Test) endmacro() @@ -51,5 +51,5 @@ macro(owncloud_add_benchmark test_class additional_cpp) ) add_definitions(-DOWNCLOUD_TEST) - add_definitions(-DOWNCLOUD_BIN_PATH=${CMAKE_BINARY_DIR}/bin) + add_definitions(-DOWNCLOUD_BIN_PATH="${CMAKE_BINARY_DIR}/bin") endmacro() diff --git a/test/testutility.cpp b/test/testutility.cpp index 5a656ac846..5093ce76de 100644 --- a/test/testutility.cpp +++ b/test/testutility.cpp @@ -11,10 +11,6 @@ #include "utility.h" -#define STR_(X) #X -#define STR(X) STR_(X) -#define BIN_PATH STR(OWNCLOUD_BIN_PATH) - using namespace OCC::Utility; class TestUtility : public QObject @@ -118,7 +114,7 @@ private slots: } // pass the binary name owncloud to the next call. This brakes branding, // but branding is not supposed to work with this. - QString ver = versionOfInstalledBinary(BIN_PATH+QLatin1String("/owncloud")); + QString ver = versionOfInstalledBinary(OWNCLOUD_BIN_PATH+QLatin1String("/owncloud")); qDebug() << "Version of installed ownCloud Binary: " << ver; QVERIFY( !ver.isEmpty()); From d01065b9a12e69ca493a232f3a8e8f3d416fed52 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Thu, 6 Jul 2017 13:43:34 +0200 Subject: [PATCH 031/107] Fix crash on account deletion Calling forgetSensitiveData() on account deletion leads to a timer for clearQNAMCache() being queued. Then the Account object is deleted. The Credentials object stays alive for now because it has a deleteLater deleter. If the timer calls into a slot on the Credentials object, the _account pointer will be invalid at this time. As a workaround, move the target slot to Account - that way it will not be called as the account object is already destroyed. However since Account and Credentials are mutually dependent, it would be much preferable if their lifetimes were linked, avoiding this category of bugs. The current behavior was introduced in d40c56eda561e3a541bf1b23f70fa8d659d3037e and I currently don't understand why - maybe there's another way of dealing with the problem that existed then. --- src/libsync/account.cpp | 5 +++++ src/libsync/account.h | 4 ++++ src/libsync/creds/httpcredentials.cpp | 11 +---------- src/libsync/creds/httpcredentials.h | 1 - 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/libsync/account.cpp b/src/libsync/account.cpp index 39d15f9719..19ace5ed0a 100644 --- a/src/libsync/account.cpp +++ b/src/libsync/account.cpp @@ -391,6 +391,11 @@ void Account::handleInvalidCredentials() emit invalidCredentials(); } +void Account::clearQNAMCache() +{ + _am->clearAccessCache(); +} + const Capabilities &Account::capabilities() const { return _capabilities; diff --git a/src/libsync/account.h b/src/libsync/account.h index 4e554e5908..66e0d1be61 100644 --- a/src/libsync/account.h +++ b/src/libsync/account.h @@ -197,6 +197,10 @@ public: /// Called by network jobs on credential errors, emits invalidCredentials() void handleInvalidCredentials(); +public slots: + /// Used when forgetting credentials + void clearQNAMCache(); + signals: /// Emitted whenever there's network activity void propagatorNetworkActivity(); diff --git a/src/libsync/creds/httpcredentials.cpp b/src/libsync/creds/httpcredentials.cpp index ce37ce93ee..1670e0101e 100644 --- a/src/libsync/creds/httpcredentials.cpp +++ b/src/libsync/creds/httpcredentials.cpp @@ -370,16 +370,7 @@ void HttpCredentials::invalidateToken() // indirectly) from QNetworkAccessManagerPrivate::authenticationRequired, which itself // is a called from a BlockingQueuedConnection from the Qt HTTP thread. And clearing the // cache needs to synchronize again with the HTTP thread. - QTimer::singleShot(0, this, SLOT(clearQNAMCache())); -} - -void HttpCredentials::clearQNAMCache() -{ -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - _account->networkAccessManager()->clearAccessCache(); -#else - _account->resetNetworkAccessManager(); -#endif + QTimer::singleShot(0, _account, SLOT(clearQNAMCache())); } void HttpCredentials::forgetSensitiveData() diff --git a/src/libsync/creds/httpcredentials.h b/src/libsync/creds/httpcredentials.h index 45b01c5ee5..03f3b8c702 100644 --- a/src/libsync/creds/httpcredentials.h +++ b/src/libsync/creds/httpcredentials.h @@ -110,7 +110,6 @@ private Q_SLOTS: void slotWriteClientCertPEMJobDone(QKeychain::Job *); void slotWriteClientKeyPEMJobDone(QKeychain::Job *); void slotWriteJobDone(QKeychain::Job *); - void clearQNAMCache(); protected: QString _user; From 4e5e290ec2cdb328dc64d2fd223a64099cb546df Mon Sep 17 00:00:00 2001 From: Jenkins for ownCloud Date: Fri, 7 Jul 2017 01:15:16 +0200 Subject: [PATCH 032/107] [tx-robot] updated from transifex --- admin/win/nsi/l10n/Portuguese.nsh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/win/nsi/l10n/Portuguese.nsh b/admin/win/nsi/l10n/Portuguese.nsh index 7d32b7c14e..6b2abd2d4f 100644 --- a/admin/win/nsi/l10n/Portuguese.nsh +++ b/admin/win/nsi/l10n/Portuguese.nsh @@ -18,7 +18,7 @@ StrCpy $SEC_APPLICATION_DETAILS "A instalar o essencial de ${APPLICATION_NAME}." StrCpy $OPTION_SECTION_SC_SHELL_EXT_SECTION "Integração para Windows Explorer" StrCpy $OPTION_SECTION_SC_SHELL_EXT_DetailPrint "A instalar integração para Windows Explorer" StrCpy $OPTION_SECTION_SC_START_MENU_SECTION "Atalho do progama no Menu Iniciar" -StrCpy $OPTION_SECTION_SC_START_MENU_DetailPrint "A adicionar o atalho de ${APPLICATION_NAME} ao Menu Inicial." +StrCpy $OPTION_SECTION_SC_START_MENU_DetailPrint "A adicionar o atalho de ${APPLICATION_NAME} no Menu Iniciar." StrCpy $OPTION_SECTION_SC_DESKTOP_SECTION "Atalho da área de trabalho" StrCpy $OPTION_SECTION_SC_DESKTOP_DetailPrint "A criar atalhos na área de trabalho" StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_SECTION "Atalho de início rápido" From 383ba63c5ae3cbcff15265544a3e8dd3a15c6269 Mon Sep 17 00:00:00 2001 From: Jenkins for ownCloud Date: Fri, 7 Jul 2017 02:18:31 +0200 Subject: [PATCH 033/107] [tx-robot] updated from transifex --- mirall.desktop.in | 4 ++++ translations/client_pt.ts | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/mirall.desktop.in b/mirall.desktop.in index f69534a713..7b1371b2bb 100644 --- a/mirall.desktop.in +++ b/mirall.desktop.in @@ -90,6 +90,9 @@ X-GNOME-Autostart-Delay=3 # Translations +# Translations + + # Translations Comment[oc]=@APPLICATION_NAME@ sincronizacion del client GenericName[oc]=Dorsièr de Sincronizacion @@ -229,6 +232,7 @@ Comment[zh_CN]=@APPLICATION_NAME@ 桌面同步客户端 GenericName[zh_CN]=文件夹同步 Name[zh_CN]=@APPLICATION_NAME@ 桌面同步客户端 Icon[zh_CN]=@APPLICATION_EXECUTABLE@ +Comment[zh_HK]=桌面版同步客户端 GenericName[zh_TW]=資料夾同步 Comment[es_AR]=Cliente de sincronización para escritorio @APPLICATION_NAME@ GenericName[es_AR]=Sincronización de directorio diff --git a/translations/client_pt.ts b/translations/client_pt.ts index a205630e8d..d5f5bb6436 100644 --- a/translations/client_pt.ts +++ b/translations/client_pt.ts @@ -3459,7 +3459,7 @@ Não é aconselhada a sua utilização. <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> - + <p>Versão %2. Para mais informação visite <a href="%3">https://%4</a></p><p>Para problemas conhecidos e ajuda, por favor, visite: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>por Por Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, e outros.</small></p><p>Direitos de Autor - ownCloud GmbH</p><p>Licenciado sob a Licença Pública Geral GNU (GPL), versão 2.0<br/>ownCloud e o logótipo ownCloud são marcas registadas de ownCloud GmbH nos Estados Unidos, outros países, ou ambos.</p> From 0238a29c7cd8bffce79ac684d3aae15bed481412 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Wed, 10 May 2017 09:37:10 +0200 Subject: [PATCH 034/107] Introduce private link sharing #5023 * SocketAPI has COPL_LOCAL_LINK / EMAIL_LOCAL_LINK commands * The nautilus and dolphing shell integrations show a submenu from which one can share as well as access the private link. * The SocketAPI provides a new GET_STRINGS command to access localized strings. * The private link can also be accessed from the user/group sharing dialog. * The numeric file id is extracted from the full id to create the private link url. --- .../dolphin/ownclouddolphinactionplugin.cpp | 28 ++++++-- .../dolphin/ownclouddolphinpluginhelper.cpp | 10 +-- .../dolphin/ownclouddolphinpluginhelper.h | 16 ++++- shell_integration/nautilus/syncstate.py | 59 +++++++++++++---- src/gui/CMakeLists.txt | 2 +- src/gui/application.cpp | 4 +- src/gui/clipboard.mm | 14 ---- src/gui/guiutility.cpp | 56 ++++++++++++++++ src/gui/guiutility.h | 41 ++++++++++++ src/gui/owncloudgui.cpp | 16 ++++- src/gui/owncloudgui.h | 11 +++- src/gui/sharedialog.cpp | 4 +- src/gui/sharedialog.h | 3 +- src/gui/sharelinkwidget.cpp | 48 +++----------- src/gui/shareusergroupwidget.cpp | 40 ++++++++++- src/gui/shareusergroupwidget.h | 9 ++- src/gui/shareusergroupwidget.ui | 14 +++- src/gui/socketapi.cpp | 66 ++++++++++++++++--- src/gui/socketapi.h | 14 +++- src/libsync/account.cpp | 6 ++ src/libsync/account.h | 3 + src/libsync/propagatorjobs.cpp | 5 ++ src/libsync/syncjournalfilerecord.cpp | 11 ++++ src/libsync/syncjournalfilerecord.h | 8 +++ test/CMakeLists.txt | 1 + 25 files changed, 386 insertions(+), 103 deletions(-) delete mode 100644 src/gui/clipboard.mm create mode 100644 src/gui/guiutility.cpp create mode 100644 src/gui/guiutility.h diff --git a/shell_integration/dolphin/ownclouddolphinactionplugin.cpp b/shell_integration/dolphin/ownclouddolphinactionplugin.cpp index dfb2bdff77..c47cb783d5 100644 --- a/shell_integration/dolphin/ownclouddolphinactionplugin.cpp +++ b/shell_integration/dolphin/ownclouddolphinactionplugin.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include "ownclouddolphinpluginhelper.h" @@ -53,12 +54,31 @@ public: } )) return {}; - auto act = new QAction(parentWidget); - act->setText(helper->shareActionString()); - connect(act, &QAction::triggered, this, [localFile, helper] { + auto menuaction = new QAction(parentWidget); + menuaction->setText(helper->contextMenuTitle()); + auto menu = new QMenu(parentWidget); + menuaction->setMenu(menu); + + auto shareAction = menu->addAction(helper->shareActionTitle()); + connect(shareAction, &QAction::triggered, this, [localFile, helper] { helper->sendCommand(QByteArray("SHARE:"+localFile.toUtf8()+"\n")); } ); - return { act }; + + if (!helper->copyPrivateLinkTitle().isEmpty()) { + auto copyPrivateLinkAction = menu->addAction(helper->copyPrivateLinkTitle()); + connect(copyPrivateLinkAction, &QAction::triggered, this, [localFile, helper] { + helper->sendCommand(QByteArray("COPY_PRIVATE_LINK:" + localFile.toUtf8() + "\n")); + }); + } + + if (!helper->emailPrivateLinkTitle().isEmpty()) { + auto emailPrivateLinkAction = menu->addAction(helper->emailPrivateLinkTitle()); + connect(emailPrivateLinkAction, &QAction::triggered, this, [localFile, helper] { + helper->sendCommand(QByteArray("EMAIL_PRIVATE_LINK:" + localFile.toUtf8() + "\n")); + }); + } + + return { menuaction }; } }; diff --git a/shell_integration/dolphin/ownclouddolphinpluginhelper.cpp b/shell_integration/dolphin/ownclouddolphinpluginhelper.cpp index 68c2a9c297..ae1705220f 100644 --- a/shell_integration/dolphin/ownclouddolphinpluginhelper.cpp +++ b/shell_integration/dolphin/ownclouddolphinpluginhelper.cpp @@ -59,7 +59,7 @@ void OwncloudDolphinPluginHelper::sendCommand(const char* data) void OwncloudDolphinPluginHelper::slotConnected() { - sendCommand("SHARE_MENU_TITLE:\n"); + sendCommand("GET_STRINGS:\n"); } void OwncloudDolphinPluginHelper::tryConnect() @@ -92,9 +92,11 @@ void OwncloudDolphinPluginHelper::slotReadyRead() QString file = QString::fromUtf8(line.constData() + col + 1, line.size() - col - 1); _paths.append(file); continue; - } else if (line.startsWith("SHARE_MENU_TITLE:")) { - auto col = line.indexOf(':'); - _shareActionString = QString::fromUtf8(line.constData() + col + 1, line.size() - col - 1); + } else if (line.startsWith("STRING:")) { + auto args = QString::fromUtf8(line).split(QLatin1Char(':')); + if (args.size() >= 3) { + _strings[args[1]] = args.mid(2).join(QLatin1Char(':')); + } continue; } emit commandRecieved(line); diff --git a/shell_integration/dolphin/ownclouddolphinpluginhelper.h b/shell_integration/dolphin/ownclouddolphinpluginhelper.h index 26762caaf4..7d4e7ba14f 100644 --- a/shell_integration/dolphin/ownclouddolphinpluginhelper.h +++ b/shell_integration/dolphin/ownclouddolphinpluginhelper.h @@ -28,11 +28,22 @@ class OWNCLOUDDOLPHINPLUGINHELPER_EXPORT OwncloudDolphinPluginHelper : public QO public: static OwncloudDolphinPluginHelper *instance(); - QString shareActionString() const { return _shareActionString; } bool isConnected() const; void sendCommand(const char *data); QVector paths() const { return _paths; } + QString contextMenuTitle() const + { + return _strings.value("CONTEXT_MENU_TITLE", "ownCloud"); + } + QString shareActionTitle() const + { + return _strings.value("SHARE_MENU_TITLE", "Share..."); + } + + QString copyPrivateLinkTitle() const { return _strings["COPY_PRIVATE_LINK_TITLE"]; } + QString emailPrivateLinkTitle() const { return _strings["EMAIL_PRIVATE_LINK_TITLE"]; } + signals: void commandRecieved(const QByteArray &cmd); @@ -47,6 +58,7 @@ private: QLocalSocket _socket; QByteArray _line; QVector _paths; - QString _shareActionString; QBasicTimer _connectTimer; + + QMap _strings; }; diff --git a/shell_integration/nautilus/syncstate.py b/shell_integration/nautilus/syncstate.py index 7fb35b80c6..9a0f35ed74 100644 --- a/shell_integration/nautilus/syncstate.py +++ b/shell_integration/nautilus/syncstate.py @@ -95,6 +95,9 @@ class SocketConnect(GObject.GObject): print("Setting connected to %r." % self.connected ) self._watch_id = GObject.io_add_watch(self._sock, GObject.IO_IN, self._handle_notify) print("Socket watch id: " + str(self._watch_id)) + + self.sendCommand('GET_STRINGS:\n') + return False # Don't run again except Exception as e: print("Could not connect to unix socket. " + str(e)) @@ -153,6 +156,13 @@ class MenuExtension(GObject.GObject, Nautilus.MenuProvider): def __init__(self): GObject.GObject.__init__(self) + self.strings = {} + socketConnect.addListener(self.handle_commands) + + def handle_commands(self, action, args): + if action == 'STRING': + self.strings[args[0]] = ':'.join(args[1:]) + def check_registered_paths(self, filename): topLevelFolder = False internalFile = False @@ -178,7 +188,6 @@ class MenuExtension(GObject.GObject, Nautilus.MenuProvider): if len(files) != 1: return file = files[0] - items = [] filename = get_local_path(file.get_uri()) # Check if its a folder (ends with an /), if yes add a "/" @@ -190,12 +199,14 @@ class MenuExtension(GObject.GObject, Nautilus.MenuProvider): # Check if toplevel folder, we need to ignore those as they cannot be shared topLevelFolder, internalFile = self.check_registered_paths(filename) if topLevelFolder or not internalFile: - return items + return [] entry = socketConnect.nautilusVFSFile_table.get(filename) if not entry: - return items + return [] + # Currently 'sharable' also controls access to private link actions, + # and we definitely don't want to show them for IGNORED. shareable = False state = entry['state'] state_ok = state.startswith('OK') @@ -212,22 +223,42 @@ class MenuExtension(GObject.GObject, Nautilus.MenuProvider): break if not shareable: - return items + return [] - # Create a menu item - labelStr = "Share with " + appname + "..." - item = Nautilus.MenuItem(name='NautilusPython::ShareItem', label=labelStr, - tip='Share file {} through {}'.format(file.get_name(), appname) ) - item.connect("activate", self.menu_share, file) - items.append(item) + # Set up the 'ownCloud...' submenu + item_owncloud = Nautilus.MenuItem( + name='IntegrationMenu', label=self.strings.get('CONTEXT_MENU_TITLE', appname)) + menu = Nautilus.Menu() + item_owncloud.set_submenu(menu) - return items + # Add share menu option + item = Nautilus.MenuItem( + name='NautilusPython::ShareItem', + label=self.strings.get('SHARE_MENU_TITLE', 'Share...')) + item.connect("activate", self.context_menu_action, 'SHARE', file) + menu.append_item(item) + + # Add permalink menu options, but hide these options for older clients + # that don't have these actions. + if 'COPY_PRIVATE_LINK_TITLE' in self.strings: + item_copyprivatelink = Nautilus.MenuItem( + name='CopyPrivateLink', label=self.strings.get('COPY_PRIVATE_LINK_TITLE', 'Copy private link to clipboard')) + item_copyprivatelink.connect("activate", self.context_menu_action, 'COPY_PRIVATE_LINK', file) + menu.append_item(item_copyprivatelink) + + if 'EMAIL_PRIVATE_LINK_TITLE' in self.strings: + item_emailprivatelink = Nautilus.MenuItem( + name='EmailPrivateLink', label=self.strings.get('EMAIL_PRIVATE_LINK_TITLE', 'Send private link by email...')) + item_emailprivatelink.connect("activate", self.context_menu_action, 'EMAIL_PRIVATE_LINK', file) + menu.append_item(item_emailprivatelink) + + return [item_owncloud] - def menu_share(self, menu, file): + def context_menu_action(self, menu, action, file): filename = get_local_path(file.get_uri()) - print("Share file " + filename) - socketConnect.sendCommand("SHARE:" + filename + "\n") + print("Context menu: " + action + ' ' + filename) + socketConnect.sendCommand(action + ":" + filename + "\n") class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.InfoProvider): diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index e40c030346..d123d58c52 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -94,6 +94,7 @@ set(client_SRCS notificationwidget.cpp notificationconfirmjob.cpp servernotificationhandler.cpp + guiutility.cpp creds/credentialsfactory.cpp creds/httpcredentialsgui.cpp creds/oauth.cpp @@ -129,7 +130,6 @@ IF( APPLE ) list(APPEND client_SRCS settingsdialogmac.cpp) list(APPEND client_SRCS socketapisocket_mac.mm) list(APPEND client_SRCS systray.mm) - list(APPEND client_SRCS clipboard.mm) if(SPARKLE_FOUND) # Define this, we need to check in updater.cpp diff --git a/src/gui/application.cpp b/src/gui/application.cpp index 40bbfb1d7e..2cb11ae30b 100644 --- a/src/gui/application.cpp +++ b/src/gui/application.cpp @@ -206,8 +206,8 @@ Application::Application(int &argc, char **argv) slotAccountStateAdded(ai.data()); } - connect(FolderMan::instance()->socketApi(), SIGNAL(shareCommandReceived(QString, QString, bool)), - _gui, SLOT(slotShowShareDialog(QString, QString, bool))); + connect(FolderMan::instance()->socketApi(), SIGNAL(shareCommandReceived(QString, QString)), + _gui, SLOT(slotShowShareDialog(QString, QString))); // startup procedure. connect(&_checkConnectionTimer, SIGNAL(timeout()), this, SLOT(slotCheckConnection())); diff --git a/src/gui/clipboard.mm b/src/gui/clipboard.mm deleted file mode 100644 index 48ea813877..0000000000 --- a/src/gui/clipboard.mm +++ /dev/null @@ -1,14 +0,0 @@ -#include -#import - -namespace OCC { - -// https://github.com/owncloud/client/issues/3300 -void copyToPasteboard(const QString &string) -{ - [[NSPasteboard generalPasteboard] clearContents]; - [[NSPasteboard generalPasteboard] setString:[NSString stringWithUTF8String:string.toUtf8().data()] - forType:NSStringPboardType]; -} - -} diff --git a/src/gui/guiutility.cpp b/src/gui/guiutility.cpp new file mode 100644 index 0000000000..27fe54897b --- /dev/null +++ b/src/gui/guiutility.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) by Christian Kamm + * + * 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 "guiutility.h" + +#include +#include +#include +#include + +using namespace OCC; + +bool Utility::openBrowser(const QUrl &url, QWidget *errorWidgetParent) +{ + if (!QDesktopServices::openUrl(url) && errorWidgetParent) { + QMessageBox::warning( + errorWidgetParent, + QCoreApplication::translate("utility", "Could not open browser"), + QCoreApplication::translate("utility", + "There was an error when launching the browser to go to " + "URL %1. Maybe no default browser is configured?") + .arg(url.toString())); + return false; + } + return true; +} + +bool Utility::openEmailComposer(const QString &subject, const QString &body, QWidget *errorWidgetParent) +{ + QUrl url(QLatin1String("mailto: ")); + url.setQueryItems({ { QLatin1String("subject"), subject }, + { QLatin1String("body"), body } }); + + if (!QDesktopServices::openUrl(url) && errorWidgetParent) { + QMessageBox::warning( + errorWidgetParent, + QCoreApplication::translate("utility", "Could not open email client"), + QCoreApplication::translate("utility", + "There was an error when launching the email client to " + "create a new message. Maybe no default email client is " + "configured?")); + return false; + } + return true; +} diff --git a/src/gui/guiutility.h b/src/gui/guiutility.h new file mode 100644 index 0000000000..55f808ac24 --- /dev/null +++ b/src/gui/guiutility.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) by Christian Kamm + * + * 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 GUIUTILITY_H +#define GUIUTILITY_H + +#include +#include +#include + +namespace OCC { +namespace Utility { + + /** Open an url in the browser. + * + * If launching the browser fails, display a message. + */ + bool openBrowser(const QUrl &url, QWidget *errorWidgetParent); + + /** Start composing a new email message. + * + * If launching the email program fails, display a message. + */ + bool openEmailComposer(const QString &subject, const QString &body, + QWidget *errorWidgetParent); + +} // namespace Utility +} // namespace OCC + +#endif diff --git a/src/gui/owncloudgui.cpp b/src/gui/owncloudgui.cpp index 684689dae7..db005f4227 100644 --- a/src/gui/owncloudgui.cpp +++ b/src/gui/owncloudgui.cpp @@ -33,6 +33,7 @@ #include "accountstate.h" #include "openfilemanager.h" #include "accountmanager.h" +#include "syncjournalfilerecord.h" #include "creds/abstractcredentials.h" #include @@ -1039,7 +1040,7 @@ void ownCloudGui::raiseDialog(QWidget *raiseWidget) } -void ownCloudGui::slotShowShareDialog(const QString &sharePath, const QString &localPath, bool resharingAllowed) +void ownCloudGui::slotShowShareDialog(const QString &sharePath, const QString &localPath) { const auto folder = FolderMan::instance()->folderForPath(localPath); if (!folder) { @@ -1052,6 +1053,17 @@ void ownCloudGui::slotShowShareDialog(const QString &sharePath, const QString &l const auto accountState = folder->accountState(); + const QString file = localPath.mid(folder->cleanPath().length() + 1); + SyncJournalFileRecord fileRecord = folder->journalDb()->getFileRecord(file); + + bool resharingAllowed = true; // lets assume the good + if (fileRecord.isValid()) { + // check the permission: Is resharing allowed? + if (!fileRecord._remotePerm.contains('R')) { + resharingAllowed = false; + } + } + // As a first approximation, set the set of permissions that can be granted // either to everything (resharing allowed) or nothing (no resharing). // @@ -1072,7 +1084,7 @@ void ownCloudGui::slotShowShareDialog(const QString &sharePath, const QString &l w = _shareDialogs[localPath]; } else { qCInfo(lcApplication) << "Opening share dialog" << sharePath << localPath << maxSharingPermissions; - w = new ShareDialog(accountState, sharePath, localPath, maxSharingPermissions); + w = new ShareDialog(accountState, sharePath, localPath, maxSharingPermissions, fileRecord.numericFileId()); w->setAttribute(Qt::WA_DeleteOnClose, true); _shareDialogs[localPath] = w; diff --git a/src/gui/owncloudgui.h b/src/gui/owncloudgui.h index c0238a7617..c24fa173a8 100644 --- a/src/gui/owncloudgui.h +++ b/src/gui/owncloudgui.h @@ -86,7 +86,16 @@ public slots: void slotOpenPath(const QString &path); void slotAccountStateChanged(); void slotTrayMessageIfServerUnsupported(Account *account); - void slotShowShareDialog(const QString &sharePath, const QString &localPath, bool resharingAllowed); + + /** + * Open a share dialog for a file or folder. + * + * sharePath is the full remote path to the item, + * localPath is the absolute local path to it (so not relative + * to the folder). + */ + void slotShowShareDialog(const QString &sharePath, const QString &localPath); + void slotRemoveDestroyedShareDialogs(); private slots: diff --git a/src/gui/sharedialog.cpp b/src/gui/sharedialog.cpp index 17399d1a41..24a912fdc2 100644 --- a/src/gui/sharedialog.cpp +++ b/src/gui/sharedialog.cpp @@ -38,6 +38,7 @@ ShareDialog::ShareDialog(QPointer accountState, const QString &sharePath, const QString &localPath, SharePermissions maxSharingPermissions, + const QByteArray &numericFileId, QWidget *parent) : QDialog(parent) , _ui(new Ui::ShareDialog) @@ -45,6 +46,7 @@ ShareDialog::ShareDialog(QPointer accountState, , _sharePath(sharePath) , _localPath(localPath) , _maxSharingPermissions(maxSharingPermissions) + , _numericFileId(numericFileId) , _linkWidget(NULL) , _userGroupWidget(NULL) , _progressIndicator(NULL) @@ -192,7 +194,7 @@ void ShareDialog::showSharingUi() && _accountState->account()->serverVersionInt() >= Account::makeServerVersion(8, 2, 0); if (userGroupSharing) { - _userGroupWidget = new ShareUserGroupWidget(_accountState->account(), _sharePath, _localPath, _maxSharingPermissions, this); + _userGroupWidget = new ShareUserGroupWidget(_accountState->account(), _sharePath, _localPath, _maxSharingPermissions, _numericFileId, this); _ui->shareWidgets->addTab(_userGroupWidget, tr("Users and Groups")); _userGroupWidget->getShares(); } diff --git a/src/gui/sharedialog.h b/src/gui/sharedialog.h index dac7b9841d..50aeea2e17 100644 --- a/src/gui/sharedialog.h +++ b/src/gui/sharedialog.h @@ -43,6 +43,7 @@ public: const QString &sharePath, const QString &localPath, SharePermissions maxSharingPermissions, + const QByteArray &numericFileId, QWidget *parent = 0); ~ShareDialog(); @@ -60,8 +61,8 @@ private: QPointer _accountState; QString _sharePath; QString _localPath; - SharePermissions _maxSharingPermissions; + QByteArray _numericFileId; ShareLinkWidget *_linkWidget; ShareUserGroupWidget *_userGroupWidget; diff --git a/src/gui/sharelinkwidget.cpp b/src/gui/sharelinkwidget.cpp index b2f6d7c7d6..f3b8e33da5 100644 --- a/src/gui/sharelinkwidget.cpp +++ b/src/gui/sharelinkwidget.cpp @@ -19,6 +19,7 @@ #include "capabilities.h" #include "sharemanager.h" +#include "guiutility.h" #include "QProgressIndicator.h" #include @@ -494,51 +495,18 @@ void ShareLinkWidget::slotCheckBoxExpireClicked() } } -#ifdef Q_OS_MAC -extern void copyToPasteboard(const QString &string); -#endif - -void ShareLinkWidget::copyShareLink(const QUrl &url) -{ -#ifdef Q_OS_MAC - copyToPasteboard(url.toString()); -#else - QClipboard *clipboard = QApplication::clipboard(); - clipboard->setText(url.toString()); -#endif -} - void ShareLinkWidget::emailShareLink(const QUrl &url) { QString fileName = _sharePath.mid(_sharePath.lastIndexOf('/') + 1); - - if (!QDesktopServices::openUrl(QUrl(QString( - "mailto: " - "?subject=I shared %1 with you" - "&body=%2") - .arg( - fileName, - url.toString()), - QUrl::TolerantMode))) { - QMessageBox::warning( - this, - tr("Could not open email client"), - tr("There was an error when launching the email client to " - "create a new message. Maybe no default email client is " - "configured?")); - } + Utility::openEmailComposer( + QString("I shared %1 with you").arg(fileName), + url.toString(), + this); } void ShareLinkWidget::openShareLink(const QUrl &url) { - if (!QDesktopServices::openUrl(url)) { - QMessageBox::warning( - this, - tr("Could not open browser"), - tr("There was an error when launching the browser to " - "view the public link share. Maybe no default browser is " - "configured?")); - } + Utility::openBrowser(url, this); } void ShareLinkWidget::slotShareLinkButtonTriggered(QAction *action) @@ -546,9 +514,9 @@ void ShareLinkWidget::slotShareLinkButtonTriggered(QAction *action) auto share = sender()->property(propertyShareC).value>(); if (action == _copyLinkAction) { - copyShareLink(share->getLink()); + QApplication::clipboard()->setText(share->getLink().toString()); } else if (action == _copyDirectLinkAction) { - copyShareLink(share->getDirectDownloadLink()); + QApplication::clipboard()->setText(share->getDirectDownloadLink().toString()); } else if (action == _emailLinkAction) { emailShareLink(share->getLink()); } else if (action == _emailDirectLinkAction) { diff --git a/src/gui/shareusergroupwidget.cpp b/src/gui/shareusergroupwidget.cpp index 1351f888f3..8cecac2a42 100644 --- a/src/gui/shareusergroupwidget.cpp +++ b/src/gui/shareusergroupwidget.cpp @@ -22,7 +22,7 @@ #include "theme.h" #include "configfile.h" #include "capabilities.h" - +#include "guiutility.h" #include "thumbnailjob.h" #include "sharee.h" #include "sharemanager.h" @@ -39,6 +39,8 @@ #include #include #include +#include +#include namespace OCC { @@ -46,6 +48,7 @@ ShareUserGroupWidget::ShareUserGroupWidget(AccountPtr account, const QString &sharePath, const QString &localPath, SharePermissions maxSharingPermissions, + const QByteArray &numericFileId, QWidget *parent) : QWidget(parent) , _ui(new Ui::ShareUserGroupWidget) @@ -53,6 +56,7 @@ ShareUserGroupWidget::ShareUserGroupWidget(AccountPtr account, , _sharePath(sharePath) , _localPath(localPath) , _maxSharingPermissions(maxSharingPermissions) + , _numericFileId(numericFileId) , _disableCompleterActivated(false) { setAttribute(Qt::WA_DeleteOnClose); @@ -80,6 +84,7 @@ ShareUserGroupWidget::ShareUserGroupWidget(AccountPtr account, connect(_manager, SIGNAL(shareCreated(QSharedPointer)), SLOT(getShares())); connect(_manager, SIGNAL(serverError(int, QString)), this, SLOT(displayError(int, QString))); connect(_ui->shareeLineEdit, SIGNAL(returnPressed()), SLOT(slotLineEditReturn())); + connect(_ui->privateLinkText, SIGNAL(linkActivated(QString)), SLOT(slotPrivateLinkShare())); // By making the next two QueuedConnections we can override // the strings the completer sets on the line edit. @@ -222,6 +227,21 @@ void ShareUserGroupWidget::slotAdjustScrollWidgetSize() } } +void ShareUserGroupWidget::slotPrivateLinkShare() +{ + auto menu = new QMenu(this); + menu->setAttribute(Qt::WA_DeleteOnClose); + + menu->addAction(tr("Open link in browser"), + this, SLOT(slotPrivateLinkOpenBrowser())); + menu->addAction(tr("Copy link to clipboard"), + this, SLOT(slotPrivateLinkCopy())); + menu->addAction(tr("Send link by email"), + this, SLOT(slotPrivateLinkEmail())); + + menu->exec(QCursor::pos()); +} + void ShareUserGroupWidget::slotShareesReady() { _pi_sharee.stopAnimation(); @@ -301,6 +321,24 @@ void ShareUserGroupWidget::displayError(int code, const QString &message) _ui->shareeLineEdit->setEnabled(true); } +void ShareUserGroupWidget::slotPrivateLinkOpenBrowser() +{ + Utility::openBrowser(_account->filePermalinkUrl(_numericFileId), this); +} + +void ShareUserGroupWidget::slotPrivateLinkCopy() +{ + QApplication::clipboard()->setText(_account->filePermalinkUrl(_numericFileId).toString()); +} + +void ShareUserGroupWidget::slotPrivateLinkEmail() +{ + Utility::openEmailComposer( + tr("I shared something with you"), + _account->filePermalinkUrl(_numericFileId).toString(), + this); +} + ShareUserLine::ShareUserLine(QSharedPointer share, SharePermissions maxSharingPermissions, bool isFile, diff --git a/src/gui/shareusergroupwidget.h b/src/gui/shareusergroupwidget.h index 36639196da..fbc2bdfa4d 100644 --- a/src/gui/shareusergroupwidget.h +++ b/src/gui/shareusergroupwidget.h @@ -57,6 +57,7 @@ public: const QString &sharePath, const QString &localPath, SharePermissions maxSharingPermissions, + const QByteArray &numericFileId, QWidget *parent = 0); ~ShareUserGroupWidget(); @@ -75,19 +76,25 @@ private slots: void slotCompleterHighlighted(const QModelIndex &index); void slotShareesReady(); void slotAdjustScrollWidgetSize(); + void slotPrivateLinkShare(); void displayError(int code, const QString &message); + void slotPrivateLinkOpenBrowser(); + void slotPrivateLinkCopy(); + void slotPrivateLinkEmail(); + private: Ui::ShareUserGroupWidget *_ui; AccountPtr _account; QString _sharePath; QString _localPath; + SharePermissions _maxSharingPermissions; + QByteArray _numericFileId; QCompleter *_completer; ShareeModel *_completerModel; QTimer _completionTimer; - SharePermissions _maxSharingPermissions; bool _isFile; bool _disableCompleterActivated; // in order to avoid that we share the contents twice ShareManager *_manager; diff --git a/src/gui/shareusergroupwidget.ui b/src/gui/shareusergroupwidget.ui index 615c5b2f19..028d897ce2 100644 --- a/src/gui/shareusergroupwidget.ui +++ b/src/gui/shareusergroupwidget.ui @@ -94,14 +94,24 @@ 0 0 - 395 - 221 + 377 + 169 + + + + <html><head/><body><p>You can direct people to this shared file or folder <a href="private link menu"><span style=" text-decoration: underline; color:#0000ff;">by giving them a private link</span></a>.</p></body></html> + + + true + + + diff --git a/src/gui/socketapi.cpp b/src/gui/socketapi.cpp index 82705c59ae..bf4e75d3b0 100644 --- a/src/gui/socketapi.cpp +++ b/src/gui/socketapi.cpp @@ -32,7 +32,9 @@ #include "account.h" #include "capabilities.h" #include "asserts.h" +#include "guiutility.h" +#include #include #include #include @@ -45,6 +47,8 @@ #include #include +#include + #include @@ -436,19 +440,10 @@ void SocketApi::command_SHARE(const QString &localFile, SocketListener *listener return; } - SyncJournalFileRecord rec = shareFolder->journalDb()->getFileRecord(localFileClean); - - bool allowReshare = true; // lets assume the good - if (rec.isValid()) { - // check the permission: Is resharing allowed? - if (!rec._remotePerm.contains('R')) { - allowReshare = false; - } - } const QString message = QLatin1String("SHARE:OK:") + QDir::toNativeSeparators(localFile); listener->sendMessage(message); - emit shareCommandReceived(remotePath, localFileClean, allowReshare); + emit shareCommandReceived(remotePath, localFileClean); } } @@ -514,6 +509,39 @@ void SocketApi::command_SHARE_MENU_TITLE(const QString &, SocketListener *listen listener->sendMessage(QLatin1String("SHARE_MENU_TITLE:") + tr("Share with %1", "parameter is ownCloud").arg(Theme::instance()->appNameGUI())); } +void SocketApi::command_COPY_PRIVATE_LINK(const QString &localFile, SocketListener *) +{ + auto url = getPrivateLinkUrl(localFile); + if (!url.isEmpty()) { + QApplication::clipboard()->setText(url.toString()); + } +} + +void SocketApi::command_EMAIL_PRIVATE_LINK(const QString &localFile, SocketListener *) +{ + auto url = getPrivateLinkUrl(localFile); + if (!url.isEmpty()) { + Utility::openEmailComposer( + tr("I shared something with you"), + url.toString(), + 0); + } +} + +void SocketApi::command_GET_STRINGS(const QString &, SocketListener *listener) +{ + static std::array, 5> strings { { + { "SHARE_MENU_TITLE", tr("Share with %1...", "parameter is ownCloud").arg(Theme::instance()->appNameGUI()) }, + { "APPNAME", Theme::instance()->appNameGUI() }, + { "CONTEXT_MENU_TITLE", Theme::instance()->appNameGUI() }, + { "COPY_PRIVATE_LINK_TITLE", tr("Copy private link to clipboard") }, + { "EMAIL_PRIVATE_LINK_TITLE", tr("Send private link by email...") }, + } }; + for (auto key_value : strings) { + listener->sendMessage(QString("STRING:%1:%2").arg(key_value.first, key_value.second)); + } +} + QString SocketApi::buildRegisterPathMessage(const QString &path) { QFileInfo fi(path); @@ -522,4 +550,22 @@ QString SocketApi::buildRegisterPathMessage(const QString &path) return message; } +QUrl SocketApi::getPrivateLinkUrl(const QString &localFile) const +{ + Folder *shareFolder = FolderMan::instance()->folderForPath(localFile); + if (!shareFolder) { + qCWarning(lcSocketApi) << "Unknown path" << localFile; + return QUrl(); + } + + const QString localFileClean = QDir::cleanPath(localFile); + const QString file = localFileClean.mid(shareFolder->cleanPath().length() + 1); + + SyncJournalFileRecord rec = shareFolder->journalDb()->getFileRecord(file); + if (rec.isValid()) { + return shareFolder->accountState()->account()->filePermalinkUrl(rec.numericFileId()); + } + return QUrl(); +} + } // namespace OCC diff --git a/src/gui/socketapi.h b/src/gui/socketapi.h index e0b4c3ada3..d4f6e7bf86 100644 --- a/src/gui/socketapi.h +++ b/src/gui/socketapi.h @@ -55,8 +55,7 @@ public slots: void slotRegisterPath(const QString &alias); signals: - void shareCommandReceived(const QString &sharePath, const QString &localPath, bool resharingAllowed); - void shareUserGroupCommandReceived(const QString &sharePath, const QString &localPath, bool resharingAllowed); + void shareCommandReceived(const QString &sharePath, const QString &localPath); private slots: void slotNewConnection(); @@ -70,13 +69,22 @@ private: Q_INVOKABLE void command_RETRIEVE_FOLDER_STATUS(const QString &argument, SocketListener *listener); Q_INVOKABLE void command_RETRIEVE_FILE_STATUS(const QString &argument, SocketListener *listener); - Q_INVOKABLE void command_SHARE(const QString &localFile, SocketListener *listener); Q_INVOKABLE void command_VERSION(const QString &argument, SocketListener *listener); Q_INVOKABLE void command_SHARE_STATUS(const QString &localFile, SocketListener *listener); Q_INVOKABLE void command_SHARE_MENU_TITLE(const QString &argument, SocketListener *listener); + + // The context menu actions + Q_INVOKABLE void command_SHARE(const QString &localFile, SocketListener *listener); + Q_INVOKABLE void command_COPY_PRIVATE_LINK(const QString &localFile, SocketListener *listener); + Q_INVOKABLE void command_EMAIL_PRIVATE_LINK(const QString &localFile, SocketListener *listener); + + /** Sends translated/branded strings that may be useful to the integration */ + Q_INVOKABLE void command_GET_STRINGS(const QString &argument, SocketListener *listener); + QString buildRegisterPathMessage(const QString &path); + QUrl getPrivateLinkUrl(const QString &localFile) const; QSet _registeredAliases; QList _listeners; diff --git a/src/libsync/account.cpp b/src/libsync/account.cpp index 19ace5ed0a..ee83e3deec 100644 --- a/src/libsync/account.cpp +++ b/src/libsync/account.cpp @@ -160,6 +160,12 @@ QUrl Account::davUrl() const return Utility::concatUrlPath(url(), davPath()); } +QUrl Account::filePermalinkUrl(const QByteArray &numericFileId) const +{ + return Utility::concatUrlPath(url(), + QLatin1String("/index.php/f/") + QUrl::toPercentEncoding(QString::fromLatin1(numericFileId))); +} + /** * clear all cookies. (Session cookies or not) */ diff --git a/src/libsync/account.h b/src/libsync/account.h index 66e0d1be61..d48b27b15c 100644 --- a/src/libsync/account.h +++ b/src/libsync/account.h @@ -107,6 +107,9 @@ public: /** Returns webdav entry URL, based on url() */ QUrl davUrl() const; + /** Returns a permalink url for a file */ + QUrl filePermalinkUrl(const QByteArray &numericFileId) const; + /** Holds the accounts credentials */ AbstractCredentials *credentials() const; void setCredentials(AbstractCredentials *cred); diff --git a/src/libsync/propagatorjobs.cpp b/src/libsync/propagatorjobs.cpp index 5cf17b26ac..65d7ef86ad 100644 --- a/src/libsync/propagatorjobs.cpp +++ b/src/libsync/propagatorjobs.cpp @@ -42,6 +42,11 @@ Q_LOGGING_CATEGORY(lcPropagateLocalRemove, "sync.propagator.localremove", QtInfo Q_LOGGING_CATEGORY(lcPropagateLocalMkdir, "sync.propagator.localmkdir", QtInfoMsg) Q_LOGGING_CATEGORY(lcPropagateLocalRename, "sync.propagator.localrename", QtInfoMsg) +QByteArray localFileIdFromFullId(const QByteArray &id) +{ + return id.left(8); +} + /** * Code inspired from Qt5's QDir::removeRecursively * The code will update the database in case of error. diff --git a/src/libsync/syncjournalfilerecord.cpp b/src/libsync/syncjournalfilerecord.cpp index b2c65931d4..96bde3eb6d 100644 --- a/src/libsync/syncjournalfilerecord.cpp +++ b/src/libsync/syncjournalfilerecord.cpp @@ -109,6 +109,17 @@ SyncFileItem SyncJournalFileRecord::toSyncFileItem() return item; } +QByteArray SyncJournalFileRecord::numericFileId() const +{ + // Use the id up until the first non-numeric character + for (int i = 0; i < _fileId.size(); ++i) { + if (_fileId[i] < '0' || _fileId[i] > '9') { + return _fileId.left(i); + } + } + return _fileId; +} + bool SyncJournalErrorBlacklistRecord::isValid() const { return !_file.isEmpty() diff --git a/src/libsync/syncjournalfilerecord.h b/src/libsync/syncjournalfilerecord.h index c7579683b9..8f33331803 100644 --- a/src/libsync/syncjournalfilerecord.h +++ b/src/libsync/syncjournalfilerecord.h @@ -48,6 +48,14 @@ public: return !_path.isEmpty(); } + /** Returns the numeric part of the full id in _fileId. + * + * On the server this is sometimes known as the internal file id. + * + * It is used in the construction of private links. + */ + QByteArray numericFileId() const; + QString _path; quint64 _inode; QDateTime _modtime; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ac0f1dca1e..860f140e9b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -55,6 +55,7 @@ list(APPEND FolderMan_SRC ../src/gui/socketapi.cpp ) list(APPEND FolderMan_SRC ../src/gui/accountstate.cpp ) list(APPEND FolderMan_SRC ../src/gui/syncrunfilelog.cpp ) list(APPEND FolderMan_SRC ../src/gui/lockwatcher.cpp ) +list(APPEND FolderMan_SRC ../src/gui/guiutility.cpp ) list(APPEND FolderMan_SRC ${FolderWatcher_SRC}) list(APPEND FolderMan_SRC stub.cpp ) owncloud_add_test(FolderMan "${FolderMan_SRC}") From 9d818066a774543e752bc323be9fcadfd509b8a3 Mon Sep 17 00:00:00 2001 From: Jocelyn Turcotte Date: Tue, 4 Jul 2017 16:41:40 +0200 Subject: [PATCH 035/107] SocketApi: Improve logging --- src/gui/guiutility.cpp | 41 +++++++++++++++++++++++++---------------- src/gui/socketapi.cpp | 9 ++------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/gui/guiutility.cpp b/src/gui/guiutility.cpp index 27fe54897b..f4b31a7af2 100644 --- a/src/gui/guiutility.cpp +++ b/src/gui/guiutility.cpp @@ -17,20 +17,26 @@ #include #include #include +#include #include using namespace OCC; +Q_LOGGING_CATEGORY(lcUtility, "gui.utility", QtInfoMsg) + bool Utility::openBrowser(const QUrl &url, QWidget *errorWidgetParent) { - if (!QDesktopServices::openUrl(url) && errorWidgetParent) { - QMessageBox::warning( - errorWidgetParent, - QCoreApplication::translate("utility", "Could not open browser"), - QCoreApplication::translate("utility", - "There was an error when launching the browser to go to " - "URL %1. Maybe no default browser is configured?") - .arg(url.toString())); + if (!QDesktopServices::openUrl(url)) { + if (errorWidgetParent) { + QMessageBox::warning( + errorWidgetParent, + QCoreApplication::translate("utility", "Could not open browser"), + QCoreApplication::translate("utility", + "There was an error when launching the browser to go to " + "URL %1. Maybe no default browser is configured?") + .arg(url.toString())); + } + qCWarning(lcUtility) << "QDesktopServices::openUrl failed for" << url; return false; } return true; @@ -42,14 +48,17 @@ bool Utility::openEmailComposer(const QString &subject, const QString &body, QWi url.setQueryItems({ { QLatin1String("subject"), subject }, { QLatin1String("body"), body } }); - if (!QDesktopServices::openUrl(url) && errorWidgetParent) { - QMessageBox::warning( - errorWidgetParent, - QCoreApplication::translate("utility", "Could not open email client"), - QCoreApplication::translate("utility", - "There was an error when launching the email client to " - "create a new message. Maybe no default email client is " - "configured?")); + if (!QDesktopServices::openUrl(url)) { + if (errorWidgetParent) { + QMessageBox::warning( + errorWidgetParent, + QCoreApplication::translate("utility", "Could not open email client"), + QCoreApplication::translate("utility", + "There was an error when launching the email client to " + "create a new message. Maybe no default email client is " + "configured?")); + } + qCWarning(lcUtility) << "QDesktopServices::openUrl failed for" << url; return false; } return true; diff --git a/src/gui/socketapi.cpp b/src/gui/socketapi.cpp index bf4e75d3b0..ccfe5167eb 100644 --- a/src/gui/socketapi.cpp +++ b/src/gui/socketapi.cpp @@ -128,7 +128,7 @@ public: void sendMessage(const QString &message, bool doWait = false) const { - qCInfo(lcSocketApi) << "Sending SocketAPI message: " << message << "to" << socket; + qCInfo(lcSocketApi) << "Sending SocketAPI message -->" << message << "to" << socket; QString localMessage = message; if (!localMessage.endsWith(QLatin1Char('\n'))) { localMessage.append(QLatin1Char('\n')); @@ -282,6 +282,7 @@ void SocketApi::slotReadSocket() // make sure that the path will match, especially on OS X. QString line = QString::fromUtf8(socket->readLine()).normalized(QString::NormalizationForm_C); line.chop(1); // remove the '\n' + qCInfo(lcSocketApi) << "Received SocketAPI message <--" << line << "from" << socket; QByteArray command = line.split(":").value(0).toAscii(); QByteArray functionWithArguments = "command_" + command + "(QString,SocketListener*)"; int indexOfMethod = staticMetaObject.indexOfMethod(functionWithArguments); @@ -373,8 +374,6 @@ void SocketApi::command_RETRIEVE_FOLDER_STATUS(const QString &argument, SocketLi void SocketApi::command_RETRIEVE_FILE_STATUS(const QString &argument, SocketListener *listener) { - qCDebug(lcSocketApi) << argument; - QString statusString; Folder *syncFolder = FolderMan::instance()->folderForPath(argument); @@ -403,8 +402,6 @@ void SocketApi::command_RETRIEVE_FILE_STATUS(const QString &argument, SocketList void SocketApi::command_SHARE(const QString &localFile, SocketListener *listener) { - qCDebug(lcSocketApi) << localFile; - auto theme = Theme::instance(); Folder *shareFolder = FolderMan::instance()->folderForPath(localFile); @@ -454,8 +451,6 @@ void SocketApi::command_VERSION(const QString &, SocketListener *listener) void SocketApi::command_SHARE_STATUS(const QString &localFile, SocketListener *listener) { - qCDebug(lcSocketApi) << localFile; - Folder *shareFolder = FolderMan::instance()->folderForPath(localFile); if (!shareFolder) { From 7b58a8284090d8d46b79b22eeb02f40669c8bdf9 Mon Sep 17 00:00:00 2001 From: Jocelyn Turcotte Date: Tue, 4 Jul 2017 16:43:17 +0200 Subject: [PATCH 036/107] SocketApi: Adjust the format of the mailto: URL Minor tweaks that weren't actually an issue, but just in case. --- src/gui/guiutility.cpp | 2 +- src/gui/socketapi.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/guiutility.cpp b/src/gui/guiutility.cpp index f4b31a7af2..0a3ee6f0f4 100644 --- a/src/gui/guiutility.cpp +++ b/src/gui/guiutility.cpp @@ -44,7 +44,7 @@ bool Utility::openBrowser(const QUrl &url, QWidget *errorWidgetParent) bool Utility::openEmailComposer(const QString &subject, const QString &body, QWidget *errorWidgetParent) { - QUrl url(QLatin1String("mailto: ")); + QUrl url(QLatin1String("mailto:")); url.setQueryItems({ { QLatin1String("subject"), subject }, { QLatin1String("body"), body } }); diff --git a/src/gui/socketapi.cpp b/src/gui/socketapi.cpp index ccfe5167eb..b43d77a4aa 100644 --- a/src/gui/socketapi.cpp +++ b/src/gui/socketapi.cpp @@ -518,7 +518,7 @@ void SocketApi::command_EMAIL_PRIVATE_LINK(const QString &localFile, SocketListe if (!url.isEmpty()) { Utility::openEmailComposer( tr("I shared something with you"), - url.toString(), + url.toString(QUrl::FullyEncoded), 0); } } From 389499d639d0cda1afb93a7e62463d487631f054 Mon Sep 17 00:00:00 2001 From: Jocelyn Turcotte Date: Tue, 4 Jul 2017 16:45:48 +0200 Subject: [PATCH 037/107] SocketApi: Add GET_STRINGS:BEGIN and END The Windows shell extension relied on the response of SHARE_MENU_TITLE to advance its state machine, but in order to use the new GET_STRINGS instead, we need to know when the last string was received. Also add BEGIN for consistency. --- src/gui/socketapi.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/socketapi.cpp b/src/gui/socketapi.cpp index b43d77a4aa..6fc4ea405c 100644 --- a/src/gui/socketapi.cpp +++ b/src/gui/socketapi.cpp @@ -532,9 +532,11 @@ void SocketApi::command_GET_STRINGS(const QString &, SocketListener *listener) { "COPY_PRIVATE_LINK_TITLE", tr("Copy private link to clipboard") }, { "EMAIL_PRIVATE_LINK_TITLE", tr("Send private link by email...") }, } }; + listener->sendMessage(QString("GET_STRINGS:BEGIN")); for (auto key_value : strings) { listener->sendMessage(QString("STRING:%1:%2").arg(key_value.first, key_value.second)); } + listener->sendMessage(QString("GET_STRINGS:END")); } QString SocketApi::buildRegisterPathMessage(const QString &path) From 7a4daf799aa8f256f2f5c9fd492993fc51de93fe Mon Sep 17 00:00:00 2001 From: Jocelyn Turcotte Date: Tue, 4 Jul 2017 16:58:55 +0200 Subject: [PATCH 038/107] shell/windows: Use a submenu to include private link actions Refactor things a bit to be able to reuse some code and clean things up. --- .../OCContextMenu/OCClientInterface.cpp | 39 ++++- .../windows/OCContextMenu/OCClientInterface.h | 11 +- .../windows/OCContextMenu/OCContextMenu.cpp | 159 +++++++++++------- .../windows/OCContextMenu/OCContextMenu.h | 6 +- .../windows/OCUtil/RemotePathChecker.cpp | 11 +- shell_integration/windows/OCUtil/StringUtil.h | 17 ++ 6 files changed, 168 insertions(+), 75 deletions(-) diff --git a/shell_integration/windows/OCContextMenu/OCClientInterface.cpp b/shell_integration/windows/OCContextMenu/OCClientInterface.cpp index 05b23ec09a..56facf6006 100644 --- a/shell_integration/windows/OCContextMenu/OCClientInterface.cpp +++ b/shell_integration/windows/OCContextMenu/OCClientInterface.cpp @@ -45,7 +45,7 @@ OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo() if (!socket.Connect(pipename)) { return {}; } - socket.SendMsg(L"SHARE_MENU_TITLE\n"); + socket.SendMsg(L"GET_STRINGS\n"); ContextMenuInfo info; std::wstring response; @@ -56,9 +56,21 @@ OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo() wstring responsePath = response.substr(14); // length of REGISTER_PATH info.watchedDirectories.push_back(responsePath); } - else if (StringUtil::begins_with(response, wstring(L"SHARE_MENU_TITLE:"))) { - info.shareMenuTitle = response.substr(17); // length of SHARE_MENU_TITLE: - break; // Stop once we received the last sent request + else if (StringUtil::begins_with(response, wstring(L"STRING:"))) { + wstring stringName, stringValue; + if (!StringUtil::extractChunks(response, stringName, stringValue)) + continue; + if (stringName == L"SHARE_MENU_TITLE") + info.shareMenuTitle = move(stringValue); + else if (stringName == L"CONTEXT_MENU_TITLE") + info.contextMenuTitle = move(stringValue); + else if (stringName == L"COPY_PRIVATE_LINK_TITLE") + info.copyLinkMenuTitle = move(stringValue); + else if (stringName == L"EMAIL_PRIVATE_LINK_TITLE") + info.emailLinkMenuTitle = move(stringValue); + } + else if (StringUtil::begins_with(response, wstring(L"GET_STRINGS:END"))) { + break; // Stop once we completely received the last sent request } } else { @@ -69,7 +81,22 @@ OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo() return info; } -void OCClientInterface::ShareObject(const std::wstring &path) +void OCClientInterface::RequestShare(const std::wstring &path) +{ + SendRequest(L"SHARE", path); +} + +void OCClientInterface::RequestCopyLink(const std::wstring &path) +{ + SendRequest(L"COPY_PRIVATE_LINK", path); +} + +void OCClientInterface::RequestEmailLink(const std::wstring &path) +{ + SendRequest(L"EMAIL_PRIVATE_LINK", path); +} + +void OCClientInterface::SendRequest(wchar_t *verb, const std::wstring &path) { auto pipename = CommunicationSocket::DefaultPipePath(); @@ -82,7 +109,7 @@ void OCClientInterface::ShareObject(const std::wstring &path) } wchar_t msg[SOCK_BUFFER] = { 0 }; - if (SUCCEEDED(StringCchPrintf(msg, SOCK_BUFFER, L"SHARE:%s\n", path.c_str()))) + if (SUCCEEDED(StringCchPrintf(msg, SOCK_BUFFER, L"%s:%s\n", verb, path.c_str()))) { socket.SendMsg(msg); } diff --git a/shell_integration/windows/OCContextMenu/OCClientInterface.h b/shell_integration/windows/OCContextMenu/OCClientInterface.h index 00d4470eaf..74e6364fec 100644 --- a/shell_integration/windows/OCContextMenu/OCClientInterface.h +++ b/shell_integration/windows/OCContextMenu/OCClientInterface.h @@ -45,10 +45,19 @@ class OCClientInterface public: struct ContextMenuInfo { std::vector watchedDirectories; + std::wstring contextMenuTitle; std::wstring shareMenuTitle; + std::wstring copyLinkMenuTitle; + std::wstring emailLinkMenuTitle; }; static ContextMenuInfo FetchInfo(); - static void ShareObject(const std::wstring &path); + + static void RequestShare(const std::wstring &path); + static void RequestCopyLink(const std::wstring &path); + static void RequestEmailLink(const std::wstring &path); + +private: + static void SendRequest(wchar_t *verb, const std::wstring &path); }; #endif //ABSTRACTSOCKETHANDLER_H diff --git a/shell_integration/windows/OCContextMenu/OCContextMenu.cpp b/shell_integration/windows/OCContextMenu/OCContextMenu.cpp index a5558fc0a3..bda86b86d8 100644 --- a/shell_integration/windows/OCContextMenu/OCContextMenu.cpp +++ b/shell_integration/windows/OCContextMenu/OCContextMenu.cpp @@ -25,19 +25,12 @@ extern HINSTANCE g_hInst; extern long g_cDllRef; -#define IDM_SHARE 0 - - +#define IDM_SHARE 0 +#define IDM_COPYLINK 1 +#define IDM_EMAILLINK 2 OCContextMenu::OCContextMenu(void) : m_cRef(1) - , m_pszMenuText(L"&Share") - , m_pszVerb("ocshare") - , m_pwszVerb(L"ocshare") - , m_pszVerbCanonicalName("OCShareViaOC") - , m_pwszVerbCanonicalName(L"OCShareViaOC") - , m_pszVerbHelpText("Share via ownCloud") - , m_pwszVerbHelpText(L"Share via ownCloud") { InterlockedIncrement(&g_cDllRef); } @@ -48,9 +41,19 @@ OCContextMenu::~OCContextMenu(void) } -void OCContextMenu::OnVerbDisplayFileName(HWND hWnd) +void OCContextMenu::OnVerbShare(HWND hWnd) { - OCClientInterface::ShareObject(std::wstring(m_szSelectedFile)); + OCClientInterface::RequestShare(std::wstring(m_szSelectedFile)); +} + +void OCContextMenu::OnVerbCopyLink(HWND hWnd) +{ + OCClientInterface::RequestCopyLink(std::wstring(m_szSelectedFile)); +} + +void OCContextMenu::OnVerbEmailLink(HWND hWnd) +{ + OCClientInterface::RequestEmailLink(std::wstring(m_szSelectedFile)); } @@ -164,29 +167,61 @@ IFACEMETHODIMP OCContextMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0)); } - InsertSeperator(hMenu, indexMenu); - indexMenu++; + InsertSeperator(hMenu, indexMenu++); - assert(!info.shareMenuTitle.empty()); - MENUITEMINFO mii = { sizeof(mii) }; - mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_STATE; - mii.wID = idCmdFirst + IDM_SHARE; - mii.fType = MFT_STRING; - mii.dwTypeData = &info.shareMenuTitle[0]; - mii.fState = MFS_ENABLED; - if (!InsertMenuItem(hMenu, indexMenu, TRUE, &mii)) + HMENU hSubmenu = CreateMenu(); { - return HRESULT_FROM_WIN32(GetLastError()); - } + MENUITEMINFO mii = { sizeof(mii) }; + mii.fMask = MIIM_SUBMENU | MIIM_FTYPE | MIIM_STRING; + mii.hSubMenu = hSubmenu; + mii.fType = MFT_STRING; + mii.dwTypeData = &info.contextMenuTitle[0]; - indexMenu++; - InsertSeperator(hMenu, indexMenu); + if (!InsertMenuItem(hMenu, indexMenu++, TRUE, &mii)) + return HRESULT_FROM_WIN32(GetLastError()); + } + InsertSeperator(hMenu, indexMenu++); + + UINT indexSubMenu = 0; + { + assert(!info.shareMenuTitle.empty()); + MENUITEMINFO mii = { sizeof(mii) }; + mii.fMask = MIIM_ID | MIIM_FTYPE | MIIM_STRING; + mii.wID = idCmdFirst + IDM_SHARE; + mii.fType = MFT_STRING; + mii.dwTypeData = &info.shareMenuTitle[0]; + + if (!InsertMenuItem(hSubmenu, indexSubMenu++, TRUE, &mii)) + return HRESULT_FROM_WIN32(GetLastError()); + } + { + assert(!info.copyLinkMenuTitle.empty()); + MENUITEMINFO mii = { sizeof(mii) }; + mii.fMask = MIIM_ID | MIIM_FTYPE | MIIM_STRING; + mii.wID = idCmdFirst + IDM_COPYLINK; + mii.fType = MFT_STRING; + mii.dwTypeData = &info.copyLinkMenuTitle[0]; + + if (!InsertMenuItem(hSubmenu, indexSubMenu++, TRUE, &mii)) + return HRESULT_FROM_WIN32(GetLastError()); + } + { + assert(!info.emailLinkMenuTitle.empty()); + MENUITEMINFO mii = { sizeof(mii) }; + mii.fMask = MIIM_ID | MIIM_FTYPE | MIIM_STRING; + mii.wID = idCmdFirst + IDM_EMAILLINK; + mii.fType = MFT_STRING; + mii.dwTypeData = &info.emailLinkMenuTitle[0]; + + if (!InsertMenuItem(hSubmenu, indexSubMenu++, TRUE, &mii)) + return HRESULT_FROM_WIN32(GetLastError()); + } // Return an HRESULT value with the severity set to SEVERITY_SUCCESS. // Set the code value to the offset of the largest command identifier // that was assigned, plus one (1). - return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_SHARE + 1)); + return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_EMAILLINK + 1)); } IFACEMETHODIMP OCContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici) @@ -197,12 +232,16 @@ IFACEMETHODIMP OCContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici) if (HIWORD(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW)) { // Is the verb supported by this context menu extension? - if (StrCmpIW(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW, m_pwszVerb) == 0) - { - OnVerbDisplayFileName(pici->hwnd); + if (StrCmpIW(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW, L"ocshare") == 0) { + OnVerbShare(pici->hwnd); } - else - { + else if (StrCmpIW(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW, L"occopylink") == 0) { + OnVerbCopyLink(pici->hwnd); + } + else if (StrCmpIW(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW, L"ocemaillink") == 0) { + OnVerbEmailLink(pici->hwnd); + } + else { // If the verb is not recognized by the context menu handler, it // must return E_FAIL to allow it to be passed on to the other // context menu handlers that might implement that verb. @@ -216,12 +255,16 @@ IFACEMETHODIMP OCContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici) { // Is the command identifier offset supported by this context menu // extension? - if (LOWORD(pici->lpVerb) == IDM_SHARE) - { - OnVerbDisplayFileName(pici->hwnd); + if (LOWORD(pici->lpVerb) == IDM_SHARE) { + OnVerbShare(pici->hwnd); } - else - { + else if (LOWORD(pici->lpVerb) == IDM_COPYLINK) { + OnVerbCopyLink(pici->hwnd); + } + else if (LOWORD(pici->lpVerb) == IDM_EMAILLINK) { + OnVerbEmailLink(pici->hwnd); + } + else { // If the verb is not recognized by the context menu handler, it // must return E_FAIL to allow it to be passed on to the other // context menu handlers that might implement that verb. @@ -237,31 +280,33 @@ IFACEMETHODIMP OCContextMenu::GetCommandString(UINT_PTR idCommand, { HRESULT hr = E_INVALIDARG; - if (idCommand == IDM_SHARE) - { - switch (uFlags) - { - case GCS_HELPTEXTW: - // Only useful for pre-Vista versions of Windows that have a - // Status bar. - hr = StringCchCopy(reinterpret_cast(pszName), cchMax, - m_pwszVerbHelpText); - break; - - case GCS_VERBW: - // GCS_VERBW is an optional feature that enables a caller to - // discover the canonical name for the verb passed in through + switch (idCommand) { + case IDM_SHARE: + if (uFlags == GCS_VERBW) { + // GCS_VERBW is an optional feature that enables a caller to + // discover the canonical name for the verb passed in through // idCommand. hr = StringCchCopy(reinterpret_cast(pszName), cchMax, - m_pwszVerbCanonicalName); - break; - - default: - hr = S_OK; + L"OCShareViaOC"); } + break; + case IDM_COPYLINK: + if (uFlags == GCS_VERBW) { + hr = StringCchCopy(reinterpret_cast(pszName), cchMax, + L"OCCopyLink"); + } + break; + case IDM_EMAILLINK: + if (uFlags == GCS_VERBW) { + hr = StringCchCopy(reinterpret_cast(pszName), cchMax, + L"OCEmailLink"); + } + break; + default: + break; } - // If the command (idCommand) is not supported by this context menu + // If the idCommand or uFlags is not supported by this context menu // extension handler, return E_INVALIDARG. return hr; diff --git a/shell_integration/windows/OCContextMenu/OCContextMenu.h b/shell_integration/windows/OCContextMenu/OCContextMenu.h index 49834fce05..7fe3e2adea 100644 --- a/shell_integration/windows/OCContextMenu/OCContextMenu.h +++ b/shell_integration/windows/OCContextMenu/OCContextMenu.h @@ -46,8 +46,10 @@ private: // The name of the selected file. wchar_t m_szSelectedFile[MAX_PATH]; - // The method that handles the "display" verb. - void OnVerbDisplayFileName(HWND hWnd); + // The method that handles the "ocshare" verb. + void OnVerbShare(HWND hWnd); + void OnVerbCopyLink(HWND hWnd); + void OnVerbEmailLink(HWND hWnd); PWSTR m_pszMenuText; PCSTR m_pszVerb; diff --git a/shell_integration/windows/OCUtil/RemotePathChecker.cpp b/shell_integration/windows/OCUtil/RemotePathChecker.cpp index 4a13c1cd56..37caee1824 100644 --- a/shell_integration/windows/OCUtil/RemotePathChecker.cpp +++ b/shell_integration/windows/OCUtil/RemotePathChecker.cpp @@ -111,17 +111,10 @@ void RemotePathChecker::workerThreadLoop() } else if (StringUtil::begins_with(response, wstring(L"STATUS:")) || StringUtil::begins_with(response, wstring(L"BROADCAST:"))) { - auto statusBegin = response.find(L':', 0); - assert(statusBegin != std::wstring::npos); - - auto statusEnd = response.find(L':', statusBegin + 1); - if (statusEnd == std::wstring::npos) { - // the command do not contains two colon? + wstring responseStatus, responsePath; + if (!StringUtil::extractChunks(response, responseStatus, responsePath)) continue; - } - auto responseStatus = response.substr(statusBegin+1, statusEnd - statusBegin-1); - auto responsePath = response.substr(statusEnd+1); auto state = _StrToFileState(responseStatus); bool wasAsked = asked.erase(responsePath) > 0; diff --git a/shell_integration/windows/OCUtil/StringUtil.h b/shell_integration/windows/OCUtil/StringUtil.h index 0f52d51dd6..8ec325aa94 100644 --- a/shell_integration/windows/OCUtil/StringUtil.h +++ b/shell_integration/windows/OCUtil/StringUtil.h @@ -17,6 +17,7 @@ #pragma once #include +#include class __declspec(dllexport) StringUtil { public: @@ -44,6 +45,22 @@ public: return (childLength == parentLength || childLength > parentLength && (child[parentLength] == L'\\' || child[parentLength - 1] == L'\\')) && wcsncmp(child, parent, parentLength) == 0; } + + static bool extractChunks(const std::wstring &source, std::wstring &secondChunk, std::wstring &thirdChunk) { + auto statusBegin = source.find(L':', 0); + assert(statusBegin != std::wstring::npos); + + auto statusEnd = source.find(L':', statusBegin + 1); + if (statusEnd == std::wstring::npos) { + // the command do not contains two colon? + return false; + } + + // Assume the caller extracted the chunk before the first colon. + secondChunk = source.substr(statusBegin + 1, statusEnd - statusBegin - 1); + thirdChunk = source.substr(statusEnd + 1); + return true; + } }; #endif // STRINGUTIL_H From b6db9789ee2919b21d9080e99e7563cbc7821046 Mon Sep 17 00:00:00 2001 From: Jocelyn Turcotte Date: Tue, 4 Jul 2017 11:34:51 -0400 Subject: [PATCH 039/107] shell/macos: Use a submenu to include private link actions --- .../FinderSyncExt/FinderSync.h | 1 + .../FinderSyncExt/FinderSync.m | 51 +++++++++++++++---- .../MacOSX/common/SyncClientProxy.h | 2 +- .../MacOSX/common/SyncClientProxy.m | 8 +-- 4 files changed, 47 insertions(+), 15 deletions(-) diff --git a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.h b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.h index a1356045b7..67a4360945 100644 --- a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.h +++ b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.h @@ -22,6 +22,7 @@ SyncClientProxy *_syncClientProxy; NSMutableSet *_registeredDirectories; NSString *_shareMenuTitle; + NSMutableDictionary *_strings; } @end diff --git a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.m b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.m index 065be2e2f5..0591824bb3 100644 --- a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.m +++ b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.m @@ -59,7 +59,7 @@ _syncClientProxy = [[SyncClientProxy alloc] initWithDelegate:self serverName:serverName]; _registeredDirectories = [[NSMutableSet alloc] init]; - _shareMenuTitle = nil; + _strings = [[NSMutableDictionary alloc] init]; [_syncClientProxy start]; return self; @@ -101,11 +101,21 @@ } }]; - if (_shareMenuTitle && !onlyRootsSelected) { - NSMenu *menu = [[NSMenu alloc] initWithTitle:@""]; - NSMenuItem *item = [menu addItemWithTitle:_shareMenuTitle action:@selector(shareMenuAction:) keyEquivalent:@"title"]; - item.image = [[NSBundle mainBundle] imageForResource:@"app.icns"]; - + id contextMenuTitle = [_strings objectForKey:@"CONTEXT_MENU_TITLE"]; + id shareTitle = [_strings objectForKey:@"SHARE_MENU_TITLE"]; + id copyLinkTitle = [_strings objectForKey:@"COPY_PRIVATE_LINK_TITLE"]; + id emailLinkTitle = [_strings objectForKey:@"EMAIL_PRIVATE_LINK_TITLE"]; + if (contextMenuTitle && !onlyRootsSelected) { + NSMenu *menu = [[NSMenu alloc] initWithTitle:@""]; + NSMenu *subMenu = [[NSMenu alloc] initWithTitle:@""]; + NSMenuItem *subMenuItem = [menu addItemWithTitle:contextMenuTitle action:nil keyEquivalent:@""]; + subMenuItem.submenu = subMenu; + subMenuItem.image = [[NSBundle mainBundle] imageForResource:@"app.icns"]; + + [subMenu addItemWithTitle:shareTitle action:@selector(shareMenuAction:) keyEquivalent:@""]; + [subMenu addItemWithTitle:copyLinkTitle action:@selector(copyLinkMenuAction:) keyEquivalent:@""]; + [subMenu addItemWithTitle:emailLinkTitle action:@selector(emailLinkMenuAction:) keyEquivalent:@""]; + return menu; } return nil; @@ -114,13 +124,33 @@ - (IBAction)shareMenuAction:(id)sender { NSArray* items = [[FIFinderSyncController defaultController] selectedItemURLs]; - + [items enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) { NSString* normalizedPath = [[obj path] decomposedStringWithCanonicalMapping]; [_syncClientProxy askOnSocket:normalizedPath query:@"SHARE"]; }]; } +- (IBAction)copyLinkMenuAction:(id)sender +{ + NSArray* items = [[FIFinderSyncController defaultController] selectedItemURLs]; + + [items enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) { + NSString* normalizedPath = [[obj path] decomposedStringWithCanonicalMapping]; + [_syncClientProxy askOnSocket:normalizedPath query:@"COPY_PRIVATE_LINK"]; + }]; +} + +- (IBAction)emailLinkMenuAction:(id)sender +{ + NSArray* items = [[FIFinderSyncController defaultController] selectedItemURLs]; + + [items enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) { + NSString* normalizedPath = [[obj path] decomposedStringWithCanonicalMapping]; + [_syncClientProxy askOnSocket:normalizedPath query:@"EMAIL_PRIVATE_LINK"]; + }]; +} + #pragma mark - SyncClientProxyDelegate implementation - (void)setResultForPath:(NSString*)path result:(NSString*)result @@ -146,15 +176,14 @@ [FIFinderSyncController defaultController].directoryURLs = _registeredDirectories; } -- (void)setShareMenuTitle:(NSString*)title +- (void)setString:(NSString*)key value:(NSString*)value { - _shareMenuTitle = title; + [_strings setObject:value forKey:key]; } - (void)connectionDidDie { - _shareMenuTitle = nil; - + [_strings removeAllObjects]; [_registeredDirectories removeAllObjects]; // For some reason the FIFinderSync cache doesn't seem to be cleared for the root item when // we reset the directoryURLs (seen on macOS 10.12 at least). diff --git a/shell_integration/MacOSX/common/SyncClientProxy.h b/shell_integration/MacOSX/common/SyncClientProxy.h index aaaa294b62..8f96334164 100644 --- a/shell_integration/MacOSX/common/SyncClientProxy.h +++ b/shell_integration/MacOSX/common/SyncClientProxy.h @@ -20,7 +20,7 @@ - (void)reFetchFileNameCacheForPath:(NSString*)path; - (void)registerPath:(NSString*)path; - (void)unregisterPath:(NSString*)path; -- (void)setShareMenuTitle:(NSString*)title; +- (void)setString:(NSString*)key value:(NSString*)value; - (void)connectionDidDie; @end diff --git a/shell_integration/MacOSX/common/SyncClientProxy.m b/shell_integration/MacOSX/common/SyncClientProxy.m index 1e89ea6764..b7b64571f7 100644 --- a/shell_integration/MacOSX/common/SyncClientProxy.m +++ b/shell_integration/MacOSX/common/SyncClientProxy.m @@ -73,7 +73,7 @@ [_remoteEnd setProtocolForProxy:@protocol(ChannelProtocol)]; // Everything is set up, start querying - [self askOnSocket:@"" query:@"SHARE_MENU_TITLE"]; + [self askOnSocket:@"" query:@"GET_STRINGS"]; } - (void)scheduleRetry @@ -119,8 +119,10 @@ } else if( [[chunks objectAtIndex:0 ] isEqualToString:@"UNREGISTER_PATH"] ) { NSString *path = [chunks objectAtIndex:1]; [_delegate unregisterPath:path]; - } else if( [[chunks objectAtIndex:0 ] isEqualToString:@"SHARE_MENU_TITLE"] ) { - [_delegate setShareMenuTitle:[chunks objectAtIndex:1]]; + } else if( [[chunks objectAtIndex:0 ] isEqualToString:@"GET_STRINGS"] ) { + // BEGIN and END messages, do nothing. + } else if( [[chunks objectAtIndex:0 ] isEqualToString:@"STRING"] ) { + [_delegate setString:[chunks objectAtIndex:1] value:[chunks objectAtIndex:2]]; } else { NSLog(@"SyncState: Unknown command %@", [chunks objectAtIndex:0]); } From 3b20684dc791d1c8c6034e95a0e4b65d722bcfc0 Mon Sep 17 00:00:00 2001 From: Jocelyn Turcotte Date: Tue, 4 Jul 2017 13:26:57 -0400 Subject: [PATCH 040/107] shell/macos: Remove legacy (< 10.10) overlay icons Having to update the plugins to add a submenu in the context menu, it's more cost-effective to remove support for legacy macOS versions than implementing and testing this setup. --- admin/osx/macosx.pkgproj | 585 ---------- admin/osx/pre_install.sh.cmake | 3 - shell_integration/MacOSX/CMakeLists.txt | 14 - .../contents.xcworkspacedata | 6 - .../MacOSX/OwnCloudFinder/ContentManager.h | 47 - .../MacOSX/OwnCloudFinder/ContentManager.m | 358 ------ .../OwnCloudFinder/ContextMenuHandlers.h | 28 - .../OwnCloudFinder/ContextMenuHandlers.m | 101 -- .../OwnCloudFinder/ContextMenuHandlers.m.unc | 74 -- .../English.lproj/InfoPlist.strings | 2 - .../MacOSX/OwnCloudFinder/Finder/Finder.h | 1016 ----------------- .../MacOSX/OwnCloudFinder/FinderHook.h | 22 - .../MacOSX/OwnCloudFinder/FinderHook.m | 125 -- .../MacOSX/OwnCloudFinder/FinishedIconCache.h | 24 - .../MacOSX/OwnCloudFinder/FinishedIconCache.m | 91 -- .../MacOSX/OwnCloudFinder/IconCache.h | 27 - .../MacOSX/OwnCloudFinder/IconCache.m | 72 -- .../OwnCloudFinder/IconOverlayHandlers.h | 23 - .../OwnCloudFinder/IconOverlayHandlers.m | 176 --- .../MacOSX/OwnCloudFinder/Info.plist | 48 - .../MacOSX/OwnCloudFinder/MenuManager.h | 29 - .../MacOSX/OwnCloudFinder/MenuManager.m | 188 --- .../OwnCloudFinder.xcodeproj/project.pbxproj | 369 ------ .../contents.xcworkspacedata | 7 - .../xcschemes/xcschememanagement.plist | 14 - .../MacOSX/OwnCloudFinder/RequestManager.h | 40 - .../MacOSX/OwnCloudFinder/RequestManager.m | 159 --- .../FinderSyncExt}/SyncClientProxy.h | 0 .../FinderSyncExt}/SyncClientProxy.m | 0 .../project.pbxproj | 21 +- .../English.lproj/InfoPlist.strings | 2 - .../MacOSX/OwnCloudInjector/Info.plist | 59 - .../LNStandardVersionComparator.h | 35 - .../LNStandardVersionComparator.m | 158 --- .../LNVersionComparisonProtocol.h | 27 - .../OwnCloudInjector/OwnCloudInjector.m | 270 ----- .../OwnCloudInjector/OwnCloudInjector.sdef | 9 - .../project.pbxproj | 269 ----- .../contents.xcworkspacedata | 7 - .../xcschemes/SyncStateFinder.osax.xcscheme | 73 -- .../xcschemes/xcschememanagement.plist | 14 - .../MacOSX/OwnCloudInjector/license.txt | 25 - shell_integration/MacOSX/check.scpt | 9 - shell_integration/MacOSX/deploy.sh | 24 - shell_integration/MacOSX/load.scpt | 6 - shell_integration/MacOSX/loadPlugin.sh | 8 - shell_integration/MacOSX/unload.scpt | 6 - src/gui/owncloudgui.cpp | 42 - 48 files changed, 6 insertions(+), 4706 deletions(-) delete mode 100644 shell_integration/MacOSX/OwnCloudFinder/ContentManager.h delete mode 100644 shell_integration/MacOSX/OwnCloudFinder/ContentManager.m delete mode 100644 shell_integration/MacOSX/OwnCloudFinder/ContextMenuHandlers.h delete mode 100644 shell_integration/MacOSX/OwnCloudFinder/ContextMenuHandlers.m delete mode 100644 shell_integration/MacOSX/OwnCloudFinder/ContextMenuHandlers.m.unc delete mode 100644 shell_integration/MacOSX/OwnCloudFinder/English.lproj/InfoPlist.strings delete mode 100644 shell_integration/MacOSX/OwnCloudFinder/Finder/Finder.h delete mode 100644 shell_integration/MacOSX/OwnCloudFinder/FinderHook.h delete mode 100644 shell_integration/MacOSX/OwnCloudFinder/FinderHook.m delete mode 100644 shell_integration/MacOSX/OwnCloudFinder/FinishedIconCache.h delete mode 100644 shell_integration/MacOSX/OwnCloudFinder/FinishedIconCache.m delete mode 100644 shell_integration/MacOSX/OwnCloudFinder/IconCache.h delete mode 100644 shell_integration/MacOSX/OwnCloudFinder/IconCache.m delete mode 100644 shell_integration/MacOSX/OwnCloudFinder/IconOverlayHandlers.h delete mode 100644 shell_integration/MacOSX/OwnCloudFinder/IconOverlayHandlers.m delete mode 100644 shell_integration/MacOSX/OwnCloudFinder/Info.plist delete mode 100644 shell_integration/MacOSX/OwnCloudFinder/MenuManager.h delete mode 100644 shell_integration/MacOSX/OwnCloudFinder/MenuManager.m delete mode 100644 shell_integration/MacOSX/OwnCloudFinder/OwnCloudFinder.xcodeproj/project.pbxproj delete mode 100644 shell_integration/MacOSX/OwnCloudFinder/OwnCloudFinder.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 100644 shell_integration/MacOSX/OwnCloudFinder/OwnCloudFinder.xcodeproj/xcuserdata/guruz.xcuserdatad/xcschemes/xcschememanagement.plist delete mode 100644 shell_integration/MacOSX/OwnCloudFinder/RequestManager.h delete mode 100644 shell_integration/MacOSX/OwnCloudFinder/RequestManager.m rename shell_integration/MacOSX/{common => OwnCloudFinderSync/FinderSyncExt}/SyncClientProxy.h (100%) rename shell_integration/MacOSX/{common => OwnCloudFinderSync/FinderSyncExt}/SyncClientProxy.m (100%) delete mode 100644 shell_integration/MacOSX/OwnCloudInjector/English.lproj/InfoPlist.strings delete mode 100644 shell_integration/MacOSX/OwnCloudInjector/Info.plist delete mode 100644 shell_integration/MacOSX/OwnCloudInjector/LNStandardVersionComparator.h delete mode 100644 shell_integration/MacOSX/OwnCloudInjector/LNStandardVersionComparator.m delete mode 100644 shell_integration/MacOSX/OwnCloudInjector/LNVersionComparisonProtocol.h delete mode 100644 shell_integration/MacOSX/OwnCloudInjector/OwnCloudInjector.m delete mode 100644 shell_integration/MacOSX/OwnCloudInjector/OwnCloudInjector.sdef delete mode 100644 shell_integration/MacOSX/OwnCloudInjector/OwnCloudInjector.xcodeproj/project.pbxproj delete mode 100644 shell_integration/MacOSX/OwnCloudInjector/OwnCloudInjector.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 100644 shell_integration/MacOSX/OwnCloudInjector/OwnCloudInjector.xcodeproj/xcshareddata/xcschemes/SyncStateFinder.osax.xcscheme delete mode 100644 shell_integration/MacOSX/OwnCloudInjector/OwnCloudInjector.xcodeproj/xcuserdata/guruz.xcuserdatad/xcschemes/xcschememanagement.plist delete mode 100644 shell_integration/MacOSX/OwnCloudInjector/license.txt delete mode 100644 shell_integration/MacOSX/check.scpt delete mode 100644 shell_integration/MacOSX/deploy.sh delete mode 100644 shell_integration/MacOSX/load.scpt delete mode 100755 shell_integration/MacOSX/loadPlugin.sh delete mode 100644 shell_integration/MacOSX/unload.scpt diff --git a/admin/osx/macosx.pkgproj b/admin/osx/macosx.pkgproj index 9b9b891bc8..927ca92dfb 100644 --- a/admin/osx/macosx.pkgproj +++ b/admin/osx/macosx.pkgproj @@ -506,536 +506,6 @@ UUID 7D7219B7-1897-48C3-8533-842BDEC46F71 - - PACKAGE_FILES - - DEFAULT_INSTALL_LOCATION - / - HIERARCHY - - CHILDREN - - - CHILDREN - - - CHILDREN - - GID - 80 - PATH - Utilities - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - GID - 80 - PATH - Applications - PATH_TYPE - 0 - PERMISSIONS - 509 - TYPE - 1 - UID - 0 - - - CHILDREN - - - CHILDREN - - GID - 80 - PATH - Application Support - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Documentation - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Filesystems - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Frameworks - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Input Methods - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Internet Plug-Ins - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - LaunchAgents - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - LaunchDaemons - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - PreferencePanes - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Preferences - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 80 - PATH - Printers - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - PrivilegedHelperTools - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - QuickLook - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - QuickTime - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Screen Savers - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - - CHILDREN - - - CHILDREN - - GID - 0 - PATH - Library/ScriptingAdditions/SyncStateFinder.osax/Contents - PATH_TYPE - 3 - PERMISSIONS - 493 - TYPE - 3 - UID - 0 - - - GID - 0 - PATH - SyncStateFinder.osax - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 2 - UID - 0 - - - GID - 0 - PATH - ScriptingAdditions - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 2 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Scripts - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Services - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - GID - 0 - PATH - Widgets - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - GID - 0 - PATH - Library - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - - CHILDREN - - - CHILDREN - - GID - 0 - PATH - Extensions - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - GID - 0 - PATH - Library - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - GID - 0 - PATH - System - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - CHILDREN - - - CHILDREN - - GID - 0 - PATH - Shared - PATH_TYPE - 0 - PERMISSIONS - 1023 - TYPE - 1 - UID - 0 - - - GID - 80 - PATH - Users - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - - GID - 0 - PATH - / - PATH_TYPE - 0 - PERMISSIONS - 493 - TYPE - 1 - UID - 0 - - PAYLOAD_TYPE - 0 - VERSION - 2 - - PACKAGE_SCRIPTS - - POSTINSTALL_PATH - - PREINSTALL_PATH - - RESOURCES - - - PACKAGE_SETTINGS - - AUTHENTICATION - 1 - CONCLUSION_ACTION - 0 - IDENTIFIER - com.ownCloud.finderPlugin - LOCATION - 0 - NAME - Legacy Finder Plugin (OS X 10.9 or older) - OVERWRITE_PERMISSIONS - - VERSION - @MIRALL_VERSION_FULL@ - - TYPE - 0 - UUID - 39F61FCD-6EAA-4F3A-81C6-25E3F667DFB5 - PROJECT @@ -1103,49 +573,6 @@ UUID 9647ADC0-BD53-4D7D-A561-73D383AACDE1 - - DESCRIPTION - - OPTIONS - - HIDDEN - - STATE - 1 - - PACKAGE_UUID - 39F61FCD-6EAA-4F3A-81C6-25E3F667DFB5 - REQUIREMENTS - - - BEHAVIOR - 1 - DICTIONARY - - IC_REQUIREMENT_JAVASCRIPT_FUNCTION - olderOsx - IC_REQUIREMENT_JAVASCRIPT_PARAMETERS - - - IDENTIFIER - fr.whitebox.Packages.requirement.javascript - MESSAGE - - NAME - JavaScript - STATE - - - - TITLE - - TOOLTIP - - TYPE - 0 - UUID - 1D2C47E0-5FD3-4623-B934-1347C66782D0 - REMOVED @@ -1450,18 +877,6 @@ @CMAKE_INSTALL_DIR@ - SHARED_GLOBAL_DATA - - IC_REQUIREMENT_JAVASCRIPT_SHARED_SOURCE_CODE - - function olderOsx() { - if(system.compareVersions(system.version.ProductVersion, '10.10') == -1) { - return true; - } - return false; - } - - TYPE 0 VERSION diff --git a/admin/osx/pre_install.sh.cmake b/admin/osx/pre_install.sh.cmake index 094a283dee..cd909e29ad 100644 --- a/admin/osx/pre_install.sh.cmake +++ b/admin/osx/pre_install.sh.cmake @@ -3,7 +3,4 @@ # kill the old version. see issue #2044 killall @APPLICATION_EXECUTABLE@ -# Unload the Finder plugin. see issue #2105 -killall Finder - exit 0 diff --git a/shell_integration/MacOSX/CMakeLists.txt b/shell_integration/MacOSX/CMakeLists.txt index 2e594bfcef..06174af7c4 100644 --- a/shell_integration/MacOSX/CMakeLists.txt +++ b/shell_integration/MacOSX/CMakeLists.txt @@ -1,12 +1,4 @@ - if(APPLE) -add_custom_target( legacy_mac_overlayplugin ALL - xcodebuild -workspace ${CMAKE_SOURCE_DIR}/shell_integration/MacOSX/OwnCloud.xcworkspace - -scheme SyncStateFinder.osax -configuration Release SYMROOT=${CMAKE_CURRENT_BINARY_DIR} - OC_APPLICATION_REV_DOMAIN=${APPLICATION_REV_DOMAIN} - OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX=${SOCKETAPI_TEAM_IDENTIFIER_PREFIX} - COMMENT building Legacy Mac Overlay icons) - # Contrary to popular belief, this is called like this no matter what theme/OEM. set(OC_OEM_SHARE_ICNS "${CMAKE_BINARY_DIR}/src/gui/ownCloud.icns") @@ -25,12 +17,6 @@ add_custom_target( mac_overlayplugin ALL VERBATIM) add_dependencies(mac_overlayplugin ${APPLICATION_EXECUTABLE}) # for the ownCloud.icns to be generated - -# legacy -INSTALL( DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Release/SyncStateFinder.osax/Contents - DESTINATION ${CMAKE_INSTALL_PREFIX}/Library/ScriptingAdditions/SyncStateFinder.osax/ ) - -# >= 10.10.x INSTALL(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Release/FinderSyncExt.appex DESTINATION ${OWNCLOUD_OSX_BUNDLE}/Contents/PlugIns USE_SOURCE_PERMISSIONS) diff --git a/shell_integration/MacOSX/OwnCloud.xcworkspace/contents.xcworkspacedata b/shell_integration/MacOSX/OwnCloud.xcworkspace/contents.xcworkspacedata index 26fba3447d..ed88ebf479 100644 --- a/shell_integration/MacOSX/OwnCloud.xcworkspace/contents.xcworkspacedata +++ b/shell_integration/MacOSX/OwnCloud.xcworkspace/contents.xcworkspacedata @@ -4,10 +4,4 @@ - - - - diff --git a/shell_integration/MacOSX/OwnCloudFinder/ContentManager.h b/shell_integration/MacOSX/OwnCloudFinder/ContentManager.h deleted file mode 100644 index 82d3b52c48..0000000000 --- a/shell_integration/MacOSX/OwnCloudFinder/ContentManager.h +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - */ - -#import - -@interface OwnCloudFinderContentManager : NSObject -{ - NSMutableDictionary* _fileNamesCache; - NSMutableDictionary* _oldFileNamesCache; - BOOL _fileIconsEnabled; - BOOL _hasChangedContent; - - NSNumber *_icnOk; - NSNumber *_icnSync; - NSNumber *_icnWarn; - NSNumber *_icnErr; - NSNumber *_icnOkSwm; - NSNumber *_icnSyncSwm; - NSNumber *_icnWarnSwm; - NSNumber *_icnErrSwm; -} - -+ (OwnCloudFinderContentManager*)sharedInstance; - -- (void)enableFileIcons:(BOOL)enable; -- (NSNumber*)iconByPath:(NSString*)path isDirectory:(BOOL)isDir; -- (void)removeAllIcons; -- (void)setIcons:(NSDictionary*)iconDictionary filterByFolder:(NSString*)filterFolder; -- (void)setResultForPath:(NSString*)path result:(NSString*)result; -- (void)clearFileNameCache; -- (void)reFetchFileNameCacheForPath:(NSString*)path; -- (void)repaintAllWindows; - -- (void)loadIconResources; - -@end \ No newline at end of file diff --git a/shell_integration/MacOSX/OwnCloudFinder/ContentManager.m b/shell_integration/MacOSX/OwnCloudFinder/ContentManager.m deleted file mode 100644 index 7fe564e59c..0000000000 --- a/shell_integration/MacOSX/OwnCloudFinder/ContentManager.m +++ /dev/null @@ -1,358 +0,0 @@ -/** - * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - */ - -#import -#import -#import -#import "ContentManager.h" -#import "MenuManager.h" -#import "RequestManager.h" -#import "IconCache.h" - -static OwnCloudFinderContentManager* sharedInstance = nil; - -@implementation OwnCloudFinderContentManager -- init -{ - self = [super init]; - - if (self) - { - _fileNamesCache = [[NSMutableDictionary alloc] init]; - _oldFileNamesCache = [[NSMutableDictionary alloc] init]; - _fileIconsEnabled = TRUE; - _hasChangedContent = TRUE; - [self loadIconResources]; - } - - return self; -} - -- (void)dealloc -{ - [self removeAllIcons]; - [_fileNamesCache release]; - [_oldFileNamesCache release]; - sharedInstance = nil; - - [super dealloc]; -} - -+ (OwnCloudFinderContentManager*)sharedInstance -{ - @synchronized(self) - { - if (sharedInstance == nil) - { - sharedInstance = [[self alloc] init]; - } - } - - return sharedInstance; -} - -- (void)loadIconResources -{ - NSBundle *extBundle = [NSBundle bundleForClass:[self class]]; - - _icnOk = [[IconCache sharedInstance] registerIcon:[extBundle imageForResource:@"ok.icns"]]; - _icnSync = [[IconCache sharedInstance] registerIcon:[extBundle imageForResource:@"sync.icns"]]; - _icnWarn = [[IconCache sharedInstance] registerIcon:[extBundle imageForResource:@"warning.icns"]]; - _icnErr = [[IconCache sharedInstance] registerIcon:[extBundle imageForResource:@"error.icns"]]; - _icnOkSwm = [[IconCache sharedInstance] registerIcon:[extBundle imageForResource:@"ok_swm.icns"]]; - _icnSyncSwm = [[IconCache sharedInstance] registerIcon:[extBundle imageForResource:@"sync_swm.icns"]]; - _icnWarnSwm = [[IconCache sharedInstance] registerIcon:[extBundle imageForResource:@"warning_swm.icns"]]; - _icnErrSwm = [[IconCache sharedInstance] registerIcon:[extBundle imageForResource:@"error_swm.icns"]]; - - // NSLog(@"Icon ok: %@ identifier: %d from bundle %@", [extBundle imageForResource:@"ok.icns"], [_icnOk intValue], extBundle); -} - -- (void)enableFileIcons:(BOOL)enable -{ - _fileIconsEnabled = enable; - - [self repaintAllWindows]; -} - -- (void)setResultForPath:(NSString*)path result:(NSString*)result -{ - if (_icnOk == nil) { - // no icon resource path registered yet - return; - } - - NSNumber *res; - res = [NSNumber numberWithInt:0]; - - if( [result isEqualToString:@"OK"] ) { - res = _icnOk; - } else if( [result isEqualToString:@"SYNC"] || [result isEqualToString:@"NEW"] ) { - res = _icnSync; - } else if( [result isEqualToString:@"IGNORE"]) { - res = _icnWarn; - } else if( [result isEqualToString:@"ERROR"]) { - res = _icnErr; - } else if( [result isEqualToString:@"OK+SWM"] ) { - res = _icnOkSwm; - } else if( [result isEqualToString:@"SYNC+SWM"] || [result isEqualToString:@"NEW+SWM"] ) { - res = _icnSyncSwm; - } else if( [result isEqualToString:@"IGNORE+SWM"]) { - res = _icnWarnSwm; - } else if( [result isEqualToString:@"ERROR+SWM"]) { - res = _icnErrSwm; - }else if( [result isEqualToString:@"NOP"]) { - // Nothing. - } else { - NSLog(@"SyncState: Unknown status code %@", result); - } - - NSString* normalizedPath = [path decomposedStringWithCanonicalMapping]; - - if (![_fileNamesCache objectForKey:normalizedPath] || ![[_fileNamesCache objectForKey:normalizedPath] isEqualTo:res]) { - [_fileNamesCache setObject:res forKey:normalizedPath]; - //NSLog(@"SET value %d %@", [res intValue], normalizedPath); - _hasChangedContent = YES; - [self performSelector:@selector(repaintAllWindowsIfNeeded) withObject:0 afterDelay:1.0]; // 1 sec - } -} - -- (NSNumber*)iconByPath:(NSString*)path isDirectory:(BOOL)isDir -{ - //NSLog(@"%@ %@", NSStringFromSelector(_cmd), path); - if (!_fileIconsEnabled) - { - NSLog(@"SyncState: Icons are NOT ENABLED!"); - // return nil; - } - - if( path == nil ) { - NSNumber *res = [NSNumber numberWithInt:0]; - return res; - } - NSString* normalizedPath = [path decomposedStringWithCanonicalMapping]; - - if (![[OwnCloudFinderRequestManager sharedInstance] isRegisteredPath:normalizedPath isDirectory:isDir]) { - return [NSNumber numberWithInt:0]; - } - - NSNumber* result = [_fileNamesCache objectForKey:normalizedPath]; - // NSLog(@"XXXXXXX Asking for icon for path %@ = %d",normalizedPath, [result intValue]); - - if( result == nil ) { - result = [NSNumber numberWithInt:0]; - // Set 0 into the cache, meaning "don't have an icon, but already requested it" - [_fileNamesCache setObject:result forKey:normalizedPath]; - // start the async call - [[OwnCloudFinderRequestManager sharedInstance] askForIcon:normalizedPath isDirectory:isDir]; - } - if ([result intValue] == 0) { - // Show the old state while we wait for the new one - NSNumber* oldResult = [_oldFileNamesCache objectForKey:normalizedPath]; - if (oldResult) - result = oldResult; - } - // NSLog(@"iconByPath return value %d", [result intValue]); - - return result; -} - -// Clears the entries from the hash to make it call again home to the desktop client. -- (void)clearFileNameCache -{ - [_fileNamesCache release]; - _fileNamesCache = [[NSMutableDictionary alloc] init]; - [_oldFileNamesCache removeAllObjects]; -} - -- (void)reFetchFileNameCacheForPath:(NSString*)path -{ - //NSLog(@"%@", NSStringFromSelector(_cmd)); - - // We won't request the new state if it finds the path in _fileNamesCache - // Move all entries to _oldFileNamesCache so that they get re-requested, but - // still available while we refill the cache - [_oldFileNamesCache addEntriesFromDictionary:_fileNamesCache]; - [_fileNamesCache removeAllObjects]; - - [self repaintAllWindows]; -} - - -- (void)removeAllIcons -{ - [_fileNamesCache removeAllObjects]; - [_oldFileNamesCache removeAllObjects]; - - [self repaintAllWindows]; -} - -- (void)repaintAllWindowsIfNeeded -{ - if (!_hasChangedContent) { - //NSLog(@"%@ Repaint scheduled but not needed", NSStringFromSelector(_cmd)); - return; - } - - _hasChangedContent = NO; - [self repaintAllWindows]; -} - -- (void)repaintAllWindows -{ - //NSLog(@"%@", NSStringFromSelector(_cmd)); - NSArray* windows = [[NSApplication sharedApplication] windows]; - - for (int i = 0; i < [windows count]; i++) - { - NSWindow* window = [windows objectAtIndex:i]; - - if (![window isVisible]) - { - continue; - } - - MenuManager* menuManager = [MenuManager sharedInstance]; - OwnCloudFinderRequestManager* requestManager = [OwnCloudFinderRequestManager sharedInstance]; - - if ([[window className] isEqualToString:@"TBrowserWindow"]) - { - NSObject* browserWindowController = [window browserWindowController]; - - BOOL repaintWindow = YES; - - NSString* filterFolder = [requestManager filterFolder]; - - if (filterFolder) - { - repaintWindow = NO; - - struct TFENodeVector* targetPath; - - if ([browserWindowController respondsToSelector:@selector(targetPath)]) - { - // 10.7 & 10.8 - targetPath = [browserWindowController targetPath]; - } - else if ([browserWindowController respondsToSelector:@selector(activeContainer)]) - { - // 10.9 - targetPath = [[browserWindowController activeContainer] targetPath]; - } - else - { - NSLog(@"SyncState: refreshing icon badges failed"); - - return; - } - - NSArray* folderPaths = [menuManager pathsForNodes:targetPath]; - - for (NSString* folderPath in folderPaths) - { - if ([folderPath hasPrefix:filterFolder] || [filterFolder hasPrefix:folderPath]) - { - repaintWindow = YES; - - break; - } - } - } - - if (repaintWindow) - { - if ([browserWindowController respondsToSelector:@selector(browserViewController)]) - { - // 10.7 & 10.8 - NSObject* browserViewController = [browserWindowController browserViewController]; - - NSObject* browserView = [browserViewController browserView]; - - dispatch_async(dispatch_get_main_queue(), ^{[browserView setNeedsDisplay:YES];}); - } - else if ([browserWindowController respondsToSelector:@selector(activeBrowserViewController)]) - { - // 10.9 - NSObject* browserViewController = [browserWindowController activeBrowserViewController]; - - NSObject* browserView = [browserViewController browserView]; - - if ([browserView isKindOfClass:(id)objc_getClass("TListView")]) - { - // List or Coverflow View - [self setNeedsDisplayForListView:browserView]; - } - else - { - // Icon or Column View - dispatch_async(dispatch_get_main_queue(), ^{[browserView setNeedsDisplay:YES];}); - } - } - else - { - NSLog(@"SyncState: refreshing icon badges failed"); - - return; - } - } - } - } -} - -- (void)setIcons:(NSDictionary*)iconDictionary filterByFolder:(NSString*)filterFolder -{ - NSLog(@"%@", NSStringFromSelector(_cmd)); - for (NSString* path in iconDictionary) - { - if (filterFolder && ![path hasPrefix:filterFolder]) - { - continue; - } - - NSString* normalizedPath = [path decomposedStringWithCanonicalMapping]; - NSNumber* iconId = [iconDictionary objectForKey:path]; - - if ([iconId intValue] == -1) - { - [_fileNamesCache removeObjectForKey:normalizedPath]; - } - else - { - [_oldFileNamesCache removeObjectForKey:normalizedPath]; - [_fileNamesCache setObject:iconId forKey:normalizedPath]; - } - } - - [self repaintAllWindows]; -} - -- (void)setNeedsDisplayForListView:(NSView*)view -{ - NSArray* subviews = [view subviews]; - - for (int i = 0; i < [subviews count]; i++) - { - NSView* subview = [subviews objectAtIndex:i]; - - if ([subview isKindOfClass:(id)objc_getClass("TListRowView")]) - { - [self setNeedsDisplayForListView:subview]; - } - else if ([subview isKindOfClass:(id)objc_getClass("TListNameCellView")]) - { - dispatch_async(dispatch_get_main_queue(), ^{[subview setNeedsDisplay:YES];}); - } - } -} - -@end diff --git a/shell_integration/MacOSX/OwnCloudFinder/ContextMenuHandlers.h b/shell_integration/MacOSX/OwnCloudFinder/ContextMenuHandlers.h deleted file mode 100644 index dc604cd6b0..0000000000 --- a/shell_integration/MacOSX/OwnCloudFinder/ContextMenuHandlers.h +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - */ - -#import - -@interface NSObject (ContextMenuHandlers) - -struct TFENodeVector; - -+ (void)OCContextMenuHandlers_handleContextMenuCommon:(unsigned int)arg1 nodes:(const struct TFENodeVector*)arg2 event:(id)arg3 view:(id)arg4 browserController:(id)arg5 addPlugIns:(BOOL)arg6; -+ (void)OCContextMenuHandlers_handleContextMenuCommon:(unsigned int)arg1 nodes:(const struct TFENodeVector*)arg2 event:(id)arg3 view:(id)arg4 windowController:(id)arg5 addPlugIns:(BOOL)arg6; -+ (void)OCContextMenuHandlers_addViewSpecificStuffToMenu:(id)arg1 browserViewController:(id)arg2 context:(unsigned int)arg3; - -- (void)OCContextMenuHandlers_configureWithNodes:(const struct TFENodeVector*)arg1 browserController:(id)arg2 container:(BOOL)arg3; -- (void)OCContextMenuHandlers_configureWithNodes:(const struct TFENodeVector*)arg1 windowController:(id)arg2 container:(BOOL)arg3; - -@end \ No newline at end of file diff --git a/shell_integration/MacOSX/OwnCloudFinder/ContextMenuHandlers.m b/shell_integration/MacOSX/OwnCloudFinder/ContextMenuHandlers.m deleted file mode 100644 index 4811cf058a..0000000000 --- a/shell_integration/MacOSX/OwnCloudFinder/ContextMenuHandlers.m +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - */ - -#import "ContextMenuHandlers.h" -#import "MenuManager.h" - -@class TIconViewController; - -@implementation NSObject (ContextMenuHandlers) - -+ (void)OCContextMenuHandlers_addViewSpecificStuffToMenu:(id)arg1 browserViewController:(id)arg2 context:(unsigned int)arg3 // 10.7 & 10.8 -{ - [self OCContextMenuHandlers_addViewSpecificStuffToMenu:arg1 browserViewController:arg2 context:arg3]; - NSLog(@"OCContextMenuHandlers_addViewSpecificStuffToMenu 10.7/10.8 %@ %@ %d", arg1, arg2, arg3); - MenuManager* menuManager = [MenuManager sharedInstance]; - if (menuManager.menuItems.count > 0) - { - [menuManager addItemsToMenu:arg1 forFiles:menuManager.menuItems]; - [menuManager.menuItems removeAllObjects]; - } -} - -+ (void)OCContextMenuHandlers_addViewSpecificStuffToMenu:(id)arg1 clickedView:(id)arg2 browserViewController:(id)arg3 context:(unsigned int)arg4 // 10.9 -{ - [self OCContextMenuHandlers_addViewSpecificStuffToMenu:arg1 clickedView:arg2 browserViewController:arg3 context:arg4]; - NSLog(@"OCContextMenuHandlers_addViewSpecificStuffToMenu 10.9 %@ %@ %@ %d", arg1, arg2, arg3, arg4); - MenuManager* menuManager = [MenuManager sharedInstance]; - if (menuManager.menuItems.count > 0) - { - [menuManager addItemsToMenu:arg1 forFiles:menuManager.menuItems]; - [menuManager.menuItems removeAllObjects]; - } -} - -+ (void)OCContextMenuHandlers_handleContextMenuCommon:(unsigned int)arg1 nodes:(const struct TFENodeVector*)arg2 event:(id)arg3 view:(id)arg4 windowController:(id)arg5 addPlugIns:(BOOL)arg6 // 10.7 -{ - MenuManager* menuManager = [MenuManager sharedInstance]; - NSLog(@"ContextMenuHandlers_handleContextMenuCommon"); - menuManager.menuItems = (NSMutableArray*)[menuManager pathsForNodes:arg2]; - [self OCContextMenuHandlers_handleContextMenuCommon:arg1 nodes:arg2 event:arg3 view:arg4 windowController:arg5 addPlugIns:arg6]; -} - -+ (void)OCContextMenuHandlers_handleContextMenuCommon:(unsigned int)arg1 nodes:(const struct TFENodeVector*)arg2 event:(id)arg3 view:(id)arg4 browserController:(id)arg5 addPlugIns:(BOOL)arg6 // 10.8 -{ - MenuManager* menuManager = [MenuManager sharedInstance]; - NSLog(@"ContextMenuHandlers_handleContextMenuCommon"); - menuManager.menuItems = (NSMutableArray*)[menuManager pathsForNodes:arg2]; - [self OCContextMenuHandlers_handleContextMenuCommon:arg1 nodes:arg2 event:arg3 view:arg4 browserController:arg5 addPlugIns:arg6]; -} - -+ (void)OCContextMenuHandlers_handleContextMenuCommon:(unsigned int)arg1 nodes:(const struct TFENodeVector*)arg2 event:(id)arg3 clickedView:(id)arg4 browserViewController:(id)arg5 addPlugIns:(BOOL)arg6 // 10.9 -{ - MenuManager* menuManager = [MenuManager sharedInstance]; - NSLog(@"ContextMenuHandlers_handleContextMenuCommon"); - menuManager.menuItems = (NSMutableArray*)[menuManager pathsForNodes:arg2]; - [self OCContextMenuHandlers_handleContextMenuCommon:arg1 nodes:arg2 event:arg3 clickedView:arg4 browserViewController:arg5 addPlugIns:arg6]; -} - -- (void)OCContextMenuHandlers_configureWithNodes:(const struct TFENodeVector*)arg1 windowController:(id)arg2 container:(BOOL)arg3 // 10.7 -{ - [self OCContextMenuHandlers_configureWithNodes:arg1 windowController:arg2 container:arg3]; - NSLog(@"ContextMenuHandlers_configureWithNodes"); - TContextMenu* realSelf = (TContextMenu*)self; - MenuManager* menuManager = [MenuManager sharedInstance]; - NSArray* selectedItems = [menuManager pathsForNodes:arg1]; - [menuManager addItemsToMenu:realSelf forFiles:selectedItems]; -} - -- (void)OCContextMenuHandlers_configureWithNodes:(const struct TFENodeVector*)arg1 browserController:(id)arg2 container:(BOOL)arg3 // 10.8 -{ - [self OCContextMenuHandlers_configureWithNodes:arg1 browserController:arg2 container:arg3]; - NSLog(@"ContextMenuHandlers_configureWithNodes"); - TContextMenu* realSelf = (TContextMenu*)self; - MenuManager* menuManager = [MenuManager sharedInstance]; - - NSArray* selectedItems = [menuManager pathsForNodes:arg1]; - [menuManager addItemsToMenu:realSelf forFiles:selectedItems]; -} - -- (void)OCContextMenuHandlers_configureFromMenuNeedsUpdate:(id)arg1 clickedView:(id)arg2 container:(BOOL)arg3 event:(id)arg4 selectedNodes:(const struct TFENodeVector *)arg5 // 10.9 -{ - [self OCContextMenuHandlers_configureFromMenuNeedsUpdate:arg1 clickedView:arg2 container:arg3 event:arg4 selectedNodes:arg5]; // 10.8 - NSLog(@"ContextMenuHandlers_configureFromMenuNeedsUpdate"); - TContextMenu* realSelf = (TContextMenu*)self; - MenuManager* menuManager = [MenuManager sharedInstance]; - NSArray* selectedItems = [menuManager pathsForNodes:arg5]; - [menuManager addItemsToMenu:realSelf forFiles:selectedItems]; -} - -@end \ No newline at end of file diff --git a/shell_integration/MacOSX/OwnCloudFinder/ContextMenuHandlers.m.unc b/shell_integration/MacOSX/OwnCloudFinder/ContextMenuHandlers.m.unc deleted file mode 100644 index f060b1555d..0000000000 --- a/shell_integration/MacOSX/OwnCloudFinder/ContextMenuHandlers.m.unc +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - */ - -#import "ContextMenuHandlers.h" -#import "Finder/Finder.h" -#import "MenuManager.h" - -@implementation NSObject (ContextMenuHandlers) - -+ (void) ContextMenuHandlers_handleContextMenuCommon:(unsigned int)arg1 nodes:(const struct TFENodeVector*)arg2 event:(id)arg3 view:(id)arg4 windowController:(id)arg5 addPlugIns:(BOOL)arg6 // Lion -{ - MenuManager* contextMenuUtils = [MenuManager sharedInstance]; - - contextMenuUtils.menuItems = (NSMutableArray*)[contextMenuUtils pathsForNodes:arg2]; - - [self ContextMenuHandlers_handleContextMenuCommon:arg1 nodes:arg2 event:arg3 view:arg4 windowController:arg5 addPlugIns:arg6]; -} - -+ (void) ContextMenuHandlers_handleContextMenuCommon:(unsigned int)arg1 nodes:(const struct TFENodeVector*)arg2 event:(id)arg3 view:(id)arg4 browserController:(id)arg5 addPlugIns:(BOOL)arg6 // Mountain Lion -{ - MenuManager* contextMenuUtils = [MenuManager sharedInstance]; - - contextMenuUtils.menuItems = (NSMutableArray*)[contextMenuUtils pathsForNodes:arg2]; - - [self ContextMenuHandlers_handleContextMenuCommon:arg1 nodes:arg2 event:arg3 view:arg4 browserController:arg5 addPlugIns:arg6]; -} - -+ (void) ContextMenuHandlers_addViewSpecificStuffToMenu:(id)arg1 browserViewController:(id)arg2 context:(unsigned int)arg3 -{ - [self ContextMenuHandlers_addViewSpecificStuffToMenu:arg1 browserViewController:arg2 context:arg3]; - - if ([MenuManager sharedInstance].menuItems.count > 0) - { - MenuManager* contextMenuUtils = [MenuManager sharedInstance]; - [contextMenuUtils addItemsToMenu:arg1 forPaths:contextMenuUtils.menuItems]; - [contextMenuUtils.menuItems removeAllObjects]; - } -} - -- (void) ContextMenuHandlers_configureWithNodes:(const struct TFENodeVector*)arg1 windowController:(id)arg2 container:(BOOL)arg3 // Lion -{ - [self ContextMenuHandlers_configureWithNodes:arg1 windowController:arg2 container:arg3]; - - TContextMenu* realSelf = (TContextMenu*)self; - MenuManager* contextMenuUtils = [MenuManager sharedInstance]; - - NSArray* selectedItems = [contextMenuUtils pathsForNodes:arg1]; - [contextMenuUtils addItemsToMenu:realSelf forPaths:selectedItems]; -} - -- (void) ContextMenuHandlers_configureWithNodes:(const struct TFENodeVector*)arg1 browserController:(id)arg2 container:(BOOL)arg3 // Mountain Lion -{ - [self ContextMenuHandlers_configureWithNodes:arg1 browserController:arg2 container:arg3]; - - TContextMenu* realSelf = (TContextMenu*)self; - MenuManager* contextMenuUtils = [MenuManager sharedInstance]; - - NSArray* selectedItems = [contextMenuUtils pathsForNodes:arg1]; - [contextMenuUtils addItemsToMenu:realSelf forPaths:selectedItems]; -} - - -@end \ No newline at end of file diff --git a/shell_integration/MacOSX/OwnCloudFinder/English.lproj/InfoPlist.strings b/shell_integration/MacOSX/OwnCloudFinder/English.lproj/InfoPlist.strings deleted file mode 100644 index 88f65cf6ea..0000000000 --- a/shell_integration/MacOSX/OwnCloudFinder/English.lproj/InfoPlist.strings +++ /dev/null @@ -1,2 +0,0 @@ -/* Localized versions of Info.plist keys */ - diff --git a/shell_integration/MacOSX/OwnCloudFinder/Finder/Finder.h b/shell_integration/MacOSX/OwnCloudFinder/Finder/Finder.h deleted file mode 100644 index 8322f7b2c5..0000000000 --- a/shell_integration/MacOSX/OwnCloudFinder/Finder/Finder.h +++ /dev/null @@ -1,1016 +0,0 @@ -// -// Finder.h -// -// Created by snow on 9/30/10. -// Copyright 2010 Canvastudio Les Nie. All rights reserved. -// - -#import -#import - -struct _NSPoint { - float x; - float y; -}; - -struct _NSSize { - float width; - float height; -}; - -struct _NSRect { - struct _NSPoint origin; - struct _NSSize size; -}; - -typedef struct { - struct TFENode *_M_start; - struct TFENode *_M_finish; - struct TFENode *_M_end_of_storage; -} _Vector_impl_6bc0f568; - -struct TFENodeVector { - _Vector_impl_6bc0f568 _M_impl; -}; - -@class TListViewController, TTableViewShrinkToFitController; - -@interface TListView : NSOutlineView -{ - TListViewController *_controller; - BOOL _itemHitOnMouseDown; - TTableViewShrinkToFitController *_stfController; -} - -- (void)dealloc; -- (void)setDelegate:(id)arg1; -- (BOOL)shouldDelayWindowOrderingForEvent:(id)arg1; -- (BOOL)acceptsFirstResponder; -- (id)columnWithStringIdentifier:(id)arg1; -- (struct CGRect)_dropHighlightBackgroundRectForRow:(long long)arg1; -- (void)drawRow:(long long)arg1 clipRect:(struct CGRect)arg2; -- (BOOL)clickedOnMoreButton:(id)arg1; -- (BOOL)handleUnicodeTextInput:(id)arg1; -- (BOOL)acceptsFirstMouse:(id)arg1; -- (unsigned long long)hitTestForEvent:(id)arg1 row:(long long)arg2; -- (id)menuForEvent:(id)arg1; -- (BOOL)_onlyDragOnContent; -- (BOOL)commonMouseDownAndEarlyReturn:(id)arg1 controller:(id)arg2; -- (void)commonPostMouseDown:(id)arg1 controller:(id)arg2; -- (BOOL)_typeSelectInterpretKeyEvent:(id)arg1; -- (void)mouseDown:(id)arg1; -- (void)drawRect:(struct CGRect)arg1; -- (BOOL)_wantsLiveResizeToUseCachedImage; -- (id)inputContext; -- (void)keyDown:(id)arg1; -- (void)expandItem:(id)arg1 expandChildren:(BOOL)arg2; -- (void)collapseItem:(id)arg1 collapseChildren:(BOOL)arg2; -- (void)selectRowIndexes:(id)arg1 byExtendingSelection:(BOOL)arg2; -- (void)editColumn:(long long)arg1 row:(long long)arg2 withEvent:(id)arg3 select:(BOOL)arg4; -- (id)preparedCellAtColumn:(long long)arg1 row:(long long)arg2; -- (void)startEditingWithNode:(const struct TFENode *)arg1; -- (void)stopEditing:(BOOL)arg1; -- (struct CGRect)maxSTFEditorFrameFromTitleFrame:(struct CGRect)arg1; -- (void)updateSTFEditorLocation; -- (BOOL)shrinkToFitTextViewAboutToOpen; -- (void)shrinkToFitTextViewEditingComplete:(id)arg1; -- (void)shrinkToFitTextViewAboutToClose; -- (id)stfEditorController; -@property(readonly, nonatomic) TListViewController *controller; // @synthesize controller=_controller; - -@end - -typedef struct { - unsigned int selected:1; - unsigned int focus:1; - unsigned int twoLines:1; - unsigned int label:3; -} CDStruct_b8373011; -typedef struct { - struct CGRect _field1; - unsigned long long _field2; - struct CGRect _field3[2]; - unsigned long long _field4; -} CDStruct_51b97681; - -@protocol IKImageProxy -- (void)bind; -- (void)unbind; -- (BOOL)isBinded; -- (int)proxyDataFormat; -- (id)proxyData; -- (void)disconnect; -- (void)connect:(id)fp8; -- (id)image; -- (id)thumbnailWithSize:(struct _NSSize)fp8 antialiased:(BOOL)fp16 qualityRequested:(int)fp20 qualityProduced:(int *)fp24; -- (BOOL)isVectorial; -- (struct _NSSize)proxySize; -- (void)lockForThreadedOperation; -- (void)unlockForThreadedOperation; -- (BOOL)isLockedForThreadedOperation; -@end - - -@interface IKImageWrapper : NSObject -{ - NSString *_path; - NSData *_dataRepresentation; - NSBitmapImageRep *_bitmapRepresentation; - id _imageProxy; - union { - struct CGImage *_cgImage; - CIImage *_ciImage; - struct CGImageSource *_cgImageSource; - NSImage *_nsImage; - } _volatileRep; - unsigned short _volatileRepresentation; - unsigned int _exifOrientation:3; - unsigned int _generatedWithIconServices:1; - unsigned int _underlyingDataAreVolatile:1; - unsigned int _isReference:1; - struct _NSSize _cachedSize; - NSDictionary *_info; -} - -+ (id)imageWithPath:(id)fp8; -+ (id)imageWithNSImage:(id)fp8; -+ (id)imageWithCGImage:(struct CGImage *)fp8; -+ (id)imageWithCGImageSource:(struct CGImageSource *)fp8; -+ (id)imageWithData:(id)fp8; -+ (id)imageWithPasteboard:(id)fp8; -+ (id)imageWithNSBitmapImageRep:(id)fp8; -+ (id)emptyImage; -+ (id)imageWithSize:(struct _NSSize)fp8; -+ (id)imageWithImageProxy:(id)fp8; -+ (id)imageWithObject:(id)fp8; -- (void)dealloc; -- (void)finalize; -- (id)initWithPath:(id)fp8; -- (id)initWithCGImage:(struct CGImage *)fp8; -- (id)initWithCGImageSource:(struct CGImageSource *)fp8; -- (id)initWithNSImage:(id)fp8; -- (id)initEmptyImage; -- (id)initWithPasteboard:(id)fp8; -- (id)initWithSize:(struct _NSSize)fp8; -- (id)initWithData:(id)fp8; -- (id)initWithNSBitmapImageRep:(id)fp8; -- (id)initWithImageProxy:(id)fp8; -- (id)initWithOpenGLID:(unsigned int)fp8 size:(struct _NSSize)fp12 offset:(struct _NSPoint)fp20 premultiplied:(BOOL)fp28 deleteWhenDone:(BOOL)fp32; -- (int)volatileRepresentation; -- (void)setVolatileRepresentation:(int)fp8; -- (void)releaseVolatileImageRep; -- (unsigned short)flags; -- (void)setFlags:(unsigned short)fp8; -- (BOOL)wasGeneratedWithIconServices; -- (void)setWasGeneratedWithIconServices:(BOOL)fp8; -- (BOOL)underlyingDataAreVolatile; -- (void)setUnderlyingDataAreVolatile:(BOOL)fp8; -- (struct CGImage *)_cgImage; -- (id)_nsImage; -- (struct CGImage *)cgImage; -- (id)nsImage:(BOOL)fp8; -- (id)nsImage; -- (struct CGImageSource *)cgImageSourceRef:(BOOL)fp8; -- (void)setCGImageSource:(struct CGImageSource *)fp8; -- (void)setCGImage:(struct CGImage *)fp8; -- (void)setNSImage:(id)fp8; -- (id)copy; -- (void)setIsReference:(BOOL)fp8; -- (void)integrateReferenceInstance:(id)fp8; -- (void)referenceWillDie; -- (id)referenceInstance; -- (id)_thumbnailWithSize:(struct _NSSize)fp8 antialiased:(BOOL)fp16 qualityRequested:(int)fp20 qualityProduced:(int *)fp24; -- (id)thumbnailWithSize:(struct _NSSize)fp8 antialiased:(BOOL)fp16 qualityRequested:(int)fp20 qualityProduced:(int *)fp24; -- (struct _NSSize)cachedSize; -- (id)_sizeOfNSImage:(id)fp8; -- (struct _NSSize)_size; -- (struct _NSSize)size; -- (void)setSize:(struct _NSSize)fp8; -- (void)setSizeWithoutSavingContent:(struct _NSSize)fp8; -- (BOOL)isVectorial; -- (BOOL)isValid; -- (BOOL)isEmpty; -- (BOOL)hasAlpha; -- (id)animatedGifsCache; -- (BOOL)isAnimatedGifs; -- (int)imageFrameCount; -- (int)loopCount; -- (float)nextFrameDelayAtIndex:(int)fp8; -- (struct CGImage *)imageAtFrameIndex:(int)fp8; -- (id)GIFRepresentation; -- (id)TIFFRepresentation; -- (id)TIFFRepresentationUsingCompression:(unsigned int)fp8 factor:(float)fp12; -- (id)IK_JPEGRepresentationWithCompressionFactor:(float)fp8; -- (id)imagePath; -- (id)_dataRepresentationFromBitmapRepresentation:(id)fp8; -- (id)_createBitmapImageRepFromCGRepresentation; -- (id)dataRepresentationFromCGRepresentationWithCompressionFactor:(float)fp8; -- (id)dataRepresentation; -- (id)imageProxy; -- (void)setImageProxy:(id)fp8; -- (void)setPath:(id)fp8; -- (void)setDataRepresentation:(id)fp8; -- (void)drawInRect:(NSRect)fp8 fromRect:(NSRect)fp24 alpha:(float)fp40; -- (void)lockFocus; -- (void)unlockFocus; -- (void)saveAsTIFFAtPath:(id)fp8; -- (void)saveAsJPGAtPath:(id)fp8; -- (id)writeToFileWithAutomaticFormat:(id)fp8; -- (BOOL)hasDataRepresentation; -- (BOOL)hasBitmapRepresentation; -- (id)bitmapRepresentation; -- (void)setBitmapRepresentation:(id)fp8; -- (struct CGContext *)cgContext; -- (void)mapIntoVRAM; -- (void)mapRepresentationIntoRAM:(int)fp8; -- (BOOL)mappedIntoRAM; -- (BOOL)mappedAndDecompressedIntoRAM; -- (BOOL)mappedIntoVRAM; -- (void)freeImageCache; -- (void)bindCGCache; -- (BOOL)hasCGCache; -- (BOOL)hasVolatileCache; -- (BOOL)hasRAMCache; -- (void)freeRAMCache; -- (void)freeVRAMCache; -- (void)freeCache; -- (BOOL)textureIsPacked; -- (unsigned int)openGLTextureID; -- (void)deleteTextureInCurrentContext; -- (void)setOpenGLTextureID:(unsigned int)fp8 withGLContext:(id)fp12; -- (unsigned int)generateNewGLTextureID; -- (struct _NSPoint)openGLTextureOffset; -- (void)setOpenGLTextureOffset:(struct _NSPoint)fp8; -- (BOOL)openGLTextureIsPremultiplied; -- (void)setOpenGLTextureIsPremultiplied:(BOOL)fp8; -- (void)setValue:(id)fp8 forKey:(id)fp12; -- (id)valueForKey:(id)fp8; -- (id)_tryToCreateCGImageRepFromNonCGFile:(id)fp8; -- (id)description; - -@end - - -@class IKMipmapItem; - -@interface IKMipmapImage : NSObject -{ - IKMipmapItem *_mipmaps[4]; - IKMipmapItem *_originalMipmap; - IKMipmapItem *_customMipmap; - unsigned long _version; - struct _NSSize _originalImageSizeCache; - unsigned int _originalImageIsInvalid:1; - unsigned int _dirty:1; - unsigned int _mark:1; - unsigned int _isReference:1; -} - -- (void)_mipmapCommonInit; -- (id)init; -- (id)initWithMipmapSizes:(id)fp8 VMUsagePolicy:(id)fp12; -- (void)_cleanUp; -- (void)dealloc; -- (void)finalize; -- (BOOL)isDirty; -- (void)setDirty:(BOOL)fp8; -- (void)setIsReference:(BOOL)fp8; -- (void)setOriginalImageIsInvalid:(BOOL)fp8; -- (unsigned long)version; -- (void)setVersion:(unsigned long)fp8; -- (BOOL)marked; -- (void)mark; -- (void)clearMark; -- (id)temporaryItem; -- (id)originalItem; -- (id)mipmapItemAtIndex:(int)fp8; -- (int)indexOfMipmapItem:(id)fp8; -- (int)highestMipmapItemIndex; -- (id)highestMipmapItem; -- (float)originalAspectRatio; -- (BOOL)originalImageIsInvalid; -- (void)checkAndMarkMipmapAsInvalid; -- (void)invalidateOriginalImageSizeCache; -- (struct _NSSize)originalImageSize; -- (void)setOriginalImageSizeCache:(struct _NSSize)fp8; -- (struct _NSSize)originalImageSizeCache; -- (id)image; -- (void)setImage:(id)fp8; -- (void)setImageWithoutInvalidate:(id)fp8; -- (id *)mipmapItems; -- (id)validMipmapItems; -- (BOOL)atLeastOneMipmapItemIsValid; -- (BOOL)allMipmapItemsAreValid; -- (BOOL)customMipmapIsValidAndMatchSize:(struct _NSSize)fp8 andQuality:(int)fp16; -- (BOOL)validateMipmap:(id)fp8 withModel:(id)fp12 withQuality:(int)fp16; -- (BOOL)validateMipmap:(id)fp8 withQuality:(int)fp12; -- (BOOL)validateMipmapAtIndex:(int)fp8 withQuality:(int)fp12; -- (int)bestMipmapIndexToValidateForSize:(int)fp8; -- (id)mipmapWithSize:(int)fp8; -- (void)setImage:(id)fp8 forMipmapSize:(int)fp12; -- (id)fastMipmapItemForSize:(int)fp8 forOpenGL:(BOOL)fp12 useMinimumQualityThreshold:(BOOL)fp16; -- (id)_fastMipmapItemForSize:(struct _NSSize)fp8 forOpenGL:(BOOL)fp16 useMinimumQualityThreshold:(BOOL)fp20; -- (id)fastMipmapItemForSize:(struct _NSSize)fp8 forGLRendering:(BOOL)fp16; -- (id)fastestMipmapItemForSize:(struct _NSSize)fp8 forGLRendering:(BOOL)fp16; -- (void)_cacheMipmapSize:(struct _NSSize)fp8 fromModel:(id)fp16; -- (id)nicestMipmapItemForSize:(struct _NSSize)fp8 forGLRendering:(BOOL)fp16 cacheIt:(BOOL)fp20; -- (BOOL)shouldUseOriginalImageToCacheNiceImageWithSize:(struct _NSSize)fp8; -- (id)niceMipmapItemForSize:(struct _NSSize)fp8 forGLRendering:(BOOL)fp16 cacheIt:(BOOL)fp20; -- (id)fastMipmapItemWithExactSize:(struct _NSSize)fp8; -- (id)nicestImageForSize:(struct _NSSize)fp8 forGLRendering:(BOOL)fp16 cacheIt:(BOOL)fp20; -- (id)niceImageForSize:(struct _NSSize)fp8 forGLRendering:(BOOL)fp16 cacheIt:(BOOL)fp20; -- (id)fastImageForSize:(struct _NSSize)fp8 forGLRendering:(BOOL)fp16; -- (id)fastestImageForSize:(struct _NSSize)fp8 forGLRendering:(BOOL)fp16; -- (id)lockMipmapAtIndex:(int)fp8; -- (void)unlockMipmapItem:(id)fp8; -- (BOOL)preloadMipmapsWithQuality:(int)fp8; -- (BOOL)containsMipmapItem:(id)fp8; -- (void)freeAllCaches; -- (void)freeTemporaryCache; -- (void)freeExpendedRepresentationCaches; -- (void)freeOriginalImageCache; -- (void)invalidateMipMaps; -- (void)setMipmapSizes:(id)fp8; -- (void)setMipmapVMUsagePolicy:(id)fp8; -- (id)referenceInstance; -- (void)integrateReferenceInstance:(id)fp8; -- (void)referenceWillDie; - -@end - - -@interface IKMipmapItem : NSObject -{ - IKMipmapImage *_parent; - IKImageWrapper *_image; - int _mipmapSize; - unsigned int _vmUsagePolicy:8; - unsigned int _thumbnailQuality:8; - unsigned int _isReference:1; -} - -- (id)init; -- (void)dealloc; -- (id)description; -- (id)parent; -- (void)setParent:(id)fp8; -- (BOOL)loaded; -- (void)mapIntoVRAM; -- (BOOL)unload; -- (BOOL)isValid; -- (int)thumbnailQuality; -- (void)setThumbnailQuality:(int)fp8; -- (id)__image; -- (id)image; -- (void)setImage:(id)fp8; -- (void)setMipmapSize:(int)fp8; -- (void)invalidate; -- (int)mipmapSize; -- (void)__setDictionaryRepresentation:(id)fp8; -- (BOOL)setAsMipmapOfImage:(id)fp8 withSize:(struct _NSSize)fp12 antialiased:(BOOL)fp20 quality:(int)fp24; -- (BOOL)setAsMipmapOfImage:(id)fp8 aspectRatio:(float)fp12 antialiased:(BOOL)fp16 quality:(int)fp20; -- (int)vmUsagePolicy; -- (void)setVmUsagePolicy:(int)fp8; -- (void)setIsReference:(BOOL)fp8; -- (void)setAsReferenceOf:(id)fp8; -- (void)integrateReferenceInstance:(id)fp8 replaceImage:(BOOL)fp12; -- (void)referenceWillDie; - -@end - - -@interface IKImageCell : NSObject -{ - id _parent; - id _cellSource; - id _proxy; - unsigned int _dataSourceIndex; - unsigned int _mipmapDBIndex; - IKMipmapImage *_mipmapImage; - unsigned int _datasourceIsVectorial:1; - float _alpha; - NSMutableDictionary *_properties; -} - -+ (id)_IKBuildImageWrapperForType:(id)fp8 withObject:(id)fp12 withOwner:(id)fp16; -- (id)init; -- (struct _NSRect)imageFrame; -- (void)invalidate; -- (void)validate; -- (void)mipmapImageChanged; -- (void)validateMipmapDBIndex; -- (id)mipmapDB; -- (unsigned int)mipmapDBIndex; -- (void)setCacheDBIndex:(unsigned int)fp8; -- (void)parentWillDie:(id)fp8; -- (void)dealloc; -- (void)finalize; -- (void)setParent:(id)fp8; -- (id)parent; -- (void)setDataSource:(id)fp8; -- (id)dataSource; -- (unsigned int)dataSourceIndex; -- (void)setDataSourceIndex:(unsigned int)fp8; -- (id)mipmapImage; -- (void)setMipmapImage:(id)fp8; -- (float)alpha; -- (void)setAlpha:(float)fp8; -- (BOOL)isAnIcon; -- (BOOL)_representationTypeCanBePlayed:(id)fp8; -- (void)removeObjectForKey:(id)fp8; -- (void)setObject:(id)fp8 forKey:(id)fp12; -- (id)objectForKey:(id)fp8; -- (void)checkMipmapVersion; -- (id)dataSourcePath; - -@end - -//@class IKImageWrapper; - -@interface TIconViewCell : IKImageBrowserCell -{ - IKImageWrapper *_titleImage; - BOOL _twoLine; - CDStruct_b8373011 _titleSettings; -} - -+ (struct CGSize)cellSizeForIconSize:(double)arg1 labelOnBottom:(BOOL)arg2 gridSpacing:(double)arg3 titleAttrs:(id)arg4 subTitleAttrs:(id)arg5; -- (id)init; -- (void)dealloc; -- (double)iconSize; -- (BOOL)labelOnBottom; -- (BOOL)showPreview; -- (BOOL)showItemInfo; -- (double)endCapWidth; -- (struct CGRect)frame; -- (struct CGRect)imageFrame; -- (double)titleOffset; -- (double)maxTitleWidth; -- (struct CGRect)titleFrame; -- (struct CGRect)subtitleFrame; -- (int)heightOfInfoSpace; -- (id)subString:(id)arg1 atIndex:(unsigned long long)arg2 attributes:(id)arg3 lineBreakMode:(unsigned long long)arg4; -- (struct CGRect)placeSubString:(id)arg1 atIndex:(unsigned long long)arg2 fromFrame:(struct CGRect)arg3 bounds:(struct CGRect)arg4 attributes:(id)arg5 lineBreakMode:(unsigned long long)arg6 position:(BOOL)arg7; -- (CDStruct_51b97681)calculateTextMetrics:(id)arg1 attributes:(id)arg2; -- (void)drawLabel:(struct CGContext *)arg1 fillRect:(struct CGRect)arg2 bounds:(struct CGRect)arg3 firstLine:(struct CGRect)arg4 secondLine:(struct CGRect)arg5 actualLineCount:(unsigned long long)arg6 selected:(BOOL)arg7 labelValue:(short)arg8 justification:(short)arg9 inset:(double)arg10 radius:(double)arg11; -- (id)constructTitleImage; -- (CDStruct_b8373011)currentTitleImageSettings; -- (BOOL)titleImageStillValid:(CDStruct_b8373011)arg1; -- (id)titleImage; -- (void)invalidate; -- (void)drawTitle; - -@end - -@protocol TShrinkToFitDelegateProtocol -- (BOOL)shrinkToFitTextViewAboutToOpen; -- (void)shrinkToFitTextViewEditingComplete:(id)arg1; -- (void)shrinkToFitTextViewAboutToClose; -@end - -@class TCocoaShrinkToFitController, TIconViewController; - -@interface TIconView : IKImageBrowserView -{ - TIconViewController *_controller; - TCocoaShrinkToFitController *_stfController; - BOOL _startEditingOnMouseUp; - BOOL _viewIsReloadingData; - BOOL _isDrawingInDragImage; - unsigned long long _editedCellIndex; - unsigned long long _selectionCountBeforeReloadingData; -} - -- (id)initWithFrame:(struct CGRect)arg1 controller:(id)arg2; -- (void)setDelegate:(id)arg1; -- (id)_viewIdentifier; -- (void)viewDidMoveToWindow; -- (BOOL)shouldPreserveVisibleRangeWhileZooming; -- (void)reloadData; -- (BOOL)isReloadingData; -- (unsigned long long)selectionCountBeforeReloadingData; -- (BOOL)respondsToSelector:(SEL)arg1; -- (void)draggingExited:(id)arg1; -- (void)dragImage:(id)arg1 at:(struct CGPoint)arg2 offset:(struct CGSize)arg3 event:(id)arg4 pasteboard:(id)arg5 source:(id)arg6 slideBack:(BOOL)arg7; -- (void)scrollSTFEditorIntoView; -- (void)updateSTFEditorLocation; -- (void)scrollWheel:(id)arg1; -- (void)browserDidScroll; -- (void)mouseDown:(id)arg1; -- (void)drawRect:(struct CGRect)arg1; -- (void)rightMouseDown:(id)arg1; -- (void)mouseDragged:(id)arg1; -- (void)mouseUp:(id)arg1; -- (BOOL)_typeSelectInterpretKeyEvent:(id)arg1; -- (id)inputContext; -- (void)keyDown:(id)arg1; -- (int)defaultHeightOfInfoSpaceWithCurrentViewOptions; -- (BOOL)isDragImageOpaque; -- (int)nextIndexInGridLayoutWithDirectionKey:(unsigned short)arg1 currentIndex:(long long)arg2; -- (void)startEditingWithNode:(const struct TFENode *)arg1 afterDelay:(BOOL)arg2; -- (void)stopEditing:(BOOL)arg1; -- (unsigned long long)editingIndex; -- (struct CGRect)maxSTFEditorFrameForCellAtIndex:(unsigned long long)arg1; -- (BOOL)editCellTitleAtIndex:(unsigned long long)arg1 withEvent:(id)arg2 select:(BOOL)arg3; -- (BOOL)shrinkToFitTextViewAboutToOpen; -- (void)shrinkToFitTextViewEditingComplete:(id)arg1; -- (void)shrinkToFitTextViewAboutToClose; -- (BOOL)hasFocus; -- (id)draggedImageWithEvent:(id)arg1 countBadge:(int)arg2 hotPoint:(struct CGPoint *)arg3; -@property(readonly, retain, nonatomic) TIconViewController *controller; // @synthesize controller=_controller; - -@end - -@class TPropertyIconController; - -@interface TIconImageView : NSImageView -{ - TPropertyIconController *_controller; -} - -- (id)initWithFrame:(struct CGRect)arg1; -- (id)initWithCoder:(id)arg1; -- (void)initCommon; -- (void)drawRect:(struct CGRect)arg1; -- (void)setImage:(id)arg1; -- (BOOL)canChangeIcon; -- (BOOL)validateCopy:(id)arg1; -- (void)copy:(id)arg1; -- (BOOL)validateCut:(id)arg1; -- (void)cut:(id)arg1; -- (BOOL)validateDelete:(id)arg1; -- (void)delete:(id)arg1; -- (BOOL)validatePaste:(id)arg1; -- (void)paste:(id)arg1; -- (BOOL)validateUndo:(id)arg1; -- (void)undo:(id)arg1; -- (BOOL)validateRedo:(id)arg1; -- (void)redo:(id)arg1; -- (BOOL)validateMenuItem:(id)arg1; -- (unsigned long long)draggingEntered:(id)arg1; -- (void)concludeDragOperation:(id)arg1; -- (void)draggingEnded:(id)arg1; -@property TPropertyIconController *controller; //@synthesize controller=_controller; - -@end - -@interface FINode : NSObject -{ - -} - -+ (id)nodeWithFENode:(const struct TFENode *)arg1; -+ (struct TFENode)asFENode:(id)arg1; -- (struct TFENode)feNode; -- (struct TFENode)feNodeFollowingAliasChainSynchronously; -- (struct TFENode)feNodeFollowingAliasChainAsyncWithTarget:(id)arg1 okToLogin:(BOOL)arg2 tryToFixIfBroken:(BOOL)arg3; -- (BOOL)nodeIs:(unsigned long long)arg1; -- (id)name; -- (id)fullPath; -- (id)kind; -- (id)kindWithoutPlatform; -- (id)copyMDAttribute:(struct __CFString *)arg1; -- (id)typeIdentifier; -- (short)labelValue; -- (id)icon; -- (BOOL)isDimmed; -- (id)modificationDate; -- (id)creationDate; -- (id)lastOpenedDate; -- (long long)fileSize; -- (id)size:(BOOL)arg1; -- (id)label; -- (id)version; -- (id)comments; -- (id)authorName; -- (id)serverUserName; -- (BOOL)supportsScreenSharing; -- (BOOL)supportsFileSharing; -- (int)serverConnectionState; -- (BOOL)isSharedServer; -- (BOOL)isODSNode; -- (BOOL)isMountedSharePoint; -- (BOOL)isIDiskNode; -- (BOOL)isVolume; -- (BOOL)volumeIsEjectableOrUnmountable; -- (void)connectToSharedServerAs; -- (void)askToUseODS; -- (void)disconnectShare; -- (void)launchScreenSharingApp; -- (id)url; -- (long long)fileSizeSync; -- (BOOL)isExtensionHidden; -- (BOOL)containsLocalizations; -- (BOOL)containsPlugins; -- (BOOL)isAlias; -- (BOOL)isMDQueryHit; -- (BOOL)isResolved; -- (BOOL)isApplication; -- (BOOL)isContainer; -- (BOOL)isPackage; -- (BOOL)isVirtual; -- (BOOL)isQueryHit; -- (unsigned long long)nodeIs64:(unsigned long long)arg1; - -@end - - -@interface TViewController : NSViewController -{ -} - -- (id)initWithCoder:(id)arg1; -- (id)initWithNibName:(id)arg1 bundle:(id)arg2; -- (void)initCommon; -- (void)loadView; - -@end - -@class IPropertyValueExtractor, NSObject, TLayoutBinder; - -@interface IPropertyValueController : TViewController -{ - NSObject *_value; - TLayoutBinder *_layoutBinder; - double _viewHeight; - IPropertyValueExtractor *_valueExtractor; - BOOL _shouldBeVisible; - BOOL _shouldBeEnabled; -} - -+ (id)propertyValueController; -+ (id)propertyValueControllerWithValueExtractor:(id)arg1; -- (id)initWithValueExtractor:(id)arg1; -- (void)initCommon; -- (void)dealloc; -- (id)defaultValue; -- (void)setView:(id)arg1; -@property(retain) IPropertyValueExtractor *valueExtractor; // @synthesize valueExtractor=_valueExtractor; -- (void)updateWithNodes:(const struct TFENodeVector *)arg1; -@property BOOL shouldBeVisible; // @synthesize shouldBeVisible=_shouldBeVisible; -- (id)extractValueFromNodes:(const struct TFENodeVector *)arg1; -- (BOOL)needsUpdateForProperty:(unsigned int)arg1; -- (BOOL)isApplicableToNodes:(const struct TFENodeVector *)arg1; -- (void)flush; -- (BOOL)canModifyNodes:(const struct TFENodeVector *)arg1; -- (BOOL)adjustSize:(BOOL)arg1; -- (void)handleNodesGoingAway:(const struct TFENodeVector *)arg1; -- (void)handleNodeMDAttributesChanged:(const struct TFENode *)arg1 attributes:(id)arg2 isDisplayAttributes:(BOOL)arg3; -@property BOOL shouldBeEnabled; // @synthesize shouldBeEnabled=_shouldBeEnabled; -@property(readonly, retain) TLayoutBinder *layoutBinder; // @synthesize layoutBinder=_layoutBinder; -@property(retain) NSObject *value; // @synthesize value=_value; - -@end - - -@interface TPropertyImageViewController : IPropertyValueController -{ -} - -@end - - - -@interface TPropertyIconController : TPropertyImageViewController -{ - struct TFENodeVector _nodes; - BOOL _nodesHaveSameIcon; - BOOL _nodesHaveCustomIcon; - BOOL _nodesCanChangeIcon; -} - -- (void)initCommon; -- (void)updateWithNodes:(const struct TFENodeVector *)arg1; -- (BOOL)canModifyNodes:(const struct TFENodeVector *)arg1; -- (BOOL)validateCopy:(id)arg1; -- (void)copy:(id)arg1; -- (BOOL)validateCut:(id)arg1; -- (void)cut:(id)arg1; -- (BOOL)validateDelete:(id)arg1; -- (void)delete:(id)arg1; -- (BOOL)validatePaste:(id)arg1; -- (void)paste:(id)arg1; -- (void)concludeDragOperation:(id)arg1; - -@end - -struct TFENode { - struct OpaqueNodeRef *fNodeRef; -}; - - -@class TViewOptionsWindowController; - -@interface TFileBasedImageView : NSImageView -{ - TViewOptionsWindowController *_controller; - struct TFENode _imageNode; -} - -@property struct TFENode *imageNode; // @dynamic imageNode; -- (void)mouseDown:(id)arg1; -- (BOOL)performDragOperation:(id)arg1; - -@end - -@interface TTextCell : NSTextFieldCell -{ - double _leftMargin; - double _rightMargin; - BOOL _drawGrayTextWhenDisabled; -} - -- (id)init; -- (id)initTextCell:(id)arg1; -- (id)initWithCoder:(id)arg1; -- (void)initializeTextCell; -- (struct CGSize)cellSizeForBounds:(struct CGRect)arg1; -- (struct CGRect)titleRectForBounds:(struct CGRect)arg1; -- (void)drawInteriorWithFrame:(struct CGRect)arg1 inView:(id)arg2; -- (void)drawWithExpansionFrame:(struct CGRect)arg1 inView:(id)arg2; -- (unsigned long long)hitTestForEvent:(id)arg1 inRect:(struct CGRect)arg2 ofView:(id)arg3; -@property BOOL drawGrayTextWhenDisabled; // @synthesize drawGrayTextWhenDisabled=_drawGrayTextWhenDisabled; -@property double rightMargin; // @synthesize rightMargin=_rightMargin; -@property double leftMargin; // @synthesize leftMargin=_leftMargin; - -@end - -struct TIconRef { - //struct TRef fIconRef; -}; - -@interface TIconAndTextCell : TTextCell -{ - struct TIconRef _icon; - struct CGSize _iconSize; - double _iconToTextSpacing; - BOOL _showIcon; -} - -- (void)initializeTextCell; -- (id)copyWithZone:(struct _NSZone *)arg1; -- (void)setIcon:(const struct TIconRef *)arg1; -@property(readonly) struct TIconRef *icon; -- (struct CGRect)titleRectForBounds:(struct CGRect)arg1; -- (struct CGRect)imageRectForBounds:(struct CGRect)arg1; -- (struct CGSize)cellSizeForBounds:(struct CGRect)arg1; -- (void)drawIconWithFrame:(struct CGRect)arg1; -- (void)drawInteriorWithFrame:(struct CGRect)arg1 inView:(id)arg2; -- (unsigned long long)hitTestForEvent:(id)arg1 inRect:(struct CGRect)arg2 ofView:(id)arg3; -@property BOOL showIcon; // @synthesize showIcon=_showIcon; -@property double iconToTextSpacing; // @synthesize iconToTextSpacing=_iconToTextSpacing; -@property struct CGSize iconSize; // @synthesize iconSize=_iconSize; - -@end - -@interface TNodeIconAndNameCell : TIconAndTextCell -{ - struct TFENode _node; -} - -- (id)copyWithZone:(struct _NSZone *)arg1; -- (const struct TFENode *)node; -- (void)setNode:(const struct TFENode *)arg1; -- (id)accessibilityAttributeNames; -- (id)accessibilityAttributeValue:(id)arg1; -- (BOOL)accessibilityIsAttributeSettable:(id)arg1; - -@end - -@class NSImage, NSView; - -@interface TListViewIconAndTextCell : TNodeIconAndNameCell -{ - NSImage *_thumbnail; - NSView *_view; -} - -- (void)initializeTextCell; -- (void)dealloc; -- (id)copyWithZone:(struct _NSZone *)arg1; -- (void)drawIconWithFrame:(struct CGRect)arg1; -- (id)controller; -- (id)accessibilityActionNames; -- (id)accessibilityActionDescription:(id)arg1; -- (void)accessibilityPerformAction:(id)arg1; -@property NSView *view; // @synthesize view=_view; -@property(retain) NSImage *thumbnail; // @synthesize thumbnail=_thumbnail; - -@end - -@interface IKImageFlowView : NSOpenGLView -{ - id _dataSource; - id _dragDestinationDelegate; - id _delegate; - void *_reserved; -} - -+ (id)pixelFormat; -+ (BOOL)flowViewIsSupportedByCurrentHardware; -+ (void)initialize; -+ (void)setImportAnimationStyle:(unsigned int)fp8; -- (void)_setDefaultTextAttributes; -- (void)_ikCommonInit; -- (id)initWithFrame:(struct _NSRect)fp8; -- (void)dealloc; -- (void)finalize; -- (void)setValue:(id)fp8 forUndefinedKey:(id)fp12; -- (id)valueForUndefinedKey:(id)fp8; -- (id)allocateNewCell; -- (void)dataSourceDidChange; -- (void)_reloadCellDataAtIndex:(int)fp8; -- (void)reloadCellDataAtIndex:(int)fp8; -- (void)reloadAllCellsData; -- (void)reloadData; -- (id)loadCellAtIndex:(int)fp8; -- (void)didStabilize; -- (BOOL)isAnimating; -- (void)setAnimationsMask:(unsigned int)fp8; -- (unsigned int)animationsMask; -- (void)_cellFinishedImportAnimation:(id)fp8; -- (BOOL)itemAtIndexIsLoaded:(unsigned int)fp8; -- (void)keyWindowChanged:(id)fp8; -- (void)setSelectedIndex:(unsigned int)fp8; -- (BOOL)hitTestWithImage:(id)fp8 x:(float)fp12 y:(float)fp16; -- (unsigned int)cellIndexAtLocation:(struct _NSPoint)fp8; -- (void)_adjustScroller; -- (void)resetCursorRects; -- (void)frameDidChange:(id)fp8; -- (void)invalidateLayout; -- (float)offset; -- (int)cellIndexAtPosition:(float)fp8; -- (int)heightOfInfoSpace; -- (int)countOfVisibleCellsOnEachSide; -- (struct _NSRange)rangeOfVisibleIndexes; -- (struct _NSRange)rangeOfVisibleIndexesAtSelection; -- (id)visibleCellIndexesAtSelection; -- (id)visibleCellIndexes; -- (void)flipCellsWithOldSelectedIndex:(unsigned int)fp8 newSelectedIndex:(unsigned int)fp12; -- (void)flowLayout:(struct _NSRange)fp8; -- (void)zoomOnSelectedLayerLayout:(struct _NSRange)fp8; -- (void)updateLayoutInRange:(struct _NSRange)fp8; -- (void)updateLayout; -- (struct _NSRect)titleFrame; -- (struct _NSRect)subtitleFrame; -- (struct _NSRect)splitterFrame; -- (double)_viewAspectRatio; -- (double)_zScreen; -- (struct _NSSize)imageRenderedSize; -- (struct _NSRect)selectedImageFrame; -- (double)_computeCameraDZ; -- (double)cameraDZ; -- (double)_computeCameraDY; -- (double)cameraDY; -- (float)convertPixelUnitTo3DUnit:(float)fp8; -- (double)alignOnPixelValue; -- (BOOL)updatesCGSurfaceOnDrawRect; -- (void)setUpdatesCGSurfaceOnDrawRect:(BOOL)fp8; -- (BOOL)showSplitter; -- (void)setShowSplitter:(BOOL)fp8; -- (id)delegate; -- (void)setDelegate:(id)fp8; -- (id)dataSource; -- (void)setDataSource:(id)fp8; -- (void)setZoomOnSelectedLayer:(BOOL)fp8; -- (BOOL)zoomOnSelectedLayer; -- (unsigned int)itemsCount; -- (id)cells; -- (unsigned int)selectedIndex; -- (unsigned int)focusedIndex; -- (id)backgroundColor; -- (void)_setBackgroundColorWithRed:(float)fp8 green:(float)fp12 blue:(float)fp16 alpha:(float)fp20; -- (BOOL)backgroundIsLight; -- (BOOL)backgroundIsBlack; -- (BOOL)_convertColor:(id)fp8 toRed:(float *)fp12 green:(float *)fp16 blue:(float *)fp20 alpha:(float *)fp24; -- (void)_getBackgroundRed:(float *)fp8 green:(float *)fp12 blue:(float *)fp16 alpha:(float *)fp20; -- (void)setBackgroundColor:(id)fp8; -- (id)cellBackgroundColor; -- (void)setCellBackgroundColor:(id)fp8; -- (id)cellBorderColor; -- (void)setCellBorderColor:(id)fp8; -- (float)imageAspectRatio; -- (void)setImageAspectRatio:(float)fp8; -- (float)scaleFactor; -- (id)cacheManager; -- (BOOL)cellsAlignOnBaseline; -- (void)setCellsAlignOnBaseline:(BOOL)fp8; -- (void)startInlinePreview; -- (void)stopInlinePreview; -- (void)inlinePreviewDidRenderImage:(void *)fp8; -- (id)thumbnailImageAtIndex:(int)fp8; -- (id)previewImageAtIndex:(int)fp8; -- (void)initRenderingContext; -- (void *)fogShader; -- (void)renewGState; -- (void)setHidden:(BOOL)fp8; -- (id)renderer; -- (void)_setAutoscalesBoundsToPixelUnits:(BOOL)fp8; -- (void)setCacheManager:(id)fp8; -- (id)imageFlowContext; -- (void)setImageFlowContext:(id)fp8; -- (void)__ikSetupGLContext:(id)fp8; -- (id)openGLContext; -- (void)setOpenGLContext:(id)fp8; -- (void)_cacheWasFlushed:(id)fp8; -- (float)fogAtLocation:(float)fp8; -- (struct _NSRect)clampedBounds; -- (struct _NSRect)clampedFrame; -- (void)drawVisibleCells:(struct _NSRect)fp8; -- (void)drawBackground; -- (void)drawTitle; -- (BOOL)installViewport; -- (void)setupGLState; -- (void)installPerspetiveViewportForPicking:(BOOL)fp8 location:(struct _NSPoint)fp12; -- (void)drawFocusRing; -- (BOOL)drawWithCurrentRendererInRect:(struct _NSRect)fp8; -- (void)__copyPixels:(void *)fp8 withSize:(struct _NSSize)fp12 toCurrentFocusedViewAtPoint:(struct _NSPoint)fp20; -- (void)__copyGLToCurrentFocusedView; -- (BOOL)_createPBuffer; -- (void)_deletePBUffer; -- (BOOL)_installPBuffer; -- (void)_copyPBufferToCGSurface; -- (void)drawRect:(struct _NSRect)fp8; - -@end - -@interface TFlowView : IKImageFlowView -{ -} - -- (id)_viewIdentifier; -- (BOOL)acceptsFirstMouse:(id)arg1; -- (BOOL)acceptsFirstResponder; -- (void)dragImage:(id)arg1 at:(struct CGPoint)arg2 offset:(struct CGSize)arg3 event:(id)arg4 pasteboard:(id)arg5 source:(id)arg6 slideBack:(BOOL)arg7; -- (BOOL)isDragImageOpaque; -- (void)mouseDown:(id)arg1; -- (void)reloadData; -- (void)resetCursorRects; -- (BOOL)shouldDelayWindowOrderingForEvent:(id)arg1; - -@end - -@interface TContextMenu : NSMenu -{ -} - -+ (id)contextMenuWithDelegate:(id)arg1; -+ (void)clearContextMenuState; -+ (BOOL)allowContextualMenuForEvent:(id)arg1; -+ (void)contextMenuClickedOnNodes:(const struct TFENodeVector *)arg1 event:(id)arg2 view:(id)arg3 windowController:(id)arg4; -+ (void)contextMenuClickedOnContainer:(const struct TFENode *)arg1 event:(id)arg2 view:(id)arg3 windowController:(id)arg4; -+ (void)populateActionMenu:(id)arg1 forWindowController:(id)arg2; -- (id)initWithTitle:(id)arg1; -- (id)initWithObject:(id)arg1 nodes:(const struct TFENodeVector *)arg2 event:(id)arg3; -- (id)initWithDelegate:(id)arg1; -- (void)dealloc; -- (void)configureWithNodes:(const struct TFENodeVector *)arg1 windowController:(id)arg2 container:(BOOL)arg3; -- (void)configureForSidebarWithNode:(const struct TFENode *)arg1 windowController:(id)arg2 constrained:(BOOL)arg3 data:(id)arg4; -- (void)configureForPathbarWithNode:(const struct TFENode *)arg1 windowController:(id)arg2; -- (void)menuDidCompleteInteraction:(id)arg1; - -@end - -@interface TContextMenu (Private) -+ (void)addViewSpecificStuffToMenu:(id)arg1 browserViewController:(id)arg2 context:(unsigned int)arg3; -+ (void)buildContextMenu:(id)arg1 forContext:(unsigned int)arg2 target:(id)arg3 maxItems:(unsigned long long)arg4 addServices:(BOOL)arg5; -+ (void)handleContextMenuCommon:(unsigned int)arg1 nodes:(const struct TFENodeVector *)arg2 event:(id)arg3 view:(id)arg4 windowController:(id)arg5 addPlugIns:(BOOL)arg6; -@end - - - -@interface TDimmableIconImageView : NSImageView -{ -} - -- (void)drawRect:(struct CGRect)arg1; - -@end - -@interface TListRowView : NSTableRowView -{ - struct TFENode _node; - TListViewController *_listViewController; -// struct TNSRef _selectionView; - _Bool _isDropTarget; -} - -@property(nonatomic) _Bool isDropTarget; // @synthesize isDropTarget=_isDropTarget; -@property(nonatomic) TListViewController *listViewController; // @synthesize listViewController=_listViewController; -@property(nonatomic) struct TFENode node; // @synthesize node=_node; -- (void)openNode; -- (void)setSelected:(BOOL)arg1; -- (void)updateCellSelectedStateAppearance; -- (void)layout; -- (void)setNeedsLayout:(BOOL)arg1; -- (struct CGRect)selectionFrame; -- (void)updateLayer; -- (_Bool)isRowAfterSelected; -- (_Bool)isRowBeforeSelected; -- (long long)selectionHighlightStyle; -- (void)forceDisclosureTriangleBackgroundStyle; -- (id)disclosureTriangleButton; -- (void)dealloc; -- (id)initWithFrame:(struct CGRect)arg1; - -@end \ No newline at end of file diff --git a/shell_integration/MacOSX/OwnCloudFinder/FinderHook.h b/shell_integration/MacOSX/OwnCloudFinder/FinderHook.h deleted file mode 100644 index 3e53b128fb..0000000000 --- a/shell_integration/MacOSX/OwnCloudFinder/FinderHook.h +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - */ - -#import - -@interface FinderHook : NSObject - -+ (void)hookClassMethod:(SEL)oldSelector inClass:(NSString*)className toCallToTheNewMethod:(SEL)newSelector; -+ (void)hookMethod:(SEL)oldSelector inClass:(NSString*)className toCallToTheNewMethod:(SEL)newSelector; - -@end \ No newline at end of file diff --git a/shell_integration/MacOSX/OwnCloudFinder/FinderHook.m b/shell_integration/MacOSX/OwnCloudFinder/FinderHook.m deleted file mode 100644 index dfb6197e64..0000000000 --- a/shell_integration/MacOSX/OwnCloudFinder/FinderHook.m +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - */ - -#import "ContentManager.h" -#import "FinderHook.h" -#import "IconCache.h" -#import "objc/objc-class.h" -#import "RequestManager.h" - -static BOOL installed = NO; - -@implementation FinderHook - -+ (void)hookClassMethod:(SEL)oldSelector inClass:(NSString*)className toCallToTheNewMethod:(SEL)newSelector -{ - Class hookedClass = NSClassFromString(className); - Method oldMethod = class_getClassMethod(hookedClass, oldSelector); - Method newMethod = class_getClassMethod(hookedClass, newSelector); - - method_exchangeImplementations(newMethod, oldMethod); -} - -+ (void)hookMethod:(SEL)oldSelector inClass:(NSString*)className toCallToTheNewMethod:(SEL)newSelector -{ - Class hookedClass = NSClassFromString(className); - Method oldMethod = class_getInstanceMethod(hookedClass, oldSelector); - Method newMethod = class_getInstanceMethod(hookedClass, newSelector); - - method_exchangeImplementations(newMethod, oldMethod); -} - -+ (void)install -{ - if (installed) - { - // NSLog(@"SyncStateFinder: already installed"); - - return; - } - - // NSLog(@"SyncStateFinder: installing SyncState Shell extension"); - - [OwnCloudFinderRequestManager sharedInstance]; - - // Icons - [self hookMethod:@selector(drawImage:) inClass:@"IKImageBrowserCell" toCallToTheNewMethod:@selector(OCIconOverlayHandlers_IKImageBrowserCell_drawImage:)]; // 10.7 & 10.8 & 10.9 (Icon View arrange by name) - - [self hookMethod:@selector(drawImage:) inClass:@"IKFinderReflectiveIconCell" toCallToTheNewMethod:@selector(OCIconOverlayHandlers_IKFinderReflectiveIconCell_drawImage:)]; // 10.7 & 10.8 & 10.9 (Icon View arrange by everything else) - - [self hookMethod:@selector(drawIconWithFrame:) inClass:@"TColumnCell" toCallToTheNewMethod:@selector(OCIconOverlayHandlers_drawIconWithFrame:)]; // 10.7 & 10.8 & 10.9 Column View - - [self hookMethod:@selector(drawRect:) inClass:@"TDimmableIconImageView" toCallToTheNewMethod:@selector(OCIconOverlayHandlers_drawRect:)]; // 10.9 (List and Coverflow Views) - - // Context Menus - [self hookClassMethod:@selector(addViewSpecificStuffToMenu:browserViewController:context:) inClass:@"TContextMenu" toCallToTheNewMethod:@selector(OCContextMenuHandlers_addViewSpecificStuffToMenu:browserViewController:context:)]; // 10.7 & 10.8 - - [self hookClassMethod:@selector(addViewSpecificStuffToMenu:clickedView:browserViewController:context:) inClass:@"TContextMenu" toCallToTheNewMethod:@selector(OCContextMenuHandlers_addViewSpecificStuffToMenu:clickedView:browserViewController:context:)]; // 10.9 - - [self hookClassMethod:@selector(handleContextMenuCommon:nodes:event:view:windowController:addPlugIns:) inClass:@"TContextMenu" toCallToTheNewMethod:@selector(OCContextMenuHandlers_handleContextMenuCommon:nodes:event:view:windowController:addPlugIns:)]; // 10.7 - - [self hookClassMethod:@selector(handleContextMenuCommon:nodes:event:view:browserController:addPlugIns:) inClass:@"TContextMenu" toCallToTheNewMethod:@selector(OCContextMenuHandlers_handleContextMenuCommon:nodes:event:view:browserController:addPlugIns:)]; // 10.8 - - [self hookClassMethod:@selector(handleContextMenuCommon:nodes:event:clickedView:browserViewController:addPlugIns:) inClass:@"TContextMenu" toCallToTheNewMethod:@selector(OCContextMenuHandlers_handleContextMenuCommon:nodes:event:clickedView:browserViewController:addPlugIns:)]; // 10.9 - - [self hookMethod:@selector(configureWithNodes:windowController:container:) inClass:@"TContextMenu" toCallToTheNewMethod:@selector(OCContextMenuHandlers_configureWithNodes:windowController:container:)]; // 10.7 - - [self hookMethod:@selector(configureWithNodes:browserController:container:) inClass:@"TContextMenu" toCallToTheNewMethod:@selector(OCContextMenuHandlers_configureWithNodes:browserController:container:)]; // 10.8 - - [self hookMethod:@selector(configureFromMenuNeedsUpdate:clickedView:container:event:selectedNodes:) inClass:@"TContextMenu" toCallToTheNewMethod:@selector(OCContextMenuHandlers_configureFromMenuNeedsUpdate:clickedView:container:event:selectedNodes:)]; // 10.9 - - installed = YES; - - // NSLog(@"SyncStateFinder: installed"); -} - -+ (void)uninstall -{ - if (!installed) - { - // NSLog(@"SyncStateFinder: not installed"); - - return; - } - - // NSLog(@"SyncStateFinder: uninstalling"); - - [[OwnCloudFinderContentManager sharedInstance] dealloc]; - - [[IconCache sharedInstance] dealloc]; - - [[OwnCloudFinderRequestManager sharedInstance] dealloc]; - - // Icons - [self hookMethod:@selector(OCIconOverlayHandlers_drawImage:) inClass:@"TIconViewCell" toCallToTheNewMethod:@selector(drawImage:)]; // 10.7 & 10.8 & 10.9 - - [self hookMethod:@selector(OCIconOverlayHandlers_drawIconWithFrame:) inClass:@"TListViewIconAndTextCell" toCallToTheNewMethod:@selector(drawIconWithFrame:)]; // 10.7 & 10.8 & 10.9 - - // Context Menus - [self hookClassMethod:@selector(OCContextMenuHandlers_addViewSpecificStuffToMenu:browserViewController:context:) inClass:@"TContextMenu" toCallToTheNewMethod:@selector(addViewSpecificStuffToMenu:browserViewController:context:)]; // 10.7 & 10.8 - - [self hookClassMethod:@selector(OCContextMenuHandlers_handleContextMenuCommon:nodes:event:view:windowController:addPlugIns:) inClass:@"TContextMenu" toCallToTheNewMethod:@selector(handleContextMenuCommon:nodes:event:view:windowController:addPlugIns:)]; // 10.7 - - [self hookMethod:@selector(OCContextMenuHandlers_configureWithNodes:windowController:container:) inClass:@"TContextMenu" toCallToTheNewMethod:@selector(configureWithNodes:windowController:container:)]; // 10.7 - - [self hookClassMethod:@selector(OCContextMenuHandlers_handleContextMenuCommon:nodes:event:view:browserController:addPlugIns:) inClass:@"TContextMenu" toCallToTheNewMethod:@selector(handleContextMenuCommon:nodes:event:view:browserController:addPlugIns:)]; // 10.8 - - [self hookMethod:@selector(OCContextMenuHandlers_configureWithNodes:browserController:container:) inClass:@"TContextMenu" toCallToTheNewMethod:@selector(configureWithNodes:browserController:container:)]; // 10.8 - - installed = NO; - - // NSLog(@"SyncStateFinder: uninstalled"); -} - -@end \ No newline at end of file diff --git a/shell_integration/MacOSX/OwnCloudFinder/FinishedIconCache.h b/shell_integration/MacOSX/OwnCloudFinder/FinishedIconCache.h deleted file mode 100644 index 031b1e3390..0000000000 --- a/shell_integration/MacOSX/OwnCloudFinder/FinishedIconCache.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// FinishedIconCache.h -// OwnCloudFinder -// -// Created by Markus Goetz on 01/10/14. -// -// - -#import -#import - -@interface FinishedIconCache : NSObject { - NSCache *_cache; - long long _hits; - long long _misses; -} - -+ (FinishedIconCache*)sharedInstance; - -- (NSImage*)getIcon:(NSString*)fileName overlayIconIndex:(int)idx width:(float)w height:(float)h; -- (void)registerIcon:(NSImage*)icon withFileName:(NSString*)fileName overlayIconIndex:(int)idx width:(float)w height:(float)h; - - -@end diff --git a/shell_integration/MacOSX/OwnCloudFinder/FinishedIconCache.m b/shell_integration/MacOSX/OwnCloudFinder/FinishedIconCache.m deleted file mode 100644 index 73c26f2d4e..0000000000 --- a/shell_integration/MacOSX/OwnCloudFinder/FinishedIconCache.m +++ /dev/null @@ -1,91 +0,0 @@ -// -// FinishedIconCache.m -// OwnCloudFinder -// -// Created by Markus Goetz on 01/10/14. -// -// - -#import "FinishedIconCache.h" - - -@interface FinishedIconCacheItem : NSObject -@property (nonatomic, strong) NSImage *icon; -@property (nonatomic) NSTimeInterval maxAge; -@end - -@implementation FinishedIconCacheItem -@synthesize icon; -@synthesize maxAge; -- (void)dealloc { - //NSLog(@"RELEASE %@ %@", self, self.icon); - if (self.icon) { - [self->icon release]; - } - [super dealloc]; -} -@end - -@implementation FinishedIconCache - -static FinishedIconCache* sharedInstance = nil; - -- init -{ - self = [super init]; - if (self) - { - _cache = [[NSCache alloc] init]; - _cache.totalCostLimit = (2880 * 1800); // mbp15 screen size - _hits = 0; - _misses = 0; - } - return self; -} - -- (void)dealloc -{ - [_cache dealloc]; - [super dealloc]; -} - -+ (FinishedIconCache*)sharedInstance -{ - @synchronized(self) - { - if (sharedInstance == nil) - { - sharedInstance = [[self alloc] init]; - } - } - return sharedInstance; -} - - -- (NSImage*)getIcon:(NSString*)fileName overlayIconIndex:(int)idx width:(float)w height:(float)h -{ - NSString *cacheKey = [NSString stringWithFormat:@"%@--%d--%f%f", fileName, idx, w,h]; - FinishedIconCacheItem *item = [_cache objectForKey:cacheKey]; - if (item) { - if (item.maxAge > [[NSDate date] timeIntervalSinceReferenceDate]) { - _hits++; - return item.icon; - } - } - _misses++; - return NULL; -} - -- (void)registerIcon:(NSImage*)icon withFileName:(NSString*)fileName overlayIconIndex:(int)idx width:(float)w height:(float)h -{ - NSString *cacheKey = [NSString stringWithFormat:@"%@--%d--%f%f", fileName, idx, w, h]; - FinishedIconCacheItem *item = [[FinishedIconCacheItem alloc] init]; - item.icon = icon; - // max age between 1 sec and 5 sec - item.maxAge = [[NSDate date] timeIntervalSinceReferenceDate] + 1.0 + 4.0*((double)arc4random() / 0x100000000); - [_cache setObject:item forKey:cacheKey cost:w*h]; - [item release]; - //NSLog(@"CACHE hit/miss ratio: %f", (float)_hits/(float)_misses); -} - -@end diff --git a/shell_integration/MacOSX/OwnCloudFinder/IconCache.h b/shell_integration/MacOSX/OwnCloudFinder/IconCache.h deleted file mode 100644 index 1b451d85c3..0000000000 --- a/shell_integration/MacOSX/OwnCloudFinder/IconCache.h +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - */ - -#import - -@interface IconCache : NSObject { - int _currentIconId; - NSMutableDictionary* _iconIdDictionary; -} - -+ (IconCache*)sharedInstance; - -- (NSImage*)getIcon:(NSNumber*)iconId; -- (NSNumber*)registerIcon:(NSImage*)image; - -@end \ No newline at end of file diff --git a/shell_integration/MacOSX/OwnCloudFinder/IconCache.m b/shell_integration/MacOSX/OwnCloudFinder/IconCache.m deleted file mode 100644 index f8370b5f71..0000000000 --- a/shell_integration/MacOSX/OwnCloudFinder/IconCache.m +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - */ - -#import "IconCache.h" - -@implementation IconCache - -static IconCache* sharedInstance = nil; - -- init -{ - self = [super init]; - - if (self) - { - _iconIdDictionary = [[NSMutableDictionary alloc] init]; - _currentIconId = 0; - } - - return self; -} - -- (void)dealloc -{ - [_iconIdDictionary release]; - sharedInstance = nil; - - [super dealloc]; -} - -+ (IconCache*)sharedInstance -{ - @synchronized(self) - { - if (sharedInstance == nil) - { - sharedInstance = [[self alloc] init]; - } - } - return sharedInstance; -} - -- (NSImage*)getIcon:(NSNumber*)iconId -{ - NSImage* image = [_iconIdDictionary objectForKey:iconId]; - - return image; -} - -- (NSNumber*)registerIcon:(NSImage*)image -{ - _currentIconId++; - - NSNumber* iconId = [NSNumber numberWithInt:_currentIconId]; - - [_iconIdDictionary setObject:image forKey:iconId]; - - return iconId; -} - -@end diff --git a/shell_integration/MacOSX/OwnCloudFinder/IconOverlayHandlers.h b/shell_integration/MacOSX/OwnCloudFinder/IconOverlayHandlers.h deleted file mode 100644 index 0657fa87fc..0000000000 --- a/shell_integration/MacOSX/OwnCloudFinder/IconOverlayHandlers.h +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - */ - -#import - -@interface NSObject (IconOverlayHandlers) - -- (void)OCIconOverlayHandlers_drawIconWithFrame:(struct CGRect)arg1; -- (void)OCIconOverlayHandlers_drawImage:(id)arg1; -- (void)OCIconOverlayHandlers_drawRect:(struct CGRect)arg1; - -@end \ No newline at end of file diff --git a/shell_integration/MacOSX/OwnCloudFinder/IconOverlayHandlers.m b/shell_integration/MacOSX/OwnCloudFinder/IconOverlayHandlers.m deleted file mode 100644 index 7db375ea6b..0000000000 --- a/shell_integration/MacOSX/OwnCloudFinder/IconOverlayHandlers.m +++ /dev/null @@ -1,176 +0,0 @@ -/** - * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - */ - -#import -#import "ContentManager.h" -#import "IconCache.h" -#import "FinishedIconCache.h" -#import "IconOverlayHandlers.h" -#import "Finder/Finder.h" - -@implementation NSObject (IconOverlayHandlers) - -- (void)OCIconOverlayHandlers_drawIconWithFrame:(struct CGRect)arg1 -{ - [self OCIconOverlayHandlers_drawIconWithFrame:arg1]; - - NSURL* url = [[NSClassFromString(@"FINode") nodeFromNodeRef:[(TIconAndTextCell*)self node]->fNodeRef] previewItemURL]; - - BOOL isDir; - if ([[NSFileManager defaultManager] fileExistsAtPath: [url path] isDirectory:&isDir] == NO) { - NSLog(@"ERROR: Could not determine file type of %@", [url path]); - isDir = NO; - } - - NSNumber* imageIndex = [[OwnCloudFinderContentManager sharedInstance] iconByPath:[url path] isDirectory:isDir]; - - //NSLog(@"1 The icon index is %d", [imageIndex intValue]); - if ([imageIndex intValue] > 0) - { - NSImage* image = [[IconCache sharedInstance] getIcon:imageIndex]; - - if (image != nil) - { - struct CGRect arg2 = [(TIconViewCell*)self imageRectForBounds:arg1]; - - [image drawInRect:NSMakeRect(arg2.origin.x, arg2.origin.y, arg2.size.width, arg2.size.height) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0 respectFlipped:TRUE hints:nil]; - } - } -} - -- (void)OCIconOverlayHandlers_IKImageBrowserCell_drawImage:(id)arg1 -{ - IKImageWrapper*imageWrapper = [self OCIconOverlayHandlers_imageWrapper:arg1]; - - [self OCIconOverlayHandlers_IKImageBrowserCell_drawImage:imageWrapper]; -} - -- (void)OCIconOverlayHandlers_IKFinderReflectiveIconCell_drawImage:(id)arg1 -{ - IKImageWrapper*imageWrapper = [self OCIconOverlayHandlers_imageWrapper:arg1]; - - [self OCIconOverlayHandlers_IKFinderReflectiveIconCell_drawImage:imageWrapper]; -} - -- (IKImageWrapper*)OCIconOverlayHandlers_imageWrapper:(id)arg1 -{ - TIconViewCell* realSelf = (TIconViewCell*)self; - FINode* node = (FINode*)[realSelf representedItem]; - - NSURL* url = [node previewItemURL]; - - BOOL isDir; - if ([[NSFileManager defaultManager] fileExistsAtPath:[url path] isDirectory:&isDir] == NO) { - NSLog(@"ERROR: Could not determine file type of %@", [url path]); - isDir = NO; - } - - NSNumber* imageIndex = [[OwnCloudFinderContentManager sharedInstance] iconByPath:[url path] isDirectory:isDir]; - //NSLog(@"2 The icon index is %d %@ %@", [imageIndex intValue], [url path], isDir ? @"isDir" : @""); - - if ([imageIndex intValue] > 0) - { - NSImage* icon = [arg1 _nsImage]; - - // Use the short term icon cache that possibly has the finished icon - FinishedIconCache *finishedIconCache = [FinishedIconCache sharedInstance]; - NSImage *finishedImage = [finishedIconCache getIcon:[url path] overlayIconIndex:imageIndex width:[icon size].width height:[icon size].height]; - if (finishedImage) { - //NSLog(@"X Got finished image from cache %@ %@", finishedImage, [url path]); - return [[[IKImageWrapper alloc] initWithNSImage:finishedImage] autorelease];; - } else { - //NSLog(@"X Need to redraw %@", [url path]); - } - - NSImage* iconimage = [[IconCache sharedInstance] getIcon:[NSNumber numberWithInt:[imageIndex intValue]]]; - - if (iconimage != nil) - { - [icon lockFocus]; - - CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort]; - - CGRect destRect = CGRectMake(0, 0, [icon size].width, [icon size].height); - - CGImageRef cgImage = [iconimage CGImageForProposedRect:&destRect - context:[NSGraphicsContext currentContext] - hints:nil]; - if (cgImage) { - CGContextDrawImage(myContext, destRect, cgImage); - //CGImageRelease(cgImage); // leak here? if we leave this code in, Finder crashes - // But actually i'm not seeing a leak in Activity Monitor.. maybe it is not really leaking? - } else { - NSLog(@"No image given!!!!!11 %@", [url path]); - } - - [icon unlockFocus]; - } - - // Insert into cache - [finishedIconCache registerIcon:icon withFileName:[url path] overlayIconIndex:imageIndex width:[icon size].width height:[icon size].height]; - - return [[[IKImageWrapper alloc] initWithNSImage:icon] autorelease]; - } - else - { - return arg1; - } -} - -- (void)OCIconOverlayHandlers_drawRect:(struct CGRect)arg1 -{ - [self OCIconOverlayHandlers_drawRect:arg1]; - - NSView* supersuperview = [[(NSView*)self superview] superview]; - - if ([supersuperview isKindOfClass:(id)objc_getClass("TListRowView")]) - { - TListRowView *listRowView = (TListRowView*) supersuperview; - FINode *fiNode; - - object_getInstanceVariable(listRowView, "_node", (void**)&fiNode); - - NSURL *url; - - if ([fiNode respondsToSelector:@selector(previewItemURL)]) - { - url = [fiNode previewItemURL]; - } - else { - return; - } - - BOOL isDir; - if ([[NSFileManager defaultManager] fileExistsAtPath:[url path] isDirectory: &isDir] == NO) { - NSLog(@"ERROR: Could not determine file type of %@", [url path]); - isDir = NO; - } - - NSNumber* imageIndex = [[OwnCloudFinderContentManager sharedInstance] iconByPath:[url path] isDirectory:isDir]; - //NSLog(@"3 The icon index is %d", [imageIndex intValue]); - - if ([imageIndex intValue] > 0) - { - NSImage* image = [[IconCache sharedInstance] getIcon:imageIndex]; - - if (image != nil) - { - [image drawInRect:NSMakeRect(arg1.origin.x, arg1.origin.y, arg1.size.width, arg1.size.height) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0 respectFlipped:TRUE hints:nil]; - } - } - - } -} - -@end \ No newline at end of file diff --git a/shell_integration/MacOSX/OwnCloudFinder/Info.plist b/shell_integration/MacOSX/OwnCloudFinder/Info.plist deleted file mode 100644 index f64f59a8ce..0000000000 --- a/shell_integration/MacOSX/OwnCloudFinder/Info.plist +++ /dev/null @@ -1,48 +0,0 @@ - - - - - SocketApiPrefix - $(OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX)$(OC_APPLICATION_REV_DOMAIN) - CFBundleDevelopmentRegion - English - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIconFile - - CFBundleIdentifier - com.owncloud.finder - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleSignature - OWNC - CFBundleVersion - 1 - CFPlugInDynamicRegisterFunction - - CFPlugInDynamicRegistration - NO - CFPlugInFactories - - 00000000-0000-0000-0000-000000000000 - MyFactoryFunction - - CFPlugInTypes - - 00000000-0000-0000-0000-000000000000 - - 00000000-0000-0000-0000-000000000000 - - - CFPlugInUnloadFunction - - NSPrincipalClass - FinderHook - - diff --git a/shell_integration/MacOSX/OwnCloudFinder/MenuManager.h b/shell_integration/MacOSX/OwnCloudFinder/MenuManager.h deleted file mode 100644 index a89fd117df..0000000000 --- a/shell_integration/MacOSX/OwnCloudFinder/MenuManager.h +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - */ - -#import - -@class TContextMenu; -struct TFENodeVector; - -@interface MenuManager : NSObject - -@property (nonatomic, strong) NSMutableArray* menuItems; - -+ (MenuManager*)sharedInstance; - -- (void)addItemsToMenu:(TContextMenu*)menu forFiles:(NSArray*)files; -- (NSArray*)pathsForNodes:(const struct TFENodeVector*)nodes; - -@end \ No newline at end of file diff --git a/shell_integration/MacOSX/OwnCloudFinder/MenuManager.m b/shell_integration/MacOSX/OwnCloudFinder/MenuManager.m deleted file mode 100644 index 1fd9e0a53b..0000000000 --- a/shell_integration/MacOSX/OwnCloudFinder/MenuManager.m +++ /dev/null @@ -1,188 +0,0 @@ -/** - * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - */ - -#import "MenuManager.h" -#import "Finder/Finder.h" -#import "RequestManager.h" - -@implementation MenuManager - -static MenuManager* sharedInstance = nil; - -+ (MenuManager*)sharedInstance -{ - @synchronized(self) - { - if (sharedInstance == nil) - { - sharedInstance = [[self alloc] init]; - } - } - return sharedInstance; -} - -- init -{ - return [super init]; -} - -- (void)addChildrenSubMenuItems:(NSMenuItem*)parentMenuItem withChildren:(NSArray*)menuItemsDictionaries forFiles:(NSArray*)files -{ - NSMenu* menu = [[NSMenu alloc] init]; - - for (int i = 0; i < [menuItemsDictionaries count]; ++i) - { - NSDictionary* menuItemDictionary = [menuItemsDictionaries objectAtIndex:i]; - - NSString* submenuTitle = [menuItemDictionary objectForKey:@"title"]; - BOOL enabled = [[menuItemDictionary objectForKey:@"enabled"] boolValue]; - NSString* uuid = [menuItemDictionary objectForKey:@"uuid"]; - NSArray* childrenSubMenuItems = (NSArray*)[menuItemDictionary objectForKey:@"contextMenuItems"]; - - if ([submenuTitle isEqualToString:@"_SEPARATOR_"]) - { - [menu addItem:[NSMenuItem separatorItem]]; - } - else if (childrenSubMenuItems != nil && [childrenSubMenuItems count] != 0) - { - NSMenuItem* submenuItem = [menu addItemWithTitle:submenuTitle action:nil keyEquivalent:@""]; - - [self addChildrenSubMenuItems:submenuItem withChildren:childrenSubMenuItems forFiles:files]; - } - else - { - [self createActionMenuItemIn:menu withTitle:submenuTitle withIndex:i enabled:enabled withUuid:uuid forFiles:files]; - } - } - - [parentMenuItem setSubmenu:menu]; - - [menu release]; -} - -- (void)addItemsToMenu:(TContextMenu*)menu forFiles:(NSArray*)files -{ - OwnCloudFinderRequestManager *requestManager = [OwnCloudFinderRequestManager sharedInstance]; - NSString *shareItemTitle = [requestManager shareItemTitle]; - if (!shareItemTitle || shareItemTitle.length == 0) { - return; - } - - for (int i = 0; i < files.count; i++) { - NSString *fn = [files objectAtIndex:i]; - BOOL isDir = false; - if ([[NSFileManager defaultManager] fileExistsAtPath:fn isDirectory:&isDir]) { - if (![requestManager isRegisteredPath:fn isDirectory:isDir]) { - return; - } - } - } - - NSMutableArray* menuItemsArray = [[[NSMutableArray alloc] init] autorelease]; - NSMutableDictionary *firstEntry = [[[NSMutableDictionary alloc] init] autorelease]; - [firstEntry setValue:[NSNumber numberWithBool:YES] forKey:@"enabled"]; - [firstEntry setValue:shareItemTitle forKey:@"title"]; - [menuItemsArray addObject:firstEntry]; - - // Find the menu with a submenu which should be the share menu position - NSInteger menuIndex = MIN(4, menu.itemArray.count); - for (int i = menuIndex; i < menu.itemArray.count; i++) { - if ([[menu itemAtIndex:i] hasSubmenu]) { - menuIndex = i; - //NSLog(@"addItemsToMenu: menuIndex --> %lu (count=%lu)", menuIndex, (unsigned long)menu.itemArray.count); - break; - } - } - - for (int i = 0; i < [menuItemsArray count]; ++i) - { - NSDictionary* menuItemDictionary = [menuItemsArray objectAtIndex:i]; - - NSString* mainMenuTitle = [menuItemDictionary objectForKey:@"title"]; - - if ([mainMenuTitle isEqualToString:@""]) - { - continue; - } - - menuIndex++; - - BOOL enabled = [[menuItemDictionary objectForKey:@"enabled"] boolValue]; - NSString* uuid = [menuItemDictionary objectForKey:@"uuid"]; - NSArray* childrenSubMenuItems = (NSArray*)[menuItemDictionary objectForKey:@"contextMenuItems"]; - - if (childrenSubMenuItems != nil && [childrenSubMenuItems count] != 0) - { - NSMenuItem* mainMenuItem = [menu insertItemWithTitle:mainMenuTitle action:nil keyEquivalent:@"" atIndex:menuIndex]; - - [self addChildrenSubMenuItems:mainMenuItem withChildren:childrenSubMenuItems forFiles:files]; - } - else - { - [self createActionMenuItemIn:menu withTitle:mainMenuTitle withIndex:menuIndex enabled:enabled withUuid:uuid forFiles:files]; - } - } -} - -- (void)createActionMenuItemIn:(NSMenu*)menu withTitle:(NSString*)title withIndex:(NSInteger*)index enabled:(BOOL)enabled withUuid:(NSString*)uuid forFiles:(NSArray*)files -{ - NSMenuItem* mainMenuItem = [menu insertItemWithTitle:title action:@selector(menuItemClicked:) keyEquivalent:@"" atIndex:index]; - - if (enabled) - { - [mainMenuItem setTarget:self]; - } - - NSDictionary* menuActionDictionary = [[NSMutableDictionary alloc] init]; - [menuActionDictionary setValue:uuid forKey:@"uuid"]; - NSMutableArray* filesArray = [files copy]; - [menuActionDictionary setValue:filesArray forKey:@"files"]; - - [mainMenuItem setRepresentedObject:menuActionDictionary]; - - [filesArray release]; - [menuActionDictionary release]; -} - -- (void)menuItemClicked:(id)param -{ - [[OwnCloudFinderRequestManager sharedInstance] menuItemClicked:[param representedObject]]; -} - -- (NSArray*)pathsForNodes:(const struct TFENodeVector*)nodes -{ - struct TFENode* start = nodes->_M_impl._M_start; - struct TFENode* end = nodes->_M_impl._M_finish; - - int count = end - start; - - NSMutableArray* selectedItems = [[NSMutableArray alloc] initWithCapacity:count]; - struct TFENode* current; - - for (current = start; current < end; ++current) - { - FINode* node = (FINode*)[NSClassFromString(@"FINode") nodeFromNodeRef:current->fNodeRef]; - - NSString* path = [[node previewItemURL] path]; - - if (path) - { - [selectedItems addObject:path]; - } - } - - return [selectedItems autorelease]; -} - -@end \ No newline at end of file diff --git a/shell_integration/MacOSX/OwnCloudFinder/OwnCloudFinder.xcodeproj/project.pbxproj b/shell_integration/MacOSX/OwnCloudFinder/OwnCloudFinder.xcodeproj/project.pbxproj deleted file mode 100644 index a0aba6a39f..0000000000 --- a/shell_integration/MacOSX/OwnCloudFinder/OwnCloudFinder.xcodeproj/project.pbxproj +++ /dev/null @@ -1,369 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 0B13ECAF173C687900548DA1 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0BFC9ACB173C57E400CDD329 /* Security.framework */; }; - 5BB74A8719DBF9BB001BAAAC /* FinishedIconCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BB74A8619DBF9BB001BAAAC /* FinishedIconCache.m */; }; - 692C18A516660C4700BF6A53 /* ContextMenuHandlers.m in Sources */ = {isa = PBXBuildFile; fileRef = 692C18A416660C4600BF6A53 /* ContextMenuHandlers.m */; }; - 692C18A9166617F500BF6A53 /* IconOverlayHandlers.m in Sources */ = {isa = PBXBuildFile; fileRef = 692C18A8166617F500BF6A53 /* IconOverlayHandlers.m */; }; - 692C18AC1666392700BF6A53 /* MenuManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 692C18AB1666392700BF6A53 /* MenuManager.m */; }; - 6993878616494C000044E4DF /* RequestManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 6993878516494C000044E4DF /* RequestManager.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; - 69948B361636D50E0093B6CE /* ContentManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 69948B351636D50E0093B6CE /* ContentManager.m */; }; - 8C37DD9F161593BD00016A95 /* FinderHook.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C37DD9A161593BD00016A95 /* FinderHook.m */; }; - 8C37DDB2161593FF00016A95 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8C37DDB1161593FF00016A95 /* Cocoa.framework */; }; - 8C37DDBA161594B400016A95 /* Quartz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8C37DDB9161594B400016A95 /* Quartz.framework */; }; - 8C99F6941622D145002D2135 /* IconCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C99F6931622D145002D2135 /* IconCache.m */; }; - 8D576314048677EA00EA77CD /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0AA1909FFE8422F4C02AAC07 /* CoreFoundation.framework */; }; - 8D5B49A804867FD3000E48DA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8D5B49A704867FD3000E48DA /* InfoPlist.strings */; }; - C220057B1B31B04C00A4FB37 /* error_swm.icns in Resources */ = {isa = PBXBuildFile; fileRef = C22005731B31B04C00A4FB37 /* error_swm.icns */; }; - C220057C1B31B04C00A4FB37 /* error.icns in Resources */ = {isa = PBXBuildFile; fileRef = C22005741B31B04C00A4FB37 /* error.icns */; }; - C220057D1B31B04C00A4FB37 /* ok_swm.icns in Resources */ = {isa = PBXBuildFile; fileRef = C22005751B31B04C00A4FB37 /* ok_swm.icns */; }; - C220057E1B31B04C00A4FB37 /* ok.icns in Resources */ = {isa = PBXBuildFile; fileRef = C22005761B31B04C00A4FB37 /* ok.icns */; }; - C220057F1B31B04C00A4FB37 /* sync_swm.icns in Resources */ = {isa = PBXBuildFile; fileRef = C22005771B31B04C00A4FB37 /* sync_swm.icns */; }; - C22005801B31B04C00A4FB37 /* sync.icns in Resources */ = {isa = PBXBuildFile; fileRef = C22005781B31B04C00A4FB37 /* sync.icns */; }; - C22005811B31B04C00A4FB37 /* warning_swm.icns in Resources */ = {isa = PBXBuildFile; fileRef = C22005791B31B04C00A4FB37 /* warning_swm.icns */; }; - C22005821B31B04C00A4FB37 /* warning.icns in Resources */ = {isa = PBXBuildFile; fileRef = C220057A1B31B04C00A4FB37 /* warning.icns */; }; - C2B573831B1CD5AE00303B36 /* SyncClientProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = C2B573821B1CD5AE00303B36 /* SyncClientProxy.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 089C167EFE841241C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; - 0AA1909FFE8422F4C02AAC07 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = ""; }; - 0B2BF60B176A43DB001246CD /* Finder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Finder.h; sourceTree = ""; }; - 0BFC9ACB173C57E400CDD329 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; - 5BB74A8519DBF9BB001BAAAC /* FinishedIconCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FinishedIconCache.h; sourceTree = ""; }; - 5BB74A8619DBF9BB001BAAAC /* FinishedIconCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FinishedIconCache.m; sourceTree = ""; }; - 692C18A316660C4600BF6A53 /* ContextMenuHandlers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContextMenuHandlers.h; sourceTree = ""; }; - 692C18A416660C4600BF6A53 /* ContextMenuHandlers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ContextMenuHandlers.m; sourceTree = ""; }; - 692C18A7166617F500BF6A53 /* IconOverlayHandlers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IconOverlayHandlers.h; sourceTree = ""; }; - 692C18A8166617F500BF6A53 /* IconOverlayHandlers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IconOverlayHandlers.m; sourceTree = ""; }; - 692C18AA1666392700BF6A53 /* MenuManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MenuManager.h; sourceTree = ""; }; - 692C18AB1666392700BF6A53 /* MenuManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MenuManager.m; sourceTree = ""; }; - 6993878416494C000044E4DF /* RequestManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RequestManager.h; sourceTree = ""; }; - 6993878516494C000044E4DF /* RequestManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RequestManager.m; sourceTree = ""; usesTabs = 1; }; - 69948B341636D50E0093B6CE /* ContentManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContentManager.h; sourceTree = ""; }; - 69948B351636D50E0093B6CE /* ContentManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ContentManager.m; sourceTree = ""; }; - 8C37DD99161593BD00016A95 /* FinderHook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FinderHook.h; sourceTree = ""; }; - 8C37DD9A161593BD00016A95 /* FinderHook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FinderHook.m; sourceTree = ""; }; - 8C37DDB1161593FF00016A95 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; - 8C37DDB9161594B400016A95 /* Quartz.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quartz.framework; path = System/Library/Frameworks/Quartz.framework; sourceTree = SDKROOT; }; - 8C99F6921622D145002D2135 /* IconCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IconCache.h; sourceTree = ""; }; - 8C99F6931622D145002D2135 /* IconCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IconCache.m; sourceTree = ""; }; - 8D576316048677EA00EA77CD /* SyncStateFinder.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SyncStateFinder.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; - 8D576317048677EA00EA77CD /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - C22005731B31B04C00A4FB37 /* error_swm.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = error_swm.icns; path = ../../icons/icns/error_swm.icns; sourceTree = ""; }; - C22005741B31B04C00A4FB37 /* error.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = error.icns; path = ../../icons/icns/error.icns; sourceTree = ""; }; - C22005751B31B04C00A4FB37 /* ok_swm.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = ok_swm.icns; path = ../../icons/icns/ok_swm.icns; sourceTree = ""; }; - C22005761B31B04C00A4FB37 /* ok.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = ok.icns; path = ../../icons/icns/ok.icns; sourceTree = ""; }; - C22005771B31B04C00A4FB37 /* sync_swm.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = sync_swm.icns; path = ../../icons/icns/sync_swm.icns; sourceTree = ""; }; - C22005781B31B04C00A4FB37 /* sync.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = sync.icns; path = ../../icons/icns/sync.icns; sourceTree = ""; }; - C22005791B31B04C00A4FB37 /* warning_swm.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = warning_swm.icns; path = ../../icons/icns/warning_swm.icns; sourceTree = ""; }; - C220057A1B31B04C00A4FB37 /* warning.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = warning.icns; path = ../../icons/icns/warning.icns; sourceTree = ""; }; - C2B573811B1CD5AE00303B36 /* SyncClientProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SyncClientProxy.h; sourceTree = ""; }; - C2B573821B1CD5AE00303B36 /* SyncClientProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SyncClientProxy.m; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 8D576313048677EA00EA77CD /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 0B13ECAF173C687900548DA1 /* Security.framework in Frameworks */, - 8D576314048677EA00EA77CD /* CoreFoundation.framework in Frameworks */, - 8C37DDB2161593FF00016A95 /* Cocoa.framework in Frameworks */, - 8C37DDBA161594B400016A95 /* Quartz.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 089C166AFE841209C02AAC07 /* SyncStateFinder */ = { - isa = PBXGroup; - children = ( - C2B573801B1CD5AE00303B36 /* common */, - 08FB77AFFE84173DC02AAC07 /* Source */, - 089C167CFE841241C02AAC07 /* Resources */, - 089C1671FE841209C02AAC07 /* External Frameworks and Libraries */, - 19C28FB6FE9D52B211CA2CBB /* Products */, - ); - name = SyncStateFinder; - sourceTree = ""; - usesTabs = 1; - }; - 089C1671FE841209C02AAC07 /* External Frameworks and Libraries */ = { - isa = PBXGroup; - children = ( - 0BFC9ACB173C57E400CDD329 /* Security.framework */, - 8C37DDB9161594B400016A95 /* Quartz.framework */, - 0AA1909FFE8422F4C02AAC07 /* CoreFoundation.framework */, - 8C37DDB1161593FF00016A95 /* Cocoa.framework */, - ); - name = "External Frameworks and Libraries"; - sourceTree = ""; - }; - 089C167CFE841241C02AAC07 /* Resources */ = { - isa = PBXGroup; - children = ( - C22005731B31B04C00A4FB37 /* error_swm.icns */, - C22005741B31B04C00A4FB37 /* error.icns */, - C22005751B31B04C00A4FB37 /* ok_swm.icns */, - C22005761B31B04C00A4FB37 /* ok.icns */, - C22005771B31B04C00A4FB37 /* sync_swm.icns */, - C22005781B31B04C00A4FB37 /* sync.icns */, - C22005791B31B04C00A4FB37 /* warning_swm.icns */, - C220057A1B31B04C00A4FB37 /* warning.icns */, - 8D576317048677EA00EA77CD /* Info.plist */, - 8D5B49A704867FD3000E48DA /* InfoPlist.strings */, - ); - name = Resources; - sourceTree = ""; - }; - 08FB77AFFE84173DC02AAC07 /* Source */ = { - isa = PBXGroup; - children = ( - 0B2BF60A176A43DB001246CD /* Finder */, - 8C37DD99161593BD00016A95 /* FinderHook.h */, - 8C37DD9A161593BD00016A95 /* FinderHook.m */, - 692C18A316660C4600BF6A53 /* ContextMenuHandlers.h */, - 692C18A416660C4600BF6A53 /* ContextMenuHandlers.m */, - 692C18A7166617F500BF6A53 /* IconOverlayHandlers.h */, - 692C18A8166617F500BF6A53 /* IconOverlayHandlers.m */, - 6993878416494C000044E4DF /* RequestManager.h */, - 6993878516494C000044E4DF /* RequestManager.m */, - 69948B341636D50E0093B6CE /* ContentManager.h */, - 69948B351636D50E0093B6CE /* ContentManager.m */, - 8C99F6921622D145002D2135 /* IconCache.h */, - 8C99F6931622D145002D2135 /* IconCache.m */, - 5BB74A8519DBF9BB001BAAAC /* FinishedIconCache.h */, - 5BB74A8619DBF9BB001BAAAC /* FinishedIconCache.m */, - 692C18AA1666392700BF6A53 /* MenuManager.h */, - 692C18AB1666392700BF6A53 /* MenuManager.m */, - ); - name = Source; - sourceTree = ""; - }; - 0B2BF60A176A43DB001246CD /* Finder */ = { - isa = PBXGroup; - children = ( - 0B2BF60B176A43DB001246CD /* Finder.h */, - ); - path = Finder; - sourceTree = ""; - }; - 19C28FB6FE9D52B211CA2CBB /* Products */ = { - isa = PBXGroup; - children = ( - 8D576316048677EA00EA77CD /* SyncStateFinder.bundle */, - ); - name = Products; - sourceTree = ""; - }; - C2B573801B1CD5AE00303B36 /* common */ = { - isa = PBXGroup; - children = ( - C2B573811B1CD5AE00303B36 /* SyncClientProxy.h */, - C2B573821B1CD5AE00303B36 /* SyncClientProxy.m */, - ); - name = common; - path = ../common; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 8D57630D048677EA00EA77CD /* SyncStateFinder */ = { - isa = PBXNativeTarget; - buildConfigurationList = 1DEB911A08733D790010E9CD /* Build configuration list for PBXNativeTarget "SyncStateFinder" */; - buildPhases = ( - 8D57630F048677EA00EA77CD /* Resources */, - 8D576311048677EA00EA77CD /* Sources */, - 8D576313048677EA00EA77CD /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = SyncStateFinder; - productInstallPath = "$(HOME)/Library/Bundles"; - productName = SyncStateFinder; - productReference = 8D576316048677EA00EA77CD /* SyncStateFinder.bundle */; - productType = "com.apple.product-type.bundle"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 089C1669FE841209C02AAC07 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0460; - }; - buildConfigurationList = 1DEB911E08733D790010E9CD /* Build configuration list for PBXProject "OwnCloudFinder" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 1; - knownRegions = ( - English, - Japanese, - French, - German, - ); - mainGroup = 089C166AFE841209C02AAC07 /* SyncStateFinder */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 8D57630D048677EA00EA77CD /* SyncStateFinder */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 8D57630F048677EA00EA77CD /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - C220057E1B31B04C00A4FB37 /* ok.icns in Resources */, - C22005821B31B04C00A4FB37 /* warning.icns in Resources */, - C220057F1B31B04C00A4FB37 /* sync_swm.icns in Resources */, - C220057C1B31B04C00A4FB37 /* error.icns in Resources */, - 8D5B49A804867FD3000E48DA /* InfoPlist.strings in Resources */, - C22005801B31B04C00A4FB37 /* sync.icns in Resources */, - C220057D1B31B04C00A4FB37 /* ok_swm.icns in Resources */, - C220057B1B31B04C00A4FB37 /* error_swm.icns in Resources */, - C22005811B31B04C00A4FB37 /* warning_swm.icns in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 8D576311048677EA00EA77CD /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 8C37DD9F161593BD00016A95 /* FinderHook.m in Sources */, - 8C99F6941622D145002D2135 /* IconCache.m in Sources */, - 69948B361636D50E0093B6CE /* ContentManager.m in Sources */, - C2B573831B1CD5AE00303B36 /* SyncClientProxy.m in Sources */, - 6993878616494C000044E4DF /* RequestManager.m in Sources */, - 5BB74A8719DBF9BB001BAAAC /* FinishedIconCache.m in Sources */, - 692C18A516660C4700BF6A53 /* ContextMenuHandlers.m in Sources */, - 692C18A9166617F500BF6A53 /* IconOverlayHandlers.m in Sources */, - 692C18AC1666392700BF6A53 /* MenuManager.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 8D5B49A704867FD3000E48DA /* InfoPlist.strings */ = { - isa = PBXVariantGroup; - children = ( - 089C167EFE841241C02AAC07 /* English */, - ); - name = InfoPlist.strings; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 1DEB911B08733D790010E9CD /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_LINK_OBJC_RUNTIME = NO; - COMBINE_HIDPI_IMAGES = YES; - CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; - COPY_PHASE_STRIP = NO; - GCC_DYNAMIC_NO_PIC = NO; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; - INFOPLIST_FILE = Info.plist; - INSTALL_PATH = "$(HOME)/Library/Bundles"; - OC_APPLICATION_REV_DOMAIN = com.owncloud.desktopclient; - OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX = ""; - PRODUCT_NAME = SyncStateFinder; - WRAPPER_EXTENSION = bundle; - }; - name = Debug; - }; - 1DEB911C08733D790010E9CD /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_LINK_OBJC_RUNTIME = NO; - COMBINE_HIDPI_IMAGES = YES; - CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_MODEL_TUNING = G5; - INFOPLIST_FILE = Info.plist; - INSTALL_PATH = "$(HOME)/Library/Bundles"; - OC_APPLICATION_REV_DOMAIN = com.owncloud.desktopclient; - OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX = ""; - PRODUCT_NAME = SyncStateFinder; - WRAPPER_EXTENSION = bundle; - }; - name = Release; - }; - 1DEB911F08733D790010E9CD /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; - COPY_PHASE_STRIP = NO; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = _DEBUG_LOG; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.7; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - }; - name = Debug; - }; - 1DEB912008733D790010E9CD /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; - COPY_PHASE_STRIP = NO; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.7; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 1DEB911A08733D790010E9CD /* Build configuration list for PBXNativeTarget "SyncStateFinder" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 1DEB911B08733D790010E9CD /* Debug */, - 1DEB911C08733D790010E9CD /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 1DEB911E08733D790010E9CD /* Build configuration list for PBXProject "OwnCloudFinder" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 1DEB911F08733D790010E9CD /* Debug */, - 1DEB912008733D790010E9CD /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 089C1669FE841209C02AAC07 /* Project object */; -} diff --git a/shell_integration/MacOSX/OwnCloudFinder/OwnCloudFinder.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/shell_integration/MacOSX/OwnCloudFinder/OwnCloudFinder.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 25ab3b4240..0000000000 --- a/shell_integration/MacOSX/OwnCloudFinder/OwnCloudFinder.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/shell_integration/MacOSX/OwnCloudFinder/OwnCloudFinder.xcodeproj/xcuserdata/guruz.xcuserdatad/xcschemes/xcschememanagement.plist b/shell_integration/MacOSX/OwnCloudFinder/OwnCloudFinder.xcodeproj/xcuserdata/guruz.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 2f490c9952..0000000000 --- a/shell_integration/MacOSX/OwnCloudFinder/OwnCloudFinder.xcodeproj/xcuserdata/guruz.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,14 +0,0 @@ - - - - - SuppressBuildableAutocreation - - 8D57630D048677EA00EA77CD - - primary - - - - - diff --git a/shell_integration/MacOSX/OwnCloudFinder/RequestManager.h b/shell_integration/MacOSX/OwnCloudFinder/RequestManager.h deleted file mode 100644 index f3924fac60..0000000000 --- a/shell_integration/MacOSX/OwnCloudFinder/RequestManager.h +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - */ - -#import -#import "RequestManager.h" -#import "SyncClientProxy.h" - -@interface OwnCloudFinderRequestManager : NSObject -{ - SyncClientProxy *_syncClientProxy; - - NSMutableArray* _requestQueue; - NSMutableDictionary* _registeredPathes; - NSMutableSet* _requestedPaths; - - NSString *_shareMenuTitle; -} - -@property (nonatomic, retain) NSString* filterFolder; - -+ (OwnCloudFinderRequestManager*)sharedInstance; - -- (BOOL)isRegisteredPath:(NSString*)path isDirectory:(BOOL)isDir; -- (void)askForIcon:(NSString*)path isDirectory:(BOOL)isDir; -- (void)menuItemClicked:(NSDictionary*)actionDictionary; - -- (NSString*) shareItemTitle; - -@end \ No newline at end of file diff --git a/shell_integration/MacOSX/OwnCloudFinder/RequestManager.m b/shell_integration/MacOSX/OwnCloudFinder/RequestManager.m deleted file mode 100644 index 1d4a5c40af..0000000000 --- a/shell_integration/MacOSX/OwnCloudFinder/RequestManager.m +++ /dev/null @@ -1,159 +0,0 @@ -/** - * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - */ - -#import "ContentManager.h" -#import "IconCache.h" -#import "RequestManager.h" - -static OwnCloudFinderRequestManager* sharedInstance = nil; - -@implementation OwnCloudFinderRequestManager - -- (id)init -{ - if ((self = [super init])) - { - // For the sake of allowing both the legacy and the FinderSync extensions to work with the same - // client build, use the same server name including the Team ID even though we won't be sandboxed. - NSBundle *extBundle = [NSBundle bundleForClass:[self class]]; - // This was added to the bundle's Info.plist to get it from the build system - NSString *socketApiPrefix = [extBundle objectForInfoDictionaryKey:@"SocketApiPrefix"]; - NSString *serverName = [socketApiPrefix stringByAppendingString:@".socketApi"]; - // NSLog(@"OwnCloudFinderRequestManager serverName %@", serverName); - - _syncClientProxy = [[SyncClientProxy alloc] initWithDelegate:self serverName:serverName]; - - _registeredPathes = [[NSMutableDictionary alloc] init]; - _requestedPaths = [[NSMutableSet alloc] init]; - - _shareMenuTitle = nil; - - // The NSConnection will block until the distant object came back and this creates a loop hanging Finder. - // Start from a timer to have time to unwind the stack first. - [NSTimer scheduledTimerWithTimeInterval:0 target:_syncClientProxy selector:@selector(start) userInfo:nil repeats:NO]; - } - - return self; -} - -- (void)dealloc -{ - sharedInstance = nil; -} - -+ (OwnCloudFinderRequestManager*)sharedInstance -{ - @synchronized(self) - { - if (sharedInstance == nil) - { - sharedInstance = [[self alloc] init]; - } - } - - return sharedInstance; -} - -- (BOOL)isRegisteredPath:(NSString*)path isDirectory:(BOOL)isDir -{ - // check if the file in question is underneath a registered directory - NSArray *regPathes = [_registeredPathes allKeys]; - BOOL registered = NO; - - NSString* checkPath = [NSString stringWithString:path]; - if (isDir && ![checkPath hasSuffix:@"/"]) { - // append a slash - checkPath = [path stringByAppendingString:@"/"]; - } - - for( NSString *regPath in regPathes ) { - if( [checkPath hasPrefix:regPath]) { - // the path was registered - registered = YES; - break; - } - } - - return registered; -} - -- (void)askForIcon:(NSString*)path isDirectory:(BOOL)isDir -{ - if( [self isRegisteredPath:path isDirectory:isDir] ) { - [_requestedPaths addObject:path]; - [_syncClientProxy askForIcon:path isDirectory:isDir]; - } -} - -- (void)setResultForPath:(NSString*)path result:(NSString*)result -{ - // The client will broadcast all changes, do not fill the cache for paths that Finder didn't ask for. - if ([_requestedPaths containsObject:path]) { - [[OwnCloudFinderContentManager sharedInstance] setResultForPath:path result:result]; - } -} - -- (void)reFetchFileNameCacheForPath:(NSString*)path -{ - [_requestedPaths removeAllObjects]; - [[OwnCloudFinderContentManager sharedInstance] reFetchFileNameCacheForPath:path]; -} - -- (void)registerPath:(NSString*)path -{ - NSNumber *one = [NSNumber numberWithInt:1]; - [_registeredPathes setObject:one forKey:path]; - [[OwnCloudFinderContentManager sharedInstance] repaintAllWindows]; -} - -- (void)unregisterPath:(NSString*)path -{ - [_registeredPathes removeObjectForKey:path]; - [[OwnCloudFinderContentManager sharedInstance] repaintAllWindows]; -} - -- (void)setShareMenuTitle:(NSString*)title -{ - _shareMenuTitle = title; -} - -- (void)connectionDidDie -{ - // NSLog(@"Socket DISconnected! %@", [err localizedDescription]); - - // clear the registered paths. - _registeredPathes = [[NSMutableDictionary alloc] init]; - [_requestedPaths removeAllObjects]; - - // clear the caches in content manager - OwnCloudFinderContentManager *contentman = [OwnCloudFinderContentManager sharedInstance]; - [contentman clearFileNameCache]; - [contentman repaintAllWindows]; -} - -- (void)menuItemClicked:(NSDictionary*)actionDictionary -{ - // NSLog(@"RequestManager menuItemClicked %@", actionDictionary); - NSArray *filePaths = [actionDictionary valueForKey:@"files"]; - for (int i = 0; i < filePaths.count; i++) { - [_syncClientProxy askOnSocket:[filePaths objectAtIndex:i] query:@"SHARE"]; - } -} - -- (NSString*) shareItemTitle -{ - return _shareMenuTitle; -} - -@end diff --git a/shell_integration/MacOSX/common/SyncClientProxy.h b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/SyncClientProxy.h similarity index 100% rename from shell_integration/MacOSX/common/SyncClientProxy.h rename to shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/SyncClientProxy.h diff --git a/shell_integration/MacOSX/common/SyncClientProxy.m b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/SyncClientProxy.m similarity index 100% rename from shell_integration/MacOSX/common/SyncClientProxy.m rename to shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/SyncClientProxy.m diff --git a/shell_integration/MacOSX/OwnCloudFinderSync/OwnCloudFinderSync.xcodeproj/project.pbxproj b/shell_integration/MacOSX/OwnCloudFinderSync/OwnCloudFinderSync.xcodeproj/project.pbxproj index f72bfc2d1b..950ac20272 100644 --- a/shell_integration/MacOSX/OwnCloudFinderSync/OwnCloudFinderSync.xcodeproj/project.pbxproj +++ b/shell_integration/MacOSX/OwnCloudFinderSync/OwnCloudFinderSync.xcodeproj/project.pbxproj @@ -11,12 +11,12 @@ C2B573D21B1CD94B00303B36 /* main.m in Resources */ = {isa = PBXBuildFile; fileRef = C2B573B91B1CD91E00303B36 /* main.m */; }; C2B573DE1B1CD9CE00303B36 /* FinderSync.m in Sources */ = {isa = PBXBuildFile; fileRef = C2B573DD1B1CD9CE00303B36 /* FinderSync.m */; }; C2B573E21B1CD9CE00303B36 /* FinderSyncExt.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = C2B573D71B1CD9CE00303B36 /* FinderSyncExt.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - C2B573E91B1DA1FB00303B36 /* SyncClientProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = C2B573E81B1DA1FB00303B36 /* SyncClientProxy.m */; }; C2B573F31B1DAD6400303B36 /* error.iconset in Resources */ = {isa = PBXBuildFile; fileRef = C2B573EB1B1DAD6400303B36 /* error.iconset */; }; C2B573F41B1DAD6400303B36 /* ok_swm.iconset in Resources */ = {isa = PBXBuildFile; fileRef = C2B573EC1B1DAD6400303B36 /* ok_swm.iconset */; }; C2B573F51B1DAD6400303B36 /* ok.iconset in Resources */ = {isa = PBXBuildFile; fileRef = C2B573ED1B1DAD6400303B36 /* ok.iconset */; }; C2B573F71B1DAD6400303B36 /* sync.iconset in Resources */ = {isa = PBXBuildFile; fileRef = C2B573EF1B1DAD6400303B36 /* sync.iconset */; }; C2B573F91B1DAD6400303B36 /* warning.iconset in Resources */ = {isa = PBXBuildFile; fileRef = C2B573F11B1DAD6400303B36 /* warning.iconset */; }; + C2C932F01F0BFC6700C8BCB3 /* SyncClientProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = C2C932EF1F0BFC6700C8BCB3 /* SyncClientProxy.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -52,13 +52,13 @@ C2B573DB1B1CD9CE00303B36 /* FinderSyncExt.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = FinderSyncExt.entitlements; sourceTree = ""; }; C2B573DC1B1CD9CE00303B36 /* FinderSync.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FinderSync.h; sourceTree = ""; }; C2B573DD1B1CD9CE00303B36 /* FinderSync.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FinderSync.m; sourceTree = ""; }; - C2B573E71B1DA1FB00303B36 /* SyncClientProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SyncClientProxy.h; sourceTree = ""; }; - C2B573E81B1DA1FB00303B36 /* SyncClientProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SyncClientProxy.m; sourceTree = ""; }; C2B573EB1B1DAD6400303B36 /* error.iconset */ = {isa = PBXFileReference; lastKnownFileType = folder.iconset; name = error.iconset; path = ../../icons/nopadding/error.iconset; sourceTree = SOURCE_ROOT; }; C2B573EC1B1DAD6400303B36 /* ok_swm.iconset */ = {isa = PBXFileReference; lastKnownFileType = folder.iconset; name = ok_swm.iconset; path = ../../icons/nopadding/ok_swm.iconset; sourceTree = SOURCE_ROOT; }; C2B573ED1B1DAD6400303B36 /* ok.iconset */ = {isa = PBXFileReference; lastKnownFileType = folder.iconset; name = ok.iconset; path = ../../icons/nopadding/ok.iconset; sourceTree = SOURCE_ROOT; }; C2B573EF1B1DAD6400303B36 /* sync.iconset */ = {isa = PBXFileReference; lastKnownFileType = folder.iconset; name = sync.iconset; path = ../../icons/nopadding/sync.iconset; sourceTree = SOURCE_ROOT; }; C2B573F11B1DAD6400303B36 /* warning.iconset */ = {isa = PBXFileReference; lastKnownFileType = folder.iconset; name = warning.iconset; path = ../../icons/nopadding/warning.iconset; sourceTree = SOURCE_ROOT; }; + C2C932EE1F0BFC6700C8BCB3 /* SyncClientProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SyncClientProxy.h; sourceTree = ""; }; + C2C932EF1F0BFC6700C8BCB3 /* SyncClientProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SyncClientProxy.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -82,7 +82,6 @@ C2B573941B1CD88000303B36 = { isa = PBXGroup; children = ( - C2B573E61B1DA1FB00303B36 /* common */, C2B573B31B1CD91E00303B36 /* desktopclient */, C2B573D81B1CD9CE00303B36 /* FinderSyncExt */, C2B573B21B1CD91E00303B36 /* Products */, @@ -118,6 +117,8 @@ C2B573D81B1CD9CE00303B36 /* FinderSyncExt */ = { isa = PBXGroup; children = ( + C2C932EE1F0BFC6700C8BCB3 /* SyncClientProxy.h */, + C2C932EF1F0BFC6700C8BCB3 /* SyncClientProxy.m */, C2B573DC1B1CD9CE00303B36 /* FinderSync.h */, C2B573DD1B1CD9CE00303B36 /* FinderSync.m */, C2B573D91B1CD9CE00303B36 /* Supporting Files */, @@ -139,16 +140,6 @@ name = "Supporting Files"; sourceTree = ""; }; - C2B573E61B1DA1FB00303B36 /* common */ = { - isa = PBXGroup; - children = ( - C2B573E71B1DA1FB00303B36 /* SyncClientProxy.h */, - C2B573E81B1DA1FB00303B36 /* SyncClientProxy.m */, - ); - name = common; - path = ../common; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -284,7 +275,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - C2B573E91B1DA1FB00303B36 /* SyncClientProxy.m in Sources */, + C2C932F01F0BFC6700C8BCB3 /* SyncClientProxy.m in Sources */, C2B573DE1B1CD9CE00303B36 /* FinderSync.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/shell_integration/MacOSX/OwnCloudInjector/English.lproj/InfoPlist.strings b/shell_integration/MacOSX/OwnCloudInjector/English.lproj/InfoPlist.strings deleted file mode 100644 index 88f65cf6ea..0000000000 --- a/shell_integration/MacOSX/OwnCloudInjector/English.lproj/InfoPlist.strings +++ /dev/null @@ -1,2 +0,0 @@ -/* Localized versions of Info.plist keys */ - diff --git a/shell_integration/MacOSX/OwnCloudInjector/Info.plist b/shell_integration/MacOSX/OwnCloudInjector/Info.plist deleted file mode 100644 index ec13d6497d..0000000000 --- a/shell_integration/MacOSX/OwnCloudInjector/Info.plist +++ /dev/null @@ -1,59 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - com.owncloud.injector - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - OwnCloudInjector - CFBundlePackageType - osax - CFBundleShortVersionString - 1.0.2 - CFBundleSignature - OWNC - CFBundleVersion - 1.0.2 - OSAScriptingDefinition - OwnCloudInjector.sdef - OSAXHandlers - - Events - - OWNClded - - Context - Process - Handler - HandleLoadedEvent - ThreadSafe - - - OWNCload - - Context - Process - Handler - HandleLoadEvent - ThreadSafe - - - OWNCunld - - Context - Process - Handler - HandleUnloadEvent - ThreadSafe - - - - - - diff --git a/shell_integration/MacOSX/OwnCloudInjector/LNStandardVersionComparator.h b/shell_integration/MacOSX/OwnCloudInjector/LNStandardVersionComparator.h deleted file mode 100644 index 224b81efdf..0000000000 --- a/shell_integration/MacOSX/OwnCloudInjector/LNStandardVersionComparator.h +++ /dev/null @@ -1,35 +0,0 @@ -// -// LNStandardVersionComparator.h -// Sparkle -// -// Created by Andy Matuschak on 12/21/07. -// Copyright 2007 Andy Matuschak. All rights reserved. -// - -#ifndef LNSTANDARDVERSIONCOMPARATOR_H -#define LNSTANDARDVERSIONCOMPARATOR_H - -#import "LNVersionComparisonProtocol.h" - -/*! - @class - @abstract Sparkle's default version comparator. - @discussion This comparator is adapted from MacPAD, by Kevin Ballard. It's "dumb" in that it does essentially string comparison, in components split by character type. - */ -@interface LNStandardVersionComparator : NSObject { } - -/*! - @method - @abstract Returns a singleton instance of the comparator. - */ -+(LNStandardVersionComparator*)defaultComparator; - -/*! - @method - @abstract Compares version strings through textual analysis. - @discussion See the implementation for more details. - */ --(NSComparisonResult)compareVersion:(NSString*)versionA toVersion:(NSString*)versionB; -@end - -#endif diff --git a/shell_integration/MacOSX/OwnCloudInjector/LNStandardVersionComparator.m b/shell_integration/MacOSX/OwnCloudInjector/LNStandardVersionComparator.m deleted file mode 100644 index 5d7e8d8829..0000000000 --- a/shell_integration/MacOSX/OwnCloudInjector/LNStandardVersionComparator.m +++ /dev/null @@ -1,158 +0,0 @@ -// -// LNStandardVersionComparator.m -// Sparkle -// -// Created by Andy Matuschak on 12/21/07. -// Copyright 2007 Andy Matuschak. All rights reserved. -// - -#import -#import "LNStandardVersionComparator.h" - -@implementation LNStandardVersionComparator - -+(LNStandardVersionComparator*) defaultComparator { - static LNStandardVersionComparator* defaultComparator = nil; - - if (defaultComparator == nil) defaultComparator = [[LNStandardVersionComparator alloc] init]; - return defaultComparator; -} - -typedef enum { - kNumberType, - kStringType, - kPeriodType -} SUCharacterType; - --(SUCharacterType) typeOfCharacter:(NSString*)character { - if ([character isEqualToString:@"."]) { - return kPeriodType; - } else if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:[character characterAtIndex:0]]) { - return kNumberType; - } else { - return kStringType; - } -} - --(NSArray*) splitVersionString:(NSString*)version { - NSString* character; - NSMutableString* s; - NSUInteger i, n; - SUCharacterType oldType, newType; - NSMutableArray* parts = [NSMutableArray array]; - - if ([version length] == 0) { - // Nothing to do here - return parts; - } - s = [[[version substringToIndex:1] mutableCopy] autorelease]; - oldType = [self typeOfCharacter:s]; - n = [version length] - 1; - for (i = 1; i <= n; ++i) { - character = [version substringWithRange:NSMakeRange(i, 1)]; - newType = [self typeOfCharacter:character]; - if ((oldType != newType) || (oldType == kPeriodType)) { - // We've reached a new segment - NSString* aPart = [[[NSString alloc] initWithString:s] autorelease]; - [parts addObject:aPart]; - [s setString:character]; - } else { - // Add character to string and continue - [s appendString:character]; - } - oldType = newType; - } - - // Add the last part onto the array - [parts addObject:[NSString stringWithString:s]]; - return parts; -} - --(NSComparisonResult) compareVersion:(NSString*)versionA toVersion:(NSString*)versionB; -{ - NSArray* partsA = [self splitVersionString:versionA]; - NSArray* partsB = [self splitVersionString:versionB]; - - NSString* partA, * partB; - NSUInteger i, n; - int intA, intB; - SUCharacterType typeA, typeB; - - n = MIN([partsA count], [partsB count]); - for (i = 0; i < n; ++i) { - partA = [partsA objectAtIndex:i]; - partB = [partsB objectAtIndex:i]; - - typeA = [self typeOfCharacter:partA]; - typeB = [self typeOfCharacter:partB]; - - // Compare types - if (typeA == typeB) { - // Same type; we can compare - if (typeA == kNumberType) { - intA = [partA intValue]; - intB = [partB intValue]; - if (intA > intB) { - return NSOrderedDescending; - } else if (intA < intB) { - return NSOrderedAscending; - } - } else if (typeA == kStringType) { - NSComparisonResult result = [partA compare:partB]; - if (result != NSOrderedSame) { - return result; - } - } - } else { - // Not the same type? Now we have to do some validity checking - if ((typeA != kStringType) && (typeB == kStringType)) { - // typeA wins - return NSOrderedDescending; - } else if ((typeA == kStringType) && (typeB != kStringType)) { - // typeB wins - return NSOrderedAscending; - } else { - // One is a number and the other is a period. The period is invalid - if (typeA == kNumberType) { - return NSOrderedDescending; - } else { - return NSOrderedAscending; - } - } - } - } - // The versions are equal up to the point where they both still have parts - // Lets check to see if one is larger than the other - if ([partsA count] != [partsB count]) { - // Yep. Lets get the next part of the larger - // n holds the index of the part we want. - NSString* missingPart; - SUCharacterType missingType; - NSComparisonResult shorterResult, largerResult; - - if ([partsA count] > [partsB count]) { - missingPart = [partsA objectAtIndex:n]; - shorterResult = NSOrderedAscending; - largerResult = NSOrderedDescending; - } else { - missingPart = [partsB objectAtIndex:n]; - shorterResult = NSOrderedDescending; - largerResult = NSOrderedAscending; - } - - missingType = [self typeOfCharacter:missingPart]; - // Check the type - if (missingType == kStringType) { - // It's a string. Shorter version wins - return shorterResult; - } else { - // It's a number/period. Larger version wins - return largerResult; - } - } - - // The 2 strings are identical - return NSOrderedSame; -} - -@end diff --git a/shell_integration/MacOSX/OwnCloudInjector/LNVersionComparisonProtocol.h b/shell_integration/MacOSX/OwnCloudInjector/LNVersionComparisonProtocol.h deleted file mode 100644 index 82686ee600..0000000000 --- a/shell_integration/MacOSX/OwnCloudInjector/LNVersionComparisonProtocol.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// LNVersionComparisonProtocol.h -// Sparkle -// -// Created by Andy Matuschak on 12/21/07. -// Copyright 2007 Andy Matuschak. All rights reserved. -// - -#ifndef LNVERSIONCOMPARISONPROTOCOL_H -#define LNVERSIONCOMPARISONPROTOCOL_H - -/*! - @protocol - @abstract Implement this protocol to provide version comparison facilities for Sparkle. - */ -@protocol LNVersionComparison - -/*! - @method - @abstract An abstract method to compare two version strings. - @discussion Should return NSOrderedAscending if b > a, NSOrderedDescending if b < a, and NSOrderedSame if they are equivalent. - */ --(NSComparisonResult)compareVersion:(NSString*)versionA toVersion:(NSString*)versionB; - -@end - -#endif diff --git a/shell_integration/MacOSX/OwnCloudInjector/OwnCloudInjector.m b/shell_integration/MacOSX/OwnCloudInjector/OwnCloudInjector.m deleted file mode 100644 index 97a3f2893b..0000000000 --- a/shell_integration/MacOSX/OwnCloudInjector/OwnCloudInjector.m +++ /dev/null @@ -1,270 +0,0 @@ -#import - -#import "LNStandardVersionComparator.h" - -#define EXPORT __attribute__((visibility("default"))) - -#define WAIT_FOR_APPLE_EVENT_TO_ENTER_HANDLER_IN_SECONDS 1.0 -#define FINDER_MIN_TESTED_VERSION @"10.7" -#define FINDER_MAX_TESTED_VERSION @"10.8.5" -#define LIFERAYNATIVITY_INJECTED_NOTIFICATION @"SyncStateInjectedNotification" - -EXPORT OSErr HandleLoadEvent(const AppleEvent* ev, AppleEvent* reply, long refcon); - -static NSString* globalLock = @"I'm the global lock to prevent concurrent handler executions"; - -// SIMBL-compatible interface -@interface OwnCloudShell : NSObject { } --(void) install; --(void) uninstall; -@end - -// just a dummy class for locating our bundle -@interface OwnCloudInjector : NSObject { } -@end - -@implementation OwnCloudInjector { } -@end - -static bool liferayNativityLoaded = false; -static NSString* liferayNativityBundleName = @"SyncStateFinder"; - -typedef struct { - NSString* location; -} configuration; - -static OSErr AEPutParamString(AppleEvent* event, AEKeyword keyword, NSString* string) { - UInt8* textBuf; - CFIndex length, maxBytes, actualBytes; - - length = CFStringGetLength((CFStringRef)string); - maxBytes = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8); - textBuf = malloc(maxBytes); - if (textBuf) { - CFStringGetBytes((CFStringRef)string, CFRangeMake(0, length), kCFStringEncodingUTF8, 0, true, (UInt8*)textBuf, maxBytes, &actualBytes); - OSErr err = AEPutParamPtr(event, keyword, typeUTF8Text, textBuf, actualBytes); - free(textBuf); - return err; - } else { - return memFullErr; - } -} - -static void reportError(AppleEvent* reply, NSString* msg) { - NSLog(@"LiferayNativityInjector: %@", msg); - AEPutParamString(reply, keyErrorString, msg); -} - -typedef enum { - InvalidBundleType, - LiferayNativityBundleType, -} LNBundleType; - -static OSErr loadBundle(LNBundleType type, AppleEvent* reply, long refcon) { - bool isLoaded = false; - NSString* bundleName = nil; - NSString* targetAppName = nil; - NSString* versionCheckKey = nil; - NSString* maxVersion = nil; - NSString* minVersion = nil; - - switch (type) { - case LiferayNativityBundleType: - isLoaded = liferayNativityLoaded; - bundleName = liferayNativityBundleName; - targetAppName = @"Finder"; - versionCheckKey = @"LiferayNativityFinderVersionCheck"; - maxVersion = FINDER_MAX_TESTED_VERSION; - minVersion = FINDER_MIN_TESTED_VERSION; - break; - default: - NSLog(@"SyncStateInjector: Failed to load bundle for type %d", type); - return 8; - - break; - } - - if (isLoaded) { - // NSLog(@"SyncStateInjector: %@ already loaded.", bundleName); - return noErr; - } - - @try { - NSBundle* mainBundle = [NSBundle mainBundle]; - if (!mainBundle) { - reportError(reply, [NSString stringWithFormat:@"Unable to locate main %@ bundle!", targetAppName]); - return 4; - } - - NSString* mainVersion = [mainBundle objectForInfoDictionaryKey:@"CFBundleVersion"]; - if (!mainVersion || ![mainVersion isKindOfClass:[NSString class]]) { - reportError(reply, [NSString stringWithFormat:@"Unable to determine %@ version!", targetAppName]); - return 5; - } - - // future compatibility check - if (type == LiferayNativityBundleType) { - // in Dock we cannot use NSAlert and similar UI stuff - this would hang the Dock process and cause 100% CPU load - NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; - if ([defaults boolForKey:versionCheckKey]) { - LNStandardVersionComparator* comparator = [LNStandardVersionComparator defaultComparator]; - if (([comparator compareVersion:mainVersion toVersion:maxVersion] == NSOrderedDescending) || - ([comparator compareVersion:mainVersion toVersion:minVersion] == NSOrderedAscending)) { - NSAlert* alert = [NSAlert new]; - [alert setMessageText:[NSString stringWithFormat:@"You have %@ version %@", targetAppName, mainVersion]]; - [alert setInformativeText:[NSString stringWithFormat:@"But %@ was properly tested only with %@ versions in range %@ - %@\n\nYou have probably updated your system and %@ version got bumped by Apple developers.\n\nYou may expect a new LiferayNativity release soon.", bundleName, targetAppName, targetAppName, minVersion, maxVersion]]; - [alert setShowsSuppressionButton:YES]; - [alert addButtonWithTitle:@"Launch LiferayNativity anyway"]; - [alert addButtonWithTitle:@"Cancel"]; - NSInteger res = [alert runModal]; - if ([[alert suppressionButton] state] == NSOnState) { - [defaults setBool:NO forKey:versionCheckKey]; - } - if (res != NSAlertFirstButtonReturn) { - // cancel - return noErr; - } - } - } - } - - NSBundle* liferayNativityInjectorBundle = [NSBundle bundleForClass:[OwnCloudInjector class]]; - NSString* liferayNativityLocation = [liferayNativityInjectorBundle pathForResource:bundleName ofType:@"bundle"]; - NSBundle* pluginBundle = [NSBundle bundleWithPath:liferayNativityLocation]; - if (!pluginBundle) { - reportError(reply, [NSString stringWithFormat:@"Unable to create bundle from path: %@ [%@]", liferayNativityLocation, liferayNativityInjectorBundle]); - return 2; - } - - NSError* error; - if (![pluginBundle loadAndReturnError:&error]) { - reportError(reply, [NSString stringWithFormat:@"Unable to load bundle from path: %@ error: %@", liferayNativityLocation, [error localizedDescription]]); - return 6; - } - - Class principalClass = [pluginBundle principalClass]; - if (!principalClass) { - reportError(reply, [NSString stringWithFormat:@"Unable to retrieve principalClass for bundle: %@", pluginBundle]); - return 3; - } - id principalClassObject = NSClassFromString(NSStringFromClass(principalClass)); - if ([principalClassObject respondsToSelector:@selector(install)]) { - // NSLog(@"SyncStateInjector: Installing %@ ...", bundleName); - [principalClassObject install]; - } - - return noErr; - } @catch (NSException* exception) { - reportError(reply, [NSString stringWithFormat:@"Failed to load %@ with exception: %@", bundleName, exception]); - } - - return 1; -} - -static LNBundleType mainBundleType(AppleEvent* reply) { - @try { - NSBundle* mainBundle = [NSBundle mainBundle]; - if (!mainBundle) { - reportError(reply, [NSString stringWithFormat:@"Unable to locate main bundle!"]); - return InvalidBundleType; - } - - if ([[mainBundle bundleIdentifier] isEqualToString:@"com.apple.finder"]) { - return LiferayNativityBundleType; - } - } @catch (NSException* exception) { - reportError(reply, [NSString stringWithFormat:@"Failed to load main bundle with exception: %@", exception]); - } - - return InvalidBundleType; -} - -EXPORT OSErr HandleLoadEvent(const AppleEvent* ev, AppleEvent* reply, long refcon) { - @synchronized(globalLock) { - @autoreleasepool { - NSBundle* injectorBundle = [NSBundle bundleForClass:[OwnCloudInjector class]]; - NSString* injectorVersion = [injectorBundle objectForInfoDictionaryKey:@"CFBundleVersion"]; - - if (!injectorVersion || ![injectorVersion isKindOfClass:[NSString class]]) { - reportError(reply, [NSString stringWithFormat:@"Unable to determine SyncStateInjector version!"]); - return 7; - } - - @try { - OSErr err = loadBundle(mainBundleType(reply), reply, refcon); - - if (err != noErr) - { - return err; - } - - pid_t pid = [[NSProcessInfo processInfo] processIdentifier]; - - [[NSDistributedNotificationCenter defaultCenter]postNotificationName:LIFERAYNATIVITY_INJECTED_NOTIFICATION object:[[NSBundle mainBundle]bundleIdentifier] userInfo:@{@"pid": @(pid)}]; - - liferayNativityLoaded = true; - - return noErr; - } @catch (NSException* exception) { - reportError(reply, [NSString stringWithFormat:@"Failed to load OwnCloudFinder with exception: %@", exception]); - } - - return 1; - } - } -} - -EXPORT OSErr HandleLoadedEvent(const AppleEvent* ev, AppleEvent* reply, long refcon) { - @synchronized(globalLock) { - @autoreleasepool { - LNBundleType type = mainBundleType(reply); - if ((type == LiferayNativityBundleType) && liferayNativityLoaded) { - return noErr; - } - reportError(reply, @"LiferayNativity not loaded"); - return 1; - } - } -} - -EXPORT OSErr HandleUnloadEvent(const AppleEvent* ev, AppleEvent* reply, long refcon) { - @synchronized(globalLock) { - @autoreleasepool { - @try { - if (!liferayNativityLoaded) { - // NSLog(@"SyncStateInjector: not loaded."); - return noErr; - } - - NSString* bundleName = liferayNativityBundleName; - - NSBundle* liferayNativityInjectorBundle = [NSBundle bundleForClass:[OwnCloudInjector class]]; - NSString* liferayNativityLocation = [liferayNativityInjectorBundle pathForResource:bundleName ofType:@"bundle"]; - NSBundle* pluginBundle = [NSBundle bundleWithPath:liferayNativityLocation]; - if (!pluginBundle) { - reportError(reply, [NSString stringWithFormat:@"Unable to create bundle from path: %@ [%@]", liferayNativityLocation, liferayNativityInjectorBundle]); - return 2; - } - - Class principalClass = [pluginBundle principalClass]; - if (!principalClass) { - reportError(reply, [NSString stringWithFormat:@"Unable to retrieve principalClass for bundle: %@", pluginBundle]); - return 3; - } - id principalClassObject = NSClassFromString(NSStringFromClass(principalClass)); - if ([principalClassObject respondsToSelector:@selector(uninstall)]) { - // NSLog(@"SyncStateInjector: Uninstalling %@ ...", bundleName); - [principalClassObject uninstall]; - } - - liferayNativityLoaded = false; - - return noErr; - } @catch (NSException* exception) { - reportError(reply, [NSString stringWithFormat:@"Failed to unload OwnCloudFinder with exception: %@", exception]); - } - - return 1; - } - } -} \ No newline at end of file diff --git a/shell_integration/MacOSX/OwnCloudInjector/OwnCloudInjector.sdef b/shell_integration/MacOSX/OwnCloudInjector/OwnCloudInjector.sdef deleted file mode 100644 index 3d4c5fdd4e..0000000000 --- a/shell_integration/MacOSX/OwnCloudInjector/OwnCloudInjector.sdef +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/shell_integration/MacOSX/OwnCloudInjector/OwnCloudInjector.xcodeproj/project.pbxproj b/shell_integration/MacOSX/OwnCloudInjector/OwnCloudInjector.xcodeproj/project.pbxproj deleted file mode 100644 index 36dd492824..0000000000 --- a/shell_integration/MacOSX/OwnCloudInjector/OwnCloudInjector.xcodeproj/project.pbxproj +++ /dev/null @@ -1,269 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 0B36CB92182461A10039B237 /* SyncStateFinder.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 0B36CB91182461A10039B237 /* SyncStateFinder.bundle */; }; - 0BD9C38E1778EF450094CF5D /* license.txt in Resources */ = {isa = PBXBuildFile; fileRef = 0BD9C38D1778EF450094CF5D /* license.txt */; }; - 8D576314048677EA00EA77CD /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0AA1909FFE8422F4C02AAC07 /* CoreFoundation.framework */; }; - 8D5B49A804867FD3000E48DA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8D5B49A704867FD3000E48DA /* InfoPlist.strings */; }; - D6ACBEA2117B7D5600F6691C /* OwnCloudInjector.m in Sources */ = {isa = PBXBuildFile; fileRef = D6ACBE9E117B7D5600F6691C /* OwnCloudInjector.m */; }; - D6ACBEA3117B7D5600F6691C /* LNStandardVersionComparator.m in Sources */ = {isa = PBXBuildFile; fileRef = D6ACBEA0117B7D5600F6691C /* LNStandardVersionComparator.m */; }; - D6ACBEA5117B7D6100F6691C /* OwnCloudInjector.sdef in Resources */ = {isa = PBXBuildFile; fileRef = D6ACBEA4117B7D6100F6691C /* OwnCloudInjector.sdef */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 089C167EFE841241C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; - 0AA1909FFE8422F4C02AAC07 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = ""; }; - 0B36CB91182461A10039B237 /* SyncStateFinder.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; path = SyncStateFinder.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; - 0BD9C38D1778EF450094CF5D /* license.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = license.txt; sourceTree = ""; }; - 8D576317048677EA00EA77CD /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - D60A992314CE37030061AD6D /* SyncStateFinder.osax */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SyncStateFinder.osax; sourceTree = BUILT_PRODUCTS_DIR; }; - D6ACBE9E117B7D5600F6691C /* OwnCloudInjector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OwnCloudInjector.m; sourceTree = ""; }; - D6ACBE9F117B7D5600F6691C /* LNVersionComparisonProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LNVersionComparisonProtocol.h; sourceTree = ""; }; - D6ACBEA0117B7D5600F6691C /* LNStandardVersionComparator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LNStandardVersionComparator.m; sourceTree = ""; }; - D6ACBEA1117B7D5600F6691C /* LNStandardVersionComparator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LNStandardVersionComparator.h; sourceTree = ""; }; - D6ACBEA4117B7D6100F6691C /* OwnCloudInjector.sdef */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = OwnCloudInjector.sdef; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 8D576313048677EA00EA77CD /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 8D576314048677EA00EA77CD /* CoreFoundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 089C166AFE841209C02AAC07 /* TotalFinder-osax */ = { - isa = PBXGroup; - children = ( - 08FB77AFFE84173DC02AAC07 /* Source */, - 089C167CFE841241C02AAC07 /* Resources */, - 089C1671FE841209C02AAC07 /* Dependencies */, - D60A992414CE37030061AD6D /* Products */, - ); - indentWidth = 2; - name = "SyncStateFinder-osax"; - sourceTree = ""; - tabWidth = 2; - usesTabs = 0; - }; - 089C1671FE841209C02AAC07 /* Dependencies */ = { - isa = PBXGroup; - children = ( - 0AA1909FFE8422F4C02AAC07 /* CoreFoundation.framework */, - ); - name = Dependencies; - sourceTree = ""; - }; - 089C167CFE841241C02AAC07 /* Resources */ = { - isa = PBXGroup; - children = ( - 0B36CB91182461A10039B237 /* SyncStateFinder.bundle */, - D6ACBEA4117B7D6100F6691C /* OwnCloudInjector.sdef */, - 8D576317048677EA00EA77CD /* Info.plist */, - 8D5B49A704867FD3000E48DA /* InfoPlist.strings */, - 0BD9C38D1778EF450094CF5D /* license.txt */, - ); - name = Resources; - sourceTree = ""; - }; - 08FB77AFFE84173DC02AAC07 /* Source */ = { - isa = PBXGroup; - children = ( - D6ACBE9E117B7D5600F6691C /* OwnCloudInjector.m */, - D6ACBE9F117B7D5600F6691C /* LNVersionComparisonProtocol.h */, - D6ACBEA0117B7D5600F6691C /* LNStandardVersionComparator.m */, - D6ACBEA1117B7D5600F6691C /* LNStandardVersionComparator.h */, - ); - name = Source; - sourceTree = ""; - }; - D60A992414CE37030061AD6D /* Products */ = { - isa = PBXGroup; - children = ( - D60A992314CE37030061AD6D /* SyncStateFinder.osax */, - ); - name = Products; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 8D57630D048677EA00EA77CD /* SyncStateFinder.osax */ = { - isa = PBXNativeTarget; - buildConfigurationList = 1DEB911A08733D790010E9CD /* Build configuration list for PBXNativeTarget "SyncStateFinder.osax" */; - buildPhases = ( - 8D57630F048677EA00EA77CD /* Resources */, - 8D576311048677EA00EA77CD /* Sources */, - 8D576313048677EA00EA77CD /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = SyncStateFinder.osax; - productInstallPath = "$(HOME)/Library/Bundles"; - productName = "SyncStateFinder-osax"; - productReference = D60A992314CE37030061AD6D /* SyncStateFinder.osax */; - productType = "com.apple.product-type.bundle"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 089C1669FE841209C02AAC07 /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = YES; - LastUpgradeCheck = 0460; - ORGANIZATIONNAME = BinaryAge; - }; - buildConfigurationList = 1DEB911E08733D790010E9CD /* Build configuration list for PBXProject "OwnCloudInjector" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 1; - knownRegions = ( - en, - English, - ); - mainGroup = 089C166AFE841209C02AAC07 /* TotalFinder-osax */; - productRefGroup = D60A992414CE37030061AD6D /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 8D57630D048677EA00EA77CD /* SyncStateFinder.osax */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 8D57630F048677EA00EA77CD /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 0B36CB92182461A10039B237 /* SyncStateFinder.bundle in Resources */, - 8D5B49A804867FD3000E48DA /* InfoPlist.strings in Resources */, - D6ACBEA5117B7D6100F6691C /* OwnCloudInjector.sdef in Resources */, - 0BD9C38E1778EF450094CF5D /* license.txt in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 8D576311048677EA00EA77CD /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D6ACBEA2117B7D5600F6691C /* OwnCloudInjector.m in Sources */, - D6ACBEA3117B7D5600F6691C /* LNStandardVersionComparator.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 8D5B49A704867FD3000E48DA /* InfoPlist.strings */ = { - isa = PBXVariantGroup; - children = ( - 089C167EFE841241C02AAC07 /* English */, - ); - name = InfoPlist.strings; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 1DEB911B08733D790010E9CD /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; - COMBINE_HIDPI_IMAGES = YES; - GCC_DYNAMIC_NO_PIC = NO; - INFOPLIST_FILE = Info.plist; - OTHER_LDFLAGS = ( - "-framework", - Foundation, - "-framework", - AppKit, - ); - PRODUCT_NAME = SyncStateFinder; - SKIP_INSTALL = YES; - WRAPPER_EXTENSION = osax; - }; - name = Debug; - }; - 1DEB911C08733D790010E9CD /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; - COMBINE_HIDPI_IMAGES = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - INFOPLIST_FILE = Info.plist; - OTHER_LDFLAGS = ( - "-framework", - Foundation, - "-framework", - AppKit, - ); - PRODUCT_NAME = SyncStateFinder; - WRAPPER_EXTENSION = osax; - }; - name = Release; - }; - 1DEB911F08733D790010E9CD /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CONFIGURATION_TEMP_DIR = "$(PROJECT_TEMP_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; - COPY_PHASE_STRIP = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_SYMBOLS_PRIVATE_EXTERN = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.7; - SDKROOT = macosx; - }; - name = Debug; - }; - 1DEB912008733D790010E9CD /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - GCC_SYMBOLS_PRIVATE_EXTERN = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.7; - SDKROOT = macosx; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 1DEB911A08733D790010E9CD /* Build configuration list for PBXNativeTarget "SyncStateFinder.osax" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 1DEB911B08733D790010E9CD /* Debug */, - 1DEB911C08733D790010E9CD /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 1DEB911E08733D790010E9CD /* Build configuration list for PBXProject "OwnCloudInjector" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 1DEB911F08733D790010E9CD /* Debug */, - 1DEB912008733D790010E9CD /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 089C1669FE841209C02AAC07 /* Project object */; -} diff --git a/shell_integration/MacOSX/OwnCloudInjector/OwnCloudInjector.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/shell_integration/MacOSX/OwnCloudInjector/OwnCloudInjector.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 25ab3b4240..0000000000 --- a/shell_integration/MacOSX/OwnCloudInjector/OwnCloudInjector.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/shell_integration/MacOSX/OwnCloudInjector/OwnCloudInjector.xcodeproj/xcshareddata/xcschemes/SyncStateFinder.osax.xcscheme b/shell_integration/MacOSX/OwnCloudInjector/OwnCloudInjector.xcodeproj/xcshareddata/xcschemes/SyncStateFinder.osax.xcscheme deleted file mode 100644 index ad6157683d..0000000000 --- a/shell_integration/MacOSX/OwnCloudInjector/OwnCloudInjector.xcodeproj/xcshareddata/xcschemes/SyncStateFinder.osax.xcscheme +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/shell_integration/MacOSX/OwnCloudInjector/OwnCloudInjector.xcodeproj/xcuserdata/guruz.xcuserdatad/xcschemes/xcschememanagement.plist b/shell_integration/MacOSX/OwnCloudInjector/OwnCloudInjector.xcodeproj/xcuserdata/guruz.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 2f490c9952..0000000000 --- a/shell_integration/MacOSX/OwnCloudInjector/OwnCloudInjector.xcodeproj/xcuserdata/guruz.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,14 +0,0 @@ - - - - - SuppressBuildableAutocreation - - 8D57630D048677EA00EA77CD - - primary - - - - - diff --git a/shell_integration/MacOSX/OwnCloudInjector/license.txt b/shell_integration/MacOSX/OwnCloudInjector/license.txt deleted file mode 100644 index 581d9194fd..0000000000 --- a/shell_integration/MacOSX/OwnCloudInjector/license.txt +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2010-2013, BinaryAge Limited -Contributors: https://github.com/binaryage/totalfinder-osax/contributors -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Antonin Hildebrand nor the - names of other contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY BINARYAGE LIMITED ``AS IS'' AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL Antonin Hildebrand BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/shell_integration/MacOSX/check.scpt b/shell_integration/MacOSX/check.scpt deleted file mode 100644 index 2f111cc435..0000000000 --- a/shell_integration/MacOSX/check.scpt +++ /dev/null @@ -1,9 +0,0 @@ -tell application "Finder" - try - «event OWNClded» - set the result to 0 - on error msg number code - set the result to code - end try -end tell - diff --git a/shell_integration/MacOSX/deploy.sh b/shell_integration/MacOSX/deploy.sh deleted file mode 100644 index ebeeba25e4..0000000000 --- a/shell_integration/MacOSX/deploy.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh - -echo "Not used anymore, please do (from build dir) (this is <= 10.9 only)" -echo sudo cp -r ./shell_integration/MacOSX/Release/SyncStateFinder.osax /Library/ScriptingAdditions/ -echo killall Finder -exit 1 - -SELFPATH=`dirname $0` -# osascript $SELFPATH/unload.scpt - -sudo rm -rf /Library/ScriptingAdditions/SyncStateFinder.osax -# Klaas' machine -OSAXDIR=$HOME/Library/Developer/Xcode/DerivedData/OwnCloud-*/Build/Products/Debug/SyncStateFinder.osax -[ -d $OSAXDIR ] ||OSAXDIR=$HOME/Library/Developer/Xcode/DerivedData/OwnCloud-*/Build/Intermediates/ArchiveIntermediates/SyncStateFinder.osax/IntermediateBuildFilesPath/UninstalledProducts/SyncStateFinder.osax - -# Markus' machine -[ -d $OSAXDIR ] || echo "OSAX does not exist" -[ -d $OSAXDIR ] && sudo cp -rv $OSAXDIR /Library/ScriptingAdditions/ - -sudo killall Finder -sleep 1 -osascript $SELFPATH/load.scpt -osascript $SELFPATH/check.scpt - diff --git a/shell_integration/MacOSX/load.scpt b/shell_integration/MacOSX/load.scpt deleted file mode 100644 index 823d2b42a6..0000000000 --- a/shell_integration/MacOSX/load.scpt +++ /dev/null @@ -1,6 +0,0 @@ -tell application "Finder" - try - «event OWNCload» - end try -end tell - diff --git a/shell_integration/MacOSX/loadPlugin.sh b/shell_integration/MacOSX/loadPlugin.sh deleted file mode 100755 index 7fd14e2ea2..0000000000 --- a/shell_integration/MacOSX/loadPlugin.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -osascript -e 'tell application "Finder" \ - try \ - «event OWNCload» \ - end try \ - end tell' - diff --git a/shell_integration/MacOSX/unload.scpt b/shell_integration/MacOSX/unload.scpt deleted file mode 100644 index 620c1a053d..0000000000 --- a/shell_integration/MacOSX/unload.scpt +++ /dev/null @@ -1,6 +0,0 @@ -tell application "Finder" - try - «event OWNCunld» - end try -end tell - diff --git a/src/gui/owncloudgui.cpp b/src/gui/owncloudgui.cpp index db005f4227..38d1caff92 100644 --- a/src/gui/owncloudgui.cpp +++ b/src/gui/owncloudgui.cpp @@ -108,48 +108,6 @@ ownCloudGui::ownCloudGui(Application *parent) SLOT(slotShowOptionalTrayMessage(QString, QString))); connect(Logger::instance(), SIGNAL(guiMessage(QString, QString)), SLOT(slotShowGuiMessage(QString, QString))); - - setupOverlayIcons(); -} - -// Use this to do platform specific code to make overlay icons appear -// in the gui -// MacOSX: perform a AppleScript code piece to load the Finder Plugin. - - -void ownCloudGui::setupOverlayIcons() -{ -#ifdef Q_OS_MAC - // Make sure that we only send the load event to the legacy plugin when - // using OS X <= 10.9 since 10.10 starts using the new FinderSync one. - if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_10) { - const QLatin1String finderExtension("/Library/ScriptingAdditions/SyncStateFinder.osax"); - if (QFile::exists(finderExtension)) { - QString aScript = QString::fromUtf8("tell application \"Finder\"\n" - " try\n" - " «event OWNCload»\n" - " end try\n" - "end tell\n"); - - QString osascript = "/usr/bin/osascript"; - QStringList processArguments; - // processArguments << "-l" << "AppleScript"; - - QProcess p; - p.start(osascript, processArguments); - p.write(aScript.toUtf8()); - p.closeWriteChannel(); - //p.waitForReadyRead(-1); - p.waitForFinished(5000); - QByteArray result = p.readAll(); - QString resultAsString(result); // if appropriate - qCInfo(lcApplication) << "Load Finder Overlay-Plugin: " << resultAsString << ": " << p.exitCode() - << (p.exitCode() != 0 ? p.errorString() : QString::null); - } else { - qCWarning(lcApplication) << finderExtension << "does not exist! Finder Overlay Plugin loading failed"; - } - } -#endif } // This should rather be in application.... or rather in ConfigFile? From ca2ffd4fb3d1133253bc63b17b7dd0ce746a8766 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Thu, 6 Jul 2017 11:32:58 +0200 Subject: [PATCH 041/107] SocketAPI: Change strings to a consistent _MENU_TITLE postfix --- .../MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.m | 4 ++-- shell_integration/dolphin/ownclouddolphinpluginhelper.h | 4 ++-- shell_integration/nautilus/syncstate.py | 8 ++++---- .../windows/OCContextMenu/OCClientInterface.cpp | 4 ++-- src/gui/socketapi.cpp | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.m b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.m index 0591824bb3..2e917d132c 100644 --- a/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.m +++ b/shell_integration/MacOSX/OwnCloudFinderSync/FinderSyncExt/FinderSync.m @@ -103,8 +103,8 @@ id contextMenuTitle = [_strings objectForKey:@"CONTEXT_MENU_TITLE"]; id shareTitle = [_strings objectForKey:@"SHARE_MENU_TITLE"]; - id copyLinkTitle = [_strings objectForKey:@"COPY_PRIVATE_LINK_TITLE"]; - id emailLinkTitle = [_strings objectForKey:@"EMAIL_PRIVATE_LINK_TITLE"]; + id copyLinkTitle = [_strings objectForKey:@"COPY_PRIVATE_LINK_MENU_TITLE"]; + id emailLinkTitle = [_strings objectForKey:@"EMAIL_PRIVATE_LINK_MENU_TITLE"]; if (contextMenuTitle && !onlyRootsSelected) { NSMenu *menu = [[NSMenu alloc] initWithTitle:@""]; NSMenu *subMenu = [[NSMenu alloc] initWithTitle:@""]; diff --git a/shell_integration/dolphin/ownclouddolphinpluginhelper.h b/shell_integration/dolphin/ownclouddolphinpluginhelper.h index 7d4e7ba14f..294d1969d4 100644 --- a/shell_integration/dolphin/ownclouddolphinpluginhelper.h +++ b/shell_integration/dolphin/ownclouddolphinpluginhelper.h @@ -41,8 +41,8 @@ public: return _strings.value("SHARE_MENU_TITLE", "Share..."); } - QString copyPrivateLinkTitle() const { return _strings["COPY_PRIVATE_LINK_TITLE"]; } - QString emailPrivateLinkTitle() const { return _strings["EMAIL_PRIVATE_LINK_TITLE"]; } + QString copyPrivateLinkTitle() const { return _strings["COPY_PRIVATE_LINK_MENU_TITLE"]; } + QString emailPrivateLinkTitle() const { return _strings["EMAIL_PRIVATE_LINK_MENU_TITLE"]; } signals: void commandRecieved(const QByteArray &cmd); diff --git a/shell_integration/nautilus/syncstate.py b/shell_integration/nautilus/syncstate.py index 9a0f35ed74..082be4e227 100644 --- a/shell_integration/nautilus/syncstate.py +++ b/shell_integration/nautilus/syncstate.py @@ -240,15 +240,15 @@ class MenuExtension(GObject.GObject, Nautilus.MenuProvider): # Add permalink menu options, but hide these options for older clients # that don't have these actions. - if 'COPY_PRIVATE_LINK_TITLE' in self.strings: + if 'COPY_PRIVATE_LINK_MENU_TITLE' in self.strings: item_copyprivatelink = Nautilus.MenuItem( - name='CopyPrivateLink', label=self.strings.get('COPY_PRIVATE_LINK_TITLE', 'Copy private link to clipboard')) + name='CopyPrivateLink', label=self.strings.get('COPY_PRIVATE_LINK_MENU_TITLE', 'Copy private link to clipboard')) item_copyprivatelink.connect("activate", self.context_menu_action, 'COPY_PRIVATE_LINK', file) menu.append_item(item_copyprivatelink) - if 'EMAIL_PRIVATE_LINK_TITLE' in self.strings: + if 'EMAIL_PRIVATE_LINK_MENU_TITLE' in self.strings: item_emailprivatelink = Nautilus.MenuItem( - name='EmailPrivateLink', label=self.strings.get('EMAIL_PRIVATE_LINK_TITLE', 'Send private link by email...')) + name='EmailPrivateLink', label=self.strings.get('EMAIL_PRIVATE_LINK_MENU_TITLE', 'Send private link by email...')) item_emailprivatelink.connect("activate", self.context_menu_action, 'EMAIL_PRIVATE_LINK', file) menu.append_item(item_emailprivatelink) diff --git a/shell_integration/windows/OCContextMenu/OCClientInterface.cpp b/shell_integration/windows/OCContextMenu/OCClientInterface.cpp index 56facf6006..6c6396fa5e 100644 --- a/shell_integration/windows/OCContextMenu/OCClientInterface.cpp +++ b/shell_integration/windows/OCContextMenu/OCClientInterface.cpp @@ -64,9 +64,9 @@ OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo() info.shareMenuTitle = move(stringValue); else if (stringName == L"CONTEXT_MENU_TITLE") info.contextMenuTitle = move(stringValue); - else if (stringName == L"COPY_PRIVATE_LINK_TITLE") + else if (stringName == L"COPY_PRIVATE_LINK_MENU_TITLE") info.copyLinkMenuTitle = move(stringValue); - else if (stringName == L"EMAIL_PRIVATE_LINK_TITLE") + else if (stringName == L"EMAIL_PRIVATE_LINK_MENU_TITLE") info.emailLinkMenuTitle = move(stringValue); } else if (StringUtil::begins_with(response, wstring(L"GET_STRINGS:END"))) { diff --git a/src/gui/socketapi.cpp b/src/gui/socketapi.cpp index 6fc4ea405c..9141bf76f1 100644 --- a/src/gui/socketapi.cpp +++ b/src/gui/socketapi.cpp @@ -529,8 +529,8 @@ void SocketApi::command_GET_STRINGS(const QString &, SocketListener *listener) { "SHARE_MENU_TITLE", tr("Share with %1...", "parameter is ownCloud").arg(Theme::instance()->appNameGUI()) }, { "APPNAME", Theme::instance()->appNameGUI() }, { "CONTEXT_MENU_TITLE", Theme::instance()->appNameGUI() }, - { "COPY_PRIVATE_LINK_TITLE", tr("Copy private link to clipboard") }, - { "EMAIL_PRIVATE_LINK_TITLE", tr("Send private link by email...") }, + { "COPY_PRIVATE_LINK_MENU_TITLE", tr("Copy private link to clipboard") }, + { "EMAIL_PRIVATE_LINK_MENU_TITLE", tr("Send private link by email...") }, } }; listener->sendMessage(QString("GET_STRINGS:BEGIN")); for (auto key_value : strings) { From 411621bf0381f1d41876b8b4aef944a5f9f28be8 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Thu, 6 Jul 2017 11:33:38 +0200 Subject: [PATCH 042/107] shell integrations: Change share menu title to "Share..." Since the appname is in the context submenu title already. Also drop APPNAME string. --- src/gui/socketapi.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/gui/socketapi.cpp b/src/gui/socketapi.cpp index 9141bf76f1..f2616d3780 100644 --- a/src/gui/socketapi.cpp +++ b/src/gui/socketapi.cpp @@ -526,8 +526,7 @@ void SocketApi::command_EMAIL_PRIVATE_LINK(const QString &localFile, SocketListe void SocketApi::command_GET_STRINGS(const QString &, SocketListener *listener) { static std::array, 5> strings { { - { "SHARE_MENU_TITLE", tr("Share with %1...", "parameter is ownCloud").arg(Theme::instance()->appNameGUI()) }, - { "APPNAME", Theme::instance()->appNameGUI() }, + { "SHARE_MENU_TITLE", tr("Share...") }, { "CONTEXT_MENU_TITLE", Theme::instance()->appNameGUI() }, { "COPY_PRIVATE_LINK_MENU_TITLE", tr("Copy private link to clipboard") }, { "EMAIL_PRIVATE_LINK_MENU_TITLE", tr("Send private link by email...") }, From e54be1c4ee878dd4c318d70430cfba4907003e84 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Thu, 15 Jun 2017 15:38:22 +0200 Subject: [PATCH 043/107] Link shares: Add "show file listing" option #5837 --- src/gui/sharelinkwidget.cpp | 33 +++++++++++++++++++++++---------- src/gui/sharelinkwidget.h | 4 ++-- src/gui/sharelinkwidget.ui | 13 ++++++++++--- src/gui/sharemanager.cpp | 22 ++++------------------ src/gui/sharemanager.h | 12 +++--------- src/libsync/capabilities.cpp | 5 +++++ src/libsync/capabilities.h | 1 + 7 files changed, 48 insertions(+), 42 deletions(-) diff --git a/src/gui/sharelinkwidget.cpp b/src/gui/sharelinkwidget.cpp index f3b8e33da5..79973b8e47 100644 --- a/src/gui/sharelinkwidget.cpp +++ b/src/gui/sharelinkwidget.cpp @@ -68,7 +68,7 @@ ShareLinkWidget::ShareLinkWidget(AccountPtr account, _pi_editing = new QProgressIndicator(); _ui->horizontalLayout_create->addWidget(_pi_create); _ui->horizontalLayout_password->addWidget(_pi_password); - _ui->horizontalLayout_editing->addWidget(_pi_editing); + _ui->layout_editing->addWidget(_pi_editing, 0, 2); _ui->horizontalLayout_expire->insertWidget(_ui->horizontalLayout_expire->count() - 1, _pi_date); connect(_ui->nameLineEdit, SIGNAL(returnPressed()), SLOT(slotShareNameEntered())); @@ -81,7 +81,8 @@ ShareLinkWidget::ShareLinkWidget(AccountPtr account, connect(_ui->pushButton_setPassword, SIGNAL(clicked(bool)), SLOT(slotPasswordReturnPressed())); connect(_ui->checkBox_expire, SIGNAL(clicked()), this, SLOT(slotCheckBoxExpireClicked())); connect(_ui->calendar, SIGNAL(dateChanged(QDate)), SLOT(slotExpireDateChanged(QDate))); - connect(_ui->checkBox_editing, SIGNAL(clicked()), this, SLOT(slotCheckBoxEditingClicked())); + connect(_ui->checkBox_editing, SIGNAL(clicked()), this, SLOT(slotPermissionsCheckboxClicked())); + connect(_ui->checkBox_fileListing, SIGNAL(clicked(bool)), this, SLOT(slotPermissionsCheckboxClicked())); _ui->errorLabel->hide(); @@ -145,10 +146,11 @@ ShareLinkWidget::ShareLinkWidget(AccountPtr account, _expiryRequired = true; } - // File can't have public upload set. - _ui->widget_editing->setVisible(!_isFile); - _ui->checkBox_editing->setEnabled( - _account->capabilities().sharePublicLinkAllowUpload()); + // File can't have public upload set; we also hide it if the capability isn't there + _ui->widget_editing->setVisible( + !_isFile && _account->capabilities().sharePublicLinkAllowUpload()); + _ui->checkBox_fileListing->setVisible( + _account->capabilities().sharePublicLinkSupportsUploadOnly()); // Prepare sharing menu @@ -217,9 +219,10 @@ void ShareLinkWidget::slotSharesFetched(const QList> &shar connect(share.data(), SIGNAL(serverError(int, QString)), SLOT(slotServerError(int, QString))); connect(share.data(), SIGNAL(shareDeleted()), SLOT(slotDeleteShareFetched())); connect(share.data(), SIGNAL(expireDateSet()), SLOT(slotExpireSet())); - connect(share.data(), SIGNAL(publicUploadSet()), SLOT(slotPublicUploadSet())); + connect(share.data(), SIGNAL(publicUploadSet()), SLOT(slotPermissionsSet())); connect(share.data(), SIGNAL(passwordSet()), SLOT(slotPasswordSet())); connect(share.data(), SIGNAL(passwordSetError(int, QString)), SLOT(slotPasswordSetError(int, QString))); + connect(share.data(), SIGNAL(permissionsSet()), SLOT(slotPermissionsSet())); // Build the table row auto row = table->rowCount(); @@ -325,6 +328,8 @@ void ShareLinkWidget::slotShareSelectionChanged() // Public upload state (box is hidden for files) if (!_isFile) { _ui->checkBox_editing->setChecked(share->getPublicUpload()); + _ui->checkBox_fileListing->setChecked(share->getShowFileListing()); + _ui->checkBox_fileListing->setEnabled(share->getPublicUpload()); } } @@ -532,14 +537,22 @@ void ShareLinkWidget::slotDeleteShareClicked() share->deleteShare(); } -void ShareLinkWidget::slotCheckBoxEditingClicked() +void ShareLinkWidget::slotPermissionsCheckboxClicked() { if (auto current = selectedShare()) { _ui->checkBox_editing->setEnabled(false); + _ui->checkBox_fileListing->setEnabled(false); _pi_editing->startAnimation(); _ui->errorLabel->hide(); - current->setPublicUpload(_ui->checkBox_editing->isChecked()); + SharePermissions perm = SharePermissionRead; + if (_ui->checkBox_editing->isChecked() && _ui->checkBox_fileListing->isChecked()) { + perm = SharePermissionRead | SharePermissionCreate + | SharePermissionUpdate | SharePermissionDelete; + } else if (_ui->checkBox_editing->isChecked() && !_ui->checkBox_fileListing->isChecked()) { + perm = SharePermissionCreate; + } + current->setPermissions(perm); } } @@ -553,7 +566,7 @@ QSharedPointer ShareLinkWidget::selectedShare() const return items.first()->data(Qt::UserRole).value>(); } -void ShareLinkWidget::slotPublicUploadSet() +void ShareLinkWidget::slotPermissionsSet() { if (sender() == selectedShare().data()) { slotShareSelectionChanged(); diff --git a/src/gui/sharelinkwidget.h b/src/gui/sharelinkwidget.h index 78dcc596b7..4dd5f1ec69 100644 --- a/src/gui/sharelinkwidget.h +++ b/src/gui/sharelinkwidget.h @@ -65,7 +65,7 @@ private slots: void slotCheckBoxPasswordClicked(); void slotCheckBoxExpireClicked(); void slotPasswordReturnPressed(); - void slotCheckBoxEditingClicked(); + void slotPermissionsCheckboxClicked(); void slotExpireDateChanged(const QDate &date); void slotPasswordChanged(const QString &newText); void slotNameEdited(QTableWidgetItem *item); @@ -77,7 +77,7 @@ private slots: void slotCreateShareRequiresPassword(const QString &message); void slotPasswordSet(); void slotExpireSet(); - void slotPublicUploadSet(); + void slotPermissionsSet(); void slotServerError(int code, const QString &message); void slotPasswordSetError(int code, const QString &message); diff --git a/src/gui/sharelinkwidget.ui b/src/gui/sharelinkwidget.ui index a04511c034..9d80573be9 100644 --- a/src/gui/sharelinkwidget.ui +++ b/src/gui/sharelinkwidget.ui @@ -181,7 +181,7 @@ - + 0 @@ -194,14 +194,14 @@ 0 - + Allow editing - + Qt::Horizontal @@ -214,6 +214,13 @@ + + + + Show file listing + + + diff --git a/src/gui/sharemanager.cpp b/src/gui/sharemanager.cpp index 10d13a0677..37c39839fa 100644 --- a/src/gui/sharemanager.cpp +++ b/src/gui/sharemanager.cpp @@ -146,17 +146,14 @@ LinkShare::LinkShare(AccountPtr account, { } -bool LinkShare::getPublicUpload() +bool LinkShare::getPublicUpload() const { - return ((_permissions & SharePermissionUpdate) && (_permissions & SharePermissionCreate)); + return _permissions & SharePermissionCreate; } -void LinkShare::setPublicUpload(bool publicUpload) +bool LinkShare::getShowFileListing() const { - OcsShareJob *job = new OcsShareJob(_account); - connect(job, SIGNAL(shareJobFinished(QJsonDocument, QVariant)), SLOT(slotPublicUploadSet(QJsonDocument, QVariant))); - connect(job, SIGNAL(ocsError(int, QString)), SLOT(slotOcsError(int, QString))); - job->setPublicUpload(getId(), publicUpload); + return _permissions & SharePermissionRead; } QString LinkShare::getName() const @@ -177,17 +174,6 @@ QString LinkShare::getToken() const return _token; } -void LinkShare::slotPublicUploadSet(const QJsonDocument &, const QVariant &value) -{ - if (value.toBool()) { - _permissions = SharePermissionRead | SharePermissionUpdate | SharePermissionCreate; - } else { - _permissions = SharePermissionRead; - } - - emit publicUploadSet(); -} - void LinkShare::setPassword(const QString &password) { OcsShareJob *job = new OcsShareJob(_account); diff --git a/src/gui/sharemanager.h b/src/gui/sharemanager.h index 62ff6ee4c6..8c361b7326 100644 --- a/src/gui/sharemanager.h +++ b/src/gui/sharemanager.h @@ -153,16 +153,12 @@ public: /* * Get the publicUpload status of this share */ - bool getPublicUpload(); + bool getPublicUpload() const; /* - * Set a share to be public upload - * This function can only be called on link shares - * - * On success the publicUploadSet signal is emitted - * In case of a server error the serverError signal is emitted. + * Whether directory listings are available (READ permission) */ - void setPublicUpload(bool publicUpload); + bool getShowFileListing() const; /* * Returns the name of the link share. Can be empty. @@ -209,14 +205,12 @@ public: signals: void expireDateSet(); - void publicUploadSet(); void passwordSet(); void passwordSetError(int statusCode, const QString &message); void nameSet(); private slots: void slotPasswordSet(const QJsonDocument &, const QVariant &value); - void slotPublicUploadSet(const QJsonDocument &, const QVariant &value); void slotExpireDateSet(const QJsonDocument &reply, const QVariant &value); void slotSetPasswordError(int statusCode, const QString &message); void slotNameSet(const QJsonDocument &, const QVariant &value); diff --git a/src/libsync/capabilities.cpp b/src/libsync/capabilities.cpp index 651b1981d5..4722c0b99b 100644 --- a/src/libsync/capabilities.cpp +++ b/src/libsync/capabilities.cpp @@ -51,6 +51,11 @@ bool Capabilities::sharePublicLinkAllowUpload() const return _capabilities["files_sharing"].toMap()["public"].toMap()["upload"].toBool(); } +bool Capabilities::sharePublicLinkSupportsUploadOnly() const +{ + return _capabilities["files_sharing"].toMap()["public"].toMap()["supports_upload_only"].toBool(); +} + bool Capabilities::sharePublicLinkEnforcePassword() const { return _capabilities["files_sharing"].toMap()["public"].toMap()["password"].toMap()["enforced"].toBool(); diff --git a/src/libsync/capabilities.h b/src/libsync/capabilities.h index 4044d85841..6baaaa0ec7 100644 --- a/src/libsync/capabilities.h +++ b/src/libsync/capabilities.h @@ -36,6 +36,7 @@ public: bool shareAPI() const; bool sharePublicLink() const; bool sharePublicLinkAllowUpload() const; + bool sharePublicLinkSupportsUploadOnly() const; bool sharePublicLinkEnforcePassword() const; bool sharePublicLinkEnforceExpireDate() const; int sharePublicLinkExpireDateDays() const; From 99b1f69271b230e59b70dd02773ad483046a4328 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Thu, 15 Jun 2017 16:03:24 +0200 Subject: [PATCH 044/107] Link share: Remove direct download if unavailable #5837 It would have been much nicer to keep the menu assigned to the QToolButton, but if one switches away from InstantPopup (to adjust the entries before they're displayed), the button always gets a menu indicator that can't be removed. --- src/gui/sharelinkwidget.cpp | 19 +++++++++++++++---- src/gui/sharelinkwidget.h | 3 ++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/gui/sharelinkwidget.cpp b/src/gui/sharelinkwidget.cpp index 79973b8e47..bba3151bbb 100644 --- a/src/gui/sharelinkwidget.cpp +++ b/src/gui/sharelinkwidget.cpp @@ -156,6 +156,8 @@ ShareLinkWidget::ShareLinkWidget(AccountPtr account, // Prepare sharing menu _shareLinkMenu = new QMenu(this); + connect(_shareLinkMenu, SIGNAL(triggered(QAction *)), + SLOT(slotShareLinkActionTriggered(QAction *))); _openLinkAction = _shareLinkMenu->addAction(tr("Open link in browser")); _copyLinkAction = _shareLinkMenu->addAction(tr("Copy link to clipboard")); _copyDirectLinkAction = _shareLinkMenu->addAction(tr("Copy link to clipboard (direct download)")); @@ -245,9 +247,7 @@ void ShareLinkWidget::slotSharesFetched(const QList> &shar auto shareButton = new QToolButton; shareButton->setText("..."); shareButton->setProperty(propertyShareC, QVariant::fromValue(linkShare)); - shareButton->setMenu(_shareLinkMenu); - shareButton->setPopupMode(QToolButton::InstantPopup); - connect(shareButton, SIGNAL(triggered(QAction *)), SLOT(slotShareLinkButtonTriggered(QAction *))); + connect(shareButton, SIGNAL(clicked(bool)), SLOT(slotShareLinkButtonClicked())); table->setCellWidget(row, 1, shareButton); auto deleteButton = new QToolButton; @@ -514,7 +514,18 @@ void ShareLinkWidget::openShareLink(const QUrl &url) Utility::openBrowser(url, this); } -void ShareLinkWidget::slotShareLinkButtonTriggered(QAction *action) +void ShareLinkWidget::slotShareLinkButtonClicked() +{ + auto share = sender()->property(propertyShareC).value>(); + bool downloadEnabled = share->getShowFileListing(); + _copyDirectLinkAction->setVisible(downloadEnabled); + _emailDirectLinkAction->setVisible(downloadEnabled); + + _shareLinkMenu->setProperty(propertyShareC, QVariant::fromValue(share)); + _shareLinkMenu->exec(QCursor::pos()); +} + +void ShareLinkWidget::slotShareLinkActionTriggered(QAction *action) { auto share = sender()->property(propertyShareC).value>(); diff --git a/src/gui/sharelinkwidget.h b/src/gui/sharelinkwidget.h index 4dd5f1ec69..1d8397505c 100644 --- a/src/gui/sharelinkwidget.h +++ b/src/gui/sharelinkwidget.h @@ -70,7 +70,8 @@ private slots: void slotPasswordChanged(const QString &newText); void slotNameEdited(QTableWidgetItem *item); - void slotShareLinkButtonTriggered(QAction *action); + void slotShareLinkButtonClicked(); + void slotShareLinkActionTriggered(QAction *action); void slotDeleteShareFetched(); void slotCreateShareFetched(const QSharedPointer share); From 186f16e6882a9a33f0282e83296948aca6ee672b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Weigert?= Date: Fri, 7 Jul 2017 16:01:20 +0200 Subject: [PATCH 045/107] Update building.rst --- doc/building.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/building.rst b/doc/building.rst index 7cc6da6987..48f6f0212e 100644 --- a/doc/building.rst +++ b/doc/building.rst @@ -30,10 +30,10 @@ you the exact sources from which the binary packages are built. These are hosted on the `ownCloud repository from OBS`_. Go to the `Index of repositories`_ to see all the Linux client repos. -1. At the `bottom of the page for each distribution +1. The source RPMs for CentOS, RHEL, Fedora, SLES, and openSUSE are at the `bottom of the page for each distribution `_ is a "Grab binary packages directly" section. - These contain source RPMs for CentOS, RHEL, Fedora, SLES, and openSUSE. + package=owncloud-client>` + the sources for DEB and Ubuntu based distributions are at e.g. http://download.opensuse.org/repositories/isv:/ownCloud:/desktop/Ubuntu_16.04/ To get the .deb source packages add the source repo for your Debian or Ubuntu version, like this example for Debian 8 From af4f1083b7be2f88d4f79904965e734d4fdde8ca Mon Sep 17 00:00:00 2001 From: Jocelyn Turcotte Date: Fri, 7 Jul 2017 18:10:48 +0200 Subject: [PATCH 046/107] shell/windows Build 46: Private links context menu --- binary | 2 +- shell_integration/windows/OCUtil/Version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/binary b/binary index 1818b48380..3425fab2c6 160000 --- a/binary +++ b/binary @@ -1 +1 @@ -Subproject commit 1818b48380f4fc50d482b980e5ec0d347f896a70 +Subproject commit 3425fab2c66118ffae3e3b16751e636ca71ee450 diff --git a/shell_integration/windows/OCUtil/Version.h b/shell_integration/windows/OCUtil/Version.h index 4010a60a10..d2c83b060a 100644 --- a/shell_integration/windows/OCUtil/Version.h +++ b/shell_integration/windows/OCUtil/Version.h @@ -2,7 +2,7 @@ // This is the number that will end up in the version window of the DLLs. // Increment this version before committing a new build if you are today's shell_integration build master. -#define OCEXT_BUILD_NUM 45 +#define OCEXT_BUILD_NUM 46 #define STRINGIZE2(s) #s #define STRINGIZE(s) STRINGIZE2(s) From 0d926f763292444a9845732d7b25ba8173e56f6e Mon Sep 17 00:00:00 2001 From: Jenkins for ownCloud Date: Sat, 8 Jul 2017 02:18:29 +0200 Subject: [PATCH 047/107] [tx-robot] updated from transifex --- mirall.desktop.in | 3 +++ translations/client_nb_NO.ts | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/mirall.desktop.in b/mirall.desktop.in index 7b1371b2bb..286554ad6e 100644 --- a/mirall.desktop.in +++ b/mirall.desktop.in @@ -93,6 +93,9 @@ X-GNOME-Autostart-Delay=3 # Translations +# Translations + + # Translations Comment[oc]=@APPLICATION_NAME@ sincronizacion del client GenericName[oc]=Dorsièr de Sincronizacion diff --git a/translations/client_nb_NO.ts b/translations/client_nb_NO.ts index 565d04cd5a..64979cce49 100644 --- a/translations/client_nb_NO.ts +++ b/translations/client_nb_NO.ts @@ -2556,7 +2556,7 @@ Det er ikke tilrådelig å bruke den. Anyone with the link has access to the file/folder - + Alle med linken har tilgang til filen/mappen @@ -3458,7 +3458,7 @@ Det er ikke tilrådelig å bruke den. <p>Version %2. For more information visit <a href="%3">https://%4</a></p><p>For known issues and help, please visit: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt, and others.</small></p><p>Copyright ownCloud GmbH</p><p>Licensed under the GNU General Public License (GPL) Version 2.0<br/>ownCloud and the ownCloud Logo are registered trademarks of ownCloud GmbH in the United States, other countries, or both.</p> - + <p>Versjon %2. For mer informasjon gå til <a href="%3">https://%4</a></p><p>For kjente problemer og hjelp, gå til: <a href="https://central.owncloud.org/c/desktop-client">https://central.owncloud.org</a></p><p><small>Av Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, Jan-Christoph Borchardt med flere.</small></p><p>Copyright ownCloud GmbH</p><p>Lisensiert under GNU General Public License (GPL) Version 2.0<br/>ownCloud og ownCloud-logo er registrerte varemerker for ownCloud GmbH i USA, andre land eller begge deler.</p> From a5ace5e71d693363e695878257eb2ece0b2806c1 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Fri, 7 Jul 2017 11:09:11 +0200 Subject: [PATCH 048/107] Account/Credentials: Have identical lifetimes The QNAM may continue to outlive both. Rename Credentials::getQNAM() to createQNAM() while we're at it - it's used to make a new QNAM that will subsequently be owned by the Account object. See d01065b9a12e69ca493a232f3a8e8f3d416fed52 for rationale. Relates to d40c56eda561e3a541bf1b23f70fa8d659d3037e 147cf798a6f13c9b53a9f1fb2db1ef26c8c63273 --- src/gui/creds/shibbolethcredentials.cpp | 2 +- src/gui/creds/shibbolethcredentials.h | 2 +- src/libsync/account.cpp | 10 ++++++---- src/libsync/account.h | 2 +- src/libsync/creds/abstractcredentials.h | 2 +- src/libsync/creds/dummycredentials.cpp | 2 +- src/libsync/creds/dummycredentials.h | 2 +- src/libsync/creds/httpcredentials.cpp | 10 ++++++---- src/libsync/creds/httpcredentials.h | 2 +- test/syncenginetestutils.h | 2 +- 10 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/gui/creds/shibbolethcredentials.cpp b/src/gui/creds/shibbolethcredentials.cpp index cc3d6fb686..b7fca6dd3d 100644 --- a/src/gui/creds/shibbolethcredentials.cpp +++ b/src/gui/creds/shibbolethcredentials.cpp @@ -90,7 +90,7 @@ QString ShibbolethCredentials::user() const return _user; } -QNetworkAccessManager *ShibbolethCredentials::getQNAM() const +QNetworkAccessManager *ShibbolethCredentials::createQNAM() const { QNetworkAccessManager *qnam(new AccessManager); connect(qnam, SIGNAL(finished(QNetworkReply *)), diff --git a/src/gui/creds/shibbolethcredentials.h b/src/gui/creds/shibbolethcredentials.h index 46b6bc08df..a4909ed741 100644 --- a/src/gui/creds/shibbolethcredentials.h +++ b/src/gui/creds/shibbolethcredentials.h @@ -53,7 +53,7 @@ public: void setAccount(Account *account) Q_DECL_OVERRIDE; QString authType() const Q_DECL_OVERRIDE; QString user() const Q_DECL_OVERRIDE; - QNetworkAccessManager *getQNAM() const Q_DECL_OVERRIDE; + QNetworkAccessManager *createQNAM() const Q_DECL_OVERRIDE; bool ready() const Q_DECL_OVERRIDE; void fetchFromKeychain() Q_DECL_OVERRIDE; void askFromUser() Q_DECL_OVERRIDE; diff --git a/src/libsync/account.cpp b/src/libsync/account.cpp index ee83e3deec..45168033f6 100644 --- a/src/libsync/account.cpp +++ b/src/libsync/account.cpp @@ -136,11 +136,13 @@ void Account::setCredentials(AbstractCredentials *cred) // The order for these two is important! Reading the credential's // settings accesses the account as well as account->_credentials, - // so deleteLater must be used. - _credentials = QSharedPointer(cred, &QObject::deleteLater); + _credentials.reset(cred); cred->setAccount(this); - _am = QSharedPointer(_credentials->getQNAM(), &QObject::deleteLater); + // Note: This way the QNAM can outlive the Account and Credentials. + // This is necessary to avoid issues with the QNAM being deleted while + // processing slotHandleSslErrors(). + _am = QSharedPointer(_credentials->createQNAM(), &QObject::deleteLater); if (jar) { _am->setCookieJar(jar); @@ -205,7 +207,7 @@ void Account::resetNetworkAccessManager() // Use a QSharedPointer to allow locking the life of the QNAM on the stack. // Make it call deleteLater to make sure that we can return to any QNAM stack frames safely. - _am = QSharedPointer(_credentials->getQNAM(), &QObject::deleteLater); + _am = QSharedPointer(_credentials->createQNAM(), &QObject::deleteLater); _am->setCookieJar(jar); // takes ownership of the old cookie jar connect(_am.data(), SIGNAL(sslErrors(QNetworkReply *, QList)), diff --git a/src/libsync/account.h b/src/libsync/account.h index d48b27b15c..b34342d452 100644 --- a/src/libsync/account.h +++ b/src/libsync/account.h @@ -246,7 +246,7 @@ private: QScopedPointer _sslErrorHandler; QuotaInfo *_quotaInfo; QSharedPointer _am; - QSharedPointer _credentials; + QScopedPointer _credentials; /// Certificates that were explicitly rejected by the user QList _rejectedCertificates; diff --git a/src/libsync/creds/abstractcredentials.h b/src/libsync/creds/abstractcredentials.h index 09ba50e3e4..9958b56666 100644 --- a/src/libsync/creds/abstractcredentials.h +++ b/src/libsync/creds/abstractcredentials.h @@ -43,7 +43,7 @@ public: virtual QString authType() const = 0; virtual QString user() const = 0; - virtual QNetworkAccessManager *getQNAM() const = 0; + virtual QNetworkAccessManager *createQNAM() const = 0; /** Whether there are credentials that can be used for a connection attempt. */ virtual bool ready() const = 0; diff --git a/src/libsync/creds/dummycredentials.cpp b/src/libsync/creds/dummycredentials.cpp index c55e979ac1..3c5609d356 100644 --- a/src/libsync/creds/dummycredentials.cpp +++ b/src/libsync/creds/dummycredentials.cpp @@ -27,7 +27,7 @@ QString DummyCredentials::user() const return _user; } -QNetworkAccessManager *DummyCredentials::getQNAM() const +QNetworkAccessManager *DummyCredentials::createQNAM() const { return new AccessManager; } diff --git a/src/libsync/creds/dummycredentials.h b/src/libsync/creds/dummycredentials.h index ad56a08dd5..6729b2e5a5 100644 --- a/src/libsync/creds/dummycredentials.h +++ b/src/libsync/creds/dummycredentials.h @@ -28,7 +28,7 @@ public: QString _password; QString authType() const Q_DECL_OVERRIDE; QString user() const Q_DECL_OVERRIDE; - QNetworkAccessManager *getQNAM() const Q_DECL_OVERRIDE; + QNetworkAccessManager *createQNAM() const Q_DECL_OVERRIDE; bool ready() const Q_DECL_OVERRIDE; bool stillValid(QNetworkReply *reply) Q_DECL_OVERRIDE; void fetchFromKeychain() Q_DECL_OVERRIDE; diff --git a/src/libsync/creds/httpcredentials.cpp b/src/libsync/creds/httpcredentials.cpp index 1670e0101e..bd1cc7aef7 100644 --- a/src/libsync/creds/httpcredentials.cpp +++ b/src/libsync/creds/httpcredentials.cpp @@ -58,7 +58,7 @@ protected: QNetworkReply *createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData) Q_DECL_OVERRIDE { QNetworkRequest req(request); - if (!_cred->password().isEmpty()) { + if (_cred && !_cred->password().isEmpty()) { if (_cred->isUsingOAuth()) { req.setRawHeader("Authorization", "Bearer " + _cred->password().toUtf8()); } else { @@ -72,7 +72,7 @@ protected: req.setRawHeader("Authorization", "Basic " + credHash); } - if (!_cred->_clientSslKey.isNull() && !_cred->_clientSslCertificate.isNull()) { + if (_cred && !_cred->_clientSslKey.isNull() && !_cred->_clientSslCertificate.isNull()) { // SSL configuration QSslConfiguration sslConfiguration = req.sslConfiguration(); sslConfiguration.setLocalCertificate(_cred->_clientSslCertificate); @@ -85,7 +85,9 @@ protected: } private: - const HttpCredentials *_cred; + // The credentials object dies along with the account, while the QNAM might + // outlive both. + QPointer _cred; }; @@ -135,7 +137,7 @@ void HttpCredentials::setAccount(Account *account) } } -QNetworkAccessManager *HttpCredentials::getQNAM() const +QNetworkAccessManager *HttpCredentials::createQNAM() const { AccessManager *qnam = new HttpCredentialsAccessManager(this); diff --git a/src/libsync/creds/httpcredentials.h b/src/libsync/creds/httpcredentials.h index 03f3b8c702..85e6d092af 100644 --- a/src/libsync/creds/httpcredentials.h +++ b/src/libsync/creds/httpcredentials.h @@ -79,7 +79,7 @@ public: HttpCredentials(const QString &user, const QString &password, const QSslCertificate &certificate = QSslCertificate(), const QSslKey &key = QSslKey()); QString authType() const Q_DECL_OVERRIDE; - QNetworkAccessManager *getQNAM() const Q_DECL_OVERRIDE; + QNetworkAccessManager *createQNAM() const Q_DECL_OVERRIDE; bool ready() const Q_DECL_OVERRIDE; void fetchFromKeychain() Q_DECL_OVERRIDE; bool stillValid(QNetworkReply *reply) Q_DECL_OVERRIDE; diff --git a/test/syncenginetestutils.h b/test/syncenginetestutils.h index 997e5054e5..b90ff480b1 100644 --- a/test/syncenginetestutils.h +++ b/test/syncenginetestutils.h @@ -778,7 +778,7 @@ public: FakeCredentials(QNetworkAccessManager *qnam) : _qnam{qnam} { } virtual QString authType() const { return "test"; } virtual QString user() const { return "admin"; } - virtual QNetworkAccessManager* getQNAM() const { return _qnam; } + virtual QNetworkAccessManager *createQNAM() const { return _qnam; } virtual bool ready() const { return true; } virtual void fetchFromKeychain() { } virtual void askFromUser() { } From 7db445559256d5cae92b980c14d8b5650c562d2e Mon Sep 17 00:00:00 2001 From: Jenkins for ownCloud Date: Sun, 9 Jul 2017 02:18:29 +0200 Subject: [PATCH 049/107] [tx-robot] updated from transifex --- mirall.desktop.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mirall.desktop.in b/mirall.desktop.in index 286554ad6e..a1a7fc584a 100644 --- a/mirall.desktop.in +++ b/mirall.desktop.in @@ -96,6 +96,9 @@ X-GNOME-Autostart-Delay=3 # Translations +# Translations + + # Translations Comment[oc]=@APPLICATION_NAME@ sincronizacion del client GenericName[oc]=Dorsièr de Sincronizacion From 2c127ae2d5e183e2aa9846cc4dd33edc83279e04 Mon Sep 17 00:00:00 2001 From: Jenkins for ownCloud Date: Mon, 10 Jul 2017 02:18:29 +0200 Subject: [PATCH 050/107] [tx-robot] updated from transifex --- mirall.desktop.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mirall.desktop.in b/mirall.desktop.in index a1a7fc584a..c62c403bc2 100644 --- a/mirall.desktop.in +++ b/mirall.desktop.in @@ -99,6 +99,9 @@ X-GNOME-Autostart-Delay=3 # Translations +# Translations + + # Translations Comment[oc]=@APPLICATION_NAME@ sincronizacion del client GenericName[oc]=Dorsièr de Sincronizacion From 122854af77805bda12b6eb975c61e36c89af2c9f Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Thu, 6 Jul 2017 14:02:21 +0200 Subject: [PATCH 051/107] FolderMan: Add missing folderListChanged emits She signal of a folder list change was not emitted when folders were deleted from the list. --- src/gui/folderman.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gui/folderman.cpp b/src/gui/folderman.cpp index ce5376b2ba..a1f1286a9e 100644 --- a/src/gui/folderman.cpp +++ b/src/gui/folderman.cpp @@ -141,6 +141,7 @@ int FolderMan::unloadAndDeleteAllFolders() _lastSyncFolder = 0; _currentSyncFolder = 0; _scheduledFolders.clear(); + emit folderListChanged(_folderMap); emit scheduleQueueChanged(); return cnt; @@ -1038,6 +1039,8 @@ void FolderMan::removeFolder(Folder *f) } else { delete f; } + + emit folderListChanged(_folderMap); } QString FolderMan::getBackupName(QString fullPathName) const From 1c9b51c330932171b82166cf97dea9d8be15f9b4 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Thu, 6 Jul 2017 14:03:22 +0200 Subject: [PATCH 052/107] IssuesTab: Hide filters if they wouldn't be useful As suggested by @dragotin --- src/gui/issueswidget.cpp | 37 +++++++++++++++++++++++++++++++++++++ src/gui/issueswidget.h | 1 + src/gui/issueswidget.ui | 6 +++--- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/gui/issueswidget.cpp b/src/gui/issueswidget.cpp index eff8a3b454..e32e89aac7 100644 --- a/src/gui/issueswidget.cpp +++ b/src/gui/issueswidget.cpp @@ -65,6 +65,8 @@ IssuesWidget::IssuesWidget(QWidget *parent) SLOT(slotAccountAdded(AccountState *))); connect(AccountManager::instance(), SIGNAL(accountRemoved(AccountState *)), SLOT(slotAccountRemoved(AccountState *))); + connect(FolderMan::instance(), SIGNAL(folderListChanged(Folder::Map)), + SLOT(slotUpdateFolderFilters())); // Adjust copyToClipboard() when making changes here! @@ -183,6 +185,7 @@ void IssuesWidget::slotRefreshIssues() void IssuesWidget::slotAccountAdded(AccountState *account) { _ui->filterAccount->addItem(account->account()->displayName(), QVariant::fromValue(account)); + updateAccountChoiceVisibility(); } void IssuesWidget::slotAccountRemoved(AccountState *account) @@ -191,6 +194,15 @@ void IssuesWidget::slotAccountRemoved(AccountState *account) if (account == _ui->filterAccount->itemData(i).value()) _ui->filterAccount->removeItem(i); } + updateAccountChoiceVisibility(); +} + +void IssuesWidget::updateAccountChoiceVisibility() +{ + bool visible = _ui->filterAccount->count() > 2; + _ui->filterAccount->setVisible(visible); + _ui->accountLabel->setVisible(visible); + slotUpdateFolderFilters(); } AccountState *IssuesWidget::currentAccountFilter() const @@ -228,6 +240,12 @@ void IssuesWidget::slotUpdateFolderFilters() { auto account = _ui->filterAccount->currentData().value(); + // If there is no account selector, show folders for the single + // available account + if (_ui->filterAccount->isHidden() && _ui->filterAccount->count() > 1) { + account = _ui->filterAccount->itemData(1).value(); + } + if (!account) { _ui->filterFolder->setCurrentIndex(0); } @@ -236,11 +254,30 @@ void IssuesWidget::slotUpdateFolderFilters() for (int i = _ui->filterFolder->count() - 1; i >= 1; --i) { _ui->filterFolder->removeItem(i); } + + // Find all selectable folders while figuring out if we need a folder + // selector in the first place + bool anyAccountHasMultipleFolders = false; + QSet accountsWithFolders; for (auto folder : FolderMan::instance()->map().values()) { + if (accountsWithFolders.contains(folder->accountState())) + anyAccountHasMultipleFolders = true; + accountsWithFolders.insert(folder->accountState()); + if (folder->accountState() != account) continue; _ui->filterFolder->addItem(folder->shortGuiLocalPath(), folder->alias()); } + + // If we don't need the combo box, hide it. + _ui->filterFolder->setVisible(anyAccountHasMultipleFolders); + _ui->folderLabel->setVisible(anyAccountHasMultipleFolders); + + // If there's no choice, select the only folder and disable + if (_ui->filterFolder->count() == 2 && anyAccountHasMultipleFolders) { + _ui->filterFolder->setCurrentIndex(1); + _ui->filterFolder->setEnabled(false); + } } void IssuesWidget::storeSyncIssues(QTextStream &ts) diff --git a/src/gui/issueswidget.h b/src/gui/issueswidget.h index 9d31e16e16..8ebe6451d7 100644 --- a/src/gui/issueswidget.h +++ b/src/gui/issueswidget.h @@ -69,6 +69,7 @@ private slots: void slotAccountRemoved(AccountState *account); private: + void updateAccountChoiceVisibility(); AccountState *currentAccountFilter() const; QString currentFolderFilter() const; bool shouldBeVisible(QTreeWidgetItem *item, AccountState *filterAccount, diff --git a/src/gui/issueswidget.ui b/src/gui/issueswidget.ui index eb15d3d8c4..5a1e1fac67 100644 --- a/src/gui/issueswidget.ui +++ b/src/gui/issueswidget.ui @@ -27,9 +27,9 @@ - + - + Account @@ -45,7 +45,7 @@ - + Folder From f52cefaa94238c83a495e39bafdbd7f8a5b9304e Mon Sep 17 00:00:00 2001 From: Jenkins for ownCloud Date: Tue, 11 Jul 2017 02:18:29 +0200 Subject: [PATCH 053/107] [tx-robot] updated from transifex --- mirall.desktop.in | 3 +++ translations/client_pl.ts | 16 ++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/mirall.desktop.in b/mirall.desktop.in index c62c403bc2..275f345bf4 100644 --- a/mirall.desktop.in +++ b/mirall.desktop.in @@ -102,6 +102,9 @@ X-GNOME-Autostart-Delay=3 # Translations +# Translations + + # Translations Comment[oc]=@APPLICATION_NAME@ sincronizacion del client GenericName[oc]=Dorsièr de Sincronizacion diff --git a/translations/client_pl.ts b/translations/client_pl.ts index 7caff29e64..5f4415b377 100644 --- a/translations/client_pl.ts +++ b/translations/client_pl.ts @@ -2023,7 +2023,7 @@ Niezalecane jest jego użycie. The downloaded file is empty despite the server announced it should have been %1. - + Pobrany plik jest pusty pomimo tego, że według zapowiedzi serwera powinien mieć %1. @@ -2184,7 +2184,7 @@ Niezalecane jest jego użycie. Local file changed during syncing. It will be resumed. - + Lokalny plik uległ zmianie w trakcie synchronizacji. Zostanie wznowiony. @@ -2383,7 +2383,7 @@ Niezalecane jest jego użycie. No subfolders currently on the server. - + Na serwerze nie ma w tej chwili żadnych podkatalogów. @@ -3491,7 +3491,7 @@ Niezalecane jest jego użycie. Ask for confirmation before synchroni&zing folders larger than - + Zapytaj o potwierdzenie przed synchroni&zowaniem folderów większych niż @@ -3628,7 +3628,7 @@ Niezalecane jest jego użycie. Ser&ver Address - + Adres ser&wera @@ -3671,7 +3671,7 @@ Kliknij QT_LAYOUT_DIRECTION - + QT_LAYOUT_DIRECTION @@ -3768,7 +3768,7 @@ Kliknij %n second(s) - + %n sekunda%n sekundy%n sekund%n sekund @@ -3817,7 +3817,7 @@ Kliknij Server version downloaded, copied changed local file into conflict file - + Pobrano wersję z serwera, zmienona wersja lokalna została skopiowana do pliku konfliktu From d34dbbdb0bc58700b0f9bb4e1ed3f9c62c6eab3d Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Mon, 3 Jul 2017 14:51:20 +0200 Subject: [PATCH 054/107] OAuth: Redirects to the server in case of sucessfull login Requires https://github.com/owncloud/oauth2/pull/45 This commit moves the reply after we got the token reply from the server, that allows to reply with an error to the browser if the login does not work. --- src/gui/creds/oauth.cpp | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/src/gui/creds/oauth.cpp b/src/gui/creds/oauth.cpp index 38e279d293..710e190ac1 100644 --- a/src/gui/creds/oauth.cpp +++ b/src/gui/creds/oauth.cpp @@ -29,15 +29,23 @@ OAuth::~OAuth() { } -static void httpReplyAndClose(QTcpSocket *socket, const char *code, const char *html) +static void httpReplyAndClose(QTcpSocket *socket, const char *code, const char *html, + const char *moreHeaders = nullptr) { socket->write("HTTP/1.1 "); socket->write(code); socket->write("\r\nContent-Type: text/html\r\nConnection: close\r\nContent-Length: "); socket->write(QByteArray::number(qstrlen(html))); + if (moreHeaders) { + socket->write("\r\n"); + socket->write(moreHeaders); + } socket->write("\r\n\r\n"); socket->write(html); socket->disconnectFromHost(); + // We don't want that deleting the server too early prevent queued data to be sent on this socket. + // The socket will be deleted after disconnection because disconnected is connected to deleteLater + socket->setParent(nullptr); } void OAuth::start() @@ -64,9 +72,6 @@ void OAuth::start() return; } - // TODO: add redirect to the page on the server - httpReplyAndClose(socket, "200 OK", "

Login Successful

You can close this window.

"); - QString code = rx.cap(1); // The 'code' is the first capture of the regexp QUrl requestToken(_account->url().toString() @@ -79,21 +84,45 @@ void OAuth::start() req.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); auto reply = _account->sendRequest("POST", requestToken, req); QTimer::singleShot(30 * 1000, reply, &QNetworkReply::abort); - QObject::connect(reply, &QNetworkReply::finished, this, [this, reply] { + QObject::connect(reply, &QNetworkReply::finished, this, [this, reply, socket] { auto jsonData = reply->readAll(); QJsonParseError jsonParseError; QJsonObject json = QJsonDocument::fromJson(jsonData, &jsonParseError).object(); QString accessToken = json["access_token"].toString(); QString refreshToken = json["refresh_token"].toString(); QString user = json["user_id"].toString(); + QUrl messageUrl = json["message_url"].toString(); if (reply->error() != QNetworkReply::NoError || jsonParseError.error != QJsonParseError::NoError || json.isEmpty() || refreshToken.isEmpty() || accessToken.isEmpty() || json["token_type"].toString() != QLatin1String("Bearer")) { - qCWarning(lcOauth) << "Error when getting the accessToken" << reply->error() << json << jsonParseError.errorString(); + QString errorReason; + QString errorFromJson = json["error"].toString(); + if (!errorFromJson.isEmpty()) { + errorReason = tr("Error returned from the server: %1") + .arg(errorFromJson.toHtmlEscaped()); + } else if (reply->error() != QNetworkReply::NoError) { + errorReason = tr("There was an error accessing the 'token' endpoint:
%1") + .arg(reply->errorString().toHtmlEscaped()); + } else if (jsonParseError.error != QJsonParseError::NoError) { + errorReason = tr("Could not parse the JSON returned from the server:
%1") + .arg(jsonParseError.errorString()); + } else { + errorReason = tr("The reply from the server did not contain all expected fields"); + } + qCWarning(lcOauth) << "Error when getting the accessToken" << json << errorReason; + httpReplyAndClose(socket, "500 Internal Server Error", + tr("

Login Error

%1

").arg(errorReason).toUtf8().constData()); emit result(Error); return; } + const char *loginSuccessfullHtml = "

Login Successful

You can close this window.

"; + if (messageUrl.isValid()) { + httpReplyAndClose(socket, "303 See Other", loginSuccessfullHtml, + QByteArray("Location: " + messageUrl.toEncoded()).constData()); + } else { + httpReplyAndClose(socket, "200 OK", loginSuccessfullHtml); + } emit result(LoggedIn, user, accessToken, refreshToken); }); }); From 5d67271acddb9173e137f2cf3bbc4a67d64947d5 Mon Sep 17 00:00:00 2001 From: Markus Goetz Date: Tue, 11 Jul 2017 17:14:49 +0200 Subject: [PATCH 055/107] ChangeLog: 2.3.3 --- ChangeLog | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ChangeLog b/ChangeLog index ff7633c23b..949dd29df7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,18 @@ ChangeLog ========= +version 2.3.3 (2017-07-XX) +* Chunking NG: Don't use old chunking on new DAV endpoint (#5855) +* Selective Sync: Skip excluded folders when reading DB, don't let them show errors +* Settings: Make window bigger so Qt version is always visible (#5760) +* Share links: Show warning that public link shares are public +* Downloads: Re-trigger folder discovery on HTTP 404 +* Overlay Icons: Fix potential hangs on Windows +* SyncJournalDB: Don't use ._ as filename pattern if that does not work because of SMB storage settings +* SyncJournalDB: Log reason for sqlite3 opening errors +* Switch Linux build also to Qt 5.6.2 +* Stopped maintaining Qt 4 buildability + version 2.3.2 (2017-05-08) * Fix more crashes (thanks to everyone submitting to our crash reporter!) * Improve compatibility with server 10.0 (#5691, X-OC-Total-Size) From 4fd773d4ae7ac30b7b9e9deb6a5a153ab294b57c Mon Sep 17 00:00:00 2001 From: Samuel Alfageme Date: Tue, 11 Jul 2017 17:29:49 +0200 Subject: [PATCH 056/107] More refs. to corresponding issue/PR on 2.3.3 changelog --- ChangeLog | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ChangeLog b/ChangeLog index 949dd29df7..617d63de75 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,14 +3,14 @@ ChangeLog version 2.3.3 (2017-07-XX) * Chunking NG: Don't use old chunking on new DAV endpoint (#5855) -* Selective Sync: Skip excluded folders when reading DB, don't let them show errors +* Selective Sync: Skip excluded folders when reading DB, don't let them show errors (#5772) * Settings: Make window bigger so Qt version is always visible (#5760) -* Share links: Show warning that public link shares are public -* Downloads: Re-trigger folder discovery on HTTP 404 +* Share links: Show warning that public link shares are public (#5786) +* Downloads: Re-trigger folder discovery on HTTP 404 (#5799) * Overlay Icons: Fix potential hangs on Windows -* SyncJournalDB: Don't use ._ as filename pattern if that does not work because of SMB storage settings +* SyncJournalDB: Don't use ._ as filename pattern if that does not work because of SMB storage settings (#5844) * SyncJournalDB: Log reason for sqlite3 opening errors -* Switch Linux build also to Qt 5.6.2 +* Switch Linux build also to Qt 5.6.2 (#5470) * Stopped maintaining Qt 4 buildability version 2.3.2 (2017-05-08) From c9db5d234f98397f1166ce618c48bf28149324f9 Mon Sep 17 00:00:00 2001 From: Jenkins for ownCloud Date: Wed, 12 Jul 2017 02:18:29 +0200 Subject: [PATCH 057/107] [tx-robot] updated from transifex --- mirall.desktop.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mirall.desktop.in b/mirall.desktop.in index 275f345bf4..55fb89e470 100644 --- a/mirall.desktop.in +++ b/mirall.desktop.in @@ -105,6 +105,9 @@ X-GNOME-Autostart-Delay=3 # Translations +# Translations + + # Translations Comment[oc]=@APPLICATION_NAME@ sincronizacion del client GenericName[oc]=Dorsièr de Sincronizacion From 5ca743dd256bac83b92a3ede48c0368f95c8942b Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Wed, 28 Jun 2017 12:45:54 +0200 Subject: [PATCH 058/107] SyncEngine: Introduce overall errors that are not tied to a file #5746 For now we use them for: * csync errors: This allows them to appear in the sync issues tab * insufficient local disk space, as a summary of individual file errors Insufficient remote space will use them too, as might other issues that are bigger than a single sync item. --- src/gui/folder.cpp | 7 ++-- src/gui/folder.h | 9 +++-- src/gui/issueswidget.cpp | 53 +++++++++++++++++++++++++++--- src/gui/issueswidget.h | 2 ++ src/libsync/owncloudpropagator.cpp | 1 + src/libsync/owncloudpropagator.h | 3 +- src/libsync/progressdispatcher.h | 5 +++ src/libsync/propagatedownload.cpp | 9 +++-- src/libsync/syncengine.cpp | 20 +++++++++++ src/libsync/syncengine.h | 11 +++++++ 10 files changed, 107 insertions(+), 13 deletions(-) diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index e00166262e..670e941dd0 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -106,6 +106,7 @@ Folder::Folder(const FolderDefinition &definition, connect(_engine.data(), SIGNAL(seenLockedFile(QString)), FolderMan::instance(), SLOT(slotSyncOnceFileUnlocks(QString))); connect(_engine.data(), SIGNAL(aboutToPropagate(SyncFileItemVector &)), SLOT(slotLogPropagationStart())); + connect(_engine.data(), SIGNAL(summaryError(QString)), SLOT(slotSyncError(QString))); _scheduleSelfTimer.setSingleShot(true); _scheduleSelfTimer.setInterval(SyncEngine::minimumFileAgeForUpload); @@ -721,10 +722,10 @@ void Folder::setDirtyNetworkLimits() _engine->setNetworkLimits(uploadLimit, downloadLimit); } - -void Folder::slotSyncError(const QString &err) +void Folder::slotSyncError(const QString &message) { - _syncResult.appendErrorString(err); + _syncResult.appendErrorString(message); + emit ProgressDispatcher::instance()->syncError(alias(), message); } void Folder::slotSyncStarted() diff --git a/src/gui/folder.h b/src/gui/folder.h index ce1b3a7d99..602d95b750 100644 --- a/src/gui/folder.h +++ b/src/gui/folder.h @@ -278,11 +278,16 @@ public slots: private slots: void slotSyncStarted(); - void slotSyncError(const QString &); - void slotCsyncUnavailable(); void slotSyncFinished(bool); + /** Adds a error message that's not tied to a specific item. + */ + void slotSyncError(const QString &message); + + void slotCsyncUnavailable(); + void slotFolderDiscovered(bool local, QString folderName); + void slotTransmissionProgress(const ProgressInfo &pi); void slotItemCompleted(const SyncFileItemPtr &); diff --git a/src/gui/issueswidget.cpp b/src/gui/issueswidget.cpp index e32e89aac7..ed1792a1ce 100644 --- a/src/gui/issueswidget.cpp +++ b/src/gui/issueswidget.cpp @@ -49,6 +49,8 @@ IssuesWidget::IssuesWidget(QWidget *parent) this, SLOT(slotProgressInfo(QString, ProgressInfo))); connect(ProgressDispatcher::instance(), SIGNAL(itemCompleted(QString, SyncFileItemPtr)), this, SLOT(slotItemCompleted(QString, SyncFileItemPtr))); + connect(ProgressDispatcher::instance(), SIGNAL(syncError(QString, QString)), + this, SLOT(addLine(QString, QString))); connect(_ui->_treeWidget, SIGNAL(itemActivated(QTreeWidgetItem *, int)), SLOT(slotOpenFile(QTreeWidgetItem *, int))); connect(_ui->copyIssuesButton, SIGNAL(clicked()), SIGNAL(copyToClipboard())); @@ -132,6 +134,15 @@ void IssuesWidget::cleanItems(const QString &folder) emit(issueCountUpdated(_ui->_treeWidget->topLevelItemCount())); } +void IssuesWidget::addItem(QTreeWidgetItem *item) +{ + if (!item) + return; + _ui->_treeWidget->insertTopLevelItem(0, item); + item->setHidden(!shouldBeVisible(item, currentAccountFilter(), currentFolderFilter())); + emit issueCountUpdated(_ui->_treeWidget->topLevelItemCount()); +} + void IssuesWidget::slotOpenFile(QTreeWidgetItem *item, int) { QString folderName = item->data(2, Qt::UserRole).toString(); @@ -164,10 +175,7 @@ void IssuesWidget::slotItemCompleted(const QString &folder, const SyncFileItemPt QTreeWidgetItem *line = ProtocolWidget::createCompletedTreewidgetItem(folder, *item); if (!line) return; - - _ui->_treeWidget->insertTopLevelItem(0, line); - line->setHidden(!shouldBeVisible(line, currentAccountFilter(), currentFolderFilter())); - emit issueCountUpdated(_ui->_treeWidget->topLevelItemCount()); + addItem(line); } void IssuesWidget::slotRefreshIssues() @@ -328,4 +336,41 @@ void IssuesWidget::showFolderErrors(const QString &folderAlias) _ui->showIgnores->setChecked(false); _ui->showWarnings->setChecked(false); } + +void IssuesWidget::addLine(const QString &folderAlias, const QString &message) +{ + SyncFileItem::Status status = SyncFileItem::NormalError; + + auto folder = FolderMan::instance()->folder(folderAlias); + if (!folder) + return; + + QStringList columns; + QDateTime timestamp = QDateTime::currentDateTime(); + const QString timeStr = ProtocolWidget::timeString(timestamp); + const QString longTimeStr = ProtocolWidget::timeString(timestamp, QLocale::LongFormat); + + columns << timeStr; + columns << tr(""); + columns << folder->shortGuiLocalPath(); + columns << message; + + QIcon icon; + if (status == SyncFileItem::NormalError + || status == SyncFileItem::FatalError) { + icon = Theme::instance()->syncStateIcon(SyncResult::Error); + } else if (Progress::isWarningKind(status)) { + icon = Theme::instance()->syncStateIcon(SyncResult::Problem); + } + + QTreeWidgetItem *twitem = new QTreeWidgetItem(columns); + twitem->setData(0, Qt::SizeHintRole, QSize(0, ActivityItemDelegate::rowHeight())); + twitem->setIcon(0, icon); + twitem->setToolTip(0, longTimeStr); + twitem->setToolTip(3, message); + twitem->setData(0, Qt::UserRole, status); + twitem->setData(2, Qt::UserRole, folderAlias); + + addItem(twitem); +} } diff --git a/src/gui/issueswidget.h b/src/gui/issueswidget.h index 8ebe6451d7..e9a0ef52d7 100644 --- a/src/gui/issueswidget.h +++ b/src/gui/issueswidget.h @@ -50,6 +50,7 @@ public: void showFolderErrors(const QString &folderAlias); public slots: + void addLine(const QString &folderAlias, const QString &message); void slotProgressInfo(const QString &folder, const ProgressInfo &progress); void slotItemCompleted(const QString &folder, const SyncFileItemPtr &item); void slotOpenFile(QTreeWidgetItem *item, int); @@ -75,6 +76,7 @@ private: bool shouldBeVisible(QTreeWidgetItem *item, AccountState *filterAccount, const QString &filterFolderAlias) const; void cleanItems(const QString &folder); + void addItem(QTreeWidgetItem *item); Ui::IssuesWidget *_ui; }; diff --git a/src/libsync/owncloudpropagator.cpp b/src/libsync/owncloudpropagator.cpp index 48ba29c0a1..96bb4ca210 100644 --- a/src/libsync/owncloudpropagator.cpp +++ b/src/libsync/owncloudpropagator.cpp @@ -238,6 +238,7 @@ void PropagateItemJob::done(SyncFileItem::Status statusArg, const QString &error _item->_status = SyncFileItem::SoftError; } + // Blacklist handling switch (_item->_status) { case SyncFileItem::SoftError: case SyncFileItem::FatalError: diff --git a/src/libsync/owncloudpropagator.h b/src/libsync/owncloudpropagator.h index 0e7dd573cc..5b769d2f3c 100644 --- a/src/libsync/owncloudpropagator.h +++ b/src/libsync/owncloudpropagator.h @@ -417,7 +417,6 @@ public: */ DiskSpaceResult diskSpaceCheck() const; - private slots: /** Emit the finished signal and make sure it is only emitted once */ @@ -445,6 +444,8 @@ signals: */ void touchedFile(const QString &fileName); + void insufficientLocalStorage(); + private: AccountPtr _account; QScopedPointer _rootJob; diff --git a/src/libsync/progressdispatcher.h b/src/libsync/progressdispatcher.h index 05aa06b4ff..24764b2cb9 100644 --- a/src/libsync/progressdispatcher.h +++ b/src/libsync/progressdispatcher.h @@ -249,6 +249,11 @@ signals: */ void itemCompleted(const QString &folder, const SyncFileItemPtr &item); + /** + * @brief A new folder-wide sync error was seen. + */ + void syncError(const QString &folder, const QString &message); + protected: void setProgressInfo(const QString &folder, const ProgressInfo &progress); diff --git a/src/libsync/propagatedownload.cpp b/src/libsync/propagatedownload.cpp index 8cc87ccac9..b5d6ac0040 100644 --- a/src/libsync/propagatedownload.cpp +++ b/src/libsync/propagatedownload.cpp @@ -427,9 +427,12 @@ void PropagateDownloadFile::startDownload() const auto diskSpaceResult = propagator()->diskSpaceCheck(); if (diskSpaceResult != OwncloudPropagator::DiskSpaceOk) { if (diskSpaceResult == OwncloudPropagator::DiskSpaceFailure) { - _item->_errorMayBeBlacklisted = true; - done(SyncFileItem::NormalError, - tr("The download would reduce free disk space below %1").arg(Utility::octetsToString(freeSpaceLimit()))); + // Using BlacklistedError here will make the error not pop up in the account + // tab: instead we'll generate a general "disk space low" message and show + // these detail errors only in the error view. + done(SyncFileItem::BlacklistedError, + tr("The download would reduce free local disk space below the limit")); + emit propagator()->insufficientLocalStorage(); } else if (diskSpaceResult == OwncloudPropagator::DiskSpaceCritical) { done(SyncFileItem::FatalError, tr("Free space on disk is less than %1").arg(Utility::octetsToString(criticalFreeSpaceLimit()))); diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index 5c1f1f10c7..7389a0ce22 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -24,6 +24,7 @@ #include "csync_private.h" #include "filesystem.h" #include "propagateremotedelete.h" +#include "propagatedownload.h" #include "asserts.h" #ifdef Q_OS_WIN @@ -1025,6 +1026,7 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult) connect(_propagator.data(), SIGNAL(finished(bool)), this, SLOT(slotFinished(bool)), Qt::QueuedConnection); connect(_propagator.data(), SIGNAL(seenLockedFile(QString)), SIGNAL(seenLockedFile(QString))); connect(_propagator.data(), SIGNAL(touchedFile(QString)), SLOT(slotAddTouchedFile(QString))); + connect(_propagator.data(), SIGNAL(insufficientLocalStorage()), SLOT(slotInsufficientLocalStorage())); // apply the network limits to the propagator setNetworkLimits(_uploadLimit, _downloadLimit); @@ -1135,6 +1137,7 @@ void SyncEngine::finalize(bool success) _seenFiles.clear(); _temporarilyUnavailablePaths.clear(); _renamedFolders.clear(); + _uniqueErrors.clear(); _clearTouchedFilesTimer.start(); } @@ -1523,4 +1526,21 @@ void SyncEngine::abort() } } +void SyncEngine::slotSummaryError(const QString &message) +{ + if (_uniqueErrors.contains(message)) + return; + + _uniqueErrors.insert(message); + emit summaryError(message); +} + +void SyncEngine::slotInsufficientLocalStorage() +{ + slotSummaryError( + tr("Disk space is low: Downloads that would reduce free space " + "below %1 were skipped.") + .arg(Utility::octetsToString(freeSpaceLimit()))); +} + } // namespace OCC diff --git a/src/libsync/syncengine.h b/src/libsync/syncengine.h index 8652ef2cb0..bc6b35bc67 100644 --- a/src/libsync/syncengine.h +++ b/src/libsync/syncengine.h @@ -121,6 +121,9 @@ signals: void transmissionProgress(const ProgressInfo &progress); + /// We've produced a new summary error. + void summaryError(const QString &message); + void finished(bool success); void started(); @@ -160,6 +163,11 @@ private slots: /** Wipes the _touchedFiles hash */ void slotClearTouchedFiles(); + /** Emit a summary error, unless it was seen before */ + void slotSummaryError(const QString &message); + + void slotInsufficientLocalStorage(); + private: void handleSyncError(CSYNC *ctx, const char *state); @@ -267,6 +275,9 @@ private: /** For clearing the _touchedFiles variable after sync finished */ QTimer _clearTouchedFilesTimer; + + /** List of unique errors that occurred in a sync run. */ + QSet _uniqueErrors; }; } From c3cb1869526d4f6448712df44b7d38b471f3bcd2 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Tue, 11 Jul 2017 12:52:40 +0200 Subject: [PATCH 059/107] ProgressInfo: Carry a sync status * A bunch of code was determining sync status by ad-hoc comparing some progress info fields. It can now just check the status, making it easier to comprehend. * There's a clear indication for "a new sync is starting", which helps wiping the issues tab at the right time. --- src/gui/folder.cpp | 11 ----------- src/gui/folder.h | 2 -- src/gui/folderstatusmodel.cpp | 11 ++++++++++- src/gui/issueswidget.cpp | 4 +--- src/gui/owncloudgui.cpp | 23 ++++++++++++---------- src/libsync/progressdispatcher.cpp | 7 +++++++ src/libsync/progressdispatcher.h | 31 ++++++++++++++++++++++++++++++ src/libsync/syncengine.cpp | 22 +++++++++++++++++---- src/libsync/syncengine.h | 2 +- 9 files changed, 81 insertions(+), 32 deletions(-) diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index 670e941dd0..cf91c6ac6d 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -97,7 +97,6 @@ Folder::Folder(const FolderDefinition &definition, SLOT(slotAboutToRemoveAllFiles(SyncFileItem::Direction, bool *))); connect(_engine.data(), SIGNAL(aboutToRestoreBackup(bool *)), SLOT(slotAboutToRestoreBackup(bool *))); - connect(_engine.data(), SIGNAL(folderDiscovered(bool, QString)), this, SLOT(slotFolderDiscovered(bool, QString))); connect(_engine.data(), SIGNAL(transmissionProgress(ProgressInfo)), this, SLOT(slotTransmissionProgress(ProgressInfo))); connect(_engine.data(), SIGNAL(itemCompleted(const SyncFileItemPtr &)), this, SLOT(slotItemCompleted(const SyncFileItemPtr &))); @@ -824,16 +823,6 @@ void Folder::slotEmitFinishedDelayed() emit syncFinished(_syncResult); } - -void Folder::slotFolderDiscovered(bool, QString folderName) -{ - ProgressInfo pi; - pi._currentDiscoveredFolder = folderName; - emit progressInfo(pi); - ProgressDispatcher::instance()->setProgressInfo(alias(), pi); -} - - // the progress comes without a folder and the valid path set. Add that here // and hand the result over to the progress dispatcher. void Folder::slotTransmissionProgress(const ProgressInfo &pi) diff --git a/src/gui/folder.h b/src/gui/folder.h index 602d95b750..29c21c76ff 100644 --- a/src/gui/folder.h +++ b/src/gui/folder.h @@ -286,8 +286,6 @@ private slots: void slotCsyncUnavailable(); - void slotFolderDiscovered(bool local, QString folderName); - void slotTransmissionProgress(const ProgressInfo &pi); void slotItemCompleted(const SyncFileItemPtr &); diff --git a/src/gui/folderstatusmodel.cpp b/src/gui/folderstatusmodel.cpp index b0f83e9c8e..7d443b6417 100644 --- a/src/gui/folderstatusmodel.cpp +++ b/src/gui/folderstatusmodel.cpp @@ -891,12 +891,21 @@ void FolderStatusModel::slotSetProgress(const ProgressInfo &progress) << FolderStatusDelegate::WarningCount << Qt::ToolTipRole; - if (!progress._currentDiscoveredFolder.isEmpty()) { + if (progress.status() == ProgressInfo::Discovery + && !progress._currentDiscoveredFolder.isEmpty()) { pi->_overallSyncString = tr("Checking for changes in '%1'").arg(progress._currentDiscoveredFolder); emit dataChanged(index(folderIndex), index(folderIndex), roles); return; } + if (progress.status() == ProgressInfo::Reconcile) { + pi->_overallSyncString = tr("Reconciling changes"); + emit dataChanged(index(folderIndex), index(folderIndex), roles); + return; + } + + // Status is Starting, Propagation or Done + if (!progress._lastCompletedItem.isEmpty() && Progress::isWarningKind(progress._lastCompletedItem._status)) { pi->_warningCount++; diff --git a/src/gui/issueswidget.cpp b/src/gui/issueswidget.cpp index ed1792a1ce..8f56da4cd6 100644 --- a/src/gui/issueswidget.cpp +++ b/src/gui/issueswidget.cpp @@ -160,11 +160,9 @@ void IssuesWidget::slotOpenFile(QTreeWidgetItem *item, int) void IssuesWidget::slotProgressInfo(const QString &folder, const ProgressInfo &progress) { - if (!progress.isUpdatingEstimates()) { + if (progress.status() == ProgressInfo::Starting) { // The sync is restarting, clean the old items cleanItems(folder); - } else if (progress.completedFiles() >= progress.totalFiles()) { - //Sync completed } } diff --git a/src/gui/owncloudgui.cpp b/src/gui/owncloudgui.cpp index 38d1caff92..65350f3677 100644 --- a/src/gui/owncloudgui.cpp +++ b/src/gui/owncloudgui.cpp @@ -748,10 +748,19 @@ void ownCloudGui::slotUpdateProgress(const QString &folder, const ProgressInfo & { Q_UNUSED(folder); - if (!progress._currentDiscoveredFolder.isEmpty()) { - _actionStatus->setText(tr("Checking for changes in '%1'") - .arg(progress._currentDiscoveredFolder)); - } else if (progress.totalSize() == 0) { + if (progress.status() == ProgressInfo::Discovery) { + if (!progress._currentDiscoveredFolder.isEmpty()) { + _actionStatus->setText(tr("Checking for changes in '%1'") + .arg(progress._currentDiscoveredFolder)); + } + } else if (progress.status() == ProgressInfo::Done) { + QTimer::singleShot(2000, this, SLOT(slotDisplayIdle())); + } + if (progress.status() != ProgressInfo::Propagation) { + return; + } + + if (progress.totalSize() == 0) { quint64 currentFile = progress.currentFile(); quint64 totalFileCount = qMax(progress.totalFiles(), currentFile); QString msg; @@ -814,12 +823,6 @@ void ownCloudGui::slotUpdateProgress(const QString &folder, const ProgressInfo & slotRebuildRecentMenus(); } } - - if (progress.isUpdatingEstimates() - && progress.completedFiles() >= progress.totalFiles() - && progress._currentDiscoveredFolder.isEmpty()) { - QTimer::singleShot(2000, this, SLOT(slotDisplayIdle())); - } } void ownCloudGui::slotDisplayIdle() diff --git a/src/libsync/progressdispatcher.cpp b/src/libsync/progressdispatcher.cpp index 2f86888ae4..d5b3f20f50 100644 --- a/src/libsync/progressdispatcher.cpp +++ b/src/libsync/progressdispatcher.cpp @@ -136,6 +136,8 @@ ProgressInfo::ProgressInfo() void ProgressInfo::reset() { + _status = Starting; + _currentItems.clear(); _currentDiscoveredFolder.clear(); _sizeProgress = Progress(); @@ -151,6 +153,11 @@ void ProgressInfo::reset() _lastCompletedItem = SyncFileItem(); } +ProgressInfo::Status ProgressInfo::status() const +{ + return _status; +} + void ProgressInfo::startEstimateUpdates() { _updateEstimatesTimer.start(1000); diff --git a/src/libsync/progressdispatcher.h b/src/libsync/progressdispatcher.h index 24764b2cb9..e968216996 100644 --- a/src/libsync/progressdispatcher.h +++ b/src/libsync/progressdispatcher.h @@ -41,6 +41,35 @@ public: */ void reset(); + /** Records the status of the sync run + */ + enum Status { + /// Emitted once at start + Starting, + + /** + * Emitted once without _currentDiscoveredFolder when it starts, + * then for each folder. + */ + Discovery, + + /// Emitted once when reconcile starts + Reconcile, + + /// Emitted during propagation, with progress data + Propagation, + + /** + * Emitted once when done + * + * Except when SyncEngine jumps directly to finalize() without going + * through slotFinished(). + */ + Done + }; + + Status status() const; + /** * Called when propagation starts. * @@ -140,6 +169,8 @@ public: friend class ProgressInfo; }; + Status _status; + struct OWNCLOUDSYNC_EXPORT ProgressItem { SyncFileItem _item; diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index 7389a0ce22..e73777f964 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -820,8 +820,12 @@ void SyncEngine::startSync() _csync_ctx->callbacks.checksum_userdata = &_checksum_hook; _stopWatch.start(); + _progressInfo->_status = ProgressInfo::Starting; + emit transmissionProgress(*_progressInfo); qCInfo(lcEngine) << "#### Discovery start ####################################################"; + _progressInfo->_status = ProgressInfo::Discovery; + emit transmissionProgress(*_progressInfo); // Usually the discovery runs in the background: We want to avoid // stealing too much time from other processes that the user might @@ -855,7 +859,7 @@ void SyncEngine::startSync() discoveryJob->moveToThread(&_thread); connect(discoveryJob, SIGNAL(finished(int)), this, SLOT(slotDiscoveryJobFinished(int))); connect(discoveryJob, SIGNAL(folderDiscovered(bool, QString)), - this, SIGNAL(folderDiscovered(bool, QString))); + this, SLOT(slotFolderDiscovered(bool, QString))); connect(discoveryJob, SIGNAL(newBigFolder(QString, bool)), this, SIGNAL(newBigFolder(QString, bool))); @@ -869,6 +873,12 @@ void SyncEngine::startSync() QMetaObject::invokeMethod(discoveryJob, "start", Qt::QueuedConnection); } +void SyncEngine::slotFolderDiscovered(bool /*local*/, const QString &folder) +{ + _progressInfo->_currentDiscoveredFolder = folder; + emit transmissionProgress(*_progressInfo); +} + void SyncEngine::slotRootEtagReceived(const QString &e) { if (_remoteRootEtag.isEmpty()) { @@ -880,9 +890,6 @@ void SyncEngine::slotRootEtagReceived(const QString &e) void SyncEngine::slotDiscoveryJobFinished(int discoveryResult) { - // To clean the progress info - emit folderDiscovered(false, QString()); - if (discoveryResult < 0) { handleSyncError(_csync_ctx, "csync_update"); return; @@ -900,6 +907,10 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult) _journal->commitIfNeededAndStartNewTransaction("Post discovery"); } + _progressInfo->_currentDiscoveredFolder.clear(); + _progressInfo->_status = ProgressInfo::Reconcile; + emit transmissionProgress(*_progressInfo); + if (csync_reconcile(_csync_ctx) < 0) { handleSyncError(_csync_ctx, "csync_reconcile"); return; @@ -997,7 +1008,9 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult) // To announce the beginning of the sync emit aboutToPropagate(syncItems); + // it's important to do this before ProgressInfo::start(), to announce start of new sync + _progressInfo->_status = ProgressInfo::Propagation; emit transmissionProgress(*_progressInfo); _progressInfo->startEstimateUpdates(); @@ -1111,6 +1124,7 @@ void SyncEngine::slotFinished(bool success) // files needed propagation, but clear the lastCompletedItem // so we don't count this twice (like Recent Files) _progressInfo->_lastCompletedItem = SyncFileItem(); + _progressInfo->_status = ProgressInfo::Done; emit transmissionProgress(*_progressInfo); finalize(success); diff --git a/src/libsync/syncengine.h b/src/libsync/syncengine.h index bc6b35bc67..deb87ef409 100644 --- a/src/libsync/syncengine.h +++ b/src/libsync/syncengine.h @@ -109,7 +109,6 @@ signals: // During update, before reconcile void rootEtag(QString); - void folderDiscovered(bool local, const QString &folderUrl); // before actual syncing (after update+reconcile) for each item void syncItemDiscovered(const SyncFileItem &); @@ -150,6 +149,7 @@ signals: void seenLockedFile(const QString &fileName); private slots: + void slotFolderDiscovered(bool local, const QString &folder); void slotRootEtagReceived(const QString &); void slotItemCompleted(const SyncFileItemPtr &item); void slotFinished(bool success); From 5d90b48790395daf63c61045b352160060485f35 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Fri, 7 Jul 2017 14:12:10 +0200 Subject: [PATCH 060/107] PropagateUpload: Put upload error handling in one function --- src/libsync/propagateupload.cpp | 23 +++++++++++++++++++++++ src/libsync/propagateupload.h | 5 +++++ src/libsync/propagateuploadng.cpp | 27 ++------------------------- src/libsync/propagateuploadv1.cpp | 20 +------------------- 4 files changed, 31 insertions(+), 44 deletions(-) diff --git a/src/libsync/propagateupload.cpp b/src/libsync/propagateupload.cpp index f3b0e8336b..041a944cb8 100644 --- a/src/libsync/propagateupload.cpp +++ b/src/libsync/propagateupload.cpp @@ -522,6 +522,29 @@ void PropagateUploadFileCommon::checkResettingErrors() } } +void PropagateUploadFileCommon::commonErrorHandling(AbstractNetworkJob *job) +{ + QByteArray replyContent; + QString errorString = job->errorStringParsingBody(&replyContent); + qCDebug(lcPropagateUpload) << replyContent; // display the XML error in the debug + + if (_item->_httpErrorCode == 412) { + // Precondition Failed: Either an etag or a checksum mismatch. + + // Maybe the bad etag is in the database, we need to clear the + // parent folder etag so we won't read from DB next sync. + propagator()->_journal->avoidReadFromDbOnNextSync(_item->_file); + propagator()->_anotherSyncNeeded = true; + } + + // Ensure errors that should eventually reset the chunked upload are tracked. + checkResettingErrors(); + + SyncFileItem::Status status = classifyError(job->reply()->error(), _item->_httpErrorCode, + &propagator()->_anotherSyncNeeded); + abortWithError(status, errorString); +} + void PropagateUploadFileCommon::slotJobDestroyed(QObject *job) { _jobs.erase(std::remove(_jobs.begin(), _jobs.end(), job), _jobs.end()); diff --git a/src/libsync/propagateupload.h b/src/libsync/propagateupload.h index 030c43020a..8d6b1d0b63 100644 --- a/src/libsync/propagateupload.h +++ b/src/libsync/propagateupload.h @@ -277,6 +277,11 @@ protected: */ void checkResettingErrors(); + /** + * Error handling functionality that is shared between jobs. + */ + void commonErrorHandling(AbstractNetworkJob *job); + // Bases headers that need to be sent with every chunk QMap headers(); }; diff --git a/src/libsync/propagateuploadng.cpp b/src/libsync/propagateuploadng.cpp index dd918e80b9..ce524a6308 100644 --- a/src/libsync/propagateuploadng.cpp +++ b/src/libsync/propagateuploadng.cpp @@ -366,16 +366,7 @@ void PropagateUploadFileNG::slotPutFinished() if (err != QNetworkReply::NoError) { _item->_httpErrorCode = job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - QByteArray replyContent; - QString errorString = job->errorStringParsingBody(&replyContent); - qCDebug(lcPropagateUpload) << replyContent; // display the XML error in the debug - - // Ensure errors that should eventually reset the chunked upload are tracked. - checkResettingErrors(); - - SyncFileItem::Status status = classifyError(err, _item->_httpErrorCode, - &propagator()->_anotherSyncNeeded); - abortWithError(status, errorString); + commonErrorHandling(job); return; } @@ -459,21 +450,7 @@ void PropagateUploadFileNG::slotMoveJobFinished() _item->_httpErrorCode = job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (err != QNetworkReply::NoError) { - if (_item->_httpErrorCode == 412) { - // Precondition Failed: Either an etag or a checksum mismatch. - - // Maybe the bad etag is in the database, we need to clear the - // parent folder etag so we won't read from DB next sync. - propagator()->_journal->avoidReadFromDbOnNextSync(_item->_file); - propagator()->_anotherSyncNeeded = true; - } - - // Ensure errors that should eventually reset the chunked upload are tracked. - checkResettingErrors(); - - SyncFileItem::Status status = classifyError(err, _item->_httpErrorCode, - &propagator()->_anotherSyncNeeded); - abortWithError(status, job->errorStringParsingBody()); + commonErrorHandling(job); return; } if (_item->_httpErrorCode != 201 && _item->_httpErrorCode != 204) { diff --git a/src/libsync/propagateuploadv1.cpp b/src/libsync/propagateuploadv1.cpp index b48b433ab7..b8cdd3daf8 100644 --- a/src/libsync/propagateuploadv1.cpp +++ b/src/libsync/propagateuploadv1.cpp @@ -201,25 +201,7 @@ void PropagateUploadFileV1::slotPutFinished() "It is restored and your edit is in the conflict file."))) { return; } - QByteArray replyContent; - QString errorString = job->errorStringParsingBody(&replyContent); - qCDebug(lcPropagateUpload) << replyContent; // display the XML error in the debug - - if (_item->_httpErrorCode == 412) { - // Precondition Failed: Either an etag or a checksum mismatch. - - // Maybe the bad etag is in the database, we need to clear the - // parent folder etag so we won't read from DB next sync. - propagator()->_journal->avoidReadFromDbOnNextSync(_item->_file); - propagator()->_anotherSyncNeeded = true; - } - - // Ensure errors that should eventually reset the chunked upload are tracked. - checkResettingErrors(); - - SyncFileItem::Status status = classifyError(err, _item->_httpErrorCode, - &propagator()->_anotherSyncNeeded); - abortWithError(status, errorString); + commonErrorHandling(job); return; } From cd1b89475c65807a52fff22f9c3a3cbb6d44b998 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Fri, 7 Jul 2017 15:11:00 +0200 Subject: [PATCH 061/107] PropagateUpload: Better messaging for 507 #5537 It now produces a summary error message indicating the problem. Adjust blacklist database table to contain 'errorCategory'. This is useful for two things: - Reestablishing summary messages based on blacklisted errors. For example if we don't retry a 507ed file, we still want to show the message about space on the server - Selectively wiping the blacklist: When we have ui for something like "I deleted some files, please retry all files now!", we want to delete all blacklist entries of a specific category only. --- src/libsync/owncloudpropagator.cpp | 24 ++++++++++++------------ src/libsync/owncloudpropagator.h | 1 + src/libsync/propagateupload.cpp | 9 +++++++++ src/libsync/syncengine.cpp | 10 ++++++++++ src/libsync/syncengine.h | 1 + src/libsync/syncjournaldb.cpp | 26 ++++++++++++++++++++------ src/libsync/syncjournaldb.h | 2 +- src/libsync/syncjournalfilerecord.cpp | 14 ++++++++++++++ src/libsync/syncjournalfilerecord.h | 13 +++++++++++++ 9 files changed, 81 insertions(+), 19 deletions(-) diff --git a/src/libsync/owncloudpropagator.cpp b/src/libsync/owncloudpropagator.cpp index 96bb4ca210..016f0cdc84 100644 --- a/src/libsync/owncloudpropagator.cpp +++ b/src/libsync/owncloudpropagator.cpp @@ -129,15 +129,7 @@ static time_t getMaxBlacklistTime() static SyncJournalErrorBlacklistRecord createBlacklistEntry( const SyncJournalErrorBlacklistRecord &old, const SyncFileItem &item) { - SyncJournalErrorBlacklistRecord entry; - - entry._errorString = item._errorString; - entry._lastTryModtime = item._modtime; - entry._lastTryEtag = item._etag; - entry._lastTryTime = Utility::qDateTimeToTime_t(QDateTime::currentDateTime()); - entry._file = item._file; - entry._renameTarget = item._renameTarget; - + auto entry = SyncJournalErrorBlacklistRecord::fromSyncFileItem(item); entry._retryCount = old._retryCount + 1; static time_t minBlacklistTime(getMinBlacklistTime()); @@ -162,6 +154,10 @@ static SyncJournalErrorBlacklistRecord createBlacklistEntry( entry._ignoreDuration = 0; } + if (item._httpErrorCode == 507) { + entry._errorCategory = SyncJournalErrorBlacklistRecord::InsufficientRemoteStorage; + } + return entry; } @@ -189,7 +185,7 @@ static void blacklistUpdate(SyncJournalDb *journal, SyncFileItem &item) } auto newEntry = createBlacklistEntry(oldEntry, item); - journal->updateErrorBlacklistEntry(newEntry); + journal->setErrorBlacklistEntry(newEntry); // Suppress the error if it was and continues to be blacklisted. // An ignoreDuration of 0 mean we're tracking the error, but not actively @@ -243,8 +239,13 @@ void PropagateItemJob::done(SyncFileItem::Status statusArg, const QString &error case SyncFileItem::SoftError: case SyncFileItem::FatalError: case SyncFileItem::NormalError: + case SyncFileItem::BlacklistedError: // Check the blacklist, possibly adjusting the item (including its status) - blacklistUpdate(propagator()->_journal, *_item); + // but not if this status comes from blacklisting in the first place + if (!(_item->_status == SyncFileItem::BlacklistedError + && _item->_instruction == CSYNC_INSTRUCTION_IGNORE)) { + blacklistUpdate(propagator()->_journal, *_item); + } break; case SyncFileItem::Success: case SyncFileItem::Restoration: @@ -260,7 +261,6 @@ void PropagateItemJob::done(SyncFileItem::Status statusArg, const QString &error case SyncFileItem::Conflict: case SyncFileItem::FileIgnored: case SyncFileItem::NoStatus: - case SyncFileItem::BlacklistedError: // nothing break; } diff --git a/src/libsync/owncloudpropagator.h b/src/libsync/owncloudpropagator.h index 5b769d2f3c..02145fa193 100644 --- a/src/libsync/owncloudpropagator.h +++ b/src/libsync/owncloudpropagator.h @@ -445,6 +445,7 @@ signals: void touchedFile(const QString &fileName); void insufficientLocalStorage(); + void insufficientRemoteStorage(); private: AccountPtr _account; diff --git a/src/libsync/propagateupload.cpp b/src/libsync/propagateupload.cpp index 041a944cb8..757aee1c44 100644 --- a/src/libsync/propagateupload.cpp +++ b/src/libsync/propagateupload.cpp @@ -542,6 +542,15 @@ void PropagateUploadFileCommon::commonErrorHandling(AbstractNetworkJob *job) SyncFileItem::Status status = classifyError(job->reply()->error(), _item->_httpErrorCode, &propagator()->_anotherSyncNeeded); + + if (_item->_httpErrorCode == 507) { + // Insufficient remote storage. + _item->_errorMayBeBlacklisted = true; + status = SyncFileItem::BlacklistedError; + errorString = tr("Upload of %1 exceeds the quota for the folder").arg(Utility::octetsToString(_item->_size)); + emit propagator()->insufficientRemoteStorage(); + } + abortWithError(status, errorString); } diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index e73777f964..65fdd61135 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -272,6 +272,10 @@ bool SyncEngine::checkErrorBlacklisting(SyncFileItem &item) auto waitSecondsStr = Utility::durationToDescriptiveString1(1000 * waitSeconds); item._errorString = tr("%1 (skipped due to earlier error, trying again in %2)").arg(entry._errorString, waitSecondsStr); + if (entry._errorCategory == SyncJournalErrorBlacklistRecord::InsufficientRemoteStorage) { + slotInsufficientRemoteStorage(); + } + return true; } @@ -1040,6 +1044,7 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult) connect(_propagator.data(), SIGNAL(seenLockedFile(QString)), SIGNAL(seenLockedFile(QString))); connect(_propagator.data(), SIGNAL(touchedFile(QString)), SLOT(slotAddTouchedFile(QString))); connect(_propagator.data(), SIGNAL(insufficientLocalStorage()), SLOT(slotInsufficientLocalStorage())); + connect(_propagator.data(), SIGNAL(insufficientRemoteStorage()), SLOT(slotInsufficientRemoteStorage())); // apply the network limits to the propagator setNetworkLimits(_uploadLimit, _downloadLimit); @@ -1557,4 +1562,9 @@ void SyncEngine::slotInsufficientLocalStorage() .arg(Utility::octetsToString(freeSpaceLimit()))); } +void SyncEngine::slotInsufficientRemoteStorage() +{ + slotSummaryError(tr("There is insufficient space available on the server for some uploads.")); +} + } // namespace OCC diff --git a/src/libsync/syncengine.h b/src/libsync/syncengine.h index deb87ef409..e307a900b7 100644 --- a/src/libsync/syncengine.h +++ b/src/libsync/syncengine.h @@ -167,6 +167,7 @@ private slots: void slotSummaryError(const QString &message); void slotInsufficientLocalStorage(); + void slotInsufficientRemoteStorage(); private: void handleSyncError(CSYNC *ctx, const char *state); diff --git a/src/libsync/syncjournaldb.cpp b/src/libsync/syncjournaldb.cpp index b6bcff4d0d..5b43a3595e 100644 --- a/src/libsync/syncjournaldb.cpp +++ b/src/libsync/syncjournaldb.cpp @@ -564,7 +564,7 @@ bool SyncJournalDb::checkConnect() return sqlFail("prepare _deleteFileRecordRecursively", *_deleteFileRecordRecursively); } - QString sql("SELECT lastTryEtag, lastTryModtime, retrycount, errorstring, lastTryTime, ignoreDuration, renameTarget " + QString sql("SELECT lastTryEtag, lastTryModtime, retrycount, errorstring, lastTryTime, ignoreDuration, renameTarget, errorCategory " "FROM blacklist WHERE path=?1"); if (Utility::fsCasePreserving()) { // if the file system is case preserving we have to check the blacklist @@ -578,8 +578,8 @@ bool SyncJournalDb::checkConnect() _setErrorBlacklistQuery.reset(new SqlQuery(_db)); if (_setErrorBlacklistQuery->prepare("INSERT OR REPLACE INTO blacklist " - "(path, lastTryEtag, lastTryModtime, retrycount, errorstring, lastTryTime, ignoreDuration, renameTarget) " - "VALUES ( ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)")) { + "(path, lastTryEtag, lastTryModtime, retrycount, errorstring, lastTryTime, ignoreDuration, renameTarget, errorCategory) " + "VALUES ( ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)")) { return sqlFail("prepare _setErrorBlacklistQuery", *_setErrorBlacklistQuery); } @@ -800,7 +800,17 @@ bool SyncJournalDb::updateErrorBlacklistTableStructure() sqlFail("updateBlacklistTableStructure: Add renameTarget", query); re = false; } - commitInternal("update database structure: add lastTryTime, ignoreDuration cols"); + commitInternal("update database structure: add renameTarget col"); + } + + if (columns.indexOf(QLatin1String("errorCategory")) == -1) { + SqlQuery query(_db); + query.prepare("ALTER TABLE blacklist ADD COLUMN errorCategory INTEGER(8);"); + if (!query.exec()) { + sqlFail("updateBlacklistTableStructure: Add errorCategory", query); + re = false; + } + commitInternal("update database structure: add errorCategory col"); } SqlQuery query(_db); @@ -1410,6 +1420,8 @@ SyncJournalErrorBlacklistRecord SyncJournalDb::errorBlacklistEntry(const QString entry._lastTryTime = _getErrorBlacklistQuery->int64Value(4); entry._ignoreDuration = _getErrorBlacklistQuery->int64Value(5); entry._renameTarget = _getErrorBlacklistQuery->stringValue(6); + entry._errorCategory = static_cast( + _getErrorBlacklistQuery->intValue(7)); entry._file = file; } _getErrorBlacklistQuery->reset_and_clear_bindings(); @@ -1501,13 +1513,14 @@ void SyncJournalDb::wipeErrorBlacklistEntry(const QString &file) } } -void SyncJournalDb::updateErrorBlacklistEntry(const SyncJournalErrorBlacklistRecord &item) +void SyncJournalDb::setErrorBlacklistEntry(const SyncJournalErrorBlacklistRecord &item) { QMutexLocker locker(&_mutex); qCInfo(lcDb) << "Setting blacklist entry for " << item._file << item._retryCount << item._errorString << item._lastTryTime << item._ignoreDuration - << item._lastTryModtime << item._lastTryEtag << item._renameTarget; + << item._lastTryModtime << item._lastTryEtag << item._renameTarget + << item._errorCategory; if (!checkConnect()) { return; @@ -1521,6 +1534,7 @@ void SyncJournalDb::updateErrorBlacklistEntry(const SyncJournalErrorBlacklistRec _setErrorBlacklistQuery->bindValue(6, QString::number(item._lastTryTime)); _setErrorBlacklistQuery->bindValue(7, QString::number(item._ignoreDuration)); _setErrorBlacklistQuery->bindValue(8, item._renameTarget); + _setErrorBlacklistQuery->bindValue(9, item._errorCategory); _setErrorBlacklistQuery->exec(); _setErrorBlacklistQuery->reset_and_clear_bindings(); } diff --git a/src/libsync/syncjournaldb.h b/src/libsync/syncjournaldb.h index 3f6c89e435..15f1d01cab 100644 --- a/src/libsync/syncjournaldb.h +++ b/src/libsync/syncjournaldb.h @@ -71,7 +71,7 @@ public: static qint64 getPHash(const QString &); - void updateErrorBlacklistEntry(const SyncJournalErrorBlacklistRecord &item); + void setErrorBlacklistEntry(const SyncJournalErrorBlacklistRecord &item); void wipeErrorBlacklistEntry(const QString &file); int wipeErrorBlacklist(); int errorBlackListEntryCount(); diff --git a/src/libsync/syncjournalfilerecord.cpp b/src/libsync/syncjournalfilerecord.cpp index 96bde3eb6d..024c327460 100644 --- a/src/libsync/syncjournalfilerecord.cpp +++ b/src/libsync/syncjournalfilerecord.cpp @@ -120,6 +120,20 @@ QByteArray SyncJournalFileRecord::numericFileId() const return _fileId; } +SyncJournalErrorBlacklistRecord SyncJournalErrorBlacklistRecord::fromSyncFileItem( + const SyncFileItem &item) +{ + SyncJournalErrorBlacklistRecord record; + record._file = item._file; + record._errorString = item._errorString; + record._lastTryModtime = item._modtime; + record._lastTryEtag = item._etag; + record._lastTryTime = Utility::qDateTimeToTime_t(QDateTime::currentDateTime()); + record._renameTarget = item._renameTarget; + record._retryCount = 1; + return record; +} + bool SyncJournalErrorBlacklistRecord::isValid() const { return !_file.isEmpty() diff --git a/src/libsync/syncjournalfilerecord.h b/src/libsync/syncjournalfilerecord.h index 8f33331803..6ef0ba169e 100644 --- a/src/libsync/syncjournalfilerecord.h +++ b/src/libsync/syncjournalfilerecord.h @@ -75,19 +75,32 @@ operator==(const SyncJournalFileRecord &lhs, class SyncJournalErrorBlacklistRecord { public: + enum Category { + /// Normal errors have no special behavior + Normal = 0, + /// These get a special summary message + InsufficientRemoteStorage + }; + SyncJournalErrorBlacklistRecord() : _retryCount(0) + , _errorCategory(Category::Normal) , _lastTryModtime(0) , _lastTryTime(0) , _ignoreDuration(0) { } + /// Create a record based on an item. + static SyncJournalErrorBlacklistRecord fromSyncFileItem(const SyncFileItem &item); + /// The number of times the operation was unsuccessful so far. int _retryCount; /// The last error string. QString _errorString; + /// The error category. Sometimes used for special actions. + Category _errorCategory; time_t _lastTryModtime; QByteArray _lastTryEtag; From e13b618a6574f3a47c12b12af6586dfc4e3efb69 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Tue, 11 Jul 2017 15:50:56 +0200 Subject: [PATCH 062/107] Add ElidedLabel A label that adjusts its text based on Qt::TextElideMode. --- src/gui/CMakeLists.txt | 1 + src/gui/elidedlabel.cpp | 49 +++++++++++++++++++++++++++++++++++++++++ src/gui/elidedlabel.h | 44 ++++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 src/gui/elidedlabel.cpp create mode 100644 src/gui/elidedlabel.h diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index d123d58c52..9011c07ab6 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -95,6 +95,7 @@ set(client_SRCS notificationconfirmjob.cpp servernotificationhandler.cpp guiutility.cpp + elidedlabel.cpp creds/credentialsfactory.cpp creds/httpcredentialsgui.cpp creds/oauth.cpp diff --git a/src/gui/elidedlabel.cpp b/src/gui/elidedlabel.cpp new file mode 100644 index 0000000000..a5ce9bb227 --- /dev/null +++ b/src/gui/elidedlabel.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) by Christian Kamm + * + * 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 "elidedlabel.h" + +#include + +namespace OCC { + +ElidedLabel::ElidedLabel(const QString &text, QWidget *parent) + : QLabel(text, parent) + , _text(text) + , _elideMode(Qt::ElideNone) +{ +} + +void ElidedLabel::setText(const QString &text) +{ + _text = text; + QLabel::setText(text); + update(); +} + +void ElidedLabel::setElideMode(Qt::TextElideMode elideMode) +{ + _elideMode = elideMode; + update(); +} + +void ElidedLabel::resizeEvent(QResizeEvent *event) +{ + QLabel::resizeEvent(event); + + QFontMetrics fm = fontMetrics(); + QString elided = fm.elidedText(_text, _elideMode, event->size().width()); + QLabel::setText(elided); +} +} diff --git a/src/gui/elidedlabel.h b/src/gui/elidedlabel.h new file mode 100644 index 0000000000..a06100c5eb --- /dev/null +++ b/src/gui/elidedlabel.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) by Christian Kamm + * + * 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 ELIDEDLABEL_H +#define ELIDEDLABEL_H + +#include + +namespace OCC { + +/// Label that can elide its text +class ElidedLabel : public QLabel +{ + Q_OBJECT +public: + explicit ElidedLabel(const QString &text, QWidget *parent = 0); + + void setText(const QString &text); + const QString &text() const { return _text; } + + void setElideMode(Qt::TextElideMode elideMode); + Qt::TextElideMode elideMode() const { return _elideMode; } + +protected: + void resizeEvent(QResizeEvent *event) override; + +private: + QString _text; + Qt::TextElideMode _elideMode; +}; +} + +#endif From a76670f3b879d5f48fd21c901d034d59d95f206a Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Tue, 11 Jul 2017 15:53:36 +0200 Subject: [PATCH 063/107] IssuesWidget: Hide folder column when possible --- src/gui/issueswidget.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/issueswidget.cpp b/src/gui/issueswidget.cpp index 8f56da4cd6..bed1d02403 100644 --- a/src/gui/issueswidget.cpp +++ b/src/gui/issueswidget.cpp @@ -186,6 +186,8 @@ void IssuesWidget::slotRefreshIssues() auto item = tree->topLevelItem(i); item->setHidden(!shouldBeVisible(item, filterAccount, filterFolderAlias)); } + + _ui->_treeWidget->setColumnHidden(2, !filterFolderAlias.isEmpty()); } void IssuesWidget::slotAccountAdded(AccountState *account) From 971abaea801a413921421a5887463c4234c0c116 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Tue, 11 Jul 2017 15:54:01 +0200 Subject: [PATCH 064/107] IssuesWidget: Add button to retry 507 errors #5537 Since these errors are blacklisted, it can take up to 24h to retry items that had a 507 error for a while. This way users can intervene and cause an upload attempt immediately. --- src/gui/folder.cpp | 7 ++- src/gui/folder.h | 2 +- src/gui/issueswidget.cpp | 76 +++++++++++++++++++++++++------- src/gui/issueswidget.h | 8 +++- src/gui/issueswidget.ui | 3 -- src/libsync/progressdispatcher.h | 12 ++++- src/libsync/syncengine.cpp | 37 ++++++++++------ src/libsync/syncengine.h | 6 +-- src/libsync/syncjournaldb.cpp | 14 ++++++ src/libsync/syncjournaldb.h | 3 +- 10 files changed, 126 insertions(+), 42 deletions(-) diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index cf91c6ac6d..7282e431f6 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -89,7 +89,6 @@ Folder::Folder(const FolderDefinition &definition, connect(_engine.data(), SIGNAL(started()), SLOT(slotSyncStarted()), Qt::QueuedConnection); connect(_engine.data(), SIGNAL(finished(bool)), SLOT(slotSyncFinished(bool)), Qt::QueuedConnection); - connect(_engine.data(), SIGNAL(csyncError(QString)), SLOT(slotSyncError(QString)), Qt::QueuedConnection); connect(_engine.data(), SIGNAL(csyncUnavailable()), SLOT(slotCsyncUnavailable()), Qt::QueuedConnection); //direct connection so the message box is blocking the sync. @@ -105,7 +104,7 @@ Folder::Folder(const FolderDefinition &definition, connect(_engine.data(), SIGNAL(seenLockedFile(QString)), FolderMan::instance(), SLOT(slotSyncOnceFileUnlocks(QString))); connect(_engine.data(), SIGNAL(aboutToPropagate(SyncFileItemVector &)), SLOT(slotLogPropagationStart())); - connect(_engine.data(), SIGNAL(summaryError(QString)), SLOT(slotSyncError(QString))); + connect(_engine.data(), &SyncEngine::syncError, this, &Folder::slotSyncError); _scheduleSelfTimer.setSingleShot(true); _scheduleSelfTimer.setInterval(SyncEngine::minimumFileAgeForUpload); @@ -721,10 +720,10 @@ void Folder::setDirtyNetworkLimits() _engine->setNetworkLimits(uploadLimit, downloadLimit); } -void Folder::slotSyncError(const QString &message) +void Folder::slotSyncError(const QString &message, ErrorCategory category) { _syncResult.appendErrorString(message); - emit ProgressDispatcher::instance()->syncError(alias(), message); + emit ProgressDispatcher::instance()->syncError(alias(), message, category); } void Folder::slotSyncStarted() diff --git a/src/gui/folder.h b/src/gui/folder.h index 29c21c76ff..1c67c07226 100644 --- a/src/gui/folder.h +++ b/src/gui/folder.h @@ -282,7 +282,7 @@ private slots: /** Adds a error message that's not tied to a specific item. */ - void slotSyncError(const QString &message); + void slotSyncError(const QString &message, ErrorCategory category = ErrorCategory::Normal); void slotCsyncUnavailable(); diff --git a/src/gui/issueswidget.cpp b/src/gui/issueswidget.cpp index bed1d02403..51b47835fa 100644 --- a/src/gui/issueswidget.cpp +++ b/src/gui/issueswidget.cpp @@ -32,6 +32,8 @@ #include "accountstate.h" #include "account.h" #include "accountmanager.h" +#include "syncjournalfilerecord.h" +#include "elidedlabel.h" #include "ui_issueswidget.h" @@ -49,8 +51,8 @@ IssuesWidget::IssuesWidget(QWidget *parent) this, SLOT(slotProgressInfo(QString, ProgressInfo))); connect(ProgressDispatcher::instance(), SIGNAL(itemCompleted(QString, SyncFileItemPtr)), this, SLOT(slotItemCompleted(QString, SyncFileItemPtr))); - connect(ProgressDispatcher::instance(), SIGNAL(syncError(QString, QString)), - this, SLOT(addLine(QString, QString))); + connect(ProgressDispatcher::instance(), &ProgressDispatcher::syncError, + this, &IssuesWidget::addError); connect(_ui->_treeWidget, SIGNAL(itemActivated(QTreeWidgetItem *, int)), SLOT(slotOpenFile(QTreeWidgetItem *, int))); connect(_ui->copyIssuesButton, SIGNAL(clicked()), SIGNAL(copyToClipboard())); @@ -138,7 +140,20 @@ void IssuesWidget::addItem(QTreeWidgetItem *item) { if (!item) return; - _ui->_treeWidget->insertTopLevelItem(0, item); + + int insertLoc = 0; + + // Insert item specific errors behind the others + if (!item->text(1).isEmpty()) { + for (int i = 0; i < _ui->_treeWidget->topLevelItemCount(); ++i) { + if (!_ui->_treeWidget->topLevelItem(i)->text(1).isEmpty()) { + insertLoc = i; + break; + } + } + } + + _ui->_treeWidget->insertTopLevelItem(insertLoc, item); item->setHidden(!shouldBeVisible(item, currentAccountFilter(), currentFolderFilter())); emit issueCountUpdated(_ui->_treeWidget->topLevelItemCount()); } @@ -337,10 +352,9 @@ void IssuesWidget::showFolderErrors(const QString &folderAlias) _ui->showWarnings->setChecked(false); } -void IssuesWidget::addLine(const QString &folderAlias, const QString &message) +void IssuesWidget::addError(const QString &folderAlias, const QString &message, + ErrorCategory category) { - SyncFileItem::Status status = SyncFileItem::NormalError; - auto folder = FolderMan::instance()->folder(folderAlias); if (!folder) return; @@ -351,26 +365,58 @@ void IssuesWidget::addLine(const QString &folderAlias, const QString &message) const QString longTimeStr = ProtocolWidget::timeString(timestamp, QLocale::LongFormat); columns << timeStr; - columns << tr(""); + columns << ""; // no "File" entry columns << folder->shortGuiLocalPath(); columns << message; - QIcon icon; - if (status == SyncFileItem::NormalError - || status == SyncFileItem::FatalError) { - icon = Theme::instance()->syncStateIcon(SyncResult::Error); - } else if (Progress::isWarningKind(status)) { - icon = Theme::instance()->syncStateIcon(SyncResult::Problem); - } + QIcon icon = Theme::instance()->syncStateIcon(SyncResult::Error); QTreeWidgetItem *twitem = new QTreeWidgetItem(columns); twitem->setData(0, Qt::SizeHintRole, QSize(0, ActivityItemDelegate::rowHeight())); twitem->setIcon(0, icon); twitem->setToolTip(0, longTimeStr); twitem->setToolTip(3, message); - twitem->setData(0, Qt::UserRole, status); + twitem->setData(0, Qt::UserRole, SyncFileItem::NormalError); twitem->setData(2, Qt::UserRole, folderAlias); addItem(twitem); + addErrorWidget(twitem, message, category); +} + +void IssuesWidget::addErrorWidget(QTreeWidgetItem *item, const QString &message, ErrorCategory category) +{ + QWidget *widget = 0; + if (category == ErrorCategory::InsufficientRemoteStorage) { + widget = new QWidget; + auto layout = new QHBoxLayout; + widget->setLayout(layout); + + auto label = new ElidedLabel(message, widget); + label->setElideMode(Qt::ElideMiddle); + layout->addWidget(label); + + auto button = new QPushButton("Retry all uploads", widget); + button->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding); + auto folderAlias = item->data(2, Qt::UserRole).toString(); + connect(button, &QPushButton::clicked, + this, [this, folderAlias]() { retryInsufficentRemoteStorageErrors(folderAlias); }); + layout->addWidget(button); + } + + if (widget) { + item->setText(3, QString()); + } + _ui->_treeWidget->setItemWidget(item, 3, widget); +} + +void IssuesWidget::retryInsufficentRemoteStorageErrors(const QString &folderAlias) +{ + auto folderman = FolderMan::instance(); + auto folder = folderman->folder(folderAlias); + if (!folder) + return; + + folder->journalDb()->wipeErrorBlacklistCategory(SyncJournalErrorBlacklistRecord::InsufficientRemoteStorage); + folderman->scheduleFolderNext(folder); } } diff --git a/src/gui/issueswidget.h b/src/gui/issueswidget.h index e9a0ef52d7..4accd89b22 100644 --- a/src/gui/issueswidget.h +++ b/src/gui/issueswidget.h @@ -50,7 +50,7 @@ public: void showFolderErrors(const QString &folderAlias); public slots: - void addLine(const QString &folderAlias, const QString &message); + void addError(const QString &folderAlias, const QString &message, ErrorCategory category); void slotProgressInfo(const QString &folder, const ProgressInfo &progress); void slotItemCompleted(const QString &folder, const SyncFileItemPtr &item); void slotOpenFile(QTreeWidgetItem *item, int); @@ -78,6 +78,12 @@ private: void cleanItems(const QString &folder); void addItem(QTreeWidgetItem *item); + /// Add the special error widget for the category, if any + void addErrorWidget(QTreeWidgetItem *item, const QString &message, ErrorCategory category); + + /// Wipes all insufficient remote storgage blacklist entries + void retryInsufficentRemoteStorageErrors(const QString &folderAlias); + Ui::IssuesWidget *_ui; }; } diff --git a/src/gui/issueswidget.ui b/src/gui/issueswidget.ui index 5a1e1fac67..27bfe11184 100644 --- a/src/gui/issueswidget.ui +++ b/src/gui/issueswidget.ui @@ -99,9 +99,6 @@ false - - true - 4 diff --git a/src/libsync/progressdispatcher.h b/src/libsync/progressdispatcher.h index e968216996..f7015f6c55 100644 --- a/src/libsync/progressdispatcher.h +++ b/src/libsync/progressdispatcher.h @@ -248,6 +248,16 @@ namespace Progress { OWNCLOUDSYNC_EXPORT bool isIgnoredKind(SyncFileItem::Status); } +/** Type of error + * + * Used for ProgressDispatcher::syncError. May trigger error interactivity + * in IssuesWidget. + */ +enum class ErrorCategory { + Normal, + InsufficientRemoteStorage, +}; + /** * @file progressdispatcher.h * @brief A singleton class to provide sync progress information to other gui classes. @@ -283,7 +293,7 @@ signals: /** * @brief A new folder-wide sync error was seen. */ - void syncError(const QString &folder, const QString &message); + void syncError(const QString &folder, const QString &message, ErrorCategory category); protected: void setProgressInfo(const QString &folder, const ProgressInfo &progress); diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index 65fdd61135..96ffbcbb79 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -709,11 +709,17 @@ void SyncEngine::handleSyncError(CSYNC *ctx, const char *state) } else if (CSYNC_STATUS_IS_EQUAL(err, CSYNC_STATUS_SERVICE_UNAVAILABLE) || CSYNC_STATUS_IS_EQUAL(err, CSYNC_STATUS_CONNECT_ERROR)) { emit csyncUnavailable(); } else { - emit csyncError(errStr); + csyncError(errStr); } finalize(false); } +void SyncEngine::csyncError(const QString &message) +{ + emit syncError(message, ErrorCategory::Normal); +} + + void SyncEngine::startSync() { if (_journal->exists()) { @@ -744,7 +750,7 @@ void SyncEngine::startSync() if (!QDir(_localPath).exists()) { _anotherSyncNeeded = DelayedFollowUp; // No _tr, it should only occur in non-mirall - emit csyncError("Unable to find local sync folder."); + csyncError("Unable to find local sync folder."); finalize(false); return; } @@ -757,11 +763,11 @@ void SyncEngine::startSync() << "and at least" << minFree << "are required"; if (freeBytes < minFree) { _anotherSyncNeeded = DelayedFollowUp; - emit csyncError(tr("Only %1 are available, need at least %2 to start", + csyncError(tr("Only %1 are available, need at least %2 to start", "Placeholders are postfixed with file sizes using Utility::octetsToString()") - .arg( - Utility::octetsToString(freeBytes), - Utility::octetsToString(minFree))); + .arg( + Utility::octetsToString(freeBytes), + Utility::octetsToString(minFree))); finalize(false); return; } @@ -794,7 +800,7 @@ void SyncEngine::startSync() if (fileRecordCount == -1) { qCWarning(lcEngine) << "No way to create a sync journal!"; - emit csyncError(tr("Unable to open or create the local sync database. Make sure you have write access in the sync folder.")); + csyncError(tr("Unable to open or create the local sync database. Make sure you have write access in the sync folder.")); finalize(false); return; // database creation error! @@ -813,7 +819,7 @@ void SyncEngine::startSync() qCInfo(lcEngine) << (usingSelectiveSync ? "Using Selective Sync" : "NOT Using Selective Sync"); } else { qCWarning(lcEngine) << "Could not retrieve selective sync list from DB"; - emit csyncError(tr("Unable to read the blacklist from the local database")); + csyncError(tr("Unable to read the blacklist from the local database")); finalize(false); return; } @@ -854,7 +860,7 @@ void SyncEngine::startSync() if (!ok) { delete discoveryJob; qCWarning(lcEngine) << "Unable to read selective sync list, aborting."; - emit csyncError(tr("Unable to read from the sync journal.")); + csyncError(tr("Unable to read from the sync journal.")); finalize(false); return; } @@ -903,7 +909,7 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult) // Sanity check if (!_journal->isConnected()) { qCWarning(lcEngine) << "Bailing out, DB failure"; - emit csyncError(tr("Cannot open the sync journal")); + csyncError(tr("Cannot open the sync journal")); finalize(false); return; } else { @@ -1101,7 +1107,7 @@ void SyncEngine::slotItemCompleted(const SyncFileItemPtr &item) _progressInfo->setProgressComplete(*item); if (item->_status == SyncFileItem::FatalError) { - emit csyncError(item->_errorString); + csyncError(item->_errorString); } emit transmissionProgress(*_progressInfo); @@ -1551,7 +1557,7 @@ void SyncEngine::slotSummaryError(const QString &message) return; _uniqueErrors.insert(message); - emit summaryError(message); + emit syncError(message, ErrorCategory::Normal); } void SyncEngine::slotInsufficientLocalStorage() @@ -1564,7 +1570,12 @@ void SyncEngine::slotInsufficientLocalStorage() void SyncEngine::slotInsufficientRemoteStorage() { - slotSummaryError(tr("There is insufficient space available on the server for some uploads.")); + auto msg = tr("There is insufficient space available on the server for some uploads."); + if (_uniqueErrors.contains(msg)) + return; + + _uniqueErrors.insert(msg); + emit syncError(msg, ErrorCategory::InsufficientRemoteStorage); } } // namespace OCC diff --git a/src/libsync/syncengine.h b/src/libsync/syncengine.h index e307a900b7..4f96e404f0 100644 --- a/src/libsync/syncengine.h +++ b/src/libsync/syncengine.h @@ -104,7 +104,6 @@ public: static qint64 minimumFileAgeForUpload; // in ms signals: - void csyncError(const QString &); void csyncUnavailable(); // During update, before reconcile @@ -120,8 +119,8 @@ signals: void transmissionProgress(const ProgressInfo &progress); - /// We've produced a new summary error. - void summaryError(const QString &message); + /// We've produced a new sync error of a type. + void syncError(const QString &message, ErrorCategory category); void finished(bool success); void started(); @@ -171,6 +170,7 @@ private slots: private: void handleSyncError(CSYNC *ctx, const char *state); + void csyncError(const QString &message); QString journalDbFilePath() const; diff --git a/src/libsync/syncjournaldb.cpp b/src/libsync/syncjournaldb.cpp index 5b43a3595e..0eb34e6097 100644 --- a/src/libsync/syncjournaldb.cpp +++ b/src/libsync/syncjournaldb.cpp @@ -1513,6 +1513,20 @@ void SyncJournalDb::wipeErrorBlacklistEntry(const QString &file) } } +void SyncJournalDb::wipeErrorBlacklistCategory(SyncJournalErrorBlacklistRecord::Category category) +{ + QMutexLocker locker(&_mutex); + if (checkConnect()) { + SqlQuery query(_db); + + query.prepare("DELETE FROM blacklist WHERE errorCategory=?1"); + query.bindValue(1, category); + if (!query.exec()) { + sqlFail("Deletion of blacklist category failed.", query); + } + } +} + void SyncJournalDb::setErrorBlacklistEntry(const SyncJournalErrorBlacklistRecord &item) { QMutexLocker locker(&_mutex); diff --git a/src/libsync/syncjournaldb.h b/src/libsync/syncjournaldb.h index 15f1d01cab..62295f62e1 100644 --- a/src/libsync/syncjournaldb.h +++ b/src/libsync/syncjournaldb.h @@ -22,10 +22,10 @@ #include "utility.h" #include "ownsql.h" +#include "syncjournalfilerecord.h" namespace OCC { class SyncJournalFileRecord; -class SyncJournalErrorBlacklistRecord; /** * @brief Class that handles the sync database @@ -73,6 +73,7 @@ public: void setErrorBlacklistEntry(const SyncJournalErrorBlacklistRecord &item); void wipeErrorBlacklistEntry(const QString &file); + void wipeErrorBlacklistCategory(SyncJournalErrorBlacklistRecord::Category category); int wipeErrorBlacklist(); int errorBlackListEntryCount(); From e7a0c1b6d07483f6c82fd3ab637638768660573e Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Wed, 12 Jul 2017 10:09:04 +0200 Subject: [PATCH 065/107] SyncEngineTest: _size now correct for uploads #5855 --- test/testsyncengine.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/testsyncengine.cpp b/test/testsyncengine.cpp index 8835bd575b..cfac432f96 100644 --- a/test/testsyncengine.cpp +++ b/test/testsyncengine.cpp @@ -438,9 +438,7 @@ private slots: QVERIFY(a1); QCOMPARE(a1->_instruction, CSYNC_INSTRUCTION_SYNC); QCOMPARE(a1->_direction, SyncFileItem::Up); - - // NOTE: This is currently a bug! #5855 - //QCOMPARE(a1->_size, quint64(5)); + QCOMPARE(a1->_size, quint64(5)); QCOMPARE(Utility::qDateTimeFromTime_t(a1->_modtime), changedMtime); QCOMPARE(a1->log._other_size, quint64(4)); From fe0de111fdd4eef1c803cc3aca13d4f70c12b7ce Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Wed, 12 Jul 2017 09:45:10 +0200 Subject: [PATCH 066/107] IssuesWidget: Fix insertion of sync errors before item errors --- src/gui/issueswidget.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gui/issueswidget.cpp b/src/gui/issueswidget.cpp index 51b47835fa..9356710ae5 100644 --- a/src/gui/issueswidget.cpp +++ b/src/gui/issueswidget.cpp @@ -146,8 +146,9 @@ void IssuesWidget::addItem(QTreeWidgetItem *item) // Insert item specific errors behind the others if (!item->text(1).isEmpty()) { for (int i = 0; i < _ui->_treeWidget->topLevelItemCount(); ++i) { - if (!_ui->_treeWidget->topLevelItem(i)->text(1).isEmpty()) { - insertLoc = i; + if (_ui->_treeWidget->topLevelItem(i)->text(1).isEmpty()) { + insertLoc = i + 1; + } else { break; } } From 20f1257e883180a89d01412d34460ee0af736a93 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Mon, 3 Jul 2017 16:15:42 +0200 Subject: [PATCH 067/107] csync: Remove iconv depedency We use iconv to normalize the string on mac. But the iconv version on mac does not support full unicode. So we will use Qt to normalize the string --- cmake/modules/FindIconv.cmake | 82 --------------------------- csync/CMakeLists.txt | 4 -- csync/DefineOptions.cmake | 3 - csync/config_csync.h.cmake | 5 -- csync/src/CMakeLists.txt | 5 -- csync/src/csync.c | 24 -------- csync/src/csync.h | 11 ---- csync/src/csync_private.h | 17 ------ csync/src/std/CMakeLists.txt | 5 -- csync/src/std/c_private.h | 7 --- csync/src/std/c_string.c | 103 +--------------------------------- csync/src/std/c_string.h | 6 +- 12 files changed, 3 insertions(+), 269 deletions(-) delete mode 100644 cmake/modules/FindIconv.cmake diff --git a/cmake/modules/FindIconv.cmake b/cmake/modules/FindIconv.cmake deleted file mode 100644 index 46d99f0581..0000000000 --- a/cmake/modules/FindIconv.cmake +++ /dev/null @@ -1,82 +0,0 @@ -# - Try to find Iconv -# Once done this will define -# -# ICONV_FOUND - system has Iconv -# ICONV_INCLUDE_DIRS - the Iconv include directory -# ICONV_LIBRARIES - Link these to use Iconv -# ICONV_DEFINITIONS - Compiler switches required for using Iconv -# -# Copyright (c) 2013 Andreas Schneider -# -# Redistribution and use is allowed according to the terms of the New -# BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. -# - -include(CheckIncludeFile) -include(CheckFunctionExists) -include(CheckLibraryExists) -include(CheckPrototypeDefinition) - -find_path(ICONV_INCLUDE_DIR - NAMES - iconv.h sys/iconv.h -) - -set(CMAKE_REQUIRED_INCLUDES ${ICONV_INCLUDE_DIR}) -check_include_file(iconv.h HAVE_ICONV_H) -check_include_file(sys/iconv.h HAVE_SYS_ICONV_H) -set(CMAKE_REQUIRED_INCLUDES) - -find_library(ICONV_LIBRARY - NAMES - iconv - libiconv - PATHS -) - -if (ICONV_LIBRARY) - get_filename_component(_ICONV_NAME ${ICONV_LIBRARY} NAME) - get_filename_component(_ICONV_PATH ${ICONV_LIBRARY} PATH) - check_library_exists(${_ICONV_NAME} iconv ${_ICONV_PATH} HAVE_ICONV) -else() - check_function_exists(iconv HAVE_ICONV) -endif() - -if (HAVE_ICONV_H OR HAVE_SYS_ICONV_H) - if (HAVE_ICONV_H) - set(_ICONV_PROTO_INCLUDE "iconv.h") - endif (HAVE_ICONV_H) - if (HAVE_SYS_ICONV_H) - set(_ICONV_PROTO_INCLUDE "sys/iconv.h") - endif (HAVE_SYS_ICONV_H) - - set(CMAKE_REQUIRED_INCLUDES ${ICONV_INCLUDE_DIR}) - check_prototype_definition(iconv - "size_t iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)" - "-1" - ${_ICONV_PROTO_INCLUDE} - HAVE_ICONV_CONST) - set(CMAKE_REQUIRED_INCLUDES) -endif (HAVE_ICONV_H OR HAVE_SYS_ICONV_H) - -set(ICONV_INCLUDE_DIRS - ${ICONV_INCLUDE_DIR} -) - -if (ICONV_LIBRARY) - set(ICONV_LIBRARIES - ${ICONV_LIBRARIES} - ${ICONV_LIBRARY} - ) -endif (ICONV_LIBRARY) - -include(FindPackageHandleStandardArgs) -if (ICONV_LIBRARIES) - find_package_handle_standard_args(Iconv DEFAULT_MSG ICONV_LIBRARIES ICONV_INCLUDE_DIRS) -else() - find_package_handle_standard_args(Iconv DEFAULT_MSG ICONV_INCLUDE_DIRS) -endif() - -# show the ICONV_INCLUDE_DIRS and ICONV_LIBRARIES variables only in the advanced view -mark_as_advanced(ICONV_INCLUDE_DIRS ICONV_LIBRARIES) diff --git a/csync/CMakeLists.txt b/csync/CMakeLists.txt index 2d4b94f2aa..d5be43a29a 100644 --- a/csync/CMakeLists.txt +++ b/csync/CMakeLists.txt @@ -18,10 +18,6 @@ include(DefineInstallationPaths) include(MacroAddPlugin) include(MacroCopyFile) -if (NOT WIN32) - find_package(Iconv) -endif (NOT WIN32) - find_package(SQLite3 3.8.0 REQUIRED) include(ConfigureChecks.cmake) diff --git a/csync/DefineOptions.cmake b/csync/DefineOptions.cmake index 9ba14800d4..485e183221 100644 --- a/csync/DefineOptions.cmake +++ b/csync/DefineOptions.cmake @@ -1,5 +1,2 @@ -if ( NOT WIN32 ) - option(WITH_ICONV "Build csync with iconv support" ON) -endif() option(UNIT_TESTING "Build with unit tests" OFF) option(MEM_NULL_TESTS "Enable NULL memory testing" OFF) diff --git a/csync/config_csync.h.cmake b/csync/config_csync.h.cmake index 155329bbd7..f5c9f09b2f 100644 --- a/csync/config_csync.h.cmake +++ b/csync/config_csync.h.cmake @@ -9,19 +9,14 @@ #cmakedefine HAVE_CLOCK_GETTIME #cmakedefine WITH_LOG4C 1 -#cmakedefine WITH_ICONV 1 #cmakedefine HAVE_ARGP_H 1 -#cmakedefine HAVE_ICONV_H 1 -#cmakedefine HAVE_SYS_ICONV_H 1 #cmakedefine HAVE_TIMEGM 1 #cmakedefine HAVE_STRERROR_R 1 #cmakedefine HAVE_UTIMES 1 #cmakedefine HAVE_LSTAT 1 #cmakedefine HAVE_FNMATCH 1 -#cmakedefine HAVE_ICONV 1 -#cmakedefine HAVE_ICONV_CONST 1 #cmakedefine HAVE___MINGW_ASPRINTF 1 #cmakedefine HAVE_ASPRINTF 1 diff --git a/csync/src/CMakeLists.txt b/csync/src/CMakeLists.txt index cb07377e90..8bce5f418d 100644 --- a/csync/src/CMakeLists.txt +++ b/csync/src/CMakeLists.txt @@ -28,11 +28,6 @@ set(CSYNC_LINK_LIBRARIES ${SQLITE3_LIBRARIES} ) -if(HAVE_ICONV AND WITH_ICONV) - list(APPEND CSYNC_PRIVATE_INCLUDE_DIRS ${ICONV_INCLUDE_DIR}) - list(APPEND CSYNC_LINK_LIBRARIES ${ICONV_LIBRARIES}) -endif() - # Specific option for builds tied to servers that do not support renaming extensions set(NO_RENAME_EXTENSION 0 CACHE BOOL "Do not issue rename if the extension changes") if(NO_RENAME_EXTENSION) diff --git a/csync/src/csync.c b/csync/src/csync.c index 9af6f0fcf8..1f75ac3bdb 100644 --- a/csync/src/csync.c +++ b/csync/src/csync.c @@ -33,13 +33,6 @@ #include #include -#ifdef HAVE_ICONV_H -#include -#endif -#ifdef HAVE_SYS_ICONV_H -#include -#endif - #include "c_lib.h" #include "csync_private.h" #include "csync_exclude.h" @@ -569,10 +562,6 @@ int csync_destroy(CSYNC *ctx) { SAFE_FREE(ctx->local.uri); SAFE_FREE(ctx->error_string); -#ifdef WITH_ICONV - c_close_iconv(); -#endif - SAFE_FREE(ctx); return rc; @@ -626,19 +615,6 @@ const char *csync_get_status_string(CSYNC *ctx) return csync_vio_get_status_string(ctx); } -#ifdef WITH_ICONV -int csync_set_iconv_codec(const char *from) -{ - c_close_iconv(); - - if (from != NULL) { - c_setup_iconv(from); - } - - return 0; -} -#endif - void csync_request_abort(CSYNC *ctx) { if (ctx != NULL) { diff --git a/csync/src/csync.h b/csync/src/csync.h index 949c9efd23..b35d4601c1 100644 --- a/csync/src/csync.h +++ b/csync/src/csync.h @@ -479,17 +479,6 @@ int OCSYNC_EXPORT csync_walk_remote_tree(CSYNC *ctx, csync_treewalk_visit_func * */ const char OCSYNC_EXPORT *csync_get_status_string(CSYNC *ctx); -#ifdef WITH_ICONV -/** - * @brief Set iconv source codec for filenames. - * - * @param from Source codec. - * - * @return 0 on success, or an iconv error number. - */ -int OCSYNC_EXPORT csync_set_iconv_codec(const char *from); -#endif - /** * @brief Aborts the current sync run as soon as possible. Can be called from another thread. * diff --git a/csync/src/csync_private.h b/csync/src/csync_private.h index 11775f345d..2edc26019d 100644 --- a/csync/src/csync_private.h +++ b/csync/src/csync_private.h @@ -42,17 +42,6 @@ #include "csync.h" #include "csync_misc.h" -#ifdef WITH_ICONV -#include -#endif - -#ifdef HAVE_ICONV_H -#include -#endif -#ifdef HAVE_SYS_ICONV_H -#include -#endif - #include "csync_macros.h" /** @@ -130,12 +119,6 @@ struct csync_s { } remote; -#if defined(HAVE_ICONV) && defined(WITH_ICONV) - struct { - iconv_t iconv_cd; - } options; -#endif - /* replica we are currently walking */ enum csync_replica_e current; diff --git a/csync/src/std/CMakeLists.txt b/csync/src/std/CMakeLists.txt index 91b8843a85..39b1fd550c 100644 --- a/csync/src/std/CMakeLists.txt +++ b/csync/src/std/CMakeLists.txt @@ -5,10 +5,6 @@ set(CSTDLIB_PUBLIC_INCLUDE_DIRS CACHE INTERNAL "cstdlib public include directories" ) -set(CSTDLIB_PRIVATE_INCLUDE_DIRS - ${ICONV_INCLUDE_DIR} -) - set(CSTDLIB_LIBRARY cstdlib CACHE INTERNAL "cstdlib library" @@ -34,7 +30,6 @@ endif() include_directories( ${CSTDLIB_PUBLIC_INCLUDE_DIRS} - ${CSTDLIB_PRIVATE_INCLUDE_DIRS} ) add_library(${CSTDLIB_LIBRARY} STATIC ${cstdlib_SRCS}) diff --git a/csync/src/std/c_private.h b/csync/src/std/c_private.h index 09c1ea36b6..26bba70db5 100644 --- a/csync/src/std/c_private.h +++ b/csync/src/std/c_private.h @@ -159,13 +159,6 @@ typedef char mbchar_t; #define _tgetcwd getcwd #endif -#ifdef WITH_ICONV -/** @internal */ -int c_setup_iconv(const char* to); -/** @internal */ -int c_close_iconv(void); -#endif - /* FIXME: Implement TLS for OS X */ #if defined(__GNUC__) && !defined(__APPLE__) # define CSYNC_THREAD __thread diff --git a/csync/src/std/c_string.c b/csync/src/std/c_string.c index e21d4374e9..6bf2d77232 100644 --- a/csync/src/std/c_string.c +++ b/csync/src/std/c_string.c @@ -40,97 +40,6 @@ #include #endif -#if defined(HAVE_ICONV) && defined(WITH_ICONV) -# ifdef HAVE_ICONV_H -# include -# endif -# ifdef HAVE_SYS_ICONV_H -# include -# endif - -typedef struct { - iconv_t to; - iconv_t from; -} iconv_conversions; - -CSYNC_THREAD iconv_conversions _iconvs = { NULL, NULL }; - -int c_setup_iconv(const char* to) { - _iconvs.to = iconv_open(to, "UTF-8"); - _iconvs.from = iconv_open("UTF-8", to); - - if (_iconvs.to == (iconv_t)-1 || _iconvs.from == (iconv_t)-1) - return -1; - - return 0; -} - -int c_close_iconv() { - int ret_to = 0; - int ret_from = 0; - if( _iconvs.to != (iconv_t) NULL ) { - ret_to = iconv_close(_iconvs.to); - } - if( _iconvs.from != (iconv_t) NULL ) { - ret_from = iconv_close(_iconvs.from); - } - - if (ret_to == -1 || ret_from == -1) - return -1; - - _iconvs.to = (iconv_t) 0; - _iconvs.from = (iconv_t) 0; - - return 0; -} - -enum iconv_direction { iconv_from_native, iconv_to_native }; - -static char *c_iconv(const char* str, enum iconv_direction dir) -{ -#ifdef HAVE_ICONV_CONST - const char *in = str; -#else - char *in = discard_const(str); -#endif - size_t size; - size_t outsize; - char *out; - char *out_in; - size_t ret; - - if (str == NULL) { - return NULL; - } - - if(_iconvs.from == NULL && _iconvs.to == NULL) { -#ifdef __APPLE__ - c_setup_iconv("UTF-8-MAC"); -#else - return c_strdup(str); -#endif - } - - size = strlen(in); - outsize = size*2; - out = c_malloc(outsize); - out_in = out; - - if (dir == iconv_to_native) { - ret = iconv(_iconvs.to, &in, &size, &out, &outsize); - } else { - ret = iconv(_iconvs.from, &in, &size, &out, &outsize); - } - - if (ret == (size_t)-1) { - SAFE_FREE(out_in); - return NULL; - } - - return out_in; -} -#endif /* defined(HAVE_ICONV) && defined(WITH_ICONV) */ - int c_strncasecmp(const char *a, const char *b, size_t n) { #ifdef _WIN32 return _strnicmp(a, b, n); @@ -285,11 +194,7 @@ char* c_utf8_from_locale(const mbchar_t *wstr) dst = mdst; } #else -#ifdef WITH_ICONV - dst = c_iconv(wstr, iconv_from_native); -#else - dst = (char*) wstr; -#endif + dst = c_strdup(wstr); #endif return dst; } @@ -317,11 +222,7 @@ mbchar_t* c_utf8_string_to_locale(const char *str) MultiByteToWideChar(CP_UTF8, 0, str, -1, dst, size_needed); } #else -#ifdef WITH_ICONV - dst = c_iconv(str, iconv_to_native); -#else - dst = (_TCHAR*) str; -#endif + dst = c_strdup(str); #endif return dst; } diff --git a/csync/src/std/c_string.h b/csync/src/std/c_string.h index 60bc010da4..304c598a23 100644 --- a/csync/src/std/c_string.h +++ b/csync/src/std/c_string.h @@ -194,8 +194,6 @@ void c_strlist_destroy(c_strlist_t *strlist); */ mbchar_t* c_utf8_string_to_locale(const char *wstr); -#if defined(_WIN32) || defined(WITH_ICONV) - /** * @brief Free buffer malloced by c_utf8_from_locale or c_utf8_to_locale(). * @@ -214,9 +212,7 @@ mbchar_t* c_utf8_string_to_locale(const char *wstr); * */ #define c_free_locale_string(x) SAFE_FREE(x) -#else -#define c_free_locale_string(x) (void)x -#endif + /** * }@ From d099c2a8db62804acdd33c65cc09474eb240e1ce Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Tue, 4 Jul 2017 11:29:24 +0200 Subject: [PATCH 068/107] csync: Move the locale<->utf8 conversion to a different file It's a C++ file so we will be able to use Qt from it --- csync/src/std/CMakeLists.txt | 3 +- csync/src/std/c_string.c | 59 -------------------- csync/src/std/c_utf8.cc | 101 +++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 60 deletions(-) create mode 100644 csync/src/std/c_utf8.cc diff --git a/csync/src/std/CMakeLists.txt b/csync/src/std/CMakeLists.txt index 39b1fd550c..77d7e0ff04 100644 --- a/csync/src/std/CMakeLists.txt +++ b/csync/src/std/CMakeLists.txt @@ -1,4 +1,4 @@ -project(cstdlib C) +project(cstdlib) set(CSTDLIB_PUBLIC_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} @@ -20,6 +20,7 @@ set(cstdlib_SRCS c_rbtree.c c_string.c c_time.c + c_utf8.cc ) if(NOT HAVE_ASPRINTF AND NOT HAVE___MINGW_ASPRINTF) diff --git a/csync/src/std/c_string.c b/csync/src/std/c_string.c index 6bf2d77232..8929f24586 100644 --- a/csync/src/std/c_string.c +++ b/csync/src/std/c_string.c @@ -167,62 +167,3 @@ void c_strlist_destroy(c_strlist_t *strlist) { SAFE_FREE(strlist->vector); SAFE_FREE(strlist); } - -/* Convert a wide multibyte String to UTF8 */ -char* c_utf8_from_locale(const mbchar_t *wstr) -{ - char *dst = NULL; -#ifdef _WIN32 - char *mdst = NULL; - int size_needed; - size_t len; -#endif - - if (wstr == NULL) { - return NULL; - } - -#ifdef _WIN32 - len = wcslen(wstr); - /* Call once to get the required size. */ - size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr, len, NULL, 0, NULL, NULL); - if (size_needed > 0) { - mdst = c_malloc(size_needed + 1); - - memset(mdst, 0, size_needed + 1); - WideCharToMultiByte(CP_UTF8, 0, wstr, len, mdst, size_needed, NULL, NULL); - dst = mdst; - } -#else - dst = c_strdup(wstr); -#endif - return dst; -} - -/* Convert a an UTF8 string to multibyte */ -mbchar_t* c_utf8_string_to_locale(const char *str) -{ - mbchar_t *dst = NULL; -#ifdef _WIN32 - size_t len; - int size_needed; -#endif - - if (str == NULL ) { - return NULL; - } - -#ifdef _WIN32 - len = strlen(str); - size_needed = MultiByteToWideChar(CP_UTF8, 0, str, len, NULL, 0); - if (size_needed > 0) { - int size_char = (size_needed + 1) * sizeof(mbchar_t); - dst = c_malloc(size_char); - memset((void*)dst, 0, size_char); - MultiByteToWideChar(CP_UTF8, 0, str, -1, dst, size_needed); - } -#else - dst = c_strdup(str); -#endif - return dst; -} diff --git a/csync/src/std/c_utf8.cc b/csync/src/std/c_utf8.cc new file mode 100644 index 0000000000..88e28c9903 --- /dev/null +++ b/csync/src/std/c_utf8.cc @@ -0,0 +1,101 @@ +/* + * cynapses libc functions + * + * Copyright (c) 2008-2013 by Andreas Schneider + * Copyright (c) 2012-2013 by Klaas Freitag + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config_csync.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef _WIN32 +#include +#endif + +extern "C" { +#include "c_alloc.h" +#include "c_string.h" + +/* Convert a locale String to UTF8 */ +char* c_utf8_from_locale(const mbchar_t *wstr) +{ + char *dst = NULL; +#ifdef _WIN32 + char *mdst = NULL; + int size_needed; + size_t len; +#endif + + if (wstr == NULL) { + return NULL; + } + +#ifdef _WIN32 + len = wcslen(wstr); + /* Call once to get the required size. */ + size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr, len, NULL, 0, NULL, NULL); + if (size_needed > 0) { + mdst = (char*)c_malloc(size_needed + 1); + + memset(mdst, 0, size_needed + 1); + WideCharToMultiByte(CP_UTF8, 0, wstr, len, mdst, size_needed, NULL, NULL); + dst = mdst; + } +#else + dst = c_strdup(wstr); +#endif + return dst; +} + +/* Convert a an UTF8 string to locale */ +mbchar_t* c_utf8_string_to_locale(const char *str) +{ + mbchar_t *dst = NULL; +#ifdef _WIN32 + size_t len; + int size_needed; +#endif + + if (str == NULL ) { + return NULL; + } + +#ifdef _WIN32 + len = strlen(str); + size_needed = MultiByteToWideChar(CP_UTF8, 0, str, len, NULL, 0); + if (size_needed > 0) { + int size_char = (size_needed + 1) * sizeof(mbchar_t); + dst = (mbchar_t*)c_malloc(size_char); + memset((void*)dst, 0, size_char); + MultiByteToWideChar(CP_UTF8, 0, str, -1, dst, size_needed); + } +#else + dst = c_strdup(str); +#endif + return dst; +} + +} From acf65b4c23ebb944b9fe1df09d08be802642596a Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Tue, 4 Jul 2017 12:20:59 +0200 Subject: [PATCH 069/107] csync: Use Qt for encodeing/decoding filesystem strings Issues: - #5661 On mac, iconv did not support all of unicode and some files with emoji in the filename could not be uploaded - #5719 , #5676 On linux, we will now support non utf-8 locale --- CMakeLists.txt | 13 +++++++++ csync/src/std/CMakeLists.txt | 1 + csync/src/std/c_utf8.cc | 53 +++++++++++++++++++----------------- src/CMakeLists.txt | 13 --------- 4 files changed, 42 insertions(+), 38 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 116dca4cc7..9b6e9810d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -191,6 +191,19 @@ add_definitions( -D__USE_MINGW_ANSI_STDIO=1 ) add_definitions( -DNOMINMAX ) endif( WIN32 ) +include(QtVersionAbstraction) +setup_qt() +if (${Qt5Core_VERSION_MAJOR} EQUAL "5") + if (${Qt5Core_VERSION_MINOR} EQUAL "6" OR ${Qt5Core_VERSION_MINOR} GREATER 6) + else() + message(STATUS "If possible compile me with Qt 5.6 or higher.") + endif() +endif() + +if (APPLE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") +endif() + # Handle Translations, pick all client_* files from trans directory. file( GLOB TRANS_FILES ${CMAKE_SOURCE_DIR}/translations/client_*.ts) set(TRANSLATIONS ${TRANS_FILES}) diff --git a/csync/src/std/CMakeLists.txt b/csync/src/std/CMakeLists.txt index 77d7e0ff04..519cada86e 100644 --- a/csync/src/std/CMakeLists.txt +++ b/csync/src/std/CMakeLists.txt @@ -36,6 +36,7 @@ include_directories( add_library(${CSTDLIB_LIBRARY} STATIC ${cstdlib_SRCS}) if(NOT WIN32) add_definitions( -fPIC ) + qt5_use_modules(${CSTDLIB_LIBRARY} Core) endif() if(NOT HAVE_FNMATCH AND WIN32) # needed for PathMatchSpec for our fnmatch replacement diff --git a/csync/src/std/c_utf8.cc b/csync/src/std/c_utf8.cc index 88e28c9903..4bd5d75ac1 100644 --- a/csync/src/std/c_utf8.cc +++ b/csync/src/std/c_utf8.cc @@ -21,18 +21,17 @@ #include "config_csync.h" -#include -#include +#ifdef _WIN32 #include #include #include - #include #include #include - -#ifdef _WIN32 #include +#else +#include +#include #endif extern "C" { @@ -42,18 +41,15 @@ extern "C" { /* Convert a locale String to UTF8 */ char* c_utf8_from_locale(const mbchar_t *wstr) { - char *dst = NULL; -#ifdef _WIN32 - char *mdst = NULL; - int size_needed; - size_t len; -#endif - if (wstr == NULL) { return NULL; } #ifdef _WIN32 + char *dst = NULL; + char *mdst = NULL; + int size_needed; + size_t len; len = wcslen(wstr); /* Call once to get the required size. */ size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr, len, NULL, 0, NULL, NULL); @@ -64,26 +60,33 @@ char* c_utf8_from_locale(const mbchar_t *wstr) WideCharToMultiByte(CP_UTF8, 0, wstr, len, mdst, size_needed, NULL, NULL); dst = mdst; } -#else - dst = c_strdup(wstr); -#endif return dst; +#else + QTextDecoder dec(QTextCodec::codecForLocale()); + QString s = dec.toUnicode(wstr, qstrlen(wstr)); + if (s.isEmpty() || dec.hasFailure()) { + /* Conversion error: since we can't report error from this function, just return the original + string. We take care of invalid utf-8 in SyncEngine::treewalkFile */ + return c_strdup(wstr); + } +#ifdef __APPLE__ + s = s.normalized(QString::NormalizationForm_C); +#endif + return c_strdup(std::move(s).toUtf8().constData()); +#endif } /* Convert a an UTF8 string to locale */ mbchar_t* c_utf8_string_to_locale(const char *str) { - mbchar_t *dst = NULL; -#ifdef _WIN32 - size_t len; - int size_needed; -#endif - if (str == NULL ) { return NULL; } - #ifdef _WIN32 + mbchar_t *dst = NULL; + size_t len; + int size_needed; + len = strlen(str); size_needed = MultiByteToWideChar(CP_UTF8, 0, str, len, NULL, 0); if (size_needed > 0) { @@ -92,10 +95,10 @@ mbchar_t* c_utf8_string_to_locale(const char *str) memset((void*)dst, 0, size_char); MultiByteToWideChar(CP_UTF8, 0, str, -1, dst, size_needed); } -#else - dst = c_strdup(str); -#endif return dst; +#else + return c_strdup(QFile::encodeName(QString::fromUtf8(str))); +#endif } } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1b53279d22..3cbacd537f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,19 +4,6 @@ endif() set(synclib_NAME ${APPLICATION_EXECUTABLE}sync) -include(QtVersionAbstraction) -setup_qt() -if (${Qt5Core_VERSION_MAJOR} EQUAL "5") - if (${Qt5Core_VERSION_MINOR} EQUAL "6" OR ${Qt5Core_VERSION_MINOR} GREATER 6) - else() - message(STATUS "If possible compile me with Qt 5.6 or higher.") - endif() -endif() - -if (APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") -endif() - if(NOT TOKEN_AUTH_ONLY) find_package(Qt5Keychain REQUIRED) endif() From 7adcb76f682b9df86d2f7caa5af230b29f7d29cc Mon Sep 17 00:00:00 2001 From: Markus Goetz Date: Tue, 4 Jul 2017 15:03:16 +0200 Subject: [PATCH 070/107] check_vio_ext: Align with new UTF8 behaviour on macOS --- csync/tests/vio_tests/check_vio_ext.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/csync/tests/vio_tests/check_vio_ext.c b/csync/tests/vio_tests/check_vio_ext.c index f91328767e..c34e708c0a 100644 --- a/csync/tests/vio_tests/check_vio_ext.c +++ b/csync/tests/vio_tests/check_vio_ext.c @@ -434,7 +434,7 @@ static void check_readdir_bigunicode(void **state) assert_int_equal(rc, 0); SAFE_FREE(p); - const char *t1 = "goodone/ugly\xEF\xBB\xBF\x32" ".txt"; + const char *t1 = "goodone/ugly\xEF\xBB\xBF\x32" ".txt"; // file with encoding error asprintf( &p, "%s/%s", CSYNC_TEST_DIR, t1 ); rc = _tmkdir(p, MKDIR_MASK); SAFE_FREE(p); @@ -444,18 +444,10 @@ static void check_readdir_bigunicode(void **state) int files_cnt = 0; traverse_dir(state, CSYNC_TEST_DIR, &files_cnt); const char *expected_result = " C:/tmp/csync_test/goodone" -#ifndef __APPLE__ - // On Mac, iconv will not return some files with fancy unicode. - // Linux is not so picky about it and return everything and let the sync engine deal with it. " C:/tmp/csync_test/goodone/ugly\xEF\xBB\xBF\x32" ".txt" -#endif ; assert_string_equal( sv->result, expected_result); -#ifdef __APPLE__ - // Bad one is recognized though.. ! - assert_string_equal( sv->ignored_dir, CSYNC_TEST_DIR "/goodone/" "ugly\xEF\xBB\xBF\x32" ".txt"); -#endif assert_int_equal(files_cnt, 0); } From 2cdf5517cb992b17a1fca5e532ae2cc7cbe3db31 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Wed, 12 Jul 2017 10:57:41 +0200 Subject: [PATCH 071/107] Conflicts: Detect and show in issues tab Incidentally fixes a potential issue where conflicts were silently- ignored and thus deleted if the parent folder was deleted. --- csync/src/csync.h | 3 ++- csync/src/csync_exclude.c | 4 ++-- csync/src/csync_exclude.h | 3 ++- csync/src/csync_update.c | 2 ++ src/gui/folder.cpp | 4 ++-- src/gui/issueswidget.cpp | 1 - src/libsync/syncengine.cpp | 4 ++++ src/libsync/syncfileitem.h | 9 ++++++++- src/libsync/syncresult.cpp | 13 +++++++++---- src/libsync/syncresult.h | 10 ++++++---- 10 files changed, 37 insertions(+), 16 deletions(-) diff --git a/csync/src/csync.h b/csync/src/csync.h index b35d4601c1..daf778c247 100644 --- a/csync/src/csync.h +++ b/csync/src/csync.h @@ -100,7 +100,8 @@ enum csync_status_codes_e { CSYNC_STATUS_INVALID_CHARACTERS, CSYNC_STATUS_INDIVIDUAL_STAT_FAILED, CSYNC_STATUS_FORBIDDEN, - CSYNC_STATUS_INDIVIDUAL_TOO_DEEP + CSYNC_STATUS_INDIVIDUAL_TOO_DEEP, + CSYNC_STATUS_INDIVIDUAL_IS_CONFLICT_FILE }; typedef enum csync_status_codes_e CSYNC_STATUS; diff --git a/csync/src/csync_exclude.c b/csync/src/csync_exclude.c index 88dd61e750..873a12d885 100644 --- a/csync/src/csync_exclude.c +++ b/csync/src/csync_exclude.c @@ -302,7 +302,7 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch /* Always ignore conflict files, not only via the exclude list */ rc = csync_fnmatch("*_conflict-*", bname, 0); if (rc == 0) { - match = CSYNC_FILE_SILENTLY_EXCLUDED; + match = CSYNC_FILE_EXCLUDE_CONFLICT; goto out; } @@ -313,7 +313,7 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch } rc = csync_fnmatch(conflict, path, 0); if (rc == 0) { - match = CSYNC_FILE_SILENTLY_EXCLUDED; + match = CSYNC_FILE_EXCLUDE_CONFLICT; SAFE_FREE(conflict); goto out; } diff --git a/csync/src/csync_exclude.h b/csync/src/csync_exclude.h index ae49bbd8d1..722d27cace 100644 --- a/csync/src/csync_exclude.h +++ b/csync/src/csync_exclude.h @@ -32,7 +32,8 @@ enum csync_exclude_type_e { CSYNC_FILE_EXCLUDE_TRAILING_SPACE, CSYNC_FILE_EXCLUDE_LONG_FILENAME, CSYNC_FILE_EXCLUDE_HIDDEN, - CSYNC_FILE_EXCLUDE_STAT_FAILED + CSYNC_FILE_EXCLUDE_STAT_FAILED, + CSYNC_FILE_EXCLUDE_CONFLICT }; typedef enum csync_exclude_type_e CSYNC_EXCLUDE_TYPE; diff --git a/csync/src/csync_update.c b/csync/src/csync_update.c index d7587765c3..1a069442e7 100644 --- a/csync/src/csync_update.c +++ b/csync/src/csync_update.c @@ -469,6 +469,8 @@ out: st->error_status = CSYNC_STATUS_INDIVIDUAL_EXCLUDE_HIDDEN; } else if (excluded == CSYNC_FILE_EXCLUDE_STAT_FAILED) { st->error_status = CSYNC_STATUS_INDIVIDUAL_STAT_FAILED; + } else if (excluded == CSYNC_FILE_EXCLUDE_CONFLICT) { + st->error_status = CSYNC_STATUS_INDIVIDUAL_IS_CONFLICT_FILE; } } } diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index 7282e431f6..d4b652ba6d 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -341,8 +341,8 @@ void Folder::showSyncResultPopup() _syncResult.numRenamedItems(), _syncResult.firstItemRenamed()->_renameTarget); } - if (_syncResult.firstConflictItem()) { - createGuiLog(_syncResult.firstConflictItem()->_file, LogStatusConflict, _syncResult.numConflictItems()); + if (_syncResult.firstNewConflictItem()) { + createGuiLog(_syncResult.firstNewConflictItem()->_file, LogStatusConflict, _syncResult.numNewConflictItems()); } if (int errorCount = _syncResult.numErrorItems()) { createGuiLog(_syncResult.firstItemError()->_file, LogStatusError, errorCount); diff --git a/src/gui/issueswidget.cpp b/src/gui/issueswidget.cpp index 9356710ae5..4803f17a4f 100644 --- a/src/gui/issueswidget.cpp +++ b/src/gui/issueswidget.cpp @@ -247,7 +247,6 @@ bool IssuesWidget::shouldBeVisible(QTreeWidgetItem *item, AccountState *filterAc visible &= (_ui->showIgnores->isChecked() || status != SyncFileItem::FileIgnored); visible &= (_ui->showWarnings->isChecked() || (status != SyncFileItem::SoftError - && status != SyncFileItem::Conflict && status != SyncFileItem::Restoration)); auto folderalias = item->data(2, Qt::UserRole).toString(); diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index 5dc4ec07b3..8fa6eb7b72 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -495,6 +495,10 @@ int SyncEngine::treewalkFile(TREE_WALK_FILE *file, bool remote) case CSYNC_STATUS_INDIVIDUAL_TOO_DEEP: item->_errorString = tr("Folder hierarchy is too deep"); break; + case CSYNC_STATUS_INDIVIDUAL_IS_CONFLICT_FILE: + item->_status = SyncFileItem::Conflict; + item->_errorString = tr("Conflict: Server version downloaded, local copy renamed and not uploaded."); + break; case CYSNC_STATUS_FILE_LOCKED_OR_OPEN: item->_errorString = QLatin1String("File locked"); // don't translate, internal use! break; diff --git a/src/libsync/syncfileitem.h b/src/libsync/syncfileitem.h index b84c9ae33a..baa8a75102 100644 --- a/src/libsync/syncfileitem.h +++ b/src/libsync/syncfileitem.h @@ -61,7 +61,14 @@ public: SoftError, ///< More like an information Success, ///< The file was properly synced - Conflict, ///< The file was properly synced, but a conflict was created + + /** Marks a conflict, old or new. + * + * With instruction:IGNORE: detected an old unresolved old conflict + * With instruction:CONFLICT: a new conflict this sync run + */ + Conflict, + FileIgnored, ///< The file is in the ignored list (or blacklisted with no retries left) Restoration, ///< The file was restored because what should have been done was not allowed diff --git a/src/libsync/syncresult.cpp b/src/libsync/syncresult.cpp index de5c81c54b..88e78320b0 100644 --- a/src/libsync/syncresult.cpp +++ b/src/libsync/syncresult.cpp @@ -25,7 +25,8 @@ SyncResult::SyncResult() , _numRemovedItems(0) , _numUpdatedItems(0) , _numRenamedItems(0) - , _numConflictItems(0) + , _numNewConflictItems(0) + , _numOldConflictItems(0) , _numErrorItems(0) { @@ -147,9 +148,13 @@ void SyncResult::processCompletedItem(const SyncFileItemPtr &item) _firstItemError = item; } } else if (item->_status == SyncFileItem::Conflict) { - _numConflictItems++; - if (!_firstConflictItem) { - _firstConflictItem = item; + if (item->_instruction == CSYNC_INSTRUCTION_CONFLICT) { + _numNewConflictItems++; + if (!_firstNewConflictItem) { + _firstNewConflictItem = item; + } + } else { + _numOldConflictItems++; } } else { if (!item->hasErrorStatus() && item->_status != SyncFileItem::FileIgnored && item->_direction == SyncFileItem::Down) { diff --git a/src/libsync/syncresult.h b/src/libsync/syncresult.h index 7c5e501aa6..ab53c8dba0 100644 --- a/src/libsync/syncresult.h +++ b/src/libsync/syncresult.h @@ -66,14 +66,15 @@ public: int numRemovedItems() const { return _numRemovedItems; } int numUpdatedItems() const { return _numUpdatedItems; } int numRenamedItems() const { return _numRenamedItems; } - int numConflictItems() const { return _numConflictItems; } + int numNewConflictItems() const { return _numNewConflictItems; } + int numOldConflictItems() const { return _numOldConflictItems; } int numErrorItems() const { return _numErrorItems; } const SyncFileItemPtr &firstItemNew() const { return _firstItemNew; } const SyncFileItemPtr &firstItemDeleted() const { return _firstItemDeleted; } const SyncFileItemPtr &firstItemUpdated() const { return _firstItemUpdated; } const SyncFileItemPtr &firstItemRenamed() const { return _firstItemRenamed; } - const SyncFileItemPtr &firstConflictItem() const { return _firstConflictItem; } + const SyncFileItemPtr &firstNewConflictItem() const { return _firstNewConflictItem; } const SyncFileItemPtr &firstItemError() const { return _firstItemError; } void processCompletedItem(const SyncFileItemPtr &item); @@ -95,14 +96,15 @@ private: int _numRemovedItems; int _numUpdatedItems; int _numRenamedItems; - int _numConflictItems; + int _numNewConflictItems; + int _numOldConflictItems; int _numErrorItems; SyncFileItemPtr _firstItemNew; SyncFileItemPtr _firstItemDeleted; SyncFileItemPtr _firstItemUpdated; SyncFileItemPtr _firstItemRenamed; - SyncFileItemPtr _firstConflictItem; + SyncFileItemPtr _firstNewConflictItem; SyncFileItemPtr _firstItemError; }; } From 7979342edffa97190f538061279f979dcb396717 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Wed, 12 Jul 2017 14:50:51 +0200 Subject: [PATCH 072/107] AccountSettings: Draw a box to indicate pending conflicts --- src/gui/folderstatusdelegate.cpp | 58 +++++++++++++++++++------------- src/gui/folderstatusdelegate.h | 1 + src/gui/folderstatusmodel.cpp | 4 +++ 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/src/gui/folderstatusdelegate.cpp b/src/gui/folderstatusdelegate.cpp index 1d93893747..0c9e5ad39b 100644 --- a/src/gui/folderstatusdelegate.cpp +++ b/src/gui/folderstatusdelegate.cpp @@ -76,12 +76,18 @@ QSize FolderStatusDelegate::sizeHint(const QStyleOptionViewItem &option, // calc height int h = rootFolderHeightWithoutErrors(fm, aliasFm); + // this already includes the bottom margin + // add some space to show an conflict indicator. + int margin = fm.height() / 4; + if (!qvariant_cast(index.data(FolderConflictMsg)).isEmpty()) { + QStringList msgs = qvariant_cast(index.data(FolderConflictMsg)); + h += margin + 2 * margin + msgs.count() * fm.height(); + } // add some space to show an error condition. if (!qvariant_cast(index.data(FolderErrorMsg)).isEmpty()) { - int margin = fm.height() / 4; QStringList errMsgs = qvariant_cast(index.data(FolderErrorMsg)); - h += margin + errMsgs.count() * fm.height(); + h += margin + 2 * margin + errMsgs.count() * fm.height(); } return QSize(0, h); @@ -98,7 +104,7 @@ int FolderStatusDelegate::rootFolderHeightWithoutErrors(const QFontMetrics &fm, h += fm.height(); // local path h += margin; // between local and remote path h += fm.height(); // remote path - h += aliasMargin; // bottom margin + h += margin; // bottom margin return h; } @@ -151,6 +157,7 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem & QString aliasText = qvariant_cast(index.data(HeaderRole)); QString pathText = qvariant_cast(index.data(FolderPathRole)); QString remotePath = qvariant_cast(index.data(FolderSecondPathRole)); + QStringList conflictTexts = qvariant_cast(index.data(FolderConflictMsg)); QStringList errorTexts = qvariant_cast(index.data(FolderErrorMsg)); int overallPercent = qvariant_cast(index.data(SyncProgressOverallPercent)); @@ -252,37 +259,40 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem & textAlign, elidedPathText); } - // paint an error overlay if there is an error string + int h = iconRect.bottom() + margin; - int h = iconRect.bottom(); - if (!errorTexts.isEmpty()) { - h += margin; - QRect errorRect = localPathRect; - errorRect.setLeft(iconRect.left()); - errorRect.setTop(h); - errorRect.setHeight(errorTexts.count() * subFm.height() + 2 * margin); - errorRect.setRight(option.rect.right() - margin); + // paint an error overlay if there is an error string or conflict string + auto drawTextBox = [&](const QStringList &texts, QColor color) { + QRect rect = localPathRect; + rect.setLeft(iconRect.left()); + rect.setTop(h); + rect.setHeight(texts.count() * subFm.height() + 2 * margin); + rect.setRight(option.rect.right() - margin); - painter->setBrush(QColor(0xbb, 0x4d, 0x4d)); + painter->setBrush(color); painter->setPen(QColor(0xaa, 0xaa, 0xaa)); - painter->drawRoundedRect(QStyle::visualRect(option.direction, option.rect, errorRect), + painter->drawRoundedRect(QStyle::visualRect(option.direction, option.rect, rect), 4, 4); painter->setPen(Qt::white); painter->setFont(errorFont); - QRect errorTextRect(errorRect.left() + margin, - errorRect.top() + margin, - errorRect.width() - 2 * margin, + QRect textRect(rect.left() + margin, + rect.top() + margin, + rect.width() - 2 * margin, subFm.height()); - foreach (QString eText, errorTexts) { - painter->drawText(QStyle::visualRect(option.direction, option.rect, errorTextRect), textAlign, - subFm.elidedText(eText, Qt::ElideLeft, errorTextRect.width())); - errorTextRect.translate(0, errorTextRect.height()); + foreach (QString eText, texts) { + painter->drawText(QStyle::visualRect(option.direction, option.rect, textRect), textAlign, + subFm.elidedText(eText, Qt::ElideLeft, textRect.width())); + textRect.translate(0, textRect.height()); } - h = errorRect.bottom(); - } - h += margin; + h = rect.bottom() + margin; + }; + + if (!conflictTexts.isEmpty()) + drawTextBox(conflictTexts, QColor(0xba, 0xba, 0x4d)); + if (!errorTexts.isEmpty()) + drawTextBox(errorTexts, QColor(0xbb, 0x4d, 0x4d)); // Sync File Progress Bar: Show it if syncFile is not empty. if (showProgess) { diff --git a/src/gui/folderstatusdelegate.h b/src/gui/folderstatusdelegate.h index a3fd5b71d2..213c822dd1 100644 --- a/src/gui/folderstatusdelegate.h +++ b/src/gui/folderstatusdelegate.h @@ -33,6 +33,7 @@ public: HeaderRole, FolderPathRole, // for a SubFolder it's the complete path FolderSecondPathRole, + FolderConflictMsg, FolderErrorMsg, FolderSyncPaused, FolderStatusIconRole, diff --git a/src/gui/folderstatusmodel.cpp b/src/gui/folderstatusmodel.cpp index 7d443b6417..ac32dc865d 100644 --- a/src/gui/folderstatusmodel.cpp +++ b/src/gui/folderstatusmodel.cpp @@ -226,6 +226,10 @@ QVariant FolderStatusModel::data(const QModelIndex &index, int role) const return f->shortGuiLocalPath(); case FolderStatusDelegate::FolderSecondPathRole: return f->remotePath(); + case FolderStatusDelegate::FolderConflictMsg: + return (f->syncResult().numNewConflictItems() + f->syncResult().numOldConflictItems() > 0) + ? QStringList(tr("There are unresolved conflicts. Click for details.")) + : QStringList(); case FolderStatusDelegate::FolderErrorMsg: return f->syncResult().errorStrings(); case FolderStatusDelegate::SyncRunning: From 6be122edc4341997c007c98774cc46ce8ed03957 Mon Sep 17 00:00:00 2001 From: Kashyap Prajapati Date: Thu, 13 Jul 2017 16:02:19 +0530 Subject: [PATCH 073/107] Broken URL fixed in Dockerfile Url to mingw repo fixed. --- admin/win/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/win/docker/Dockerfile b/admin/win/docker/Dockerfile index 8e40a4991e..1629b97602 100644 --- a/admin/win/docker/Dockerfile +++ b/admin/win/docker/Dockerfile @@ -8,7 +8,7 @@ ENV HOME /root ENV REFRESHED_AT 20160421 RUN zypper --non-interactive --gpg-auto-import-keys refresh -RUN zypper --non-interactive --gpg-auto-import-keys ar http://download.opensuse.org/repositories/windows:/mingw/openSUSE_42.1/windows:mingw.repo +RUN zypper --non-interactive --gpg-auto-import-keys ar http://download.opensuse.org/repositories/windows:/mingw/openSUSE_Leap_42.1/windows:mingw.repo RUN zypper --non-interactive --gpg-auto-import-keys ar http://download.opensuse.org/repositories/isv:ownCloud:toolchains:mingw:win32:2.3/openSUSE_Leap_42.1/isv:ownCloud:toolchains:mingw:win32:2.3.repo RUN zypper --non-interactive --gpg-auto-import-keys install cmake make mingw32-cross-binutils mingw32-cross-cpp mingw32-cross-gcc \ mingw32-cross-gcc-c++ mingw32-cross-pkg-config mingw32-filesystem \ From 5738110cb632cb62a36783bbabcc03300db7ff0f Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Thu, 13 Jul 2017 11:27:02 +0200 Subject: [PATCH 074/107] OAuth2: Have a link to the browser in the owncloud UI When the browser is open, ad a link in the ui to re-open the browser. Issue #5893 --- src/gui/accountsettings.cpp | 22 +++++++++++++++++++--- src/gui/accountsettings.h | 2 +- src/gui/accountstate.cpp | 4 +++- src/gui/accountstate.h | 7 +++++-- src/gui/application.cpp | 3 ++- src/gui/creds/httpcredentialsgui.cpp | 3 +++ src/gui/creds/httpcredentialsgui.h | 9 +++++++++ src/gui/creds/oauth.cpp | 11 ++++++----- src/gui/creds/oauth.h | 2 ++ 9 files changed, 50 insertions(+), 13 deletions(-) diff --git a/src/gui/accountsettings.cpp b/src/gui/accountsettings.cpp index 6da34d5904..51a6130a67 100644 --- a/src/gui/accountsettings.cpp +++ b/src/gui/accountsettings.cpp @@ -30,6 +30,7 @@ #include "accountmanager.h" #include "owncloudsetupwizard.h" #include "creds/abstractcredentials.h" +#include "creds/httpcredentialsgui.h" #include "tooltipupdater.h" #include "filesystem.h" @@ -180,8 +181,8 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent) ui->connectLabel->setText(tr("No account configured.")); - connect(_accountState, SIGNAL(stateChanged(int)), SLOT(slotAccountStateChanged(int))); - slotAccountStateChanged(_accountState->state()); + connect(_accountState, &AccountState::stateChanged, this, &AccountSettings::slotAccountStateChanged); + slotAccountStateChanged(); connect(&_quotaInfo, SIGNAL(quotaUpdated(qint64, qint64)), this, SLOT(slotUpdateQuota(qint64, qint64))); @@ -622,8 +623,9 @@ void AccountSettings::slotUpdateQuota(qint64 total, qint64 used) } } -void AccountSettings::slotAccountStateChanged(int state) +void AccountSettings::slotAccountStateChanged() { + int state = _accountState ? _accountState->state() : AccountState::Disconnected; if (_accountState) { ui->sslButton->updateAccountState(_accountState); AccountPtr account = _accountState->account(); @@ -654,6 +656,20 @@ void AccountSettings::slotAccountStateChanged(int state) showConnectionLabel(tr("Server %1 is currently in maintenance mode.").arg(server)); } else if (state == AccountState::SignedOut) { showConnectionLabel(tr("Signed out from %1.").arg(serverWithUser)); + } else if (state == AccountState::AskingCredentials) { + QUrl url; + if (auto cred = qobject_cast(account->credentials())) { + connect(cred, &HttpCredentialsGui::authorisationLinkChanged, + this, &AccountSettings::slotAccountStateChanged, Qt::UniqueConnection); + url = cred->authorisationLink(); + } + if (url.isValid()) { + showConnectionLabel(tr("Obtaining authorization from the browser. " + "Click here to re-open the browser.") + .arg(url.toString(QUrl::FullyEncoded))); + } else { + showConnectionLabel(tr("Connecting to %1...").arg(serverWithUser)); + } } else { showConnectionLabel(tr("No connection to %1 at %2.") .arg(Utility::escape(Theme::instance()->appNameGUI()), server), diff --git a/src/gui/accountsettings.h b/src/gui/accountsettings.h index 3e45d7e4a7..eaef07eb47 100644 --- a/src/gui/accountsettings.h +++ b/src/gui/accountsettings.h @@ -65,7 +65,7 @@ signals: public slots: void slotOpenOC(); void slotUpdateQuota(qint64, qint64); - void slotAccountStateChanged(int state); + void slotAccountStateChanged(); AccountState *accountsState() { return _accountState; } diff --git a/src/gui/accountstate.cpp b/src/gui/accountstate.cpp index d2025dde18..dbe998ce1a 100644 --- a/src/gui/accountstate.cpp +++ b/src/gui/accountstate.cpp @@ -133,6 +133,8 @@ QString AccountState::stateString(State state) return tr("Network error"); case ConfigurationError: return tr("Configuration error"); + case AskingCredentials: + return tr("Asking Credentials"); } return tr("Unknown account state"); } @@ -307,7 +309,7 @@ void AccountState::slotInvalidCredentials() account()->credentials()->invalidateToken(); account()->credentials()->askFromUser(); - setState(ConfigurationError); + setState(AskingCredentials); _waitingForNewCredentials = true; } diff --git a/src/gui/accountstate.h b/src/gui/accountstate.h index 2333bcece8..ce36c94fbe 100644 --- a/src/gui/accountstate.h +++ b/src/gui/accountstate.h @@ -64,8 +64,11 @@ public: /// again automatically. NetworkError, - /// An error like invalid credentials where retrying won't help. - ConfigurationError + /// Server configuration error. (For example: unsupported version) + ConfigurationError, + + /// We are currently asking the user for credentials + AskingCredentials }; /// The actual current connectivity status. diff --git a/src/gui/application.cpp b/src/gui/application.cpp index 2cb11ae30b..3ebbb8fc5d 100644 --- a/src/gui/application.cpp +++ b/src/gui/application.cpp @@ -308,7 +308,8 @@ void Application::slotCheckConnection() // Don't check if we're manually signed out or // when the error is permanent. if (state != AccountState::SignedOut - && state != AccountState::ConfigurationError) { + && state != AccountState::ConfigurationError + && state != AccountState::AskingCredentials) { accountState->checkConnectivity(); } } diff --git a/src/gui/creds/httpcredentialsgui.cpp b/src/gui/creds/httpcredentialsgui.cpp index 9c629ce654..dbc1c7e185 100644 --- a/src/gui/creds/httpcredentialsgui.cpp +++ b/src/gui/creds/httpcredentialsgui.cpp @@ -42,7 +42,10 @@ void HttpCredentialsGui::askFromUser() _asyncAuth.reset(new OAuth(_account, this)); connect(_asyncAuth.data(), &OAuth::result, this, &HttpCredentialsGui::asyncAuthResult); + connect(_asyncAuth.data(), &OAuth::destroyed, + this, &HttpCredentialsGui::authorisationLinkChanged); _asyncAuth->start(); + emit authorisationLinkChanged(); } else if (reply->error() == QNetworkReply::AuthenticationRequiredError) { // Show the dialog // We will re-enter the event loop, so better wait the next iteration diff --git a/src/gui/creds/httpcredentialsgui.h b/src/gui/creds/httpcredentialsgui.h index 0eaeeee812..fefc4dd1d2 100644 --- a/src/gui/creds/httpcredentialsgui.h +++ b/src/gui/creds/httpcredentialsgui.h @@ -49,12 +49,21 @@ public: * or call showDialog to ask the password */ Q_INVOKABLE void askFromUser() Q_DECL_OVERRIDE; + /** + * In case of oauth, return an URL to the link to open the browser. + * An invalid URL otherwise + */ + QUrl authorisationLink() const { return _asyncAuth ? _asyncAuth->authorisationLink() : QUrl(); } + static QString requestAppPasswordText(const Account *account); private slots: void asyncAuthResult(OAuth::Result, const QString &user, const QString &accessToken, const QString &refreshToken); void showDialog(); +signals: + void authorisationLinkChanged(); + private: QScopedPointer> _asyncAuth; }; diff --git a/src/gui/creds/oauth.cpp b/src/gui/creds/oauth.cpp index 710e190ac1..43133f91f6 100644 --- a/src/gui/creds/oauth.cpp +++ b/src/gui/creds/oauth.cpp @@ -131,17 +131,18 @@ void OAuth::start() QTimer::singleShot(5 * 60 * 1000, this, [this] { result(Error); }); } - -bool OAuth::openBrowser() +QUrl OAuth::authorisationLink() const { Q_ASSERT(_server.isListening()); - auto url = QUrl(_account->url().toString() + return QUrl(_account->url().toString() + QLatin1String("/index.php/apps/oauth2/authorize?response_type=code&client_id=") + Theme::instance()->oauthClientId() + QLatin1String("&redirect_uri=http://localhost:") + QString::number(_server.serverPort())); +} - - if (!QDesktopServices::openUrl(url)) { +bool OAuth::openBrowser() +{ + if (!QDesktopServices::openUrl(authorisationLink())) { // We cannot open the browser, then we claim we don't support OAuth. emit result(NotSupported, QString()); return false; diff --git a/src/gui/creds/oauth.h b/src/gui/creds/oauth.h index fe7fd1c402..943f294f04 100644 --- a/src/gui/creds/oauth.h +++ b/src/gui/creds/oauth.h @@ -15,6 +15,7 @@ #pragma once #include #include +#include namespace OCC { @@ -53,6 +54,7 @@ public: Q_ENUM(Result); void start(); bool openBrowser(); + QUrl authorisationLink() const; signals: /** From 1c2d5f16c86657f6a03583e02e168810c1e3746f Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Thu, 13 Jul 2017 10:53:16 +0200 Subject: [PATCH 075/107] Account: Send a X-Request-ID header #5853 --- src/libsync/account.cpp | 14 ++++++++++++++ src/libsync/account.h | 3 +++ 2 files changed, 17 insertions(+) diff --git a/src/libsync/account.cpp b/src/libsync/account.cpp index 45168033f6..2d798c0ec3 100644 --- a/src/libsync/account.cpp +++ b/src/libsync/account.cpp @@ -33,6 +33,8 @@ #include #include +#include + namespace OCC { Q_LOGGING_CATEGORY(lcAccount, "sync.account", QtInfoMsg) @@ -123,6 +125,13 @@ AbstractCredentials *Account::credentials() const return _credentials.data(); } +static QByteArray generateRequestId() +{ + // Use a UUID with the starting and ending curly brace removed. + auto uuid = QUuid::createUuid().toByteArray(); + return uuid.mid(1, uuid.size() - 2); +} + void Account::setCredentials(AbstractCredentials *cred) { // set active credential manager @@ -155,6 +164,10 @@ void Account::setCredentials(AbstractCredentials *cred) SLOT(slotCredentialsFetched())); connect(_credentials.data(), SIGNAL(asked()), SLOT(slotCredentialsAsked())); + + // Generate a new request id + _requestId = generateRequestId(); + qCInfo(lcAccount) << "Account for" << url() << "has X-Request-ID" << _requestId; } QUrl Account::davUrl() const @@ -230,6 +243,7 @@ QNetworkReply *Account::sendRequest(const QByteArray &verb, const QUrl &url, QNe { req.setUrl(url); req.setSslConfiguration(this->getOrCreateSslConfig()); + req.setRawHeader("X-Request-ID", _requestId); if (verb == "HEAD" && !data) { return _am->head(req); } else if (verb == "GET" && !data) { diff --git a/src/libsync/account.h b/src/libsync/account.h index b34342d452..9fdc79ac96 100644 --- a/src/libsync/account.h +++ b/src/libsync/account.h @@ -251,6 +251,9 @@ private: /// Certificates that were explicitly rejected by the user QList _rejectedCertificates; + /// X-Request-ID to send in network requests + QByteArray _requestId; + static QString _configFileName; QString _davPath; // defaults to value from theme, might be overwritten in brandings From bd107e133fbe428b44edc283d973a552dd93630b Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Thu, 13 Jul 2017 11:06:04 +0200 Subject: [PATCH 076/107] Ensure qsrand is called --- src/cmd/cmd.cpp | 3 ++- src/gui/application.cpp | 3 +++ src/gui/folder.cpp | 1 - 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/cmd/cmd.cpp b/src/cmd/cmd.cpp index 5baeb8a8be..5f7bc5e0f5 100644 --- a/src/cmd/cmd.cpp +++ b/src/cmd/cmd.cpp @@ -15,6 +15,7 @@ */ #include +#include #include #include #include @@ -311,7 +312,7 @@ int main(int argc, char **argv) qputenv("OPENSSL_CONF", opensslConf.toLocal8Bit()); #endif - qsrand(QTime::currentTime().msec() * QCoreApplication::applicationPid()); + qsrand(std::random_device()()); CmdOptions options; options.silent = false; diff --git a/src/gui/application.cpp b/src/gui/application.cpp index 3ebbb8fc5d..f06054b3f3 100644 --- a/src/gui/application.cpp +++ b/src/gui/application.cpp @@ -17,6 +17,7 @@ #include "application.h" #include +#include #include "config.h" #include "account.h" @@ -109,6 +110,8 @@ Application::Application(int &argc, char **argv) { _startedAt.start(); + qsrand(std::random_device()()); + #ifdef Q_OS_WIN // Ensure OpenSSL config file is only loaded from app directory QString opensslConf = QCoreApplication::applicationDirPath() + QString("/openssl.cnf"); diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index d4b652ba6d..f5e23c88be 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -62,7 +62,6 @@ Folder::Folder(const FolderDefinition &definition, , _fileLog(new SyncRunFileLog) , _saveBackwardsCompatible(false) { - qsrand(QTime::currentTime().msec()); _timeSinceLastSyncStart.start(); _timeSinceLastSyncDone.start(); From 1fb68e8711e7efa194cf5cd4c915b025e9572038 Mon Sep 17 00:00:00 2001 From: Markus Goetz Date: Thu, 13 Jul 2017 20:24:44 +0200 Subject: [PATCH 077/107] X-Request-ID: Send per request not per account #5853 --- src/libsync/accessmanager.cpp | 14 ++++++++++++++ src/libsync/account.cpp | 14 -------------- src/libsync/account.h | 3 --- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/libsync/accessmanager.cpp b/src/libsync/accessmanager.cpp index 1d8e91d4cb..4c19c20a19 100644 --- a/src/libsync/accessmanager.cpp +++ b/src/libsync/accessmanager.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "cookiejar.h" #include "accessmanager.h" @@ -59,6 +60,13 @@ void AccessManager::setRawCookie(const QByteArray &rawCookie, const QUrl &url) jar->setCookiesFromUrl(cookieList, url); } +static QByteArray generateRequestId() +{ + // Use a UUID with the starting and ending curly brace removed. + auto uuid = QUuid::createUuid().toByteArray(); + return uuid.mid(1, uuid.size() - 2); +} + QNetworkReply *AccessManager::createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *outgoingData) { QNetworkRequest newRequest(request); @@ -79,6 +87,12 @@ QNetworkReply *AccessManager::createRequest(QNetworkAccessManager::Operation op, if (verb == "PROPFIND") { newRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("text/xml; charset=utf-8")); } + + // Generate a new request id + QByteArray requestId = generateRequestId(); + qInfo(lcAccessManager) << op << verb << newRequest.url().toString() << "has X-Request-ID" << requestId; + newRequest.setRawHeader("X-Request-ID", requestId); + return QNetworkAccessManager::createRequest(op, newRequest, outgoingData); } diff --git a/src/libsync/account.cpp b/src/libsync/account.cpp index 2d798c0ec3..45168033f6 100644 --- a/src/libsync/account.cpp +++ b/src/libsync/account.cpp @@ -33,8 +33,6 @@ #include #include -#include - namespace OCC { Q_LOGGING_CATEGORY(lcAccount, "sync.account", QtInfoMsg) @@ -125,13 +123,6 @@ AbstractCredentials *Account::credentials() const return _credentials.data(); } -static QByteArray generateRequestId() -{ - // Use a UUID with the starting and ending curly brace removed. - auto uuid = QUuid::createUuid().toByteArray(); - return uuid.mid(1, uuid.size() - 2); -} - void Account::setCredentials(AbstractCredentials *cred) { // set active credential manager @@ -164,10 +155,6 @@ void Account::setCredentials(AbstractCredentials *cred) SLOT(slotCredentialsFetched())); connect(_credentials.data(), SIGNAL(asked()), SLOT(slotCredentialsAsked())); - - // Generate a new request id - _requestId = generateRequestId(); - qCInfo(lcAccount) << "Account for" << url() << "has X-Request-ID" << _requestId; } QUrl Account::davUrl() const @@ -243,7 +230,6 @@ QNetworkReply *Account::sendRequest(const QByteArray &verb, const QUrl &url, QNe { req.setUrl(url); req.setSslConfiguration(this->getOrCreateSslConfig()); - req.setRawHeader("X-Request-ID", _requestId); if (verb == "HEAD" && !data) { return _am->head(req); } else if (verb == "GET" && !data) { diff --git a/src/libsync/account.h b/src/libsync/account.h index 9fdc79ac96..b34342d452 100644 --- a/src/libsync/account.h +++ b/src/libsync/account.h @@ -251,9 +251,6 @@ private: /// Certificates that were explicitly rejected by the user QList _rejectedCertificates; - /// X-Request-ID to send in network requests - QByteArray _requestId; - static QString _configFileName; QString _davPath; // defaults to value from theme, might be overwritten in brandings From 06f3a70f9ab02f486ad8f63d2730b769ce84ee9b Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Thu, 13 Jul 2017 15:58:07 +0200 Subject: [PATCH 078/107] OAuth: Better message when loggin in with the wrong username Since the user is already in the browser, put the error message in the browser with a message to log out and then log in as the right user. Issue #5895 --- src/gui/creds/httpcredentialsgui.cpp | 9 ++++----- src/gui/creds/oauth.cpp | 12 ++++++++++++ src/gui/creds/oauth.h | 3 +++ 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/gui/creds/httpcredentialsgui.cpp b/src/gui/creds/httpcredentialsgui.cpp index dbc1c7e185..fca457a1e9 100644 --- a/src/gui/creds/httpcredentialsgui.cpp +++ b/src/gui/creds/httpcredentialsgui.cpp @@ -23,6 +23,7 @@ #include "theme.h" #include "account.h" #include +#include "asserts.h" using namespace QKeychain; @@ -40,6 +41,7 @@ void HttpCredentialsGui::askFromUser() if (reply->rawHeader("WWW-Authenticate").contains("Bearer ")) { // OAuth _asyncAuth.reset(new OAuth(_account, this)); + _asyncAuth->_expectedUser = _user; connect(_asyncAuth.data(), &OAuth::result, this, &HttpCredentialsGui::asyncAuthResult); connect(_asyncAuth.data(), &OAuth::destroyed, @@ -74,11 +76,8 @@ void HttpCredentialsGui::asyncAuthResult(OAuth::Result r, const QString &user, break; } - if (_user != user) { - QMessageBox::warning(nullptr, tr("Login Error"), tr("You must sign in as user %1").arg(_user)); - _asyncAuth->openBrowser(); - return; - } + ASSERT(_user == user); // ensured by _asyncAuth + _password = token; _refreshToken = refreshToken; _ready = true; diff --git a/src/gui/creds/oauth.cpp b/src/gui/creds/oauth.cpp index 43133f91f6..185b091fcb 100644 --- a/src/gui/creds/oauth.cpp +++ b/src/gui/creds/oauth.cpp @@ -116,6 +116,18 @@ void OAuth::start() emit result(Error); return; } + if (!_expectedUser.isNull() && user != _expectedUser) { + // Connected with the wrong user + QString message = tr("

Wrong user

" + "

You logged-in with user %1, but must login with user %2.
" + "Please log out of %3 in another tab, then click here " + "and log in as user %2

") + .arg(user, _expectedUser, Theme::instance()->appNameGUI(), + authorisationLink().toString(QUrl::FullyEncoded)); + httpReplyAndClose(socket, "200 OK", message.toUtf8().constData()); + // We are still listening on the socket so we will get the new connection + return; + } const char *loginSuccessfullHtml = "

Login Successful

You can close this window.

"; if (messageUrl.isValid()) { httpReplyAndClose(socket, "303 See Other", loginSuccessfullHtml, diff --git a/src/gui/creds/oauth.h b/src/gui/creds/oauth.h index 943f294f04..7024396473 100644 --- a/src/gui/creds/oauth.h +++ b/src/gui/creds/oauth.h @@ -66,6 +66,9 @@ signals: private: Account *_account; QTcpServer _server; + +public: + QString _expectedUser; }; From 1c6a83d0ddc4ede4390e252e56506a62c2e05ff5 Mon Sep 17 00:00:00 2001 From: Kaustubh Welankar Date: Fri, 14 Jul 2017 16:50:49 +0530 Subject: [PATCH 079/107] Fix for issue number 5692 --- src/cmd/cmd.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cmd/cmd.cpp b/src/cmd/cmd.cpp index 5baeb8a8be..d748612ec3 100644 --- a/src/cmd/cmd.cpp +++ b/src/cmd/cmd.cpp @@ -442,6 +442,11 @@ int main(int argc, char **argv) loop.exec(); #endif + if (job->reply()->error() != QNetworkReply::NoError){ + std::cout<<"Authentication Error\n"; + return EXIT_FAILURE; + } + // much lower age than the default since this utility is usually made to be run right after a change in the tests SyncEngine::minimumFileAgeForUpload = 0; From ab580103c7cc1707ed510a17604402ac129c00c8 Mon Sep 17 00:00:00 2001 From: Markus Goetz Date: Fri, 14 Jul 2017 15:37:05 +0200 Subject: [PATCH 080/107] csync_vio: Better log for stat errors --- csync/src/vio/csync_vio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csync/src/vio/csync_vio.c b/csync/src/vio/csync_vio.c index 3993af9a1d..909117836a 100644 --- a/csync/src/vio/csync_vio.c +++ b/csync/src/vio/csync_vio.c @@ -115,7 +115,7 @@ int csync_vio_stat(CSYNC *ctx, const char *uri, csync_vio_file_stat_t *buf) { case LOCAL_REPLICA: rc = csync_vio_local_stat(uri, buf); if (rc < 0) { - CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "Local stat failed, errno %d", errno); + CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "Local stat failed, errno %d for %s", errno, uri); } break; default: From 90ed57f478774ab7119e044e3e7cca1013e5661e Mon Sep 17 00:00:00 2001 From: Kaustubh Welankar Date: Sat, 15 Jul 2017 16:50:40 +0530 Subject: [PATCH 081/107] Change error message. Removed Qt5 if check --- src/cmd/cmd.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/cmd/cmd.cpp b/src/cmd/cmd.cpp index d748612ec3..0778eb3630 100644 --- a/src/cmd/cmd.cpp +++ b/src/cmd/cmd.cpp @@ -425,7 +425,6 @@ int main(int argc, char **argv) account->setCredentials(cred); account->setSslErrorHandler(sslErrorHandler); -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) //obtain capabilities using event loop QEventLoop loop; @@ -440,10 +439,9 @@ int main(int argc, char **argv) job->start(); loop.exec(); -#endif if (job->reply()->error() != QNetworkReply::NoError){ - std::cout<<"Authentication Error\n"; + std::cout<<"Error connecting to server\n"; return EXIT_FAILURE; } From 520923b5a7670cd5fba1363968b9a6d5e815ff0a Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Mon, 22 May 2017 14:41:06 +0200 Subject: [PATCH 082/107] HTTP/2 Support We need Qt 5.9 for HTTP2 because, even if Qt 5.8 already has support for it, there is some critical bug in the HTTP2 implementation which make it unusable [ https://codereview.qt-project.org/186050 and https://codereview.qt-project.org/186066 ] When using HTTP2, we can use many more parallel network request, this is especially good for small file handling Lower the priority of the GET and PUT propagation jobs, so the quota or selective sync ui PROPFIND will not be blocked by them --- src/libsync/accessmanager.cpp | 5 +++++ src/libsync/account.h | 5 +++++ src/libsync/connectionvalidator.cpp | 10 +++++++++- src/libsync/owncloudpropagator.cpp | 12 ++++++------ src/libsync/propagatedownload.cpp | 2 ++ src/libsync/propagateupload.cpp | 2 ++ src/libsync/syncengine.cpp | 2 +- 7 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/libsync/accessmanager.cpp b/src/libsync/accessmanager.cpp index 4c19c20a19..e9f485d93c 100644 --- a/src/libsync/accessmanager.cpp +++ b/src/libsync/accessmanager.cpp @@ -93,6 +93,11 @@ QNetworkReply *AccessManager::createRequest(QNetworkAccessManager::Operation op, qInfo(lcAccessManager) << op << verb << newRequest.url().toString() << "has X-Request-ID" << requestId; newRequest.setRawHeader("X-Request-ID", requestId); +#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) + // only enable HTTP2 with Qt 5.9 because Qt 5.8.0 has too many bugs (only use one connection if the server does not support HTTP2) + newRequest.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true); +#endif + return QNetworkAccessManager::createRequest(op, newRequest, outgoingData); } diff --git a/src/libsync/account.h b/src/libsync/account.h index b34342d452..de1cd0293c 100644 --- a/src/libsync/account.h +++ b/src/libsync/account.h @@ -189,6 +189,10 @@ public: /** Detects a specific bug in older server versions */ bool rootEtagChangesNotOnlySubFolderEtags(); + /** True when the server supports HTTP2 */ + bool isHttp2Supported() { return _http2Supported; } + void setHttp2Supported(bool value) { _http2Supported = value; }; + void clearCookieJar(); void lendCookieJarTo(QNetworkAccessManager *guest); QString cookieJarPath(); @@ -247,6 +251,7 @@ private: QuotaInfo *_quotaInfo; QSharedPointer _am; QScopedPointer _credentials; + bool _http2Supported = false; /// Certificates that were explicitly rejected by the user QList _rejectedCertificates; diff --git a/src/libsync/connectionvalidator.cpp b/src/libsync/connectionvalidator.cpp index 0b5897f1ff..9eff065dee 100644 --- a/src/libsync/connectionvalidator.cpp +++ b/src/libsync/connectionvalidator.cpp @@ -282,10 +282,18 @@ bool ConnectionValidator::setAndCheckServerVersion(const QString &version) reportResult(ServerVersionMismatch); return false; } - // We attempt to work with servers >= 5.0.0 but warn users. // Check usages of Account::serverVersionUnsupported() for details. +#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) + // Record that the server supports HTTP/2 + if (auto job = qobject_cast(sender())) { + if (auto reply = job->reply()) { + _account->setHttp2Supported( + reply->attribute(QNetworkRequest::HTTP2WasUsedAttribute).toBool()); + } + } +#endif return true; } diff --git a/src/libsync/owncloudpropagator.cpp b/src/libsync/owncloudpropagator.cpp index 016f0cdc84..e76e00e8e1 100644 --- a/src/libsync/owncloudpropagator.cpp +++ b/src/libsync/owncloudpropagator.cpp @@ -84,18 +84,18 @@ int OwncloudPropagator::maximumActiveTransferJob() // disable parallelism when there is a network limit. return 1; } - return qCeil(hardMaximumActiveJob() / 2.); + return qMax(3, qCeil(hardMaximumActiveJob() / 2.)); } /* The maximum number of active jobs in parallel */ int OwncloudPropagator::hardMaximumActiveJob() { static int max = qgetenv("OWNCLOUD_MAX_PARALLEL").toUInt(); - if (!max) { - max = 6; //default (Qt cannot do more anyway) - // TODO: increase this number when using HTTP2 - } - return max; + if (max) + return max; + if (_account->isHttp2Supported()) + return 20; + return 6; // (Qt cannot do more anyway) } PropagateItemJob::~PropagateItemJob() diff --git a/src/libsync/propagatedownload.cpp b/src/libsync/propagatedownload.cpp index b5d6ac0040..2a6ca14394 100644 --- a/src/libsync/propagatedownload.cpp +++ b/src/libsync/propagatedownload.cpp @@ -118,6 +118,8 @@ void GETFileJob::start() req.setRawHeader(it.key(), it.value()); } + req.setPriority(QNetworkRequest::LowPriority); // Long downloads must not block non-propagation jobs. + if (_directDownloadUrl.isEmpty()) { sendRequest("GET", makeDavUrl(path()), req); } else { diff --git a/src/libsync/propagateupload.cpp b/src/libsync/propagateupload.cpp index 757aee1c44..1cd0266277 100644 --- a/src/libsync/propagateupload.cpp +++ b/src/libsync/propagateupload.cpp @@ -79,6 +79,8 @@ void PUTFileJob::start() req.setRawHeader(it.key(), it.value()); } + req.setPriority(QNetworkRequest::LowPriority); // Long uploads must not block non-propagation jobs. + if (_url.isValid()) { sendRequest("PUT", _url, req, _device); } else { diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index 8fa6eb7b72..967f5ca6a0 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -852,7 +852,7 @@ void SyncEngine::startSync() _discoveryMainThread->setParent(this); connect(this, SIGNAL(finished(bool)), _discoveryMainThread, SLOT(deleteLater())); qCInfo(lcEngine) << "Server" << account()->serverVersion() - << QString("rootEtagChangesNotOnlySubFolderEtags=%1").arg(account()->rootEtagChangesNotOnlySubFolderEtags()); + << (account()->isHttp2Supported() ? "Using HTTP/2" : ""); if (account()->rootEtagChangesNotOnlySubFolderEtags()) { connect(_discoveryMainThread, SIGNAL(etag(QString)), this, SLOT(slotRootEtagReceived(QString))); } else { From b1363d1a79113d78890fa9bada78c3e89b6e8dd0 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Tue, 13 Jun 2017 15:29:01 +0200 Subject: [PATCH 083/107] AccessManager: Disable HTTP/2 without TLS Qt would otherwise still try to do HTTP/2 connection even over "http://". And that does not work with server that does not support it --- src/libsync/accessmanager.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libsync/accessmanager.cpp b/src/libsync/accessmanager.cpp index e9f485d93c..fb131b02c2 100644 --- a/src/libsync/accessmanager.cpp +++ b/src/libsync/accessmanager.cpp @@ -94,8 +94,11 @@ QNetworkReply *AccessManager::createRequest(QNetworkAccessManager::Operation op, newRequest.setRawHeader("X-Request-ID", requestId); #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) - // only enable HTTP2 with Qt 5.9 because Qt 5.8.0 has too many bugs (only use one connection if the server does not support HTTP2) - newRequest.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true); + // only enable HTTP2 with Qt 5.9 because Qt 5.8.0 has too many bugs + // (only use one connection if the server does not support HTTP2) + if (newRequest.url().scheme() == "https") { // Not for "http": QTBUG-61397 + newRequest.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true); + } #endif return QNetworkAccessManager::createRequest(op, newRequest, outgoingData); From 3cea550d08423579cccc15997d510ece6fec49ed Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Sat, 15 Jul 2017 09:36:24 +0200 Subject: [PATCH 084/107] PropagateUpload: Remove the Qt workaround against data corruption This was only for Qt <= 5.4, which we don't really support anymore. And even then, the data corruption is happens very seldomly anyway. --- src/libsync/propagateupload.cpp | 23 ----------------------- src/libsync/propagateupload.h | 4 ---- 2 files changed, 27 deletions(-) diff --git a/src/libsync/propagateupload.cpp b/src/libsync/propagateupload.cpp index 1cd0266277..3032bb65fa 100644 --- a/src/libsync/propagateupload.cpp +++ b/src/libsync/propagateupload.cpp @@ -35,12 +35,6 @@ #include #include -#if QT_VERSION < QT_VERSION_CHECK(5, 4, 2) -namespace { -const char owncloudShouldSoftCancelPropertyName[] = "owncloud-should-soft-cancel"; -} -#endif - namespace OCC { Q_LOGGING_CATEGORY(lcPutJob, "sync.networkjob.put", QtInfoMsg) @@ -93,27 +87,10 @@ void PUTFileJob::start() connect(reply(), SIGNAL(uploadProgress(qint64, qint64)), this, SIGNAL(uploadProgress(qint64, qint64))); connect(this, SIGNAL(networkActivity()), account().data(), SIGNAL(propagatorNetworkActivity())); - -// For Qt versions not including https://codereview.qt-project.org/110150 -// Also do the runtime check if compiled with an old Qt but running with fixed one. -// (workaround disabled on windows and mac because the binaries we ship have patched qt) -#if QT_VERSION < QT_VERSION_CHECK(5, 4, 2) && !defined Q_OS_WIN && !defined Q_OS_MAC - if (QLatin1String(qVersion()) < QLatin1String("5.4.2")) - connect(_device, SIGNAL(wasReset()), this, SLOT(slotSoftAbort())); -#endif - _requestTimer.start(); AbstractNetworkJob::start(); } -#if QT_VERSION < QT_VERSION_CHECK(5, 4, 2) -void PUTFileJob::slotSoftAbort() -{ - reply()->setProperty(owncloudShouldSoftCancelPropertyName, true); - reply()->abort(); -} -#endif - void PollJob::start() { setTimeout(120 * 1000); diff --git a/src/libsync/propagateupload.h b/src/libsync/propagateupload.h index 8d6b1d0b63..dd75509254 100644 --- a/src/libsync/propagateupload.h +++ b/src/libsync/propagateupload.h @@ -154,10 +154,6 @@ signals: void finishedSignal(); void uploadProgress(qint64, qint64); -private slots: -#if QT_VERSION < 0x050402 - void slotSoftAbort(); -#endif }; /** From 50874eecfab50c626b0d00b46afa178f0c5b8184 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Fri, 14 Jul 2017 15:56:32 +0200 Subject: [PATCH 085/107] OAuth: Add the user in the authorize call Issues: #5897, https://github.com/owncloud/oauth2/issues/48 --- src/gui/creds/oauth.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/gui/creds/oauth.cpp b/src/gui/creds/oauth.cpp index 185b091fcb..e711755b57 100644 --- a/src/gui/creds/oauth.cpp +++ b/src/gui/creds/oauth.cpp @@ -146,10 +146,13 @@ void OAuth::start() QUrl OAuth::authorisationLink() const { Q_ASSERT(_server.isListening()); - return QUrl(_account->url().toString() + QUrl url = QUrl(_account->url().toString() + QLatin1String("/index.php/apps/oauth2/authorize?response_type=code&client_id=") + Theme::instance()->oauthClientId() + QLatin1String("&redirect_uri=http://localhost:") + QString::number(_server.serverPort())); + if (!_expectedUser.isNull()) + url.addQueryItem("user", _expectedUser); + return url; } bool OAuth::openBrowser() From d9813dbc90ca6b1f7c14b941f7b1c9aff019ff89 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Tue, 18 Jul 2017 08:58:07 +0200 Subject: [PATCH 086/107] Propagator: fix a qMax which should have been a qMin --- src/libsync/owncloudpropagator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsync/owncloudpropagator.cpp b/src/libsync/owncloudpropagator.cpp index e76e00e8e1..17ab9facab 100644 --- a/src/libsync/owncloudpropagator.cpp +++ b/src/libsync/owncloudpropagator.cpp @@ -84,7 +84,7 @@ int OwncloudPropagator::maximumActiveTransferJob() // disable parallelism when there is a network limit. return 1; } - return qMax(3, qCeil(hardMaximumActiveJob() / 2.)); + return qMin(3, qCeil(hardMaximumActiveJob() / 2.)); } /* The maximum number of active jobs in parallel */ From 075c423c62da59cf12bdba676a7b17efa274a708 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Fri, 21 Jul 2017 08:54:06 +0200 Subject: [PATCH 087/107] buildint.rst: Put back reference to openssl on windows OpenSSL might no longer be needed to build the client, but we still need it to run it. That reverts part of commit 6e57b0219edd7dcc99509620277bb53478d958a6 for this file. Fix #5858 Reported in https://central.owncloud.org/t/locally-built-client-error-creating-ssl-context/8264 --- doc/building.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/doc/building.rst b/doc/building.rst index 48f6f0212e..5207b45f7a 100644 --- a/doc/building.rst +++ b/doc/building.rst @@ -120,6 +120,7 @@ follow `Windows Installer Build (Cross-Compile)`_ instead. * Make sure that you have CMake_ and Git_. * Download the Qt_ MinGW package. You will use the MinGW version bundled with it. + * Download an `OpenSSL Windows Build`_ (the non-"Light" version) 2. Get the QtKeychain_ sources as well as the latest versions of the ownCloud client from Git as follows:: @@ -129,10 +130,11 @@ follow `Windows Installer Build (Cross-Compile)`_ instead. 3. Open the Qt MinGW shortcut console from the Start Menu -4. Make sure that your qtkeychain source directories are in your PATH. This will - allow CMake to find the library and headers, as well as allow the ownCloud - client to find the DLLs at runtime:: +4. Make sure that OpenSSL's ``bin`` directory as well as your qtkeychain source + directories are in your PATH. This will allow CMake to find the library and + headers, as well as allow the ownCloud client to find the DLLs at runtime:: + set PATH=C:\\bin;%PATH% set PATH=C:\;%PATH% 5. Build qtkeychain **directly in the source directory** so that the DLL is built @@ -277,6 +279,7 @@ The following are known cmake parameters: .. _Git: http://git-scm.com .. _MacPorts: http://www.macports.org .. _Homebrew: http://mxcl.github.com/homebrew/ +.. _OpenSSL Windows Build: http://slproweb.com/products/Win32OpenSSL.html .. _Qt: http://www.qt.io/download .. _Microsoft Authenticode: https://msdn.microsoft.com/en-us/library/ie/ms537361%28v=vs.85%29.aspx .. _QtKeychain: https://github.com/frankosterfeld/qtkeychain From 5a1bf7d8feb1900fd93ca4f9c613cef4c9cbff93 Mon Sep 17 00:00:00 2001 From: Jocelyn Turcotte Date: Mon, 24 Jul 2017 15:11:30 +0200 Subject: [PATCH 088/107] windows: Fix a memory leak in FileSystem::longWinPath Also use c_path_to_UNC directly instead of doing an extra UTF-8->wchar conversion. --- src/libsync/filesystem.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/libsync/filesystem.cpp b/src/libsync/filesystem.cpp index c068f46943..18e6f800ff 100644 --- a/src/libsync/filesystem.cpp +++ b/src/libsync/filesystem.cpp @@ -54,12 +54,14 @@ Q_LOGGING_CATEGORY(lcFileSystem, "sync.filesystem", QtInfoMsg) QString FileSystem::longWinPath(const QString &inpath) { - QString path(inpath); - #ifdef Q_OS_WIN - path = QString::fromWCharArray(static_cast(c_utf8_path_to_locale(inpath.toUtf8()))); -#endif + const char *unc_str = c_path_to_UNC(inpath.toUtf8()); + QString path = QString::fromUtf8(unc_str); + free((void*)unc_str); return path; +#else + return inpath; +#endif } bool FileSystem::fileEquals(const QString &fn1, const QString &fn2) From a41dc00160f1a2510ab71cfcff9ef0c61ff3f6e8 Mon Sep 17 00:00:00 2001 From: Jocelyn Turcotte Date: Mon, 24 Jul 2017 08:03:05 +0200 Subject: [PATCH 089/107] Don't keep the list of touched files for the whole sync We only want to know if they were touched within the last 15 seconds, so change the data structure to use a QMultiMap, and sort them by QElapsedTimer. This allows us to iterate over old entries ordered by time and to stop once we find a recent entry. This makes the look-up slower but in most cases the folder watcher will report any change within milliseconds, and we start from the most recent. What this really makes slower are actual user file changes while a fast sync is underways which will need to iterate over the whole map to find out the file isn't there. This reduces the growth of the memory usage when downloading a large amount of files. --- src/gui/folder.cpp | 5 ++--- src/libsync/syncengine.cpp | 34 ++++++++++++++++++++++++++-------- src/libsync/syncengine.h | 8 ++------ 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index f5e23c88be..4ca5de844f 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -459,9 +459,8 @@ void Folder::slotWatchedPathChanged(const QString &path) // own process. Therefore nothing needs to be done here! #else // Use the path to figure out whether it was our own change - const auto maxNotificationDelay = 15 * 1000; - qint64 time = _engine->timeSinceFileTouched(path); - if (time != -1 && time < maxNotificationDelay) { + if (_engine->wasFileTouched(path)) { + qCDebug(lcFolder) << "Changed path was touched by SyncEngine, ignoring:" << path; return; } #endif diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index 967f5ca6a0..d4afcc57c6 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -55,6 +55,7 @@ namespace OCC { Q_LOGGING_CATEGORY(lcEngine, "sync.engine", QtInfoMsg) +static const int s_touchedFilesMaxAgeMs = 15 * 1000; bool SyncEngine::s_anySyncRunning = false; qint64 SyncEngine::minimumFileAgeForUpload = 2000; @@ -1512,12 +1513,27 @@ void SyncEngine::restoreOldFiles(SyncFileItemVector &syncItems) void SyncEngine::slotAddTouchedFile(const QString &fn) { + QElapsedTimer now; + now.start(); QString file = QDir::cleanPath(fn); - QElapsedTimer timer; - timer.start(); + // Iterate from the oldest and remove anything older than 15 seconds. + while (true) { + auto first = _touchedFiles.begin(); + if (first == _touchedFiles.end()) + break; + // Compare to our new QElapsedTimer instead of using elapsed(). + // This avoids querying the current time from the OS for every loop. + if (now.msecsSinceReference() - first.key().msecsSinceReference() <= s_touchedFilesMaxAgeMs) { + // We found the first path younger than 15 second, keep the rest. + break; + } - _touchedFiles.insert(file, timer); + _touchedFiles.erase(first); + } + + // This should be the largest QElapsedTimer yet, use constEnd() as hint. + _touchedFiles.insert(_touchedFiles.constEnd(), now, file); } void SyncEngine::slotClearTouchedFiles() @@ -1525,13 +1541,15 @@ void SyncEngine::slotClearTouchedFiles() _touchedFiles.clear(); } -qint64 SyncEngine::timeSinceFileTouched(const QString &fn) const +bool SyncEngine::wasFileTouched(const QString &fn) const { - if (!_touchedFiles.contains(fn)) { - return -1; + // Start from the end (most recent) and look for our path. Check the time just in case. + auto begin = _touchedFiles.constBegin(); + for (auto it = _touchedFiles.constEnd(); it != begin; --it) { + if ((it-1).value() == fn) + return (it-1).key().elapsed() <= s_touchedFilesMaxAgeMs; } - - return _touchedFiles[fn].elapsed(); + return false; } AccountPtr SyncEngine::account() const diff --git a/src/libsync/syncengine.h b/src/libsync/syncengine.h index 4f96e404f0..a275618a43 100644 --- a/src/libsync/syncengine.h +++ b/src/libsync/syncengine.h @@ -87,11 +87,7 @@ public: /* Returns whether another sync is needed to complete the sync */ AnotherSyncNeeded isAnotherSyncNeeded() { return _anotherSyncNeeded; } - /** Get the ms since a file was touched, or -1 if it wasn't. - * - * Thread-safe. - */ - qint64 timeSinceFileTouched(const QString &fn) const; + bool wasFileTouched(const QString &fn) const; AccountPtr account() const; SyncJournalDb *journal() const { return _journal; } @@ -272,7 +268,7 @@ private: AnotherSyncNeeded _anotherSyncNeeded; /** Stores the time since a job touched a file. */ - QHash _touchedFiles; + QMultiMap _touchedFiles; /** For clearing the _touchedFiles variable after sync finished */ QTimer _clearTouchedFilesTimer; From a831164d657b4352079ac667d03937042e225787 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Fri, 21 Jul 2017 11:28:15 +0200 Subject: [PATCH 090/107] Revert "Revert "Discovery: consider also the "shared by me" as shared"" This reverts commit efa7821dd28491ad0b93fec53be6d4ad0f94f19f. This reverts the revert, but also add a check that the server version is bigger than 10.0 Issue #4788 --- src/libsync/discoveryphase.cpp | 22 +++++++++++++++++++++- src/libsync/syncfilestatus.cpp | 14 +++++++------- src/libsync/syncfilestatus.h | 8 ++++---- src/libsync/syncfilestatustracker.cpp | 2 +- test/syncenginetestutils.h | 4 +++- test/testsyncfilestatustracker.cpp | 7 ++++++- 6 files changed, 42 insertions(+), 15 deletions(-) diff --git a/src/libsync/discoveryphase.cpp b/src/libsync/discoveryphase.cpp index 16b98faeb2..dcad77390c 100644 --- a/src/libsync/discoveryphase.cpp +++ b/src/libsync/discoveryphase.cpp @@ -276,6 +276,10 @@ void DiscoverySingleDirectoryJob::start() << "http://owncloud.org/ns:checksums"; if (_isRootPath) props << "http://owncloud.org/ns:data-fingerprint"; + if (_account->serverVersionInt() >= Account::makeServerVersion(10, 0, 0)) { + // Server older than 10.0 have performances issue if we ask for the share-types on every PROPFIND + props << "http://owncloud.org/ns:share-types"; + } lsColJob->setProperties(props); @@ -371,9 +375,25 @@ static csync_vio_file_stat_t *propertyMapToFileStat(const QMap if (!checksum.isEmpty()) { file_stat->checksumHeader = strdup(checksum.constData()); } + } else if (property == "share-types" && !value.isEmpty()) { + // Since QMap is sorted, "share-types" is always "permissions". + if (file_stat->remotePerm[0] == '\0' || !(file_stat->fields & CSYNC_VIO_FILE_STAT_FIELDS_PERM)) { + qWarning() << "Server returned a share type, but no permissions?"; + } else { + // S means shared with me. + // But for our purpose, we want to know if the file is shared. It does not matter + // if we are the owner or not. + // Piggy back on the persmission field 'S' + if (!std::strchr(file_stat->remotePerm, 'S')) { + if (std::strlen(file_stat->remotePerm) < sizeof(file_stat->remotePerm) - 1) { + std::strcat(file_stat->remotePerm, "S"); + } else { + qWarning() << "permissions too large" << file_stat->remotePerm; + } + } + } } } - return file_stat; } diff --git a/src/libsync/syncfilestatus.cpp b/src/libsync/syncfilestatus.cpp index 12a9977860..8d1a5933fe 100644 --- a/src/libsync/syncfilestatus.cpp +++ b/src/libsync/syncfilestatus.cpp @@ -17,13 +17,13 @@ namespace OCC { SyncFileStatus::SyncFileStatus() : _tag(StatusNone) - , _sharedWithMe(false) + , _shared(false) { } SyncFileStatus::SyncFileStatus(SyncFileStatusTag tag) : _tag(tag) - , _sharedWithMe(false) + , _shared(false) { } @@ -37,14 +37,14 @@ SyncFileStatus::SyncFileStatusTag SyncFileStatus::tag() const return _tag; } -void SyncFileStatus::setSharedWithMe(bool isShared) +void SyncFileStatus::setShared(bool isShared) { - _sharedWithMe = isShared; + _shared = isShared; } -bool SyncFileStatus::sharedWithMe() const +bool SyncFileStatus::shared() const { - return _sharedWithMe; + return _shared; } QString SyncFileStatus::toSocketAPIString() const @@ -71,7 +71,7 @@ QString SyncFileStatus::toSocketAPIString() const statusString = QLatin1String("ERROR"); break; } - if (canBeShared && _sharedWithMe) { + if (canBeShared && _shared) { statusString += QLatin1String("+SWM"); } diff --git a/src/libsync/syncfilestatus.h b/src/libsync/syncfilestatus.h index 27006a5722..e4743f7a3f 100644 --- a/src/libsync/syncfilestatus.h +++ b/src/libsync/syncfilestatus.h @@ -44,19 +44,19 @@ public: void set(SyncFileStatusTag tag); SyncFileStatusTag tag() const; - void setSharedWithMe(bool isShared); - bool sharedWithMe() const; + void setShared(bool isShared); + bool shared() const; QString toSocketAPIString() const; private: SyncFileStatusTag _tag; - bool _sharedWithMe; + bool _shared; }; inline bool operator==(const SyncFileStatus &a, const SyncFileStatus &b) { - return a.tag() == b.tag() && a.sharedWithMe() == b.sharedWithMe(); + return a.tag() == b.tag() && a.shared() == b.shared(); } inline bool operator!=(const SyncFileStatus &a, const SyncFileStatus &b) diff --git a/src/libsync/syncfilestatustracker.cpp b/src/libsync/syncfilestatustracker.cpp index e643099b29..9d4fa6e37d 100644 --- a/src/libsync/syncfilestatustracker.cpp +++ b/src/libsync/syncfilestatustracker.cpp @@ -286,7 +286,7 @@ SyncFileStatus SyncFileStatusTracker::resolveSyncAndErrorStatus(const QString &r ASSERT(sharedFlag != UnknownShared, "The shared status needs to have been fetched from a SyncFileItem or the DB at this point."); if (sharedFlag == Shared) - status.setSharedWithMe(true); + status.setShared(true); return status; } diff --git a/test/syncenginetestutils.h b/test/syncenginetestutils.h index b90ff480b1..062b62cea7 100644 --- a/test/syncenginetestutils.h +++ b/test/syncenginetestutils.h @@ -289,6 +289,7 @@ public: QString etag = generateEtag(); QByteArray fileId = generateFileId(); QByteArray checksums; + QByteArray extraDavProperties; qint64 size = 0; char contentChar = 'W'; @@ -360,6 +361,7 @@ public: xml.writeTextElement(ocUri, QStringLiteral("permissions"), fileInfo.isShared ? QStringLiteral("SRDNVCKW") : QStringLiteral("RDNVCKW")); xml.writeTextElement(ocUri, QStringLiteral("id"), fileInfo.fileId); xml.writeTextElement(ocUri, QStringLiteral("checksums"), fileInfo.checksums); + buffer.write(fileInfo.extraDavProperties); xml.writeEndElement(); // prop xml.writeTextElement(davUri, QStringLiteral("status"), "HTTP/1.1 200 OK"); xml.writeEndElement(); // propstat @@ -826,7 +828,7 @@ public: OCC::SyncEngine &syncEngine() const { return *_syncEngine; } FileModifier &localModifier() { return _localModifier; } - FileModifier &remoteModifier() { return _fakeQnam->currentRemoteState(); } + FileInfo &remoteModifier() { return _fakeQnam->currentRemoteState(); } FileInfo currentLocalState() { QDir rootDir{_tempDir.path()}; FileInfo rootTemplate; diff --git a/test/testsyncfilestatustracker.cpp b/test/testsyncfilestatustracker.cpp index bcb6ae2058..741ae1dc6d 100644 --- a/test/testsyncfilestatustracker.cpp +++ b/test/testsyncfilestatustracker.cpp @@ -411,11 +411,14 @@ private slots: void sharedStatus() { SyncFileStatus sharedUpToDateStatus(SyncFileStatus::StatusUpToDate); - sharedUpToDateStatus.setSharedWithMe(true); + sharedUpToDateStatus.setShared(true); FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()}; fakeFolder.remoteModifier().insert("S/s0"); fakeFolder.remoteModifier().appendByte("S/s1"); + fakeFolder.remoteModifier().insert("B/b3"); + fakeFolder.remoteModifier().find("B/b3")->extraDavProperties = "0"; + StatusPushSpy statusSpy(fakeFolder.syncEngine()); fakeFolder.scheduleSync(); @@ -435,6 +438,8 @@ private slots: QEXPECT_FAIL("", "We currently only know if a new file is shared on the second sync, after a PROPFIND.", Continue); QCOMPARE(statusSpy.statusOf("S/s0"), sharedUpToDateStatus); QCOMPARE(statusSpy.statusOf("S/s1"), sharedUpToDateStatus); + QCOMPARE(statusSpy.statusOf("B/b1").shared(), false); + QCOMPARE(statusSpy.statusOf("B/b3"), sharedUpToDateStatus); QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); } From c043840cb19fca81a56e87e32465388011da6b55 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Tue, 18 Jul 2017 14:53:41 +0200 Subject: [PATCH 091/107] OAuth: Fix refresh of token after expiration Before commit d3b00532b1dd9f44cc606e6738b53345c37582cf, fetchFromKeychain was called everytime we detect that the creds are invalid (in AccountState::slotInvalidCredentials) But since that commit, AccountState was calling askFromUser directly, breaking the refresh of the token. So I made sure AccountState::slotInvalidCredentials still calls refreshAccessToken. Another change that was made was too be sure to clear the cookies in HttpCredentials::invalidateToken even when we are only clearing the access_token. That's because the session with a cookie may stay valid longer than the access_token --- src/gui/accountstate.cpp | 16 +++++++++++----- src/libsync/creds/httpcredentials.cpp | 12 ++++++++---- src/libsync/creds/httpcredentials.h | 9 ++++++--- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/gui/accountstate.cpp b/src/gui/accountstate.cpp index dbe998ce1a..09300b1abd 100644 --- a/src/gui/accountstate.cpp +++ b/src/gui/accountstate.cpp @@ -16,6 +16,7 @@ #include "accountmanager.h" #include "account.h" #include "creds/abstractcredentials.h" +#include "creds/httpcredentials.h" #include "logger.h" #include "configfile.h" @@ -305,12 +306,17 @@ void AccountState::slotInvalidCredentials() qCInfo(lcAccountState) << "Invalid credentials for" << _account->url().toString() << "asking user"; - if (account()->credentials()->ready()) - account()->credentials()->invalidateToken(); - account()->credentials()->askFromUser(); - - setState(AskingCredentials); _waitingForNewCredentials = true; + setState(AskingCredentials); + + if (account()->credentials()->ready()) { + account()->credentials()->invalidateToken(); + if (auto creds = qobject_cast(account()->credentials())) { + if (creds->refreshAccessToken()) + return; + } + } + account()->credentials()->askFromUser(); } void AccountState::slotCredentialsFetched(AbstractCredentials *) diff --git a/src/libsync/creds/httpcredentials.cpp b/src/libsync/creds/httpcredentials.cpp index bd1cc7aef7..2ebb9f9a20 100644 --- a/src/libsync/creds/httpcredentials.cpp +++ b/src/libsync/creds/httpcredentials.cpp @@ -292,8 +292,11 @@ void HttpCredentials::slotReadJobDone(QKeychain::Job *incomingJob) } } -void HttpCredentials::refreshAccessToken() +bool HttpCredentials::refreshAccessToken() { + if (_refreshToken.isEmpty()) + return false; + QUrl requestToken(_account->url().toString() + QLatin1String("/index.php/apps/oauth2/api/v1/token?grant_type=refresh_token&refresh_token=") + _refreshToken); @@ -324,6 +327,7 @@ void HttpCredentials::refreshAccessToken() } emit fetched(); }); + return true; } @@ -344,6 +348,9 @@ void HttpCredentials::invalidateToken() return; } + // clear the session cookie. + _account->clearCookieJar(); + if (!_refreshToken.isEmpty()) { // Only invalidate the access_token (_password) but keep the _refreshToken in the keychain // (when coming from forgetSensitiveData, the _refreshToken is cleared) @@ -364,9 +371,6 @@ void HttpCredentials::invalidateToken() job2->setKey(kck); job2->start(); - // clear the session cookie. - _account->clearCookieJar(); - // let QNAM forget about the password // This needs to be done later in the event loop because we might be called (directly or // indirectly) from QNetworkAccessManagerPrivate::authenticationRequired, which itself diff --git a/src/libsync/creds/httpcredentials.h b/src/libsync/creds/httpcredentials.h index 85e6d092af..df3bcdabc4 100644 --- a/src/libsync/creds/httpcredentials.h +++ b/src/libsync/creds/httpcredentials.h @@ -46,8 +46,8 @@ namespace OCC { 1) First, AccountState will attempt to load the certificate from the keychain - ----> fetchFromKeychain ------------------------> shortcut to refreshAccessToken if the cached - | } information is still valid + ----> fetchFromKeychain + | } v } slotReadClientCertPEMJobDone } There are first 3 QtKeychain jobs to fetch | } the TLS client keys, if any, and the password @@ -92,7 +92,10 @@ public: QString fetchUser(); virtual bool sslIsTrusted() { return false; } - void refreshAccessToken(); + /* If we still have a valid refresh token, try to refresh it assynchronously and emit fetched() + * otherwise return false + */ + bool refreshAccessToken(); // To fetch the user name as early as possible void setAccount(Account *account) Q_DECL_OVERRIDE; From 6ae88514d8fe875751300ebf5c746a4e7e8a9eb5 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Mon, 24 Jul 2017 12:00:29 +0200 Subject: [PATCH 092/107] OAuth: clear refresh token when the server claim not to support oauth Allow upgrade path when the server removes support for oauth Relates: https://github.com/owncloud/client/issues/5848#issuecomment-317353049 We also need to force the account to commit the config to the disk, otherwise we may not register we are no longer using owncloud and we risk sending the password as the token to the token refresh API call --- src/gui/creds/httpcredentialsgui.cpp | 1 + src/libsync/creds/httpcredentials.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/gui/creds/httpcredentialsgui.cpp b/src/gui/creds/httpcredentialsgui.cpp index fca457a1e9..2ab2d0d30e 100644 --- a/src/gui/creds/httpcredentialsgui.cpp +++ b/src/gui/creds/httpcredentialsgui.cpp @@ -120,6 +120,7 @@ void HttpCredentialsGui::showDialog() bool ok = dialog.exec(); if (ok) { _password = dialog.textValue(); + _refreshToken.clear(); _ready = true; persist(); } diff --git a/src/libsync/creds/httpcredentials.cpp b/src/libsync/creds/httpcredentials.cpp index 2ebb9f9a20..0f71f445ee 100644 --- a/src/libsync/creds/httpcredentials.cpp +++ b/src/libsync/creds/httpcredentials.cpp @@ -397,6 +397,7 @@ void HttpCredentials::persist() _account->setCredentialSetting(QLatin1String(userC), _user); _account->setCredentialSetting(QLatin1String(isOAuthC), isUsingOAuth()); + _account->wantsAccountSaved(_account); // write cert WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName()); From cdce7f204407b1293f2b506faa4adffeb6f3140f Mon Sep 17 00:00:00 2001 From: Markus Goetz Date: Wed, 26 Jul 2017 11:46:18 +0200 Subject: [PATCH 093/107] encoding_tests: No more iconv --- csync/tests/encoding_tests/check_encoding.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/csync/tests/encoding_tests/check_encoding.c b/csync/tests/encoding_tests/check_encoding.c index bbfbe99c8d..eb761d318a 100644 --- a/csync/tests/encoding_tests/check_encoding.c +++ b/csync/tests/encoding_tests/check_encoding.c @@ -29,30 +29,12 @@ static void setup(void **state) { - int rc = 0; - (void) state; /* unused */ - -#ifdef HAVE_ICONV -#ifdef __APPLE__ - /* this test only works on apple because linux does not know the - * UTF-8-MAC encoding that we use here. */ - rc = c_setup_iconv("UTF-8-MAC"); -#endif -#endif - assert_int_equal(rc, 0); } static void teardown(void **state) { - int rc = 0; - (void) state; /* unused */ -#ifdef HAVE_ICONV - // this must never crash - rc = c_close_iconv(); -#endif - assert_int_equal(rc, 0); } static void check_iconv_to_native_normalization(void **state) From 41ed603abf62fa70d0b0b62df0e1e20604893c3d Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Fri, 21 Jul 2017 15:54:47 +0200 Subject: [PATCH 094/107] AccountState: reset _waitingForNewCredentials when signin in Just to force the logic to re-ask the credenticals, in case we were already asking them when singin off. Issue: https://github.com/owncloud/client/issues/5893#issuecomment-316949686 --- src/gui/accountstate.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/accountstate.cpp b/src/gui/accountstate.cpp index 09300b1abd..63316f6d2d 100644 --- a/src/gui/accountstate.cpp +++ b/src/gui/accountstate.cpp @@ -154,6 +154,7 @@ void AccountState::signOutByUi() void AccountState::signIn() { if (_state == SignedOut) { + _waitingForNewCredentials = false; setState(Disconnected); } } From 90cce0ab56dde825a01d5943fb26c9352b18a730 Mon Sep 17 00:00:00 2001 From: Jocelyn Turcotte Date: Thu, 27 Jul 2017 10:46:46 +0200 Subject: [PATCH 095/107] Fix the selective sync notification folder list being cropped The maximumHeight would stay at the last animated value. Issue #5492 --- src/gui/accountsettings.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/gui/accountsettings.cpp b/src/gui/accountsettings.cpp index 51a6130a67..392bd1efce 100644 --- a/src/gui/accountsettings.cpp +++ b/src/gui/accountsettings.cpp @@ -804,9 +804,12 @@ void AccountSettings::refreshSelectiveSyncStatus() auto anim = new QPropertyAnimation(ui->selectiveSyncStatus, "maximumHeight", ui->selectiveSyncStatus); anim->setEndValue(shouldBeVisible ? hint.height() : 0); anim->start(QAbstractAnimation::DeleteWhenStopped); - if (!shouldBeVisible) { - connect(anim, SIGNAL(finished()), ui->selectiveSyncStatus, SLOT(hide())); - } + connect(anim, &QPropertyAnimation::finished, [this, shouldBeVisible]() { + ui->selectiveSyncStatus->setMaximumHeight(QWIDGETSIZE_MAX); + if (!shouldBeVisible) { + ui->selectiveSyncStatus->hide(); + } + }); } } From 7a96e8a292c9d543ce772a5aadb76d36157b7dd2 Mon Sep 17 00:00:00 2001 From: Jocelyn Turcotte Date: Thu, 27 Jul 2017 10:53:17 +0200 Subject: [PATCH 096/107] General settings: Put all advanced checkboxes in a layout The mac style has the concept of Layout Item Rectangle that bleeds widgets margins into parent margins. This unfortunately doesn't work when the parent layout doesn't have any margin, like we do when we have the hierarchy: Vertical(normal margin) / Horizontal(no margin, uses parent layout spacing) /Widget I guess Qt should propagate remaining uneaten margins to grand-parent layouts and so on to have this work properly, but nobody seems to have touched that code since Qt 4.4. So just try to make the problem less worse by making sure that all checkboxes we want to align are either in the same layout, or in a loyout of their own. Issue #5492 --- src/gui/generalsettings.ui | 202 +++++++++++++++++++------------------ 1 file changed, 105 insertions(+), 97 deletions(-) diff --git a/src/gui/generalsettings.ui b/src/gui/generalsettings.ui index 20f2c86d37..43d006fae2 100644 --- a/src/gui/generalsettings.ui +++ b/src/gui/generalsettings.ui @@ -47,103 +47,6 @@ - - - - Advanced - - - - - - - - Edit &Ignored Files - - - - - - - Qt::Horizontal - - - - 555 - 20 - - - - - - - - - - - - Ask for confirmation before synchronizing folders larger than - - - true - - - - - - - 999999 - - - 99 - - - - - - - MB - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Ask for confirmation before synchronizing external storages - - - - - - - - 0 - 0 - - - - S&how crash reporter - - - - - - @@ -230,6 +133,111 @@ + + + + Advanced + + + + + + + + Edit &Ignored Files + + + + + + + Qt::Horizontal + + + + 555 + 20 + + + + + + + + + + + + Ask for confirmation before synchronizing folders larger than + + + true + + + + + + + 999999 + + + 99 + + + + + + + MB + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Ask for confirmation before synchronizing external storages + + + + + + + + + + + + 0 + 0 + + + + S&how crash reporter + + + + + + + + From f707a43b3cff0491ac5b0428d0e18a3aa3fa2db3 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Fri, 28 Jul 2017 10:13:07 +0200 Subject: [PATCH 097/107] FolderWizard: fix crash in FolderWizardRemotePath::slotHandleLsColNetworkError The Job is a LsColJob, not a MkColJob! Reproduce by entering a name with invalid character that cause an error 400 in the folder wizard's remote path line edit. (Relates issue #5924) --- src/gui/folderwizard.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gui/folderwizard.cpp b/src/gui/folderwizard.cpp index 940cf8b3ac..168f60d88a 100644 --- a/src/gui/folderwizard.cpp +++ b/src/gui/folderwizard.cpp @@ -22,6 +22,7 @@ #include "accountstate.h" #include "creds/abstractcredentials.h" #include "wizard/owncloudwizard.h" +#include "asserts.h" #include #include @@ -229,7 +230,8 @@ void FolderWizardRemotePath::slotHandleMkdirNetworkError(QNetworkReply *reply) void FolderWizardRemotePath::slotHandleLsColNetworkError(QNetworkReply * /*reply*/) { - auto job = qobject_cast(sender()); + auto job = qobject_cast(sender()); + ASSERT(job); showWarn(tr("Failed to list a folder. Error: %1") .arg(job->errorStringParsingBody())); } From 3731eba9b60f430aa8d491d77a439219be09f81b Mon Sep 17 00:00:00 2001 From: Matthew Setter Date: Mon, 31 Jul 2017 14:04:02 +0200 Subject: [PATCH 098/107] Clean up the client docs removing old, outdated, content This makes changes as suggested by @michaelstingl in https://github.com/owncloud/documentation/issues/3240. --- .gitmodules | 3 -- doc/ocdoc | 1 - doc/owncloud.1.rst | 34 --------------- doc/owncloudcmd.1.rst | 98 ------------------------------------------- 4 files changed, 136 deletions(-) delete mode 160000 doc/ocdoc delete mode 100644 doc/owncloud.1.rst delete mode 100644 doc/owncloudcmd.1.rst diff --git a/.gitmodules b/.gitmodules index ad27bc9740..ab18390aa5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "doc/ocdoc"] - path = doc/ocdoc - url = https://github.com/owncloud/documentation [submodule "src/3rdparty/qtmacgoodies"] path = src/3rdparty/qtmacgoodies url = https://github.com/guruz/qtmacgoodies.git diff --git a/doc/ocdoc b/doc/ocdoc deleted file mode 160000 index 2fdd8b2833..0000000000 --- a/doc/ocdoc +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2fdd8b2833ea1db7da08a61afda665eb6ecaa017 diff --git a/doc/owncloud.1.rst b/doc/owncloud.1.rst deleted file mode 100644 index 27092f0b65..0000000000 --- a/doc/owncloud.1.rst +++ /dev/null @@ -1,34 +0,0 @@ -:orphan: - -owncloud(1) ------------ - -SYNOPSIS -======== -*owncloud* [`OPTIONS`...] - - -DESCRIPTION -=========== -The ownCloud Client is a file synchronization desktop utility. It synchronizes files on your local computer, tablet, or handheld device with an ownCloud Server. If you make a change to the files on one device, the change is propagated to all other synchronized devices using the desktop synchronization clients. - -Normally, you start the client by clicking on the desktop icon or by starting it from the client application menu. After starting, an ownCloud icon appears in the computer system tray or on your tablet or handheld device. - -Options -======= -.. include:: options.rst - -Config File -=========== -.. include:: conffile.rst - -BUGS -==== - -Please report bugs at https://github.com/owncloud/client/issues. - - -SEE ALSO -======== -:manpage:`owncloudcmd(1)` - diff --git a/doc/owncloudcmd.1.rst b/doc/owncloudcmd.1.rst deleted file mode 100644 index 573b0d1f12..0000000000 --- a/doc/owncloudcmd.1.rst +++ /dev/null @@ -1,98 +0,0 @@ -:orphan: - -owncloudcmd(1) --------------- - -SYNOPSIS -======== -*owncloudcmd* [`OPTIONS`...] sourcedir owncloudurl - -DESCRIPTION -=========== -owncloudcmd is the command line tool used for the ownCloud file synchronization -desktop utility. - -Contrary to the :manpage:`owncloud(1)` GUI client, `owncloudcmd` only performs -a single sync run and then exits. In so doing, `owncloudcmd` replaces the -`ocsync` binary used for the same purpose in earlier releases. - -A *sync run* synchronizes a single local directory using a WebDAV share on a -remote ownCloud server. - -To invoke the command line client, provide the local and the remote repository: -The first parameter is the local directory. The second parameter is -the server URL. - -.. note:: Prior to the 1.6 release of owncloudcmd, the tool only accepted - ``owncloud://`` or ``ownclouds://`` in place of ``http://`` and ``https://`` as - a scheme. See ``Examples`` for details. - -OPTIONS -======= -``--user``, ``-u`` ``[user]`` - Use ``user`` as the login name. - -``--password``, ``-p`` ``[password]`` - Use ``password`` as the password. - -``-n`` - Use ``netrc (5)`` for login. - -``--non-interactive`` - Do not prompt for questions. - -``--silent``, ``--s`` - Inhibits verbose log output. - -``--trust`` - Trust any SSL certificate, including invalid ones. - -``--httpproxy http://[user@pass:]:`` - Uses ``server`` as HTTP proxy. - -``--nonshib`` - Uses Non Shibboleth WebDAV Authentication - -``--davpath [path]`` - Overrides the WebDAV Path with ``path`` - -``--exclude [file]`` - Exclude list file - -``--unsyncedfolders [file]`` - File containing the list of un-synced folders (selective sync) - -``--max-sync-retries [n]`` - Retries maximum n times (defaults to 3) - -``-h`` - Sync hidden files,do not ignore them - -Example -======= -To synchronize the ownCloud directory ``Music`` to the local directory ``media/music`` -through a proxy listening on port ``8080`` on the gateway machine ``192.168.178.1``, -the command line would be:: - - $ owncloudcmd --httpproxy http://192.168.178.1:8080 \ - $HOME/media/music \ - https://server/owncloud/remote.php/webdav/Music - -``owncloudcmd`` will enquire user name and password, unless they have -been specified on the command line or ``-n`` (see `netrc(5)`) has been passed. - -Using the legacy scheme, it would be:: - - $ owncloudcmd --httpproxy http://192.168.178.1:8080 \ - $HOME/media/music \ - ownclouds://server/owncloud/remote.php/webdav/Music - - -BUGS -==== -Please report bugs at https://github.com/owncloud/client/issues. - -SEE ALSO -======== -:manpage:`owncloud(1)` - From 9e36cabcbff1c7c0bc3dd0e836d08b5224916999 Mon Sep 17 00:00:00 2001 From: Jocelyn Turcotte Date: Fri, 28 Jul 2017 17:51:52 +0200 Subject: [PATCH 099/107] macos: Add a sidebar icon In the process, use an iconset to generate the icns using iconutil. Also add some missing icon resolutions according to the guidelines. Issue #296 --- cmake/modules/AddAppIconMacro.cmake | 87 ++++++++++++++++---------- src/gui/CMakeLists.txt | 2 +- theme/colored/owncloud-icon-1024.png | Bin 0 -> 52860 bytes theme/colored/owncloud-icon-16.png | Bin 0 -> 495 bytes theme/colored/owncloud-sidebar-16.png | Bin 0 -> 495 bytes theme/colored/owncloud-sidebar-18.png | Bin 0 -> 625 bytes theme/colored/owncloud-sidebar-32.png | Bin 0 -> 1061 bytes theme/colored/owncloud-sidebar-36.png | Bin 0 -> 1159 bytes theme/colored/owncloud-sidebar-64.png | Bin 0 -> 2153 bytes 9 files changed, 54 insertions(+), 35 deletions(-) create mode 100644 theme/colored/owncloud-icon-1024.png create mode 100644 theme/colored/owncloud-icon-16.png create mode 100644 theme/colored/owncloud-sidebar-16.png create mode 100644 theme/colored/owncloud-sidebar-18.png create mode 100644 theme/colored/owncloud-sidebar-32.png create mode 100644 theme/colored/owncloud-sidebar-36.png create mode 100644 theme/colored/owncloud-sidebar-64.png diff --git a/cmake/modules/AddAppIconMacro.cmake b/cmake/modules/AddAppIconMacro.cmake index 3106b1bfae..dc833bd9d9 100644 --- a/cmake/modules/AddAppIconMacro.cmake +++ b/cmake/modules/AddAppIconMacro.cmake @@ -70,45 +70,64 @@ macro (KDE4_ADD_APP_ICON appsources pattern) endif(PNG2ICO_EXECUTABLE AND WINDRES_EXECUTABLE) endif(WIN32) if (APPLE) - # first convert image to a tiff using the Mac OS X "sips" utility, - # then use tiff2icns to convert to an icon - find_program(SIPS_EXECUTABLE NAMES sips) - find_program(TIFF2ICNS_EXECUTABLE NAMES tiff2icns) - if (SIPS_EXECUTABLE AND TIFF2ICNS_EXECUTABLE) - file(GLOB_RECURSE files "${pattern}") - # we can only test for the 128-icon like that - we don't use patterns anymore - foreach (it ${files}) - if (it MATCHES ".*128.*" ) - set (_icon ${it}) - endif (it MATCHES ".*128.*") - endforeach (it) + file(GLOB_RECURSE files "${pattern}") + file(MAKE_DIRECTORY ${appsources}.iconset) - if (_icon) - - # first, get the basename of our app icon - add_custom_command(OUTPUT ${_outfilename}.icns ${outfilename}.tiff - COMMAND ${SIPS_EXECUTABLE} -s format tiff ${_icon} --out ${outfilename}.tiff - COMMAND ${TIFF2ICNS_EXECUTABLE} ${outfilename}.tiff ${_outfilename}.icns - DEPENDS ${_icon} - ) + # List from: + # https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Optimizing/Optimizing.html#//apple_ref/doc/uid/TP40012302-CH7-SW4 + foreach (it ${files}) + if (it MATCHES ".*icon-16.*") + configure_file(${it} ${appsources}.iconset/icon_16x16.png COPYONLY) + elseif (it MATCHES ".*icon-32.*") + configure_file(${it} ${appsources}.iconset/icon_16x16@2x.png COPYONLY) + configure_file(${it} ${appsources}.iconset/icon_32x32.png COPYONLY) + elseif (it MATCHES ".*icon-64.*") + configure_file(${it} ${appsources}.iconset/icon_32x32@2x.png COPYONLY) + elseif (it MATCHES ".*icon-128.*") + configure_file(${it} ${appsources}.iconset/icon_128x128.png COPYONLY) + elseif (it MATCHES ".*icon-256.*") + configure_file(${it} ${appsources}.iconset/icon_128x128@2x.png COPYONLY) + configure_file(${it} ${appsources}.iconset/icon_256x256.png COPYONLY) + elseif (it MATCHES ".*icon-512.*") + configure_file(${it} ${appsources}.iconset/icon_256x256@2x.png COPYONLY) + configure_file(${it} ${appsources}.iconset/icon_512x512.png COPYONLY) + elseif (it MATCHES ".*icon-1024.*") + configure_file(${it} ${appsources}.iconset/icon_512x512@2x.png COPYONLY) + endif() + endforeach (it) - # This will register the icon into the bundle - set(MACOSX_BUNDLE_ICON_FILE ${appsources}.icns) + # Copy the sidebar icons in the main app bundle for the FinderSync extension to pick. + # https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/Finder.html#//apple_ref/doc/uid/TP40014214-CH15-SW15 + foreach (it ${files}) + if (it MATCHES ".*sidebar-16.*") + configure_file(${it} ${appsources}.iconset/sidebar_16x16.png COPYONLY) + elseif (it MATCHES ".*sidebar-18.*") + configure_file(${it} ${appsources}.iconset/sidebar_18x18.png COPYONLY) + elseif (it MATCHES ".*sidebar-32.*") + configure_file(${it} ${appsources}.iconset/sidebar_16x16@2x.png COPYONLY) + configure_file(${it} ${appsources}.iconset/sidebar_32x32.png COPYONLY) + elseif (it MATCHES ".*sidebar-36.*") + configure_file(${it} ${appsources}.iconset/sidebar_18x18@2x.png COPYONLY) + elseif (it MATCHES ".*sidebar-64.*") + configure_file(${it} ${appsources}.iconset/sidebar_32x32@2x.png COPYONLY) + endif() + endforeach (it) - # Append the icns file to the sources list so it will be a dependency to the - # main target - list(APPEND ${appsources} ${_outfilename}.icns) + add_custom_command(OUTPUT ${_outfilename}.icns + COMMAND echo === Building bundle icns with iconset: + COMMAND ls -1 ${appsources}.iconset + COMMAND iconutil -c icns -o ${_outfilename}.icns ${appsources}.iconset + DEPENDS ${files} + ) - # Install the icon into the Resources dir in the bundle - set_source_files_properties(${_outfilename}.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources) + # This will register the icon into the bundle + set(MACOSX_BUNDLE_ICON_FILE ${appsources}.icns) - else(_icon) - # TODO - try to scale a non-128 icon...? Try to convert an SVG on the fly? - message(STATUS "Unable to find an 128x128 icon that matches pattern ${pattern} for variable ${appsources} - application will not have an application icon!") - endif(_icon) + # Append the icns file to the sources list so it will be a dependency to the + # main target + list(APPEND ${appsources} ${_outfilename}.icns) - else(SIPS_EXECUTABLE AND TIFF2ICNS_EXECUTABLE) - message(STATUS "Unable to find the sips and tiff2icns utilities - application will not have an application icon!") - endif(SIPS_EXECUTABLE AND TIFF2ICNS_EXECUTABLE) + # Install the icon into the Resources dir in the bundle + set_source_files_properties(${_outfilename}.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources) endif(APPLE) endmacro (KDE4_ADD_APP_ICON) diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 9011c07ab6..2157d3cd68 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -232,7 +232,7 @@ if (NOT DEFINED APPLICATION_ICON_NAME) set(APPLICATION_ICON_NAME ${APPLICATION_SHORTNAME}) endif() -kde4_add_app_icon( ownCloud "${theme_dir}/colored/${APPLICATION_ICON_NAME}-icon*.png") +kde4_add_app_icon( ownCloud "${theme_dir}/colored/${APPLICATION_ICON_NAME}-*.png") list(APPEND final_src ${ownCloud}) set(ownCloud ${ownCloud_old}) diff --git a/theme/colored/owncloud-icon-1024.png b/theme/colored/owncloud-icon-1024.png new file mode 100644 index 0000000000000000000000000000000000000000..e32847c1c91208540f2f876b4bd6dbf8266d5931 GIT binary patch literal 52860 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

MkHU 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

#SeiI)8=}0Yb$+We$eE~k@S-YM`>rR#q{# z#wD-52R+%XgG{B4s_?nSaP73w12WZnnGG?uMP3y=JyVes0`aIKDpNijv+kfcy4$Z^ zxi}S8^H^%hA+~NWW{h4-WMw4#vyu#@=w$idc^zqA{*wE%o5VgHyN&}<21KN)kLgqT z&F%t%BHS!d7B$$Fy^rR1waz_#hTz3%(~63+$=r3IA?1{l%lkqO3x>UV)uGZ> z7#)u>111xhOxL2)mThNfo9QI|w&;u-HOQ>8mZRNydT1Xt1gl1{EeHF!#oNP-ZBH1g z%x5;CmoTIUS=<}SZC!c{zB*LCqCj_bMF;)j6YhB2S!idZ6cjT0rfAZ^O{-4|88?+( z4=8FAbv%nw$~}j#4(SL*2dX;V~Od_)pT@!jd zUDY98bSF7P;zIW70*nB1U-I2=Pa4eR5pt7{bJ28J42W@1>B8>N=FA-k^Mgg)f7e?| znq92jw^%Y~!UYKjKN-nA9qRA3mucq{iGkYk4V^8U~T9Pj=5SSSz_y zH*im@t69s0EC$6n@rhKG*!J8sa;l&YyLXQ4K0j3mvj0%e(ebf@cOzdOY#lb`?n z&u%(ZM)ubS%#~7p-G=R%tl35xR1BUFjFv%^+3rA??hg`-oN$!Mu0KWOk!9tJ$OJ(* zx8Ifr$&150bPOukNZ?J6bbMel2vHo6He_NKRnkM3>OxXoVb#RWO%=UENj1^i(q}bQ waksuHUYm*o`hS1_j{^UX0{_3Gz#!@~5g`F}(S3dIzn8FoG^EQ5ES7CTbJfFhm1>fNOqYKnpWOXVMbHF!Dw zWdy}(lFxx!CBCJUkYWv7O2zESy=xb$`!(6#@ocKnoaaB!By%;;2kf&cxCkJ#0JJLC zBVhDUf~T*iIz`iU7wPKX+JRZ8ameumcpa6?TIIS90CAHg!`q~DUc0->ew(gESe>I!sTbsWy zr-ZLqd?e9_cn@mZuQpzi=|KY9lDZJLvRb`9j^bgMmK0i5avi*2vA8#-V3NcJq4Opc l0=@WcE~~)YQPci!_yZuuiOrMj=PdvL002ovPDHLkV1mpK*FgXP literal 0 HcmV?d00001 diff --git a/theme/colored/owncloud-sidebar-16.png b/theme/colored/owncloud-sidebar-16.png new file mode 100644 index 0000000000000000000000000000000000000000..5c01c4fe9e855c3d6f40a7254ee8d4809973adea GIT binary patch literal 495 zcmVFoG^EQ5ES7CTbJfFhm1>fNOqYKnpWOXVMbHF!Dw zWdy}(lFxx!CBCJUkYWv7O2zESy=xb$`!(6#@ocKnoaaB!By%;;2kf&cxCkJ#0JJLC zBVhDUf~T*iIz`iU7wPKX+JRZ8ameumcpa6?TIIS90CAHg!`q~DUc0->ew(gESe>I!sTbsWy zr-ZLqd?e9_cn@mZuQpzi=|KY9lDZJLvRb`9j^bgMmK0i5avi*2vA8#-V3NcJq4Opc l0=@WcE~~)YQPci!_yZuuiOrMj=PdvL002ovPDHLkV1mpK*FgXP literal 0 HcmV?d00001 diff --git a/theme/colored/owncloud-sidebar-18.png b/theme/colored/owncloud-sidebar-18.png new file mode 100644 index 0000000000000000000000000000000000000000..bff61757415e2dbb06bcaae5d7a5cf4385e4923e GIT binary patch literal 625 zcmV-%0*?KOP)|L=guCDFwG`2MYy3ge;P(bP=tDc5pEMpo1XRD46dj-*?~lIH+jT zK;89>_gtQPI2ZUgRjO8(mtVYDTO`dwyV5+kIW@Hi0M~$-nRnHiTpAA(&0x@#gD=nKa8Xv1k-gtd{_^H z3BM6_)uQ0;$b+Zn7ECmJfAR$B1`W(}3SR@*4Pe<%dzO%jfP2I-fBe9%$~@53vGRJ) zvpv@GiU@>|M?!9bU}@9cSqRIdTWKIK*^tq~k-a_uq%PjRcPNbfGbATPL;ygD8s|ZP zAL+OPkQ{&<=&}Hg0YK_o5amG{2n@(NfDaH;1KFwyP>_<}R<|gh<-UnYPaOZOb11YmUAHWn=bNIyGsX00000 LNkvXXu0mjfi{%XA literal 0 HcmV?d00001 diff --git a/theme/colored/owncloud-sidebar-32.png b/theme/colored/owncloud-sidebar-32.png new file mode 100644 index 0000000000000000000000000000000000000000..721c04e4858f09706c0d9cc2a8a5839548e43f3f GIT binary patch literal 1061 zcmV+=1ls$FP)%#QM~yAvP$m549mtloU2mF{Kp}twttDi^U96qUj>BY%UCPpl;7|UsoTd z>)pw=LiS}BJRhFxz7D_pe;>Hz?o6bG3GF@!FbM!;|4UNykQ^Xg69E6p? z_yJ;ap*EF@2Rpr&Ko!!^=J_@o?|Vd2J$M=DME%~D@}ilUj;EF`y*N_Bz!GfP^~GI~ zEXMo?Wiqel2RR%UY5}jg(rhl98Se&pHBu3PIfna37klE?P3x-$9n~zrtBw0#HhfKg zH+u&gvM-F*AXS*gc4-CIoeAEBD7x$ds>4)Y0Mzu)soyeje))=M_FOb0nh_J)qsN5< z|7N5Xo6M~$noY^~kN(B!X&PJ2?Y4ZsnUx%9@A&<`id#Ac6iAYE75ax>aJ{GeLZB2t z?me#XC(b$Kg=3Pybq*+C1jT}LH}L5|zn2;s4+7WZW%m-OSJd++y)W!qcZoo;oaJNd zPjhfe9c~&vYVA%a#5s`@Z)cMv{tBKSIK^R|r=dn!s}uc7IBe7KYNJ`J!?Ka%!%6pK zrKB4ZDVOa{(yzdVlFmS0L88)uSwW@nk3p-h)LBlGqx+F(hK@Nz%z6|;0Fb;atDn;k zdgsSQIORi16@s4yW?piXi}0Gep96awY4Bsl-5@lkS$&y}{$Acj9|$058^&s41i)nb zzOI%(g*8#KiGY^?L8MMxzBuKk(uWUFxt_R80unB(? fP6A88FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H11O!P$K~z|U?UhSxl~ojl*E;`gsiYOuShV4hND#D8Q(Hh75IO*B zjAFn*sZkt2YLr$*Mo7(7jIV^y7(yZgj1Nd{B7q1dCIo>&3)os~B{)&3RFc+5tOL2d z_nf^P6iRPj&?-*2EBOa!zs`5|dHg^|8D(^XiQ`Q?+1@qfA`{HE{`|&wzX)Ih7Ddb_Y`@of6cLrv3)-7eS&}04hHz%_dn5 z03UtX(lo(q>*{5tJ^vagVX5OWIS;eRgmOXtWa{&YI3}~N$>=sh(=Qpnp$lE+)x)U& zye~=#J0z_qY2yZpH#B5hBDFqAK88zY%@G?V^q8~vouUPjHUWd?2!Mn}$-fgpH3D-0 z6z%?^@gV?*#|?CAcK*vL?;jp$lk{Yim9@bqLY-qM7^UuFfiqIT+m5J(8bOxk;{Ifl$H6AbXqXNBAfB5d0 zxs`CbglY1zL-xX*CG8Z%^>Zdl>YuXhhQd-{r4vVNRE&< z)!zGL9)Pxfpx&gP%k7-NG3oOi&Yk>&$M&Md;_(Z`^rg05`HPt&l*cD58 z|2>4_L2)^PwwNIvIX(vj0B|6u!K?*j$u;tv7BDM}yw9Gbm-_lgq9WGYo7zVX0Rn`H z0H*{FY_^^<3!7b(HEozfnw~CnQ>urYw9c)Oz&Lxw%WqvrH;BGxUEl ziq;fDC^b{mXIDh=cki_=$mWlTt&C1EDX9-4i{ShUxmA_Ue5y2C-obEBKy+#Rp_8vxeS*3FNBWkyT|04H>Y$Q`YZ zS2tg-yY`j3WfUJb;2sA#0Y8X5Fvry#yl|l^>M6MQL7KZW5>M{kR^2{Y*E7l}ql|7y Z{R1F<8diQsqkI4W002ovPDHLkV1icD4=Dfu literal 0 HcmV?d00001 diff --git a/theme/colored/owncloud-sidebar-64.png b/theme/colored/owncloud-sidebar-64.png new file mode 100644 index 0000000000000000000000000000000000000000..5c6f17f0cd078587d6c496e0c604127a00f4d736 GIT binary patch literal 2153 zcmZ`*`9IT-1AfnPhYt7M$JHk>awN&-CPN6zH5$1JG3PAiUdTC$OgY2XkdRLwLXMTQ zIaBr(bL5(9_~|HuHbS6n10JBm+=D!Uu&^*?AAi4K zlzX73azKz*!Mctp0I)BZ8S32)pIXb`w-sBI=)v1&dpKgI)XOe5zZkcSE9iTa12iOu|8d=wmR*&lqd7X{7=p>@zL{^W*ZH)eDU_o z@5wNf`rah_Imi7-`e;pnF-s8^oD9<_jSvS?^qnhlN82@$&kV!WaT z7y;q;J)6~e(k` znP|tXWD@pPgZtdLzY-oFu2Mjqq5`i>4#YDHc*4>95>~=Gd#0>z1ufqm7P_#!NR7|a z%Kg@DTr4UuuH#Rf)M+kNPnv^9%d)5>*f7;uK`>!(B`fN(r&^+5OZmnudm`ZNT07q! zz*&Y~0EAUoZH_k(D6aAzIwrB*CzwN}X;*2mpaL}an%#{%#S2YaNCb1%CH(4&*Ufk+$r~#)B8lrtZ#7v#rp< z5o)r9|7dAQAn#ZP8`4F|Sg*6qqnuZjmTu&3iji~#d;@k27-`5`HFB zl3w4hA#-UYTTR9qvG7DTY$#IA@516LQVIcF>vgW26iH2y(97V)t1WeJj@`%0pr6AF zAY}7d;u8ZJhdf=Pw+p40Ko*!{wzHf|WrKIFfMAwODigOF%>%L8Sw%!~T7r&I$q1cW zq9g8p5>w+VVkyH7yP^x^aB&_ST>r-pFX**P5a{sX#Y+cckP$1%y6 z;CoZNdYfWvFc4xAwy~y*;kC%|DyTB?gJ=Q7fjxOI51YPv>hmPv*;wDaqs~X9Shm~d zaqGkaRu%FHBpuKcLb+XTkPrIq@n;!uM(YIru_KuDUv-ewfPA}lYdOCBNp^+roU`Vn z%ph`CqhpE5!)3b(#b6?oVmOxNzE02Bx>-icXGgMP~$gBle4~ezBr3Ep1%C#UaJ3Rgk~X zcIy42fM!Mt_wB<&A^xV_tBgt3GK)>-&(l7JR$M;(>dkxssZfr@h68{-fqY(981$23}qAu&3nV{+xUyR7N$ysarm}dieL2 zNM>bpLl1|Q2UY{}kp;1>i7fr0crCZXVQnEoMofOg&^l#Uh0HGsh*ZS)$kGB+`tMB1 zzFYM;b5;^jQsC z1my?txyHItL$j)BB6u{Et1VtS-51V8OE{^)TcTkCRiYV$@rJPxLxyA1R0i*>3%0p; z_jnSk4+$|jy7S%^wOek=78XUzG2((9kJ@wOa}+|x0KuP+&@McWQOMk=i?EB4psQu29}F^sw$jheXh>R z_oe$j-Ev=bwRq1jYs=Za;1h3rj&@*`mvBRy{eAD)oyR95)SMZy&Wm0b_j8(iI@#%l zM^0_19pi9jn;1V6#y#V_cWa+)vnt-6=*QzY_j2kDlfFPwAd;T=cP1aYz#EP0TITP5 zL7JIMGkM~kob_9luUAYf$YJqvtMR>b{RxGjN3bs{9StVhWdTy|cu`8bYJQ&=)0O(? z!Sq-2&Z(f>WOmH-e4f$kpj-rK@rCErpL*{bjwP*;az)JJxxDxxFH+ix)Fo};|1yRy a$plX&?r!qO<@%i70>I43(vYO@7W)sj Date: Wed, 10 May 2017 09:46:57 +0200 Subject: [PATCH 100/107] Update client docs version string to 2.4.0 Need to update this to match the latest version. --- doc/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index 61b0d63027..ce5bf4279c 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -48,9 +48,9 @@ copyright = u'2013-2016, The ownCloud developers' # built documents. # # The short X.Y version. -version = '2.3.0' +version = '2.4.0' # The full version, including alpha/beta/rc tags. -release = '2.3.0' +release = '2.4.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. From 13a5653c4e4ee3c504445ec909fe1cba36bd27ea Mon Sep 17 00:00:00 2001 From: Markus Goetz Date: Fri, 4 Aug 2017 15:23:44 +0200 Subject: [PATCH 101/107] ChangeLog: More 2.4.0 stuff --- ChangeLog | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/ChangeLog b/ChangeLog index ad96ac6566..8ba22d5df2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,32 +4,50 @@ ChangeLog version 2.4.0 (2017-0X-XX) * OAuth2 authentication support * Sharing: Add support for multiple public link shares (#5655) -* Sharing: Add option to copy/email direct links (#5627) +* Sharing: Add option to copy/email direct links (#5627, #5023) +* Sharing: Add option "show file listing" (#5837) * Sharing: Show warning that links are public * Sharing: Many UI improvements +* Sharing: Make "can edit" partially checked sometimes #5642 * Wizards: Never propose an existing folder for syncing (#5597) * Wizard: Don't show last page anymore, go to settings directly (#5726) * Settings Dialog: Display the user server avatar * Selective Sync: Open sub folder context menu (#5596) * Selective Sync: Skip excluded folders when reading db -* Selective Sync: SelectiveSync: Remove local files of unselected folder despite other modified files (#5783) -* Exclude list: remove .htaccess -* Detect maintenance mode (#4485) +* Selective Sync: Remove local files of unselected folder despite other modified files (#5783) +* Exclude list: remove .htaccess form list of excluded files +* Sync Issues: More functional error view including filters and conflicts #5516 * Discovery: Increase the MAX_DEPTH and show deep folders as ignored * Downloads: Remove empty temporary if disk space full (#5746) * Downloads: Re-trigger folder discovery on 404 * When creating explorer favorite use more specific windows functions (#5690) +* AccountSettings: Triggering log in re-ask about previously rejected certificates #5819 * Added owncloudcmd bandwidth limit parameter (#5707) * AccountSettings: Sync with clean discovery on Ctrl-F6 (#5666) -* Dynamic size of chunks in chunked uploads for improved big file upload performance -* ShareDialog: Make "can edit" partially checked sometimes #5642 -* Require Qt5 -* Switch 3rdparty/json usage to Qt5's QJson (#5710) +* Sync: Dynamic sizing of chunks in chunked uploads for improved big file upload performance +* Sync: Introduce overall errors that are not tied to a file #5746 +* Sync: Better messaging for 507 Insufficient Storage #5537 +* Sync: Create conflicts by comparing the hash of files with identical mtime/size #5589 +* Sync: Blacklist: Don't let errors become warnings #5516 +* macOS: Finder sidebar icon #296 * Reduce memory usage +* Fix at least one memory leak * Documentation improvements * Logging improvements (with Qt logging categories), new --logdebug parameter * Harmonize source code style with clang-format * Crash fixes +* Test improvements +* Windows Overlays: Potential hang fixes +* Small UI layout fixes +* Maintenance Mode: Detect maintenance mode (#4485) +* Maintenance Mode: Add a 1 to 5 min reconnection delay #5872 +* HTTP: Send a unique X-Request-ID with each request #5853 +* HTTP: Support HTTP2 when built and running with Qt 5.9.x (Official packages still on Qt 5.6.x) +* owncloudcmd: Don't start if connection or auth fails #5692 +* Overlays: Consider also the "shared by me" as shared (#4788) +* Switch 3rdparty/json usage to Qt5's QJson (#5710) +* OpenSSL: Don't require directly, only via Qt +* Remove iconv dependency, use Qt for file system locale encoding/decoding (emoji filename support on macOS) version 2.3.3 (2017-07-XX) * Chunking NG: Don't use old chunking on new DAV endpoint (#5855) From 4aad3184462dd4d27a63f32ed843fc7794a764c9 Mon Sep 17 00:00:00 2001 From: Markus Goetz Date: Fri, 4 Aug 2017 15:42:11 +0200 Subject: [PATCH 102/107] Upload: Log the checksum / file hash --- src/libsync/propagateuploadng.cpp | 4 +--- src/libsync/propagateuploadv1.cpp | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libsync/propagateuploadng.cpp b/src/libsync/propagateuploadng.cpp index ce524a6308..7717203a14 100644 --- a/src/libsync/propagateuploadng.cpp +++ b/src/libsync/propagateuploadng.cpp @@ -284,9 +284,9 @@ void PropagateUploadFileNG::startNextChunk() headers["If"] = "<" + destination.toUtf8() + "> ([" + ifMatch + "])"; } if (!_transmissionChecksumHeader.isEmpty()) { + qCInfo(lcPropagateUpload) << destination << _transmissionChecksumHeader; headers[checkSumHeaderC] = _transmissionChecksumHeader; } - headers["OC-Total-Length"] = QByteArray::number(fileSize); auto job = new MoveJob(propagator()->account(), Utility::concatUrlPath(chunkUrl(), "/.file"), @@ -333,8 +333,6 @@ void PropagateUploadFileNG::startNextChunk() job->start(); propagator()->_activeJobList.append(this); _currentChunk++; - - // FIXME! parallel chunk? } void PropagateUploadFileNG::slotPutFinished() diff --git a/src/libsync/propagateuploadv1.cpp b/src/libsync/propagateuploadv1.cpp index b8cdd3daf8..74a5f6e52e 100644 --- a/src/libsync/propagateuploadv1.cpp +++ b/src/libsync/propagateuploadv1.cpp @@ -104,6 +104,7 @@ void PropagateUploadFileV1::startNextChunk() qCDebug(lcPropagateUpload) << _chunkCount << isFinalChunk << chunkStart << currentChunkSize; if (isFinalChunk && !_transmissionChecksumHeader.isEmpty()) { + qCInfo(lcPropagateUpload) << propagator()->_remoteFolder + path << _transmissionChecksumHeader; headers[checkSumHeaderC] = _transmissionChecksumHeader; } From 20a979173f648a134d97497165a71130861c9066 Mon Sep 17 00:00:00 2001 From: Markus Goetz Date: Thu, 3 Aug 2017 19:54:20 +0200 Subject: [PATCH 103/107] Sync Folder: Add file manager favorite also in folder wizard #455 Not only in the account wizard. --- src/gui/accountsettings.cpp | 1 + src/gui/owncloudsetupwizard.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/gui/accountsettings.cpp b/src/gui/accountsettings.cpp index 392bd1efce..a5a5e105dc 100644 --- a/src/gui/accountsettings.cpp +++ b/src/gui/accountsettings.cpp @@ -396,6 +396,7 @@ void AccountSettings::slotFolderWizardAccepted() } } FileSystem::setFolderMinimumPermissions(definition.localPath); + Utility::setupFavLink(definition.localPath); } /* take the value from the definition of already existing folders. All folders have diff --git a/src/gui/owncloudsetupwizard.cpp b/src/gui/owncloudsetupwizard.cpp index f124fa265b..dcd510f78d 100644 --- a/src/gui/owncloudsetupwizard.cpp +++ b/src/gui/owncloudsetupwizard.cpp @@ -383,6 +383,7 @@ void OwncloudSetupWizard::slotCreateLocalAndRemoteFolders(const QString &localFo bool nextStep = true; if (fi.exists()) { FileSystem::setFolderMinimumPermissions(localFolder); + Utility::setupFavLink(localFolder); // there is an existing local folder. If its non empty, it can only be synced if the // ownCloud is newly created. _ocWizard->appendToConfigurationLog( From 1c3127e43b01b872008c2f5b58fc3b30916de3e9 Mon Sep 17 00:00:00 2001 From: Markus Goetz Date: Mon, 14 Aug 2017 14:23:25 +0200 Subject: [PATCH 104/107] CMake: Print the Qt version and path #5957 #5932 --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b6e9810d4..38193d0068 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -198,7 +198,12 @@ if (${Qt5Core_VERSION_MAJOR} EQUAL "5") else() message(STATUS "If possible compile me with Qt 5.6 or higher.") endif() + if (${Qt5Core_VERSION_MINOR} EQUAL "9" OR ${Qt5Core_VERSION_MINOR} GREATER 9) + else() + message(STATUS "For HTTP2 use Qt 5.9.2 or higher.") + endif() endif() +message("Qt ${Qt5Core_VERSION} at ${Qt5Core_INCLUDE_DIRS}") if (APPLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") From 251f1d0047971f54f4167a9db326fea763a61462 Mon Sep 17 00:00:00 2001 From: Jocelyn Turcotte Date: Fri, 11 Aug 2017 17:19:02 +0200 Subject: [PATCH 105/107] Windows: Use the application icon for the sidebar By setting the icon in Desktop.ini of the root folder, this adds the icon both when browsing the folder directly and to the sidebar shortcut. To avoid overwriting any user setting that could exist in Desktop.ini, only do this if the file doesn't exist. Editing .ini files on Windows isn't trivial and isn't worth it given that this file won't exist most of the time. Fixes #2446 --- src/libsync/utility_win.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/libsync/utility_win.cpp b/src/libsync/utility_win.cpp index f46cde4d54..3762808b7b 100644 --- a/src/libsync/utility_win.cpp +++ b/src/libsync/utility_win.cpp @@ -28,8 +28,20 @@ namespace OCC { static void setupFavLink_private(const QString &folder) { - // Windows Explorer: Place under "Favorites" (Links) + // First create a Desktop.ini so that the folder and favorite link show our application's icon. + QFile desktopIni(folder + QLatin1String("/Desktop.ini")); + if (desktopIni.exists()) { + qCWarning(lcUtility) << desktopIni.fileName() << "already exists, not overwriting it to set the folder icon."; + } else { + qCInfo(lcUtility) << "Creating" << desktopIni.fileName() << "to set a folder icon in Explorer."; + desktopIni.open(QFile::WriteOnly); + desktopIni.write("[.ShellClassInfo]\r\nIconResource="); + desktopIni.write(QDir::toNativeSeparators(qApp->applicationFilePath()).toUtf8()); + desktopIni.write(",0\r\n"); + desktopIni.close(); + } + // Windows Explorer: Place under "Favorites" (Links) QString linkName; QDir folderDir(QDir::fromNativeSeparators(folder)); @@ -41,7 +53,7 @@ static void setupFavLink_private(const QString &folder) linkName = QDir(links).filePath(folderDir.dirName() + QLatin1String(".lnk")); CoTaskMemFree(path); } - qCDebug(lcUtility) << " creating link from " << linkName << " to " << folder; + qCInfo(lcUtility) << "Creating favorite link from" << folder << "to" << linkName; if (!QFile::link(folder, linkName)) qCWarning(lcUtility) << "linking" << folder << "to" << linkName << "failed!"; } From a480a318fd4bd2c6bbfa980ccb901e976a83e4aa Mon Sep 17 00:00:00 2001 From: Jocelyn Turcotte Date: Mon, 14 Aug 2017 11:42:30 +0200 Subject: [PATCH 106/107] Hardcode Desktop.ini in the exclude list This prevents it from being removed from the exclude list, which would be an issue since the client itself creates this file in a way that wouldn't match on machines with different installation paths. --- csync/src/csync_exclude.c | 7 +++++++ sync-exclude.lst | 1 - 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/csync/src/csync_exclude.c b/csync/src/csync_exclude.c index 873a12d885..fea6625044 100644 --- a/csync/src/csync_exclude.c +++ b/csync/src/csync_exclude.c @@ -293,6 +293,13 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch } #endif + /* We create a desktop.ini on Windows for the sidebar icon, make sure we don't sync them. */ + rc = csync_fnmatch("Desktop.ini", bname, 0); + if (rc == 0) { + match = CSYNC_FILE_SILENTLY_EXCLUDED; + goto out; + } + rc = csync_fnmatch(".owncloudsync.log*", bname, 0); if (rc == 0) { match = CSYNC_FILE_SILENTLY_EXCLUDED; diff --git a/sync-exclude.lst b/sync-exclude.lst index 9d58aa9fbd..bdf6c34415 100644 --- a/sync-exclude.lst +++ b/sync-exclude.lst @@ -8,7 +8,6 @@ ].ds_store ._* ]Thumbs.db -desktop.ini System Volume Information .*.sw? From 6e47d9a2e5ddb40e59d69e7bafd711c72fa1c2ff Mon Sep 17 00:00:00 2001 From: Martin Date: Sun, 20 Aug 2017 18:17:58 +0200 Subject: [PATCH 107/107] Documentation improvement for #5969 --- doc/navigating.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/navigating.rst b/doc/navigating.rst index c136b07b44..b4cf521835 100644 --- a/doc/navigating.rst +++ b/doc/navigating.rst @@ -190,7 +190,9 @@ The Activity window contains the log of your recent activities, organized over three tabs: **Server Activities**, which includes new shares and files downloaded and deleted, **Sync Protocol**, which displays local activities such as which local folders your files went into, and **Not Synced** shows errors -such as files not synced. +such as files not synced. Double clicking an entry pointing to an existing +file in **Server Activities** or **Sync Protocol** will open the folder containing +the file and highlight it. .. figure:: images/client-8.png :alt: Activity windows logs all server and client activities.