diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.5) -set(PIM_VERSION "5.13.40") +set(PIM_VERSION "5.14.40") set(KDEPIM_RUNTIME_VERSION_NUMBER ${PIM_VERSION}) project(kdepim-runtime VERSION ${KDEPIM_RUNTIME_VERSION_NUMBER}) @@ -37,7 +37,7 @@ # 3.2 set(KDEPIM_DEV_VERSION alpha) -set(RELEASE_SERVICE_VERSION "20.03.70") +set(RELEASE_SERVICE_VERSION "20.07.40") # add an extra space if(DEFINED KDEPIM_DEV_VERSION) @@ -48,7 +48,7 @@ configure_file(kdepim-runtime-version.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/kdepim-runtime-version.h @ONLY) -set(KF5_MIN_VERSION "5.67.0") +set(KF5_MIN_VERSION "5.68.0") find_package(ECM ${KF5_MIN_VERSION} CONFIG REQUIRED) set(CMAKE_MODULE_PATH ${kdepim-runtime_SOURCE_DIR}/cmake/ ${ECM_MODULE_PATH}) @@ -68,40 +68,35 @@ set(KDEPIMRUNTIME_LIB_VERSION "${KDEPIM_RUNTIME_VERSION_NUMBER}") set(KDEPIMRUNTIME_LIB_SOVERSION "5") -set(AKONADI_VERSION "5.13.41") - -set(IDENTITYMANAGEMENT_LIB_VERSION "5.13.40") -set(KMAILTRANSPORT_LIB_VERSION "5.13.40") -set(CALENDARUTILS_LIB_VERSION "5.13.40") -set(KDAV_LIB_VERSION "5.13.40") -set(KIMAP_LIB_VERSION "5.13.40") -set(KMBOX_LIB_VERSION "5.13.40") -set(AKONADICALENDAR_LIB_VERSION "5.13.40") -set(KONTACTINTERFACE_LIB_VERSION "5.13.40") -set(AKONADIKALARM_LIB_VERSION "5.13.40") -set(KMIME_LIB_VERSION "5.13.40") -set(XMLRPCCLIENT_LIB_VERSION "5.13.40") -set(AKONADIMIME_LIB_VERSION "5.13.40") -set(AKONADICONTACT_LIB_VERSION "5.13.40") -set(AKONADINOTE_LIB_VERSION "5.13.40") -set(PIMCOMMON_LIB_VERSION "5.13.40") -set(KGAPI_LIB_VERSION "5.13.40") +set(AKONADI_VERSION "5.14.40") + +set(IDENTITYMANAGEMENT_LIB_VERSION "5.14.40") +set(KMAILTRANSPORT_LIB_VERSION "5.14.40") +set(CALENDARUTILS_LIB_VERSION "5.14.40") +set(KDAV_LIB_VERSION "5.14.40") +set(KIMAP_LIB_VERSION "5.14.40") +set(KMBOX_LIB_VERSION "5.14.40") +set(AKONADICALENDAR_LIB_VERSION "5.14.40") +set(KONTACTINTERFACE_LIB_VERSION "5.14.40") +set(AKONADIKALARM_LIB_VERSION "5.14.40") +set(KMIME_LIB_VERSION "5.14.40") +set(XMLRPCCLIENT_LIB_VERSION "5.14.40") +set(AKONADIMIME_LIB_VERSION "5.14.40") +set(AKONADICONTACT_LIB_VERSION "5.14.40") +set(AKONADINOTE_LIB_VERSION "5.14.40") +set(PIMCOMMON_LIB_VERSION "5.14.40") +set(KGAPI_LIB_VERSION "5.14.40") set( SharedMimeInfo_MINIMUM_VERSION "1.3" ) find_package(SharedMimeInfo ${SharedMimeInfo_MINIMUM_VERSION} REQUIRED) find_package(Sasl2) set_package_properties(Sasl2 PROPERTIES TYPE REQUIRED) -find_package(Qca-qt5) +find_package(Qca-qt5 2.3.0 CONFIG REQUIRED) option(KDEPIM_RUN_AKONADI_TEST "Enable autotest based on Akonadi." TRUE) # QT5 package find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED TextToSpeech Network Widgets Test XmlPatterns DBus WebEngineWidgets NetworkAuth) -if (NOT Qca-qt5_FOUND) - message(STATUS "QCA not found, public key authentication will not be supported") -else() - add_definitions(-DHAVE_QCA) -endif() # KF5 package @@ -140,7 +135,7 @@ option(NO_REGENERATE_MIME "Don't regenerate mime file (developer-only option)" FALSE ) if (EXISTS "${CMAKE_SOURCE_DIR}/.git") - add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x060000) + #add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x050e00) add_definitions(-DKF_DISABLE_DEPRECATED_BEFORE_AND_AT=0x600000) endif() find_package(Xsltproc) @@ -159,15 +154,11 @@ if (NOT NO_REGENERATE_MIME) update_xdg_mimetypes(${KDE_INSTALL_MIMEDIR}) endif() -if (ECM_VERSION VERSION_LESS "5.68.0") - install(FILES kdepim-runtime.renamecategories kdepim-runtime.categories DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}) -else () - ecm_qt_install_logging_categories( +ecm_qt_install_logging_categories( EXPORT KDEPIMRUNTIME FILE kdepim-runtime.categories DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR} ) -endif() feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES diff --git a/agents/maildispatcher/CMakeLists.txt b/agents/maildispatcher/CMakeLists.txt --- a/agents/maildispatcher/CMakeLists.txt +++ b/agents/maildispatcher/CMakeLists.txt @@ -9,15 +9,11 @@ storeresultjob.cpp ) -if (ECM_VERSION VERSION_LESS "5.68.0") - ecm_qt_declare_logging_category(maildispatcheragent_SRCS HEADER maildispatcher_debug.h IDENTIFIER MAILDISPATCHER_LOG CATEGORY_NAME org.kde.pim.maildispatcher) -else() - ecm_qt_declare_logging_category(maildispatcheragent_SRCS HEADER maildispatcher_debug.h IDENTIFIER MAILDISPATCHER_LOG CATEGORY_NAME org.kde.pim.maildispatcher +ecm_qt_declare_logging_category(maildispatcheragent_SRCS HEADER maildispatcher_debug.h IDENTIFIER MAILDISPATCHER_LOG CATEGORY_NAME org.kde.pim.maildispatcher DESCRIPTION "maildispacher agent (kdepim-runtime)" OLD_CATEGORY_NAMES log_maildispatcher EXPORT KDEPIMRUNTIME ) -endif() kconfig_add_kcfg_files(maildispatcheragent_SRCS settings.kcfgc) kcfg_generate_dbus_interface(${CMAKE_CURRENT_SOURCE_DIR}/maildispatcheragent.kcfg org.kde.Akonadi.MailDispatcher.Settings) diff --git a/agents/newmailnotifier/CMakeLists.txt b/agents/newmailnotifier/CMakeLists.txt --- a/agents/newmailnotifier/CMakeLists.txt +++ b/agents/newmailnotifier/CMakeLists.txt @@ -9,15 +9,11 @@ newmailnotifieragentsettings.kcfgc ) -if (ECM_VERSION VERSION_LESS "5.68.0") - ecm_qt_declare_logging_category(newmailnotifier_common_SRCS HEADER newmailnotifier_debug.h IDENTIFIER NEWMAILNOTIFIER_LOG CATEGORY_NAME org.kde.pim.newmailnotifier) -else() - ecm_qt_declare_logging_category(newmailnotifier_common_SRCS HEADER newmailnotifier_debug.h IDENTIFIER NEWMAILNOTIFIER_LOG CATEGORY_NAME org.kde.pim.newmailnotifier +ecm_qt_declare_logging_category(newmailnotifier_common_SRCS HEADER newmailnotifier_debug.h IDENTIFIER NEWMAILNOTIFIER_LOG CATEGORY_NAME org.kde.pim.newmailnotifier DESCRIPTION "mailnotifier agent (kdepim-runtime)" OLD_CATEGORY_NAMES log_newmailnotifier EXPORT KDEPIMRUNTIME ) -endif() set(newmailnotifieragent_SRCS diff --git a/agents/newmailnotifier/tests/CMakeLists.txt b/agents/newmailnotifier/tests/CMakeLists.txt --- a/agents/newmailnotifier/tests/CMakeLists.txt +++ b/agents/newmailnotifier/tests/CMakeLists.txt @@ -1,13 +1,5 @@ set(newmailnotifieragent_common_SRCS) -if (ECM_VERSION VERSION_LESS "5.68.0") ecm_qt_declare_logging_category(newmailnotifieragent_common_SRCS HEADER newmailnotifier_debug.h IDENTIFIER NEWMAILNOTIFIER_LOG CATEGORY_NAME org.kde.pim.newmailnotifier) -else() - ecm_qt_declare_logging_category(newmailnotifieragent_common_SRCS HEADER newmailnotifier_debug.h IDENTIFIER NEWMAILNOTIFIER_LOG CATEGORY_NAME org.kde.pim.newmailnotifier - DESCRIPTION "mailnotifier agent (kdepim-runtime)" - OLD_CATEGORY_NAMES log_newmailnotifier - EXPORT KDEPIMRUNTIME - ) -endif() diff --git a/kdepim-runtime.categories b/kdepim-runtime.categories deleted file mode 100644 --- a/kdepim-runtime.categories +++ /dev/null @@ -1,33 +0,0 @@ -org.kde.pim.maildispatcher maildispacher agent (kdepim-runtime) IDENTIFIER [MAILDISPATCHER_LOG] -org.kde.pim.newmailnotifier mailnotifier agent (kdepim-runtime) IDENTIFIER [NEWMAILNOTIFIER_LOG] -org.kde.pim.akonadislave kioslave (akonadi) IDENTIFIER [AKONADISLAVE_LOG] -org.kde.pim.maildirresource maildir resource (kdepim-runtime) IDENTIFIER [MAILDIRRESOURCE_LOG] -org.kde.pim.libmaildir libmaildir (kdepim-runtime) IDENTIFIER [LIBMAILDIR_LOG] -org.kde.pim.imapresource imap resource (kdepim-runtime) IDENTIFIER [IMAPRESOURCE_LOG] -org.kde.pim.mboxresource mbox resource (kdepim-runtime) IDENTIFIER [MBOXRESOURCE_LOG] -org.kde.pim.birthdays birthdays resource (kdepim-runtime) IDENTIFIER [BIRTHDAYS_LOG] -org.kde.pim.davresource dav resource (kdepim-runtime) IDENTIFIER [DAVRESOURCE_LOG] -org.kde.pim.kolabresource kolab resource (kdepim-runtime) IDENTIFIER [KOLABRESOURCE_LOG] -org.kde.pim.mixedmaildirresource mixedmaildir resource (kdepim-runtime) IDENTIFIER [MIXEDMAILDIRRESOURCE_LOG] -org.kde.pim.mixedmaildir mixed maildir resource (kdepim-runtime) IDENTIFIER [MIXEDMAILDIR_LOG] -org.kde.pim.kalarmresource kalarm file resource (kdepim-runtime) IDENTIFIER [KALARMRESOURCE_LOG] -org.kde.pim.kalarmdirresource kalarm directory resource (kdepim-runtime) IDENTIFIER [KALARMDIRRESOURCE_LOG] -org.kde.pim.pop3resource pop3 resource (kdepim-runtime) IDENTIFIER [POP3RESOURCE_LOG] -org.kde.pim.resources_contacts contacts resource (kdepim-runtime) IDENTIFIER [CONTACTSRESOURCES_LOG] -org.kde.pim.pop3 kioslave (kdepim-runtime) IDENTIFIER [POP3_LOG] -org.kde.pim.tomboynotesresource tomboynote resource (kdepim-runtime) IDENTIFIER [TOMBOYNOTESRESOURCE_LOG] -org.kde.pim.fbresource facebook resource (kdepim-runtime) IDENTIFIER [FBRESOURCE_LOG] -org.kde.pim.pimkolab pimkolab lib (kdepim-runtime) IDENTIFIER [PIMKOLAB_LOG] -org.kde.pim.ews exchange ews resource (kdepim-runtime) IDENTIFIER [EWSRES_LOG] -org.kde.pim.ews.agentif exchange ews agent (kdepim-runtime) IDENTIFIER [EWSRES_AGENTIF_LOG] -org.kde.pim.ews.mta ews mta (kdepim-runtime) IDENTIFIER [EWSRES_MTA_LOG] -org.kde.pim.ews.client ews client (kdepim-runtime) IDENTIFIER [EWSCLI_LOG] -org.kde.pim.ews.client.proto ews client proto (kdepim-runtime) IDENTIFIER [EWSCLI_PROTO_LOG] -org.kde.pim.ews.client.request ews client request (kdepim-runtime) IDENTIFIER [EWSCLI_REQUEST_LOG] -org.kde.pim.ews.client.failedrequest ews client failed request (kdepim-runtime) IDENTIFIER [EWSCLI_FAILEDREQUEST_LOG] -org.kde.pim.filestore resource filestore lib (kdepim-runtime) IDENTIFIER [AKONADIFILESTORE_LOG] -org.kde.pim.imapresource.trace resource imap trace (kdepim-runtime) IDENTIFIER [IMAPRESOURCE_TRACE] -org.kde.pim.kolabresource.trace resource kolab trace (kdepim-runtime) IDENTIFIER [KOLABRESOURCE_TRACE] -org.kde.pim.google.calendar resource google calendar (kdepim-runtime) IDENTIFIER [GOOGLE_CALENDAR_LOG] -org.kde.pim.vcarddirresource vcarddir resource (kdepim-runtime) IDENTIFIER [VCARDDIRRESOURCE_LOG] -org.kde.pim.migration migration (kdepim-runtime) IDENTIFIER [MIGRATION_LOG] diff --git a/kdepim-runtime.renamecategories b/kdepim-runtime.renamecategories deleted file mode 100644 --- a/kdepim-runtime.renamecategories +++ /dev/null @@ -1,21 +0,0 @@ -log_maildispatcher org.kde.pim.maildispatcher -log_newmailnotifier org.kde.pim.newmailnotifier -log_akonadislave org.kde.pim.akonadislave -log_maildirresource org.kde.pim.maildirresource -log_libmaildir org.kde.pim.libmaildir -log_imapresource org.kde.pim.imapresource -log_mboxresource org.kde.pim.mboxresource -log_birthdays org.kde.pim.birthdays -log_davresource org.kde.pim.davresource -log_kolabresource org.kde.pim.kolabresource -log_mixedmaildirresource org.kde.pim.mixedmaildirresource -log_mixedmaildir org.kde.pim.mixedmaildir -log_kalarmresource org.kde.pim.kalarmresource -log_kalarmdirresource org.kde.pim.kalarmdirresource -log_pop3resource org.kde.pim.pop3resource -log_akonadi_serializer_kalarm org.kde.pim.akonadi_serializer_kalarm -log_akonadi_serializer_mail org.kde.pim.akonadi_serializer_mail -log_resources_contacts org.kde.pim.resources_contacts -log_pop3 org.kde.pim.pop3 -log_tomboynotesresource org.kde.pim.tomboynotesresource -log_invitationagent org.kde.pim.invitationagent diff --git a/kioslave/akonadi/CMakeLists.txt b/kioslave/akonadi/CMakeLists.txt --- a/kioslave/akonadi/CMakeLists.txt +++ b/kioslave/akonadi/CMakeLists.txt @@ -1,13 +1,9 @@ set(kio_akonadi_SRCS akonadislave.cpp) -if (ECM_VERSION VERSION_LESS "5.68.0") - ecm_qt_declare_logging_category(kio_akonadi_SRCS HEADER akonadislave_debug.h IDENTIFIER AKONADISLAVE_LOG CATEGORY_NAME org.kde.pim.akonadislave) -else() - ecm_qt_declare_logging_category(kio_akonadi_SRCS HEADER akonadislave_debug.h IDENTIFIER AKONADISLAVE_LOG CATEGORY_NAME org.kde.pim.akonadislave - DESCRIPTION "kioslave (akonadi)" - OLD_CATEGORY_NAMES log_akonadislave - EXPORT KDEPIMRUNTIME +ecm_qt_declare_logging_category(kio_akonadi_SRCS HEADER akonadislave_debug.h IDENTIFIER AKONADISLAVE_LOG CATEGORY_NAME org.kde.pim.akonadislave + DESCRIPTION "kioslave (akonadi)" + OLD_CATEGORY_NAMES log_akonadislave + EXPORT KDEPIMRUNTIME ) -endif() add_library(kio_akonadi MODULE ${kio_akonadi_SRCS}) diff --git a/kioslave/pop3/CMakeLists.txt b/kioslave/pop3/CMakeLists.txt --- a/kioslave/pop3/CMakeLists.txt +++ b/kioslave/pop3/CMakeLists.txt @@ -2,15 +2,11 @@ set(kio_pop3_PART_SRCS pop3.cpp pop3_debug.cpp ) -if (ECM_VERSION VERSION_LESS "5.68.0") - ecm_qt_declare_logging_category(kio_pop3_PART_SRCS HEADER pop3_debug.h IDENTIFIER POP3_LOG CATEGORY_NAME org.kde.pim.pop3) -else() - ecm_qt_declare_logging_category(kio_pop3_PART_SRCS HEADER pop3_debug.h IDENTIFIER POP3_LOG CATEGORY_NAME org.kde.pim.pop3 - DESCRIPTION "kioslave (kdepim-runtime)" - OLD_CATEGORY_NAMES log_pop3resource - EXPORT KDEPIMRUNTIME +ecm_qt_declare_logging_category(kio_pop3_PART_SRCS HEADER pop3_debug.h IDENTIFIER POP3_LOG CATEGORY_NAME org.kde.pim.pop3 + DESCRIPTION "kioslave (kdepim-runtime)" + OLD_CATEGORY_NAMES log_pop3resource + EXPORT KDEPIMRUNTIME ) -endif() diff --git a/migration/gid/CMakeLists.txt b/migration/gid/CMakeLists.txt --- a/migration/gid/CMakeLists.txt +++ b/migration/gid/CMakeLists.txt @@ -1,34 +1,30 @@ set(gid_SRCS - gidmigrator.cpp - gidmigrationjob.cpp - ${MIGRATION_AKONADI_SHARED_SOURCES} -) + gidmigrator.cpp + gidmigrationjob.cpp + ${MIGRATION_AKONADI_SHARED_SOURCES} + ) -if (ECM_VERSION VERSION_LESS "5.68.0") - ecm_qt_declare_logging_category(gid_SRCS HEADER migration_debug.h IDENTIFIER MIGRATION_LOG CATEGORY_NAME org.kde.pim.migration) -else() - ecm_qt_declare_logging_category(gid_SRCS HEADER migration_debug.h IDENTIFIER MIGRATION_LOG CATEGORY_NAME org.kde.pim.migration - DESCRIPTION "migration (kdepim-runtime)" - EXPORT KDEPIMRUNTIME +ecm_qt_declare_logging_category(gid_SRCS HEADER migration_debug.h IDENTIFIER MIGRATION_LOG CATEGORY_NAME org.kde.pim.migration + DESCRIPTION "migration (kdepim-runtime)" + EXPORT KDEPIMRUNTIME ) -endif() add_library(gidmigration STATIC ${gid_SRCS}) target_link_libraries(gidmigration - KF5::AkonadiCore - KF5::Mime - KF5::ConfigCore - KF5::I18n - Qt5::Core - Qt5::Widgets - KF5::WidgetsAddons -) + KF5::AkonadiCore + KF5::Mime + KF5::ConfigCore + KF5::I18n + Qt5::Core + Qt5::Widgets + KF5::WidgetsAddons + ) add_executable(gidmigrator main.cpp) target_link_libraries(gidmigrator - gidmigration - KF5::AkonadiWidgets - KF5::Mime -) + gidmigration + KF5::AkonadiWidgets + KF5::Mime + ) install(TARGETS gidmigrator ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt --- a/resources/CMakeLists.txt +++ b/resources/CMakeLists.txt @@ -53,6 +53,7 @@ add_subdirectory( pop3 ) add_subdirectory( google ) +add_subdirectory( google-groupware ) add_subdirectory( shared ) add_subdirectory( birthdays ) diff --git a/resources/birthdays/CMakeLists.txt b/resources/birthdays/CMakeLists.txt --- a/resources/birthdays/CMakeLists.txt +++ b/resources/birthdays/CMakeLists.txt @@ -4,48 +4,44 @@ kconfig_add_kcfg_files(birthdayresource_common_SRCS settings.kcfgc - ) + ) set( birthdayresource_srcs - birthdaysresource.cpp - ${birthdayresource_common_SRCS} -) + birthdaysresource.cpp + ${birthdayresource_common_SRCS} + ) kcfg_generate_dbus_interface(${CMAKE_CURRENT_SOURCE_DIR}/birthdaysresource.kcfg org.kde.Akonadi.Birthdays.Settings) qt5_add_dbus_adaptor(birthdayresource_srcs - ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.Birthdays.Settings.xml settings.h Settings -) - -if (ECM_VERSION VERSION_LESS "5.68.0") - ecm_qt_declare_logging_category(birthdayresource_srcs HEADER birthdays_debug.h IDENTIFIER BIRTHDAYS_LOG CATEGORY_NAME org.kde.pim.birthdays) -else() - ecm_qt_declare_logging_category(birthdayresource_srcs HEADER birthdays_debug.h IDENTIFIER BIRTHDAYS_LOG CATEGORY_NAME org.kde.pim.birthdays - DESCRIPTION "birthdays resource (kdepim-runtime)" - OLD_CATEGORY_NAMES log_birthdays - EXPORT KDEPIMRUNTIME + ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.Birthdays.Settings.xml settings.h Settings + ) + +ecm_qt_declare_logging_category(birthdayresource_srcs HEADER birthdays_debug.h IDENTIFIER BIRTHDAYS_LOG CATEGORY_NAME org.kde.pim.birthdays + DESCRIPTION "birthdays resource (kdepim-runtime)" + OLD_CATEGORY_NAMES log_birthdays + EXPORT KDEPIMRUNTIME ) -endif() add_executable(akonadi_birthdays_resource ${birthdayresource_srcs}) if( APPLE ) - set_target_properties(akonadi_birthdays_resource PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/../Info.plist.template) - set_target_properties(akonadi_birthdays_resource PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.Akonadi.Birthdays") - set_target_properties(akonadi_birthdays_resource PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "KDE Akonadi Birthdays Resource") + set_target_properties(akonadi_birthdays_resource PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/../Info.plist.template) + set_target_properties(akonadi_birthdays_resource PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.Akonadi.Birthdays") + set_target_properties(akonadi_birthdays_resource PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "KDE Akonadi Birthdays Resource") endif () target_link_libraries(akonadi_birthdays_resource - KF5::AkonadiCore - KF5::CalendarCore - KF5::AkonadiAgentBase - KF5::Contacts - KF5::AkonadiWidgets - KF5::I18n -) + KF5::AkonadiCore + KF5::CalendarCore + KF5::AkonadiAgentBase + KF5::Contacts + KF5::AkonadiWidgets + KF5::I18n + ) install( TARGETS akonadi_birthdays_resource ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} ) install( FILES birthdaysresource.desktop DESTINATION "${KDE_INSTALL_DATAROOTDIR}/akonadi/agents" ) diff --git a/resources/birthdays/birthdaysresource.desktop b/resources/birthdays/birthdaysresource.desktop --- a/resources/birthdays/birthdaysresource.desktop +++ b/resources/birthdays/birthdaysresource.desktop @@ -80,7 +80,7 @@ Comment[pt_BR]=Fornece acesso às datas de aniversário e de casamento de contatos do seu livro de endereços como eventos de calendário Comment[ru]=Доступ к информации о днях рождения и годовщинах свадеб из адресной книги KDE как к календарю с событиями Comment[sk]=Poskytuje prístup k narodeninám a výročiam kontaktov vo vašom adresári ako udalosti kalendára -Comment[sl]=Omogoča dostop do rojstnih dni in obletnic stikov, shranjenih v vašem imeniku. Na voljo so kot koledarski dogodki. +Comment[sl]=Omogoča dostop do rojstnih dni in obletnic stikov, shranjenih v vašem imeniku. Na voljo so kot koledarski dogodki Comment[sr]=Омогућава приступ датумима рођендана и годишњица контаката у вашем адресару као календарским догађајима Comment[sr@ijekavian]=Омогућава приступ датумима рођендана и годишњица контаката у вашем адресару као календарским догађајима Comment[sr@ijekavianlatin]=Omogućava pristup datumima rođendana i godišnjica kontakata u vašem adresaru kao kalendarskim događajima diff --git a/resources/contacts/CMakeLists.txt b/resources/contacts/CMakeLists.txt --- a/resources/contacts/CMakeLists.txt +++ b/resources/contacts/CMakeLists.txt @@ -9,42 +9,38 @@ kconfig_add_kcfg_files(contactsresource_common_SRCS settings.kcfgc - ) + ) ########### next target ############### set( contactsresource_SRCS - contactsresource.cpp - ${contactsresource_common_SRCS} -) + contactsresource.cpp + ${contactsresource_common_SRCS} + ) kcfg_generate_dbus_interface(${CMAKE_CURRENT_SOURCE_DIR}/contactsresource.kcfg org.kde.Akonadi.Contacts.Settings) qt5_add_dbus_adaptor(contactsresource_SRCS - ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.Contacts.Settings.xml settings.h ContactsResourceSettings contactsresourcesettingsadaptor ContactsResourceSettingsAdaptor -) - -if (ECM_VERSION VERSION_LESS "5.68.0") - ecm_qt_declare_logging_category(contactsresource_SRCS HEADER contacts_resources_debug.h IDENTIFIER CONTACTSRESOURCES_LOG CATEGORY_NAME org.kde.pim.resources_contacts) -else() - ecm_qt_declare_logging_category(contactsresource_SRCS HEADER contacts_resources_debug.h IDENTIFIER CONTACTSRESOURCES_LOG CATEGORY_NAME org.kde.pim.resources_contacts - DESCRIPTION "contacts resource (kdepim-runtime)" - OLD_CATEGORY_NAMES log_resources_contacts - EXPORT KDEPIMRUNTIME + ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.Contacts.Settings.xml settings.h ContactsResourceSettings contactsresourcesettingsadaptor ContactsResourceSettingsAdaptor + ) + +ecm_qt_declare_logging_category(contactsresource_SRCS HEADER contacts_resources_debug.h IDENTIFIER CONTACTSRESOURCES_LOG CATEGORY_NAME org.kde.pim.resources_contacts + DESCRIPTION "contacts resource (kdepim-runtime)" + OLD_CATEGORY_NAMES log_resources_contacts + EXPORT KDEPIMRUNTIME ) -endif() install( FILES contactsresource.desktop DESTINATION "${KDE_INSTALL_DATAROOTDIR}/akonadi/agents" ) add_executable(akonadi_contacts_resource ${contactsresource_SRCS}) target_link_libraries(akonadi_contacts_resource - KF5::AkonadiCore - KF5::AkonadiAgentBase - KF5::Contacts - KF5::I18n - KF5::KIOWidgets - KF5::ConfigWidgets -) + KF5::AkonadiCore + KF5::AkonadiAgentBase + KF5::Contacts + KF5::I18n + KF5::KIOWidgets + KF5::ConfigWidgets + ) install(TARGETS akonadi_contacts_resource ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/resources/dav/resource/CMakeLists.txt b/resources/dav/resource/CMakeLists.txt --- a/resources/dav/resource/CMakeLists.txt +++ b/resources/dav/resource/CMakeLists.txt @@ -35,17 +35,12 @@ davgroupwareresource.cpp ) -if (ECM_VERSION VERSION_LESS "5.68.0") - ecm_qt_declare_logging_category(contactsresource_SRCS HEADER contacts_resources_debug.h IDENTIFIER CONTACTSRESOURCES_LOG CATEGORY_NAME org.kde.pim.resources_contacts) -else() - ecm_qt_declare_logging_category(contactsresource_SRCS HEADER contacts_resources_debug.h IDENTIFIER CONTACTSRESOURCES_LOG CATEGORY_NAME org.kde.pim.resources_contacts - DESCRIPTION "contacts resource (kdepim-runtime)" - OLD_CATEGORY_NAMES log_resources_contacts - EXPORT KDEPIMRUNTIME +ecm_qt_declare_logging_category(davgroupwareresource_SRCS HEADER davresource_debug.h IDENTIFIER DAVRESOURCE_LOG CATEGORY_NAME org.kde.pim.davresource + DESCRIPTION "dav resource (kdepim-runtime)" + OLD_CATEGORY_NAMES log_davresource + EXPORT KDEPIMRUNTIME ) -endif() -ecm_qt_declare_logging_category(davgroupwareresource_SRCS HEADER davresource_debug.h IDENTIFIER DAVRESOURCE_LOG CATEGORY_NAME org.kde.pim.davresource) install( FILES davgroupwareresource.desktop DESTINATION "${KDE_INSTALL_DATAROOTDIR}/akonadi/agents" ) install( FILES davgroupwareprovider.desktop DESTINATION ${KDE_INSTALL_KSERVICETYPES5DIR} ) diff --git a/resources/dav/resource/davgroupwareresource.desktop b/resources/dav/resource/davgroupwareresource.desktop --- a/resources/dav/resource/davgroupwareresource.desktop +++ b/resources/dav/resource/davgroupwareresource.desktop @@ -63,6 +63,7 @@ Comment[pt_BR]=Recurso para gerenciar calendários e livros de endereços por DAV (CalDAV, GroupDAV) Comment[ru]=Источники данных для управления календарями и адресными книгами DAV (CalDAV, GroupDAV) Comment[sk]=Zdroj na správu DAV kalendárov a adresárov kontaktov (CalDAV, GroupDAV) +Comment[sl]=Vir za upravljanje koledarjev in imenikov DAV (CalDAV, GroupDAV) Comment[sv]=Resurs för att hantera DAV-kalendrar och adressböcker (CalDAV, GroupDAV) Comment[uk]=Ресурс для керування календарями і адресними книгами DAV (CalDAV, GroupDAV) Comment[x-test]=xxResource to manage DAV calendars and address books (CalDAV, GroupDAV)xx diff --git a/resources/dav/services/nextcloud.desktop b/resources/dav/services/nextcloud.desktop --- a/resources/dav/services/nextcloud.desktop +++ b/resources/dav/services/nextcloud.desktop @@ -19,6 +19,7 @@ Name[pt_BR]=Nextcloud Name[ru]=Nextcloud Name[sk]=Nextcloud +Name[sl]=Nextcloud Name[sv]=Nextcloud Name[uk]=Nextcloud Name[x-test]=xxNextcloudxx diff --git a/resources/ews/CMakeLists.txt b/resources/ews/CMakeLists.txt --- a/resources/ews/CMakeLists.txt +++ b/resources/ews/CMakeLists.txt @@ -75,31 +75,39 @@ ewssubscriptionwidget.cpp) ecm_qt_declare_logging_category(ewsresource_SRCS - HEADER ewsres_debug.h - IDENTIFIER EWSRES_LOG - CATEGORY_NAME org.kde.pim.ews) + HEADER ewsres_debug.h + IDENTIFIER EWSRES_LOG + CATEGORY_NAME org.kde.pim.ews + DESCRIPTION "ews resource (kdepim-runtime)" + EXPORT KDEPIMRUNTIME + ) + + ecm_qt_declare_logging_category(ewsresource_SRCS - HEADER ewsres_agentif_debug.h - IDENTIFIER EWSRES_AGENTIF_LOG - CATEGORY_NAME org.kde.pim.ews.agentif) + HEADER ewsres_agentif_debug.h + IDENTIFIER EWSRES_AGENTIF_LOG + CATEGORY_NAME org.kde.pim.ews.agentif + DESCRIPTION "exchange ews agent (kdepim-runtime)" + EXPORT KDEPIMRUNTIME + ) ki18n_wrap_ui(ewsresource_SRCS ewsconfigdialog.ui) kconfig_add_kcfg_files(ewsresource_SRCS ewssettingsbase.kcfgc) kcfg_generate_dbus_interface(${CMAKE_CURRENT_SOURCE_DIR}/ewsresource.kcfg org.kde.Akonadi.Ews.Settings) qt5_add_dbus_adaptor(ewsresource_SRCS - ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.Ews.Settings.xml ewssettings.h EwsSettings ewssettingsadaptor EwsSettingsAdaptor -) + ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.Ews.Settings.xml ewssettings.h EwsSettings ewssettingsadaptor EwsSettingsAdaptor + ) qt5_generate_dbus_interface( ${CMAKE_CURRENT_SOURCE_DIR}/ewsresource.h org.kde.Akonadi.Ews.Resource.xml OPTIONS -a ) qt5_add_dbus_adaptor(ewsresource_SRCS - ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.Ews.Resource.xml ewsresource.h EwsResource ewsresourceadaptor EwsResourceAdaptor -) + ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.Ews.Resource.xml ewsresource.h EwsResource ewsresourceadaptor EwsResourceAdaptor + ) qt5_generate_dbus_interface( ${CMAKE_CURRENT_SOURCE_DIR}/ewssettings.h org.kde.Akonadi.Ews.Wallet.xml OPTIONS -a ) qt5_add_dbus_adaptor(ewsresource_SRCS - ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.Ews.Wallet.xml ewssettings.h EwsSettings ewswalletadaptor EwsWalletAdaptor -) + ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.Ews.Wallet.xml ewssettings.h EwsSettings ewswalletadaptor EwsWalletAdaptor + ) @@ -121,69 +129,72 @@ ewsclient) if (HAVE_SEPARATE_MTA_RESOURCE) - set(ewsmtaresource_SRCS - ewsmtaconfigdialog.cpp - ewsmtaresource.cpp) - - ecm_qt_declare_logging_category(ewsmtaresource_SRCS - HEADER ewsres_mta_debug.h - IDENTIFIER EWSRES_MTA_LOG - CATEGORY_NAME org.kde.pim.ews.mta) - - ki18n_wrap_ui(ewsmtaresource_SRCS ewsmtaconfigdialog.ui) - - kconfig_add_kcfg_files(ewsmtaresource_SRCS ewsmtasettings.kcfgc) - kcfg_generate_dbus_interface(${CMAKE_CURRENT_SOURCE_DIR}/ewsmtaresource.kcfg org.kde.Akonadi.EwsMta.Settings) - qt5_add_dbus_adaptor(ewsmtaresource_SRCS - ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.EwsMta.Settings.xml ewsmtasettings.h EwsMtaSettings ewsmtasettingsadaptor - ) - qt5_add_dbus_interface(ewsmtaresource_SRCS ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.Ews.Resource.xml ewsresourceinterface) - - add_executable(akonadi_ewsmta_resource ${ewsmtaresource_SRCS}) - target_link_libraries(akonadi_ewsmta_resource - KF5::AkonadiAgentBase - KF5::AkonadiCore - KF5::AkonadiWidgets - KF5::I18n - KF5::WindowSystem - KF5::Mime) - - set(EWS_MTA_CAPABILITIES "X-EwsMailTransport") + set(ewsmtaresource_SRCS + ewsmtaconfigdialog.cpp + ewsmtaresource.cpp) + ecm_qt_declare_logging_category(ewsmtaresource_SRCS + HEADER ewsres_mta_debug.h + IDENTIFIER EWSRES_MTA_LOG + CATEGORY_NAME org.kde.pim.ews.mta + DESCRIPTION "ews mta (kdepim-runtime)" + EXPORT KDEPIMRUNTIME + ) + + + ki18n_wrap_ui(ewsmtaresource_SRCS ewsmtaconfigdialog.ui) + + kconfig_add_kcfg_files(ewsmtaresource_SRCS ewsmtasettings.kcfgc) + kcfg_generate_dbus_interface(${CMAKE_CURRENT_SOURCE_DIR}/ewsmtaresource.kcfg org.kde.Akonadi.EwsMta.Settings) + qt5_add_dbus_adaptor(ewsmtaresource_SRCS + ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.EwsMta.Settings.xml ewsmtasettings.h EwsMtaSettings ewsmtasettingsadaptor + ) + qt5_add_dbus_interface(ewsmtaresource_SRCS ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.Ews.Resource.xml ewsresourceinterface) + + add_executable(akonadi_ewsmta_resource ${ewsmtaresource_SRCS}) + target_link_libraries(akonadi_ewsmta_resource + KF5::AkonadiAgentBase + KF5::AkonadiCore + KF5::AkonadiWidgets + KF5::I18n + KF5::WindowSystem + KF5::Mime) + + set(EWS_MTA_CAPABILITIES "X-EwsMailTransport") else () - set(EWS_MTA_CAPABILITIES "MailTransport,Synchronizable") + set(EWS_MTA_CAPABILITIES "MailTransport,Synchronizable") endif () configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ewsresource.desktop.cmake ${CMAKE_CURRENT_BINARY_DIR}/ewsresource.desktop) if (BUILD_TESTING) - add_subdirectory(test) + add_subdirectory(test) endif () install(FILES ${CMAKE_CURRENT_BINARY_DIR}/ewsresource.desktop - DESTINATION "${KDE_INSTALL_DATAROOTDIR}/akonadi/agents" ) + DESTINATION "${KDE_INSTALL_DATAROOTDIR}/akonadi/agents" ) install(TARGETS akonadi_ews_resource ${INSTALL_TARGETS_DEFAULT_ARGS}) if (HAVE_SEPARATE_MTA_RESOURCE) - install(FILES ewsmtaresource.desktop - DESTINATION "${KDE_INSTALL_DATAROOTDIR}/akonadi/agents" ) - install(TARGETS akonadi_ewsmta_resource ${INSTALL_TARGETS_DEFAULT_ARGS}) + install(FILES ewsmtaresource.desktop + DESTINATION "${KDE_INSTALL_DATAROOTDIR}/akonadi/agents" ) + install(TARGETS akonadi_ewsmta_resource ${INSTALL_TARGETS_DEFAULT_ARGS}) endif () ecm_install_icons( ICONS - icons/16-apps-akonadi-ews.png - icons/22-apps-akonadi-ews.png - icons/24-apps-akonadi-ews.png - icons/32-apps-akonadi-ews.png - icons/48-apps-akonadi-ews.png - icons/64-apps-akonadi-ews.png - icons/72-apps-akonadi-ews.png - icons/96-apps-akonadi-ews.png - icons/128-apps-akonadi-ews.png + icons/16-apps-akonadi-ews.png + icons/22-apps-akonadi-ews.png + icons/24-apps-akonadi-ews.png + icons/32-apps-akonadi-ews.png + icons/48-apps-akonadi-ews.png + icons/64-apps-akonadi-ews.png + icons/72-apps-akonadi-ews.png + icons/96-apps-akonadi-ews.png + icons/128-apps-akonadi-ews.png DESTINATION ${KDE_INSTALL_ICONDIR} THEME hicolor -) + ) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/akonadi_ews_resource.notifyrc DESTINATION "${KNOTIFYRC_INSTALL_DIR}") diff --git a/resources/ews/akonadi_ews_resource.notifyrc b/resources/ews/akonadi_ews_resource.notifyrc --- a/resources/ews/akonadi_ews_resource.notifyrc +++ b/resources/ews/akonadi_ews_resource.notifyrc @@ -20,6 +20,7 @@ Comment[pt_BR]=Microsoft Exchange Server (EWS) Comment[ru]=Сервер Microsoft Exchange (EWS) Comment[sk]=Microsoft Exchange Server (EWS) +Comment[sl]=Strežnik Microsoft exchange server (EWS) Comment[sv]=Microsoft Exchange-server (EWS) Comment[uk]=Сервер Microsoft Exchange (EWS) Comment[x-test]=xxMicrosoft Exchange Server (EWS)xx @@ -48,6 +49,7 @@ Name[pt_BR]=Autenticação do Microsoft Exchange expirada Name[ru]=Закончился срок действия подтверждения подлинности на сервере Microsoft Exchange Name[sk]=Platnosť overenia servera Microsoft Exchange vypršala +Name[sl]=Poverilnica za Microsoft Exchange je potekla Name[sv]=Microsoft Exchange behörighet utgången Name[uk]=Строк дії розпізнавання Microsoft Exchange вичерпано Name[x-test]=xxMicrosoft Exchange authentication expiredxx @@ -72,6 +74,7 @@ Comment[pt_BR]=As credenciais usadas para acessar o Microsoft Exchange não são mais válidas Comment[ru]=Учётные данные, использованные для доступа к Microsoft Exchange, более недействительны Comment[sk]=Poverenia použité na prístup k Microsoft Exchange už nie sú platné +Comment[sl]=Poverilnice za dostop do Microsoft Exchange niso več veljavne Comment[sv]=Inloggningsinformation använd för att komma åt Microsoft Exchange är inte längre giltig Comment[uk]=Реєстраційні дані для доступу Microsoft Exchange втратили чинність Comment[x-test]=xxCredentials used to access Microsoft Exchange are no longer validxx diff --git a/resources/ews/ewsclient/CMakeLists.txt b/resources/ews/ewsclient/CMakeLists.txt --- a/resources/ews/ewsclient/CMakeLists.txt +++ b/resources/ews/ewsclient/CMakeLists.txt @@ -19,98 +19,111 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}/../) set(EWSCLIENT_SRCS - auth/ewsabstractauth.cpp - auth/ewspasswordauth.cpp - ewsattachment.cpp - ewsattendee.cpp - ewsclient.cpp - ewscreatefolderrequest.cpp - ewscreateitemrequest.cpp - ewsdeletefolderrequest.cpp - ewsdeleteitemrequest.cpp - ewseffectiverights.cpp - ewseventrequestbase.cpp - ewsfindfolderrequest.cpp - ewsfinditemrequest.cpp - ewsfolder.cpp - ewsfoldershape.cpp - ewsgeteventsrequest.cpp - ewsgetstreamingeventsrequest.cpp - ewsgetfolderrequest.cpp - ewsgetitemrequest.cpp - ewsid.cpp - ewsitem.cpp - ewsitembase.cpp - ewsitemshape.cpp - ewsjob.cpp - ewsmailbox.cpp - ewsmovefolderrequest.cpp - ewsmoveitemrequest.cpp - ewsoccurrence.cpp - ewspoxautodiscoverrequest.cpp - ewspropertyfield.cpp - ewsrecurrence.cpp - ewsrequest.cpp - ewsserverversion.cpp - ewssubscriberequest.cpp - ewssyncfolderhierarchyrequest.cpp - ewssyncfolderitemsrequest.cpp - ewstypes.cpp - ewsunsubscriberequest.cpp - ewsupdatefolderrequest.cpp - ewsupdateitemrequest.cpp - ewsxml.cpp - ewsclient_debug.cpp) + auth/ewsabstractauth.cpp + auth/ewspasswordauth.cpp + ewsattachment.cpp + ewsattendee.cpp + ewsclient.cpp + ewscreatefolderrequest.cpp + ewscreateitemrequest.cpp + ewsdeletefolderrequest.cpp + ewsdeleteitemrequest.cpp + ewseffectiverights.cpp + ewseventrequestbase.cpp + ewsfindfolderrequest.cpp + ewsfinditemrequest.cpp + ewsfolder.cpp + ewsfoldershape.cpp + ewsgeteventsrequest.cpp + ewsgetstreamingeventsrequest.cpp + ewsgetfolderrequest.cpp + ewsgetitemrequest.cpp + ewsid.cpp + ewsitem.cpp + ewsitembase.cpp + ewsitemshape.cpp + ewsjob.cpp + ewsmailbox.cpp + ewsmovefolderrequest.cpp + ewsmoveitemrequest.cpp + ewsoccurrence.cpp + ewspoxautodiscoverrequest.cpp + ewspropertyfield.cpp + ewsrecurrence.cpp + ewsrequest.cpp + ewsserverversion.cpp + ewssubscriberequest.cpp + ewssyncfolderhierarchyrequest.cpp + ewssyncfolderitemsrequest.cpp + ewstypes.cpp + ewsunsubscriberequest.cpp + ewsupdatefolderrequest.cpp + ewsupdateitemrequest.cpp + ewsxml.cpp + ewsclient_debug.cpp) if (Qt5NetworkAuth_FOUND) - list(APPEND EWSCLIENT_SRCS - auth/ewsoauth.cpp) + list(APPEND EWSCLIENT_SRCS + auth/ewsoauth.cpp) endif () if (Qca-qt5_FOUND) - list(APPEND EWSCLIENT_SRCS - auth/ewspkeyauthjob.cpp) + list(APPEND EWSCLIENT_SRCS + auth/ewspkeyauthjob.cpp) endif () ecm_qt_declare_logging_category(EWSCLIENT_SRCS - HEADER ewscli_debug.h - IDENTIFIER EWSCLI_LOG - CATEGORY_NAME org.kde.pim.ews.client) + HEADER ewscli_debug.h + IDENTIFIER EWSCLI_LOG + CATEGORY_NAME org.kde.pim.ews.client + DESCRIPTION "ews client (kdepim-runtime)" + EXPORT KDEPIMRUNTIME + ) ecm_qt_declare_logging_category(EWSCLIENT_SRCS - HEADER ewscli_proto_debug.h - IDENTIFIER EWSCLI_PROTO_LOG - CATEGORY_NAME org.kde.pim.ews.client.proto - DEFAULT_SEVERITY Warning) + HEADER ewscli_proto_debug.h + IDENTIFIER EWSCLI_PROTO_LOG + CATEGORY_NAME org.kde.pim.ews.client.proto + DEFAULT_SEVERITY Warning + DESCRIPTION "ews client proto (kdepim-runtime)" + EXPORT KDEPIMRUNTIME + ) ecm_qt_declare_logging_category(EWSCLIENT_SRCS - HEADER ewscli_req_debug.h - IDENTIFIER EWSCLI_REQUEST_LOG - CATEGORY_NAME org.kde.pim.ews.client.request - DEFAULT_SEVERITY Warning) + HEADER ewscli_req_debug.h + IDENTIFIER EWSCLI_REQUEST_LOG + CATEGORY_NAME org.kde.pim.ews.client.request + DEFAULT_SEVERITY Warning + DESCRIPTION "ews client request (kdepim-runtime)" + EXPORT KDEPIMRUNTIME + ) ecm_qt_declare_logging_category(EWSCLIENT_SRCS - HEADER ewscli_failedreq_debug.h - IDENTIFIER EWSCLI_FAILEDREQUEST_LOG - CATEGORY_NAME org.kde.pim.ews.client.failedrequest - DEFAULT_SEVERITY Warning) + HEADER ewscli_failedreq_debug.h + IDENTIFIER EWSCLI_FAILEDREQUEST_LOG + CATEGORY_NAME org.kde.pim.ews.client.failedrequest + DEFAULT_SEVERITY Warning + DESCRIPTION "ews client failed request (kdepim-runtime)" + EXPORT KDEPIMRUNTIME + ) + add_library(ewsclient STATIC ${EWSCLIENT_SRCS}) target_link_libraries(ewsclient - Qt5::Network - KF5::KIOCore - KF5::KIOFileWidgets - KF5::KIOWidgets - KF5::KIONTLM - KF5::Codecs - KF5::I18n - KF5::Mime - KF5::CalendarCore) + Qt5::Network + KF5::KIOCore + KF5::KIOFileWidgets + KF5::KIOWidgets + KF5::KIONTLM + KF5::Codecs + KF5::I18n + KF5::Mime + KF5::CalendarCore) if (Qt5NetworkAuth_FOUND) - target_link_libraries(ewsclient - Qt5::NetworkAuth - Qt5::WebEngineWidgets) + target_link_libraries(ewsclient + Qt5::NetworkAuth + Qt5::WebEngineWidgets) endif () if (Qca-qt5_FOUND) - target_link_libraries(ewsclient - qca-qt5) + target_link_libraries(ewsclient + qca-qt5) endif () diff --git a/resources/ews/ewsclient/auth/ewsoauth.h b/resources/ews/ewsclient/auth/ewsoauth.h --- a/resources/ews/ewsclient/auth/ewsoauth.h +++ b/resources/ews/ewsclient/auth/ewsoauth.h @@ -25,7 +25,6 @@ #include "ewsabstractauth.h" -class QWidget; class EwsOAuthPrivate; class EwsOAuth : public EwsAbstractAuth diff --git a/resources/ews/ewsclient/auth/ewsoauth.cpp b/resources/ews/ewsclient/auth/ewsoauth.cpp --- a/resources/ews/ewsclient/auth/ewsoauth.cpp +++ b/resources/ews/ewsclient/auth/ewsoauth.cpp @@ -39,23 +39,19 @@ #include #include #include -#ifdef HAVE_QCA #include "ewspkeyauthjob.h" #endif -#endif #include #include "ewsclient_debug.h" static const auto o365AuthorizationUrl = QUrl(QStringLiteral("https://login.microsoftonline.com/common/oauth2/authorize")); static const auto o365AccessTokenUrl = QUrl(QStringLiteral("https://login.microsoftonline.com/common/oauth2/token")); static const auto o365FakeUserAgent = QStringLiteral("Mozilla/5.0 (Linux; Android 7.0; SM-G930V Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.125 Mobile Safari/537.36"); static const auto o365Resource = QStringLiteral("https%3A%2F%2Foutlook.office365.com%2F"); -#ifdef HAVE_QCA static const auto pkeyAuthSuffix = QStringLiteral(" PKeyAuth/1.0"); static const auto pkeyRedirectUri = QStringLiteral("urn:http-auth:PKeyAuth"); static const QString pkeyPasswordMapKey = QStringLiteral("pkey-password"); -#endif static const QString accessTokenMapKey = QStringLiteral("access-token"); static const QString refreshTokenMapKey = QStringLiteral("refresh-token"); @@ -132,9 +128,7 @@ void granted(); void error(const QString &error, const QString &errorDescription, const QUrl &uri); QVariantMap queryToVarmap(const QUrl &url); -#ifdef HAVE_QCA void pkeyAuthResult(KJob *job); -#endif QWebEngineView mWebView; QWebEngineProfile mWebProfile; @@ -148,9 +142,7 @@ const QString mRedirectUri; bool mAuthenticated; QPointer mWebDialog; -#ifdef HAVE_QCA QString mPKeyPassword; -#endif EwsOAuth *q_ptr = nullptr; Q_DECLARE_PUBLIC(EwsOAuth) @@ -163,12 +155,7 @@ void EwsOAuthReplyHandler::networkReplyFinished(QNetworkReply *reply) { -#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) - const auto networkError = reply->error(); -#else - const auto networkError = reply->networkError(); -#endif - if (networkError != QNetworkReply::NoError) { + if (reply->error() != QNetworkReply::NoError) { Q_EMIT replyError(reply->errorString()); return; } else if (reply->header(QNetworkRequest::ContentTypeHeader).isNull()) { @@ -230,9 +217,7 @@ qCDebugNC(EWSCLI_LOG) << QStringLiteral("Intercepted browser navigation to ") << url; if ((url.toString(QUrl::RemoveQuery) == mRedirectUri) -#ifdef HAVE_QCA || (url.toString(QUrl::RemoveQuery) == pkeyRedirectUri) -#endif ) { qCDebug(EWSCLI_LOG) << QStringLiteral("Found redirect URI - blocking request"); @@ -325,14 +310,12 @@ * Fortunately enough this can be worked around by faking the user agent to something "supported". */ auto userAgent = o365FakeUserAgent; -#ifdef HAVE_QCA if (!q->mPKeyCertFile.isNull() && !q->mPKeyKeyFile.isNull()) { qCInfoNC(EWSCLI_LOG) << QStringLiteral("Found PKeyAuth certificates"); userAgent += pkeyAuthSuffix; } else { qCInfoNC(EWSCLI_LOG) << QStringLiteral("PKeyAuth certificates not found"); } -#endif mWebProfile.setHttpUserAgent(userAgent); mWebDialog = new QDialog(q->mAuthParentWidget); @@ -370,7 +353,6 @@ mWebView.stop(); mWebDialog->hide(); -#ifdef HAVE_QCA Q_Q(EwsOAuth); if (url.toString(QUrl::RemoveQuery) == pkeyRedirectUri) { qCDebugNC(EWSCLI_LOG) << QStringLiteral("Found PKeyAuth URI"); @@ -383,11 +365,9 @@ return; } -#endif Q_EMIT mOAuth2.authorizationCallbackReceived(queryToVarmap(url)); } -#ifdef HAVE_QCA void EwsOAuthPrivate::pkeyAuthResult(KJob *j) { EwsPKeyAuthJob *job = qobject_cast(j); @@ -402,8 +382,6 @@ Q_EMIT mOAuth2.authorizationCallbackReceived(varmap); } -#endif - void EwsOAuthPrivate::granted() { Q_Q(EwsOAuth); @@ -502,11 +480,9 @@ { Q_D(EwsOAuth); -#ifdef HAVE_QCA if (map.contains(pkeyPasswordMapKey)) { d->mPKeyPassword = map[pkeyPasswordMapKey]; } -#endif if (map.contains(refreshTokenMapKey)) { d->mOAuth2.setRefreshToken(map[refreshTokenMapKey]); } diff --git a/resources/ews/ewsclient/auth/ewspkeyauthjob.cpp b/resources/ews/ewsclient/auth/ewspkeyauthjob.cpp --- a/resources/ews/ewsclient/auth/ewspkeyauthjob.cpp +++ b/resources/ews/ewsclient/auth/ewspkeyauthjob.cpp @@ -114,12 +114,7 @@ void EwsPKeyAuthJob::authRequestFinished() { -#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) - const auto networkError = mAuthReply->error(); -#else - const auto networkError = mAuthReply->networkError(); -#endif - if (networkError == QNetworkReply::NoError) { + if (mAuthReply->error() == QNetworkReply::NoError) { mResultUri = mAuthReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); if (!mResultUri.isValid()) { setErrorMsg(QStringLiteral("Incorrect or missing redirect URI in PKeyAuth response")); diff --git a/resources/ews/ewsconfigdialog.cpp b/resources/ews/ewsconfigdialog.cpp --- a/resources/ews/ewsconfigdialog.cpp +++ b/resources/ews/ewsconfigdialog.cpp @@ -127,16 +127,14 @@ mUi->authOAuth2RadioButton->setChecked(true); mUi->pkeyAuthGroupBox->setEnabled(true); } -#ifdef HAVE_QCA mUi->pkeyAuthCert->setText(mSettings->pKeyCert()); mUi->pkeyAuthKey->setText(mSettings->pKeyKey()); connect(mSettings.data(), &EwsSettings::mapRequestFinished, this, [&](const QMap &map) { if (map.contains(pkeyPasswordMapKey)) { mUi->pkeyAuthPassword->setPassword(map[pkeyPasswordMapKey]); } }); mSettings->requestMap(); -#endif int selectedIndex = -1; int i = 0; @@ -229,19 +227,18 @@ if (mUi->authOAuth2RadioButton->isChecked()) { mSettings->setAuthMode(QStringLiteral("oauth2")); } -#ifdef HAVE_QCA if (mUi->pkeyAuthGroupBox->isEnabled() && !mUi->pkeyAuthCert->text().isEmpty() && !mUi->pkeyAuthKey->text().isEmpty()) { mSettings->setPKeyCert(mUi->pkeyAuthCert->text()); mSettings->setPKeyKey(mUi->pkeyAuthKey->text()); const QMap map = {{pkeyPasswordMapKey, mUi->pkeyAuthPassword->password()}}; mSettings->setMap(map); } -#endif if (!mAuthMap.isEmpty()) { mSettings->setMap(mAuthMap); } + mSettings->setPassword(mUi->passwordEdit->password()); mSettings->save(); } @@ -454,16 +451,17 @@ auth = new EwsOAuth(this, mUi->kcfg_Email->text(), mSettings->oAuth2AppId(), mSettings->oAuth2ReturnUri()); } else if (mUi->authUsernameRadioButton->isChecked()) { auth = new EwsPasswordAuth(fullUsername(), this); + } else { + //Be sure that it will not crash. + return auth; } auth->setAuthParentWidget(this); -#ifdef HAVE_QCA if (mUi->pkeyAuthGroupBox->isEnabled() && !mUi->pkeyAuthCert->text().isEmpty() && !mUi->pkeyAuthKey->text().isEmpty()) { auth->setPKeyAuthCertificateFiles(mUi->pkeyAuthCert->text(), mUi->pkeyAuthKey->text()); mAuthMap[pkeyPasswordMapKey] = mUi->pkeyAuthPassword->password(); } -#endif connect(auth, &EwsAbstractAuth::requestWalletPassword, this, [&](bool) { auth->walletPasswordRequestFinished(mUi->passwordEdit->password()); diff --git a/resources/ews/ewsresource.h b/resources/ews/ewsresource.h --- a/resources/ews/ewsresource.h +++ b/resources/ews/ewsresource.h @@ -126,6 +126,7 @@ void authFailed(const QString &error); void requestAuthFailed(); void emitReadyStatus(); + void adjustRootCollectionName(const QString &newName); public Q_SLOTS: Q_SCRIPTABLE void sendMessage(const QString &id, const QByteArray &content); Q_SIGNALS: diff --git a/resources/ews/ewsresource.cpp b/resources/ews/ewsresource.cpp --- a/resources/ews/ewsresource.cpp +++ b/resources/ews/ewsresource.cpp @@ -145,6 +145,7 @@ QMetaObject::invokeMethod(this, &EwsResource::delayedInit, Qt::QueuedConnection); connect(this, &AgentBase::reloadConfiguration, this, &EwsResource::reloadConfig); + connect(this, &ResourceBase::nameChanged, this, &EwsResource::adjustRootCollectionName); } EwsResource::~EwsResource() @@ -302,6 +303,7 @@ CollectionFetchJob *fetchJob = qobject_cast(job); if (fetchJob && !fetchJob->collections().isEmpty()) { mRootCollection = fetchJob->collections().first(); + adjustRootCollectionName(name()); qCDebugNC(EWSRES_LOG) << QStringLiteral("Root collection fetched: ") << mRootCollection; } } @@ -1408,4 +1410,15 @@ Q_EMIT percent(0); } +void EwsResource::adjustRootCollectionName(const QString &newName) +{ + if (mRootCollection.isValid()) { + auto *attr = mRootCollection.attribute(Akonadi::Collection::AddIfMissing); + if (attr->displayName() != newName) { + attr->setDisplayName(newName); + new CollectionModifyJob(mRootCollection); + } + } +} + AKONADI_RESOURCE_MAIN(EwsResource) diff --git a/resources/ews/ewssettings.cpp b/resources/ews/ewssettings.cpp --- a/resources/ews/ewssettings.cpp +++ b/resources/ews/ewssettings.cpp @@ -336,11 +336,9 @@ auth = new EwsPasswordAuth(user, parent); } -#ifdef HAVE_QCA if (!pKeyCert().isNull() && !pKeyKey().isNull()) { auth->setPKeyAuthCertificateFiles(pKeyCert(), pKeyKey()); } -#endif connect(auth, &EwsAbstractAuth::requestWalletPassword, this, &EwsSettings::requestPassword); connect(auth, &EwsAbstractAuth::requestWalletMap, this, &EwsSettings::requestMap); diff --git a/resources/folderarchivesettings/folderarchiveaccountinfo.h b/resources/folderarchivesettings/folderarchiveaccountinfo.h --- a/resources/folderarchivesettings/folderarchiveaccountinfo.h +++ b/resources/folderarchivesettings/folderarchiveaccountinfo.h @@ -27,7 +27,7 @@ { public: FolderArchiveAccountInfo(); - FolderArchiveAccountInfo(const KConfigGroup &config); + explicit FolderArchiveAccountInfo(const KConfigGroup &config); ~FolderArchiveAccountInfo(); enum FolderArchiveType { diff --git a/resources/google-groupware/CMakeLists.txt b/resources/google-groupware/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/resources/google-groupware/CMakeLists.txt @@ -0,0 +1,85 @@ +add_definitions(-DTRANSLATION_DOMAIN=\"akonadi_google_resource\") + +set(googleresource_SRCS + googleresource.cpp + googlesettings.cpp + googlesettingsdialog.cpp + defaultreminderattribute.cpp + + generichandler.cpp + calendarhandler.cpp + taskhandler.cpp + contacthandler.cpp + ${accounts_SRCS}) + +if (ECM_VERSION VERSION_LESS "5.68.0") + ecm_qt_declare_logging_category(googleresource_SRCS HEADER googleresource_debug.h IDENTIFIER GOOGLE_LOG CATEGORY_NAME org.kde.pim.google) + ecm_qt_declare_logging_category(googleresource_SRCS HEADER googlecalendar_debug.h IDENTIFIER GOOGLE_CALENDAR_LOG CATEGORY_NAME org.kde.pim.google.calendar) + ecm_qt_declare_logging_category(googleresource_SRCS HEADER googletasks_debug.h IDENTIFIER GOOGLE_TASKS_LOG CATEGORY_NAME org.kde.pim.google.tasks) + ecm_qt_declare_logging_category(googleresource_SRCS HEADER googlecontacts_debug.h IDENTIFIER GOOGLE_CONTACTS_LOG CATEGORY_NAME org.kde.pim.google.contacts) +else() + ecm_qt_declare_logging_category(googleresource_SRCS HEADER googleresource_debug.h IDENTIFIER GOOGLE_LOG CATEGORY_NAME org.kde.pim.google + DESCRIPTION "resource google (kdepim-runtime)" + EXPORT KDEPIMRUNTIME) + ecm_qt_declare_logging_category(googleresource_SRCS HEADER googlecalendar_debug.h IDENTIFIER GOOGLE_CALENDAR_LOG CATEGORY_NAME org.kde.pim.google.calendar + DESCRIPTION "resource google calendar (kdepim-runtime)" + EXPORT KDEPIMRUNTIME) + ecm_qt_declare_logging_category(googleresource_SRCS HEADER googletasks_debug.h IDENTIFIER GOOGLE_TASKS_LOG CATEGORY_NAME org.kde.pim.google.tasks + DESCRIPTION "resource google tasks (kdepim-runtime)" + EXPORT KDEPIMRUNTIME) + ecm_qt_declare_logging_category(googleresource_SRCS HEADER googlecontacts_debug.h IDENTIFIER GOOGLE_CONTACTS_LOG CATEGORY_NAME org.kde.pim.google.contacts + DESCRIPTION "resource google contacts (kdepim-runtime)" + EXPORT KDEPIMRUNTIME) +endif() + +ki18n_wrap_ui(googleresource_SRCS googlesettingsdialog.ui) + +kconfig_add_kcfg_files(googleresource_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/settingsbase.kcfgc) + +kcfg_generate_dbus_interface( + ${CMAKE_CURRENT_SOURCE_DIR}/settingsbase.kcfg + org.kde.Akonadi.Google.Settings +) + +qt5_add_dbus_adaptor(googleresource_SRCS + ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.Google.Settings.xml + ${CMAKE_CURRENT_SOURCE_DIR}/googlesettings.h GoogleSettings +) + +add_executable(akonadi_google_resource ${googleresource_SRCS}) + +if( APPLE ) + set_target_properties(akonadi_google_resource PROPERTIES + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/../../Info.plist.template + ) + set_target_properties(akonadi_google_resource PROPERTIES + MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.Akonadi.google" + ) + set_target_properties(akonadi_google_resource PROPERTIES + MACOSX_BUNDLE_BUNDLE_NAME "KDE Akonadi Google Resource" + ) +endif() + +target_link_libraries(akonadi_google_resource + KF5::AkonadiCalendar + KF5::AkonadiCore + KF5::AkonadiAgentBase + KF5::CalendarCore + KF5::Contacts + KF5::Wallet + KF5::I18n + KF5::WindowSystem + KF5::Completion + KF5::TextWidgets + KPim::GAPICalendar + KPim::GAPIContacts + KPim::GAPICore + KPim::GAPITasks +) + +install(TARGETS akonadi_google_resource ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) + +install( + FILES googleresource.desktop + DESTINATION "${KDE_INSTALL_DATAROOTDIR}/akonadi/agents" +) diff --git a/resources/google-groupware/calendarhandler.h b/resources/google-groupware/calendarhandler.h new file mode 100644 --- /dev/null +++ b/resources/google-groupware/calendarhandler.h @@ -0,0 +1,59 @@ +/* + Copyright (C) 2011-2013 Daniel Vrátil + 2020 Igor Pobiko + + 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 3 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef CALENDARHANDLER_H +#define CALENDARHANDLER_H + +#include "generichandler.h" +#include +#include + +class CalendarHandler : public GenericHandler +{ + Q_OBJECT +public: + typedef QSharedPointer Ptr; + + using GenericHandler::GenericHandler; + + QString mimetype() override; + bool canPerformTask(const Akonadi::Item &item) override; + + void retrieveCollections() override; + void retrieveItems(const Akonadi::Collection &collection) override; + + void itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) override; + void itemChanged(const Akonadi::Item &item, const QSet< QByteArray > &partIdentifiers) override; + void itemsRemoved(const Akonadi::Item::List &items) override; + void itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &collectionSource, const Akonadi::Collection &collectionDestination) override; + + void collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent) override; + void collectionChanged(const Akonadi::Collection &collection) override; + void collectionRemoved(const Akonadi::Collection &collection) override; + // FreeBusy + QDateTime lastCacheUpdate() const; + void canHandleFreeBusy(const QString &email) const; + void retrieveFreeBusy(const QString &email, const QDateTime &start, const QDateTime &end); +private Q_SLOTS: + void slotCollectionsRetrieved(KGAPI2::Job *job); + void slotItemsRetrieved(KGAPI2::Job *job); +private: + void setupCollection(Akonadi::Collection &collection, const KGAPI2::CalendarPtr &group); +}; + +#endif // CALENDARHANDLER_H diff --git a/resources/google-groupware/calendarhandler.cpp b/resources/google-groupware/calendarhandler.cpp new file mode 100644 --- /dev/null +++ b/resources/google-groupware/calendarhandler.cpp @@ -0,0 +1,395 @@ +/* + Copyright (C) 2011-2013 Daniel Vrátil + 2020 Igor Poboiko + + 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 3 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "calendarhandler.h" +#include "defaultreminderattribute.h" +#include "googleresource.h" +#include "googlesettings.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "googlecalendar_debug.h" + +using namespace KGAPI2; +using namespace Akonadi; + +static constexpr uint32_t KGAPIEventVersion = 1; + +QString CalendarHandler::mimetype() +{ + return KCalendarCore::Event::eventMimeType(); +} + +bool CalendarHandler::canPerformTask(const Item &item) +{ + return m_resource->canPerformTask(item, mimetype()); +} + +void CalendarHandler::setupCollection(Collection &collection, const CalendarPtr &calendar) +{ + collection.setContentMimeTypes({ mimetype() }); + collection.setName(calendar->uid()); + collection.setParentCollection(m_resource->rootCollection()); + collection.setRemoteId(calendar->uid()); + if (calendar->editable()) { + collection.setRights(Collection::CanChangeCollection + |Collection::CanDeleteCollection + |Collection::CanCreateItem + |Collection::CanChangeItem + |Collection::CanDeleteItem); + } else { + collection.setRights(Collection::ReadOnly); + } + // TODO: for some reason, KOrganizer creates virtual collections + //newCollection.setVirtual(false); + // Setting icon + auto attr = collection.attribute(Collection::AddIfMissing); + attr->setDisplayName(calendar->title()); + attr->setIconName(QStringLiteral("view-calendar")); + // Setting color + if (calendar->backgroundColor().isValid()) { + auto colorAttr = collection.attribute(Collection::AddIfMissing); + colorAttr->setColor(calendar->backgroundColor()); + } + // Setting default remoinders + auto reminderAttr = collection.attribute(Collection::AddIfMissing); + reminderAttr->setReminders(calendar->defaultReminders()); + // Block email reminders, since Google sends them for us + auto blockAlarms = collection.attribute(Collection::AddIfMissing); + blockAlarms->blockAlarmType(KCalendarCore::Alarm::Audio, false); + blockAlarms->blockAlarmType(KCalendarCore::Alarm::Display, false); + blockAlarms->blockAlarmType(KCalendarCore::Alarm::Procedure, false); +} + +void CalendarHandler::retrieveCollections() +{ + Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Retrieving calendars")); + qCDebug(GOOGLE_CALENDAR_LOG) << "Retrieving calendars..."; + auto job = new CalendarFetchJob(m_settings->accountPtr(), this); + connect(job, &CalendarFetchJob::finished, this, &CalendarHandler::slotCollectionsRetrieved); +} + +void CalendarHandler::slotCollectionsRetrieved(KGAPI2::Job *job) +{ + if (!m_resource->handleError(job)) { + return; + } + qCDebug(GOOGLE_CALENDAR_LOG) << "Calendars retrieved"; + + const ObjectsList calendars = qobject_cast(job)->items(); + Collection::List collections; + collections.reserve(calendars.count()); + const QStringList activeCalendars = m_settings->calendars(); + for (const auto &object : calendars) { + const CalendarPtr &calendar = object.dynamicCast(); + qCDebug(GOOGLE_CALENDAR_LOG) << " -" << calendar->title() << "(" << calendar->uid() << ")"; + if (!activeCalendars.contains(calendar->uid())) { + qCDebug(GOOGLE_CALENDAR_LOG) << "Skipping, not subscribed"; + continue; + } + Collection collection; + setupCollection(collection, calendar); + collections << collection; + } + + m_resource->collectionsRetrievedFromHandler(collections); +} + +void CalendarHandler::retrieveItems(const Collection &collection) +{ + qCDebug(GOOGLE_CALENDAR_LOG) << "Retrieving events for calendar" << collection.remoteId(); + QString syncToken = collection.remoteRevision(); + auto job = new EventFetchJob(collection.remoteId(), m_settings->accountPtr(), this); + if (!syncToken.isEmpty()) { + qCDebug(GOOGLE_CALENDAR_LOG) << "Using sync token" << syncToken; + job->setSyncToken(syncToken); + job->setFetchDeleted(true); + } else { + // No need to fetch deleted items for non-incremental update + job->setFetchDeleted(false); + if (!m_settings->eventsSince().isEmpty()) { + const QDate date = QDate::fromString(m_settings->eventsSince(), Qt::ISODate); +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) + job->setTimeMin(QDateTime(date).toSecsSinceEpoch()); +#else + job->setTimeMin(QDateTime(date.startOfDay()).toSecsSinceEpoch()); +#endif + } + } + + job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection)); + connect(job, &EventFetchJob::finished, this, &CalendarHandler::slotItemsRetrieved); + + Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Retrieving events for calendar '%1'", collection.displayName())); +} + +void CalendarHandler::slotItemsRetrieved(KGAPI2::Job *job) +{ + if (!m_resource->handleError(job)) { + return; + } + Item::List changedItems, removedItems; + Collection collection = job->property(COLLECTION_PROPERTY).value(); + DefaultReminderAttribute *attr = collection.attribute(); + + auto fetchJob = qobject_cast(job); + const ObjectsList objects = fetchJob->items(); + bool isIncremental = !fetchJob->syncToken().isEmpty(); + qCDebug(GOOGLE_CALENDAR_LOG) << "Retrieved" << objects.count() << "events for calendar" << collection.remoteId(); + for (const ObjectPtr &object : objects) { + const EventPtr event = object.dynamicCast(); + if (event->useDefaultReminders() && attr) { + const KCalendarCore::Alarm::List alarms = attr->alarms(event.data()); + for (const KCalendarCore::Alarm::Ptr &alarm : alarms) { + event->addAlarm(alarm); + } + } + + Item item; + item.setMimeType(KCalendarCore::Event::eventMimeType()); + item.setParentCollection(collection); + item.setRemoteId(event->id()); + item.setRemoteRevision(event->etag()); + item.setPayload(event.dynamicCast()); + + if (event->deleted()) { + qCDebug(GOOGLE_CALENDAR_LOG) << " - removed" << event->uid(); + removedItems << item; + } else { + qCDebug(GOOGLE_CALENDAR_LOG) << " - changed" << event->uid(); + changedItems << item; + } + } + + if (!isIncremental) { + m_resource->itemsRetrieved(changedItems); + } else { + m_resource->itemsRetrievedIncremental(changedItems, removedItems); + } + qCDebug(GOOGLE_CALENDAR_LOG) << "Next sync token:" << fetchJob->syncToken(); + collection.setRemoteRevision(fetchJob->syncToken()); + new CollectionModifyJob(collection, this); + + m_resource->emitReadyStatus(); +} + +void CalendarHandler::itemAdded(const Item &item, const Collection &collection) +{ + Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Adding event to calendar '%1'", collection.name())); + qCDebug(GOOGLE_CALENDAR_LOG) << "Event added to calendar" << collection.remoteId(); + KCalendarCore::Event::Ptr event = item.payload(); + EventPtr kevent(new Event(*event)); + auto *job = new EventCreateJob(kevent, collection.remoteId(), m_settings->accountPtr(), this); + job->setSendUpdates(SendUpdatesPolicy::None); + connect(job, &EventCreateJob::finished, this, [this, item](KGAPI2::Job *job){ + if (!m_resource->handleError(job)) { + return; + } + Item newItem(item); + const EventPtr event = qobject_cast(job)->items().first().dynamicCast(); + qCDebug(GOOGLE_CALENDAR_LOG) << "Event added"; + newItem.setRemoteId(event->id()); + newItem.setRemoteRevision(event->etag()); + newItem.setGid(event->uid()); + m_resource->changeCommitted(newItem); + newItem.setPayload(event.dynamicCast()); + new ItemModifyJob(newItem, this); + m_resource->emitReadyStatus(); + }); +} + +void CalendarHandler::itemChanged(const Item &item, const QSet< QByteArray > &partIdentifiers) +{ + Q_UNUSED(partIdentifiers); + Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Changing event in calendar '%1'", item.parentCollection().displayName())); + qCDebug(GOOGLE_CALENDAR_LOG) << "Changing event" << item.remoteId(); + KCalendarCore::Event::Ptr event = item.payload(); + EventPtr kevent(new Event(*event)); + auto job = new EventModifyJob(kevent, item.parentCollection().remoteId(), m_settings->accountPtr(), this); + job->setSendUpdates(SendUpdatesPolicy::None); + job->setProperty(ITEM_PROPERTY, QVariant::fromValue(item)); + connect(job, &EventModifyJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); +} + +void CalendarHandler::itemsRemoved(const Item::List &items) +{ + Q_EMIT m_resource->status(AgentBase::Running, i18ncp("@info:status", "Removing %1 events", "Removing %1 event", items.count())); + QStringList eventIds; + eventIds.reserve(items.count()); + std::transform(items.cbegin(), items.cend(), std::back_inserter(eventIds), + [](const Item &item){ + return item.remoteId(); + }); + qCDebug(GOOGLE_CALENDAR_LOG) << "Removing events:" << eventIds; + // TODO: what if events are from diferent calendars? + auto job = new EventDeleteJob(eventIds, items.first().parentCollection().remoteId(), m_settings->accountPtr(), this); + job->setProperty(ITEMS_PROPERTY, QVariant::fromValue(items)); + connect(job, &EventDeleteJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); +} + +void CalendarHandler::itemsMoved(const Item::List &items, const Collection &collectionSource, const Collection &collectionDestination) +{ + Q_EMIT m_resource->status(AgentBase::Running, i18ncp("@info:status", "Moving %1 events from calendar '%2' to calendar '%3'", + "Moving %1 event from calendar '%2' to calendar '%3'", + items.count(), collectionSource.displayName(), collectionDestination.displayName())); + QStringList eventIds; + eventIds.reserve(items.count()); + std::transform(items.cbegin(), items.cend(), std::back_inserter(eventIds), + [](const Item &item){ + return item.remoteId(); + }); + qCDebug(GOOGLE_CALENDAR_LOG) << "Moving events" << eventIds << "from" << collectionSource.remoteId() << "to" << collectionDestination.remoteId(); + auto job = new EventMoveJob(eventIds, collectionSource.remoteId(), collectionDestination.remoteId(), m_settings->accountPtr(), this); + job->setProperty(ITEMS_PROPERTY, QVariant::fromValue(items)); + connect(job, &EventMoveJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); +} + +void CalendarHandler::collectionAdded(const Collection &collection, const Collection &parent) +{ + Q_UNUSED(parent); + Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Creating calendar '%1'", collection.displayName())); + qCDebug(GOOGLE_CALENDAR_LOG) << "Adding calendar" << collection.displayName(); + CalendarPtr calendar(new Calendar()); + calendar->setTitle(collection.displayName()); + calendar->setEditable(true); + + auto job = new CalendarCreateJob(calendar, m_settings->accountPtr(), this); + connect(job, &CalendarCreateJob::finished, this, [this, collection](KGAPI2::Job *job){ + if (!m_resource->handleError(job)) { + return; + } + CalendarPtr calendar = qobject_cast(job)->items().first().dynamicCast(); + qCDebug(GOOGLE_CALENDAR_LOG) << "Created calendar" << calendar->uid(); + // Enable newly added calendar in settings, otherwise user won't see it + m_settings->addCalendar(calendar->uid()); + // TODO: the calendar returned by google is almost empty, i.e. it's not "editable", + // does not contain the color, etc + calendar->setEditable(true); + // Populate remoteId & other stuff + Collection newCollection(collection); + setupCollection(newCollection, calendar); + m_resource->changeCommitted(newCollection); + m_resource->emitReadyStatus(); + + }); +} + +void CalendarHandler::collectionChanged(const Collection &collection) +{ + Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Changing calendar '%1'", collection.displayName())); + qCDebug(GOOGLE_CALENDAR_LOG) << "Changing calendar" << collection.remoteId(); + CalendarPtr calendar(new Calendar()); + calendar->setUid(collection.remoteId()); + calendar->setTitle(collection.displayName()); + calendar->setEditable(true); + auto job = new CalendarModifyJob(calendar, m_settings->accountPtr(), this); + job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection)); + connect(job, &CalendarModifyJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); +} + +void CalendarHandler::collectionRemoved(const Collection &collection) +{ + Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Removing calendar '%1'", collection.displayName())); + qCDebug(GOOGLE_CALENDAR_LOG) << "Removing calendar" << collection.remoteId(); + auto job = new CalendarDeleteJob(collection.remoteId(), m_settings->accountPtr(), this); + job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection)); + connect(job, &CalendarDeleteJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); +} + +QDateTime CalendarHandler::lastCacheUpdate() const +{ + return QDateTime(); +} + +void CalendarHandler::canHandleFreeBusy(const QString &email) const +{ + if (m_resource->canPerformTask()) { + m_resource->handlesFreeBusy(email, false); + return; + } + + auto job = new FreeBusyQueryJob(email, + QDateTime::currentDateTimeUtc(), + QDateTime::currentDateTimeUtc().addSecs(3600), + m_settings->accountPtr(), + const_cast(this)); + connect(job, &FreeBusyQueryJob::finished, this, [this](KGAPI2::Job *job){ + auto queryJob = qobject_cast(job); + if (!m_resource->handleError(job, false)) { + m_resource->handlesFreeBusy(queryJob->id(), false); + return; + } + m_resource->handlesFreeBusy(queryJob->id(), true); + }); +} + +void CalendarHandler::retrieveFreeBusy(const QString &email, const QDateTime &start, const QDateTime &end) +{ + if (m_resource->canPerformTask()) { + m_resource->freeBusyRetrieved(email, QString(), false, QString()); + return; + } + + auto job = new FreeBusyQueryJob(email, start, end, m_settings->accountPtr(), this); + connect(job, &FreeBusyQueryJob::finished, this, [this](KGAPI2::Job *job) { + auto queryJob = qobject_cast(job); + + if (!m_resource->handleError(job, false)) { + m_resource->freeBusyRetrieved(queryJob->id(), QString(), false, QString()); + return; + } + + KCalendarCore::FreeBusy::Ptr fb(new KCalendarCore::FreeBusy); + fb->setUid(QStringLiteral("%1%2@google.com").arg(QDateTime::currentDateTimeUtc().toString(QStringLiteral("yyyyMMddTHHmmssZ")))); + fb->setOrganizer(job->account()->accountName()); + fb->addAttendee(KCalendarCore::Attendee(QString(), queryJob->id())); + // FIXME: is it really sort? + fb->setDateTime(QDateTime::currentDateTimeUtc(), KCalendarCore::IncidenceBase::RoleSort); + const auto ranges = queryJob->busy(); + for (const auto &range : ranges) { + fb->addPeriod(range.busyStart, range.busyEnd); + } + + KCalendarCore::ICalFormat format; + const QString fbStr = format.createScheduleMessage(fb, KCalendarCore::iTIPRequest); + + m_resource->freeBusyRetrieved(queryJob->id(), fbStr, true, QString()); + }); +} diff --git a/resources/google-groupware/contacthandler.h b/resources/google-groupware/contacthandler.h new file mode 100644 --- /dev/null +++ b/resources/google-groupware/contacthandler.h @@ -0,0 +1,61 @@ +/* + Copyright (C) 2011, 2012 Dan Vratil + 2020 Igor Poboiko + + 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 3 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#ifndef CONTACTHANDLER_H +#define CONTACTHANDLER_H + +#include "generichandler.h" +#include +#include + + +class ContactHandler : public GenericHandler +{ + Q_OBJECT +public: + using GenericHandler::GenericHandler; + + QString mimetype() override; + bool canPerformTask(const Akonadi::Item &item) override; + + void retrieveCollections() override; + void retrieveItems(const Akonadi::Collection &collection) override; + + void itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) override; + void itemChanged(const Akonadi::Item &item, const QSet< QByteArray > &partIdentifiers) override; + void itemsRemoved(const Akonadi::Item::List &items) override; + void itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &collectionSource, const Akonadi::Collection &collectionDestination) override; + void itemsLinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection) override; + void itemsUnlinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection) override; + + void collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent) override; + void collectionChanged(const Akonadi::Collection &collection) override; + void collectionRemoved(const Akonadi::Collection &collection) override; +private Q_SLOTS: + void slotCollectionsRetrieved(KGAPI2::Job *job); + void slotItemsRetrieved(KGAPI2::Job *job); + void slotUpdatePhotosItemsRetrieved(KJob *job); + void retrieveContactsPhotos(const QVariant &arguments); +private: + QString myContactsRemoteId() const; + void setupCollection(Akonadi::Collection &collection, const KGAPI2::ContactsGroupPtr &group); + QMap m_collections; +}; + +#endif // CONTACTHANDLER_H diff --git a/resources/google-groupware/contacthandler.cpp b/resources/google-groupware/contacthandler.cpp new file mode 100644 --- /dev/null +++ b/resources/google-groupware/contacthandler.cpp @@ -0,0 +1,494 @@ +/* + Copyright (C) 2011-2013 Daniel Vrátil + 2020 Igor Poboiko + + 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 3 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "contacthandler.h" +#include "googleresource.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "googlecontacts_debug.h" + +#define OTHERCONTACTS_REMOTEID QStringLiteral("OtherContacts") + +using namespace KGAPI2; +using namespace Akonadi; + +QString ContactHandler::mimetype() +{ + return KContacts::Addressee::mimeType(); +} + +bool ContactHandler::canPerformTask(const Item &item) +{ + return m_resource->canPerformTask(item, mimetype()); +} + +QString ContactHandler::myContactsRemoteId() const +{ + return QStringLiteral("http://www.google.com/m8/feeds/groups/%1/base/6").arg(QString::fromLatin1(QUrl::toPercentEncoding(m_settings->accountPtr()->accountName()))); +} + +void ContactHandler::setupCollection(Collection &collection, const ContactsGroupPtr &group) +{ + collection.setContentMimeTypes({ KContacts::Addressee::mimeType() }); + collection.setName(group->id()); + collection.setRemoteId(group->id()); + collection.setParentCollection(m_resource->rootCollection()); + + QString realName = group->title(); + if (group->isSystemGroup()) { + if (group->title().contains(QLatin1String("Coworkers"))) { + realName = i18nc("Name of a group of contacts", "Coworkers"); + } else if (group->title().contains(QLatin1String("Friends"))) { + realName = i18nc("Name of a group of contacts", "Friends"); + } else if (group->title().contains(QLatin1String("Family"))) { + realName = i18nc("Name of a group of contacts", "Family"); + } else if (group->title().contains(QLatin1String("My Contacts"))) { + realName = i18nc("Name of a group of contacts", "My Contacts"); + } + } + + // "My Contacts" is the only one not virtual + if (group->id() == myContactsRemoteId()) { + collection.setRights(Collection::CanCreateItem + |Collection::CanChangeItem + |Collection::CanDeleteItem); + } else { + collection.setRights(Collection::CanLinkItem + |Collection::CanUnlinkItem + |Collection::CanChangeItem); + collection.setVirtual(true); + if (!group->isSystemGroup()) { + collection.setRights(collection.rights() + |Collection::CanChangeCollection + |Collection::CanDeleteCollection); + } + } + + auto attr = collection.attribute(Collection::AddIfMissing); + attr->setDisplayName(realName); + attr->setIconName(QStringLiteral("view-pim-contacts")); +} + +void ContactHandler::retrieveCollections() +{ + Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Retrieving contacts groups")); + qCDebug(GOOGLE_CONTACTS_LOG) << "Retrieving contacts groups..."; + m_collections.clear(); + + Collection otherCollection; + otherCollection.setContentMimeTypes({ KContacts::Addressee::mimeType() }); + otherCollection.setName(i18n("Other Contacts")); + otherCollection.setParentCollection(m_resource->rootCollection()); + otherCollection.setRights(Collection::CanCreateItem + |Collection::CanChangeItem + |Collection::CanDeleteItem); + otherCollection.setRemoteId(OTHERCONTACTS_REMOTEID); + + auto attr = otherCollection.attribute(Collection::AddIfMissing); + attr->setDisplayName(i18n("Other Contacts")); + attr->setIconName(QStringLiteral("view-pim-contacts")); + + m_resource->collectionsRetrieved({ otherCollection }); + m_collections[ OTHERCONTACTS_REMOTEID ] = otherCollection; + + auto job = new ContactsGroupFetchJob(m_settings->accountPtr(), this); + connect(job, &ContactFetchJob::finished, this, &ContactHandler::slotCollectionsRetrieved); +} + +void ContactHandler::slotCollectionsRetrieved(KGAPI2::Job *job) +{ + if (!m_resource->handleError(job)) { + return; + } + qCDebug(GOOGLE_CONTACTS_LOG) << "Contacts groups retrieved"; + + const ObjectsList objects = qobject_cast(job)->items(); + Collection::List collections; + collections.reserve(objects.count()); + std::transform(objects.cbegin(), objects.cend(), std::back_inserter(collections), + [this](const ObjectPtr &object){ + const ContactsGroupPtr group = object.dynamicCast(); + qCDebug(GOOGLE_CONTACTS_LOG) << " -" << group->title() << "(" << group->id() << ")"; + Collection collection; + setupCollection(collection, group); + m_collections[ collection.remoteId() ] = collection; + return collection; + }); + m_resource->collectionsRetrievedFromHandler(collections); +} + +void ContactHandler::retrieveItems(const Collection &collection) +{ + // Contacts are stored inside "My Contacts" and "Other Contacts" only + if ((collection.remoteId() != OTHERCONTACTS_REMOTEID) + && (collection.remoteId() != myContactsRemoteId())) { + m_resource->itemsRetrievalDone(); + return; + } + Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Retrieving contacts for group '%1'", collection.displayName())); + qCDebug(GOOGLE_CONTACTS_LOG) << "Retreiving contacts for group" << collection.remoteId() << "..."; + + auto job = new ContactFetchJob(m_settings->accountPtr(), this); + if (!collection.remoteRevision().isEmpty()) { + job->setFetchOnlyUpdated(collection.remoteRevision().toLongLong()); + job->setFetchDeleted(true); + } else { + // No need to fetch deleted items for a non-incremental update + job->setFetchDeleted(false); + } + connect(job, &ContactFetchJob::finished, this, &ContactHandler::slotItemsRetrieved); +} + +void ContactHandler::slotItemsRetrieved(KGAPI2::Job *job) +{ + if (!m_resource->handleError(job)) { + return; + } + + Collection collection = m_resource->currentCollection(); + + Item::List changedItems, removedItems; + QHash groupsMap; + QStringList changedPhotos; + + auto fetchJob = qobject_cast(job); + bool isIncremental = (fetchJob->fetchOnlyUpdated() > 0); + const ObjectsList objects = fetchJob->items(); + qCDebug(GOOGLE_CONTACTS_LOG) << "Retrieved" << objects.count() << "contacts"; + for (const ObjectPtr &object : objects) { + const ContactPtr contact = object.dynamicCast(); + + Item item; + item.setMimeType( mimetype() ); + item.setParentCollection(collection); + item.setRemoteId(contact->uid()); + item.setRemoteRevision(contact->etag()); + item.setPayload(*contact.dynamicCast()); + + if (contact->deleted() + || (collection.remoteId() == OTHERCONTACTS_REMOTEID && !contact->groups().isEmpty()) + || (collection.remoteId() == myContactsRemoteId() && contact->groups().isEmpty())) { + qCDebug(GOOGLE_CONTACTS_LOG) << " - removed" << contact->uid(); + removedItems << item; + } else { + qCDebug(GOOGLE_CONTACTS_LOG) << " - changed" << contact->uid(); + changedItems << item; + changedPhotos << contact->uid(); + } + + const QStringList groups = contact->groups(); + for (const QString &group : groups) { + // We don't link contacts to "My Contacts" + if (group != myContactsRemoteId()) { + groupsMap[group] << item; + } + } + } + + if (isIncremental) { + m_resource->itemsRetrievedIncremental(changedItems, removedItems); + } else { + m_resource->itemsRetrieved(changedItems); + } + + for (auto iter = groupsMap.constBegin(), iterEnd = groupsMap.constEnd(); iter != iterEnd; ++iter) { + new LinkJob(m_collections[iter.key()], iter.value(), this); + } + // TODO: unlink if the group was removed! + + if (!changedPhotos.isEmpty()) { + QVariantMap map; + map[QStringLiteral("collection")] = QVariant::fromValue(collection); + map[QStringLiteral("modified")] = QVariant::fromValue(changedPhotos); + m_resource->scheduleCustomTask(this, "retrieveContactsPhotos", map); + } + + const QDateTime local(QDateTime::currentDateTime()); + const QDateTime UTC(local.toUTC()); + + collection.setRemoteRevision(QString::number(UTC.toSecsSinceEpoch())); + new CollectionModifyJob(collection, this); + + m_resource->emitReadyStatus(); +} + +void ContactHandler::retrieveContactsPhotos(const QVariant &argument) +{ + if (!m_resource->canPerformTask()) { + return; + } + const auto map = argument.value(); + const auto collection = map[QStringLiteral("collection")].value(); + const auto changedPhotos = map[QStringLiteral("modified")].toStringList(); + Q_EMIT m_resource->status(AgentBase::Running, i18ncp("@info:status", "Retrieving %1 contacts photos for group '%2'", + "Retrieving %1 contact photo for group '%2'", + changedPhotos.count(), collection.displayName())); + + Item::List items; + items.reserve(changedPhotos.size()); + std::transform(changedPhotos.cbegin(), changedPhotos.cend(), std::back_inserter(items), + [](const QString &contact){ + Item item; + item.setRemoteId(contact); + return item; + }); + auto job = new ItemFetchJob(items, this); + job->setCollection(collection); + job->fetchScope().fetchFullPayload(true); + connect(job, &ItemFetchJob::finished, this, &ContactHandler::slotUpdatePhotosItemsRetrieved); +} + +void ContactHandler::slotUpdatePhotosItemsRetrieved(KJob *job) +{ + // Make sure account is still valid + if (!m_resource->canPerformTask()) { + return; + } + const Item::List items = qobject_cast(job)->items(); + ContactsList contacts; + qCDebug(GOOGLE_CONTACTS_LOG) << "Fetched" << items.count() << "contacts for photo update"; + contacts.reserve(items.size()); + std::transform(items.cbegin(), items.cend(), std::back_inserter(contacts), + [](const Item &item){ + const KContacts::Addressee addressee = item.payload(); + const ContactPtr contact(new Contact(addressee)); + return contact; + }); + + qCDebug(GOOGLE_CONTACTS_LOG) << "Starting fetching photos..."; + auto photoJob = new ContactFetchPhotoJob(contacts, m_settings->accountPtr(), this); + photoJob->setProperty("processedItems", 0); + connect(photoJob, &ContactFetchPhotoJob::photoFetched, this, [this, items](KGAPI2::Job *job, const ContactPtr &contact){ + qCDebug(GOOGLE_CONTACTS_LOG) << " - fetched photo for contact" << contact->uid(); + int processedItems = job->property("processedItems").toInt(); + processedItems++; + job->setProperty("processedItems", processedItems); + Q_EMIT m_resource->percent(100.0f*processedItems / items.count()); + + auto it = std::find_if(items.cbegin(), items.cend(), [&contact](const Item &item){ + return item.remoteId() == contact->uid(); + }); + if (it != items.cend()) { + Item newItem(*it); + newItem.setPayload(*contact.dynamicCast()); + new ItemModifyJob(newItem, this); + } + }); + + connect(photoJob, &ContactFetchPhotoJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); +} + +void ContactHandler::itemAdded(const Item &item, const Collection &collection) +{ + Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Adding contact to group '%1'", collection.displayName())); + auto addressee = item.payload< KContacts::Addressee >(); + ContactPtr contact(new Contact(addressee)); + qCDebug(GOOGLE_CONTACTS_LOG) << "Creating contact"; + + if (collection.remoteId() == myContactsRemoteId()) { + contact->addGroup(myContactsRemoteId()); + } + + auto job = new ContactCreateJob(contact, m_settings->accountPtr(), this); + connect(job, &ContactCreateJob::finished, this, [this, item](KGAPI2::Job *job){ + if (!m_resource->handleError(job)) { + return; + } + ContactPtr contact = qobject_cast(job)->items().first().dynamicCast(); + Item newItem = item; + qCDebug(GOOGLE_CONTACTS_LOG) << "Contact" << contact->uid() << "created"; + newItem.setRemoteId(contact->uid()); + newItem.setRemoteRevision(contact->etag()); + m_resource->changeCommitted(newItem); + newItem.setPayload(*contact.dynamicCast()); + new ItemModifyJob(newItem, this); + m_resource->emitReadyStatus(); + }); +} + +void ContactHandler::itemChanged(const Item &item, const QSet< QByteArray > &partIdentifiers) +{ + Q_UNUSED(partIdentifiers); + Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Changing contact")); + qCDebug(GOOGLE_CONTACTS_LOG) << "Changing contact" << item.remoteId(); + KContacts::Addressee addressee = item.payload< KContacts::Addressee >(); + ContactPtr contact(new Contact(addressee)); + auto job = new ContactModifyJob(contact, m_settings->accountPtr(), this); + job->setProperty(ITEM_PROPERTY, QVariant::fromValue(item)); + connect(job, &ContactModifyJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); +} + +void ContactHandler::itemsRemoved(const Item::List &items) +{ + Q_EMIT m_resource->status(AgentBase::Running, i18ncp("@info:status", "Removing %1 contacts", "Removing %1 contact", items.count())); + QStringList contactIds; + contactIds.reserve(items.count()); + std::transform(items.cbegin(), items.cend(), std::back_inserter(contactIds), + [](const Item &item){ + return item.remoteId(); + }); + qCDebug(GOOGLE_CONTACTS_LOG) << "Removing contacts" << contactIds; + auto job = new ContactDeleteJob(contactIds, m_settings->accountPtr(), this); + job->setProperty(ITEMS_PROPERTY, QVariant::fromValue(items)); + connect(job, &ContactDeleteJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); +} + +void ContactHandler::itemsMoved(const Item::List &items, const Collection &collectionSource, const Collection &collectionDestination) +{ + qCDebug(GOOGLE_CONTACTS_LOG) << "Moving contacts from" << collectionSource.remoteId() << "to" << collectionDestination.remoteId(); + if (!(((collectionSource.remoteId() == myContactsRemoteId()) && (collectionDestination.remoteId() == OTHERCONTACTS_REMOTEID)) || + ((collectionSource.remoteId() == OTHERCONTACTS_REMOTEID) && (collectionDestination.remoteId() == myContactsRemoteId())))) { + m_resource->cancelTask(i18n("Invalid source or destination collection")); + } + + Q_EMIT m_resource->status(AgentBase::Running, i18ncp("@info:status", "Moving %1 contacts from group '%2' to '%3'", + "Moving %1 contact from group '%2' to '%3'", + items.count(), collectionSource.remoteId(), collectionDestination.remoteId())); + ContactsList contacts; + contacts.reserve(items.count()); + std::transform(items.cbegin(), items.cend(), std::back_inserter(contacts), + [this, &collectionSource, &collectionDestination](const Item &item){ + KContacts::Addressee addressee = item.payload(); + ContactPtr contact(new Contact(addressee)); + // MyContacts -> OtherContacts + if (collectionSource.remoteId() == myContactsRemoteId() + && collectionDestination.remoteId() == OTHERCONTACTS_REMOTEID) { + contact->clearGroups(); + // OtherContacts -> MyContacts + } else if (collectionSource.remoteId() == OTHERCONTACTS_REMOTEID + && collectionDestination.remoteId() == myContactsRemoteId()) { + contact->addGroup(myContactsRemoteId()); + } + + return contact; + }); + qCDebug(GOOGLE_CONTACTS_LOG) << "Moving contacts from" << collectionSource.remoteId() << "to" << collectionDestination.remoteId(); + auto job = new ContactModifyJob(contacts, m_settings->accountPtr(), this); + job->setProperty(ITEMS_PROPERTY, QVariant::fromValue(items)); + connect(job, &ContactModifyJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); +} + +void ContactHandler::itemsLinked(const Item::List &items, const Collection &collection) +{ + Q_EMIT m_resource->status(AgentBase::Running, i18ncp("@info:status", "Linking %1 contact", "Linking %1 contacts", items.count())); + qCDebug(GOOGLE_CONTACTS_LOG) << "Linking" << items.count() << "contacts to group" << collection.remoteId(); + + ContactsList contacts; + contacts.reserve(items.count()); + std::transform(items.cbegin(), items.cend(), std::back_inserter(contacts), + [this, &collection](const Item &item){ + KContacts::Addressee addressee = item.payload(); + ContactPtr contact(new Contact(addressee)); + contact->addGroup(collection.remoteId()); + return contact; + }); + auto job = new ContactModifyJob(contacts, m_settings->accountPtr(), this); + job->setProperty(ITEMS_PROPERTY, QVariant::fromValue(items)); + connect(job, &ContactModifyJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); +} + +void ContactHandler::itemsUnlinked(const Item::List &items, const Collection &collection) +{ + Q_EMIT m_resource->status(AgentBase::Running, i18ncp("@info:status", "Unlinking %1 contact", "Unlinking %1 contacts", items.count())); + qCDebug(GOOGLE_CONTACTS_LOG) << "Unlinking" << items.count() << "contacts from group" << collection.remoteId(); + + ContactsList contacts; + contacts.reserve(items.count()); + std::transform(items.cbegin(), items.cend(), std::back_inserter(contacts), + [this, &collection](const Item &item){ + KContacts::Addressee addressee = item.payload(); + ContactPtr contact(new Contact(addressee)); + contact->removeGroup(collection.remoteId()); + return contact; + }); + auto job = new ContactModifyJob(contacts, m_settings->accountPtr(), this); + job->setProperty(ITEMS_PROPERTY, QVariant::fromValue(items)); + connect(job, &ContactModifyJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); +} + + +void ContactHandler::collectionAdded(const Collection &collection, const Collection & /*parent*/) +{ + Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Creating new contact group '%1'", collection.displayName())); + qCDebug(GOOGLE_CONTACTS_LOG) << "Adding contact group" << collection.displayName(); + ContactsGroupPtr group(new ContactsGroup); + group->setTitle(collection.name()); + group->setIsSystemGroup(false); + + auto job = new ContactsGroupCreateJob(group, m_settings->accountPtr(), this); + connect(job, &ContactsGroupCreateJob::finished, this, [this, collection](KGAPI2::Job *job){ + if (!m_resource->handleError(job)) { + return; + } + + ContactsGroupPtr group = qobject_cast(job)->items().first().dynamicCast(); + qCDebug(GOOGLE_CONTACTS_LOG) << "Contact group created:" << group->id(); + Collection newCollection(collection); + setupCollection(newCollection, group); + m_collections[ newCollection.remoteId() ] = newCollection; + m_resource->changeCommitted(newCollection); + m_resource->emitReadyStatus(); + }); +} + +void ContactHandler::collectionChanged(const Collection &collection) +{ + Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Changing contact group '%1'", collection.displayName())); + qCDebug(GOOGLE_CONTACTS_LOG) << "Changing contact group" << collection.remoteId(); + + ContactsGroupPtr group(new ContactsGroup()); + group->setId(collection.remoteId()); + group->setTitle(collection.displayName()); + + auto job = new ContactsGroupModifyJob(group, m_settings->accountPtr(), this); + job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection)); + connect(job, &ContactsGroupModifyJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); +} + +void ContactHandler::collectionRemoved(const Collection &collection) +{ + Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Removing contact group '%1'", collection.displayName())); + qCDebug(GOOGLE_CONTACTS_LOG) << "Removing contact group" << collection.remoteId(); + auto job = new ContactsGroupDeleteJob(collection.remoteId(), m_settings->accountPtr(), this); + job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection)); + connect(job, &ContactsGroupDeleteJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); +} diff --git a/resources/google-groupware/defaultreminderattribute.h b/resources/google-groupware/defaultreminderattribute.h new file mode 100644 --- /dev/null +++ b/resources/google-groupware/defaultreminderattribute.h @@ -0,0 +1,45 @@ +/* + Copyright (C) 2011-2013 Daniel Vrátil + + 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 3 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GOOGLE_CALENDAR_DEFAULTREMINDERATTRIBUTE_H +#define GOOGLE_CALENDAR_DEFAULTREMINDERATTRIBUTE_H + +#include + +#include + +#include +#include + +class DefaultReminderAttribute : public Akonadi::Attribute +{ +public: + explicit DefaultReminderAttribute(); + + Attribute *clone() const override; + void deserialize(const QByteArray &data) override; + QByteArray serialized() const override; + QByteArray type() const override; + + void setReminders(const KGAPI2::RemindersList &reminders); + KCalendarCore::Alarm::List alarms(KCalendarCore::Incidence *incidence) const; + +private: + KGAPI2::RemindersList m_reminders; +}; + +#endif // DEFAULTREMINDERATTRIBUTE_H diff --git a/resources/google-groupware/defaultreminderattribute.cpp b/resources/google-groupware/defaultreminderattribute.cpp new file mode 100644 --- /dev/null +++ b/resources/google-groupware/defaultreminderattribute.cpp @@ -0,0 +1,115 @@ +/* + Copyright (C) 2011-2013 Daniel Vrátil + + 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 3 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "defaultreminderattribute.h" + +#include +#include + +#include + +using namespace KGAPI2; + +DefaultReminderAttribute::DefaultReminderAttribute() +{ +} + +Akonadi::Attribute *DefaultReminderAttribute::clone() const +{ + DefaultReminderAttribute *attr = new DefaultReminderAttribute(); + attr->setReminders(m_reminders); + + return attr; +} + +void DefaultReminderAttribute::setReminders(const RemindersList &reminders) +{ + m_reminders = reminders; +} + +void DefaultReminderAttribute::deserialize(const QByteArray &data) +{ + QJsonDocument json = QJsonDocument::fromJson(data); + if (json.isNull()) { + return; + } + + const QVariant var = json.toVariant(); + const QVariantList list = var.toList(); + for (const QVariant &l : list) { + QVariantMap reminder = l.toMap(); + + KGAPI2::ReminderPtr rem(new KGAPI2::Reminder); + + if (reminder[QStringLiteral("type")].toString() == QLatin1String("display")) { + rem->setType(KCalendarCore::Alarm::Display); + } else if (reminder[QStringLiteral("type")].toString() == QLatin1String("email")) { + rem->setType(KCalendarCore::Alarm::Email); + } + + KCalendarCore::Duration offset(reminder[QStringLiteral("time")].toInt(), KCalendarCore::Duration::Seconds); + rem->setStartOffset(offset); + + m_reminders << rem; + } +} + +QByteArray DefaultReminderAttribute::serialized() const +{ + QVariantList list; + list.reserve(m_reminders.count()); + + for (const ReminderPtr &rem : qAsConst(m_reminders)) { + QVariantMap reminder; + + if (rem->type() == KCalendarCore::Alarm::Display) { + reminder[QStringLiteral("type")] = QLatin1String("display"); + } else if (rem->type() == KCalendarCore::Alarm::Email) { + reminder[QStringLiteral("type")] = QLatin1String("email"); + } + + reminder[QStringLiteral("time")] = rem->startOffset().asSeconds(); + + list << reminder; + } + QJsonDocument serialized = QJsonDocument::fromVariant(list); + return serialized.toJson(); +} + +KCalendarCore::Alarm::List DefaultReminderAttribute::alarms(KCalendarCore::Incidence *incidence) const +{ + KCalendarCore::Alarm::List alarms; + alarms.reserve(m_reminders.count()); + for (const ReminderPtr &reminder : qAsConst(m_reminders)) { + KCalendarCore::Alarm::Ptr alarm(new KCalendarCore::Alarm(incidence)); + + alarm->setType(reminder->type()); + alarm->setTime(incidence->dtStart()); + alarm->setStartOffset(reminder->startOffset()); + alarm->setEnabled(true); + + alarms << alarm; + } + + return alarms; +} + +QByteArray DefaultReminderAttribute::type() const +{ + static const QByteArray sType("defaultReminders"); + return sType; +} diff --git a/resources/google-groupware/generichandler.h b/resources/google-groupware/generichandler.h new file mode 100644 --- /dev/null +++ b/resources/google-groupware/generichandler.h @@ -0,0 +1,66 @@ +/* + Copyright (C) 2020 Igor Poboiko + + 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 3 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GENERICHANDLER_H +#define GENERICHANDLER_H + +#include + +#include +#include + +#include +#include + +namespace KGAPI2 { + class Job; +} + +class GoogleResource; +class GoogleSettings; + +class GenericHandler : public QObject +{ + Q_OBJECT +public: + typedef QSharedPointer Ptr; + + GenericHandler(GoogleResource *resource, GoogleSettings *settings); + virtual ~GenericHandler(); + + virtual QString mimetype() = 0; + virtual bool canPerformTask(const Akonadi::Item &item) = 0; + + virtual void retrieveCollections() = 0; + virtual void retrieveItems(const Akonadi::Collection &collection) = 0; + + virtual void itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) = 0; + virtual void itemChanged(const Akonadi::Item &item, const QSet< QByteArray > &partIdentifiers) = 0; + virtual void itemsRemoved(const Akonadi::Item::List &items) = 0; + virtual void itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &collectionSource, const Akonadi::Collection &collectionDestination) = 0; + virtual void itemsLinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection); + virtual void itemsUnlinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection); + + virtual void collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent) = 0; + virtual void collectionChanged(const Akonadi::Collection &collection) = 0; + virtual void collectionRemoved(const Akonadi::Collection &collection) = 0; +protected: + GoogleResource *m_resource = nullptr; + GoogleSettings *m_settings = nullptr; +}; + +#endif // GENERICHANDLER_H diff --git a/resources/google-groupware/generichandler.cpp b/resources/google-groupware/generichandler.cpp new file mode 100644 --- /dev/null +++ b/resources/google-groupware/generichandler.cpp @@ -0,0 +1,40 @@ +/* + Copyright (C) 2020 Igor Poboiko + + 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 3 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#include "generichandler.h" +#include "googleresource.h" + +GenericHandler::GenericHandler(GoogleResource *resource, GoogleSettings *settings) + : m_resource(resource) + , m_settings(settings) +{ +} + +GenericHandler::~GenericHandler() = default; + +void GenericHandler::itemsLinked(const Akonadi::Item::List &/*items*/, const Akonadi::Collection &/*collection*/) +{ + m_resource->cancelTask(i18n("Cannot handle item linking")); +} + +void GenericHandler::itemsUnlinked(const Akonadi::Item::List &/*items*/, const Akonadi::Collection &/*collection*/) +{ + m_resource->cancelTask(i18n("Cannot handle item unlinking")); +} + +#include "moc_generichandler.cpp" diff --git a/resources/google-groupware/googleresource.h b/resources/google-groupware/googleresource.h new file mode 100644 --- /dev/null +++ b/resources/google-groupware/googleresource.h @@ -0,0 +1,128 @@ +/* + Copyright (C) 2011-2013 Daniel Vrátil + + 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 3 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GOOGLERESOURCE_H +#define GOOGLERESOURCE_H + +#include +#include +#include + +#include + +#include "googlesettings.h" +#include "generichandler.h" +#include "calendarhandler.h" + +#include + +#include + +#define ITEM_PROPERTY "_AkonadiItem" +#define ITEMS_PROPERTY "_AkonadiItems" +#define COLLECTION_PROPERTY "_AkonadiCollection" +#define JOB_PROPERTY "_KGAPI2Job" + +namespace KGAPI2 { +class Job; +} + +class GoogleSettings; + +class GoogleResource : public Akonadi::ResourceBase, public Akonadi::AgentBase::ObserverV3, public Akonadi::FreeBusyProviderBase +{ + Q_OBJECT + +public: + explicit GoogleResource(const QString &id); + ~GoogleResource() override; + + QList scopes() const; + + void cleanup() override; +public Q_SLOTS: + void configure(WId windowId) override; + void reloadConfig(); +protected: + int runConfigurationDialog(WId windowId); + void updateResourceName(); + // Freebusy + QDateTime lastCacheUpdate() const override; + void canHandleFreeBusy(const QString &email) const override; + void retrieveFreeBusy(const QString &email, const QDateTime &start, const QDateTime &end) override; + + template + bool canPerformTask(const Akonadi::Item &item, const QString &mimeType = QString()) + { + if (item.isValid() && !item.hasPayload()) { + cancelTask(i18n("Invalid item payload.")); + return false; + } else if (item.isValid() && mimeType != item.mimeType()) { + cancelTask(i18n("Invalid payload MIME type. Expected %1, found %2", mimeType, item.mimeType())); + return false; + } + + return canPerformTask(); + } + + bool canPerformTask(); + /** + * KAccounts support abstraction. + * + * Returns 0 when compiled without KAccounts or not configured for KAccounts + */ + int accountId() const; + + Akonadi::Collection rootCollection() const; + void emitReadyStatus(); + void collectionsRetrievedFromHandler(const Akonadi::Collection::List &collections); +protected Q_SLOTS: + void retrieveCollections() override; + void retrieveItems(const Akonadi::Collection &collection) override; + + void itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) override; + void itemChanged(const Akonadi::Item &item, const QSet< QByteArray > &partIdentifiers) override; + void itemsRemoved(const Akonadi::Item::List &items) override; + void itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &collectionSource, const Akonadi::Collection &collectionDestination) override; + void itemsLinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection) override; + void itemsUnlinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection) override; + + void collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent) override; + void collectionChanged(const Akonadi::Collection &collection) override; + void collectionRemoved(const Akonadi::Collection &collection) override; + + bool handleError(KGAPI2::Job *job, bool cancelTask = true); + + virtual void slotAuthJobFinished(KGAPI2::Job *job); + virtual void slotGenericJobFinished(KGAPI2::Job *job); +private: + bool m_isConfiguring = false; + GoogleSettings *m_settings = nullptr; + Akonadi::Collection m_rootCollection; + + QList m_handlers; + CalendarHandler::Ptr m_freeBusyHandler; + int m_jobs; + + friend class GoogleSettingsDialog; + friend class GenericHandler; + friend class CalendarHandler; + friend class ContactHandler; + friend class TaskHandler; +}; + +#endif // GOOGLERESOURCE_H diff --git a/resources/google-groupware/googleresource.cpp b/resources/google-groupware/googleresource.cpp new file mode 100644 --- /dev/null +++ b/resources/google-groupware/googleresource.cpp @@ -0,0 +1,525 @@ +/* + Copyright (C) 2011-2013 Daniel Vrátil + 2020 Igor Poboiko + + 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 3 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "googleresource.h" +#include "googlesettings.h" +#include "googlesettingsdialog.h" +#include "googleresource_debug.h" +#include "settingsadaptor.h" + +#include "calendarhandler.h" +#include "contacthandler.h" +#include "taskhandler.h" + +#include "defaultreminderattribute.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + + +#define CALENDARS_PROPERTY "_KGAPI2CalendarPtr" +#define ROOT_COLLECTION_REMOTEID QStringLiteral("RootCollection") + + +Q_DECLARE_METATYPE(KGAPI2::Job *) + +using namespace KGAPI2; +using namespace Akonadi; + +GoogleResource::GoogleResource(const QString &id) + : ResourceBase(id) + , AgentBase::ObserverV3() +{ + AttributeFactory::registerAttribute< DefaultReminderAttribute >(); + + connect(this, &GoogleResource::reloadConfiguration, this, &GoogleResource::reloadConfig); + + setNeedsNetwork(true); + + changeRecorder()->itemFetchScope().fetchFullPayload(true); + changeRecorder()->itemFetchScope().setAncestorRetrieval(ItemFetchScope::All); + changeRecorder()->fetchCollection(true); + changeRecorder()->collectionFetchScope().setAncestorRetrieval(CollectionFetchScope::All); + + m_settings = new GoogleSettings(); + m_settings->setWindowId(winIdForDialogs()); + connect(m_settings, &GoogleSettings::accountReady, this, [this](bool ready){ + if (accountId() > 0) { + return; + } + if (!ready) { + Q_EMIT status(Broken, i18n("Can't access KWallet")); + return; + } + if (m_settings->accountPtr().isNull()) { + Q_EMIT status(NotConfigured); + return; + } + emitReadyStatus(); + synchronize(); + }); + + Q_EMIT status(NotConfigured, i18n("Waiting for KWallet...")); + updateResourceName(); + + m_freeBusyHandler.reset(new CalendarHandler(this, m_settings)); + m_handlers << m_freeBusyHandler; + m_handlers << GenericHandler::Ptr(new ContactHandler(this, m_settings)); + m_handlers << GenericHandler::Ptr(new TaskHandler(this, m_settings)); + + new SettingsAdaptor(m_settings); + QDBusConnection::sessionBus().registerObject(QStringLiteral("/Settings"), + m_settings, QDBusConnection::ExportAdaptors); +} + +GoogleResource::~GoogleResource() +{ +} + +void GoogleResource::cleanup() +{ + m_settings->cleanup(); + ResourceBase::cleanup(); +} + +void GoogleResource::emitReadyStatus() +{ + Q_EMIT status(Idle, i18nc("@info:status", "Ready")); +} + +Collection GoogleResource::rootCollection() const +{ + return m_rootCollection; +} + +void GoogleResource::configure(WId windowId) +{ + if (!m_settings->isReady() || m_isConfiguring) { + Q_EMIT configurationDialogAccepted(); + return; + } + + m_isConfiguring = true; + + QScopedPointer settingsDialog(new GoogleSettingsDialog(this, m_settings, windowId)); + settingsDialog->setWindowIcon(QIcon::fromTheme(QStringLiteral("im-google"))); + if (settingsDialog->exec() == QDialog::Accepted) { + updateResourceName(); + + Q_EMIT configurationDialogAccepted(); + + if (m_settings->accountPtr().isNull()) { + Q_EMIT status(NotConfigured, i18n("Configured account does not exist")); + m_isConfiguring = false; + return; + } + + emitReadyStatus(); + synchronize(); + } else { + updateResourceName(); + + Q_EMIT configurationDialogRejected(); + } + + m_isConfiguring = false; +} + +QList GoogleResource::scopes() const +{ + // TODO: determine it based on what user wants? + const QList< QUrl > scopes = {Account::accountInfoScopeUrl(), Account::calendarScopeUrl(), + Account::contactsScopeUrl(), Account::tasksScopeUrl()}; + return scopes; +} + +void GoogleResource::updateResourceName() +{ + const QString accountName = m_settings->account(); + setName(i18nc("%1 is account name (user@gmail.com)", "Google Groupware (%1)", accountName.isEmpty() ? i18n("not configured") : accountName)); +} + +void GoogleResource::reloadConfig() +{ + const AccountPtr account = m_settings->accountPtr(); + if (account.isNull() || account->accountName().isEmpty()) { + Q_EMIT status(NotConfigured, i18n("Configured account does not exist")); + } else { + emitReadyStatus(); + } +} + +bool GoogleResource::handleError(KGAPI2::Job *job, bool _cancelTask) +{ + if ((job->error() == KGAPI2::NoError) || (job->error() == KGAPI2::OK)) { + return true; + } + qCWarning(GOOGLE_LOG) << "Got error:" << job << job->errorString(); + AccountPtr account = job->account(); + if (job->error() == KGAPI2::Unauthorized) { + const QList resourceScopes = scopes(); + for (const QUrl &scope : resourceScopes) { + if (!account->scopes().contains(scope)) { + account->addScope(scope); + } + } + + AuthJob *authJob = new AuthJob(account, m_settings->clientId(), m_settings->clientSecret(), this); + authJob->setProperty(JOB_PROPERTY, QVariant::fromValue(job)); + connect(authJob, &AuthJob::finished, this, &GoogleResource::slotAuthJobFinished); + return false; + } + + if (_cancelTask) { + cancelTask(job->errorString()); + } + return false; +} + +bool GoogleResource::canPerformTask() +{ + if (!m_settings->accountPtr() && accountId() == 0) { + cancelTask(i18nc("@info:status", "Resource is not configured")); + Q_EMIT status(NotConfigured, i18nc("@info:status", "Resource is not configured")); + return false; + } + + return true; +} + +void GoogleResource::slotAuthJobFinished(KGAPI2::Job *job) +{ + if (job->error() != KGAPI2::NoError) { + cancelTask(i18n("Failed to refresh tokens")); + return; + } + + AuthJob *authJob = qobject_cast(job); + AccountPtr account = authJob->account(); + if (!m_settings->storeAccount(account)) { + qCWarning(GOOGLE_LOG) << "Failed to store account in KWallet"; + } + + KGAPI2::Job *otherJob = job->property(JOB_PROPERTY).value(); + if (otherJob) { + otherJob->setAccount(account); + otherJob->restart(); + } +} + +void GoogleResource::slotGenericJobFinished(KGAPI2::Job *job) +{ + if (!handleError(job)) { + return; + } + if (job->property(ITEM_PROPERTY).isValid()) { + qCDebug(GOOGLE_LOG) << "Item change committed"; + changeCommitted(job->property(ITEM_PROPERTY).value()); + } else if (job->property(ITEMS_PROPERTY).isValid()) { + qCDebug(GOOGLE_LOG) << "Items changes committed"; + changesCommitted(job->property(ITEMS_PROPERTY).value()); + } else if (job->property(COLLECTION_PROPERTY).isValid()) { + qCDebug(GOOGLE_LOG) << "Collection change committed"; + changeCommitted(job->property(COLLECTION_PROPERTY).value()); + } else { + qCDebug(GOOGLE_LOG) << "Task done"; + taskDone(); + } + + emitReadyStatus(); +} + +int GoogleResource::accountId() const +{ + return 0; +} + +QDateTime GoogleResource::lastCacheUpdate() const +{ + if (m_freeBusyHandler) { + return m_freeBusyHandler->lastCacheUpdate(); + } + return QDateTime(); +} + +void GoogleResource::canHandleFreeBusy(const QString &email) const +{ + if (m_freeBusyHandler) { + m_freeBusyHandler->canHandleFreeBusy(email); + } else { + handlesFreeBusy(email, false); + } +} + +void GoogleResource::retrieveFreeBusy(const QString &email, const QDateTime &start, const QDateTime &end) +{ + if (m_freeBusyHandler) { + m_freeBusyHandler->retrieveFreeBusy(email, start, end); + } else { + freeBusyRetrieved(email, QString(), false, QString()); + } +} + +/* + * Collection handling + */ +void GoogleResource::retrieveCollections() +{ + if (!canPerformTask()) { + return; + } + qCDebug(GOOGLE_LOG) << "Retrieve Collections"; + + setCollectionStreamingEnabled(true); + CachePolicy cachePolicy; + if (m_settings->enableIntervalCheck()) { + cachePolicy.setInheritFromParent(false); + cachePolicy.setIntervalCheckTime(m_settings->intervalCheckTime()); + } + + // Setting up root collection + m_rootCollection = Collection(); + m_rootCollection.setContentMimeTypes({ Collection::mimeType(), Collection::virtualMimeType() }); + m_rootCollection.setRemoteId(ROOT_COLLECTION_REMOTEID); + m_rootCollection.setName(m_settings->accountPtr()->accountName()); + m_rootCollection.setParentCollection(Collection::root()); + m_rootCollection.setRights(Collection::CanCreateCollection); + m_rootCollection.setCachePolicy(cachePolicy); + + EntityDisplayAttribute *attr = m_rootCollection.attribute(Collection::AddIfMissing); + attr->setDisplayName(m_settings->accountPtr()->accountName()); + attr->setIconName(QStringLiteral("im-google")); + + collectionsRetrieved({ m_rootCollection }); + + m_jobs = m_handlers.count(); + for (auto &handler : m_handlers) { + handler->retrieveCollections(); + } +} + +void GoogleResource::collectionsRetrievedFromHandler(const Collection::List &collections) +{ + collectionsRetrieved(collections); + m_jobs--; + if (m_jobs == 0) { + qCDebug(GOOGLE_LOG) << "All collections retrieved!"; + collectionsRetrievalDone(); + //taskDone(); // ??? + emitReadyStatus(); + } +} + +void GoogleResource::retrieveItems(const Collection &collection) +{ + if (!canPerformTask()) { + return; + } + + auto it = std::find_if(m_handlers.begin(), m_handlers.end(), + [&collection](const GenericHandler::Ptr &handler){ + return collection.contentMimeTypes().contains(handler->mimetype()); + }); + + if (it != m_handlers.end()) { + (*it)->retrieveItems(collection); + } else { + qCWarning(GOOGLE_LOG) << "Unknown collection" << collection.name(); + itemsRetrieved({}); + } +} + +void GoogleResource::itemAdded(const Item &item, const Collection &collection) +{ + if (!canPerformTask()) { + return; + } + + auto it = std::find_if(m_handlers.begin(), m_handlers.end(), + [&collection, &item](const GenericHandler::Ptr &handler){ + return collection.contentMimeTypes().contains(handler->mimetype()) + && handler->canPerformTask(item); + }); + if (it != m_handlers.end()) { + (*it)->itemAdded(item, collection); + } else { + qCWarning(GOOGLE_LOG) << "Could not add item" << item.mimeType(); + cancelTask(i18n("Invalid payload type")); + } +} + +void GoogleResource::itemChanged(const Item &item, const QSet< QByteArray > &partIdentifiers) +{ + Q_UNUSED(partIdentifiers); + if (!canPerformTask()) { + return; + } + auto it = std::find_if(m_handlers.begin(), m_handlers.end(), + [&item](const GenericHandler::Ptr &handler){ + return handler->canPerformTask(item); + }); + if (it != m_handlers.end()) { + (*it)->itemChanged(item, partIdentifiers); + } else { + qCWarning(GOOGLE_LOG) << "Could not change item" << item.mimeType(); + cancelTask(i18n("Invalid payload type")); + } +} + +void GoogleResource::itemsRemoved(const Item::List &items) +{ + if (!canPerformTask()) { + return; + } + // TODO: what if items have different mimetypes? + const QString mimeType = items.first().mimeType(); + auto it = std::find_if(m_handlers.begin(), m_handlers.end(), + [&mimeType](const GenericHandler::Ptr &handler){ + return handler->mimetype() == mimeType; + }); + if (it != m_handlers.end()) { + (*it)->itemsRemoved(items); + } else { + qCWarning(GOOGLE_LOG) << "Could not remove item" << mimeType; + cancelTask(i18n("Invalid payload type")); + } +} + +void GoogleResource::itemsMoved(const Item::List &items, const Collection &collectionSource, const Collection &collectionDestination) +{ + if (!canPerformTask()) { + return; + } + // TODO: what if items have different mimetypes? + auto it = std::find_if(m_handlers.begin(), m_handlers.end(), + [&item = items.first()](const GenericHandler::Ptr &handler){ + return handler->canPerformTask(item); + }); + if (it != m_handlers.end()) { + (*it)->itemsMoved(items, collectionSource, collectionDestination); + } else { + qCWarning(GOOGLE_LOG) << "Could not move item" << items.first().mimeType() << "from" << collectionSource.remoteId() << "to" << collectionDestination.remoteId(); + cancelTask(i18n("Invalid payload type")); + } +} + +void GoogleResource::itemsLinked(const Item::List &items, const Collection &collection) +{ + if (!canPerformTask()) { + return; + } + // TODO: what if items have different mimetypes? + auto it = std::find_if(m_handlers.begin(), m_handlers.end(), + [&item = items.first()](const GenericHandler::Ptr &handler){ + return handler->canPerformTask(item); + }); + if (it != m_handlers.end()) { + (*it)->itemsLinked(items, collection); + } else { + qCWarning(GOOGLE_LOG) << "Could not link item" << items.first().mimeType() << "to" << collection.remoteId(); + cancelTask(i18n("Invalid payload type")); + } +} + +void GoogleResource::itemsUnlinked(const Item::List &items, const Collection &collection) +{ + if (!canPerformTask()) { + return; + } + // TODO: what if items have different mimetypes? + auto it = std::find_if(m_handlers.begin(), m_handlers.end(), + [&item = items.first()](const GenericHandler::Ptr &handler){ + return handler->canPerformTask(item); + }); + if (it != m_handlers.end()) { + (*it)->itemsUnlinked(items, collection); + } else { + qCWarning(GOOGLE_LOG) << "Could not unlink item mimetype" << items.first().mimeType() << "from" << collection.remoteId(); + cancelTask(i18n("Invalid payload type")); + } +} + +void GoogleResource::collectionAdded(const Collection &collection, const Collection &parent) +{ + if (!canPerformTask()) { + return; + } + auto it = std::find_if(m_handlers.begin(), m_handlers.end(), + [&collection](const GenericHandler::Ptr &handler){ + return collection.contentMimeTypes().contains(handler->mimetype()); + }); + if (it != m_handlers.end()) { + (*it)->collectionAdded(collection, parent); + } else { + qCWarning(GOOGLE_LOG) << "Could not add collection" << collection.displayName() << "mimetypes:" << collection.contentMimeTypes(); + cancelTask(i18n("Unknown collection mimetype")); + } +} + +void GoogleResource::collectionChanged(const Collection &collection) +{ + if (!canPerformTask()) { + return; + } + auto it = std::find_if(m_handlers.begin(), m_handlers.end(), + [&collection](const GenericHandler::Ptr &handler){ + return collection.contentMimeTypes().contains(handler->mimetype()); + }); + if (it != m_handlers.end()) { + (*it)->collectionChanged(collection); + } else { + qCWarning(GOOGLE_LOG) << "Could not change collection" << collection.displayName() << "mimetypes:" << collection.contentMimeTypes(); + cancelTask(i18n("Unknown collection mimetype")); + } +} + +void GoogleResource::collectionRemoved(const Collection &collection) +{ + if (!canPerformTask()) { + return; + } + auto it = std::find_if(m_handlers.begin(), m_handlers.end(), + [&collection](const GenericHandler::Ptr &handler){ + return collection.contentMimeTypes().contains(handler->mimetype()); + }); + if (it != m_handlers.end()) { + (*it)->collectionRemoved(collection); + } else { + qCWarning(GOOGLE_LOG) << "Could not remove collection" << collection.displayName() << "mimetypes:" << collection.contentMimeTypes(); + cancelTask(i18n("Unknown collection mimetype")); + } +} + +AKONADI_RESOURCE_MAIN(GoogleResource) diff --git a/resources/google-groupware/googleresource.desktop b/resources/google-groupware/googleresource.desktop new file mode 100644 --- /dev/null +++ b/resources/google-groupware/googleresource.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Name=Google Groupware +Comment=Access your Google Calendars, Contacts and Tasks from KDE +Type=AkonadiResource +Exec=akonadi_google_resource +X-Akonadi-MimeTypes=text/calendar,text/directory,application/x-vnd.akonadi.calendar.event,application/x-vnd.akonadi.calendar.todo,application/x-vnd.akonadi.calendar.freebusy +X-Akonadi-Capabilities=Resource,FreeBusyProvider +X-Akonadi-Identifier=akonadi_google_resource +X-Akonadi-Custom-KAccounts=google-contacts,google-calendar +Icon=im-google diff --git a/resources/google-groupware/googlesettings.h b/resources/google-groupware/googlesettings.h new file mode 100644 --- /dev/null +++ b/resources/google-groupware/googlesettings.h @@ -0,0 +1,77 @@ +/* + Copyright (C) 2013 Daniel Vrátil + 2020 Igor Poboiko + + 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 3 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GOOGLESETTINGS_H +#define GOOGLESETTINGS_H + +#include "settingsbase.h" + +#include +#include + +#include + +namespace KWallet { +class Wallet; +} + +/** + * @brief Settings object + * + * Provides read-only access to application clientId and + * clientSecret and read-write access to accessToken and + * refreshToken. Interacts with KWallet. + */ +class GoogleSettings : public SettingsBase +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.Akonadi.Google.ExtendedSettings") + +public: + GoogleSettings(); + void setWindowId(WId id); + void setResourceId(const QString &resourceIdentifier); + + QString appId() const; + QString clientId() const; + QString clientSecret() const; + + void addCalendar(const QString &calendar); + void addTaskList(const QString &taskList); + + KGAPI2::AccountPtr accountPtr(); + // Wallet + bool isReady() const; + bool storeAccount(KGAPI2::AccountPtr account); + void cleanup(); +Q_SIGNALS: + void accountReady(bool ready); + void accountChanged(); +private Q_SLOTS: + void slotWalletOpened(bool success); +private: + WId m_winId; + QString m_resourceId; + bool m_isReady = false; + KGAPI2::AccountPtr m_account; + QPointer m_wallet; + + KGAPI2::AccountPtr fetchAccountFromWallet(const QString &accountName); +}; + +#endif // GOOGLESETTINGS_H diff --git a/resources/google-groupware/googlesettings.cpp b/resources/google-groupware/googlesettings.cpp new file mode 100644 --- /dev/null +++ b/resources/google-groupware/googlesettings.cpp @@ -0,0 +1,183 @@ +/* + Copyright (C) 2011-2013 Dan Vratil + 2020 Igor Poboiko + + 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 3 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "googlesettings.h" +#include "settingsbase.h" +#include "googleresource_debug.h" + +#include +#include +#include + +using namespace KWallet; +using namespace KGAPI2; + +static const QString googleWalletFolder = QStringLiteral("Akonadi Google"); + +GoogleSettings::GoogleSettings() + : m_winId(0) +{ + m_wallet = Wallet::openWallet(Wallet::NetworkWallet(), + m_winId, Wallet::Asynchronous); + if (m_wallet) { + connect(m_wallet.data(), &Wallet::walletOpened, this, &GoogleSettings::slotWalletOpened); + } else { + qCWarning(GOOGLE_LOG) << "Failed to open wallet!"; + } +} + +void GoogleSettings::slotWalletOpened(bool success) +{ + if (!success) { + qCWarning(GOOGLE_LOG) << "Failed to open wallet!"; + Q_EMIT accountReady(false); + return; + } + + if (!m_wallet->hasFolder(googleWalletFolder) + && !m_wallet->createFolder(googleWalletFolder)) { + qCWarning(GOOGLE_LOG) << "Failed to create wallet folder" << googleWalletFolder; + Q_EMIT accountReady(false); + return; + } + + if (!m_wallet->setFolder(googleWalletFolder)) { + qWarning() << "Failed to open wallet folder" << googleWalletFolder; + Q_EMIT accountReady(false); + return; + } + qCDebug(GOOGLE_LOG) << "Wallet opened, reading" << account(); + if (!account().isEmpty()) { + m_account = fetchAccountFromWallet(account()); + } + m_isReady = true; + Q_EMIT accountReady(true); +} + +KGAPI2::AccountPtr GoogleSettings::fetchAccountFromWallet(const QString &accountName) +{ + if (!m_wallet->entryList().contains(accountName)) { + qCDebug(GOOGLE_LOG) << "Account" << accountName << "not found in KWallet"; + return AccountPtr(); + } + + QMap map; + m_wallet->readMap(accountName, map); + +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) + const QStringList scopes = map[QStringLiteral("scopes")].split(QLatin1Char(','), QString::SkipEmptyParts); +#else + const QStringList scopes = map[QStringLiteral("scopes")].split(QLatin1Char(','), Qt::SkipEmptyParts); +#endif + QList scopeUrls; + scopeUrls.reserve(scopes.count()); + for (const QString &scope : scopes) { + scopeUrls << QUrl(scope); + } + AccountPtr account(new Account(accountName, + map[QStringLiteral("accessToken")], + map[QStringLiteral("refreshToken")], + scopeUrls)); + return account; +} + +bool GoogleSettings::storeAccount(AccountPtr account) +{ + // Removing the old one (if present) + if (m_account && (account->accountName() != m_account->accountName())) { + cleanup(); + } + // Populating the new one + m_account = account; + + QStringList scopes; + const QList urlScopes = m_account->scopes(); + scopes.reserve(urlScopes.count()); + for (const QUrl &url : urlScopes) { + scopes << url.toString(); + } + + QMap map; + map[QStringLiteral("accessToken")] = m_account->accessToken(); + map[QStringLiteral("refreshToken")] = m_account->refreshToken(); + map[QStringLiteral("scopes")] = scopes.join(QLatin1Char(',')); + // Removing previous junk (if present) + cleanup(); + if (m_wallet->writeMap(m_account->accountName(), map) != 0) { + qCWarning(GOOGLE_LOG) << "Failed to write new account entry to wallet"; + return false; + } + SettingsBase::setAccount(m_account->accountName()); + m_isReady = true; + return true; +} + +void GoogleSettings::cleanup() +{ + if (m_account && m_wallet) { + m_wallet->removeEntry(m_account->accountName()); + } +} + +void GoogleSettings::addCalendar(const QString &calendar) +{ + if (calendars().isEmpty() || calendars().contains(calendar)) { + return; + } + setCalendars(calendars() << calendar); + save(); +} + +void GoogleSettings::addTaskList(const QString &taskList) +{ + if (calendars().isEmpty() || taskLists().contains(taskList)) { + return; + } + setTaskLists(taskLists() << taskList); + save(); +} + +QString GoogleSettings::clientId() const +{ + return QStringLiteral("554041944266.apps.googleusercontent.com"); +} + +QString GoogleSettings::clientSecret() const +{ + return QStringLiteral("mdT1DjzohxN3npUUzkENT0gO"); +} + +bool GoogleSettings::isReady() const +{ + return m_isReady; +} + +AccountPtr GoogleSettings::accountPtr() +{ + return m_account; +} + +void GoogleSettings::setWindowId(WId id) +{ + m_winId = id; +} + +void GoogleSettings::setResourceId(const QString &resourceIdentificator) +{ + m_resourceId = resourceIdentificator; +} diff --git a/resources/google-groupware/googlesettingsdialog.h b/resources/google-groupware/googlesettingsdialog.h new file mode 100644 --- /dev/null +++ b/resources/google-groupware/googlesettingsdialog.h @@ -0,0 +1,56 @@ +/* + Copyright (C) 2013 Daniel Vrátil + 2020 Igor Poboiko + + 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 3 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GOOGLESETTINGSDIALOG_H +#define GOOGLESETTINGSDIALOG_H + +#include +#include + +namespace Ui { + class GoogleSettingsDialog; +} +namespace KGAPI2 { + class Job; +} +class GoogleResource; +class GoogleSettings; + +class GoogleSettingsDialog : public QDialog +{ + Q_OBJECT +public: + explicit GoogleSettingsDialog(GoogleResource *resource, GoogleSettings *settings, WId wId); + ~GoogleSettingsDialog(); +protected: + bool handleError(KGAPI2::Job *job); + void accountChanged(); +private: + GoogleResource *m_resource; + GoogleSettings *m_settings; + Ui::GoogleSettingsDialog *m_ui = nullptr; + KGAPI2::AccountPtr m_account; +private Q_SLOTS: + void slotConfigure(); + void slotAuthJobFinished(KGAPI2::Job *job); + void slotSaveSettings(); + void slotReloadCalendars(); + void slotReloadTaskLists(); +}; + +#endif // GOOGLESETTINGSDIALOG_H diff --git a/resources/google-groupware/googlesettingsdialog.cpp b/resources/google-groupware/googlesettingsdialog.cpp new file mode 100644 --- /dev/null +++ b/resources/google-groupware/googlesettingsdialog.cpp @@ -0,0 +1,294 @@ +/* + Copyright (C) 2013 Daniel Vrátil + 2020 Igor Poboiko + + 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 3 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "googlesettingsdialog.h" +#include "ui_googlesettingsdialog.h" +#include "googlesettings.h" +#include "googleresource.h" +#include "googleresource_debug.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace KGAPI2; + +GoogleSettingsDialog::GoogleSettingsDialog(GoogleResource *resource, GoogleSettings *settings, WId wId) + : QDialog() + , m_resource(resource) + , m_settings(settings) +{ + if (wId) { + setAttribute(Qt::WA_NativeWindow, true); + KWindowSystem::setMainWindow(windowHandle(), wId); + } + QVBoxLayout *mainLayout = new QVBoxLayout(this); + + QWidget *mainWidget = new QWidget(this); + mainLayout->addWidget(mainWidget); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); + okButton->setDefault(true); + okButton->setShortcut(Qt::CTRL | Qt::Key_Return); + mainLayout->addWidget(buttonBox); + + m_ui = new Ui::GoogleSettingsDialog; + m_ui->setupUi(mainWidget); + + m_ui->refreshSpinBox->setSuffix(ki18np(" minute", " minutes")); + m_ui->enableRefresh->setChecked(m_settings->enableIntervalCheck()); + m_ui->refreshSpinBox->setEnabled(m_settings->enableIntervalCheck()); + if (m_settings->enableIntervalCheck()) { + m_ui->refreshSpinBox->setValue(m_settings->intervalCheckTime()); + } else { + m_ui->refreshSpinBox->setValue(30); + } + + m_ui->eventsLimitCombo->setMaximumDate(QDate::currentDate()); + m_ui->eventsLimitCombo->setMinimumDate(QDate::fromString(QStringLiteral("2000-01-01"), Qt::ISODate)); + m_ui->eventsLimitCombo->setOptions(KDateComboBox::EditDate | KDateComboBox::SelectDate + |KDateComboBox::DatePicker | KDateComboBox::WarnOnInvalid); + if (m_settings->eventsSince().isEmpty()) { + const QString ds = QStringLiteral("%1-01-01").arg(QString::number(QDate::currentDate().year() - 3)); + m_ui->eventsLimitCombo->setDate(QDate::fromString(ds, Qt::ISODate)); + } else { + m_ui->eventsLimitCombo->setDate(QDate::fromString(m_settings->eventsSince(), Qt::ISODate)); + } + + connect(buttonBox, &QDialogButtonBox::accepted, this, &GoogleSettingsDialog::slotSaveSettings); + connect(buttonBox, &QDialogButtonBox::rejected, this, &GoogleSettingsDialog::reject); + connect(m_ui->reloadCalendarsBtn, &QPushButton::clicked, this, &GoogleSettingsDialog::slotReloadCalendars); + connect(m_ui->reloadTaskListsBtn, &QPushButton::clicked, this, &GoogleSettingsDialog::slotReloadTaskLists); + connect(m_ui->configureBtn, &QPushButton::clicked, this, &GoogleSettingsDialog::slotConfigure); + if (m_settings->isReady()) { + m_account = m_settings->accountPtr(); + } + connect(m_settings, &GoogleSettings::accountReady, this, [this](bool ready){ + if (ready) { + m_account = m_settings->accountPtr(); + accountChanged(); + } + }); + QMetaObject::invokeMethod(this, &GoogleSettingsDialog::accountChanged, Qt::QueuedConnection); +} + +GoogleSettingsDialog::~GoogleSettingsDialog() +{ + delete m_ui; +} + +bool GoogleSettingsDialog::handleError(Job *job) +{ + if ((job->error() == KGAPI2::NoError) || (job->error() == KGAPI2::OK)) { + return true; + } + + if (job->error() == KGAPI2::Unauthorized) { + qCDebug(GOOGLE_LOG) << job << job->errorString(); + const QList resourceScopes = m_resource->scopes(); + for (const QUrl &scope : resourceScopes) { + if (!m_account->scopes().contains(scope)) { + m_account->addScope(scope); + } + } + + AuthJob *authJob = new AuthJob(m_account, m_settings->clientId(), + m_settings->clientSecret(), this); + authJob->setProperty(JOB_PROPERTY, QVariant::fromValue(job)); + connect(authJob, &AuthJob::finished, this, &GoogleSettingsDialog::slotAuthJobFinished); + + return false; + } + + KMessageBox::sorry(this, job->errorString()); + return false; +} + +void GoogleSettingsDialog::accountChanged() +{ + if (!m_account) { + m_ui->accountLabel->setText(i18n("not configured")); + m_ui->calendarsBox->setDisabled(true); + m_ui->calendarsList->clear(); + m_ui->taskListsBox->setDisabled(true); + m_ui->taskListsList->clear(); + return; + } + m_ui->accountLabel->setText(QStringLiteral("%1").arg(m_account->accountName())); + slotReloadCalendars(); + slotReloadTaskLists(); +} + +void GoogleSettingsDialog::slotConfigure() +{ + m_account = AccountPtr(new Account()); + const QList resourceScopes = m_resource->scopes(); + for (const QUrl &scope : resourceScopes) { + if (!m_account->scopes().contains(scope)) { + m_account->addScope(scope); + } + } + AuthJob *authJob = new AuthJob(m_account, + m_settings->clientId(), + m_settings->clientSecret()); + connect(authJob, &AuthJob::finished, this, &GoogleSettingsDialog::slotAuthJobFinished); +} + +void GoogleSettingsDialog::slotAuthJobFinished(Job *job) +{ + auto authJob = qobject_cast(job); + m_account = authJob->account(); + if (authJob->error() != KGAPI2::NoError) { + KMessageBox::sorry(this, authJob->errorString()); + return; + } + accountChanged(); + + auto otherJob = job->property(JOB_PROPERTY).value(); + if (otherJob) { + otherJob->setAccount(m_account); + otherJob->restart(); + } +} + +void GoogleSettingsDialog::slotSaveSettings() +{ + if (!m_account || !m_settings->storeAccount(m_account)) { + m_settings->setAccount(QString()); + m_settings->setEnableIntervalCheck(m_ui->enableRefresh->isChecked()); + m_settings->setIntervalCheckTime(m_ui->refreshSpinBox->value()); + m_settings->setCalendars({}); + m_settings->setTaskLists({}); + m_settings->setEventsSince(QString()); + m_settings->save(); + return; + } + m_settings->setAccount(m_account->accountName()); + m_settings->setEnableIntervalCheck(m_ui->enableRefresh->isChecked()); + m_settings->setIntervalCheckTime(m_ui->refreshSpinBox->value()); + + QStringList calendars; + for (int i = 0; i < m_ui->calendarsList->count(); i++) { + QListWidgetItem *item = m_ui->calendarsList->item(i); + + if (item->checkState() == Qt::Checked) { + calendars.append(item->data(Qt::UserRole).toString()); + } + } + m_settings->setCalendars(calendars); + + if (m_ui->eventsLimitCombo->isValid()) { + m_settings->setEventsSince(m_ui->eventsLimitCombo->date().toString(Qt::ISODate)); + } + + QStringList taskLists; + for (int i = 0; i < m_ui->taskListsList->count(); i++) { + QListWidgetItem *item = m_ui->taskListsList->item(i); + + if (item->checkState() == Qt::Checked) { + taskLists.append(item->data(Qt::UserRole).toString()); + } + } + m_settings->setTaskLists(taskLists); + m_settings->save(); + + accept(); +} + +void GoogleSettingsDialog::slotReloadCalendars() +{ + m_ui->calendarsBox->setDisabled(true); + m_ui->calendarsList->clear(); + + if (!m_account) { + return; + } + + auto fetchJob = new CalendarFetchJob(m_account, this); + connect(fetchJob, &CalendarFetchJob::finished, this, [this](Job *job){ + if (!handleError(job) || !m_account) { + m_ui->calendarsBox->setEnabled(false); + return; + } + + const ObjectsList objects = qobject_cast(job)->items(); + + QStringList activeCalendars; + if (m_account->accountName() == m_settings->account()) { + activeCalendars = m_settings->calendars(); + } + m_ui->calendarsList->clear(); + for (const ObjectPtr &object : objects) { + const CalendarPtr calendar = object.dynamicCast(); + + QListWidgetItem *item = new QListWidgetItem(calendar->title()); + item->setData(Qt::UserRole, calendar->uid()); + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable); + item->setCheckState((activeCalendars.isEmpty() || activeCalendars.contains(calendar->uid())) ? Qt::Checked : Qt::Unchecked); + m_ui->calendarsList->addItem(item); + } + + m_ui->calendarsBox->setEnabled(true); + }); +} + +void GoogleSettingsDialog::slotReloadTaskLists() +{ + if (!m_account) { + return; + } + + m_ui->taskListsBox->setDisabled(true); + m_ui->taskListsList->clear(); + + auto job = new TaskListFetchJob(m_account, this); + connect(job, &TaskListFetchJob::finished, this, [this](KGAPI2::Job *job){ + if (!handleError(job) || !m_account) { + m_ui->taskListsBox->setDisabled(true); + return; + } + + const ObjectsList objects = qobject_cast(job)->items(); + + QStringList activeTaskLists; + if (m_account->accountName() == m_settings->account()) { + activeTaskLists = m_settings->taskLists(); + } + m_ui->taskListsList->clear(); + for (const ObjectPtr &object : objects) { + const TaskListPtr taskList = object.dynamicCast(); + + QListWidgetItem *item = new QListWidgetItem(taskList->title()); + item->setData(Qt::UserRole, taskList->uid()); + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable); + item->setCheckState((activeTaskLists.isEmpty() || activeTaskLists.contains(taskList->uid())) ? Qt::Checked : Qt::Unchecked); + m_ui->taskListsList->addItem(item); + } + + m_ui->taskListsBox->setEnabled(true); + + }); +} diff --git a/resources/google-groupware/googlesettingsdialog.ui b/resources/google-groupware/googlesettingsdialog.ui new file mode 100644 --- /dev/null +++ b/resources/google-groupware/googlesettingsdialog.ui @@ -0,0 +1,182 @@ + + + GoogleSettingsDialog + + + + 0 + 0 + 584 + 680 + + + + + + + + + Account: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + <b>not confgured</b> + + + + + + + Configure... + + + + + + + + + Refresh + + + false + + + + + + Refresh interval: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + refreshSpinBox + + + + + + + 10 + + + 720 + + + 30 + + + + + + + Enable interval refresh + + + + + + + + + + Calendars + + + + + + + + + Fetch only events since + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + eventsLimitCombo + + + + + + + + + + Reload + + + + .. + + + + + + + + + + Tasklists + + + + + + + + + Reload + + + + .. + + + + + + + + + + + KPluralHandlingSpinBox + QSpinBox +
kpluralhandlingspinbox.h
+
+ + KDateComboBox + QComboBox +
kdatecombobox.h
+
+
+ + + + enableRefresh + toggled(bool) + refreshSpinBox + setEnabled(bool) + + + 182 + 99 + + + 312 + 128 + + + + +
diff --git a/resources/google-groupware/settingsbase.kcfg b/resources/google-groupware/settingsbase.kcfg new file mode 100644 --- /dev/null +++ b/resources/google-groupware/settingsbase.kcfg @@ -0,0 +1,37 @@ + + + + + + + + + + + 0 + + + + + + + + + + + + + + + false + + + 60 + + + diff --git a/resources/google-groupware/settingsbase.kcfgc b/resources/google-groupware/settingsbase.kcfgc new file mode 100644 --- /dev/null +++ b/resources/google-groupware/settingsbase.kcfgc @@ -0,0 +1,6 @@ +File=settingsbase.kcfg +ClassName=SettingsBase +Mutators=true +ItemAccessors=true +Singleton=false +GlobalEnums=true diff --git a/resources/google-groupware/taskhandler.h b/resources/google-groupware/taskhandler.h new file mode 100644 --- /dev/null +++ b/resources/google-groupware/taskhandler.h @@ -0,0 +1,53 @@ +/* + Copyright (C) 2011-2013 Daniel Vrátil + 2020 Igor Pobiko + + 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 3 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef TASKHANDLER_H +#define TASKHANDLER_H + +#include "generichandler.h" +#include + + +class TaskHandler : public GenericHandler +{ +public: + using GenericHandler::GenericHandler; + + QString mimetype() override; + bool canPerformTask(const Akonadi::Item &item) override; + + void retrieveCollections() override; + void retrieveItems(const Akonadi::Collection &collection) override; + + void itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) override; + void itemChanged(const Akonadi::Item &item, const QSet< QByteArray > &partIdentifiers) override; + void itemsRemoved(const Akonadi::Item::List &items) override; + void itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &collectionSource, const Akonadi::Collection &collectionDestination) override; + + void collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent) override; + void collectionChanged(const Akonadi::Collection &collection) override; + void collectionRemoved(const Akonadi::Collection &collection) override; +private Q_SLOTS: + void slotCollectionsRetrieved(KGAPI2::Job *job); + void slotItemsRetrieved(KGAPI2::Job *job); +private: + void setupCollection(Akonadi::Collection &colleciton, const KGAPI2::TaskListPtr &taskList); + void doRemoveTasks(const Akonadi::Item::List &items); +}; + +#endif // TASKHANDLER_H diff --git a/resources/google-groupware/taskhandler.cpp b/resources/google-groupware/taskhandler.cpp new file mode 100644 --- /dev/null +++ b/resources/google-groupware/taskhandler.cpp @@ -0,0 +1,359 @@ +/* + Copyright (C) 2011-2013 Daniel Vrátil + 2020 Igor Poboiko + + 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 3 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "taskhandler.h" +#include "googleresource.h" +#include "googlesettings.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "googletasks_debug.h" + +#define TASK_PROPERTY "_KGAPI2::TaskPtr" + +using namespace KGAPI2; +using namespace Akonadi; + +QString TaskHandler::mimetype() +{ + return KCalendarCore::Todo::todoMimeType(); +} + +bool TaskHandler::canPerformTask(const Item &item) +{ + return m_resource->canPerformTask(item, mimetype()); +} + +void TaskHandler::setupCollection(Collection &collection, const TaskListPtr &taskList) +{ + collection.setContentMimeTypes({ mimetype() }); + collection.setName(taskList->uid()); + collection.setParentCollection(m_resource->rootCollection()); + collection.setRemoteId(taskList->uid()); + collection.setRights(Collection::CanChangeCollection + |Collection::CanCreateItem + |Collection::CanChangeItem + |Collection::CanDeleteItem); + + EntityDisplayAttribute *attr = collection.attribute(Collection::AddIfMissing); + attr->setDisplayName(taskList->title()); + attr->setIconName(QStringLiteral("view-pim-tasks")); +} + +void TaskHandler::retrieveCollections() +{ + Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Retrieving task lists")); + qCDebug(GOOGLE_TASKS_LOG) << "Retrieving tasks..."; + auto job = new TaskListFetchJob(m_settings->accountPtr(), this); + connect(job, &TaskListFetchJob::finished, this, &TaskHandler::slotCollectionsRetrieved); +} + +void TaskHandler::slotCollectionsRetrieved(KGAPI2::Job *job) +{ + if (!m_resource->handleError(job)) { + return; + } + qCDebug(GOOGLE_TASKS_LOG) << "Task lists retrieved"; + + const ObjectsList taskLists = qobject_cast(job)->items(); + const QStringList activeTaskLists = m_settings->taskLists(); + Collection::List collections; + for (const ObjectPtr &object : taskLists) { + const TaskListPtr &taskList = object.dynamicCast(); + qCDebug(GOOGLE_TASKS_LOG) << " -" << taskList->title() << "(" << taskList->uid() << ")"; + + if (!activeTaskLists.contains(taskList->uid())) { + qCDebug(GOOGLE_TASKS_LOG) << "Skipping, not subscribed"; + continue; + } + + Collection collection; + setupCollection(collection, taskList); + collections << collection; + } + + m_resource->collectionsRetrievedFromHandler(collections); +} + +void TaskHandler::retrieveItems(const Collection &collection) +{ + Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Retrieving tasks for list '%1'", collection.displayName())); + qCDebug(GOOGLE_TASKS_LOG) << "Retrieving tasks for list" << collection.remoteId(); + // https://bugs.kde.org/show_bug.cgi?id=308122: we can only request changes in + // max. last 25 days, otherwise we get an error. + int lastSyncDelta = -1; + if (!collection.remoteRevision().isEmpty()) { + lastSyncDelta = QDateTime::currentDateTimeUtc().toSecsSinceEpoch() - collection.remoteRevision().toULongLong(); + } + + auto job = new TaskFetchJob(collection.remoteId(), m_settings->accountPtr(), this); + if (lastSyncDelta > -1 && lastSyncDelta < 25 * 25 * 3600) { + job->setFetchOnlyUpdated(collection.remoteRevision().toULongLong()); + job->setFetchDeleted(true); + } else { + // No need to fetch deleted items for non-incremental update + job->setFetchDeleted(false); + } + job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection)); + connect(job, &TaskFetchJob::finished, this, &TaskHandler::slotItemsRetrieved); +} + +void TaskHandler::slotItemsRetrieved(KGAPI2::Job *job) +{ + if (!m_resource->handleError(job)) { + return; + } + Item::List changedItems, removedItems; + + const ObjectsList &objects = qobject_cast(job)->items(); + Collection collection = job->property(COLLECTION_PROPERTY).value(); + bool isIncremental = (qobject_cast(job)->fetchOnlyUpdated() > 0); + qCDebug(GOOGLE_TASKS_LOG) << "Retrieved" << objects.count() << "tasks for list" << collection.remoteId(); + for (const auto &object : objects) { + const TaskPtr task = object.dynamicCast(); + + Item item; + item.setMimeType(KCalendarCore::Todo::todoMimeType()); + item.setParentCollection(collection); + item.setRemoteId(task->uid()); + item.setRemoteRevision(task->etag()); + item.setPayload(task.dynamicCast()); + + if (task->deleted()) { + qCDebug(GOOGLE_TASKS_LOG) << " - removed" << task->uid(); + removedItems << item; + } else { + qCDebug(GOOGLE_TASKS_LOG) << " - changed" << task->uid(); + changedItems << item; + } + } + + if (isIncremental) { + m_resource->itemsRetrievedIncremental(changedItems, removedItems); + } else { + m_resource->itemsRetrieved(changedItems); + } + const QDateTime local(QDateTime::currentDateTime()); + const QDateTime UTC(local.toUTC()); + + collection.setRemoteRevision(QString::number(UTC.toSecsSinceEpoch())); + new CollectionModifyJob(collection, this); + + m_resource->emitReadyStatus(); +} + +void TaskHandler::itemAdded(const Item &item, const Collection &collection) +{ + Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Adding event to calendar '%1'", collection.displayName())); + KCalendarCore::Todo::Ptr todo = item.payload(); + TaskPtr task(new Task(*todo)); + const QString parentRemoteId = task->relatedTo(KCalendarCore::Incidence::RelTypeParent); + qCDebug(GOOGLE_TASKS_LOG) << "Task added to list" << collection.remoteId() << "with parent" << parentRemoteId; + auto job = new TaskCreateJob(task, item.parentCollection().remoteId(), m_settings->accountPtr(), this); + job->setParentItem(parentRemoteId); + connect(job, &TaskCreateJob::finished, this, [this, item](KGAPI2::Job *job){ + if (!m_resource->handleError(job)) { + return; + } + Item newItem = item; + const TaskPtr task = qobject_cast(job)->items().first().dynamicCast(); + qCDebug(GOOGLE_TASKS_LOG) << "Task added"; + newItem.setRemoteId(task->uid()); + newItem.setRemoteRevision(task->etag()); + newItem.setGid(task->uid()); + m_resource->changeCommitted(newItem); + newItem.setPayload(task.dynamicCast()); + new ItemModifyJob(newItem, this); + m_resource->emitReadyStatus(); + }); +} + +void TaskHandler::itemChanged(const Item &item, const QSet< QByteArray > &partIdentifiers) +{ + Q_UNUSED(partIdentifiers); + Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Changing task in list '%1'", item.parentCollection().displayName())); + qCDebug(GOOGLE_TASKS_LOG) << "Changing task" << item.remoteId(); + + KCalendarCore::Todo::Ptr todo = item.payload(); + const QString parentUid = todo->relatedTo(KCalendarCore::Incidence::RelTypeParent); + // First we move it to a new parent, if there is + auto job = new TaskMoveJob(item.remoteId(), item.parentCollection().remoteId(), parentUid, m_settings->accountPtr(), this); + connect(job, &TaskMoveJob::finished, this, [this, todo, item](KGAPI2::Job *job){ + if (!m_resource->handleError(job)) { + return; + } + TaskPtr task(new Task(*todo)); + auto newJob = new TaskModifyJob(task, item.parentCollection().remoteId(), job->account(), this); + newJob->setProperty(ITEM_PROPERTY, QVariant::fromValue(item)); + connect(newJob, &TaskModifyJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); + }); +} + +void TaskHandler::itemsRemoved(const Item::List &items) +{ + Q_EMIT m_resource->status(AgentBase::Running, i18ncp("@info:status", "Removing %1 tasks", "Removing %1 task", items.count())); + qCDebug(GOOGLE_TASKS_LOG) << "Removing" << items.count() << "tasks"; + /* Google always automatically removes tasks with all their subtasks. In KOrganizer + * by default we only remove the item we are given. For this reason we have to first + * fetch all tasks, find all sub-tasks for the task being removed and detach them + * from the task. Only then the task can be safely removed. */ + // TODO: what if items belong to different collections? + auto job = new ItemFetchJob(items.first().parentCollection()); + job->fetchScope().fetchFullPayload(true); + connect(job, &ItemFetchJob::finished, this, [this, items](KJob *job){ + if (job->error()) { + m_resource->cancelTask(i18n("Failed to delete task: %1", job->errorString())); + return; + } + const Item::List fetchedItems = qobject_cast(job)->items(); + Item::List detachItems; + TasksList detachTasks; + for (const Item &fetchedItem : fetchedItems) { + auto todo = fetchedItem.payload(); + TaskPtr task(new Task(*todo)); + const QString parentId = task->relatedTo(KCalendarCore::Incidence::RelTypeParent); + if (parentId.isEmpty()) { + continue; + } + + auto it = std::find_if(items.cbegin(), items.cend(), [&parentId](const Item &item){ + return item.remoteId() == parentId; + }); + if (it != items.cend()) { + Item newItem(fetchedItem); + qCDebug(GOOGLE_TASKS_LOG) << "Detaching child" << newItem.remoteId() << "from" << parentId; + todo->setRelatedTo(QString(), KCalendarCore::Incidence::RelTypeParent); + newItem.setPayload(todo); + detachItems << newItem; + detachTasks << task; + } + } + /* If there are no items do detach, then delete the task right now */ + if (detachItems.isEmpty()) { + doRemoveTasks(items); + return; + } + + qCDebug(GOOGLE_TASKS_LOG) << "Reparenting" << detachItems.count() << "children..."; + auto moveJob = new TaskMoveJob(detachTasks, items.first().parentCollection().remoteId(), + QString(), m_settings->accountPtr(), this); + connect(moveJob, &TaskMoveJob::finished, this, [this, items, detachItems](KGAPI2::Job *job){ + if (job->error()) { + m_resource->cancelTask(i18n("Failed to reparent subtasks: %1", job->errorString())); + return; + } + // Update items inside Akonadi DB too + new ItemModifyJob(detachItems); + // Perform actual removal + doRemoveTasks(items); + }); + }); +} + +void TaskHandler::doRemoveTasks(const Item::List &items) +{ + // Make sure account is still valid + if (!m_resource->canPerformTask()) { + return; + } + QStringList taskIds; + taskIds.reserve(items.count()); + std::transform(items.cbegin(), items.cend(), std::back_inserter(taskIds), + [](const Item &item){ + return item.remoteId(); + }); + + /* Now finally we can safely remove the task we wanted to */ + // TODO: what if tasks are deleted from different collections? + auto job = new TaskDeleteJob(taskIds, items.first().parentCollection().remoteId(), m_settings->accountPtr(), this); + job->setProperty(ITEMS_PROPERTY, QVariant::fromValue(items)); + connect(job, &TaskDeleteJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); +} + +void TaskHandler::itemsMoved(const Item::List &/*item*/, const Collection &/*collectionSource*/, const Collection &/*collectionDestination*/) +{ + m_resource->cancelTask(i18n("Moving tasks between task lists is not supported")); +} + +void TaskHandler::collectionAdded(const Collection &collection, const Collection &/*parent*/) +{ + Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Creating new task list '%1'", collection.displayName())); + qCDebug(GOOGLE_TASKS_LOG) << "Adding task list" << collection.displayName(); + TaskListPtr taskList(new TaskList()); + taskList->setTitle(collection.displayName()); + + auto job = new TaskListCreateJob(taskList, m_settings->accountPtr(), this); + connect(job, &TaskListCreateJob::finished, this, [this, collection](KGAPI2::Job *job){ + if (!m_resource->handleError(job)) { + return; + } + + TaskListPtr taskList = qobject_cast(job)->items().first().dynamicCast(); + qCDebug(GOOGLE_TASKS_LOG) << "Task list created:" << taskList->uid(); + // Enable newly added task list in settings + m_settings->addTaskList(taskList->uid()); + // Populate remoteId & other stuff + Collection newCollection(collection); + setupCollection(newCollection, taskList); + m_resource->changeCommitted(newCollection); + m_resource->emitReadyStatus(); + }); +} + +void TaskHandler::collectionChanged(const Collection &collection) +{ + Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Changing task list '%1'", collection.displayName())); + qCDebug(GOOGLE_TASKS_LOG) << "Changing task list" << collection.remoteId(); + + TaskListPtr taskList(new TaskList()); + taskList->setUid(collection.remoteId()); + taskList->setTitle(collection.displayName()); + auto job = new TaskListModifyJob(taskList, m_settings->accountPtr(), this); + job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection)); + connect(job, &TaskListModifyJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); +} + +void TaskHandler::collectionRemoved(const Collection &collection) +{ + Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Removing task list '%1'", collection.displayName())); + qCDebug(GOOGLE_TASKS_LOG) << "Removing task list" << collection.remoteId(); + auto job = new TaskListDeleteJob(collection.remoteId(), m_settings->accountPtr(), this); + job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection)); + connect(job, &TaskListDeleteJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); +} diff --git a/resources/google/calendar/CMakeLists.txt b/resources/google/calendar/CMakeLists.txt --- a/resources/google/calendar/CMakeLists.txt +++ b/resources/google/calendar/CMakeLists.txt @@ -3,74 +3,70 @@ set(calendarresource_SRCS - calendarresource.cpp - defaultreminderattribute.cpp - settings.cpp - settingsdialog.cpp - ../common/googleresource.cpp - ../common/googleaccountmanager.cpp - ../common/googlesettings.cpp - ../common/googlesettingsdialog.cpp - ../common/kgapiversionattribute.cpp - ${accounts_SRCS} -) -if (ECM_VERSION VERSION_LESS "5.68.0") - ecm_qt_declare_logging_category(calendarresource_SRCS HEADER googlecalendarresource_debug.h IDENTIFIER GOOGLE_CALENDAR_LOG CATEGORY_NAME org.kde.pim.google.calendar) -else() - ecm_qt_declare_logging_category(calendarresource_SRCS HEADER googlecalendarresource_debug.h IDENTIFIER GOOGLE_CALENDAR_LOG CATEGORY_NAME org.kde.pim.google.calendar - DESCRIPTION "resource google calendar (kdepim-runtime)" - EXPORT KDEPIMRUNTIME + calendarresource.cpp + defaultreminderattribute.cpp + settings.cpp + settingsdialog.cpp + ../common/googleresource.cpp + ../common/googleaccountmanager.cpp + ../common/googlesettings.cpp + ../common/googlesettingsdialog.cpp + ../common/kgapiversionattribute.cpp + ${accounts_SRCS} + ) +ecm_qt_declare_logging_category(calendarresource_SRCS HEADER googlecalendarresource_debug.h IDENTIFIER GOOGLE_CALENDAR_LOG CATEGORY_NAME org.kde.pim.google.calendar + DESCRIPTION "resource google calendar (kdepim-runtime)" + EXPORT KDEPIMRUNTIME ) -endif() kconfig_add_kcfg_files(calendarresource_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/settingsbase.kcfgc) kcfg_generate_dbus_interface( - ${CMAKE_CURRENT_SOURCE_DIR}/settingsbase.kcfg - org.kde.Akonadi.GoogleCalendar.Settings -) + ${CMAKE_CURRENT_SOURCE_DIR}/settingsbase.kcfg + org.kde.Akonadi.GoogleCalendar.Settings + ) qt5_add_dbus_adaptor(calendarresource_SRCS - ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.GoogleCalendar.Settings.xml - ${CMAKE_CURRENT_SOURCE_DIR}/settings.h Settings -) + ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.GoogleCalendar.Settings.xml + ${CMAKE_CURRENT_SOURCE_DIR}/settings.h Settings + ) add_executable(akonadi_googlecalendar_resource ${calendarresource_SRCS}) if( APPLE ) - set_target_properties(akonadi_googlecalendar_resource PROPERTIES - MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/../../Info.plist.template - ) - set_target_properties(akonadi_googlecalendar_resource PROPERTIES - MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.Akonadi.googlecalendar" - ) - set_target_properties(akonadi_googlecalendar_resource PROPERTIES - MACOSX_BUNDLE_BUNDLE_NAME "KDE Akonadi Google Calendar Resource" - ) + set_target_properties(akonadi_googlecalendar_resource PROPERTIES + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/../../Info.plist.template + ) + set_target_properties(akonadi_googlecalendar_resource PROPERTIES + MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.Akonadi.googlecalendar" + ) + set_target_properties(akonadi_googlecalendar_resource PROPERTIES + MACOSX_BUNDLE_BUNDLE_NAME "KDE Akonadi Google Calendar Resource" + ) endif() target_link_libraries(akonadi_googlecalendar_resource - KF5::AkonadiCore - KF5::CalendarCore - KF5::AkonadiCalendar - KPim::GAPICalendar - KPim::GAPICore - KPim::GAPITasks - KF5::AkonadiAgentBase - KF5::Wallet - KF5::I18n - KF5::WindowSystem - KF5::Completion - KF5::TextWidgets -) + KF5::AkonadiCore + KF5::CalendarCore + KF5::AkonadiCalendar + KPim::GAPICalendar + KPim::GAPICore + KPim::GAPITasks + KF5::AkonadiAgentBase + KF5::Wallet + KF5::I18n + KF5::WindowSystem + KF5::Completion + KF5::TextWidgets + ) install(TARGETS akonadi_googlecalendar_resource ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install( - FILES googlecalendarresource.desktop - DESTINATION "${KDE_INSTALL_DATAROOTDIR}/akonadi/agents" -) + FILES googlecalendarresource.desktop + DESTINATION "${KDE_INSTALL_DATAROOTDIR}/akonadi/agents" + ) diff --git a/resources/google/contacts/contactsresource.cpp b/resources/google/contacts/contactsresource.cpp --- a/resources/google/contacts/contactsresource.cpp +++ b/resources/google/contacts/contactsresource.cpp @@ -375,9 +375,9 @@ collection.setRemoteId(group->id()); collection.setVirtual(true); - EntityDisplayAttribute *attr = collection.attribute(Collection::AddIfMissing); - attr->setDisplayName(realName); - attr->setIconName(QStringLiteral("view-pim-contacts")); + EntityDisplayAttribute *collAttr = collection.attribute(Collection::AddIfMissing); + collAttr->setDisplayName(realName); + collAttr->setIconName(QStringLiteral("view-pim-contacts")); m_collections[ collection.remoteId() ] = collection; } @@ -523,6 +523,14 @@ item.setRemoteId(contact->uid()); item.setRemoteRevision(contact->etag()); changeCommitted(item); + /** + * An Addressee inside Akonadi DB has a default UID, which differs from + * one obtained from Google, so we end up having a confision between remoteId + * and UID. Since changeCommitted does not update the payload, we need to + * update it here explicitly. + */ + item.setPayload(*contact.dynamicCast()); + new ItemModifyJob(item); } else if (collection.isValid()) { ContactsGroupCreateJob *createJob = qobject_cast(job); Q_ASSERT(createJob->items().count() == 1); diff --git a/resources/icaldir/icaldirresource.desktop b/resources/icaldir/icaldirresource.desktop --- a/resources/icaldir/icaldirresource.desktop +++ b/resources/icaldir/icaldirresource.desktop @@ -55,6 +55,7 @@ Comment[ru]=Обеспечивает доступ к элементам календаря, каждый из которых хранится в отдельном файле в указанном каталоге Comment[se]=Dáinna beasat kaleandarmerkošidda, vurkejuvvon okta fiilan, dihto katalogas Comment[sk]=Poskytuje prístup k položkám kalendára, každý uložený v jednom súbore, v danom adresári +Comment[sl]=Nudi dostop do koledarskih vnosov, ki so shranjeni vsak posebej v svoji datoteki, v dani mapi Comment[sv]=Ger tillgång till kalenderobjekt, vart och ett lagrat i en enda fil i en angiven katalog Comment[uk]=Надає доступ до записів календаря, кожен з яких зберігається у окремому файлі у вказаному каталозі Comment[x-test]=xxProvides access to calendar items, each stored in a single file, in a given directoryxx diff --git a/resources/imap/CMakeLists.txt b/resources/imap/CMakeLists.txt --- a/resources/imap/CMakeLists.txt +++ b/resources/imap/CMakeLists.txt @@ -45,12 +45,17 @@ ${AKONADI_IMAPATTRIBUTES_SHARED_SOURCES} ) - -ecm_qt_declare_logging_category(imapresource_LIB_SRCS HEADER imapresource_debug.h IDENTIFIER IMAPRESOURCE_LOG CATEGORY_NAME org.kde.pim.imapresource) +ecm_qt_declare_logging_category(imapresource_LIB_SRCS HEADER imapresource_debug.h IDENTIFIER IMAPRESOURCE_LOG CATEGORY_NAME org.kde.pim.imapresource + DESCRIPTION "imap resource (kdepim-runtime)" + OLD_CATEGORY_NAMES log_imapresource + EXPORT KDEPIMRUNTIME + ) ecm_qt_declare_logging_category(imapresource_LIB_SRCS HEADER imapresource_trace.h IDENTIFIER IMAPRESOURCE_TRACE CATEGORY_NAME org.kde.pim.imapresource.trace -) + DESCRIPTION "resource kolab trace (kdepim-runtime)" + EXPORT KDEPIMRUNTIME + ) kcfg_generate_dbus_interface( ${CMAKE_CURRENT_SOURCE_DIR}/imapresource.kcfg org.kde.Akonadi.Imap.Settings ) kconfig_add_kcfg_files(imapresource_LIB_SRCS settingsbase.kcfgc) diff --git a/resources/kalarm/kalarm/CMakeLists.txt b/resources/kalarm/kalarm/CMakeLists.txt --- a/resources/kalarm/kalarm/CMakeLists.txt +++ b/resources/kalarm/kalarm/CMakeLists.txt @@ -2,7 +2,7 @@ ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../shared ${CMAKE_CURRENT_SOURCE_DIR}/../../ical/shared -) + ) set(kalarmresource_common_SRCS) kconfig_add_kcfg_files(kalarmresource_common_SRCS settings.kcfgc) @@ -13,34 +13,30 @@ # Fix a race condition. alarmtyperadiowidget.cpp is used by two targets, we have # to be sure the ui file was generated before building them. set_source_files_properties( - ../shared/alarmtyperadiowidget.cpp - PROPERTIES OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/ui_alarmtyperadiowidget.h -) + ../shared/alarmtyperadiowidget.cpp + PROPERTIES OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/ui_alarmtyperadiowidget.h + ) set(kalarmresource_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../../ical/shared/icalresourcebase.cpp kalarmresource.cpp ../shared/kalarmresourcecommon.cpp ../shared/alarmtyperadiowidget.cpp ${kalarmresource_common_SRCS} -) + ) install(FILES kalarmresource.desktop DESTINATION "${KDE_INSTALL_DATAROOTDIR}/akonadi/agents") ki18n_wrap_ui(kalarmresource_SRCS ../shared/alarmtyperadiowidget.ui) kcfg_generate_dbus_interface(${CMAKE_CURRENT_SOURCE_DIR}/kalarmresource.kcfg org.kde.Akonadi.KAlarm.Settings) qt5_add_dbus_adaptor(kalarmresource_SRCS ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.KAlarm.Settings.xml settings.h Akonadi_KAlarm_Resource::Settings icalsettingsadaptor ICalSettingsAdaptor) -if (ECM_VERSION VERSION_LESS "5.68.0") - ecm_qt_declare_logging_category(kalarmresource_SRCS HEADER kalarmresource_debug.h IDENTIFIER KALARMRESOURCE_LOG CATEGORY_NAME org.kde.pim.kalarmresource) -else() - ecm_qt_declare_logging_category(kalarmresource_SRCS HEADER kalarmresource_debug.h IDENTIFIER KALARMRESOURCE_LOG CATEGORY_NAME org.kde.pim.kalarmresource - DESCRIPTION "kalarm file resource (kdepim-runtime)" - OLD_CATEGORY_NAMES log_kalarmresource - EXPORT KDEPIMRUNTIME +ecm_qt_declare_logging_category(kalarmresource_SRCS HEADER kalarmresource_debug.h IDENTIFIER KALARMRESOURCE_LOG CATEGORY_NAME org.kde.pim.kalarmresource + DESCRIPTION "kalarm file resource (kdepim-runtime)" + OLD_CATEGORY_NAMES log_kalarmresource + EXPORT KDEPIMRUNTIME ) -endif() add_custom_target(kalarm_resource_xml ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.KAlarm.Settings.xml) @@ -53,14 +49,14 @@ endif () target_link_libraries(akonadi_kalarm_resource - KF5::AlarmCalendar - KF5::AkonadiCore - KF5::CalendarCore - KF5::KIOCore - KF5::AkonadiAgentBase - KF5::I18n - akonadi-singlefileresource - ) + KF5::AlarmCalendar + KF5::AkonadiCore + KF5::CalendarCore + KF5::KIOCore + KF5::AkonadiAgentBase + KF5::I18n + akonadi-singlefileresource + ) install(TARGETS akonadi_kalarm_resource ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) @@ -71,16 +67,16 @@ kalarmconfig.cpp ../shared/alarmtyperadiowidget.cpp ${kalarmresource_common_SRCS} -) + ) kcoreaddons_add_plugin(kalarmconfig SOURCES ${kalarmconfig_SRCS} JSON "kalarmconfig.json" INSTALL_NAMESPACE "akonadi/config" -) + ) target_link_libraries(kalarmconfig KF5::AkonadiCore KF5::AlarmCalendar akonadi-singlefileresource -) + ) diff --git a/resources/kalarm/kalarmdir/CMakeLists.txt b/resources/kalarm/kalarmdir/CMakeLists.txt --- a/resources/kalarm/kalarmdir/CMakeLists.txt +++ b/resources/kalarm/kalarmdir/CMakeLists.txt @@ -1,7 +1,7 @@ include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../shared -) + ) ########### next target ############### add_definitions(-DSETTINGS_NAMESPACE=Akonadi_KAlarm_Dir_Resource) @@ -12,7 +12,7 @@ kalarmdirresource.cpp ../shared/kalarmresourcecommon.cpp ../shared/alarmtypewidget.cpp -) + ) install(FILES kalarmdirresource.desktop DESTINATION "${KDE_INSTALL_DATAROOTDIR}/akonadi/agents") @@ -22,15 +22,11 @@ qt5_add_dbus_adaptor(kalarmdirresource_SRCS ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.KAlarmDir.Settings.xml settings.h Akonadi_KAlarm_Dir_Resource::Settings kalarmdirsettingsadaptor KAlarmDirSettingsAdaptor) -if (ECM_VERSION VERSION_LESS "5.68.0") - ecm_qt_declare_logging_category(kalarmdirresource_SRCS HEADER kalarmdirresource_debug.h IDENTIFIER KALARMDIRRESOURCE_LOG CATEGORY_NAME org.kde.pim.kalarmdirresource) -else() - ecm_qt_declare_logging_category(kalarmdirresource_SRCS HEADER kalarmdirresource_debug.h IDENTIFIER KALARMDIRRESOURCE_LOG CATEGORY_NAME org.kde.pim.kalarmdirresource - DESCRIPTION "kalarm directory resource (kdepim-runtime)" - OLD_CATEGORY_NAMES log_kalarmdirresource - EXPORT KDEPIMRUNTIME +ecm_qt_declare_logging_category(kalarmdirresource_SRCS HEADER kalarmdirresource_debug.h IDENTIFIER KALARMDIRRESOURCE_LOG CATEGORY_NAME org.kde.pim.kalarmdirresource + DESCRIPTION "kalarm directory resource (kdepim-runtime)" + OLD_CATEGORY_NAMES log_kalarmdirresource + EXPORT KDEPIMRUNTIME ) -endif() add_custom_target(kalarmdir_resource_xml ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.KAlarmDir.Settings.xml) @@ -44,14 +40,14 @@ endif () target_link_libraries(akonadi_kalarm_dir_resource - KF5::KIOCore - KF5::KIOWidgets - KF5::ConfigWidgets - KF5::WindowSystem - KF5::AlarmCalendar - KF5::AkonadiCore - KF5::CalendarCore - KF5::AkonadiAgentBase - ) + KF5::KIOCore + KF5::KIOWidgets + KF5::ConfigWidgets + KF5::WindowSystem + KF5::AlarmCalendar + KF5::AkonadiCore + KF5::CalendarCore + KF5::AkonadiAgentBase + ) install(TARGETS akonadi_kalarm_dir_resource ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/resources/kalarm/kalarmdir/kalarmdirresource.cpp b/resources/kalarm/kalarmdir/kalarmdirresource.cpp --- a/resources/kalarm/kalarmdir/kalarmdirresource.cpp +++ b/resources/kalarm/kalarmdir/kalarmdirresource.cpp @@ -63,9 +63,9 @@ #define DEBUG_DATA(func) \ qCDebug(KALARMDIRRESOURCE_LOG)<setBySeconds(QVector::fromStdVector(rrule.bysecond()).toList()); +#else + const std::vector bySecond = rrule.bysecond(); + const QVector stdVector = QVector(bySecond.begin(), bySecond.end()); + defaultRR->setBySeconds(stdVector.toList()); +#endif } if (!rrule.byminute().empty()) { +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) defaultRR->setByMinutes(QVector::fromStdVector(rrule.byminute()).toList()); +#else + const std::vector byMinutes = rrule.byminute(); + const QVector stdVector = QVector(byMinutes.begin(), byMinutes.end()); + defaultRR->setByMinutes(stdVector.toList()); +#endif + } if (!rrule.byhour().empty()) { +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) defaultRR->setByHours(QVector::fromStdVector(rrule.byhour()).toList()); +#else + const std::vector byHours = rrule.byhour(); + const QVector stdVector = QVector(byHours.begin(), byHours.end()); + defaultRR->setByHours(stdVector.toList()); +#endif + } if (!rrule.byday().empty()) { QList daypos; @@ -547,16 +567,41 @@ defaultRR->setByDays(daypos); } if (!rrule.bymonthday().empty()) { +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) defaultRR->setByMonthDays(QVector::fromStdVector(rrule.bymonthday()).toList()); +#else + const std::vector byMonthDays = rrule.bymonthday(); + const QVector stdVector = QVector(byMonthDays.begin(), byMonthDays.end()); + defaultRR->setByMonthDays(stdVector.toList()); +#endif } if (!rrule.byyearday().empty()) { +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) defaultRR->setByYearDays(QVector::fromStdVector(rrule.byyearday()).toList()); +#else + const std::vector byYearDays = rrule.byyearday(); + const QVector stdVector = QVector(byYearDays.begin(), byYearDays.end()); + defaultRR->setByYearDays(stdVector.toList()); +#endif } if (!rrule.byweekno().empty()) { +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) defaultRR->setByWeekNumbers(QVector::fromStdVector(rrule.byweekno()).toList()); +#else + const std::vector byWeekNumbers = rrule.byweekno(); + const QVector stdVector = QVector(byWeekNumbers.begin(), byWeekNumbers.end()); + defaultRR->setByWeekNumbers(stdVector.toList()); +#endif } if (!rrule.bymonth().empty()) { +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) defaultRR->setByMonths(QVector::fromStdVector(rrule.bymonth()).toList()); +#else + const std::vector byMonths = rrule.bymonth(); + const QVector stdVector = QVector(byMonths.begin(), byMonths.end()); + defaultRR->setByMonths(stdVector.toList()); +#endif + } } foreach (const Kolab::cDateTime &dt, event.recurrenceDates()) { diff --git a/resources/kolab/pimkolab/freebusy/freebusy.cpp b/resources/kolab/pimkolab/freebusy/freebusy.cpp --- a/resources/kolab/pimkolab/freebusy/freebusy.cpp +++ b/resources/kolab/pimkolab/freebusy/freebusy.cpp @@ -157,8 +157,6 @@ Q_ASSERT(tmpEnd.isValid()); if (allDay) { tmpStart.setTime(QTime(0, 0, 0, 0)); - } - if (allDay) { tmpEnd.setTime(QTime(23, 59, 59, 999)); //The window is inclusive } return Kolab::Period(Kolab::Conversion::fromDate(tmpStart, allDay), Kolab::Conversion::fromDate(tmpEnd, allDay)); diff --git a/resources/maildir/CMakeLists.txt b/resources/maildir/CMakeLists.txt --- a/resources/maildir/CMakeLists.txt +++ b/resources/maildir/CMakeLists.txt @@ -26,15 +26,11 @@ ${maildir_common_SRCS} ) -if (ECM_VERSION VERSION_LESS "5.68.0") - ecm_qt_declare_logging_category(maildirresource_SRCS HEADER maildirresource_debug.h IDENTIFIER MAILDIRRESOURCE_LOG CATEGORY_NAME org.kde.pim.maildirresource) -else() - ecm_qt_declare_logging_category(maildirresource_SRCS HEADER maildirresource_debug.h IDENTIFIER MAILDIRRESOURCE_LOG CATEGORY_NAME org.kde.pim.maildirresource +ecm_qt_declare_logging_category(maildirresource_SRCS HEADER maildirresource_debug.h IDENTIFIER MAILDIRRESOURCE_LOG CATEGORY_NAME org.kde.pim.maildirresource DESCRIPTION "maildir resource (kdepim-runtime)" OLD_CATEGORY_NAMES log_maildirresource EXPORT KDEPIMRUNTIME ) -endif() kcfg_generate_dbus_interface(${CMAKE_CURRENT_SOURCE_DIR}/maildirresource.kcfg org.kde.Akonadi.Maildir.Settings) diff --git a/resources/maildir/libmaildir/CMakeLists.txt b/resources/maildir/libmaildir/CMakeLists.txt --- a/resources/maildir/libmaildir/CMakeLists.txt +++ b/resources/maildir/libmaildir/CMakeLists.txt @@ -1,33 +1,29 @@ if (BUILD_TESTING) - add_subdirectory( autotests ) + add_subdirectory( autotests ) endif() add_definitions(-DTRANSLATION_DOMAIN=\"akonadi_maildir_resource\") set(maildir_LIB_SRCS keycache.cpp maildir.cpp) -if (ECM_VERSION VERSION_LESS "5.68.0") - ecm_qt_declare_logging_category(maildir_LIB_SRCS HEADER libmaildir_debug.h IDENTIFIER LIBMAILDIR_LOG CATEGORY_NAME org.kde.pim.libmaildir) -else() - ecm_qt_declare_logging_category(maildir_LIB_SRCS HEADER libmaildir_debug.h IDENTIFIER LIBMAILDIR_LOG CATEGORY_NAME org.kde.pim.libmaildir - DESCRIPTION "libmaildir (kdepim-runtime)" - OLD_CATEGORY_NAMES log_libmaildir - EXPORT KDEPIMRUNTIME +ecm_qt_declare_logging_category(maildir_LIB_SRCS HEADER libmaildir_debug.h IDENTIFIER LIBMAILDIR_LOG CATEGORY_NAME org.kde.pim.libmaildir + DESCRIPTION "libmaildir (kdepim-runtime)" + OLD_CATEGORY_NAMES log_libmaildir + EXPORT KDEPIMRUNTIME ) -endif() add_library(maildir ${maildir_LIB_SRCS}) generate_export_header(maildir BASE_NAME maildir) target_link_libraries(maildir - PUBLIC + PUBLIC KF5::AkonadiMime - PRIVATE + PRIVATE KF5::I18n Qt5::Network -) + ) set_target_properties(maildir PROPERTIES VERSION ${KDEPIMRUNTIME_LIB_VERSION} SOVERSION ${KDEPIMRUNTIME_LIB_SOVERSION} ) diff --git a/resources/maildir/libmaildir/maildir.cpp b/resources/maildir/libmaildir/maildir.cpp --- a/resources/maildir/libmaildir/maildir.cpp +++ b/resources/maildir/libmaildir/maildir.cpp @@ -256,7 +256,7 @@ const QStringList lstMaildir = subFolderList(); for (const QString &sf : lstMaildir) { const Maildir subMd = Maildir(path() + QLatin1Char('/') + sf); - if (!subMd.isValid()) { + if (!subMd.isValid(createMissingFolders)) { d->lastError = subMd.lastError(); return false; } diff --git a/resources/mbox/CMakeLists.txt b/resources/mbox/CMakeLists.txt --- a/resources/mbox/CMakeLists.txt +++ b/resources/mbox/CMakeLists.txt @@ -3,55 +3,51 @@ set(mboxresource_common_SRCS deleteditemsattribute.cpp -) + ) kconfig_add_kcfg_files(mboxresource_common_SRCS settings.kcfgc) ################################## Resource ################################# add_definitions(-DTRANSLATION_DOMAIN=\"akonadi_mbox_resource\") set( mboxresource_SRCS - mboxresource.cpp - ${mboxresource_common_SRCS} -) + mboxresource.cpp + ${mboxresource_common_SRCS} + ) # mboxresource.cpp needs UI files generated for another target. We must be sure the files # were created before building the akonadi_mbox_resource target. add_custom_target(generated_headers - DEPENDS - ${CMAKE_CURRENT_BINARY_DIR}/ui_compactpage.h - ${CMAKE_CURRENT_BINARY_DIR}/ui_lockfilepage.h -) + DEPENDS + ${CMAKE_CURRENT_BINARY_DIR}/ui_compactpage.h + ${CMAKE_CURRENT_BINARY_DIR}/ui_lockfilepage.h + ) set_source_files_properties( - ${CMAKE_CURRENT_BINARY_DIR}/ui_compactpage.h - ${CMAKE_CURRENT_BINARY_DIR}/ui_lockfilepage.h - PROPERTIES GENERATED TRUE -) - -if (ECM_VERSION VERSION_LESS "5.68.0") - ecm_qt_declare_logging_category(mboxresource_SRCS HEADER mboxresource_debug.h IDENTIFIER MBOXRESOURCE_LOG CATEGORY_NAME org.kde.pim.mboxresource) -else() - ecm_qt_declare_logging_category(mboxresource_SRCS HEADER mboxresource_debug.h IDENTIFIER MBOXRESOURCE_LOG CATEGORY_NAME org.kde.pim.mboxresource - DESCRIPTION "mbox resource (kdepim-runtime)" - OLD_CATEGORY_NAMES log_mboxresource - EXPORT KDEPIMRUNTIME + ${CMAKE_CURRENT_BINARY_DIR}/ui_compactpage.h + ${CMAKE_CURRENT_BINARY_DIR}/ui_lockfilepage.h + PROPERTIES GENERATED TRUE + ) + +ecm_qt_declare_logging_category(mboxresource_SRCS HEADER mboxresource_debug.h IDENTIFIER MBOXRESOURCE_LOG CATEGORY_NAME org.kde.pim.mboxresource + DESCRIPTION "mbox resource (kdepim-runtime)" + OLD_CATEGORY_NAMES log_mboxresource + EXPORT KDEPIMRUNTIME ) -endif() install( FILES mboxresource.desktop DESTINATION "${KDE_INSTALL_DATAROOTDIR}/akonadi/agents" ) kcfg_generate_dbus_interface(${CMAKE_CURRENT_SOURCE_DIR}/mboxresource.kcfg org.kde.Akonadi.Mbox.Settings) qt5_add_dbus_adaptor(mboxresource_SRCS - ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.Mbox.Settings.xml settings.h Settings -) + ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.Mbox.Settings.xml settings.h Settings + ) add_executable(akonadi_mbox_resource ${mboxresource_SRCS}) add_dependencies(akonadi_mbox_resource generated_headers) if( APPLE ) - set_target_properties(akonadi_mbox_resource PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/../Info.plist.template) - set_target_properties(akonadi_mbox_resource PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.Akonadi.Mbox") - set_target_properties(akonadi_mbox_resource PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "KDE Akonadi Mbox Resource") + set_target_properties(akonadi_mbox_resource PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/../Info.plist.template) + set_target_properties(akonadi_mbox_resource PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.Akonadi.Mbox") + set_target_properties(akonadi_mbox_resource PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "KDE Akonadi Mbox Resource") endif () target_link_libraries(akonadi_mbox_resource @@ -64,11 +60,11 @@ KF5::AkonadiAgentBase KF5::Completion akonadi-singlefileresource -) + ) install(TARGETS akonadi_mbox_resource ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) if (BUILD_TESTING) - add_subdirectory(autotests) + add_subdirectory(autotests) endif() ############################# Config plugin ################################ @@ -78,23 +74,23 @@ lockmethodpage.cpp compactpage.cpp ${mboxresource_common_SRCS} -) + ) ki18n_wrap_ui(mboxconfig_SRCS - compactpage.ui - lockfilepage.ui -) + compactpage.ui + lockfilepage.ui + ) kcoreaddons_add_plugin(mboxconfig SOURCES ${mboxconfig_SRCS} JSON "mboxconfig.json" INSTALL_NAMESPACE "akonadi/config" -) + ) target_link_libraries(mboxconfig KF5::AkonadiCore KF5::ConfigWidgets KF5::KIOWidgets KF5::I18n KF5::Mbox akonadi-singlefileresource -) + ) diff --git a/resources/mixedmaildir/CMakeLists.txt b/resources/mixedmaildir/CMakeLists.txt --- a/resources/mixedmaildir/CMakeLists.txt +++ b/resources/mixedmaildir/CMakeLists.txt @@ -5,7 +5,7 @@ ${kdepim-runtime_SOURCE_DIR}/resources/maildir ${kdepim-runtime_SOURCE_DIR}/resources/mbox ${CMAKE_CURRENT_SOURCE_DIR}/kmindexreader -) + ) add_definitions(-DTRANSLATION_DOMAIN=\"akonadi_mixedmaildir_resource\") @@ -17,74 +17,81 @@ ############################# Resource ####################################### set( mixedmaildirresource_SRCS - compactchangehelper.cpp - mixedmaildirresource.cpp - mixedmaildirstore.cpp - retrieveitemsjob.cpp - ${mixedmaildir_common_SRCS} -) - -ecm_qt_declare_logging_category(mixedmaildirresource_SRCS HEADER mixedmaildirresource_debug.h IDENTIFIER MIXEDMAILDIRRESOURCE_LOG CATEGORY_NAME org.kde.pim.mixedmaildirresource) -ecm_qt_declare_logging_category(mixedmaildirresource_SRCS HEADER mixedmaildir_debug.h IDENTIFIER MIXEDMAILDIR_LOG CATEGORY_NAME org.kde.pim.mixedmaildir) + compactchangehelper.cpp + mixedmaildirresource.cpp + mixedmaildirstore.cpp + retrieveitemsjob.cpp + ${mixedmaildir_common_SRCS} + ) +ecm_qt_declare_logging_category(mixedmaildirresource_SRCS HEADER mixedmaildirresource_debug.h IDENTIFIER MIXEDMAILDIRRESOURCE_LOG CATEGORY_NAME org.kde.pim.mixedmaildirresource + DESCRIPTION "mixed maildir resource (kdepim-runtime)" + OLD_CATEGORY_NAMES log_mixedmaildir + EXPORT KDEPIMRUNTIME + ) +ecm_qt_declare_logging_category(mixedmaildirresource_SRCS HEADER mixedmaildir_debug.h IDENTIFIER MIXEDMAILDIR_LOG CATEGORY_NAME org.kde.pim.mixedmaildir + DESCRIPTION "mixedmaildir resource (kdepim-runtime)" + OLD_CATEGORY_NAMES log_mixedmaildirresource + EXPORT KDEPIMRUNTIME + ) install( FILES mixedmaildirresource.desktop DESTINATION "${KDE_INSTALL_DATAROOTDIR}/akonadi/agents" ) kcfg_generate_dbus_interface(${CMAKE_CURRENT_SOURCE_DIR}/mixedmaildirresource.kcfg org.kde.Akonadi.MixedMaildir.Settings) qt5_add_dbus_adaptor(mixedmaildirresource_SRCS - ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.MixedMaildir.Settings.xml settings.h Settings -) + ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.MixedMaildir.Settings.xml settings.h Settings + ) add_executable(akonadi_mixedmaildir_resource ${mixedmaildirresource_SRCS}) if( APPLE ) - set_target_properties(akonadi_mixedmaildir_resource PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/../Info.plist.template) - set_target_properties(akonadi_mixedmaildir_resource PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.Akonadi.MixedMaildir") - set_target_properties(akonadi_mixedmaildir_resource PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "KDE Akonadi MixedMaildir Resource") + set_target_properties(akonadi_mixedmaildir_resource PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/../Info.plist.template) + set_target_properties(akonadi_mixedmaildir_resource PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.Akonadi.MixedMaildir") + set_target_properties(akonadi_mixedmaildir_resource PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "KDE Akonadi MixedMaildir Resource") endif () target_link_libraries(akonadi_mixedmaildir_resource - kmindexreader - maildir - akonadi-filestore - KF5::AkonadiCore - KF5::AkonadiMime - KF5::KIOCore - KF5::Mbox - KF5::Mime - KF5::AkonadiAgentBase - KF5::I18n - akonadi-singlefileresource -) + kmindexreader + maildir + akonadi-filestore + KF5::AkonadiCore + KF5::AkonadiMime + KF5::KIOCore + KF5::Mbox + KF5::Mime + KF5::AkonadiAgentBase + KF5::I18n + akonadi-singlefileresource + ) install(TARGETS akonadi_mixedmaildir_resource ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.MixedMaildir.Settings.xml - DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR}) + DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR}) # unit tests if (BUILD_TESTING) - add_subdirectory( autotests ) + add_subdirectory( autotests ) endif () ###################################### Config Plugin ######################### set(mixedmaildirconfig_SRCS mixedmaildirconfig.cpp configwidget.cpp ${mixedmaildir_common_SRCS} -) + ) ki18n_wrap_ui(mixedmaildirconfig_SRCS settings.ui) kcoreaddons_add_plugin(mixedmaildirconfig SOURCES ${mixedmaildirconfig_SRCS} JSON "mixedmaildirconfig.json" INSTALL_NAMESPACE "akonadi/config" -) + ) target_link_libraries(mixedmaildirconfig KF5::AkonadiCore KF5::I18n KF5::KIOWidgets KF5::ConfigWidgets maildir -) + ) diff --git a/resources/openxchange/openxchangeresource.desktop b/resources/openxchange/openxchangeresource.desktop --- a/resources/openxchange/openxchangeresource.desktop +++ b/resources/openxchange/openxchangeresource.desktop @@ -69,6 +69,7 @@ Comment[pt_BR]=Fornece acesso aos compromissos, tarefas e contatos de um servidor groupware Open-Xchange. Comment[ru]=Доступ к встречам, задачам и контактам на сервере совместной работы Open-Xchange Comment[sk]=Poskytuje prístup k udalostiam, úlohám a kontaktom skupinového servera Open-Xchange. +Comment[sl]=Omogoča dostop do sestankov, opravil in stikov, shranjenih na strežniku za skupinsko delo Open-Xchange. Comment[sv]=Ger tillgång till möten, uppgifter och kontakter lagrade på en Open-Xchange grupprogramserver. Comment[uk]=Надає доступ до записів зустрічей, завдань і контактів, які зберігаються на сервері групової роботи Open-Xchange. Comment[x-test]=xxProvides access to the appointments, tasks, and contacts of an Open-Xchange groupware server.xx diff --git a/resources/pop3/CMakeLists.txt b/resources/pop3/CMakeLists.txt --- a/resources/pop3/CMakeLists.txt +++ b/resources/pop3/CMakeLists.txt @@ -3,55 +3,51 @@ set(pop3_common_SRCS settings.cpp -) + ) kconfig_add_kcfg_files(pop3_common_SRCS settingsbase.kcfgc) -if (ECM_VERSION VERSION_LESS "5.68.0") - ecm_qt_declare_logging_category(pop3_common_SRCS HEADER pop3resource_debug.h IDENTIFIER POP3RESOURCE_LOG CATEGORY_NAME org.kde.pim.pop3resource) -else() - ecm_qt_declare_logging_category(pop3_common_SRCS HEADER pop3resource_debug.h IDENTIFIER POP3RESOURCE_LOG CATEGORY_NAME org.kde.pim.pop3resource - DESCRIPTION "pop3 resource (kdepim-runtime)" - OLD_CATEGORY_NAMES log_pop3resource - EXPORT KDEPIMRUNTIME +ecm_qt_declare_logging_category(pop3_common_SRCS HEADER pop3resource_debug.h IDENTIFIER POP3RESOURCE_LOG CATEGORY_NAME org.kde.pim.pop3resource + DESCRIPTION "pop3 resource (kdepim-runtime)" + OLD_CATEGORY_NAMES log_pop3resource + EXPORT KDEPIMRUNTIME ) -endif() kcfg_generate_dbus_interface(${CMAKE_CURRENT_SOURCE_DIR}/settings.kcfg org.kde.Akonadi.POP3.Settings) qt5_add_dbus_adaptor(pop3_common_SRCS - ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.POP3.Settings.xml settings.h Settings -) + ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.POP3.Settings.xml settings.h Settings + ) ################################# Resource #################################### set( pop3resource_SRCS - pop3resource.cpp - jobs.cpp - ${pop3_common_SRCS} -) + pop3resource.cpp + jobs.cpp + ${pop3_common_SRCS} + ) install( FILES pop3resource.desktop DESTINATION "${KDE_INSTALL_DATAROOTDIR}/akonadi/agents" ) #add_executable(akonadi_pop3_resource RUN_UNINSTALLED ${pop3resource_SRCS}) add_executable(akonadi_pop3_resource ${pop3resource_SRCS}) if( APPLE ) - set_target_properties(akonadi_pop3_resource PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/../Info.plist.template) - set_target_properties(akonadi_pop3_resource PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.Akonadi.POP3") - set_target_properties(akonadi_pop3_resource PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "KDE Akonadi POP3 Resource") + set_target_properties(akonadi_pop3_resource PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/../Info.plist.template) + set_target_properties(akonadi_pop3_resource PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.Akonadi.POP3") + set_target_properties(akonadi_pop3_resource PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "KDE Akonadi POP3 Resource") endif () target_link_libraries( akonadi_pop3_resource - KF5::AkonadiCore - KF5::AkonadiMime - KF5::KIOCore - KF5::Mime - KF5::MailTransport - KF5::AkonadiAgentBase - KF5::I18n - KF5::Notifications - Qt5::DBus - KF5::PimCommon -) + KF5::AkonadiCore + KF5::AkonadiMime + KF5::KIOCore + KF5::Mime + KF5::MailTransport + KF5::AkonadiAgentBase + KF5::I18n + KF5::Notifications + Qt5::DBus + KF5::PimCommon + ) if(BUILD_TESTING) add_subdirectory( autotests ) @@ -69,14 +65,14 @@ pop3config.cpp accountwidget.cpp ${pop3_common_SRCS} -) + ) ki18n_wrap_ui(pop3config_SRCS popsettings.ui) kcoreaddons_add_plugin(pop3config SOURCES ${pop3config_SRCS} JSON "pop3config.json" INSTALL_NAMESPACE "akonadi/config" -) + ) target_link_libraries(pop3config KF5::AkonadiCore KF5::TextWidgets @@ -87,4 +83,4 @@ KF5::AkonadiWidgets KF5::AkonadiAgentBase KF5::AkonadiMime -) + ) diff --git a/resources/pop3/accountwidget.h b/resources/pop3/accountwidget.h --- a/resources/pop3/accountwidget.h +++ b/resources/pop3/accountwidget.h @@ -32,7 +32,6 @@ class Wallet; } -class POP3Resource; class KJob; class AccountWidget : public QWidget, private Ui::PopPage diff --git a/resources/shared/filestore/CMakeLists.txt b/resources/shared/filestore/CMakeLists.txt --- a/resources/shared/filestore/CMakeLists.txt +++ b/resources/shared/filestore/CMakeLists.txt @@ -2,42 +2,45 @@ add_definitions(-DTRANSLATION_DOMAIN=\"akonadi-filestore\") set(akonadi-filestore_SRCS - abstractlocalstore.cpp - collectioncreatejob.cpp - collectiondeletejob.cpp - collectionfetchjob.cpp - collectionmodifyjob.cpp - collectionmovejob.cpp - entitycompactchangeattribute.cpp - itemcreatejob.cpp - itemdeletejob.cpp - itemfetchjob.cpp - itemmodifyjob.cpp - itemmovejob.cpp - job.cpp - session.cpp - sessionimpls.cpp - storecompactjob.cpp -) - -ecm_qt_declare_logging_category(akonadi-filestore_SRCS HEADER akonadifilestore_debug.h IDENTIFIER AKONADIFILESTORE_LOG CATEGORY_NAME org.kde.pim.filestore) + abstractlocalstore.cpp + collectioncreatejob.cpp + collectiondeletejob.cpp + collectionfetchjob.cpp + collectionmodifyjob.cpp + collectionmovejob.cpp + entitycompactchangeattribute.cpp + itemcreatejob.cpp + itemdeletejob.cpp + itemfetchjob.cpp + itemmodifyjob.cpp + itemmovejob.cpp + job.cpp + session.cpp + sessionimpls.cpp + storecompactjob.cpp + ) + +ecm_qt_declare_logging_category(akonadi-filestore_SRCS HEADER akonadifilestore_debug.h IDENTIFIER AKONADIFILESTORE_LOG CATEGORY_NAME org.kde.pim.filestore + DESCRIPTION "resource filestore lib (kdepim-runtime)" + EXPORT KDEPIMRUNTIME + ) add_library(akonadi-filestore ${akonadi-filestore_SRCS} ) generate_export_header(akonadi-filestore BASE_NAME akonadi-filestore) target_link_libraries(akonadi-filestore - PUBLIC - KF5::AkonadiCore - PRIVATE - KF5::I18n -) + PUBLIC + KF5::AkonadiCore + PRIVATE + KF5::I18n + ) set_target_properties(akonadi-filestore PROPERTIES VERSION ${KDEPIMRUNTIME_LIB_VERSION} SOVERSION ${KDEPIMRUNTIME_LIB_SOVERSION} ) install(TARGETS akonadi-filestore ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} LIBRARY NAMELINK_SKIP) if (BUILD_TESTING) - add_subdirectory( autotests ) + add_subdirectory( autotests ) endif () diff --git a/resources/shared/singlefileresource/singlefileresourceconfigwidgetbase.cpp b/resources/shared/singlefileresource/singlefileresourceconfigwidgetbase.cpp --- a/resources/shared/singlefileresource/singlefileresourceconfigwidgetbase.cpp +++ b/resources/shared/singlefileresource/singlefileresourceconfigwidgetbase.cpp @@ -22,6 +22,7 @@ #include "singlefileresourceconfigwidgetbase.h" #include +#include #include #include @@ -139,10 +140,11 @@ if (mStatJob) { mStatJob->kill(); } - - mStatJob = KIO::stat(currentUrl, KIO::DefaultFlags | KIO::HideProgressInfo); - mStatJob->setDetails(2); // All details. - mStatJob->setSide(KIO::StatJob::SourceSide); +#if KIO_VERSION < QT_VERSION_CHECK(5, 69, 0) + mStatJob = KIO::stat(currentUrl, KIO::StatJob::SourceSide, 2, KIO::DefaultFlags | KIO::HideProgressInfo); +#else + mStatJob = KIO::statDetails(currentUrl, KIO::StatJob::SourceSide, KIO::StatDetail::StatDefaultDetails, KIO::DefaultFlags | KIO::HideProgressInfo); +#endif connect(mStatJob, &KIO::StatJob::result, this, &SingleFileResourceConfigWidgetBase::slotStatJobResult); @@ -160,9 +162,12 @@ QUrl dirUrl(ui.kcfg_Path->url()); dirUrl = KIO::upUrl(dirUrl); - mStatJob = KIO::stat(dirUrl, KIO::DefaultFlags | KIO::HideProgressInfo); - mStatJob->setDetails(2); // All details. - mStatJob->setSide(KIO::StatJob::SourceSide); +#if KIO_VERSION < QT_VERSION_CHECK(5, 69, 0) + mStatJob = KIO::stat(dirUrl, KIO::StatJob::SourceSide, 2, KIO::DefaultFlags | KIO::HideProgressInfo); +#else + mStatJob = KIO::statDetails(dirUrl, KIO::StatJob::SourceSide, KIO::StatDetail::StatDefaultDetails, KIO::DefaultFlags | KIO::HideProgressInfo); +#endif + connect(mStatJob, &KIO::StatJob::result, this, &SingleFileResourceConfigWidgetBase::slotStatJobResult); diff --git a/resources/tomboynotes/CMakeLists.txt b/resources/tomboynotes/CMakeLists.txt --- a/resources/tomboynotes/CMakeLists.txt +++ b/resources/tomboynotes/CMakeLists.txt @@ -7,7 +7,7 @@ kconfig_add_kcfg_files(tomboynotesresource_common_SRCS settings.kcfgc -) + ) set(tomboynotesresource_SRCS tomboynotesresource.cpp @@ -30,42 +30,34 @@ o2/o2replyserver.cpp o2/o2requestor.cpp o2/o2simplecrypt.cpp -) - -if (ECM_VERSION VERSION_LESS "5.68.0") - ecm_qt_declare_logging_category(tomboynotesresource_SRCS - HEADER debug.h - IDENTIFIER TOMBOYNOTESRESOURCE_LOG - CATEGORY_NAME org.kde.pim.tomboynotesresource ) -else() - ecm_qt_declare_logging_category(tomboynotesresource_SRCS - HEADER debug.h - IDENTIFIER TOMBOYNOTESRESOURCE_LOG - CATEGORY_NAME org.kde.pim.tomboynotesresource - DESCRIPTION "tomboynote resource (kdepim-runtime)" - OLD_CATEGORY_NAMES log_tomboynotesresource - EXPORT KDEPIMRUNTIME + +ecm_qt_declare_logging_category(tomboynotesresource_SRCS + HEADER debug.h + IDENTIFIER TOMBOYNOTESRESOURCE_LOG + CATEGORY_NAME org.kde.pim.tomboynotesresource + DESCRIPTION "tomboynote resource (kdepim-runtime)" + OLD_CATEGORY_NAMES log_tomboynotesresource + EXPORT KDEPIMRUNTIME ) -endif() kconfig_add_kcfg_files(tomboynotesresource_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/settings.kcfgc -) + ) kcfg_generate_dbus_interface( ${CMAKE_CURRENT_SOURCE_DIR}/tomboynotesresource.kcfg org.kde.Akonadi.TomboyNotes.Settings -) + ) qt5_add_dbus_adaptor(tomboynotesresource_SRCS ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.TomboyNotes.Settings.xml ${CMAKE_CURRENT_BINARY_DIR}/settings.h Settings -) + ) add_executable(akonadi_tomboynotes_resource ${tomboynotesresource_SRCS}) @@ -84,13 +76,13 @@ KF5::KIONTLM KF5::KIOWidgets KF5::Mime -) + ) install(TARGETS akonadi_tomboynotes_resource ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES tomboynotesresource.desktop - DESTINATION "${KDE_INSTALL_DATAROOTDIR}/akonadi/agents" -) + DESTINATION "${KDE_INSTALL_DATAROOTDIR}/akonadi/agents" + ) ############################# Config plugin ################################ set(tomboynotesconfig_ui_SRCS) diff --git a/resources/tomboynotes/o2/o1.cpp b/resources/tomboynotes/o2/o1.cpp --- a/resources/tomboynotes/o2/o1.cpp +++ b/resources/tomboynotes/o2/o1.cpp @@ -275,13 +275,8 @@ { qCDebug(TOMBOYNOTESRESOURCE_LOG) << "O1::onTokenRequestFinished"; QNetworkReply *reply = qobject_cast(sender()); - reply->deleteLater(); -#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) - const auto networkError = reply->error(); -#else - const auto networkError = reply->networkError(); -#endif - if (networkError != QNetworkReply::NoError) { + reply->deleteLater(); + if (reply->error() != QNetworkReply::NoError) { qCWarning(TOMBOYNOTESRESOURCE_LOG) << "O1::onTokenRequestFinished: " << reply->errorString(); return; } @@ -367,12 +362,7 @@ QNetworkReply *reply = qobject_cast(sender()); reply->deleteLater(); -#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) - const auto networkError = reply->error(); -#else - const auto networkError = reply->networkError(); -#endif - if (networkError != QNetworkReply::NoError) { + if (reply->error() != QNetworkReply::NoError) { qCWarning(TOMBOYNOTESRESOURCE_LOG) << "O1::onTokenExchangeFinished: " << reply->errorString(); return; } diff --git a/resources/tomboynotes/o2/o2.cpp b/resources/tomboynotes/o2/o2.cpp --- a/resources/tomboynotes/o2/o2.cpp +++ b/resources/tomboynotes/o2/o2.cpp @@ -279,12 +279,7 @@ { qCDebug(TOMBOYNOTESRESOURCE_LOG) << "O2::onTokenReplyFinished"; QNetworkReply *tokenReply = qobject_cast(sender()); -#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) - const auto networkError = tokenReply->error(); -#else - const auto networkError = tokenReply->networkError(); -#endif - if (networkError == QNetworkReply::NoError) { + if (tokenReply->error() == QNetworkReply::NoError) { QByteArray replyData = tokenReply->readAll(); QVariantMap tokens = parseTokenResponse(replyData); @@ -395,13 +390,8 @@ void O2::onRefreshFinished() { QNetworkReply *refreshReply = qobject_cast(sender()); -#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) - const auto networkError = refreshReply->error(); -#else - const auto networkError = refreshReply->networkError(); -#endif - qCDebug(TOMBOYNOTESRESOURCE_LOG) << "O2::onRefreshFinished: Error" << (int)networkError << refreshReply->errorString(); - if (networkError == QNetworkReply::NoError) { + qCDebug(TOMBOYNOTESRESOURCE_LOG) << "O2::onRefreshFinished: Error" << (int)refreshReply->error() << refreshReply->errorString(); + if (refreshReply->error() == QNetworkReply::NoError) { QByteArray reply = refreshReply->readAll(); QVariantMap tokens = parseTokenResponse(reply); setToken(tokens.value(QLatin1String(O2_OAUTH2_ACCESS_TOKEN)).toString()); diff --git a/resources/tomboynotes/o2/o2requestor.cpp b/resources/tomboynotes/o2/o2requestor.cpp --- a/resources/tomboynotes/o2/o2requestor.cpp +++ b/resources/tomboynotes/o2/o2requestor.cpp @@ -82,18 +82,14 @@ void O2Requestor::onRequestFinished() { QNetworkReply *senderReply = qobject_cast(sender()); -#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) - const auto networkError = senderReply->error(); -#else - const auto networkError = senderReply->networkError(); -#endif + QNetworkReply::NetworkError error = senderReply->error(); if (status_ == Idle) { return; } if (reply_ != senderReply) { return; } - if (networkError == QNetworkReply::NoError) { + if (error == QNetworkReply::NoError) { QTimer::singleShot(10, this, &O2Requestor::finish); } } diff --git a/resources/tomboynotes/tomboyjobbase.cpp b/resources/tomboynotes/tomboyjobbase.cpp --- a/resources/tomboynotes/tomboyjobbase.cpp +++ b/resources/tomboynotes/tomboyjobbase.cpp @@ -42,12 +42,7 @@ void TomboyJobBase::checkReplyError() { -#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) - const auto networkError = mReply->error(); -#else - const auto networkError = mReply->networkError(); -#endif - switch (networkError) { + switch (mReply->error()) { case QNetworkReply::NoError: setError(TomboyJobError::NoError); break; diff --git a/resources/vcarddir/CMakeLists.txt b/resources/vcarddir/CMakeLists.txt --- a/resources/vcarddir/CMakeLists.txt +++ b/resources/vcarddir/CMakeLists.txt @@ -12,14 +12,10 @@ ${vcarddirresource_common_SRCS} vcarddirresource.cpp ) -if (ECM_VERSION VERSION_LESS "5.68.0") - ecm_qt_declare_logging_category(vcarddirresource_SRCS HEADER vcarddirresource_debug.h IDENTIFIER VCARDDIRRESOURCE_LOG CATEGORY_NAME org.kde.pim.vcarddirresource) -else() - ecm_qt_declare_logging_category(vcarddirresource_SRCS HEADER vcarddirresource_debug.h IDENTIFIER VCARDDIRRESOURCE_LOG CATEGORY_NAME org.kde.pim.vcarddirresource +ecm_qt_declare_logging_category(vcarddirresource_SRCS HEADER vcarddirresource_debug.h IDENTIFIER VCARDDIRRESOURCE_LOG CATEGORY_NAME org.kde.pim.vcarddirresource DESCRIPTION "vcarddir resource (kdepim-runtime)" EXPORT KDEPIMRUNTIME ) -endif()