mirror of
https://gitlab.linphone.org/BC/public/linphone-desktop.git
synced 2026-01-17 11:28:07 +00:00
Create a plugin system
- Add feedback on errors - More generic import requests - Replace storing password by a native popup that ask passwork when needed - Store passwords in settings file - Import Feedback in advanced settings tab - Use English as default translator if no translations are found - Add OpenSSL packaging for Windows to deal with Qt connections - Add option to overwrite plugin if exists - Plugin load/unload managment. Hot-Dynamic load of plugins. Safely test the loaded plugin - Set plugin data with default value when all GUI items are loaded - Rewrite folder priority - Add filename info from pluginloader - Add plugin versionning - Specify inputs for saving - Copy desktop headers in OUTPUT to be used by external projects - Add a plugins folder auto-managed by cmake - Remove obsolete contact api submodule - Clean plugin example - Add specific behaviour for plugin type : inputs have been splitted by Capability. - Update save/load to be more generic and add clean function for configurations - Instantiate Importer List model - Add IDE integration for plugins - Set input fields to be dependent of capability - Change signals interface to take account capability
This commit is contained in:
parent
de06195c32
commit
5eba9a5ece
60 changed files with 2552 additions and 135 deletions
10
.gitmodules
vendored
10
.gitmodules
vendored
|
|
@ -1,6 +1,6 @@
|
|||
[submodule "submodules/externals/minizip"]
|
||||
path = submodules/externals/minizip
|
||||
url = ../../public/external/minizip.git
|
||||
[submodule "linphone-sdk"]
|
||||
path = linphone-sdk
|
||||
url = ../../public/linphone-sdk.git
|
||||
path = linphone-sdk
|
||||
url = https://gitlab.linphone.org/BC/public/linphone-sdk.git
|
||||
[submodule "plugins/contacts/contacts-api"]
|
||||
path = plugins/contacts/contacts-api
|
||||
url = https://gitlab.linphone.org/BC/public/linphone-desktop-plugins/contacts/contacts-api.git
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ set(LINPHONE_OUTPUT_DIR "${CMAKE_BINARY_DIR}/linphone-sdk/desktop")
|
|||
|
||||
set(APPLICATION_OUTPUT_DIR "${CMAKE_BINARY_DIR}/OUTPUT")
|
||||
|
||||
set(CMAKE_PREFIX_PATH "${LINPHONE_OUTPUT_DIR};${APPLICATION_OUTPUT_DIR}${PREFIX_PATH}")
|
||||
set(CMAKE_PREFIX_PATH "${LINPHONE_OUTPUT_DIR};${APPLICATION_OUTPUT_DIR};${APPLICATION_OUTPUT_DIR}/include${PREFIX_PATH}")
|
||||
string(REPLACE ";" "|" PREFIX_PATH "${CMAKE_PREFIX_PATH}")
|
||||
#set(PREFIX_PATH "${LINPHONE_OUTPUT_DIR}|${APPLICATION_OUTPUT_DIR}${PREFIX_PATH}")
|
||||
|
||||
|
|
@ -95,6 +95,10 @@ option(ENABLE_FFMPEG "Build mediastreamer2 with ffmpeg video support." YES)
|
|||
option(ENABLE_BUILD_VERBOSE "Enable the build generation to be more verbose" NO)
|
||||
option(ENABLE_OPENH264 "Enable the use of OpenH264 codec" YES)
|
||||
option(ENABLE_NON_FREE_CODECS "Enable the use of non free codecs" YES)
|
||||
option(ENABLE_BUILD_APP_PLUGINS "Enable the build of plugins" YES)
|
||||
option(ENABLE_BUILD_EXAMPLES "Enable the build of examples" NO)
|
||||
|
||||
|
||||
|
||||
if(WIN32 OR APPLE)
|
||||
else()
|
||||
|
|
@ -114,6 +118,7 @@ list(APPEND APP_OPTIONS "-DENABLE_FFMPEG=${ENABLE_FFMPEG}")
|
|||
list(APPEND APP_OPTIONS "-DENABLE_BUILD_VERBOSE=${ENABLE_BUILD_VERBOSE}")
|
||||
list(APPEND APP_OPTIONS "-DENABLE_OPENH264=${ENABLE_OPENH264}")
|
||||
list(APPEND APP_OPTIONS "-DENABLE_NON_FREE_CODECS=${ENABLE_NON_FREE_CODECS}")
|
||||
list(APPEND APP_OPTIONS "-DENABLE_BUILD_EXAMPLES=${ENABLE_BUILD_EXAMPLES}")
|
||||
|
||||
if(ENABLE_V4L)
|
||||
list(APPEND APP_OPTIONS "-DENABLE_V4L=${ENABLE_V4L}")
|
||||
|
|
@ -176,7 +181,6 @@ find_package(belcard CONFIG QUIET)
|
|||
find_package(Mediastreamer2 CONFIG QUIET)
|
||||
find_package(ortp CONFIG QUIET)
|
||||
|
||||
|
||||
if(NOT (LinphoneCxx_FOUND) OR NOT (Linphone_FOUND) OR NOT (bctoolbox_FOUND) OR NOT (belcard_FOUND) OR NOT (Mediastreamer2_FOUND) OR NOT (ortp_FOUND) OR FORCE_APP_EXTERNAL_PROJECTS)
|
||||
message("Projects are set as External projects. You can start building them by using for example : cmake --build . --target install")
|
||||
ExternalProject_Add(linphone-qt PREFIX "${CMAKE_BINARY_DIR}/linphone-app"
|
||||
|
|
@ -191,16 +195,38 @@ if(NOT (LinphoneCxx_FOUND) OR NOT (Linphone_FOUND) OR NOT (bctoolbox_FOUND) OR N
|
|||
# ${APP_OPTIONS}
|
||||
BUILD_ALWAYS ON
|
||||
)
|
||||
if( ENABLE_BUILD_APP_PLUGINS)
|
||||
ExternalProject_Add(app-plugins PREFIX "${CMAKE_BINARY_DIR}/plugins-app"
|
||||
SOURCE_DIR "${CMAKE_SOURCE_DIR}/plugins"
|
||||
INSTALL_DIR "${APPLICATION_OUTPUT_DIR}"
|
||||
BINARY_DIR "${CMAKE_BINARY_DIR}/plugins-app"
|
||||
DEPENDS ${APP_DEPENDS} linphone-qt
|
||||
BUILD_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --config $<CONFIG> ${PROJECT_BUILD_COMMAND}
|
||||
INSTALL_COMMAND ${CMAKE_COMMAND} -E echo "Install step is already done at build time."
|
||||
LIST_SEPARATOR | # Use the alternate list separator
|
||||
CMAKE_ARGS ${APP_OPTIONS} ${USER_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_PREFIX_PATH=${PREFIX_PATH}
|
||||
)
|
||||
endif()
|
||||
install(CODE "message(STATUS Running install)")
|
||||
set(AUTO_REGENERATION auto_regeneration)
|
||||
add_custom_target(${AUTO_REGENERATION} ALL
|
||||
COMMAND ${CMAKE_COMMAND} ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
DEPENDS linphone-qt)
|
||||
if( ENABLE_BUILD_APP_PLUGINS)
|
||||
add_custom_target(${AUTO_REGENERATION} ALL
|
||||
COMMAND ${CMAKE_COMMAND} ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
DEPENDS app-plugins)
|
||||
else()
|
||||
add_custom_target(${AUTO_REGENERATION} ALL
|
||||
COMMAND ${CMAKE_COMMAND} ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
DEPENDS linphone-qt)
|
||||
endif()
|
||||
else()
|
||||
message("Adding Linphone Desktop in an IDE-friendly state")
|
||||
set(CMAKE_INSTALL_PREFIX "${APPLICATION_OUTPUT_DIR}")
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/linphone-app)
|
||||
add_dependencies(app-library ${APP_DEPENDS})
|
||||
if( ENABLE_BUILD_APP_PLUGINS)
|
||||
add_custom_command(TARGET sdk PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/linphone-app/include/" "${CMAKE_INSTALL_PREFIX}/include/")
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/plugins "plugins-app")
|
||||
endif()
|
||||
endif()
|
||||
ExternalProject_Add(linphone-qt-only PREFIX "${CMAKE_BINARY_DIR}/linphone-app"
|
||||
SOURCE_DIR "${CMAKE_SOURCE_DIR}/linphone-app"
|
||||
|
|
|
|||
|
|
@ -132,6 +132,10 @@ set(SOURCES
|
|||
src/components/conference/ConferenceModel.cpp
|
||||
src/components/contact/ContactModel.cpp
|
||||
src/components/contact/VcardModel.cpp
|
||||
src/components/contacts/ContactsImporterModel.cpp
|
||||
src/components/contacts/ContactsImporterPluginsManager.cpp
|
||||
src/components/contacts/ContactsImporterListModel.cpp
|
||||
src/components/contacts/ContactsImporterListProxyModel.cpp
|
||||
src/components/contacts/ContactsListModel.cpp
|
||||
src/components/contacts/ContactsListProxyModel.cpp
|
||||
src/components/core/CoreHandlers.cpp
|
||||
|
|
@ -161,6 +165,11 @@ set(SOURCES
|
|||
src/utils/MediastreamerUtils.cpp
|
||||
src/utils/QExifImageHeader.cpp
|
||||
src/utils/Utils.cpp
|
||||
src/utils/plugins/PluginDataAPI.cpp
|
||||
src/utils/plugins/PluginNetworkHelper.cpp
|
||||
src/utils/plugins/LinphonePlugin.cpp
|
||||
src/utils/plugins/PluginsManager.cpp
|
||||
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
|
|
@ -194,6 +203,10 @@ set(HEADERS
|
|||
src/components/conference/ConferenceModel.hpp
|
||||
src/components/contact/ContactModel.hpp
|
||||
src/components/contact/VcardModel.hpp
|
||||
src/components/contacts/ContactsImporterModel.hpp
|
||||
src/components/contacts/ContactsImporterPluginsManager.hpp
|
||||
src/components/contacts/ContactsImporterListModel.hpp
|
||||
src/components/contacts/ContactsImporterListProxyModel.hpp
|
||||
src/components/contacts/ContactsListModel.hpp
|
||||
src/components/contacts/ContactsListProxyModel.hpp
|
||||
src/components/core/CoreHandlers.hpp
|
||||
|
|
@ -224,8 +237,12 @@ set(HEADERS
|
|||
src/utils/MediastreamerUtils.hpp
|
||||
src/utils/QExifImageHeader.hpp
|
||||
src/utils/Utils.hpp
|
||||
src/utils/plugins/PluginsManager.hpp
|
||||
include/LinphoneApp/PluginDataAPI.hpp
|
||||
include/LinphoneApp/PluginNetworkHelper.hpp
|
||||
include/LinphoneApp/LinphonePlugin.hpp
|
||||
)
|
||||
|
||||
list(APPEND SOURCES include/LinphoneApp/PluginExample.json)
|
||||
set(MAIN_FILE src/app/main.cpp)
|
||||
|
||||
if (APPLE)
|
||||
|
|
@ -336,8 +353,8 @@ list(APPEND _QML_IMPORT_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/ui/scripts")
|
|||
list(APPEND _QML_IMPORT_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/ui/views")
|
||||
|
||||
|
||||
set(QML_IMPORT_PATH ${_QML_IMPORT_PATHS} CACHE STRING "Path used to locate CMake modules by Qt Creator" FORCE)
|
||||
set(QML2_IMPORT_PATH ${_QML_IMPORT_PATHS} CACHE STRING "Path used to locate CMake modules by Qt Creator" FORCE)
|
||||
set(QML_IMPORT_PATH "${_QML_IMPORT_PATHS}" CACHE STRING "Path used to locate CMake modules by Qt Creator" FORCE)
|
||||
set(QML2_IMPORT_PATH "${_QML_IMPORT_PATHS}" CACHE STRING "Path used to locate CMake modules by Qt Creator" FORCE)
|
||||
set(QML_SOURCES_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/ui/")
|
||||
set(QML_MODULES_PATHS ${QML_SOURCES_PATHS})
|
||||
if(ENABLE_BUILD_VERBOSE)#useful to copy these Paths to QML previewers
|
||||
|
|
@ -421,9 +438,12 @@ endif()
|
|||
|
||||
#add_dependencies(project_b_exe project_a)
|
||||
#target_link_libraries(project_b_exe ${install_dir}/lib/alib.lib)
|
||||
|
||||
if(ENABLE_APP_EXPORT_PLUGIN)
|
||||
add_definitions(-DENABLE_APP_EXPORT_PLUGIN)
|
||||
endif()
|
||||
|
||||
set(INCLUDED_DIRECTORIES "${LINPHONECXX_INCLUDE_DIRS}" )
|
||||
list(APPEND INCLUDED_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/include")
|
||||
set(LIBRARIES_LIST ${BCTOOLBOX_CORE_LIBRARIES} ${BELCARD_LIBRARIES} ${LINPHONE_LIBRARIES} ${LINPHONECXX_LIBRARIES} ${MEDIASTREAMER2_LIBRARIES} ${ORTP_LIBRARIES} ${OPUS_LIBRARIES})
|
||||
if(WIN32)
|
||||
set(LIBRARIES)
|
||||
|
|
@ -439,7 +459,6 @@ else()
|
|||
set(LIBRARIES ${LIBRARIES_LIST})
|
||||
endif()
|
||||
|
||||
|
||||
if(ENABLE_BUILD_VERBOSE)
|
||||
message("LIBRARIES : ${LIBRARIES}")
|
||||
endif()
|
||||
|
|
@ -465,6 +484,9 @@ foreach (package ${QT5_PACKAGES_OPTIONAL})
|
|||
endif ()
|
||||
endforeach ()
|
||||
|
||||
#find_library(CONTACTS_PLUGIN_LIBRARY linphoneAppContacts HINTS "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
|
||||
#list(APPEND LIBRARIES ${CONTACTS_PLUGIN_LIBRARY})
|
||||
|
||||
if (APPLE)
|
||||
list(APPEND LIBRARIES "-framework Cocoa -framework IOKit -framework AVFoundation")
|
||||
# -framework linphone") #This doesn't work yet
|
||||
|
|
@ -478,6 +500,7 @@ target_link_libraries(${TARGET_NAME} ${LIBRARIES})
|
|||
if(WIN32)
|
||||
target_link_libraries(${TARGET_NAME} wsock32 ws2_32)
|
||||
endif()
|
||||
target_compile_definitions(${APP_LIBRARY} PUBLIC ENABLE_APP_EXPORT_PLUGIN)
|
||||
|
||||
add_dependencies(${APP_LIBRARY} update_translations ${TARGET_NAME}-git-version)
|
||||
add_dependencies(${TARGET_NAME} ${APP_LIBRARY})
|
||||
|
|
@ -488,6 +511,10 @@ add_dependencies(${TARGET_NAME} ${APP_LIBRARY})
|
|||
# ------------------------------------------------------------------------------
|
||||
set(TOOLS_DIR "${CMAKE_BINARY_DIR}/programs")
|
||||
set(LINPHONE_BUILDER_SIGNING_IDENTITY ${LINPHONE_BUILDER_SIGNING_IDENTITY})
|
||||
add_custom_command(TARGET ${TARGET_NAME} PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/include/" "${CMAKE_INSTALL_PREFIX}/include/")
|
||||
#add_custom_command(TARGET ${TARGET_NAME} PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/include/LinphoneApp/*" "${CMAKE_INSTALL_PREFIX}/include/LinphoneApp/")
|
||||
#configure_file("${CMAKE_CURRENT_SOURCE_DIR}/include/*" "${CMAKE_INSTALL_PREFIX}/include/LinphoneApp/" COPYONLY)
|
||||
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include" DESTINATION ${CMAKE_INSTALL_PREFIX})
|
||||
|
||||
add_subdirectory(build)
|
||||
add_subdirectory(cmake_builder/linphone_package)
|
||||
|
|
@ -496,7 +523,9 @@ add_subdirectory(cmake_builder/linphone_package)
|
|||
# ------------------------------------------------------------------------------
|
||||
# To start better integration into IDE.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
source_group(
|
||||
"Json" REGULAR_EXPRESSION ".+\.json$"
|
||||
)
|
||||
source_group(
|
||||
"Qml" REGULAR_EXPRESSION ".+\.qml$"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1183,6 +1183,10 @@ Klik her: <a href="%1">%1</a>
|
|||
<source>logsMailerSuccess</source>
|
||||
<translation>Logfiler blev uploadet til %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>contactsTitle</source>
|
||||
<translation type="unfinished">Kontakter</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAudio</name>
|
||||
|
|
|
|||
|
|
@ -1183,6 +1183,10 @@ Klicken Sie hier: <a href="%1">%1</a>
|
|||
<source>logsMailerSuccess</source>
|
||||
<translation>Protokolle wurden auf %1 hochgeladen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>contactsTitle</source>
|
||||
<translation type="unfinished">Kontakte</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAudio</name>
|
||||
|
|
|
|||
|
|
@ -1199,6 +1199,10 @@ Click here: <a href="%1">%1</a>
|
|||
<source>logsMailerSuccess</source>
|
||||
<translation>Logs were uploaded to %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>contactsTitle</source>
|
||||
<translation>Address Book Connector</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAudio</name>
|
||||
|
|
|
|||
|
|
@ -1183,6 +1183,10 @@ Haga clic aquí: <a href="%1">%1 </a>
|
|||
<source>logsMailerSuccess</source>
|
||||
<translation>Los registros se cargaron a %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>contactsTitle</source>
|
||||
<translation type="unfinished">Contactos</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAudio</name>
|
||||
|
|
|
|||
|
|
@ -1183,6 +1183,10 @@ Cliquez ici : <a href="%1">%1</a>
|
|||
<source>logsMailerSuccess</source>
|
||||
<translation>Les logs ont été uploadé sur %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>contactsTitle</source>
|
||||
<translation type="unfinished">Contacts</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAudio</name>
|
||||
|
|
|
|||
|
|
@ -1182,6 +1182,10 @@ Kattintson ide: <a href="%1">%1</a></translation>
|
|||
<source>logsMailerSuccess</source>
|
||||
<translation>A naplókat feltöltötték a %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>contactsTitle</source>
|
||||
<translation type="unfinished">Névjegyek</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAudio</name>
|
||||
|
|
|
|||
|
|
@ -1183,6 +1183,10 @@ Clicca: <a href="%1">%1</a>
|
|||
<source>logsMailerSuccess</source>
|
||||
<translation>I log sono stati caricati a %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>contactsTitle</source>
|
||||
<translation type="unfinished">Contatti</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAudio</name>
|
||||
|
|
|
|||
|
|
@ -1183,6 +1183,10 @@
|
|||
<source>logsMailerSuccess</source>
|
||||
<translation>ログが %1 にアップロードされました</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>contactsTitle</source>
|
||||
<translation type="unfinished">連絡先</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAudio</name>
|
||||
|
|
|
|||
|
|
@ -1183,6 +1183,10 @@ Spustelėkite čia: <a href="%1">%1</a>
|
|||
<source>logsMailerSuccess</source>
|
||||
<translation>Žurnalai buvo įkelti į %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>contactsTitle</source>
|
||||
<translation type="unfinished">Kontaktai</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAudio</name>
|
||||
|
|
|
|||
|
|
@ -1182,6 +1182,10 @@ Clique aqui: <a href="%1">%1 </a></translation>
|
|||
<source>logsMailerSuccess</source>
|
||||
<translation>Os registos foram enviados para %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>contactsTitle</source>
|
||||
<translation type="unfinished">Contatos</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAudio</name>
|
||||
|
|
|
|||
|
|
@ -1183,6 +1183,10 @@
|
|||
<source>logsMailerSuccess</source>
|
||||
<translation>Журналы были загружены в %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>contactsTitle</source>
|
||||
<translation type="unfinished">Контакты</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAudio</name>
|
||||
|
|
|
|||
|
|
@ -1183,6 +1183,10 @@ Klicka här: <a href="%1">%1</a>
|
|||
<source>logsMailerSuccess</source>
|
||||
<translation>Loggar laddades upp till %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>contactsTitle</source>
|
||||
<translation type="unfinished">Kontakter</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAudio</name>
|
||||
|
|
|
|||
|
|
@ -1183,6 +1183,10 @@ Buraya tıklayın: <a href="%1">%1</a>
|
|||
<source>logsMailerSuccess</source>
|
||||
<translation>Günlükler %1 dosyasına yüklendi</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>contactsTitle</source>
|
||||
<translation type="unfinished">Kişiler</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAudio</name>
|
||||
|
|
|
|||
|
|
@ -1183,6 +1183,10 @@
|
|||
<source>logsMailerSuccess</source>
|
||||
<translation>Журнали було вивантажено до %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>contactsTitle</source>
|
||||
<translation type="unfinished">Контакти</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAudio</name>
|
||||
|
|
|
|||
|
|
@ -1183,6 +1183,10 @@
|
|||
<source>logsMailerSuccess</source>
|
||||
<translation>日志已上传到 %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>contactsTitle</source>
|
||||
<translation type="unfinished">联系人</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsAudio</name>
|
||||
|
|
|
|||
|
|
@ -129,6 +129,7 @@ if (WIN32)
|
|||
install(FILES ${GRAMMAR_FILES} DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/belr/grammars/" )
|
||||
install(DIRECTORY "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_DATAROOTDIR}/images" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}" USE_SOURCE_PERMISSIONS OPTIONAL)
|
||||
install(DIRECTORY "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_DATAROOTDIR}/sounds" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}" USE_SOURCE_PERMISSIONS)
|
||||
install(DIRECTORY "${CMAKE_INSTALL_PREFIX}/plugins/" DESTINATION "plugins" USE_SOURCE_PERMISSIONS OPTIONAL)
|
||||
install(FILES "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_DATAROOTDIR}/Linphone/rootca.pem" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/${EXECUTABLE_NAME}/")
|
||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../../assets/linphonerc-factory" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/${EXECUTABLE_NAME}")
|
||||
set(APP_QT_CONF_DPI "0")
|
||||
|
|
|
|||
44
linphone-app/include/LinphoneApp/LinphonePlugin.hpp
Normal file
44
linphone-app/include/LinphoneApp/LinphonePlugin.hpp
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#ifndef LINPHONE_APP_PLUGIN_H
|
||||
#define LINPHONE_APP_PLUGIN_H
|
||||
#include <QtPlugin>
|
||||
#include <QObject>
|
||||
#include <QVersionNumber>
|
||||
// Overload this class to make a plugin for the address book importer
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#ifdef ENABLE_APP_EXPORT_PLUGIN
|
||||
#define LINPHONEAPP_DLL_API __declspec(dllexport)
|
||||
#else
|
||||
#define LINPHONEAPP_DLL_API __declspec(dllimport)
|
||||
#endif
|
||||
#else
|
||||
#ifdef ENABLE_APP_EXPORT_PLUGIN
|
||||
#define LINPHONEAPP_DLL_API __attribute__((visibility("default")))
|
||||
#else
|
||||
#define LINPHONEAPP_DLL_API
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class QPluginLoader;
|
||||
class PluginDataAPI;
|
||||
|
||||
class LinphonePlugin
|
||||
{
|
||||
// These macro are an example to use in the custom plugin
|
||||
//Q_OBJECT
|
||||
//Q_PLUGIN_METADATA(IID LinphonePlugin_iid FILE "PluginExample.json")// You have to set the Capabilities for your plugin
|
||||
//Q_INTERFACES(LinphonePlugin)
|
||||
//-----------------------------------------------------------
|
||||
public:
|
||||
virtual ~LinphonePlugin() {}
|
||||
|
||||
// Specific to DataAPI. See their section
|
||||
virtual QString getGUIDescriptionToJson() const = 0;// Describe the GUI to be used for the plugin. Json are in Utf8
|
||||
|
||||
virtual PluginDataAPI * createInstance(void* core, QPluginLoader * pluginLoader) = 0;// Create an instance of the plugin in LinphoneAppPluginType.
|
||||
};
|
||||
|
||||
#define LinphonePlugin_iid "linphoneApp.LinphonePlugin/1.0"
|
||||
Q_DECLARE_INTERFACE(LinphonePlugin, LinphonePlugin_iid)
|
||||
|
||||
#endif // LINPHONE_APP_PLUGIN_H
|
||||
55
linphone-app/include/LinphoneApp/PluginDataAPI.hpp
Normal file
55
linphone-app/include/LinphoneApp/PluginDataAPI.hpp
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#ifndef LINPHONE_APP_PLUGIN_DATA_H
|
||||
#define LINPHONE_APP_PLUGIN_DATA_H
|
||||
|
||||
#include <QVariantMap>
|
||||
|
||||
#ifdef ENABLE_APP_EXPORT_PLUGIN
|
||||
#include "include/LinphoneApp/LinphonePlugin.hpp"
|
||||
#else
|
||||
#include <LinphoneApp/LinphonePlugin.hpp>
|
||||
#endif
|
||||
|
||||
class QPluginLoader;
|
||||
class LinphonePlugin;
|
||||
|
||||
// This class regroup Data interface for importing contacts
|
||||
class LINPHONEAPP_DLL_API PluginDataAPI : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
typedef enum{ALL=-1, NOTHING=0, CONTACTS=1, LAST} PluginCapability;// LAST must not be used. It is for internal process only.
|
||||
PluginDataAPI(LinphonePlugin * plugin, void * linphoneCore, QPluginLoader * pluginLoader);
|
||||
virtual ~PluginDataAPI();
|
||||
|
||||
virtual bool isValid(const bool &pRequestData=true, QString * pError= nullptr) = 0; // Test if the passed data is valid. Used for saving.
|
||||
virtual void setInputFields(const PluginCapability& capability = ALL, const QVariantMap &inputFields = QVariantMap());// Set all inputs for the selected capability
|
||||
virtual QMap<PluginCapability, QVariantMap> getInputFields(const PluginCapability& capability);// Get all inputs
|
||||
virtual QMap<PluginCapability, QVariantMap> getInputFieldsToSave(const PluginCapability& capability = ALL);// Get all inputs to save in config file.
|
||||
|
||||
// Configuration management
|
||||
void setSectionConfiguration(const QString& section);
|
||||
virtual void loadConfiguration(const PluginCapability& capability = ALL);
|
||||
virtual void saveConfiguration(const PluginCapability& capability = ALL);
|
||||
virtual void cleanAllConfigurations();// Remove all saved configuration
|
||||
|
||||
QPluginLoader * getPluginLoader();// Used to retrieve the loader that created this instance, in order to unload it
|
||||
|
||||
virtual void run(const PluginCapability& actionType)=0;
|
||||
|
||||
signals:
|
||||
void dataReceived(const PluginCapability& actionType, QVector<QMultiMap<QString,QString> > data);
|
||||
//------------------------------------
|
||||
|
||||
void inputFieldsChanged(const PluginCapability&, const QVariantMap &inputFields); // Input fields have been changed
|
||||
void message(const QtMsgType& type, const QString &message); // Send a message to GUI
|
||||
|
||||
|
||||
protected:
|
||||
QMap<PluginCapability, QVariantMap> mInputFields;
|
||||
void * mLinphoneCore;
|
||||
LinphonePlugin * mPlugin;
|
||||
QPluginLoader * mPluginLoader;
|
||||
private:
|
||||
QString mSectionConfigurationName;
|
||||
};
|
||||
|
||||
#endif // LINPHONE_APP_PLUGIN_DATA_H
|
||||
6
linphone-app/include/LinphoneApp/PluginExample.json
Normal file
6
linphone-app/include/LinphoneApp/PluginExample.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"Name" : "ExamplePlugin",
|
||||
"Version" : "1.0.0",
|
||||
"Capabilities" : "Contacts",
|
||||
"Description" : "This is an example for describing your plugin. Replace all fields above to be usable by the Application."
|
||||
}
|
||||
40
linphone-app/include/LinphoneApp/PluginNetworkHelper.hpp
Normal file
40
linphone-app/include/LinphoneApp/PluginNetworkHelper.hpp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#ifndef LINPHONE_APP_NETWORK_HELPER_H
|
||||
#define LINPHONE_APP_NETWORK_HELPER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QtNetwork>
|
||||
// This class is used to define network operation to retrieve Addresses from Network
|
||||
|
||||
|
||||
#ifdef ENABLE_APP_EXPORT_PLUGIN
|
||||
#include "include/LinphoneApp/LinphonePlugin.hpp"
|
||||
#else
|
||||
#include <LinphoneApp/LinphonePlugin.hpp>
|
||||
#endif
|
||||
|
||||
class LINPHONEAPP_DLL_API PluginNetworkHelper : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PluginNetworkHelper();
|
||||
virtual ~PluginNetworkHelper();
|
||||
virtual QString prepareRequest()const=0; // Called when requesting an Url.
|
||||
|
||||
void request();
|
||||
|
||||
QPointer<QNetworkReply> mNetworkReply;
|
||||
QNetworkAccessManager mManager;
|
||||
signals:
|
||||
void requestFinished(const QByteArray &data); // The request is over and have data
|
||||
void message(const QtMsgType &type, const QString &message);
|
||||
|
||||
private:
|
||||
void handleReadyData();
|
||||
void handleFinished ();
|
||||
void handleError (QNetworkReply::NetworkError code);
|
||||
void handleSslErrors (const QList<QSslError> &sslErrors);
|
||||
|
||||
QByteArray mBuffer;
|
||||
};
|
||||
|
||||
#endif // LINPHONE_APP_NETWORK_HELPER_H
|
||||
|
|
@ -583,6 +583,7 @@ void App::registerTypes () {
|
|||
registerType<ConferenceHelperModel>("ConferenceHelperModel");
|
||||
registerType<ConferenceModel>("ConferenceModel");
|
||||
registerType<ContactsListProxyModel>("ContactsListProxyModel");
|
||||
registerType<ContactsImporterListProxyModel>("ContactsImporterListProxyModel");
|
||||
registerType<FileDownloader>("FileDownloader");
|
||||
registerType<FileExtractor>("FileExtractor");
|
||||
registerType<HistoryProxyModel>("HistoryProxyModel");
|
||||
|
|
@ -601,6 +602,7 @@ void App::registerTypes () {
|
|||
registerUncreatableType<ChatModel>("ChatModel");
|
||||
registerUncreatableType<ConferenceHelperModel::ConferenceAddModel>("ConferenceAddModel");
|
||||
registerUncreatableType<ContactModel>("ContactModel");
|
||||
registerUncreatableType<ContactsImporterModel>("ContactsImporterModel");
|
||||
registerUncreatableType<HistoryModel>("HistoryModel");
|
||||
registerUncreatableType<SipAddressObserver>("SipAddressObserver");
|
||||
registerUncreatableType<VcardModel>("VcardModel");
|
||||
|
|
@ -616,6 +618,7 @@ void App::registerSharedTypes () {
|
|||
registerSharedSingletonType<SipAddressesModel, &CoreManager::getSipAddressesModel>("SipAddressesModel");
|
||||
registerSharedSingletonType<CallsListModel, &CoreManager::getCallsListModel>("CallsListModel");
|
||||
registerSharedSingletonType<ContactsListModel, &CoreManager::getContactsListModel>("ContactsListModel");
|
||||
registerSharedSingletonType<ContactsImporterListModel, &CoreManager::getContactsImporterListModel>("ContactsImporterListModel");
|
||||
}
|
||||
|
||||
void App::registerToolTypes () {
|
||||
|
|
@ -625,6 +628,7 @@ void App::registerToolTypes () {
|
|||
registerToolType<DesktopTools>("DesktopTools");
|
||||
registerToolType<TextToSpeech>("TextToSpeech");
|
||||
registerToolType<Units>("Units");
|
||||
registerToolType<ContactsImporterPluginsManager>("ContactsImporterPluginsManager");
|
||||
}
|
||||
|
||||
void App::registerSharedToolTypes () {
|
||||
|
|
|
|||
|
|
@ -41,7 +41,12 @@ namespace {
|
|||
constexpr char PathCodecs[] = "/codecs/";
|
||||
constexpr char PathTools[] = "/tools/";
|
||||
constexpr char PathLogs[] = "/logs/";
|
||||
//constexpr char PathPlugins[] = "/plugins/"; // Unused
|
||||
#ifdef APPLE
|
||||
constexpr char PathPlugins[] = "/Plugins/";
|
||||
#else
|
||||
constexpr char PathPlugins[] = "/plugins/";
|
||||
#endif
|
||||
constexpr char PathPluginsApp[] = "app/";
|
||||
constexpr char PathThumbnails[] = "/thumbnails/";
|
||||
constexpr char PathUserCertificates[] = "/usr-crt/";
|
||||
|
||||
|
|
@ -159,6 +164,10 @@ static inline QString getAppPackageMsPluginsDirPath () {
|
|||
return dir.absolutePath();
|
||||
}
|
||||
|
||||
static inline QString getAppPackagePluginsDirPath () {
|
||||
return getAppPackageDir().absolutePath() + PathPlugins;
|
||||
}
|
||||
|
||||
static inline QString getAppAssistantConfigDirPath () {
|
||||
return getAppPackageDataDirPath() + PathAssistantConfig;
|
||||
}
|
||||
|
|
@ -187,6 +196,9 @@ static inline QString getAppMessageHistoryFilePath () {
|
|||
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + PathMessageHistoryList;
|
||||
}
|
||||
|
||||
static inline QString getAppPluginsDirPath () {
|
||||
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)+ PathPlugins;
|
||||
}
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool Paths::filePathExists (const string &path) {
|
||||
|
|
@ -265,6 +277,21 @@ string Paths::getPackageMsPluginsDirPath () {
|
|||
return getReadableDirPath(getAppPackageMsPluginsDirPath());
|
||||
}
|
||||
|
||||
string Paths::getPackagePluginsAppDirPath () {
|
||||
return getReadableDirPath(getAppPackagePluginsDirPath()+PathPluginsApp);
|
||||
}
|
||||
|
||||
string Paths::getPluginsAppDirPath () {
|
||||
return getWritableDirPath(getAppPluginsDirPath()+PathPluginsApp);
|
||||
}
|
||||
|
||||
QStringList Paths::getPluginsAppFolders() {
|
||||
QStringList pluginPaths;
|
||||
pluginPaths << Utils::coreStringToAppString(Paths::getPluginsAppDirPath());
|
||||
pluginPaths << Utils::coreStringToAppString(Paths::getPackagePluginsAppDirPath());
|
||||
return pluginPaths;
|
||||
}
|
||||
|
||||
string Paths::getRootCaFilePath () {
|
||||
return getReadableFilePath(getAppRootCaFilePath());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
namespace Paths {
|
||||
bool filePathExists (const std::string &path);
|
||||
|
||||
|
||||
std::string getAssistantConfigDirPath ();
|
||||
std::string getAvatarsDirPath ();
|
||||
std::string getCallHistoryFilePath ();
|
||||
|
|
@ -42,6 +43,9 @@ namespace Paths {
|
|||
std::string getMessageHistoryFilePath ();
|
||||
std::string getPackageDataDirPath ();
|
||||
std::string getPackageMsPluginsDirPath ();
|
||||
std::string getPackagePluginsAppDirPath ();
|
||||
std::string getPluginsAppDirPath ();
|
||||
QStringList getPluginsAppFolders();
|
||||
std::string getRootCaFilePath ();
|
||||
std::string getThumbnailsDirPath ();
|
||||
std::string getToolsDirPath ();
|
||||
|
|
|
|||
|
|
@ -37,6 +37,10 @@
|
|||
#include "contact/VcardModel.hpp"
|
||||
#include "contacts/ContactsListModel.hpp"
|
||||
#include "contacts/ContactsListProxyModel.hpp"
|
||||
#include "contacts/ContactsImporterModel.hpp"
|
||||
#include "contacts/ContactsImporterPluginsManager.hpp"
|
||||
#include "contacts/ContactsImporterListModel.hpp"
|
||||
#include "contacts/ContactsImporterListProxyModel.hpp"
|
||||
#include "core/CoreHandlers.hpp"
|
||||
#include "core/CoreManager.hpp"
|
||||
#include "file/FileDownloader.hpp"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,209 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <QQmlApplicationEngine>
|
||||
|
||||
#include "app/App.hpp"
|
||||
#include "ContactsImporterModel.hpp"
|
||||
#include "ContactsImporterListModel.hpp"
|
||||
#include "ContactsImporterPluginsManager.hpp"
|
||||
#include "components/core/CoreManager.hpp"
|
||||
#include "utils/Utils.hpp"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
using namespace std;
|
||||
|
||||
ContactsImporterListModel::ContactsImporterListModel (QObject *parent) : QAbstractListModel(parent) {
|
||||
// Init contacts with linphone friends list.
|
||||
mMaxContactsImporterId = -1;
|
||||
QQmlEngine *engine = App::getInstance()->getEngine();
|
||||
auto config = CoreManager::getInstance()->getCore()->getConfig();
|
||||
PluginsManager::getPlugins();// Initialize list
|
||||
// Read configuration file
|
||||
std::list<std::string> sections = config->getSectionsNamesList();
|
||||
for(auto section : sections){
|
||||
QString qtSection = Utils::coreStringToAppString(section);
|
||||
QStringList parse = qtSection.split("_");// PluginsManager::gPluginsConfigSection_id_capability
|
||||
if( parse.size() > 2){
|
||||
QVariantMap importData;
|
||||
if( parse[2].toInt() == PluginDataAPI::CONTACTS){// We only care about Contacts
|
||||
int id = parse[1].toInt();
|
||||
mMaxContactsImporterId = qMax(id, mMaxContactsImporterId);
|
||||
std::list<std::string> keys = config->getKeysNamesList(section);
|
||||
auto keyName = std::find(keys.begin(), keys.end(), "pluginID");
|
||||
if( keyName != keys.end()){
|
||||
QString pluginID = Utils::coreStringToAppString(config->getString(section, *keyName, ""));
|
||||
PluginDataAPI* data = static_cast<PluginDataAPI*>(PluginsManager::createInstance(pluginID));
|
||||
if(data) {
|
||||
ContactsImporterModel * model = new ContactsImporterModel(data, this);
|
||||
// See: http://doc.qt.io/qt-5/qtqml-cppintegration-data.html#data-ownership
|
||||
// The returned value must have a explicit parent or a QQmlEngine::CppOwnership.
|
||||
engine->setObjectOwnership(model, QQmlEngine::CppOwnership);
|
||||
model->setIdentity(id);
|
||||
model->loadConfiguration();// Read the configuration contacts inside the plugin
|
||||
addContactsImporter(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// GUI methods
|
||||
|
||||
int ContactsImporterListModel::rowCount (const QModelIndex &) const {
|
||||
return mList.count();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> ContactsImporterListModel::roleNames () const {
|
||||
QHash<int, QByteArray> roles;
|
||||
roles[Qt::DisplayRole] = "$contactsImporter";
|
||||
return roles;
|
||||
}
|
||||
|
||||
QVariant ContactsImporterListModel::data (const QModelIndex &index, int role) const {
|
||||
int row = index.row();
|
||||
|
||||
if (!index.isValid() || row < 0 || row >= mList.count())
|
||||
return QVariant();
|
||||
|
||||
if (role == Qt::DisplayRole)
|
||||
return QVariant::fromValue(mList[row]);
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool ContactsImporterListModel::removeRow (int row, const QModelIndex &parent) {
|
||||
return removeRows(row, 1, parent);
|
||||
}
|
||||
|
||||
bool ContactsImporterListModel::removeRows (int row, int count, const QModelIndex &parent) {
|
||||
int limit = row + count - 1;
|
||||
|
||||
if (row < 0 || count < 0 || limit >= mList.count())
|
||||
return false;
|
||||
|
||||
beginRemoveRows(parent, row, limit);
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
ContactsImporterModel *contactsImporter = dynamic_cast<ContactsImporterModel*>(mList.takeAt(row));
|
||||
emit contactsImporterRemoved(contactsImporter);
|
||||
contactsImporter->deleteLater();
|
||||
}
|
||||
|
||||
endRemoveRows();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
ContactsImporterModel *ContactsImporterListModel::findContactsImporterModelFromId (const int &id) const {
|
||||
auto it = find_if(mList.begin(), mList.end(), [id](PluginsModel *contactsImporterModel) {
|
||||
return contactsImporterModel->getIdentity() == id;
|
||||
});
|
||||
return it != mList.end() ? dynamic_cast<ContactsImporterModel*>(*it) : nullptr;
|
||||
}
|
||||
|
||||
QList<PluginsModel*> ContactsImporterListModel::getList(){
|
||||
return mList;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
ContactsImporterModel *ContactsImporterListModel::createContactsImporter(QVariantMap data){
|
||||
ContactsImporterModel *contactsImporter = nullptr;
|
||||
if( data.contains("pluginID")){
|
||||
PluginDataAPI * dataInstance = static_cast<PluginDataAPI*>(PluginsManager::createInstance(data["pluginID"].toString()));
|
||||
if(dataInstance) {
|
||||
// get default values
|
||||
contactsImporter = new ContactsImporterModel(dataInstance, this);
|
||||
App::getInstance()->getEngine()->setObjectOwnership(contactsImporter, QQmlEngine::CppOwnership);
|
||||
QVariantMap newData = ContactsImporterPluginsManager::getDefaultValues(data["pluginID"].toString());// Start with defaults from plugin
|
||||
QVariantMap InstanceFields = contactsImporter->getFields();
|
||||
for(auto field = InstanceFields.begin() ; field != InstanceFields.end() ; ++field)// Insert or Update with the defaults of an instance
|
||||
newData[field.key()] = field.value();
|
||||
for(auto field = data.begin() ; field != data.end() ; ++field)// Insert or Update with Application data
|
||||
newData[field.key()] = field.value();
|
||||
contactsImporter->setIdentity(++mMaxContactsImporterId);
|
||||
contactsImporter->setFields(newData);
|
||||
|
||||
int row = mList.count();
|
||||
|
||||
beginInsertRows(QModelIndex(), row, row);
|
||||
addContactsImporter(contactsImporter);
|
||||
endInsertRows();
|
||||
|
||||
emit contactsImporterAdded(contactsImporter);
|
||||
}
|
||||
}
|
||||
|
||||
return contactsImporter;
|
||||
|
||||
}
|
||||
ContactsImporterModel *ContactsImporterListModel::addContactsImporter (QVariantMap data, int pId) {
|
||||
ContactsImporterModel *contactsImporter = findContactsImporterModelFromId(pId);
|
||||
if (contactsImporter) {
|
||||
contactsImporter->setFields(data);
|
||||
return contactsImporter;
|
||||
}else
|
||||
return createContactsImporter(data);
|
||||
}
|
||||
|
||||
void ContactsImporterListModel::removeContactsImporter (ContactsImporterModel *contactsImporter) {
|
||||
int index = mList.indexOf(contactsImporter);
|
||||
if (index >=0){
|
||||
if( contactsImporter->getIdentity() >=0 ){// Remove from configuration
|
||||
int id = contactsImporter->getIdentity();
|
||||
string section = Utils::appStringToCoreString(PluginsManager::gPluginsConfigSection+"_"+QString::number(id)+"_"+QString::number(PluginDataAPI::CONTACTS));
|
||||
CoreManager::getInstance()->getCore()->getConfig()->cleanSection(section);
|
||||
if( id == mMaxContactsImporterId)// Decrease mMaxContactsImporterId in a safe way
|
||||
--mMaxContactsImporterId;
|
||||
}
|
||||
removeRow(index);
|
||||
}
|
||||
}
|
||||
|
||||
void ContactsImporterListModel::importContacts(const int &pId){
|
||||
if( pId >=0) {
|
||||
ContactsImporterModel *contactsImporter = findContactsImporterModelFromId(pId);
|
||||
if( contactsImporter)
|
||||
contactsImporter->importContacts();
|
||||
}else // Import from all current connectors
|
||||
for(auto importer : mList)
|
||||
dynamic_cast<ContactsImporterModel*>(importer)->importContacts();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void ContactsImporterListModel::addContactsImporter (ContactsImporterModel *contactsImporter) {
|
||||
// Connect all update signals
|
||||
QObject::connect(contactsImporter, &ContactsImporterModel::fieldsChanged, this, [this, contactsImporter]() {
|
||||
emit contactsImporterUpdated(contactsImporter);
|
||||
});
|
||||
QObject::connect(contactsImporter, &ContactsImporterModel::identityChanged, this, [this, contactsImporter]() {
|
||||
emit contactsImporterUpdated(contactsImporter);
|
||||
});
|
||||
mList << contactsImporter;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------
|
||||
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CONTACTS_IMPORTER_LIST_MODEL_H_
|
||||
#define CONTACTS_IMPORTER_LIST_MODEL_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
class ContactsImporterModel;
|
||||
class PluginsModel;
|
||||
|
||||
// Store all connectors
|
||||
|
||||
class ContactsImporterListModel : public QAbstractListModel {
|
||||
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
ContactsImporterListModel (QObject *parent = Q_NULLPTR);
|
||||
|
||||
int rowCount (const QModelIndex &index = QModelIndex()) const override;
|
||||
|
||||
QHash<int, QByteArray> roleNames () const override;
|
||||
QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
bool removeRow (int row, const QModelIndex &parent = QModelIndex());
|
||||
bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override;
|
||||
|
||||
ContactsImporterModel *findContactsImporterModelFromId (const int &id) const;
|
||||
QList<PluginsModel*> getList();
|
||||
|
||||
Q_INVOKABLE ContactsImporterModel *createContactsImporter(QVariantMap data);
|
||||
Q_INVOKABLE ContactsImporterModel *addContactsImporter (QVariantMap data, int id=-1);
|
||||
Q_INVOKABLE void removeContactsImporter (ContactsImporterModel *importer);
|
||||
Q_INVOKABLE void importContacts(const int &id = -1); // Import contacts for all enabled importer if -1
|
||||
//-----------------------------------------------------------------------------------
|
||||
|
||||
signals:
|
||||
void contactsImporterAdded (ContactsImporterModel *contact);
|
||||
void contactsImporterRemoved (const ContactsImporterModel *contact);
|
||||
void contactsImporterUpdated (ContactsImporterModel *contact);
|
||||
|
||||
private:
|
||||
void addContactsImporter (ContactsImporterModel *contactsImporter);
|
||||
|
||||
QList<PluginsModel *> mList;
|
||||
int mMaxContactsImporterId; // Used to ensure unicity on ID when creating a connector
|
||||
};
|
||||
|
||||
#endif // CONTACTS_IMPORTER_LIST_MODEL_H_
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "components/contacts/ContactsImporterModel.hpp"
|
||||
#include "components/core/CoreManager.hpp"
|
||||
#include "utils/Utils.hpp"
|
||||
|
||||
#include "ContactsImporterListModel.hpp"
|
||||
#include "ContactsImporterListProxyModel.hpp"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
ContactsImporterListProxyModel::ContactsImporterListProxyModel (QObject *parent) : QSortFilterProxyModel(parent) {
|
||||
setSourceModel(CoreManager::getInstance()->getContactsImporterListModel());
|
||||
sort(0);// Sort by identity
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool ContactsImporterListProxyModel::filterAcceptsRow (
|
||||
int sourceRow,
|
||||
const QModelIndex &sourceParent
|
||||
) const {
|
||||
Q_UNUSED(sourceRow)
|
||||
Q_UNUSED(sourceParent)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ContactsImporterListProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const {
|
||||
const ContactsImporterModel *contactA = sourceModel()->data(left).value<ContactsImporterModel *>();
|
||||
const ContactsImporterModel *contactB = sourceModel()->data(right).value<ContactsImporterModel *>();
|
||||
|
||||
return contactA->getIdentity() <= contactB->getIdentity();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CONTACTS_IMPORTER_LIST_PROXY_MODEL_H_
|
||||
#define CONTACTS_IMPORTER_LIST_PROXY_MODEL_H_
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
class ContactsImporterModel;
|
||||
class ContactsImporterListModel;
|
||||
|
||||
// Manage the list of connectors
|
||||
|
||||
class ContactsImporterListProxyModel : public QSortFilterProxyModel {
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
ContactsImporterListProxyModel (QObject *parent = Q_NULLPTR);
|
||||
|
||||
protected:
|
||||
bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override;
|
||||
bool lessThan (const QModelIndex &left, const QModelIndex &right) const override;
|
||||
|
||||
};
|
||||
|
||||
#endif // CONTACTS_IMPORTER_LIST_PROXY_MODEL_H_
|
||||
129
linphone-app/src/components/contacts/ContactsImporterModel.cpp
Normal file
129
linphone-app/src/components/contacts/ContactsImporterModel.cpp
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ContactsImporterModel.hpp"
|
||||
#include "ContactsImporterPluginsManager.hpp"
|
||||
#include "../../utils/Utils.hpp"
|
||||
|
||||
//#include <linphoneapp/contacts/ContactsImporterDataAPI.hpp>
|
||||
#include "include/LinphoneApp/PluginDataAPI.hpp"
|
||||
|
||||
#include <QPluginLoader>
|
||||
#include <QDebug>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
ContactsImporterModel::ContactsImporterModel (PluginDataAPI * data, QObject *parent) : PluginsModel(parent) {
|
||||
mIdentity = -1;
|
||||
mData = nullptr;
|
||||
setDataAPI(data);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void ContactsImporterModel::setDataAPI(PluginDataAPI *data){
|
||||
if(mData){// Unload the current plugin loader and delete it from memory
|
||||
QPluginLoader * loader = mData->getPluginLoader();
|
||||
delete mData;
|
||||
if(loader){
|
||||
loader->unload();
|
||||
delete loader;
|
||||
}
|
||||
mData = data;
|
||||
}else
|
||||
mData = data;
|
||||
if( mData){
|
||||
connect(mData, &PluginDataAPI::inputFieldsChanged, this, &ContactsImporterModel::fieldsChanged);
|
||||
connect(mData, &PluginDataAPI::message, this, &ContactsImporterModel::messageReceived);
|
||||
connect(mData, &PluginDataAPI::dataReceived, this, &ContactsImporterModel::parsedContacts);
|
||||
}
|
||||
}
|
||||
PluginDataAPI *ContactsImporterModel::getDataAPI(){
|
||||
return mData;
|
||||
}
|
||||
bool ContactsImporterModel::isUsable(){
|
||||
if( mData){
|
||||
if( !mData->getPluginLoader()->isLoaded())
|
||||
mData->getPluginLoader()->load();
|
||||
return mData->getPluginLoader()->isLoaded();
|
||||
}else
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariantMap ContactsImporterModel::getFields(){
|
||||
return (isUsable()?mData->getInputFields(PluginDataAPI::CONTACTS)[PluginDataAPI::CONTACTS] :QVariantMap());
|
||||
}
|
||||
|
||||
void ContactsImporterModel::setFields(const QVariantMap &pFields){
|
||||
if( isUsable())
|
||||
mData->setInputFields(PluginDataAPI::CONTACTS, pFields);
|
||||
}
|
||||
|
||||
int ContactsImporterModel::getIdentity()const{
|
||||
return mIdentity;
|
||||
}
|
||||
|
||||
void ContactsImporterModel::setIdentity(const int &pIdentity){
|
||||
if( mIdentity != pIdentity){
|
||||
mIdentity = pIdentity;
|
||||
if(mData && mData->getPluginLoader()->isLoaded())
|
||||
mData->setSectionConfiguration(PluginsManager::gPluginsConfigSection+"_"+QString::number(mIdentity));
|
||||
emit identityChanged(mIdentity);
|
||||
}
|
||||
}
|
||||
|
||||
void ContactsImporterModel::loadConfiguration(){
|
||||
if(isUsable())
|
||||
mData->loadConfiguration(PluginDataAPI::CONTACTS);
|
||||
}
|
||||
|
||||
void ContactsImporterModel::importContacts(){
|
||||
if(isUsable()){
|
||||
qInfo() << "Importing contacts with " << mData->getInputFields(PluginDataAPI::CONTACTS)[PluginDataAPI::CONTACTS]["pluginTitle"];
|
||||
QPluginLoader * loader = mData->getPluginLoader();
|
||||
if( !loader)
|
||||
qWarning() << "Loader is NULL";
|
||||
else{
|
||||
qWarning() << "Plugin loaded Status : " << loader->isLoaded() << " for " << loader->fileName();
|
||||
}
|
||||
mData->run(PluginDataAPI::CONTACTS);
|
||||
}else
|
||||
qWarning() << "Cannot import contacts, mData is NULL or plugin cannot be loaded ";
|
||||
}
|
||||
|
||||
void ContactsImporterModel::parsedContacts(const PluginDataAPI::PluginCapability& actionType, QVector<QMultiMap<QString, QString> > contacts){
|
||||
if(actionType == PluginDataAPI::CONTACTS)
|
||||
ContactsImporterPluginsManager::importContacts(contacts);
|
||||
}
|
||||
|
||||
void ContactsImporterModel::updateInputs(const PluginDataAPI::PluginCapability& capability, const QVariantMap &inputs){
|
||||
if(capability == PluginDataAPI::CONTACTS)
|
||||
setFields(inputs);
|
||||
}
|
||||
|
||||
void ContactsImporterModel::messageReceived(const QtMsgType& type, const QString &message){
|
||||
if( type == QtMsgType::QtInfoMsg)
|
||||
emit statusMessage(message);
|
||||
else
|
||||
emit errorMessage(message);
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CONTACTS_IMPORTER_MODEL_H_
|
||||
#define CONTACTS_IMPORTER_MODEL_H_
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariantMap>
|
||||
|
||||
#include "utils/plugins/PluginsManager.hpp"
|
||||
#include "include/LinphoneApp/PluginDataAPI.hpp"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
class ContactsImporterModel : public PluginsModel {
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QVariantMap fields READ getFields WRITE setFields NOTIFY fieldsChanged)
|
||||
Q_PROPERTY(int identity READ getIdentity WRITE setIdentity NOTIFY identityChanged)
|
||||
|
||||
public:
|
||||
ContactsImporterModel (PluginDataAPI * data, QObject *parent = nullptr);
|
||||
|
||||
void setDataAPI(PluginDataAPI *data);
|
||||
PluginDataAPI *getDataAPI();
|
||||
bool isUsable(); // Return true if the plugin can be load and has been loaded.
|
||||
|
||||
QVariantMap getFields();
|
||||
void setFields(const QVariantMap &pFields);
|
||||
|
||||
int getIdentity()const;
|
||||
void setIdentity(const int &pIdentity);
|
||||
|
||||
void loadConfiguration();
|
||||
Q_INVOKABLE void importContacts();
|
||||
|
||||
public slots:
|
||||
void parsedContacts(const PluginDataAPI::PluginCapability& actionType, QVector<QMultiMap<QString, QString> > contacts);
|
||||
void updateInputs(const PluginDataAPI::PluginCapability&, const QVariantMap &inputs);
|
||||
void messageReceived(const QtMsgType& type, const QString &message);
|
||||
|
||||
signals:
|
||||
void fieldsChanged (const PluginDataAPI::PluginCapability&, QVariantMap fields);
|
||||
void identityChanged(int identity);
|
||||
void errorMessage(const QString& message);
|
||||
void statusMessage(const QString& message);
|
||||
|
||||
private:
|
||||
int mIdentity; // The identity of the model in configuration. It must be unique between all contact plugins.
|
||||
PluginDataAPI *mData; // The instance of the plugin with its plugin Loader.
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(ContactsImporterModel *);
|
||||
|
||||
#endif // CONTACTS_IMPORTER_MODEL_H_
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ContactsImporterPluginsManager.hpp"
|
||||
#include "ContactsImporterModel.hpp"
|
||||
#include "include/LinphoneApp/PluginNetworkHelper.hpp"
|
||||
|
||||
#include "utils/Utils.hpp"
|
||||
#include "app/paths/Paths.hpp"
|
||||
#include "components/contact/VcardModel.hpp"
|
||||
#include "components/contacts/ContactsListModel.hpp"
|
||||
#include "components/contacts/ContactsImporterListModel.hpp"
|
||||
#include "components/core/CoreManager.hpp"
|
||||
#include "components/sip-addresses/SipAddressesModel.hpp"
|
||||
|
||||
|
||||
#include <QDir>
|
||||
#include <QPluginLoader>
|
||||
#include <QDebug>
|
||||
#include <QJsonDocument>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
|
||||
// =============================================================================
|
||||
|
||||
ContactsImporterPluginsManager::ContactsImporterPluginsManager(QObject * parent) : PluginsManager(parent){
|
||||
}
|
||||
|
||||
QVariantMap ContactsImporterPluginsManager::getContactsImporterPluginDescription(const QString& pluginID) {
|
||||
QVariantMap description;
|
||||
QJsonDocument doc = getJson(pluginID);
|
||||
|
||||
description = doc.toVariant().toMap();
|
||||
|
||||
if(description.contains("fields")){
|
||||
auto fields = description["fields"].toList();
|
||||
auto removedFields = std::remove_if(fields.begin(), fields.end(),
|
||||
[](const QVariant& f){
|
||||
auto field = f.toMap();
|
||||
return field.contains("capability") && ((field["capability"].toInt() & PluginDataAPI::CONTACTS) != PluginDataAPI::CONTACTS);
|
||||
});
|
||||
fields.erase(removedFields, fields.end());
|
||||
description["fields"] = fields;
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
void ContactsImporterPluginsManager::openNewPlugin(){
|
||||
PluginsManager::openNewPlugin("Import Address Book Connector");
|
||||
}
|
||||
|
||||
QVariantList ContactsImporterPluginsManager::getPlugins(){
|
||||
return PluginsManager::getPlugins(PluginDataAPI::CONTACTS);
|
||||
}
|
||||
|
||||
void ContactsImporterPluginsManager::importContacts(ContactsImporterModel * model) {
|
||||
if(model){
|
||||
QString pluginID = model->getFields()["pluginID"].toString();
|
||||
if(!pluginID.isEmpty()){
|
||||
if( !PluginsManager::gPluginsMap.contains(pluginID))
|
||||
qInfo() << "Unknown " << pluginID;
|
||||
model->importContacts();
|
||||
}else
|
||||
qWarning() << "Error : Cannot import contacts : pluginID is empty";
|
||||
}
|
||||
}
|
||||
|
||||
void ContactsImporterPluginsManager::importContacts(const QVector<QMultiMap<QString, QString> >& pContacts ){
|
||||
for(int i = 0 ; i < pContacts.size() ; ++i){
|
||||
VcardModel * card = CoreManager::getInstance()->createDetachedVcardModel();
|
||||
SipAddressesModel * sipConvertion = CoreManager::getInstance()->getSipAddressesModel();
|
||||
QString domain = pContacts[i].values("sipDomain").at(0);
|
||||
//if(pContacts[i].contains("phoneNumber"))
|
||||
// card->addSipAddress(sipConvertion->interpretSipAddress(pContacts[i].values("phoneNumber").at(0)+"@"+domain, false));
|
||||
if(pContacts[i].contains("displayName") && pContacts[i].values("displayName").size() > 0)
|
||||
card->setUsername(pContacts[i].values("displayName").at(0));
|
||||
if(pContacts[i].contains("sipUsername") && pContacts[i].values("sipUsername").size() > 0){
|
||||
QString sipUsername = pContacts[i].values("sipUsername").at(0);
|
||||
QString convertedUsername = sipConvertion->interpretSipAddress(sipUsername, domain);
|
||||
if(!convertedUsername.contains(domain)){
|
||||
convertedUsername = convertedUsername.replace('@',"%40")+"@"+domain;
|
||||
}
|
||||
card->addSipAddress(convertedUsername);
|
||||
if( sipUsername.contains('@')){
|
||||
card->addEmail(sipUsername);
|
||||
}
|
||||
}
|
||||
if(pContacts[i].contains("email"))
|
||||
for(auto email : pContacts[i].values("email"))
|
||||
card->addEmail(email);
|
||||
if(pContacts[i].contains("organization"))
|
||||
for(auto company : pContacts[i].values("organization"))
|
||||
card->addCompany(company);
|
||||
if( card->getSipAddresses().size()>0){
|
||||
CoreManager::getInstance()->getContactsListModel()->addContact(card);
|
||||
}else
|
||||
delete card;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CONTACTS_IMPORTER_PLUGINS_MANAGER_MODEL_H_
|
||||
#define CONTACTS_IMPORTER_PLUGINS_MANAGER_MODEL_H_
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariantList>
|
||||
|
||||
// =============================================================================
|
||||
#include "utils/plugins/PluginsManager.hpp"
|
||||
|
||||
class ContactsImporterModel;
|
||||
class PluginContactsDataAPI;
|
||||
|
||||
class QPluginLoader;
|
||||
|
||||
class ContactsImporterPluginsManager : public PluginsManager{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ContactsImporterPluginsManager (QObject *parent = Q_NULLPTR);
|
||||
|
||||
Q_INVOKABLE static void openNewPlugin(); // Open a File Dialog. Test if the file can be load and have a matched version. Replace old plugins from custom paths and with the same plugin title.
|
||||
Q_INVOKABLE static QVariantList getPlugins(); // Get a list of all available plugins
|
||||
Q_INVOKABLE static QVariantMap getContactsImporterPluginDescription(const QString& pluginID); // Get the description of the plugin. It is used for GUI to create dynamically items
|
||||
Q_INVOKABLE static void importContacts(ContactsImporterModel * model); // Request the import of the model
|
||||
static void importContacts(const QVector<QMultiMap<QString, QString> >& contacts ); // Merge these data into contacts
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // CONTACTS_IMPORTER_PLUGINS_MANAGER_MODEL_H_
|
||||
|
|
@ -32,10 +32,12 @@
|
|||
#include "components/chat/ChatModel.hpp"
|
||||
#include "components/contact/VcardModel.hpp"
|
||||
#include "components/contacts/ContactsListModel.hpp"
|
||||
#include "components/contacts/ContactsImporterListModel.hpp"
|
||||
#include "components/history/HistoryModel.hpp"
|
||||
#include "components/settings/AccountSettingsModel.hpp"
|
||||
#include "components/settings/SettingsModel.hpp"
|
||||
#include "components/sip-addresses/SipAddressesModel.hpp"
|
||||
|
||||
#include "utils/Utils.hpp"
|
||||
|
||||
#if defined(Q_OS_MACOS)
|
||||
|
|
@ -46,6 +48,7 @@
|
|||
|
||||
#include "CoreHandlers.hpp"
|
||||
#include "CoreManager.hpp"
|
||||
#include <linphone/core.h>
|
||||
|
||||
#include <linphone/core.h>
|
||||
|
||||
|
|
@ -78,7 +81,6 @@ CoreManager::CoreManager (QObject *parent, const QString &configPath) :
|
|||
QObject::connect(coreHandlers, &CoreHandlers::coreStarted, this, &CoreManager::initCoreManager, Qt::QueuedConnection);
|
||||
QObject::connect(coreHandlers, &CoreHandlers::coreStopped, this, &CoreManager::stopIterate, Qt::QueuedConnection);
|
||||
QObject::connect(coreHandlers, &CoreHandlers::logsUploadStateChanged, this, &CoreManager::handleLogsUploadStateChanged);
|
||||
|
||||
createLinphoneCore(configPath);
|
||||
}
|
||||
|
||||
|
|
@ -93,6 +95,7 @@ CoreManager::~CoreManager(){
|
|||
void CoreManager::initCoreManager(){
|
||||
mCallsListModel = new CallsListModel(this);
|
||||
mContactsListModel = new ContactsListModel(this);
|
||||
mContactsImporterListModel = new ContactsImporterListModel(this);
|
||||
mAccountSettingsModel = new AccountSettingsModel(this);
|
||||
mSettingsModel = new SettingsModel(this);
|
||||
mSipAddressesModel = new SipAddressesModel(this);
|
||||
|
|
@ -225,8 +228,7 @@ void CoreManager::setDatabasesPaths () {
|
|||
SET_DATABASE_PATH(Friends, Paths::getFriendsListFilePath());
|
||||
SET_DATABASE_PATH(CallLogs, Paths::getCallHistoryFilePath());
|
||||
if(QFile::exists(Utils::coreStringToAppString(Paths::getMessageHistoryFilePath()))){
|
||||
linphone_core_set_chat_database_path(mCore->cPtr(), Paths::getMessageHistoryFilePath().c_str());
|
||||
//SET_DATABASE_PATH(Chat, Paths::getMessageHistoryFilePath());// Setting the message database let SDK to migrate data
|
||||
linphone_core_set_chat_database_path(mCore->cPtr(), Paths::getMessageHistoryFilePath().c_str());// Setting the message database let SDK to migrate data
|
||||
QFile::remove(Utils::coreStringToAppString(Paths::getMessageHistoryFilePath()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ class AccountSettingsModel;
|
|||
class CallsListModel;
|
||||
class ChatModel;
|
||||
class ContactsListModel;
|
||||
class ContactsImporterListModel;
|
||||
class CoreHandlers;
|
||||
class EventCountNotifier;
|
||||
class HistoryModel;
|
||||
|
|
@ -93,6 +94,13 @@ public:
|
|||
Q_CHECK_PTR(mContactsListModel);
|
||||
return mContactsListModel;
|
||||
}
|
||||
|
||||
ContactsImporterListModel *getContactsImporterListModel () const {
|
||||
Q_CHECK_PTR(mContactsImporterListModel);
|
||||
return mContactsImporterListModel;
|
||||
}
|
||||
|
||||
|
||||
|
||||
SipAddressesModel *getSipAddressesModel () const {
|
||||
Q_CHECK_PTR(mSipAddressesModel);
|
||||
|
|
@ -180,6 +188,8 @@ private:
|
|||
|
||||
CallsListModel *mCallsListModel = nullptr;
|
||||
ContactsListModel *mContactsListModel = nullptr;
|
||||
ContactsImporterListModel *mContactsImporterListModel = nullptr;
|
||||
|
||||
SipAddressesModel *mSipAddressesModel = nullptr;
|
||||
SettingsModel *mSettingsModel = nullptr;
|
||||
AccountSettingsModel *mAccountSettingsModel = nullptr;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
|
||||
#include <QDir>
|
||||
#include <QtDebug>
|
||||
#include <QPluginLoader>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
|
|
@ -27,6 +29,7 @@
|
|||
#include "app/logger/Logger.hpp"
|
||||
#include "app/paths/Paths.hpp"
|
||||
#include "components/core/CoreManager.hpp"
|
||||
#include "include/LinphoneApp/PluginNetworkHelper.hpp"
|
||||
#include "utils/Utils.hpp"
|
||||
#include "utils/MediastreamerUtils.hpp"
|
||||
#include "SettingsModel.hpp"
|
||||
|
|
@ -41,6 +44,7 @@ namespace {
|
|||
}
|
||||
|
||||
const string SettingsModel::UiSection("ui");
|
||||
const string SettingsModel::ContactsSection("contacts_import");
|
||||
|
||||
SettingsModel::SettingsModel (QObject *parent) : QObject(parent) {
|
||||
CoreManager *coreManager = CoreManager::getInstance();
|
||||
|
|
@ -102,6 +106,7 @@ void SettingsModel::onSettingsTabChanged(int idx) {
|
|||
case 4://ui
|
||||
break;
|
||||
case 5://advanced
|
||||
accessAdvancedSettings();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -1187,6 +1192,12 @@ void SettingsModel::setExitOnClose (bool value) {
|
|||
// Advanced.
|
||||
// =============================================================================
|
||||
|
||||
void SettingsModel::accessAdvancedSettings() {
|
||||
emit contactImporterChanged();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
QString SettingsModel::getLogsFolder () const {
|
||||
return getLogsFolder(mConfig);
|
||||
}
|
||||
|
|
@ -1263,7 +1274,6 @@ bool SettingsModel::getLogsEnabled (const shared_ptr<linphone::Config> &config)
|
|||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
bool SettingsModel::getDeveloperSettingsEnabled () const {
|
||||
#ifdef DEBUG
|
||||
return !!mConfig->getInt(UiSection, "developer_settings", 0);
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <QVariantMap>
|
||||
|
||||
#include "components/core/CoreHandlers.hpp"
|
||||
#include "components/contacts/ContactsImporterModel.hpp"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
|
|
@ -425,7 +426,10 @@ public:
|
|||
bool getExitOnClose () const;
|
||||
void setExitOnClose (bool value);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Advanced. ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
void accessAdvancedSettings();
|
||||
|
||||
QString getLogsFolder () const;
|
||||
void setLogsFolder (const QString &folder);
|
||||
|
|
@ -456,6 +460,7 @@ public:
|
|||
bool getIsInCall() const;
|
||||
|
||||
static const std::string UiSection;
|
||||
static const std::string ContactsSection;
|
||||
|
||||
// ===========================================================================
|
||||
// SIGNALS.
|
||||
|
|
@ -532,7 +537,7 @@ signals:
|
|||
void fileTransferUrlChanged (const QString &url);
|
||||
|
||||
void mediaEncryptionChanged (MediaEncryption encryption);
|
||||
void limeStateChanged (bool state);
|
||||
void limeStateChanged (bool state);
|
||||
|
||||
void contactsEnabledChanged (bool status);
|
||||
|
||||
|
|
@ -586,6 +591,8 @@ signals:
|
|||
void logsUploadUrlChanged (const QString &url);
|
||||
void logsEnabledChanged (bool status);
|
||||
void logsEmailChanged (const QString &email);
|
||||
|
||||
void contactImporterChanged();
|
||||
|
||||
bool developerSettingsEnabledChanged (bool status);
|
||||
|
||||
|
|
|
|||
|
|
@ -196,6 +196,32 @@ QString SipAddressesModel::interpretSipAddress (const QString &sipAddress, bool
|
|||
return Utils::coreStringToAppString(lAddress->asStringUriOnly());
|
||||
return QString("");
|
||||
}
|
||||
QString SipAddressesModel::interpretSipAddress (const QString &sipAddress, const QString &domain) {
|
||||
auto core = CoreManager::getInstance()->getCore();
|
||||
if(!core){
|
||||
qWarning() << "No core to interpret address";
|
||||
}else{
|
||||
auto proxyConfig = CoreManager::getInstance()->getCore()->createProxyConfig();
|
||||
if( !proxyConfig) {
|
||||
}else{
|
||||
shared_ptr<linphone::Address> lAddressTemp = core->createPrimaryContactParsed();// Create an address
|
||||
if( lAddressTemp ){
|
||||
lAddressTemp->setDomain(Utils::appStringToCoreString(domain)); // Set the domain and use the address into proxy
|
||||
proxyConfig->setIdentityAddress(lAddressTemp);
|
||||
shared_ptr<linphone::Address> lAddress = proxyConfig->normalizeSipUri(Utils::appStringToCoreString(sipAddress));
|
||||
if (lAddress) {
|
||||
return Utils::coreStringToAppString(lAddress->asStringUriOnly());
|
||||
} else {
|
||||
qWarning() << "Cannot normalize Sip Uri : " << sipAddress << " / " << domain;
|
||||
return QString("");
|
||||
}
|
||||
}else{
|
||||
qWarning() << "Cannot create a Primary Contact Parsed";
|
||||
}
|
||||
}
|
||||
}
|
||||
return QString("");
|
||||
}
|
||||
|
||||
QString SipAddressesModel::interpretSipAddress (const QUrl &sipAddress) {
|
||||
return sipAddress.toString();
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ public:
|
|||
|
||||
Q_INVOKABLE static QString interpretSipAddress (const QString &sipAddress, bool checkUsername = true);
|
||||
Q_INVOKABLE static QString interpretSipAddress (const QUrl &sipAddress);
|
||||
Q_INVOKABLE static QString interpretSipAddress (const QString &sipAddress, const QString &domain);
|
||||
|
||||
Q_INVOKABLE static bool addressIsValid (const QString &address);
|
||||
Q_INVOKABLE static bool sipAddressIsValid (const QString &sipAddress);
|
||||
|
|
|
|||
1
linphone-app/src/utils/plugins/LinphonePlugin.cpp
Normal file
1
linphone-app/src/utils/plugins/LinphonePlugin.cpp
Normal file
|
|
@ -0,0 +1 @@
|
|||
#include "include/LinphoneApp/LinphonePlugin.hpp"
|
||||
118
linphone-app/src/utils/plugins/PluginDataAPI.cpp
Normal file
118
linphone-app/src/utils/plugins/PluginDataAPI.cpp
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
#include "include/LinphoneApp/PluginDataAPI.hpp"
|
||||
|
||||
#include <linphone++/core.hh>
|
||||
#include <linphone++/config.hh>
|
||||
|
||||
#include <QVariantMap>
|
||||
#include <QJsonDocument>
|
||||
#include <QPluginLoader>
|
||||
// This class regroup Data interface for importing contacts
|
||||
#include "include/LinphoneApp/LinphonePlugin.hpp"
|
||||
|
||||
PluginDataAPI::PluginDataAPI(LinphonePlugin * plugin, void* linphoneCore, QPluginLoader * pluginLoader) : mPlugin(plugin), mLinphoneCore(linphoneCore), mPluginLoader(pluginLoader){
|
||||
QVariantMap defaultValues;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(mPlugin->getGUIDescriptionToJson().toUtf8());
|
||||
QVariantMap description = doc.toVariant().toMap();
|
||||
mPluginLoader->setLoadHints(0);
|
||||
// First, get all fields where their target is ALL. It will be act as a "default field"
|
||||
for(auto field : description["fields"].toList()){
|
||||
auto details = field.toMap();
|
||||
if( details.contains("fieldId") && details.contains("defaultData")){
|
||||
int fieldCapability = details["capability"].toInt();
|
||||
if( fieldCapability == PluginCapability::ALL){
|
||||
for(int capability = PluginCapability::CONTACTS ; capability != PluginCapability::LAST ; ++capability){
|
||||
mInputFields[static_cast<PluginCapability>(capability)][details["fieldId"].toString()] = details["defaultData"].toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Second, get all fields that are not for ALL and add them
|
||||
for(auto field : description["fields"].toList()){
|
||||
auto details = field.toMap();
|
||||
if( details.contains("fieldId") && details.contains("defaultData")){
|
||||
int fieldCapability = details["capability"].toInt();
|
||||
if( fieldCapability> PluginCapability::NOTHING)
|
||||
mInputFields[static_cast<PluginCapability>(fieldCapability)][details["fieldId"].toString()] = details["defaultData"].toString();
|
||||
}
|
||||
}
|
||||
for(auto inputFields : mInputFields)
|
||||
inputFields["enabled"] = 0;
|
||||
}
|
||||
PluginDataAPI::~PluginDataAPI(){
|
||||
}
|
||||
|
||||
void PluginDataAPI::setInputFields(const PluginCapability& pCapability, const QVariantMap &inputFields){
|
||||
for(int capabilityIndex = (pCapability == PluginCapability::ALL?PluginCapability::CONTACTS:pCapability); capabilityIndex != (pCapability == PluginCapability::ALL?PluginCapability::LAST:pCapability+1) ; ++capabilityIndex){
|
||||
PluginCapability selectedCapability = static_cast<PluginCapability>(capabilityIndex);
|
||||
if(mInputFields[selectedCapability] != inputFields) {
|
||||
mInputFields[selectedCapability] = inputFields;
|
||||
if( isValid(false))
|
||||
saveConfiguration(selectedCapability);
|
||||
emit inputFieldsChanged(selectedCapability, mInputFields[selectedCapability]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QMap<PluginDataAPI::PluginCapability, QVariantMap> PluginDataAPI::getInputFields(const PluginCapability& capability){
|
||||
if( capability == PluginCapability::ALL)
|
||||
return mInputFields;
|
||||
else{
|
||||
QMap<PluginDataAPI::PluginCapability, QVariantMap> data;
|
||||
data[capability] = mInputFields[capability];
|
||||
return data;
|
||||
}
|
||||
}
|
||||
QMap<PluginDataAPI::PluginCapability, QVariantMap> PluginDataAPI::getInputFieldsToSave(const PluginCapability& capability) {
|
||||
return getInputFields(capability);
|
||||
}
|
||||
//----------------------------- CONFIGURATION ---------------------------------------
|
||||
|
||||
void PluginDataAPI::setSectionConfiguration(const QString& section){
|
||||
mSectionConfigurationName = section;
|
||||
}
|
||||
|
||||
void PluginDataAPI::loadConfiguration(const PluginCapability& pCapability){
|
||||
if( mSectionConfigurationName != "") {
|
||||
for(int capabilityIndex = (pCapability == PluginCapability::ALL?PluginCapability::CONTACTS:pCapability); capabilityIndex != (pCapability == PluginCapability::ALL?PluginCapability::LAST:pCapability+1) ; ++capabilityIndex){
|
||||
PluginCapability currentCapability = static_cast<PluginCapability>(capabilityIndex);
|
||||
std::shared_ptr<linphone::Config> config = static_cast<linphone::Core*>(mLinphoneCore)->getConfig();
|
||||
QVariantMap importData;
|
||||
std::string sectionName = (mSectionConfigurationName+"_"+QString::number(capabilityIndex)).toStdString();
|
||||
std::list<std::string> keys = config->getKeysNamesList(sectionName);
|
||||
for(auto key : keys){
|
||||
std::string value = config->getString(sectionName, key, "");
|
||||
importData[QString::fromLocal8Bit(key.c_str(), int(key.size()))] = QString::fromLocal8Bit(value.c_str(), int(value.size()));
|
||||
}
|
||||
//Do not use setInputFields(importData); as we don't want to save the configuration
|
||||
mInputFields[currentCapability] = importData;
|
||||
emit inputFieldsChanged(currentCapability, mInputFields[currentCapability]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PluginDataAPI::saveConfiguration(const PluginCapability& pCapability){
|
||||
if( mSectionConfigurationName != "") {
|
||||
auto inputs = getInputFieldsToSave(pCapability);
|
||||
for(QMap<PluginCapability, QVariantMap>::Iterator input = inputs.begin() ; input != inputs.end() ; ++input){
|
||||
PluginCapability currentCapability = input.key();
|
||||
std::string sectionName = (mSectionConfigurationName+"_"+QString::number(currentCapability)).toStdString();
|
||||
std::shared_ptr<linphone::Config> config = static_cast<linphone::Core*>(mLinphoneCore)->getConfig();
|
||||
QVariantMap inputsToSave = inputs[currentCapability];
|
||||
config->cleanSection(sectionName);// Remove fields that doesn't exist anymore (like temporary variables)
|
||||
for(auto field = inputsToSave.begin() ; field != inputsToSave.end() ; ++field)
|
||||
config->setString(sectionName, qPrintable(field.key()), qPrintable(field.value().toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
void PluginDataAPI::cleanAllConfigurations(){
|
||||
for(int capabilityIndex = PluginCapability::ALL ; capabilityIndex != PluginCapability::LAST ; ++capabilityIndex){
|
||||
std::string sectionName = (mSectionConfigurationName+"_"+QString::number(capabilityIndex)).toStdString();
|
||||
std::shared_ptr<linphone::Config> config = static_cast<linphone::Core*>(mLinphoneCore)->getConfig();
|
||||
config->cleanSection(sectionName);
|
||||
}
|
||||
}
|
||||
//----------------------------- -------------------------------------------------------
|
||||
|
||||
QPluginLoader * PluginDataAPI::getPluginLoader(){
|
||||
return mPluginLoader;
|
||||
}
|
||||
55
linphone-app/src/utils/plugins/PluginNetworkHelper.cpp
Normal file
55
linphone-app/src/utils/plugins/PluginNetworkHelper.cpp
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#include "include/LinphoneApp/PluginNetworkHelper.hpp"
|
||||
#include <QObject>
|
||||
#include <QtNetwork>
|
||||
// This class is used to define network operation to retrieve Addresses from Network
|
||||
|
||||
PluginNetworkHelper::PluginNetworkHelper(){}
|
||||
PluginNetworkHelper::~PluginNetworkHelper(){}
|
||||
void PluginNetworkHelper::request(){ // Create QNetworkReply and make network requests
|
||||
QNetworkRequest request(prepareRequest());
|
||||
|
||||
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
mNetworkReply = mManager.get(request);
|
||||
|
||||
#if QT_CONFIG(ssl)
|
||||
mNetworkReply->ignoreSslErrors();
|
||||
#endif
|
||||
|
||||
QNetworkReply *data = mNetworkReply.data();
|
||||
|
||||
QObject::connect(data, &QNetworkReply::readyRead, this, &PluginNetworkHelper::handleReadyData);
|
||||
QObject::connect(data, &QNetworkReply::finished, this, &PluginNetworkHelper::handleFinished);
|
||||
QObject::connect(data, QNonConstOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), this, &PluginNetworkHelper::handleError);
|
||||
|
||||
#if QT_CONFIG(ssl)
|
||||
QObject::connect(data, &QNetworkReply::sslErrors, this, &PluginNetworkHelper::handleSslErrors);
|
||||
#endif
|
||||
}
|
||||
void PluginNetworkHelper::handleReadyData(){
|
||||
mBuffer.append(mNetworkReply->readAll());
|
||||
}
|
||||
void PluginNetworkHelper::handleFinished (){
|
||||
if (mNetworkReply->error() == QNetworkReply::NoError){
|
||||
mBuffer.append(mNetworkReply->readAll());
|
||||
emit requestFinished(mBuffer);
|
||||
}else {
|
||||
qWarning() << mNetworkReply->errorString();
|
||||
emit message(QtWarningMsg, "Error while dealing with network. See logs for details.");
|
||||
}
|
||||
mBuffer.clear();
|
||||
}
|
||||
void PluginNetworkHelper::handleError (QNetworkReply::NetworkError code) {
|
||||
if (code != QNetworkReply::OperationCanceledError) {
|
||||
QString url = mNetworkReply->url().host();
|
||||
QString errorString = mNetworkReply->errorString();
|
||||
qWarning() << QStringLiteral("Download failed: %1 from %2").arg(errorString).arg(url);
|
||||
}
|
||||
}
|
||||
void PluginNetworkHelper::handleSslErrors (const QList<QSslError> &sslErrors){
|
||||
#if QT_CONFIG(ssl)
|
||||
for (const QSslError &error : sslErrors)
|
||||
qWarning() << QStringLiteral("SSL error %1 : %2").arg(error.error()).arg(error.errorString());
|
||||
#else
|
||||
Q_UNUSED(sslErrors);
|
||||
#endif
|
||||
}
|
||||
271
linphone-app/src/utils/plugins/PluginsManager.cpp
Normal file
271
linphone-app/src/utils/plugins/PluginsManager.cpp
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PluginsManager.hpp"
|
||||
//#include "ContactsImporterModel.hpp"
|
||||
#include "include/LinphoneApp/LinphonePlugin.hpp"
|
||||
#include "include/LinphoneApp/PluginNetworkHelper.hpp"
|
||||
|
||||
#include "utils/Utils.hpp"
|
||||
#include "app/paths/Paths.hpp"
|
||||
#include "components/contact/VcardModel.hpp"
|
||||
#include "components/contacts/ContactsListModel.hpp"
|
||||
#include "components/contacts/ContactsImporterListModel.hpp"
|
||||
#include "components/contacts/ContactsImporterModel.hpp"
|
||||
#include "components/core/CoreManager.hpp"
|
||||
#include "components/sip-addresses/SipAddressesModel.hpp"
|
||||
|
||||
|
||||
#include <QDir>
|
||||
#include <QPluginLoader>
|
||||
#include <QDebug>
|
||||
#include <QJsonDocument>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
|
||||
// =============================================================================
|
||||
|
||||
QMap<QString, QString> PluginsManager::gPluginsMap;
|
||||
QString PluginsManager::gPluginsConfigSection = "AppPlugin";
|
||||
|
||||
PluginsManager::PluginsManager(QObject * parent) : QObject(parent){
|
||||
}
|
||||
|
||||
QPluginLoader * PluginsManager::getPlugin(const QString &pluginIdentity){
|
||||
QStringList pluginPaths = Paths::getPluginsAppFolders();// Get all paths
|
||||
if( gPluginsMap.contains(pluginIdentity)){
|
||||
for(int i = 0 ; i < pluginPaths.size() ; ++i) {
|
||||
QString pluginPath = pluginPaths[i] +gPluginsMap[pluginIdentity];
|
||||
QPluginLoader * loader = new QPluginLoader(pluginPath);
|
||||
loader->setLoadHints(0); // this force Qt to unload the plugin from memory when we request it. Be carefull by not having a plugin instance or data created inside the plugin after the unload.
|
||||
if( auto instance = loader->instance()) {
|
||||
auto plugin = qobject_cast< LinphonePlugin* >(instance);
|
||||
if (plugin )
|
||||
return loader;
|
||||
else{
|
||||
qWarning() << loader->errorString();
|
||||
loader->unload();
|
||||
}
|
||||
}
|
||||
delete loader;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void * PluginsManager::createInstance(const QString &pluginIdentity){
|
||||
void * dataInstance = nullptr;
|
||||
LinphonePlugin * plugin = nullptr;
|
||||
if( gPluginsMap.contains(pluginIdentity)){
|
||||
QStringList pluginPaths = Paths::getPluginsAppFolders();
|
||||
for(int i = 0 ; i < pluginPaths.size() ; ++i) {
|
||||
QString pluginPath = pluginPaths[i] +gPluginsMap[pluginIdentity];
|
||||
QPluginLoader * loader = new QPluginLoader(pluginPath);
|
||||
loader->setLoadHints(0); // this force Qt to unload the plugin from memory when we request it. Be carefull by not having a plugin instance or data created inside the plugin after the unload.
|
||||
if( auto instance = loader->instance()) {
|
||||
plugin = qobject_cast< LinphonePlugin* >(instance);
|
||||
if (plugin) {
|
||||
try{
|
||||
dataInstance = plugin->createInstance(CoreManager::getInstance()->getCore().get(), loader);
|
||||
return dataInstance;
|
||||
}catch(...){
|
||||
loader->unload();
|
||||
}
|
||||
}else
|
||||
loader->unload();
|
||||
}
|
||||
delete loader;
|
||||
}
|
||||
}
|
||||
return dataInstance;
|
||||
}
|
||||
|
||||
QJsonDocument PluginsManager::getJson(const QString &pluginIdentity){
|
||||
QJsonDocument doc;
|
||||
QPluginLoader * pluginLoader = getPlugin(pluginIdentity);
|
||||
if( pluginLoader ){
|
||||
auto instance = pluginLoader->instance();
|
||||
if( instance ){
|
||||
LinphonePlugin * plugin = qobject_cast< LinphonePlugin* >(instance);
|
||||
if( plugin ){
|
||||
doc = QJsonDocument::fromJson(plugin->getGUIDescriptionToJson().toUtf8());
|
||||
}
|
||||
}
|
||||
pluginLoader->unload();
|
||||
delete pluginLoader;
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
QList<PluginsModel*> PluginsManager::getImporterModels(const QStringList &capabilities){
|
||||
QList<PluginsModel*> models;
|
||||
for(int i = 0 ; i < capabilities.size() ; ++i){
|
||||
if( capabilities[i] == "CONTACTS")
|
||||
models += CoreManager::getInstance()->getContactsImporterListModel()->getList();
|
||||
}
|
||||
return models;
|
||||
}
|
||||
void PluginsManager::openNewPlugin(const QString &pTitle){
|
||||
|
||||
QString fileName = QFileDialog::getOpenFileName(nullptr, pTitle);
|
||||
QString pluginIdentity;
|
||||
QStringList capabilities;
|
||||
QList<PluginsModel*> modelsToReset;
|
||||
int doCopy = QMessageBox::Yes;
|
||||
bool cannotRemovePlugin = false;
|
||||
//QVersionNumber pluginVersion, apiVersion = LinphonePlugin::gPluginVersion;
|
||||
if(fileName != ""){
|
||||
QFileInfo fileInfo(fileName);
|
||||
QString path = Utils::coreStringToAppString(Paths::getPluginsAppDirPath());
|
||||
if( !QLibrary::isLibrary(fileName)){
|
||||
QMessageBox::information(nullptr, pTitle, "The file is not a plugin");
|
||||
}else{
|
||||
QPluginLoader loader(fileName);
|
||||
loader.setLoadHints(0);
|
||||
QJsonObject metaData = loader.metaData()["MetaData"].toObject();
|
||||
if( metaData.contains("ID") && metaData.contains("Capabilities")){
|
||||
capabilities = metaData["Capabilities"].toString().toUpper().remove(' ').split(",");
|
||||
pluginIdentity = metaData["ID"].toString();
|
||||
}
|
||||
if(!pluginIdentity.isEmpty()){// Check all plugins that have this title
|
||||
QStringList oldPlugins;
|
||||
if( gPluginsMap.contains(pluginIdentity))
|
||||
oldPlugins << gPluginsMap[pluginIdentity];
|
||||
if( QFile::exists(path+fileInfo.fileName()))
|
||||
oldPlugins << path+fileInfo.fileName();
|
||||
if(oldPlugins.size() > 0){
|
||||
doCopy = QMessageBox::question(nullptr, pTitle, "The plugin already exists. Do you want to overwrite it?\n"+oldPlugins.join('\n'), QMessageBox::Yes, QMessageBox::No);
|
||||
if( doCopy == QMessageBox::Yes){
|
||||
if(gPluginsMap.contains(pluginIdentity)){
|
||||
auto importers = CoreManager::getInstance()->getContactsImporterListModel()->getList();
|
||||
for(auto importer : importers){
|
||||
QJsonObject pluginMetaData(importer->getDataAPI()->getPluginLoader()->metaData());
|
||||
if( pluginMetaData.contains("ID") && pluginMetaData["ID"].toString() == pluginIdentity){
|
||||
importer->setDataAPI(nullptr);
|
||||
modelsToReset.append(importer);
|
||||
}
|
||||
}
|
||||
QStringList pluginPaths = Paths::getPluginsAppFolders();
|
||||
for(int i = 0 ; !cannotRemovePlugin && i < pluginPaths.size()-1 ; ++i) {// Ignore the last path as it is the app folder
|
||||
QString pluginPath = pluginPaths[i];
|
||||
if(QFile::exists(pluginPath+gPluginsMap[pluginIdentity])){
|
||||
cannotRemovePlugin = !QFile::remove(pluginPath+gPluginsMap[pluginIdentity]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!cannotRemovePlugin && QFile::exists(path+fileInfo.fileName()))
|
||||
cannotRemovePlugin = !QFile::remove(path+fileInfo.fileName());
|
||||
if(!cannotRemovePlugin)
|
||||
gPluginsMap[pluginIdentity] = "";
|
||||
}
|
||||
}
|
||||
}else
|
||||
doCopy = QMessageBox::No;
|
||||
if(doCopy == QMessageBox::Yes ){
|
||||
|
||||
if( cannotRemovePlugin)// Qt will not unload library from memory so files cannot be removed. See https://bugreports.qt.io/browse/QTBUG-68880
|
||||
QMessageBox::information(nullptr, pTitle, "The plugin cannot be replaced. You have to exit the application and delete manually the plugin file in\n"+path);
|
||||
else if( !QFile::copy(fileName, path+fileInfo.fileName()))
|
||||
QMessageBox::information(nullptr, pTitle, "The plugin cannot be copied. You have to copy manually the plugin file to\n"+path);
|
||||
else {
|
||||
gPluginsMap[pluginIdentity] = fileInfo.fileName();
|
||||
for(auto importer : modelsToReset)
|
||||
importer->setDataAPI(static_cast<PluginDataAPI*>(createInstance(pluginIdentity)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVariantList PluginsManager::getPlugins(const int& capabilities) {
|
||||
QVariantList plugins;
|
||||
QStringList pluginPaths = Paths::getPluginsAppFolders();
|
||||
if(capabilities<0)
|
||||
gPluginsMap.clear();
|
||||
for(int pathIndex = pluginPaths.size()-1 ; pathIndex >= 0 ; --pathIndex) {// Start from app package. This sort ensure the priority on user plugins
|
||||
QString pluginPath = pluginPaths[pathIndex];
|
||||
QDir dir(pluginPath);
|
||||
QStringList pluginFiles = dir.entryList(QDir::Files);
|
||||
for(int i = 0 ; i < pluginFiles.size() ; ++i) {
|
||||
if( QLibrary::isLibrary(pluginPath+pluginFiles[i])){
|
||||
QPluginLoader loader(pluginPath+pluginFiles[i]);
|
||||
loader.setLoadHints(0); // this force Qt to unload the plugin from memory when we request it. Be carefull by not having a plugin instance or data created inside the plugin after the unload.
|
||||
if (auto instance = loader.instance()) {
|
||||
LinphonePlugin * plugin = qobject_cast< LinphonePlugin* >(instance);
|
||||
if ( plugin){
|
||||
QJsonObject metaData = loader.metaData()["MetaData"].toObject();
|
||||
if( metaData.contains("ID")){
|
||||
bool getIt = false;
|
||||
if(capabilities>=0 ){
|
||||
if(metaData.contains("Capabilities")){
|
||||
QString pluginCapabilities = metaData["Capabilities"].toString().toUpper().remove(' ');
|
||||
if( (capabilities & PluginDataAPI::CONTACTS) == PluginDataAPI::CONTACTS && pluginCapabilities.contains("CONTACTS")){
|
||||
getIt = true;
|
||||
}
|
||||
}else
|
||||
qWarning()<< "The plugin " << pluginFiles[i] << " must have Capabilities in its metadata";
|
||||
}else
|
||||
getIt = true;
|
||||
if(getIt){
|
||||
QJsonDocument doc = QJsonDocument::fromJson(plugin->getGUIDescriptionToJson().toUtf8());
|
||||
QVariantMap desc;
|
||||
desc["pluginTitle"] = doc["pluginTitle"];
|
||||
desc["pluginID"] = metaData["ID"].toString();
|
||||
if(!doc["pluginTitle"].toString().isEmpty()){
|
||||
gPluginsMap[metaData["ID"].toString()] = pluginFiles[i];
|
||||
plugins.push_back(desc);
|
||||
}
|
||||
}
|
||||
}else
|
||||
qWarning()<< "The plugin " << pluginFiles[i] << " must have ID in its metadata";
|
||||
} else {
|
||||
qWarning()<< "The plugin " << pluginFiles[i] << " should be updated to this version of API : " << loader.metaData()["IID"].toString();
|
||||
}
|
||||
loader.unload();
|
||||
} else {
|
||||
qWarning()<< "The plugin " << pluginFiles[i] << " cannot be used : " << loader.errorString();
|
||||
}
|
||||
}
|
||||
}
|
||||
std::sort(plugins.begin(), plugins.end());
|
||||
}
|
||||
return plugins;
|
||||
}
|
||||
QVariantMap PluginsManager::getPluginDescription(const QString& pluginIdentity) {
|
||||
QVariantMap description;
|
||||
QJsonDocument doc = getJson(pluginIdentity);
|
||||
description = doc.toVariant().toMap();
|
||||
return description;
|
||||
}
|
||||
|
||||
|
||||
QVariantMap PluginsManager::getDefaultValues(const QString& pluginIdentity){
|
||||
QVariantMap defaultValues;
|
||||
QVariantMap description;
|
||||
QJsonDocument doc = getJson(pluginIdentity);
|
||||
description = doc.toVariant().toMap();
|
||||
for(auto field : description["fields"].toList()){
|
||||
auto details = field.toMap();
|
||||
if( details.contains("fieldId") && details.contains("defaultData")){
|
||||
defaultValues[details["fieldId"].toString()] = details["defaultData"].toString();
|
||||
}
|
||||
}
|
||||
return defaultValues;
|
||||
}
|
||||
47
linphone-app/src/utils/plugins/PluginsManager.hpp
Normal file
47
linphone-app/src/utils/plugins/PluginsManager.hpp
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
//const QVersionNumber ContactsImporterPlugin::gPluginVersion = QVersionNumber::fromString(PLUGIN_CONTACT_VERSION);
|
||||
//const QVersionNumber ContactsImporterPlugin::gPluginVersion = QVersionNumber::fromString("1.0.0");
|
||||
//const QVersionNumber _ContactsImporterPlugin::gPluginVersion = QVersionNumber::fromString("1.0.0");
|
||||
#ifndef PLUGINS_MANAGER_MODEL_H_
|
||||
#define PLUGINS_MANAGER_MODEL_H_
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariantList>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
class ContactsImporterModel;
|
||||
class PluginDataAPI;
|
||||
class QPluginLoader;
|
||||
|
||||
class PluginsModel : public QObject{
|
||||
public:
|
||||
PluginsModel(QObject *parent = nullptr) : QObject(parent){}
|
||||
virtual ~PluginsModel(){}
|
||||
virtual void setDataAPI(PluginDataAPI*) = 0;
|
||||
virtual PluginDataAPI* getDataAPI() = 0;
|
||||
virtual int getIdentity()const = 0;
|
||||
virtual QVariantMap getFields() = 0;
|
||||
};
|
||||
class PluginsManager : public QObject{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PluginsManager (QObject *parent = Q_NULLPTR);
|
||||
|
||||
static QPluginLoader * getPlugin(const QString &pluginIdentity); // Return a plugin loader with Hints to 0 (unload will force Qt to remove the plugin from memory).
|
||||
static QVariantList getPlugins(const int& capabilities = -1); // Return all loaded plugins that have selected capabilities (PluginCapability flags)
|
||||
static void * createInstance(const QString &pluginIdentity); //Return a data instance from a plugin name.
|
||||
static QJsonDocument getJson(const QString &pluginIdentity); // Get the description of the plugin int the Json format.
|
||||
|
||||
Q_INVOKABLE static void openNewPlugin(const QString &pTitle); // Open a File Dialog. Test if the file can be load and have a matched version. Replace old plugins from custom paths and with the same plugin title.
|
||||
static QVariantMap getDefaultValues(const QString& pluginIdentity); // Get the default values of each fields for th eplugin
|
||||
QVariantMap getPluginDescription(const QString& pluginIdentity);
|
||||
|
||||
|
||||
|
||||
QList<PluginsModel*> getImporterModels(const QStringList &capabilities);
|
||||
static QMap<QString, QString> gPluginsMap; // Map between Identity and plugin path
|
||||
static QString gPluginsConfigSection; // The root name of the plugin's section in configuration file
|
||||
};
|
||||
|
||||
|
||||
#endif // CONTACTS_IMPORTER_PLUGINS_MANAGER_MODEL_H_
|
||||
|
|
@ -6,24 +6,37 @@ import Common.Styles 1.0
|
|||
|
||||
Column {
|
||||
id: formTable
|
||||
|
||||
width: parent.width
|
||||
|
||||
property alias titles: header.model
|
||||
property bool disableLineTitle: false
|
||||
|
||||
property int legendLineWidth: FormTableStyle.entry.width
|
||||
|
||||
property var maxWidthStyle : FormTableStyle.entry.maxWidth
|
||||
|
||||
readonly property double maxItemWidth: {
|
||||
var n = titles.length
|
||||
var curWidth = (width - FormTableStyle.entry.width) / n - (n - 1) * FormTableLineStyle.spacing
|
||||
var maxWidth = FormTableStyle.entry.maxWidth
|
||||
|
||||
return curWidth < maxWidth ? curWidth : maxWidth
|
||||
}
|
||||
readonly property double maxItemWidth: computeMaxItemWidth()
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function updateMaxItemWidth(){
|
||||
maxItemWidth = computeMaxItemWidth();
|
||||
}
|
||||
function computeMaxItemWidth(){
|
||||
var n = 1;
|
||||
// if( titles)
|
||||
// n = titles.length
|
||||
// else{
|
||||
for(var line = 0 ; line < formTable.visibleChildren.length ; ++line){
|
||||
var column = formTable.visibleChildren[line].visibleChildren.length;
|
||||
n = Math.max(n, column-1);
|
||||
}
|
||||
// }
|
||||
var curWidth = (width - (disableLineTitle?0:legendLineWidth) ) /n - FormTableLineStyle.spacing
|
||||
var maxWidth = maxWidthStyle
|
||||
return curWidth < maxWidth ? curWidth : maxWidth
|
||||
}
|
||||
spacing: FormTableStyle.spacing
|
||||
width: parent.width
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -100,5 +100,6 @@ Switch {
|
|||
anchors.fill: parent
|
||||
|
||||
onClicked: control.enabled && control.clicked()
|
||||
onPressed: control.enabled && control.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,120 +1,319 @@
|
|||
import QtQuick 2.7
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls 2.5
|
||||
|
||||
import Common 1.0
|
||||
import Linphone 1.0
|
||||
|
||||
import App.Styles 1.0
|
||||
import Linphone.Styles 1.0
|
||||
import Common.Styles 1.0
|
||||
|
||||
import ContactsImporterPluginsManager 1.0
|
||||
|
||||
import 'SettingsAdvanced.js' as Logic
|
||||
|
||||
// =============================================================================
|
||||
|
||||
TabContainer {
|
||||
Column {
|
||||
spacing: SettingsWindowStyle.forms.spacing
|
||||
width: parent.width
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Logs.
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
Form {
|
||||
title: qsTr('logsTitle')
|
||||
width: parent.width
|
||||
|
||||
FormLine {
|
||||
FormGroup {
|
||||
label: qsTr('logsFolderLabel')
|
||||
|
||||
FileChooserButton {
|
||||
selectedFile: SettingsModel.logsFolder
|
||||
selectFolder: true
|
||||
|
||||
onAccepted: SettingsModel.logsFolder = selectedFile
|
||||
}
|
||||
Column {
|
||||
spacing: SettingsWindowStyle.forms.spacing
|
||||
width: parent.width
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Logs.
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
Form {
|
||||
title: qsTr('logsTitle')
|
||||
width: parent.width
|
||||
|
||||
FormLine {
|
||||
FormGroup {
|
||||
label: qsTr('logsFolderLabel')
|
||||
|
||||
FileChooserButton {
|
||||
selectedFile: SettingsModel.logsFolder
|
||||
selectFolder: true
|
||||
|
||||
onAccepted: SettingsModel.logsFolder = selectedFile
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormLine {
|
||||
FormGroup {
|
||||
label: qsTr('logsUploadUrlLabel')
|
||||
|
||||
TextField {
|
||||
readOnly: true
|
||||
text: SettingsModel.logsUploadUrl
|
||||
|
||||
onEditingFinished: SettingsModel.logsUploadUrl = text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormLine {
|
||||
FormGroup {
|
||||
label: qsTr('logsEnabledLabel')
|
||||
|
||||
Switch {
|
||||
checked: SettingsModel.logsEnabled
|
||||
|
||||
onClicked: SettingsModel.logsEnabled = !checked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormLine {
|
||||
FormGroup {
|
||||
label: qsTr('logsUploadUrlLabel')
|
||||
|
||||
TextField {
|
||||
readOnly: true
|
||||
text: SettingsModel.logsUploadUrl
|
||||
|
||||
onEditingFinished: SettingsModel.logsUploadUrl = text
|
||||
}
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
spacing: SettingsAdvancedStyle.buttons.spacing
|
||||
|
||||
TextButtonB {
|
||||
text: qsTr('cleanLogs')
|
||||
|
||||
onClicked: Logic.cleanLogs()
|
||||
}
|
||||
|
||||
TextButtonB {
|
||||
enabled: !sendLogsBlock.loading && SettingsModel.logsEnabled
|
||||
text: qsTr('sendLogs')
|
||||
|
||||
onClicked: sendLogsBlock.execute()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormLine {
|
||||
FormGroup {
|
||||
label: qsTr('logsEnabledLabel')
|
||||
|
||||
Switch {
|
||||
checked: SettingsModel.logsEnabled
|
||||
|
||||
onClicked: SettingsModel.logsEnabled = !checked
|
||||
}
|
||||
RequestBlock {
|
||||
id: sendLogsBlock
|
||||
|
||||
action: CoreManager.sendLogs
|
||||
width: parent.width
|
||||
|
||||
|
||||
Connections {
|
||||
target: CoreManager
|
||||
|
||||
onLogsUploaded: Logic.handleLogsUploaded(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormEmptyLine {}
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
spacing: SettingsAdvancedStyle.buttons.spacing
|
||||
|
||||
TextButtonB {
|
||||
text: qsTr('cleanLogs')
|
||||
|
||||
onClicked: Logic.cleanLogs()
|
||||
}
|
||||
|
||||
TextButtonB {
|
||||
enabled: !sendLogsBlock.loading && SettingsModel.logsEnabled
|
||||
text: qsTr('sendLogs')
|
||||
|
||||
onClicked: sendLogsBlock.execute()
|
||||
}
|
||||
}
|
||||
|
||||
RequestBlock {
|
||||
id: sendLogsBlock
|
||||
|
||||
action: CoreManager.sendLogs
|
||||
width: parent.width
|
||||
|
||||
|
||||
Connections {
|
||||
target: CoreManager
|
||||
|
||||
onLogsUploaded: Logic.handleLogsUploaded(url)
|
||||
}
|
||||
}
|
||||
onVisibleChanged: sendLogsBlock.setText('')
|
||||
// -------------------------------------------------------------------------
|
||||
// Developer settings.
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
Form {
|
||||
title: qsTr('developerSettingsTitle')
|
||||
visible: SettingsModel.developerSettingsEnabled
|
||||
width: parent.width
|
||||
|
||||
FormLine {
|
||||
FormGroup {
|
||||
label: qsTr('developerSettingsEnabledLabel')
|
||||
|
||||
Switch {
|
||||
checked: SettingsModel.developerSettingsEnabled
|
||||
|
||||
onClicked: SettingsModel.developerSettingsEnabled = !checked
|
||||
}
|
||||
onVisibleChanged: sendLogsBlock.setText('')
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// ADDRESS BOOK
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
Form {
|
||||
title: qsTr('contactsTitle')
|
||||
width: parent.width
|
||||
FormTable {
|
||||
id:contactsImporterTable
|
||||
width :parent.width
|
||||
legendLineWidth:0
|
||||
disableLineTitle:importerRepeater.count!=1
|
||||
titles: (importerRepeater.count==1? getTitles(): [])
|
||||
function getTitles(repeaterModel){
|
||||
var fields = importerRepeater.itemAt(0).pluginDescription['fields'];
|
||||
var t = [''];
|
||||
for(var i = 0 ; i < fields.length ; ++i){
|
||||
t.push(fields[i]['placeholder']);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
Repeater{
|
||||
id:importerRepeater
|
||||
model:ContactsImporterListProxyModel{id:contactsImporterList}
|
||||
|
||||
delegate : FormTable{
|
||||
//readonly property double maxItemWidth: contactsImporterTable.maxItemWidth
|
||||
property var pluginDescription : importerLine.pluginDescription
|
||||
width :parent.width
|
||||
legendLineWidth:80
|
||||
disableLineTitle:true
|
||||
FormTableLine {
|
||||
id:importerLine
|
||||
property var fields : modelData.fields
|
||||
property int identity : modelData.identity
|
||||
property var pluginDescription : ContactsImporterPluginsManager.getContactsImporterPluginDescription(fields["pluginID"]) // Plugin definition
|
||||
|
||||
FormTableEntry {
|
||||
Row{
|
||||
width :parent.width
|
||||
ActionButton {
|
||||
id:removeImporter
|
||||
icon: 'cancel'
|
||||
iconSize:CallsStyle.entry.iconActionSize
|
||||
onClicked:ContactsImporterListModel.removeContactsImporter(modelData)
|
||||
}
|
||||
Text{
|
||||
height:parent.height
|
||||
width:parent.width-removeImporter.width
|
||||
text:importerLine.pluginDescription['pluginTitle']
|
||||
horizontalAlignment:Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.WordWrap
|
||||
font {
|
||||
bold: true
|
||||
pointSize: FormTableStyle.entry.text.pointSize
|
||||
}
|
||||
TooltipArea{
|
||||
text:importerLine.pluginDescription['pluginDescription']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Repeater{
|
||||
model:importerLine.pluginDescription['fields']
|
||||
delegate: FormTableEntry {
|
||||
Loader{
|
||||
sourceComponent: (modelData['type']==0 ? textComponent:textFieldComponent)
|
||||
active:true
|
||||
width:parent.width
|
||||
Component{
|
||||
id: textComponent
|
||||
Text {
|
||||
id: text
|
||||
color: FormTableStyle.entry.text.color
|
||||
elide: Text.ElideRight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: importerLine.fields[modelData['fieldId']]?importerLine.fields[modelData['fieldId']]:''
|
||||
height: FormTableStyle.entry.height
|
||||
width: parent.width
|
||||
font {
|
||||
bold: true
|
||||
pointSize: FormTableStyle.entry.text.pointSize
|
||||
}
|
||||
}
|
||||
}
|
||||
Component{
|
||||
id: textFieldComponent
|
||||
TextField {
|
||||
readOnly: false
|
||||
width:parent.width
|
||||
placeholderText : modelData['placeholder']
|
||||
text: importerLine.fields[modelData['fieldId']]?importerLine.fields[modelData['fieldId']]:''
|
||||
echoMode: (modelData['hiddenText']?TextInput.Password:TextInput.Normal)
|
||||
onEditingFinished:{
|
||||
importerLine.fields[modelData['fieldId']] = text
|
||||
}
|
||||
Component.onCompleted: importerLine.fields[modelData['fieldId']] = text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}// Repeater : Fields
|
||||
FormTableEntry {
|
||||
Switch {
|
||||
checked: modelData.fields["enabled"]>0
|
||||
onClicked: {
|
||||
checked = !checked
|
||||
importerLine.fields["enabled"] = (checked?1:0)
|
||||
ContactsImporterListModel.addContactsImporter(importerLine.fields, importerLine.identity)
|
||||
|
||||
if(checked){
|
||||
ContactsImporterListModel.importContacts(importerLine.identity)
|
||||
}else
|
||||
contactsImporterStatus.text = ''
|
||||
}
|
||||
}
|
||||
}//FormTableEntry
|
||||
}//FormTableLine
|
||||
FormTableLine{
|
||||
width:parent.width-parent.legendLineWidth
|
||||
FormTableEntry {
|
||||
id:contactsImporterStatusEntry
|
||||
visible:contactsImporterStatus.text!==''
|
||||
width:parent.width
|
||||
TextEdit{
|
||||
id:contactsImporterStatus
|
||||
property bool isError:false
|
||||
selectByMouse: true
|
||||
readOnly:true
|
||||
color: (isError?SettingsAdvancedStyle.error.color:SettingsAdvancedStyle.info.color)
|
||||
width:parent.width
|
||||
horizontalAlignment:Text.AlignRight
|
||||
font {
|
||||
italic: true
|
||||
pointSize: SettingsAdvancedStyle.info.pointSize
|
||||
}
|
||||
Connections{
|
||||
target:modelData
|
||||
onStatusMessage:{contactsImporterStatus.isError=false;contactsImporterStatus.text=message;}
|
||||
onErrorMessage:{contactsImporterStatus.isError=true;contactsImporterStatus.text=message;}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}//Column
|
||||
}// Repeater : Importer
|
||||
|
||||
}
|
||||
Row{
|
||||
spacing:SettingsAdvancedStyle.buttons.spacing
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
ActionButton {
|
||||
icon: 'options'
|
||||
iconSize:CallsStyle.entry.iconActionSize
|
||||
onClicked:{
|
||||
ContactsImporterPluginsManager.openNewPlugin();
|
||||
pluginChoice.model = ContactsImporterPluginsManager.getPlugins();
|
||||
}
|
||||
}
|
||||
ComboBox{
|
||||
id: pluginChoice
|
||||
model:ContactsImporterPluginsManager.getPlugins()
|
||||
textRole: "pluginTitle"
|
||||
displayText: currentIndex === -1 ? 'No Plugins to load' : currentText
|
||||
Text{// Hack, combobox show empty text when empty
|
||||
anchors.fill:parent
|
||||
visible:pluginChoice.currentIndex===-1
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
text: 'No Plugins to load'
|
||||
font {
|
||||
bold:false
|
||||
italic: true
|
||||
pointSize: FormTableStyle.entry.text.pointSize
|
||||
}
|
||||
}
|
||||
Connections{
|
||||
target:SettingsModel
|
||||
onContactImporterChanged:pluginChoice.model=ContactsImporterPluginsManager.getPlugins()
|
||||
}
|
||||
}
|
||||
ActionButton {
|
||||
icon: 'add'
|
||||
iconSize:CallsStyle.entry.iconActionSize
|
||||
visible:pluginChoice.currentIndex>=0
|
||||
onClicked:{
|
||||
if( pluginChoice.currentIndex >= 0)
|
||||
ContactsImporterListModel.createContactsImporter({"pluginID":pluginChoice.model[pluginChoice.currentIndex]["pluginID"]})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Developer settings.
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
Form {
|
||||
title: qsTr('developerSettingsTitle')
|
||||
visible: SettingsModel.developerSettingsEnabled
|
||||
width: parent.width
|
||||
|
||||
FormLine {
|
||||
FormGroup {
|
||||
label: qsTr('developerSettingsEnabledLabel')
|
||||
|
||||
Switch {
|
||||
checked: SettingsModel.developerSettingsEnabled
|
||||
|
||||
onClicked: SettingsModel.developerSettingsEnabled = !checked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,20 @@
|
|||
pragma Singleton
|
||||
import QtQml 2.2
|
||||
|
||||
import Colors 1.0
|
||||
import Units 1.0
|
||||
// =============================================================================
|
||||
|
||||
QtObject {
|
||||
property QtObject buttons: QtObject {
|
||||
property int spacing: 10
|
||||
}
|
||||
|
||||
property QtObject error: QtObject {
|
||||
property color color: Colors.error
|
||||
}
|
||||
property QtObject info: QtObject {
|
||||
property color color: Colors.j
|
||||
property int pointSize: Units.dp * 11
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 47a49990b579ecbd5b01c81e6e8e2f00cd662a89
|
||||
Subproject commit 4a3c4b2cb39181eb2e3eaaed1617b2aa866c8d33
|
||||
42
plugins/CMakeLists.txt
Normal file
42
plugins/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
################################################################################
|
||||
#
|
||||
# Copyright (c) 2017-2020 Belledonne Communications SARL.
|
||||
#
|
||||
# This file is part of linphone-desktop
|
||||
# (see https://www.linphone.org).
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
|
||||
## Add custom plugins
|
||||
macro(get_all_subdirs result curdir)
|
||||
file(GLOB children RELATIVE ${curdir} ${curdir}/*)
|
||||
set(dirlist "")
|
||||
foreach(child ${children})
|
||||
if(IS_DIRECTORY ${curdir}/${child} AND (ENABLE_BUILD_EXAMPLES OR NOT ${child} MATCHES "example"))
|
||||
list(APPEND dirlist ${child})
|
||||
endif()
|
||||
endforeach()
|
||||
set(${result} ${dirlist})
|
||||
endmacro()
|
||||
get_all_subdirs(SUBDIRS ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
set(CMAKE_PREFIX_PATH "${CMAKE_PREFIX_PATH};${CMAKE_INSTALL_PREFIX}/include")
|
||||
|
||||
foreach(subdir ${SUBDIRS})
|
||||
message("Adding ${subdir} plugin")
|
||||
add_subdirectory(${subdir})
|
||||
endforeach()
|
||||
133
plugins/example/CMakeLists.txt
Normal file
133
plugins/example/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
################################################################################
|
||||
#
|
||||
# Copyright (c) 2017-2020 Belledonne Communications SARL.
|
||||
#
|
||||
# This file is part of linphone-desktop
|
||||
# (see https://www.linphone.org).
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
#-------------------------------------------------
|
||||
# Customizable data
|
||||
set(SOURCES
|
||||
src/Plugin.cpp
|
||||
src/NetworkAPI.cpp
|
||||
src/DataAPI.cpp
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
src/Plugin.hpp
|
||||
src/NetworkAPI.hpp
|
||||
src/DataAPI.hpp
|
||||
)
|
||||
|
||||
list(APPEND SOURCES
|
||||
src/PluginMetaData.json)
|
||||
|
||||
set(TARGET_NAME linphonePluginExample )
|
||||
#-------------------------------------------------
|
||||
|
||||
find_package(bctoolbox CONFIG)
|
||||
set(FULL_VERSION )
|
||||
bc_compute_full_version(FULL_VERSION)
|
||||
set(version_major )
|
||||
set(version_minor )
|
||||
set(version_patch )
|
||||
set(identifiers )
|
||||
set(metadata )
|
||||
bc_parse_full_version("${FULL_VERSION}" version_major version_minor version_patch identifiers metadata)
|
||||
set(PLUGIN_VERSION "${version_major}.${version_minor}.${version_patch}")
|
||||
|
||||
project(${TARGET_NAME} VERSION ${PLUGIN_VERSION})
|
||||
|
||||
include(GNUInstallDirs)
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
message("${TARGET_NAME} version : ${PLUGIN_VERSION}")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
SET_PROPERTY(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS true)
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
set(CMAKE_INSTALL_RPATH "$ORIGIN;$ORIGIN/lib64;$ORIGIN/../lib64;$ORIGIN/lib;$ORIGIN/../lib")
|
||||
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
endif()
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../cmake")
|
||||
list(APPEND CMAKE_PREFIX_PATH ${CMAKE_INSTALL_PREFIX}/include)
|
||||
|
||||
if(WIN32)
|
||||
set(EXECUTABLE_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_DIR} )
|
||||
set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_DIR} )
|
||||
set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_DIR} )
|
||||
foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} )# Apply to all configurations
|
||||
string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG )
|
||||
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${EXECUTABLE_OUTPUT_DIR} )
|
||||
set( CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${EXECUTABLE_OUTPUT_DIR} )
|
||||
set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${EXECUTABLE_OUTPUT_DIR} )
|
||||
endforeach( OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES )
|
||||
endif()
|
||||
|
||||
# Build configuration
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG -DQT_NO_DEBUG")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DQT_QML_DEBUG -DQT_DECLARATIVE_DEBUG")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG -DQT_QML_DEBUG -DQT_DECLARATIVE_DEBUG" )
|
||||
|
||||
if( WIN32)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_WINSOCKAPI_")#remove error from windows headers order
|
||||
endif()
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)#useful for config.h
|
||||
|
||||
set(QT5_PACKAGES Core Widgets Network)
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
|
||||
find_package(Qt5 COMPONENTS ${QT5_PACKAGES} REQUIRED)
|
||||
find_package(Qt5 COMPONENTS ${QT5_PACKAGES_OPTIONAL} QUIET)
|
||||
|
||||
find_package(LinphoneCxx CONFIG)
|
||||
find_package(bctoolbox CONFIG)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Build.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
add_library(${TARGET_NAME} SHARED ${SOURCES} ${HEADERS})
|
||||
|
||||
target_link_libraries(${TARGET_NAME} PRIVATE Qt5::Widgets Qt5::Network ${LINPHONECXX_LIBRARIES})
|
||||
target_compile_options(${TARGET_NAME} PRIVATE ${COMPILE_OPTIONS})
|
||||
set_source_files_properties( ${TARGET_NAME} PROPERTIES EXTERNAL_OBJECT true GENERATED true )
|
||||
set_property(TARGET ${TARGET_NAME} PROPERTY POSITION_INDEPENDENT_CODE ON) #Need by Qt
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${CMAKE_PREFIX_PATH} ${LINPHONECXX_INCLUDE_DIRS})
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES OUTPUT_NAME "${TARGET_NAME}-${PLUGIN_VERSION}")
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# IDE
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
source_group(
|
||||
"Json" REGULAR_EXPRESSION ".+\.json$"
|
||||
)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Install.
|
||||
#-------------------------------------------------------------------------------
|
||||
set(LINPHONE_APP_CONTACT_PLUGINS_PATH "plugins/app")
|
||||
install(TARGETS ${TARGET_NAME} DESTINATION "${LINPHONE_APP_CONTACT_PLUGINS_PATH}")
|
||||
|
||||
|
||||
|
||||
157
plugins/example/src/DataAPI.cpp
Normal file
157
plugins/example/src/DataAPI.cpp
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
#include "DataAPI.hpp"
|
||||
#include "NetworkAPI.hpp"
|
||||
#include "Plugin.hpp"
|
||||
|
||||
#include <QInputDialog>
|
||||
#include <QPluginLoader>
|
||||
#include <linphone++/proxy_config.hh>
|
||||
|
||||
DataAPI::DataAPI(Plugin *plugin, void * core, QPluginLoader * pluginLoader) :PluginDataAPI(plugin, core, pluginLoader){
|
||||
auto proxyConfig = static_cast<linphone::Core*>(mLinphoneCore)->getDefaultProxyConfig();
|
||||
QVariantMap account;
|
||||
std::string domain;
|
||||
if(proxyConfig)
|
||||
domain = proxyConfig->getDomain();
|
||||
else{
|
||||
proxyConfig = static_cast<linphone::Core*>(mLinphoneCore)->createProxyConfig();
|
||||
if(proxyConfig)
|
||||
domain = proxyConfig->getDomain();
|
||||
if(domain == "")
|
||||
domain = "sip.linphone.org";
|
||||
}
|
||||
mInputFields[CONTACTS]["SIP_Domain"] = QString::fromLocal8Bit(domain.c_str(), int(domain.size()));
|
||||
}
|
||||
|
||||
QString DataAPI::getUrl()const{
|
||||
return mInputFields[CONTACTS]["URL"].toString();
|
||||
}
|
||||
QString DataAPI::getDomain()const{
|
||||
return mInputFields[CONTACTS]["SIP_Domain"].toString();
|
||||
}
|
||||
QString DataAPI::getUsername()const{
|
||||
return mInputFields[CONTACTS]["Username"].toString();
|
||||
}
|
||||
QString DataAPI::getPassword()const{
|
||||
return mInputFields[CONTACTS]["Password"].toString();
|
||||
}
|
||||
QString DataAPI::getKey()const{
|
||||
return mInputFields[CONTACTS]["Key"].toString();
|
||||
}
|
||||
bool DataAPI::isEnabled()const{
|
||||
return mInputFields[CONTACTS]["enabled"].toInt()>0;
|
||||
}
|
||||
void DataAPI::setPassword(const QString &password){
|
||||
mInputFields[CONTACTS]["Password"] = password;
|
||||
}
|
||||
|
||||
bool DataAPI::isValid(const bool &pRequestData, QString * pError){
|
||||
QStringList errors;
|
||||
if( getDomain().isEmpty())
|
||||
errors << "Domain is empty.";
|
||||
if( getUrl().isEmpty())
|
||||
errors << "Url is empty.";
|
||||
if( getUsername().isEmpty())
|
||||
errors << "Username is empty.";
|
||||
if( getPassword().isEmpty() && getKey().isEmpty()){
|
||||
if(pRequestData)
|
||||
setPassword(QInputDialog::getText(nullptr, "Linphone example Address Book","Password",QLineEdit::EchoMode::Password));
|
||||
if( getPassword().isEmpty())
|
||||
errors << "Password is empty.";
|
||||
}
|
||||
if( errors.size() > 0){
|
||||
if(pError)
|
||||
*pError = "Data is invalid : " + errors.join(" ");
|
||||
return false;
|
||||
}else
|
||||
return true;
|
||||
}
|
||||
|
||||
QMap<PluginDataAPI::PluginCapability, QVariantMap> DataAPI::getInputFieldsToSave(const PluginCapability& capability){// Remove Password from config file
|
||||
QMap<PluginCapability, QVariantMap> data = mInputFields;
|
||||
data[CONTACTS].remove("Password");
|
||||
return data;
|
||||
}
|
||||
|
||||
void DataAPI::run(const PluginCapability& actionType){
|
||||
if( actionType == PluginCapability::CONTACTS){
|
||||
NetworkAPI * network = new NetworkAPI(this);
|
||||
QObject::connect(this, &PluginDataAPI::dataReceived, network, &DataAPI::deleteLater);
|
||||
network->startRequest();
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------
|
||||
|
||||
void DataAPI::parse(const QByteArray& p_data){
|
||||
QVector<QMultiMap<QString,QString> > parsedData;
|
||||
QString statusText;
|
||||
if(!p_data.isEmpty()) {
|
||||
QJsonDocument doc = QJsonDocument::fromJson(p_data);
|
||||
QJsonObject responses = doc.object();
|
||||
QString status = responses["status"].toString();
|
||||
QString comment = responses["comment"].toString();
|
||||
if( responses.size() == 0){
|
||||
statusText = "Contacts are not in Json format.";
|
||||
}else if( status != "OK"){
|
||||
statusText = status;
|
||||
if( statusText.isEmpty())
|
||||
statusText = "Cannot parse the request: The URL may not be valid.";
|
||||
if(!comment.isEmpty())
|
||||
statusText += " "+comment;
|
||||
if( mInputFields[CONTACTS].contains("Key")){
|
||||
QVariantMap newInputs = mInputFields[CONTACTS];
|
||||
newInputs.remove("Key");// Reset key on error
|
||||
setInputFields(CONTACTS, newInputs);
|
||||
}
|
||||
}else{
|
||||
if( responses.contains("key")){
|
||||
QVariantMap newInputs = mInputFields[CONTACTS];
|
||||
newInputs["Key"] = responses["key"].toString();
|
||||
setInputFields(CONTACTS, newInputs);
|
||||
}
|
||||
if( responses.contains("contacts")){
|
||||
QJsonArray contacts = responses["contacts"].toArray();
|
||||
int contactCount = 0;
|
||||
for(int i = 0 ; i < contacts.size() ; ++i){
|
||||
QMultiMap<QString, QString> cardData;
|
||||
QJsonObject contact = contacts[i].toObject();
|
||||
QString phoneNumber = contact["number"].toString();
|
||||
QStringList name;
|
||||
bool haveData = false;
|
||||
QString company = contact["company"].toString();
|
||||
|
||||
|
||||
if( contact.contains("firstname") && contact["firstname"].toString() != "")
|
||||
name << contact["firstname"].toString();
|
||||
if( contact.contains("surname") && contact["surname"].toString() != "")
|
||||
name << contact["surname"].toString();
|
||||
|
||||
if(name.size() > 0){
|
||||
QString username = name.join(" ");
|
||||
cardData.insert("displayName", username);
|
||||
}
|
||||
if(!phoneNumber.isEmpty()) {
|
||||
cardData.insert("phoneNumber", phoneNumber);
|
||||
cardData.insert("sipUsername", phoneNumber);
|
||||
haveData = true;
|
||||
}
|
||||
if(!company.isEmpty())
|
||||
cardData.insert("organization", company);
|
||||
if( haveData){
|
||||
cardData.insert("sipDomain", mInputFields[CONTACTS]["SIP_Domain"].toString());
|
||||
parsedData.push_back(cardData);
|
||||
++contactCount;
|
||||
}
|
||||
}
|
||||
QString messageStatus = QString::number(contactCount) +" contact"+(contactCount>1?"s":"")+" have been synchronized at "+QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
|
||||
emit message(QtInfoMsg, messageStatus);
|
||||
qInfo() << messageStatus;
|
||||
}
|
||||
}
|
||||
}else
|
||||
statusText = "Cannot parse the request: The URL may not be valid.";
|
||||
if( !statusText.isEmpty())
|
||||
emit message(QtWarningMsg, statusText);
|
||||
emit dataReceived(PluginDataAPI::CONTACTS, parsedData);
|
||||
}
|
||||
|
||||
|
||||
43
plugins/example/src/DataAPI.hpp
Normal file
43
plugins/example/src/DataAPI.hpp
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef DATAAPI_HPP
|
||||
#define DATAAPI_HPP
|
||||
|
||||
#include <QObject>
|
||||
#include <QtNetwork>
|
||||
#include <QVariantMap>
|
||||
|
||||
#include <LinphoneApp/PluginDataAPI.hpp>
|
||||
#include <linphone++/core.hh>
|
||||
|
||||
class Plugin;
|
||||
class QPluginLoader;
|
||||
|
||||
// Example of address book importer
|
||||
|
||||
class DataAPI : public PluginDataAPI
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DataAPI(Plugin *plugin, void *core, QPluginLoader * pluginLoader);
|
||||
virtual ~DataAPI(){}
|
||||
|
||||
QString getUrl()const;
|
||||
QString getDomain()const;
|
||||
QString getUsername()const;
|
||||
QString getPassword()const;
|
||||
QString getKey()const;
|
||||
bool isEnabled()const;
|
||||
|
||||
void setPassword(const QString &password);
|
||||
|
||||
virtual bool isValid(const bool &requestData, QString * pError= nullptr);// Test data and send signal. Used to get feedback
|
||||
|
||||
virtual QMap<PluginDataAPI::PluginCapability, QVariantMap> getInputFieldsToSave(const PluginCapability& capability);
|
||||
|
||||
virtual void run(const PluginCapability& actionType);
|
||||
public slots:
|
||||
virtual void parse(const QByteArray& p_data);
|
||||
signals:
|
||||
void inputFieldsChanged(const PluginCapability& capability, const QVariantMap &inputs); // The plugin made updates on input
|
||||
};
|
||||
|
||||
#endif // DATAAPI_HPP
|
||||
58
plugins/example/src/NetworkAPI.cpp
Normal file
58
plugins/example/src/NetworkAPI.cpp
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
#include "NetworkAPI.hpp"
|
||||
#include "DataAPI.hpp"
|
||||
|
||||
#include <QInputDialog>
|
||||
#include <LinphoneApp/PluginDataAPI.hpp>
|
||||
|
||||
|
||||
NetworkAPI::NetworkAPI(DataAPI * data) : mData(data){
|
||||
if(mData ) {
|
||||
connect(this, SIGNAL(requestFinished(const QByteArray&)), mData, SLOT(parse(const QByteArray&)));
|
||||
connect(this, &NetworkAPI::message, mData, &DataAPI::message);
|
||||
}
|
||||
}
|
||||
|
||||
NetworkAPI::~NetworkAPI(){
|
||||
}
|
||||
|
||||
bool NetworkAPI::isEnabled()const{
|
||||
return mData && mData->isEnabled();
|
||||
}
|
||||
bool NetworkAPI::isValid(PluginDataAPI * pData, const bool &pShowError){
|
||||
QString errorMessage;
|
||||
DataAPI * data = dynamic_cast<DataAPI*>(pData);
|
||||
bool ok = data;
|
||||
if(!ok)
|
||||
errorMessage = "These data are invalid";
|
||||
else
|
||||
ok = pData->isValid(true, &errorMessage);
|
||||
if(!ok && pShowError){
|
||||
qWarning() << errorMessage;
|
||||
emit message(QtMsgType::QtWarningMsg, errorMessage);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------
|
||||
|
||||
QString NetworkAPI::prepareRequest()const{
|
||||
QString url = mData->getUrl()+"?user="+mData->getUsername()+"&";
|
||||
if( mData->getKey() != "")
|
||||
url += "key="+mData->getKey();
|
||||
else
|
||||
url += "password="+mData->getPassword();
|
||||
return url;
|
||||
}
|
||||
|
||||
void NetworkAPI::startRequest() {
|
||||
bool doRequest = false;
|
||||
if(isValid(mData)){
|
||||
if(isEnabled()){
|
||||
mCurrentStep=0;
|
||||
doRequest = true;
|
||||
}
|
||||
}
|
||||
if(doRequest)
|
||||
request();
|
||||
else
|
||||
mData->parse(QByteArray());
|
||||
}
|
||||
33
plugins/example/src/NetworkAPI.hpp
Normal file
33
plugins/example/src/NetworkAPI.hpp
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef NETWORKAPI_HPP
|
||||
#define NETWORKAPI_HPP
|
||||
|
||||
#include <QObject>
|
||||
#include <QtNetwork>
|
||||
|
||||
|
||||
#include <LinphoneApp/PluginNetworkHelper.hpp>
|
||||
|
||||
class DataAPI;
|
||||
class PluginDataAPI;
|
||||
// Interface between Network API and Data.
|
||||
class NetworkAPI : public PluginNetworkHelper
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
NetworkAPI(DataAPI * data);
|
||||
virtual ~NetworkAPI();
|
||||
|
||||
bool isEnabled()const; // Interface to test if data is enabled
|
||||
bool isValid(PluginDataAPI * pData, const bool &pShowError = true);// Test if data is valid
|
||||
|
||||
virtual QString prepareRequest()const;// Prepare request for URL
|
||||
|
||||
void startRequest();
|
||||
|
||||
// Data
|
||||
DataAPI * mData;
|
||||
int mCurrentStep;
|
||||
|
||||
};
|
||||
|
||||
#endif // NETWORKAPI_HPP
|
||||
76
plugins/example/src/Plugin.cpp
Normal file
76
plugins/example/src/Plugin.cpp
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*******************************************************************************/
|
||||
#include "Plugin.hpp"
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
|
||||
#include "DataAPI.hpp"
|
||||
#include "NetworkAPI.hpp"
|
||||
|
||||
QString Plugin::getGUIDescriptionToJson()const{
|
||||
QJsonObject description;
|
||||
description["pluginTitle"] = "Plugin Example";
|
||||
description["pluginDescription"] = "This is a test plugin to import an address book from an URL";
|
||||
|
||||
QJsonObject field;
|
||||
QJsonArray fields;
|
||||
field["placeholder"] = "SIP Domain";
|
||||
field["fieldId"] = "SIP_Domain";
|
||||
field["defaultData"] = ""; // Set by the Data instance from Core
|
||||
field["type"] = 1;
|
||||
field["capability"] = PluginDataAPI::CONTACTS;
|
||||
fields.append(field);
|
||||
|
||||
field = QJsonObject();
|
||||
field["placeholder"] = "URL";
|
||||
field["fieldId"] = "URL";
|
||||
field["defaultData"] = "";
|
||||
field["type"] = 1;
|
||||
field["capability"] = PluginDataAPI::CONTACTS;
|
||||
fields.append(field);
|
||||
|
||||
field = QJsonObject();
|
||||
field["placeholder"] = "Username";
|
||||
field["fieldId"] = "Username";
|
||||
field["defaultData"] = "username@domain.com";
|
||||
field["type"] = 1;
|
||||
field["capability"] = PluginDataAPI::CONTACTS;
|
||||
fields.append(field);
|
||||
|
||||
field = QJsonObject();
|
||||
field["placeholder"] = "Password";
|
||||
field["fieldId"] = "Password";
|
||||
field["defaultData"] = "This is a pass";
|
||||
field["type"] = 1;
|
||||
field["hiddenText"] = true;
|
||||
field["capability"] = PluginDataAPI::CONTACTS;
|
||||
fields.append(field);
|
||||
|
||||
description["fields"] = fields;
|
||||
|
||||
QJsonDocument document(description);
|
||||
return document.toJson();
|
||||
}
|
||||
|
||||
PluginDataAPI * Plugin::createInstance(void * core, QPluginLoader *pluginLoader){
|
||||
return new DataAPI(this, core, pluginLoader);
|
||||
}
|
||||
45
plugins/example/src/Plugin.hpp
Normal file
45
plugins/example/src/Plugin.hpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*******************************************************************************/
|
||||
#ifndef PLUGIN_HPP
|
||||
#define PLUGIN_HPP
|
||||
|
||||
#include <QObject>
|
||||
#include <QtPlugin>
|
||||
#include <LinphoneApp/LinphonePlugin.hpp>
|
||||
#include <LinphoneApp/PluginDataAPI.hpp>
|
||||
#include <linphone++/core.hh>
|
||||
//-----------------------------
|
||||
|
||||
class QPluginLoader;
|
||||
|
||||
class Plugin : public QObject, public LinphonePlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID LinphonePlugin_iid FILE "PluginMetaData.json")
|
||||
Q_INTERFACES(LinphonePlugin)
|
||||
public:
|
||||
Plugin(){}
|
||||
virtual QString getGUIDescriptionToJson() const;
|
||||
virtual PluginDataAPI * createInstance(void* core, QPluginLoader * pluginLoader);
|
||||
};
|
||||
|
||||
#endif
|
||||
6
plugins/example/src/PluginMetaData.json
Normal file
6
plugins/example/src/PluginMetaData.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"ID" : "ExamplePlugin. This ID must be unique from all plugins.",
|
||||
"Version" : "1.0.0",
|
||||
"Capabilities" : "Contacts",
|
||||
"Description" : "This is an example for describing your plugin. Replace all fields above to be usable by the Application."
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue