From 77fad7ba863fc2ecd3c5636cab5fa41f22ea9260 Mon Sep 17 00:00:00 2001 From: Christophe Deschamps Date: Thu, 5 Sep 2024 08:33:21 +0000 Subject: [PATCH] LDAP AdressBooks settings --- Linphone/core/App.cpp | 4 + Linphone/core/CMakeLists.txt | 6 + Linphone/core/address-books/LdapCore.cpp | 106 +++++++++++ Linphone/core/address-books/LdapCore.hpp | 72 ++++++++ Linphone/core/address-books/LdapGui.cpp | 46 +++++ Linphone/core/address-books/LdapGui.hpp | 42 +++++ Linphone/core/address-books/LdapList.cpp | 100 ++++++++++ Linphone/core/address-books/LdapList.hpp | 58 ++++++ Linphone/core/address-books/LdapProxy.cpp | 77 ++++++++ Linphone/core/address-books/LdapProxy.hpp | 60 ++++++ Linphone/model/CMakeLists.txt | 2 + Linphone/model/address-books/LdapModel.cpp | 85 +++++++++ Linphone/model/address-books/LdapModel.hpp | 70 +++++++ Linphone/tool/AbstractObject.hpp | 44 +++++ .../view/App/Layout/AbstractDetailsLayout.qml | 42 ++++- .../Account/AccountSettingsGeneralLayout.qml | 8 +- .../AccountSettingsParametersLayout.qml | 4 +- .../Layout/Settings/CallSettingsLayout.qml | 4 +- .../Settings/ContactsSettingsLayout.qml | 137 ++++++++++++++ .../Layout/Settings/DebugSettingsLayout.qml | 9 +- .../Layout/Settings/LdapSettingsLayout.qml | 171 ++++++++++++++++++ .../Settings/SecuritySettingsLayout.qml | 4 +- Linphone/view/CMakeLists.txt | 4 +- Linphone/view/Item/Contact/Contact.qml | 2 +- Linphone/view/Layout/ValidatedTextField.qml | 34 ++-- Linphone/view/Page/Main/SettingsPage.qml | 2 +- Linphone/view/Style/Typography.qml | 4 +- 27 files changed, 1158 insertions(+), 39 deletions(-) create mode 100644 Linphone/core/address-books/LdapCore.cpp create mode 100644 Linphone/core/address-books/LdapCore.hpp create mode 100644 Linphone/core/address-books/LdapGui.cpp create mode 100644 Linphone/core/address-books/LdapGui.hpp create mode 100644 Linphone/core/address-books/LdapList.cpp create mode 100644 Linphone/core/address-books/LdapList.hpp create mode 100644 Linphone/core/address-books/LdapProxy.cpp create mode 100644 Linphone/core/address-books/LdapProxy.hpp create mode 100644 Linphone/model/address-books/LdapModel.cpp create mode 100644 Linphone/model/address-books/LdapModel.hpp create mode 100644 Linphone/view/App/Layout/Settings/ContactsSettingsLayout.qml create mode 100644 Linphone/view/App/Layout/Settings/LdapSettingsLayout.qml diff --git a/Linphone/core/App.cpp b/Linphone/core/App.cpp index 114ec56f7..cbbd1b4a1 100644 --- a/Linphone/core/App.cpp +++ b/Linphone/core/App.cpp @@ -39,6 +39,8 @@ #include "core/account/AccountDeviceGui.hpp" #include "core/account/AccountDeviceProxy.hpp" #include "core/account/AccountProxy.hpp" +#include "core/address-books/LdapGui.hpp" +#include "core/address-books/LdapProxy.hpp" #include "core/call-history/CallHistoryProxy.hpp" #include "core/call/CallCore.hpp" #include "core/call/CallGui.hpp" @@ -562,6 +564,8 @@ void App::initCppInterfaces() { QLatin1String("Uncreatable")); qmlRegisterUncreatableType(Constants::MainQmlUri, 1, 0, "AuthenticationDialogCpp", QLatin1String("Uncreatable")); + qmlRegisterType(Constants::MainQmlUri, 1, 0, "LdapGui"); + qmlRegisterType(Constants::MainQmlUri, 1, 0, "LdapProxy"); LinphoneEnums::registerMetaTypes(); } diff --git a/Linphone/core/CMakeLists.txt b/Linphone/core/CMakeLists.txt index cf3b50378..0f55ed67b 100644 --- a/Linphone/core/CMakeLists.txt +++ b/Linphone/core/CMakeLists.txt @@ -67,6 +67,12 @@ list(APPEND _LINPHONEAPP_SOURCES core/videoSource/VideoSourceDescriptorCore.cpp core/videoSource/VideoSourceDescriptorGui.cpp + + core/address-books/LdapCore.cpp + core/address-books/LdapGui.cpp + core/address-books/LdapProxy.cpp + core/address-books/LdapList.cpp + ) ## Single Application diff --git a/Linphone/core/address-books/LdapCore.cpp b/Linphone/core/address-books/LdapCore.cpp new file mode 100644 index 000000000..40eeae893 --- /dev/null +++ b/Linphone/core/address-books/LdapCore.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2010-2024 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 . + */ + +#include "LdapCore.hpp" +#include "core/App.hpp" + +DEFINE_ABSTRACT_OBJECT(LdapCore) + +QSharedPointer LdapCore::create(const std::shared_ptr &ldap) { + auto sharedPointer = QSharedPointer(new LdapCore(ldap), &QObject::deleteLater); + sharedPointer->setSelf(sharedPointer); + sharedPointer->moveToThread(App::getInstance()->thread()); + return sharedPointer; +} + +LdapCore::LdapCore(const std::shared_ptr &ldap) : QObject(nullptr) { + App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership); + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + mLdapModel = Utils::makeQObject_ptr(ldap); + + INIT_CORE_MEMBER(Enabled, mLdapModel) + INIT_CORE_MEMBER(Server, mLdapModel) + INIT_CORE_MEMBER(BindDn, mLdapModel) + INIT_CORE_MEMBER(Password, mLdapModel) + INIT_CORE_MEMBER(AuthMethod, mLdapModel) + INIT_CORE_MEMBER(Tls, mLdapModel) + INIT_CORE_MEMBER(ServerCertificatesVerificationMode, mLdapModel) + INIT_CORE_MEMBER(BaseObject, mLdapModel) + INIT_CORE_MEMBER(Filter, mLdapModel) + INIT_CORE_MEMBER(MaxResults, mLdapModel) + INIT_CORE_MEMBER(Timeout, mLdapModel) + INIT_CORE_MEMBER(Delay, mLdapModel) + INIT_CORE_MEMBER(MinChars, mLdapModel) + INIT_CORE_MEMBER(NameAttribute, mLdapModel) + INIT_CORE_MEMBER(SipAttribute, mLdapModel) + INIT_CORE_MEMBER(SipDomain, mLdapModel) + INIT_CORE_MEMBER(DebugLevel, mLdapModel) +} + +LdapCore::~LdapCore() { + mustBeInMainThread(log().arg(Q_FUNC_INFO)); +} + +void LdapCore::save() { + mLdapModelConnection->invokeToModel([this]() { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + mLdapModel->save(); + }); +} + +void LdapCore::remove() { + mLdapModelConnection->invokeToModel([this]() { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + mLdapModel->remove(); + }); +} + +bool LdapCore::isValid() { + return !mServer.isEmpty() && !mBaseObject.isEmpty(); +} + +void LdapCore::setSelf(QSharedPointer me) { + mLdapModelConnection = QSharedPointer>( + new SafeConnection(me, mLdapModel), &QObject::deleteLater); + + DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, bool, enabled, Enabled) + DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, QString, server, Server) + DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, QString, bindDn, BindDn) + DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, QString, password, Password) + DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, linphone::Ldap::AuthMethod, + authMethod, AuthMethod) + DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, bool, tls, Tls) + DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, + linphone::Ldap::CertVerificationMode, serverCertificatesVerificationMode, + ServerCertificatesVerificationMode) + DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, QString, baseObject, BaseObject) + DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, QString, filter, Filter) + DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, int, maxResults, MaxResults) + DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, int, timeout, Timeout) + DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, int, delay, Delay) + DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, int, minChars, MinChars) + DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, QString, nameAttribute, + NameAttribute) + DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, QString, sipAttribute, + SipAttribute) + DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, QString, sipDomain, SipDomain) + DEFINE_CORE_GETSET_CONNECT(mLdapModelConnection, LdapCore, LdapModel, mLdapModel, linphone::Ldap::DebugLevel, + debugLevel, DebugLevel) +} diff --git a/Linphone/core/address-books/LdapCore.hpp b/Linphone/core/address-books/LdapCore.hpp new file mode 100644 index 000000000..1b6705506 --- /dev/null +++ b/Linphone/core/address-books/LdapCore.hpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2010-2024 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 . + */ + +#ifndef LDAP_CORE_H_ +#define LDAP_CORE_H_ + +#include "model/address-books/LdapModel.hpp" +#include "tool/AbstractObject.hpp" +#include "tool/thread/SafeConnection.hpp" +#include +#include +#include + +class LdapCore : public QObject, public AbstractObject { + Q_OBJECT + +public: + static QSharedPointer create(const std::shared_ptr &ldap); + LdapCore(const std::shared_ptr &ldap); + ~LdapCore(); + + void setSelf(QSharedPointer me); + + Q_INVOKABLE void remove(); + Q_INVOKABLE void save(); + Q_INVOKABLE bool isValid(); + + DECLARE_CORE_GETSET(bool, enabled, Enabled) + DECLARE_CORE_GETSET(QString, server, Server) + DECLARE_CORE_GETSET(QString, bindDn, BindDn) + DECLARE_CORE_GETSET(QString, password, Password) + DECLARE_CORE_GETSET(linphone::Ldap::AuthMethod, authMethod, AuthMethod) + DECLARE_CORE_GETSET(bool, tls, Tls) + DECLARE_CORE_GETSET(linphone::Ldap::CertVerificationMode, + serverCertificatesVerificationMode, + ServerCertificatesVerificationMode) + DECLARE_CORE_GETSET(QString, baseObject, BaseObject) + DECLARE_CORE_GETSET(QString, filter, Filter) + DECLARE_CORE_GETSET(int, maxResults, MaxResults) + DECLARE_CORE_GETSET(int, timeout, Timeout) + DECLARE_CORE_GETSET(int, delay, Delay) + DECLARE_CORE_GETSET(int, minChars, MinChars) + DECLARE_CORE_GETSET(QString, nameAttribute, NameAttribute) + DECLARE_CORE_GETSET(QString, sipAttribute, SipAttribute) + DECLARE_CORE_GETSET(QString, sipDomain, SipDomain) + DECLARE_CORE_GETSET(linphone::Ldap::DebugLevel, debugLevel, DebugLevel) + +private: + std::shared_ptr mLdapModel; + QSharedPointer> mLdapModelConnection; + + DECLARE_ABSTRACT_OBJECT +}; +Q_DECLARE_METATYPE(LdapCore *) +#endif diff --git a/Linphone/core/address-books/LdapGui.cpp b/Linphone/core/address-books/LdapGui.cpp new file mode 100644 index 000000000..e2c6956c3 --- /dev/null +++ b/Linphone/core/address-books/LdapGui.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2010-2024 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 . + */ + +#include "LdapGui.hpp" +#include "core/App.hpp" + +DEFINE_ABSTRACT_OBJECT(LdapGui) + +LdapGui::LdapGui(QObject *parent) : QObject(parent) { + mustBeInMainThread(getClassName()); + App::postModelSync([this]() { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + mCore = LdapCore::create(nullptr); + }); +} + +LdapGui::LdapGui(QSharedPointer core) { + App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); + mCore = core; + if (isInLinphoneThread()) moveToThread(App::getInstance()->thread()); +} + +LdapGui::~LdapGui() { + mustBeInMainThread("~" + getClassName()); +} + +LdapCore *LdapGui::getCore() const { + return mCore.get(); +} diff --git a/Linphone/core/address-books/LdapGui.hpp b/Linphone/core/address-books/LdapGui.hpp new file mode 100644 index 000000000..c345e4d36 --- /dev/null +++ b/Linphone/core/address-books/LdapGui.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2010-2024 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 . + */ + +#ifndef LDAP_GUI_H_ +#define LDAP_GUI_H_ + +#include "LdapCore.hpp" +#include +#include + +class LdapGui : public QObject, public AbstractObject { + Q_OBJECT + + Q_PROPERTY(LdapCore *core READ getCore CONSTANT) + +public: + LdapGui(QObject *parent = nullptr); + LdapGui(QSharedPointer core); + ~LdapGui(); + LdapCore *getCore() const; + QSharedPointer mCore; + DECLARE_ABSTRACT_OBJECT +}; + +#endif diff --git a/Linphone/core/address-books/LdapList.cpp b/Linphone/core/address-books/LdapList.cpp new file mode 100644 index 000000000..413b6cc75 --- /dev/null +++ b/Linphone/core/address-books/LdapList.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2010-2024 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 . + */ + +#include "LdapList.hpp" +#include "LdapGui.hpp" +#include "core/App.hpp" +#include "model/object/VariantObject.hpp" +#include +#include + +// ============================================================================= + +DEFINE_ABSTRACT_OBJECT(LdapList) + +QSharedPointer LdapList::create() { + auto model = QSharedPointer(new LdapList(), &QObject::deleteLater); + model->moveToThread(App::getInstance()->thread()); + model->setSelf(model); + return model; +} + +QSharedPointer LdapList::createLdapCore(const std::shared_ptr &ldap) { + auto LdapCore = LdapCore::create(ldap); + return LdapCore; +} + +LdapList::LdapList(QObject *parent) : ListProxy(parent) { + mustBeInMainThread(getClassName()); +} + +LdapList::~LdapList() { + mustBeInMainThread("~" + getClassName()); + mModelConnection = nullptr; +} + +void LdapList::setSelf(QSharedPointer me) { + mModelConnection = QSharedPointer>( + new SafeConnection(me, CoreModel::getInstance()), &QObject::deleteLater); + mModelConnection->makeConnectToCore(&LdapList::lUpdate, [this]() { + mModelConnection->invokeToModel([this]() { + QList> *ldaps = new QList>(); + mustBeInLinphoneThread(getClassName()); + for (auto ldap : CoreModel::getInstance()->getCore()->getLdapList()) { + auto model = createLdapCore(ldap); + ldaps->push_back(model); + } + mModelConnection->invokeToCore([this, ldaps]() { + mustBeInMainThread(getClassName()); + resetData(); + add(*ldaps); + delete ldaps; + }); + }); + }); + emit lUpdate(); +} + +void LdapList::removeAllEntries() { + beginResetModel(); + for (auto it = mList.rbegin(); it != mList.rend(); ++it) { + auto ldap = it->objectCast(); + ldap->remove(); + } + mList.clear(); + endResetModel(); +} + +void LdapList::remove(const int &row) { + beginRemoveRows(QModelIndex(), row, row); + auto item = mList[row].objectCast(); + item->remove(); + mList.takeAt(row); + endRemoveRows(); +} + +QVariant LdapList::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(new LdapGui(mList[row].objectCast())); + } + return QVariant(); +} diff --git a/Linphone/core/address-books/LdapList.hpp b/Linphone/core/address-books/LdapList.hpp new file mode 100644 index 000000000..74283d098 --- /dev/null +++ b/Linphone/core/address-books/LdapList.hpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2010-2024 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 . + */ + +#ifndef LDAP_LIST_H_ +#define LDAP_LIST_H_ + +#include "../proxy/ListProxy.hpp" +#include "tool/AbstractObject.hpp" +#include "tool/thread/SafeConnection.hpp" +#include + +class LdapCore; +class CoreModel; +// ============================================================================= + +class LdapList : public ListProxy, public AbstractObject { + Q_OBJECT + +public: + static QSharedPointer create(); + // Create a LdapCore and make connections to List. + QSharedPointer createLdapCore(const std::shared_ptr &ldap); + LdapList(QObject *parent = Q_NULLPTR); + ~LdapList(); + + void setSelf(QSharedPointer me); + + void removeAllEntries(); + void remove(const int &row); + + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + +signals: + void lUpdate(); + +private: + QSharedPointer> mModelConnection; + DECLARE_ABSTRACT_OBJECT +}; + +#endif diff --git a/Linphone/core/address-books/LdapProxy.cpp b/Linphone/core/address-books/LdapProxy.cpp new file mode 100644 index 000000000..ecdb2d6ad --- /dev/null +++ b/Linphone/core/address-books/LdapProxy.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2010-2024 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 . + */ + +#include "LdapProxy.hpp" +#include "LdapGui.hpp" +#include "LdapList.hpp" + +DEFINE_ABSTRACT_OBJECT(LdapProxy) + +LdapProxy::LdapProxy(QObject *parent) : SortFilterProxy(parent) { + mLdapList = LdapList::create(); + setSourceModel(mLdapList.get()); +} + +LdapProxy::~LdapProxy() { + setSourceModel(nullptr); +} + +QString LdapProxy::getFilterText() const { + return mFilterText; +} + +void LdapProxy::setFilterText(const QString &filter) { + if (mFilterText != filter) { + mFilterText = filter; + invalidate(); + emit filterTextChanged(); + } +} + +void LdapProxy::removeAllEntries() { + static_cast(sourceModel())->removeAllEntries(); +} + +void LdapProxy::removeEntriesWithFilter() { + std::list> itemList(rowCount()); + for (auto i = rowCount() - 1; i >= 0; --i) { + auto item = getItemAt(i); + itemList.emplace_back(item); + } + for (auto item : itemList) { + mLdapList->ListProxy::remove(item.get()); + if (item) item->remove(); + } +} + +bool LdapProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { + return true; +} + +bool LdapProxy::lessThan(const QModelIndex &left, const QModelIndex &right) const { + auto l = getItemAt(left.row()); + auto r = getItemAt(right.row()); + + return l->mSipDomain < r->mSipDomain; +} + +void LdapProxy::updateView() { + mLdapList->lUpdate(); +} diff --git a/Linphone/core/address-books/LdapProxy.hpp b/Linphone/core/address-books/LdapProxy.hpp new file mode 100644 index 000000000..4c485719a --- /dev/null +++ b/Linphone/core/address-books/LdapProxy.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2010-2024 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 . + */ + +#ifndef LDAP_PROXY_H_ +#define LDAP_PROXY_H_ + +#include "../proxy/SortFilterProxy.hpp" +#include "LdapGui.hpp" +#include "LdapList.hpp" +#include "tool/AbstractObject.hpp" + +// ============================================================================= + +class LdapProxy : public SortFilterProxy, public AbstractObject { + Q_OBJECT + + Q_PROPERTY(QString filterText READ getFilterText WRITE setFilterText NOTIFY filterTextChanged) + +public: + LdapProxy(QObject *parent = Q_NULLPTR); + ~LdapProxy(); + + QString getFilterText() const; + void setFilterText(const QString &filter); + + Q_INVOKABLE void removeAllEntries(); + Q_INVOKABLE void removeEntriesWithFilter(); + Q_INVOKABLE void updateView(); + +signals: + void filterTextChanged(); + +protected: + virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; + virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; + + QString mFilterText; + QSharedPointer mLdapList; + + DECLARE_ABSTRACT_OBJECT +}; + +#endif diff --git a/Linphone/model/CMakeLists.txt b/Linphone/model/CMakeLists.txt index 1566e7464..f89e14b0a 100644 --- a/Linphone/model/CMakeLists.txt +++ b/Linphone/model/CMakeLists.txt @@ -40,6 +40,8 @@ list(APPEND _LINPHONEAPP_SOURCES model/tool/ToolModel.cpp model/videoSource/VideoSourceDescriptorModel.cpp + + model/address-books/LdapModel.cpp ) set(_LINPHONEAPP_SOURCES ${_LINPHONEAPP_SOURCES} PARENT_SCOPE) diff --git a/Linphone/model/address-books/LdapModel.cpp b/Linphone/model/address-books/LdapModel.cpp new file mode 100644 index 000000000..c6f368687 --- /dev/null +++ b/Linphone/model/address-books/LdapModel.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2010-2024 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 . + */ + +#include "LdapModel.hpp" +#include "model/core/CoreModel.hpp" +#include "tool/Utils.hpp" + +DEFINE_ABSTRACT_OBJECT(LdapModel) + +LdapModel::LdapModel(const std::shared_ptr &ldap, QObject *parent) { + mustBeInLinphoneThread(getClassName()); + if (ldap) { + mLdap = ldap; + mLdapParamsClone = mLdap->getParams()->clone(); + } else { + mLdap = nullptr; + mLdapParamsClone = CoreModel::getInstance()->getCore()->createLdapParams(); + mLdapParamsClone->setTimeout(5); + mLdapParamsClone->setDelay(2000); + mLdapParamsClone->setMinChars(3); + mLdapParamsClone->enableTls(true); + } +} + +LdapModel::~LdapModel() { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); +} + +void LdapModel::save() { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + if (mLdap) + CoreModel::getInstance()->getCore()->removeLdap( + mLdap); // Need to do remove/add when updating, as setParams on existing one also adds it to core. + mLdap = CoreModel::getInstance()->getCore()->createLdapWithParams(mLdapParamsClone); + CoreModel::getInstance()->getCore()->addLdap(mLdap); + lDebug() << log().arg("LDAP Server saved"); + mLdapParamsClone = mLdap->getParams()->clone(); + emit saved(); +} + +void LdapModel::remove() { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + CoreModel::getInstance()->getCore()->removeLdap(mLdap); + lDebug() << log().arg("LDAP Server removed"); + emit removed(); +} + +DEFINE_GETSET(LdapModel, bool, enabled, Enabled, mLdapParamsClone) +DEFINE_GETSET_MODEL_STRING(LdapModel, server, Server, mLdapParamsClone) +DEFINE_GETSET_MODEL_STRING(LdapModel, bindDn, BindDn, mLdapParamsClone) +DEFINE_GETSET_MODEL_STRING(LdapModel, password, Password, mLdapParamsClone) +DEFINE_GETSET(LdapModel, linphone::Ldap::AuthMethod, authMethod, AuthMethod, mLdapParamsClone) +DEFINE_GETSET_ENABLED(LdapModel, tls, Tls, mLdapParamsClone) +DEFINE_GETSET(LdapModel, + linphone::Ldap::CertVerificationMode, + serverCertificatesVerificationMode, + ServerCertificatesVerificationMode, + mLdapParamsClone) +DEFINE_GETSET_MODEL_STRING(LdapModel, baseObject, BaseObject, mLdapParamsClone) +DEFINE_GETSET_MODEL_STRING(LdapModel, filter, Filter, mLdapParamsClone) +DEFINE_GETSET(LdapModel, int, maxResults, MaxResults, mLdapParamsClone) +DEFINE_GETSET(LdapModel, int, timeout, Timeout, mLdapParamsClone) +DEFINE_GETSET(LdapModel, int, delay, Delay, mLdapParamsClone) +DEFINE_GETSET(LdapModel, int, minChars, MinChars, mLdapParamsClone) +DEFINE_GETSET_MODEL_STRING(LdapModel, nameAttribute, NameAttribute, mLdapParamsClone) +DEFINE_GETSET_MODEL_STRING(LdapModel, sipAttribute, SipAttribute, mLdapParamsClone) +DEFINE_GETSET_MODEL_STRING(LdapModel, sipDomain, SipDomain, mLdapParamsClone) +DEFINE_GETSET(LdapModel, linphone::Ldap::DebugLevel, debugLevel, DebugLevel, mLdapParamsClone) diff --git a/Linphone/model/address-books/LdapModel.hpp b/Linphone/model/address-books/LdapModel.hpp new file mode 100644 index 000000000..e1e0d5138 --- /dev/null +++ b/Linphone/model/address-books/LdapModel.hpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2010-2024 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 . + */ + +#ifndef LDAP_MODEL_H_ +#define LDAP_MODEL_H_ + +#include "tool/AbstractObject.hpp" +#include +#include + +class LdapModel : public QObject, public AbstractObject { + Q_OBJECT + +public: + LdapModel(const std::shared_ptr &ldap, QObject *parent = nullptr); + ~LdapModel(); + + void setDefaultParams(); + void save(); + void remove(); + + DECLARE_GETSET(bool, enabled, Enabled) + DECLARE_GETSET(QString, server, Server) + DECLARE_GETSET(QString, bindDn, BindDn) + DECLARE_GETSET(QString, password, Password) + DECLARE_GETSET(linphone::Ldap::AuthMethod, authMethod, AuthMethod) + DECLARE_GETSET(bool, tls, Tls) + DECLARE_GETSET(linphone::Ldap::CertVerificationMode, + serverCertificatesVerificationMode, + ServerCertificatesVerificationMode) + DECLARE_GETSET(QString, baseObject, BaseObject) + DECLARE_GETSET(QString, filter, Filter) + DECLARE_GETSET(int, maxResults, MaxResults) + DECLARE_GETSET(int, timeout, Timeout) + DECLARE_GETSET(int, delay, Delay) + DECLARE_GETSET(int, minChars, MinChars) + DECLARE_GETSET(QString, nameAttribute, NameAttribute) + DECLARE_GETSET(QString, sipAttribute, SipAttribute) + DECLARE_GETSET(QString, sipDomain, SipDomain) + DECLARE_GETSET(linphone::Ldap::DebugLevel, debugLevel, DebugLevel) + +signals: + void saved(); + void removed(); + +private: + std::shared_ptr mLdap; + std::shared_ptr mLdapParamsClone; + + DECLARE_ABSTRACT_OBJECT +}; + +#endif diff --git a/Linphone/tool/AbstractObject.hpp b/Linphone/tool/AbstractObject.hpp index a45227b8c..24476b3e8 100644 --- a/Linphone/tool/AbstractObject.hpp +++ b/Linphone/tool/AbstractObject.hpp @@ -126,6 +126,50 @@ public: #define DEFINE_NOTIFY_CONFIG_READY(x, X) \ emit x##Changed(get##X()); +#define DECLARE_GETSET_API(type, x, X) \ + type get##X() const; \ + void set##X(type data); \ + Q_SIGNAL void x##Changed(type x); + +#define DEFINE_GETSET(Class, type, x, X, ownerNotNull) \ + type Class::get##X() const { \ + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); \ + return ownerNotNull->get##X(); \ + } \ + void Class::set##X(type data) { \ + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); \ + if (get##X() != data) { \ + ownerNotNull->set##X(data); \ + emit x##Changed(data); \ + } \ + } + +#define DEFINE_GETSET_MODEL_STRING(Class, x, X, ownerNotNull) \ +QString Class::get##X() const { \ + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); \ + return Utils::coreStringToAppString(ownerNotNull->get##X()); \ + } \ + void Class::set##X(QString data) { \ + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); \ + if (get##X() != data) { \ + ownerNotNull->set##X(Utils::appStringToCoreString(data)); \ + emit x##Changed(data); \ + } \ + } + +#define DEFINE_GETSET_ENABLED(Class, x, X, ownerNotNull) \ +bool Class::get##X() const { \ + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); \ + return ownerNotNull->x##Enabled(); \ + } \ + void Class::set##X(bool data) { \ + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); \ + if (get##X() != data) { \ + ownerNotNull->enable##X(data); \ + emit x##Changed(data); \ + } \ + } + class AbstractObject { public: virtual QString getClassName() const = 0; diff --git a/Linphone/view/App/Layout/AbstractDetailsLayout.qml b/Linphone/view/App/Layout/AbstractDetailsLayout.qml index 6013c39a6..5c726a39e 100644 --- a/Linphone/view/App/Layout/AbstractDetailsLayout.qml +++ b/Linphone/view/App/Layout/AbstractDetailsLayout.qml @@ -10,7 +10,8 @@ Rectangle { width: container.width height: container.height property string titleText - property var component + property var contentComponent + property var topbarOptionalComponent property var model color: 'white' property var container @@ -38,12 +39,40 @@ Rectangle { id: content width: parent.width spacing: 10 * DefaultStyle.dp - Text { - text: titleText - font: Typography.h3 + RowLayout { Layout.fillWidth: true Layout.topMargin: 20 * DefaultStyle.dp - color: DefaultStyle.main2_600 + spacing: 5 * DefaultStyle.dp + Button { + id: backButton + Layout.preferredHeight: 24 * DefaultStyle.dp + Layout.preferredWidth: 24 * DefaultStyle.dp + icon.source: AppIcons.leftArrow + width: 24 * DefaultStyle.dp + height: 24 * DefaultStyle.dp + focus: true + visible: mainItem.container.depth > 1 + Layout.rightMargin: 41 * DefaultStyle.dp + onClicked: { + mainItem.container.pop() + } + background: Item { + anchors.fill: parent + } + } + Text { + text: titleText + color: DefaultStyle.main2_600 + font: Typography.h3 + } + Item { + Layout.fillWidth: true + } + Loader { + Layout.alignment: Qt.AlignRight + sourceComponent: mainItem.topbarOptionalComponent + Layout.rightMargin: 34 * DefaultStyle.dp + } } Rectangle { Layout.fillWidth: true @@ -52,9 +81,8 @@ Rectangle { color: DefaultStyle.main2_500main } Loader { - id:loader Layout.fillWidth: true - sourceComponent: mainItem.component + sourceComponent: mainItem.contentComponent } Item { Layout.fillHeight: true diff --git a/Linphone/view/App/Layout/Account/AccountSettingsGeneralLayout.qml b/Linphone/view/App/Layout/Account/AccountSettingsGeneralLayout.qml index 36c233238..3be86c3fa 100644 --- a/Linphone/view/App/Layout/Account/AccountSettingsGeneralLayout.qml +++ b/Linphone/view/App/Layout/Account/AccountSettingsGeneralLayout.qml @@ -9,10 +9,10 @@ import UtilsCpp AbstractDetailsLayout { id: mainItem - component: main + contentComponent: content property alias account: mainItem.model Component { - id: main + id: content ColumnLayout { width: parent.width spacing: 5 * DefaultStyle.dp @@ -229,10 +229,6 @@ AbstractDetailsLayout { } } } - Component.onCompleted: { - } - Component.onDestruction: { - } } Rectangle { Layout.fillWidth: true diff --git a/Linphone/view/App/Layout/Account/AccountSettingsParametersLayout.qml b/Linphone/view/App/Layout/Account/AccountSettingsParametersLayout.qml index c99f19758..effec81f5 100644 --- a/Linphone/view/App/Layout/Account/AccountSettingsParametersLayout.qml +++ b/Linphone/view/App/Layout/Account/AccountSettingsParametersLayout.qml @@ -9,10 +9,10 @@ import UtilsCpp AbstractDetailsLayout { id: mainItem - component: main + contentComponent: content property alias account: mainItem.model Component { - id: main + id: content ColumnLayout { width: parent.width spacing: 5 * DefaultStyle.dp diff --git a/Linphone/view/App/Layout/Settings/CallSettingsLayout.qml b/Linphone/view/App/Layout/Settings/CallSettingsLayout.qml index 17a6650e8..c2f2cc50f 100644 --- a/Linphone/view/App/Layout/Settings/CallSettingsLayout.qml +++ b/Linphone/view/App/Layout/Settings/CallSettingsLayout.qml @@ -6,10 +6,10 @@ import Linphone import SettingsCpp 1.0 AbstractDetailsLayout { - component: settings + contentComponent: content width: parent.width Component { - id: settings + id: content ColumnLayout { width: parent.width RowLayout { diff --git a/Linphone/view/App/Layout/Settings/ContactsSettingsLayout.qml b/Linphone/view/App/Layout/Settings/ContactsSettingsLayout.qml new file mode 100644 index 000000000..e66268791 --- /dev/null +++ b/Linphone/view/App/Layout/Settings/ContactsSettingsLayout.qml @@ -0,0 +1,137 @@ + +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls as Control +import SettingsCpp 1.0 +import Linphone + +AbstractDetailsLayout { + id: mainItem + contentComponent: content + function layoutUrl(name) { + return layoutsPath+"/"+name+".qml" + } + Component { + id: content + RowLayout { + spacing: 5 * DefaultStyle.dp + ColumnLayout { + Layout.fillWidth: true + spacing: 5 * DefaultStyle.dp + ColumnLayout { + Layout.preferredWidth: 341 * DefaultStyle.dp + Layout.maximumWidth: 341 * DefaultStyle.dp + spacing: 5 * DefaultStyle.dp + Text { + text: qsTr("Annuaires LDAP") + font: Typography.h4 + wrapMode: Text.WordWrap + color: DefaultStyle.main2_600 + Layout.fillWidth: true + } + Text { + text: qsTr("Ajouter vos annuaires LDAP pour pouvoir effectuer des recherches dans la magic search bar.") + font: Typography.p1s + wrapMode: Text.WordWrap + color: DefaultStyle.main2_600 + Layout.fillWidth: true + } + } + Item { + Layout.fillHeight: true + } + } + ColumnLayout { + Layout.rightMargin: 25 * DefaultStyle.dp + Layout.fillWidth: true + Layout.fillHeight: true + spacing: 27 * DefaultStyle.dp + Layout.leftMargin: 76 * DefaultStyle.dp + Layout.topMargin: 16 * DefaultStyle.dp + Repeater { + model: LdapProxy { + id: proxyModel + } + RowLayout { + Layout.fillWidth: true + Layout.alignment: Qt.AlignLeft|Qt.AlignHCenter + spacing: 5 * DefaultStyle.dp + Text { + text: modelData.core.server + font: Typography.p2l + wrapMode: Text.WordWrap + color: DefaultStyle.main2_600 + Layout.fillWidth: true + Layout.leftMargin: 17 * DefaultStyle.dp + } + Item { + Layout.fillWidth: true + } + Button { + background: Item{} + icon.source: AppIcons.pencil + icon.width: 24 * DefaultStyle.dp + icon.height: 24 * DefaultStyle.dp + contentImageColor: DefaultStyle.main2_600 + onClicked: { + var ldapGui = Qt.createQmlObject('import Linphone + LdapGui{ + }', mainItem) + mainItem.container.push(layoutUrl("LdapSettingsLayout"), { + titleText: qsTr("Modifier un annuaire LDAP"), + model: modelData, + container: mainItem.container, + isNew: false}) + } + } + Switch { + id: switchButton + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + Layout.rightMargin: 17 * DefaultStyle.dp + checked: modelData.core["enabled"] + onToggled: { + binding.when = true + modelData.core.save() + } + } + Binding { + id: binding + target: modelData.core + property: "enabled" + value: switchButton.checked + when: false + } + } + onVisibleChanged: { + proxyModel.updateView() + } + Component.onCompleted: { + proxyModel.updateView() + } + } + RowLayout { + Layout.fillWidth: true + spacing: 5 * DefaultStyle.dp + Item { + Layout.fillWidth: true + } + Button { + Layout.alignment: Qt.AlignRight | Qt.AlignHCenter + text: qsTr("Ajouter") + onClicked: { + var ldapGui = Qt.createQmlObject('import Linphone + LdapGui{ + }', mainItem) + mainItem.container.push(layoutUrl("LdapSettingsLayout"), { + titleText: qsTr("Ajouter un annuaire LDAP"), + model: ldapGui, + container: mainItem.container, + isNew: true}) + } + } + } + } + + } + } +} diff --git a/Linphone/view/App/Layout/Settings/DebugSettingsLayout.qml b/Linphone/view/App/Layout/Settings/DebugSettingsLayout.qml index 419b86588..195e5a168 100644 --- a/Linphone/view/App/Layout/Settings/DebugSettingsLayout.qml +++ b/Linphone/view/App/Layout/Settings/DebugSettingsLayout.qml @@ -12,12 +12,14 @@ AbstractDetailsLayout { Layout.fillHeight: true id: mainItem property string logsUrl - + contentComponent: content + Dialog { id: deleteLogs text: qsTr("Les traces de débogage seront supprimées. Souhaitez-vous continuer ?") onAccepted: SettingsCpp.cleanLogs() } + Dialog { id: shareLogs text: qsTr("Les traces de débogage ont été téléversées. Comment souhaitez-vous partager le lien ? ") @@ -43,8 +45,9 @@ AbstractDetailsLayout { } ] } + Component { - id: debug + id: content ColumnLayout { spacing: 40 * DefaultStyle.dp SwitchSetting { @@ -73,6 +76,7 @@ AbstractDetailsLayout { } } } + Connections { target: SettingsCpp function onLogsUploadTerminated(status, url) { @@ -85,5 +89,4 @@ AbstractDetailsLayout { } } } - component: debug } diff --git a/Linphone/view/App/Layout/Settings/LdapSettingsLayout.qml b/Linphone/view/App/Layout/Settings/LdapSettingsLayout.qml new file mode 100644 index 000000000..73d429998 --- /dev/null +++ b/Linphone/view/App/Layout/Settings/LdapSettingsLayout.qml @@ -0,0 +1,171 @@ +import QtCore +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls as Control +import QtQuick.Dialogs +import Linphone +import SettingsCpp 1.0 +import UtilsCpp + +AbstractDetailsLayout { + id: mainItem + contentComponent: content + topbarOptionalComponent: topBar + property alias ldapGui: mainItem.model + property bool isNew: false + Component { + id: topBar + RowLayout { + spacing: 20 * DefaultStyle.dp + Button { + background: Item{} + icon.source: AppIcons.trashCan + icon.width: 32 * DefaultStyle.dp + icon.height: 32 * DefaultStyle.dp + contentImageColor: DefaultStyle.main2_600 + visible: !isNew + onClicked: { + var mainWin = UtilsCpp.getMainWindow() + mainWin.showConfirmationLambdaPopup( + qsTr("Supprimer l'annuaire LDAP ?"), + "", + function (confirmed) { + if (confirmed) { + ldapGui.core.remove() + mainItem.container.pop() + } + } + ) + } + } + Button { + text: qsTr("Enregistrer") + onClicked: { + if (ldapGui.core.isValid()) { + ldapGui.core.save() + UtilsCpp.showInformationPopup(qsTr("Succès"), qsTr("L'annuaire LDAP a été sauvegardé"), true, mainWindow) + } else { + UtilsCpp.showInformationPopup(qsTr("Erreur"), qsTr("Une erreur s'est produite, la configuration LDAP n'a pas été sauvegardée !"), false, mainWindow) + } + } + } + } + } + + Component { + id: content + ColumnLayout { + width: parent.width + spacing: 5 * DefaultStyle.dp + RowLayout { + Layout.topMargin: 16 * DefaultStyle.dp + spacing: 5 * DefaultStyle.dp + ColumnLayout { + Layout.fillWidth: true + spacing: 5 * DefaultStyle.dp + ColumnLayout { + Layout.preferredWidth: 341 * DefaultStyle.dp + Layout.maximumWidth: 341 * DefaultStyle.dp + Layout.minimumWidth: 341 * DefaultStyle.dp + spacing: 5 * DefaultStyle.dp + Text { + Layout.fillWidth: true + text: qsTr("Annuaires LDAP") + font: Typography.h4 + wrapMode: Text.WordWrap + color: DefaultStyle.main2_600 + } + Text { + text: qsTr("Ajouter vos annuaires LDAP pour pouvoir effectuer des recherches dans la magic search bar.") + font: Typography.p1s + wrapMode: Text.WordWrap + color: DefaultStyle.main2_600 + Layout.fillWidth: true + } + } + Item { + Layout.fillHeight: true + } + } + ColumnLayout { + Layout.fillWidth: true + spacing: 20 * DefaultStyle.dp + Layout.rightMargin: 44 * DefaultStyle.dp + Layout.topMargin: 20 * DefaultStyle.dp + Layout.leftMargin: 64 * DefaultStyle.dp + ValidatedTextField { + id: server + propertyName: "server" + propertyOwner: ldapGui.core + title: qsTr("URL du serveur (ne peut être vide)") + } + ValidatedTextField { + propertyName: "bindDn" + propertyOwner: ldapGui.core + title: qsTr("Bind DN") + } + ValidatedTextField { + propertyName: "password" + hidden: true + propertyOwner: ldapGui.core + title: qsTr("Mot de passe") + } + SwitchSetting { + titleText: qsTr("Utiliser TLS") + propertyName: "tls" + propertyOwner: ldapGui.core + } + ValidatedTextField { + propertyName: "baseObject" + propertyOwner: ldapGui.core + title: qsTr("Base de recherche (ne peut être vide)") + } + ValidatedTextField { + propertyName: "filter" + propertyOwner: ldapGui.core + title: qsTr("Filtre") + } + ValidatedTextField { + propertyName: "maxResults" + propertyOwner: ldapGui.core + validator: RegularExpressionValidator { regularExpression: /[0-9]+/ } + title: qsTr("Nombre maximum de résultats") + } + ValidatedTextField { + propertyName: "delay" + propertyOwner: ldapGui.core + validator: RegularExpressionValidator { regularExpression: /[0-9]+/ } + title: qsTr("Délai entre 2 requêtes (en millisecondes)") + } + ValidatedTextField { + propertyName: "timeout" + propertyOwner: ldapGui.core + title: qsTr("Durée maximun (en secondes)") + validator: RegularExpressionValidator { regularExpression: /[0-9]+/ } + } + ValidatedTextField { + propertyName: "minChars" + propertyOwner: ldapGui.core + title: qsTr("Nombre minimum de caractères pour la requête") + validator: RegularExpressionValidator { regularExpression: /[0-9]+/ } + } + ValidatedTextField { + propertyName: "nameAttribute" + propertyOwner: ldapGui.core + title: qsTr("Attributs de nom") + } + ValidatedTextField { + propertyName: "sipAttribute" + propertyOwner: ldapGui.core + title: qsTr("Attributs SIP") + } + ValidatedTextField { + propertyName: "sipDomain" + propertyOwner: ldapGui.core + title: qsTr("Domaine SIP") + } + } + } + } + } +} diff --git a/Linphone/view/App/Layout/Settings/SecuritySettingsLayout.qml b/Linphone/view/App/Layout/Settings/SecuritySettingsLayout.qml index f9a4430cf..f01dc72a8 100644 --- a/Linphone/view/App/Layout/Settings/SecuritySettingsLayout.qml +++ b/Linphone/view/App/Layout/Settings/SecuritySettingsLayout.qml @@ -6,8 +6,9 @@ import SettingsCpp 1.0 import Linphone AbstractDetailsLayout { + contentComponent: content Component { - id: settings + id: content Column { spacing: 40 * DefaultStyle.dp SwitchSetting { @@ -18,5 +19,4 @@ AbstractDetailsLayout { } } } - component: settings } diff --git a/Linphone/view/CMakeLists.txt b/Linphone/view/CMakeLists.txt index 90ee508b7..dc0826d95 100644 --- a/Linphone/view/CMakeLists.txt +++ b/Linphone/view/CMakeLists.txt @@ -23,7 +23,9 @@ list(APPEND _LINPHONEAPP_QML_FILES view/App/Layout/Settings/SecuritySettingsLayout.qml view/App/Layout/Settings/CallSettingsLayout.qml view/App/Layout/Settings/DebugSettingsLayout.qml - + view/App/Layout/Settings/ContactsSettingsLayout.qml + view/App/Layout/Settings/LdapSettingsLayout.qml + view/App/Layout/Account/AccountSettingsGeneralLayout.qml view/App/Layout/Account/AccountSettingsParametersLayout.qml diff --git a/Linphone/view/Item/Contact/Contact.qml b/Linphone/view/Item/Contact/Contact.qml index 1ac25d752..273c3167d 100644 --- a/Linphone/view/Item/Contact/Contact.qml +++ b/Linphone/view/Item/Contact/Contact.qml @@ -106,7 +106,7 @@ Rectangle{ Layout.preferredHeight: 26 * DefaultStyle.dp Layout.fillHeight: true Layout.leftMargin: 40 * DefaultStyle.dp - visible: true // mainItem.account.core.unreadCallNotifications > 0 + visible: mainItem.account.core.unreadCallNotifications > 0 Rectangle{ id: unreadNotifications anchors.verticalCenter: parent.verticalCenter diff --git a/Linphone/view/Layout/ValidatedTextField.qml b/Linphone/view/Layout/ValidatedTextField.qml index 1f3d05c21..7ff2d9c39 100644 --- a/Linphone/view/Layout/ValidatedTextField.qml +++ b/Linphone/view/Layout/ValidatedTextField.qml @@ -19,6 +19,10 @@ FormItemLayout { property var isValid: function(text) { return true; } + property alias hidden: textField.hidden + property alias validator: textField.validator + property bool empty: mainItem.propertyOwner[mainItem.propertyName]?.length == 0 + property bool canBeEmpty: true contentItem: TextField { id: textField property var initialReading: true @@ -33,23 +37,29 @@ FormItemLayout { onTriggered: textField.editingFinished() } onEditingFinished: { + updateText() + } + onTextChanged: { + idleTimer.restart() + updateText() + } + function updateText() { + mainItem.empty = text.length == 0 if (initialReading) { initialReading = false return } - if (text.length != 0) { - if (isValid(text)) { - mainItem.errorMessage = "" - if (mainItem.propertyOwner[mainItem.propertyName] != text) - mainItem.propertyOwner[mainItem.propertyName] = text - } else { - mainItem.errorMessage = qsTr("Format non reconnu") - } - } else + if (!canBeEmpty && mainItem.empty) { + mainItem.errorMessage = qsTr("ne peut être vide") + return + } + if (isValid(text)) { mainItem.errorMessage = "" - } - onTextChanged: { - idleTimer.restart() + if (mainItem.propertyOwner[mainItem.propertyName] != text) + mainItem.propertyOwner[mainItem.propertyName] = text + } else { + mainItem.errorMessage = qsTr("Format non reconnu") + } } } } diff --git a/Linphone/view/Page/Main/SettingsPage.qml b/Linphone/view/Page/Main/SettingsPage.qml index 55c29a21f..379fd196a 100644 --- a/Linphone/view/Page/Main/SettingsPage.qml +++ b/Linphone/view/Page/Main/SettingsPage.qml @@ -11,7 +11,7 @@ AbstractMasterDetailPage { {title: qsTr("Appels"), layout: "CallSettingsLayout"}, //{title: qsTr("Sécurité"), layout: "SecuritySettingsLayout"}, {title: qsTr("Conversations"), layout: "ChatSettingsLayout", visible: !SettingsCpp.disableChatFeature}, - {title: qsTr("Contacts"), layout: "ContactSettingsLayout"}, + {title: qsTr("Contacts"), layout: "ContactsSettingsLayout"}, {title: qsTr("Réunions"), layout: "MeetingsSettingsLayout", visible: !SettingsCpp.disableMeetingsFeature}, {title: qsTr("Affichage"), layout: "DisplaySettingsLayout"}, {title: qsTr("Réseau"), layout: "NetworkSettingsLayout"}, diff --git a/Linphone/view/Style/Typography.qml b/Linphone/view/Style/Typography.qml index 0761a98ec..4d77f4132 100644 --- a/Linphone/view/Style/Typography.qml +++ b/Linphone/view/Style/Typography.qml @@ -52,14 +52,14 @@ QtObject { weight: 400 * DefaultStyle.dp }) - // Text/P1 - Paratraph text + // Text/P1 - Paratraph text property font p1s: Qt.font( { family: DefaultStyle.defaultFont, pixelSize: 13 * DefaultStyle.dp, weight: 400 * DefaultStyle.dp }) - // Bouton/B2 - Medium Bouton + // Button/B2 - Medium Button property font b2: Qt.font( { family: DefaultStyle.defaultFont, pixelSize: 15 * DefaultStyle.dp,