more work on mqtt, not entirely finished
This commit is contained in:
parent
f3d944e890
commit
9101e0d1bb
@ -1,32 +0,0 @@
|
|||||||
#include "mqttbrokerconfiguration.h"
|
|
||||||
|
|
||||||
MqttBrokerConfiguration::MqttBrokerConfiguration(QObject *parent) : QObject(parent)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
QString MqttBrokerConfiguration::username() const
|
|
||||||
{
|
|
||||||
return m_username;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MqttBrokerConfiguration::setUsername(const QString &username)
|
|
||||||
{
|
|
||||||
if (m_username != username) {
|
|
||||||
m_username = username;
|
|
||||||
emit usernameChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString MqttBrokerConfiguration::password() const
|
|
||||||
{
|
|
||||||
return m_password;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MqttBrokerConfiguration::setPassword(const QString &password)
|
|
||||||
{
|
|
||||||
if (m_password != password) {
|
|
||||||
m_password = password;
|
|
||||||
emit passwordChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
#ifndef MQTTBROKERCONFIGURATION_H
|
|
||||||
#define MQTTBROKERCONFIGURATION_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
class MqttBrokerConfiguration : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
Q_PROPERTY(QString username READ username WRITE setUsername NOTIFY usernameChanged)
|
|
||||||
Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged)
|
|
||||||
public:
|
|
||||||
explicit MqttBrokerConfiguration(QObject *parent = nullptr);
|
|
||||||
|
|
||||||
QString username() const;
|
|
||||||
void setUsername(const QString &username);
|
|
||||||
|
|
||||||
QString password() const;
|
|
||||||
void setPassword(const QString &password);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void usernameChanged();
|
|
||||||
void passwordChanged();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString m_username;
|
|
||||||
QString m_password;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // MQTTBROKERCONFIGURATION_H
|
|
||||||
88
libnymea-app-core/configuration/mqttpolicies.cpp
Normal file
88
libnymea-app-core/configuration/mqttpolicies.cpp
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#include "mqttpolicies.h"
|
||||||
|
#include "mqttpolicy.h"
|
||||||
|
|
||||||
|
MqttPolicies::MqttPolicies(QObject *parent) : QAbstractListModel(parent)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int MqttPolicies::rowCount(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(index)
|
||||||
|
return m_list.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant MqttPolicies::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
switch (role) {
|
||||||
|
case RoleClientId:
|
||||||
|
return m_list.at(index.row())->clientId();
|
||||||
|
case RoleUsername:
|
||||||
|
return m_list.at(index.row())->username();
|
||||||
|
case RolePassword:
|
||||||
|
return m_list.at(index.row())->password();
|
||||||
|
case RoleAllowedPublishTopicFilters:
|
||||||
|
return m_list.at(index.row())->allowedPublishTopicFilters();
|
||||||
|
case RoleAllowedSubscribeTopicFilters:
|
||||||
|
return m_list.at(index.row())->allowedSubscribeTopicFilters();
|
||||||
|
}
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> MqttPolicies::roleNames() const
|
||||||
|
{
|
||||||
|
QHash<int, QByteArray> roles;
|
||||||
|
roles.insert(RoleClientId, "clientId");
|
||||||
|
roles.insert(RoleUsername, "username");
|
||||||
|
roles.insert(RolePassword, "password");
|
||||||
|
roles.insert(RoleAllowedPublishTopicFilters, "allowedPublishTopicFilters");
|
||||||
|
roles.insert(RoleAllowedSubscribeTopicFilters, "allowedSubscribeTopicFilters");
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttPolicies::addPolicy(MqttPolicy *policy)
|
||||||
|
{
|
||||||
|
policy->setParent(this);
|
||||||
|
beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
|
||||||
|
m_list.append(policy);
|
||||||
|
|
||||||
|
connect(policy, &MqttPolicy::clientIdChanged, this, [this, policy]() {
|
||||||
|
QModelIndex index = this->index(m_list.indexOf(policy));
|
||||||
|
emit dataChanged(index, index, {RoleClientId});
|
||||||
|
});
|
||||||
|
connect(policy, &MqttPolicy::usernameChanged, this, [this, policy]() {
|
||||||
|
QModelIndex index = this->index(m_list.indexOf(policy));
|
||||||
|
emit dataChanged(index, index, {RoleUsername});
|
||||||
|
});
|
||||||
|
connect(policy, &MqttPolicy::passwordChanged, this, [this, policy]() {
|
||||||
|
QModelIndex index = this->index(m_list.indexOf(policy));
|
||||||
|
emit dataChanged(index, index, {RolePassword});
|
||||||
|
});
|
||||||
|
connect(policy, &MqttPolicy::allowedPublishTopicFiltersChanged, this, [this, policy]() {
|
||||||
|
QModelIndex index = this->index(m_list.indexOf(policy));
|
||||||
|
emit dataChanged(index, index, {RoleAllowedPublishTopicFilters});
|
||||||
|
});
|
||||||
|
connect(policy, &MqttPolicy::allowedSubscribeTopicFiltersChanged, this, [this, policy]() {
|
||||||
|
QModelIndex index = this->index(m_list.indexOf(policy));
|
||||||
|
emit dataChanged(index, index, {RoleAllowedSubscribeTopicFilters});
|
||||||
|
});
|
||||||
|
|
||||||
|
endInsertRows();
|
||||||
|
emit countChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
MqttPolicy *MqttPolicies::get(int index) const
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= m_list.count()){
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return m_list.at(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttPolicies::clear()
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
qDeleteAll(m_list);
|
||||||
|
m_list.clear();
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
41
libnymea-app-core/configuration/mqttpolicies.h
Normal file
41
libnymea-app-core/configuration/mqttpolicies.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#ifndef MQTTPOLICIES_H
|
||||||
|
#define MQTTPOLICIES_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
|
||||||
|
class MqttPolicy;
|
||||||
|
|
||||||
|
class MqttPolicies : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
|
||||||
|
public:
|
||||||
|
enum Roles {
|
||||||
|
RoleClientId,
|
||||||
|
RoleUsername,
|
||||||
|
RolePassword,
|
||||||
|
RoleAllowedPublishTopicFilters,
|
||||||
|
RoleAllowedSubscribeTopicFilters
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit MqttPolicies(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex &index = QModelIndex()) const override;
|
||||||
|
QVariant data(const QModelIndex &index, int role) const override;
|
||||||
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
void addPolicy(MqttPolicy *policy);
|
||||||
|
void removePolicy(MqttPolicy *policy);
|
||||||
|
|
||||||
|
Q_INVOKABLE MqttPolicy* get(int index) const;
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
signals:
|
||||||
|
void countChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QList<MqttPolicy*> m_list;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MQTTPOLICIES_H
|
||||||
77
libnymea-app-core/configuration/mqttpolicy.cpp
Normal file
77
libnymea-app-core/configuration/mqttpolicy.cpp
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#include "mqttpolicy.h"
|
||||||
|
|
||||||
|
MqttPolicy::MqttPolicy(const QString &clientId, const QString &username, const QString &password, const QStringList &allowedPublishTopicFilters, const QStringList &allowedSubscribeTopicFilters, QObject *parent):
|
||||||
|
QObject(parent),
|
||||||
|
m_clientId(clientId),
|
||||||
|
m_username(username),
|
||||||
|
m_password(password),
|
||||||
|
m_allowedSubscribeTopicFilters(allowedSubscribeTopicFilters),
|
||||||
|
m_allowedPublishTopicFilters(allowedPublishTopicFilters)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MqttPolicy::clientId() const
|
||||||
|
{
|
||||||
|
return m_clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttPolicy::setClientId(const QString &clientId)
|
||||||
|
{
|
||||||
|
if (m_clientId != clientId) {
|
||||||
|
m_clientId = clientId;
|
||||||
|
emit clientIdChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MqttPolicy::username() const
|
||||||
|
{
|
||||||
|
return m_username;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttPolicy::setUsername(const QString &username)
|
||||||
|
{
|
||||||
|
if (m_username != username) {
|
||||||
|
m_username = username;
|
||||||
|
emit usernameChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MqttPolicy::password() const
|
||||||
|
{
|
||||||
|
return m_password;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttPolicy::setPassword(const QString &password)
|
||||||
|
{
|
||||||
|
if (m_password != password) {
|
||||||
|
m_password = password;
|
||||||
|
emit passwordChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList MqttPolicy::allowedPublishTopicFilters() const
|
||||||
|
{
|
||||||
|
return m_allowedPublishTopicFilters;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttPolicy::setAllowedPublishTopicFilters(const QStringList &allowedPublishTopicFilters)
|
||||||
|
{
|
||||||
|
if (m_allowedPublishTopicFilters != allowedPublishTopicFilters) {
|
||||||
|
m_allowedPublishTopicFilters = allowedPublishTopicFilters;
|
||||||
|
emit allowedPublishTopicFiltersChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList MqttPolicy::allowedSubscribeTopicFilters() const
|
||||||
|
{
|
||||||
|
return m_allowedSubscribeTopicFilters;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttPolicy::setAllowedSubscribeTopicFilters(const QStringList &allowedSubscribeTopicFilters)
|
||||||
|
{
|
||||||
|
if (m_allowedSubscribeTopicFilters != allowedSubscribeTopicFilters) {
|
||||||
|
m_allowedSubscribeTopicFilters = allowedSubscribeTopicFilters;
|
||||||
|
emit allowedSubscribeTopicFiltersChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
53
libnymea-app-core/configuration/mqttpolicy.h
Normal file
53
libnymea-app-core/configuration/mqttpolicy.h
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#ifndef MQTTPOLICY_H
|
||||||
|
#define MQTTPOLICY_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class MqttPolicy : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(QString clientId READ clientId WRITE setClientId NOTIFY clientIdChanged)
|
||||||
|
Q_PROPERTY(QString username READ username WRITE setUsername NOTIFY usernameChanged)
|
||||||
|
Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged)
|
||||||
|
Q_PROPERTY(QStringList allowedPublishTopicFilters READ allowedPublishTopicFilters WRITE setAllowedPublishTopicFilters NOTIFY allowedPublishTopicFiltersChanged)
|
||||||
|
Q_PROPERTY(QStringList allowedSubscribeTopicFilters READ allowedSubscribeTopicFilters WRITE setAllowedSubscribeTopicFilters NOTIFY allowedSubscribeTopicFiltersChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MqttPolicy(const QString &clientId = QString(),
|
||||||
|
const QString &username = QString(),
|
||||||
|
const QString &password = QString(),
|
||||||
|
const QStringList &allowedPublishTopicFilters = QStringList(),
|
||||||
|
const QStringList &allowedSubscribeTopicFilters = QStringList(),
|
||||||
|
QObject *parent = nullptr);
|
||||||
|
|
||||||
|
QString clientId() const;
|
||||||
|
void setClientId(const QString &clientId);
|
||||||
|
|
||||||
|
QString username() const;
|
||||||
|
void setUsername(const QString &username);
|
||||||
|
|
||||||
|
QString password() const;
|
||||||
|
void setPassword(const QString &password);
|
||||||
|
|
||||||
|
QStringList allowedPublishTopicFilters() const;
|
||||||
|
void setAllowedPublishTopicFilters(const QStringList &allowedPublishTopicFilters);
|
||||||
|
|
||||||
|
QStringList allowedSubscribeTopicFilters() const;
|
||||||
|
void setAllowedSubscribeTopicFilters(const QStringList &allowedSubscribeTopicFilters);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void clientIdChanged();
|
||||||
|
void usernameChanged();
|
||||||
|
void passwordChanged();
|
||||||
|
void allowedPublishTopicFiltersChanged();
|
||||||
|
void allowedSubscribeTopicFiltersChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_clientId;
|
||||||
|
QString m_username;
|
||||||
|
QString m_password;
|
||||||
|
QStringList m_allowedPublishTopicFilters;
|
||||||
|
QStringList m_allowedSubscribeTopicFilters;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MQTTPOLICY_H
|
||||||
@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
#include "serverconfiguration.h"
|
#include "serverconfiguration.h"
|
||||||
#include "serverconfigurations.h"
|
#include "serverconfigurations.h"
|
||||||
#include "mqttbrokerconfiguration.h"
|
#include "mqttpolicy.h"
|
||||||
|
#include "mqttpolicies.h"
|
||||||
|
|
||||||
#include "jsonrpc/jsonrpcclient.h"
|
#include "jsonrpc/jsonrpcclient.h"
|
||||||
|
|
||||||
@ -14,7 +15,7 @@ NymeaConfiguration::NymeaConfiguration(JsonRpcClient *client, QObject *parent):
|
|||||||
m_tcpServerConfigurations(new ServerConfigurations(this)),
|
m_tcpServerConfigurations(new ServerConfigurations(this)),
|
||||||
m_webSocketServerConfigurations(new ServerConfigurations(this)),
|
m_webSocketServerConfigurations(new ServerConfigurations(this)),
|
||||||
m_mqttServerConfigurations(new ServerConfigurations(this)),
|
m_mqttServerConfigurations(new ServerConfigurations(this)),
|
||||||
m_mqttBrokerConfiguration(new MqttBrokerConfiguration(this))
|
m_mqttPolicies(new MqttPolicies(this))
|
||||||
{
|
{
|
||||||
client->registerNotificationHandler(this, "notificationReceived");
|
client->registerNotificationHandler(this, "notificationReceived");
|
||||||
}
|
}
|
||||||
@ -32,6 +33,8 @@ void NymeaConfiguration::init()
|
|||||||
m_client->sendCommand("Configuration.GetConfigurations", this, "getConfigurationsResponse");
|
m_client->sendCommand("Configuration.GetConfigurations", this, "getConfigurationsResponse");
|
||||||
m_client->sendCommand("Configuration.GetAvailableLanguages", this, "getAvailableLanguagesResponse");
|
m_client->sendCommand("Configuration.GetAvailableLanguages", this, "getAvailableLanguagesResponse");
|
||||||
m_client->sendCommand("Configuration.GetTimeZones", this, "getTimezonesResponse");
|
m_client->sendCommand("Configuration.GetTimeZones", this, "getTimezonesResponse");
|
||||||
|
m_client->sendCommand("Configuration.GetMqttServerConfigurations", this, "getMqttServerConfigsReply");
|
||||||
|
m_client->sendCommand("Configuration.GetMqttPolicies", this, "getMqttPoliciesReply");
|
||||||
}
|
}
|
||||||
|
|
||||||
QString NymeaConfiguration::serverName() const
|
QString NymeaConfiguration::serverName() const
|
||||||
@ -119,9 +122,9 @@ ServerConfigurations *NymeaConfiguration::mqttServerConfigurations() const
|
|||||||
return m_mqttServerConfigurations;
|
return m_mqttServerConfigurations;
|
||||||
}
|
}
|
||||||
|
|
||||||
MqttBrokerConfiguration *NymeaConfiguration::mqttBrokerConfiguration() const
|
MqttPolicies *NymeaConfiguration::mqttPolicies() const
|
||||||
{
|
{
|
||||||
return m_mqttBrokerConfiguration;
|
return m_mqttPolicies;
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerConfiguration *NymeaConfiguration::createServerConfiguration(const QString &address, int port, bool authEnabled, bool sslEnabled)
|
ServerConfiguration *NymeaConfiguration::createServerConfiguration(const QString &address, int port, bool authEnabled, bool sslEnabled)
|
||||||
@ -129,6 +132,11 @@ ServerConfiguration *NymeaConfiguration::createServerConfiguration(const QString
|
|||||||
return new ServerConfiguration(QUuid::createUuid().toString(), QHostAddress(address), port, authEnabled, sslEnabled);
|
return new ServerConfiguration(QUuid::createUuid().toString(), QHostAddress(address), port, authEnabled, sslEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MqttPolicy *NymeaConfiguration::createMqttPolicy() const
|
||||||
|
{
|
||||||
|
return new MqttPolicy();
|
||||||
|
}
|
||||||
|
|
||||||
void NymeaConfiguration::setTcpServerConfiguration(ServerConfiguration *configuration)
|
void NymeaConfiguration::setTcpServerConfiguration(ServerConfiguration *configuration)
|
||||||
{
|
{
|
||||||
QVariantMap params;
|
QVariantMap params;
|
||||||
@ -189,6 +197,26 @@ void NymeaConfiguration::deleteMqttServerConfiguration(const QString &id)
|
|||||||
m_client->sendCommand("Configuration.DeleteMqttServerConfiguration", params, this, "deleteMqttConfigReply");
|
m_client->sendCommand("Configuration.DeleteMqttServerConfiguration", params, this, "deleteMqttConfigReply");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NymeaConfiguration::updateMqttPolicy(MqttPolicy *policy)
|
||||||
|
{
|
||||||
|
QVariantMap params;
|
||||||
|
QVariantMap policyMap;
|
||||||
|
policyMap.insert("clientId", policy->clientId());
|
||||||
|
policyMap.insert("username", policy->username());
|
||||||
|
policyMap.insert("password", policy->password());
|
||||||
|
policyMap.insert("allowedPublishTopicFilters", policy->allowedPublishTopicFilters());
|
||||||
|
policyMap.insert("allowedSubscribeTopicFilters", policy->allowedSubscribeTopicFilters());
|
||||||
|
params.insert("policy", policyMap);
|
||||||
|
m_client->sendCommand("Configuration.SetMqttPolicy", params, this, "setMqttPolicyReply");
|
||||||
|
}
|
||||||
|
|
||||||
|
void NymeaConfiguration::deleteMqttPolicy(const QString &clientId)
|
||||||
|
{
|
||||||
|
QVariantMap params;
|
||||||
|
params.insert("clientId", clientId);
|
||||||
|
m_client->sendCommand("Configuration.RemoveMqttPolicy", params);
|
||||||
|
}
|
||||||
|
|
||||||
void NymeaConfiguration::getConfigurationsResponse(const QVariantMap ¶ms)
|
void NymeaConfiguration::getConfigurationsResponse(const QVariantMap ¶ms)
|
||||||
{
|
{
|
||||||
qDebug() << "have config reply" << params;
|
qDebug() << "have config reply" << params;
|
||||||
@ -218,14 +246,6 @@ void NymeaConfiguration::getConfigurationsResponse(const QVariantMap ¶ms)
|
|||||||
ServerConfiguration *config = new ServerConfiguration(websocketConfigMap.value("id").toString(), QHostAddress(websocketConfigMap.value("address").toString()), websocketConfigMap.value("port").toInt(), websocketConfigMap.value("authenticationEnabled").toBool(), websocketConfigMap.value("sslEnabled").toBool());
|
ServerConfiguration *config = new ServerConfiguration(websocketConfigMap.value("id").toString(), QHostAddress(websocketConfigMap.value("address").toString()), websocketConfigMap.value("port").toInt(), websocketConfigMap.value("authenticationEnabled").toBool(), websocketConfigMap.value("sslEnabled").toBool());
|
||||||
m_webSocketServerConfigurations->addConfiguration(config);
|
m_webSocketServerConfigurations->addConfiguration(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_client->ensureServerVersion("1.11")) {
|
|
||||||
foreach (const QVariant &mqttServerVariant, params.value("params").toMap().value("mqttServerConfigurations").toList()) {
|
|
||||||
QVariantMap mqttConfigMap = mqttServerVariant.toMap();
|
|
||||||
ServerConfiguration *config = new ServerConfiguration(mqttConfigMap.value("id").toString(), QHostAddress(mqttConfigMap.value("address").toString()), mqttConfigMap.value("port").toInt(), mqttConfigMap.value("authenticationEnabled").toBool(), mqttConfigMap.value("sslEnabled").toBool());
|
|
||||||
m_mqttServerConfigurations->addConfiguration(config);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NymeaConfiguration::getAvailableLanguagesResponse(const QVariantMap ¶ms)
|
void NymeaConfiguration::getAvailableLanguagesResponse(const QVariantMap ¶ms)
|
||||||
@ -286,6 +306,16 @@ void NymeaConfiguration::deleteWebSocketConfigReply(const QVariantMap ¶ms)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NymeaConfiguration::getMqttServerConfigsReply(const QVariantMap ¶ms)
|
||||||
|
{
|
||||||
|
m_mqttServerConfigurations->clear();
|
||||||
|
foreach (const QVariant &mqttServerVariant, params.value("params").toMap().value("mqttServerConfigurations").toList()) {
|
||||||
|
QVariantMap mqttConfigMap = mqttServerVariant.toMap();
|
||||||
|
ServerConfiguration *config = new ServerConfiguration(mqttConfigMap.value("id").toString(), QHostAddress(mqttConfigMap.value("address").toString()), mqttConfigMap.value("port").toInt(), mqttConfigMap.value("authenticationEnabled").toBool(), mqttConfigMap.value("sslEnabled").toBool());
|
||||||
|
m_mqttServerConfigurations->addConfiguration(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void NymeaConfiguration::setMqttConfigReply(const QVariantMap ¶ms)
|
void NymeaConfiguration::setMqttConfigReply(const QVariantMap ¶ms)
|
||||||
{
|
{
|
||||||
qDebug() << "Set mqtt config reply" << params;
|
qDebug() << "Set mqtt config reply" << params;
|
||||||
@ -296,6 +326,27 @@ void NymeaConfiguration::deleteMqttConfigReply(const QVariantMap ¶ms)
|
|||||||
qDebug() << "Delete Mqtt Broker config reply:" << params;
|
qDebug() << "Delete Mqtt Broker config reply:" << params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NymeaConfiguration::getMqttPoliciesReply(const QVariantMap ¶ms)
|
||||||
|
{
|
||||||
|
qDebug() << "Mqtt polices:" << params;
|
||||||
|
m_mqttPolicies->clear();
|
||||||
|
foreach (const QVariant &policyVariant, params.value("params").toMap().value("mqttPolicies").toList()) {
|
||||||
|
QVariantMap policyMap = policyVariant.toMap();
|
||||||
|
MqttPolicy *policy = new MqttPolicy(
|
||||||
|
policyMap.value("clientId").toString(),
|
||||||
|
policyMap.value("username").toString(),
|
||||||
|
policyMap.value("password").toString(),
|
||||||
|
policyMap.value("allowedPublishTopicFilters").toStringList(),
|
||||||
|
policyMap.value("allowedSubscribeTopicFilters").toStringList());
|
||||||
|
m_mqttPolicies->addPolicy(policy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NymeaConfiguration::setMqttPolicyReply(const QVariantMap ¶ms)
|
||||||
|
{
|
||||||
|
qDebug() << "Set MQTT policy reply" << params;
|
||||||
|
}
|
||||||
|
|
||||||
void NymeaConfiguration::notificationReceived(const QVariantMap ¬ification)
|
void NymeaConfiguration::notificationReceived(const QVariantMap ¬ification)
|
||||||
{
|
{
|
||||||
QString notif = notification.value("notification").toString();
|
QString notif = notification.value("notification").toString();
|
||||||
@ -368,6 +419,25 @@ void NymeaConfiguration::notificationReceived(const QVariantMap ¬ification)
|
|||||||
m_mqttServerConfigurations->removeConfiguration(notification.value("params").toMap().value("id").toString());
|
m_mqttServerConfigurations->removeConfiguration(notification.value("params").toMap().value("id").toString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (notif == "Configuration.MqttPolicyChanged") {
|
||||||
|
MqttPolicy *policy = nullptr;
|
||||||
|
QVariantMap policyMap = notification.value("params").toMap();
|
||||||
|
for (int i = 0; i < m_mqttPolicies->rowCount(); i++) {
|
||||||
|
if (m_mqttPolicies->get(i)->clientId() == policyMap.value("clientId").toString()) {
|
||||||
|
policy = m_mqttPolicies->get(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!policy) {
|
||||||
|
policy = new MqttPolicy(policyMap.value("clientId").toString());
|
||||||
|
m_mqttPolicies->addPolicy(policy);
|
||||||
|
}
|
||||||
|
policy->setUsername(policyMap.value("username").toString());
|
||||||
|
policy->setPassword(policyMap.value("password").toString());
|
||||||
|
policy->setAllowedPublishTopicFilters(policyMap.value("allowedPublishTopicFilters").toStringList());
|
||||||
|
policy->setAllowedSubscribeTopicFilters(policyMap.value("allowedSubscribeTopicFilters").toStringList());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
qDebug() << "Unhandled Configuration notification" << notif << notification;
|
qDebug() << "Unhandled Configuration notification" << notif << notification;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,8 @@
|
|||||||
class JsonRpcClient;
|
class JsonRpcClient;
|
||||||
class ServerConfiguration;
|
class ServerConfiguration;
|
||||||
class ServerConfigurations;
|
class ServerConfigurations;
|
||||||
class MqttBrokerConfiguration;
|
class MqttPolicy;
|
||||||
|
class MqttPolicies;
|
||||||
|
|
||||||
class NymeaConfiguration : public JsonHandler
|
class NymeaConfiguration : public JsonHandler
|
||||||
{
|
{
|
||||||
@ -29,7 +30,7 @@ class NymeaConfiguration : public JsonHandler
|
|||||||
Q_PROPERTY(ServerConfigurations* webSocketServerConfigurations READ webSocketServerConfigurations CONSTANT)
|
Q_PROPERTY(ServerConfigurations* webSocketServerConfigurations READ webSocketServerConfigurations CONSTANT)
|
||||||
Q_PROPERTY(ServerConfigurations* mqttServerConfigurations READ mqttServerConfigurations CONSTANT)
|
Q_PROPERTY(ServerConfigurations* mqttServerConfigurations READ mqttServerConfigurations CONSTANT)
|
||||||
|
|
||||||
Q_PROPERTY(MqttBrokerConfiguration* mqttBrokerConfiguration READ mqttBrokerConfiguration CONSTANT)
|
Q_PROPERTY(MqttPolicies* mqttPolicies READ mqttPolicies CONSTANT)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit NymeaConfiguration(JsonRpcClient* client, QObject *parent = nullptr);
|
explicit NymeaConfiguration(JsonRpcClient* client, QObject *parent = nullptr);
|
||||||
@ -56,10 +57,10 @@ public:
|
|||||||
ServerConfigurations *tcpServerConfigurations() const;
|
ServerConfigurations *tcpServerConfigurations() const;
|
||||||
ServerConfigurations *webSocketServerConfigurations() const;
|
ServerConfigurations *webSocketServerConfigurations() const;
|
||||||
ServerConfigurations *mqttServerConfigurations() const;
|
ServerConfigurations *mqttServerConfigurations() const;
|
||||||
|
MqttPolicies *mqttPolicies() const;
|
||||||
MqttBrokerConfiguration *mqttBrokerConfiguration() const;
|
|
||||||
|
|
||||||
Q_INVOKABLE ServerConfiguration* createServerConfiguration(const QString &address = "0.0.0.0", int port = 0, bool authEnabled = false, bool sslEnabled = false);
|
Q_INVOKABLE ServerConfiguration* createServerConfiguration(const QString &address = "0.0.0.0", int port = 0, bool authEnabled = false, bool sslEnabled = false);
|
||||||
|
Q_INVOKABLE MqttPolicy* createMqttPolicy() const;
|
||||||
|
|
||||||
Q_INVOKABLE void setTcpServerConfiguration(ServerConfiguration *configuration);
|
Q_INVOKABLE void setTcpServerConfiguration(ServerConfiguration *configuration);
|
||||||
Q_INVOKABLE void setWebSocketServerConfiguration(ServerConfiguration *configuration);
|
Q_INVOKABLE void setWebSocketServerConfiguration(ServerConfiguration *configuration);
|
||||||
@ -69,6 +70,8 @@ public:
|
|||||||
Q_INVOKABLE void deleteWebSocketServerConfiguration(const QString &id);
|
Q_INVOKABLE void deleteWebSocketServerConfiguration(const QString &id);
|
||||||
Q_INVOKABLE void deleteMqttServerConfiguration(const QString &id);
|
Q_INVOKABLE void deleteMqttServerConfiguration(const QString &id);
|
||||||
|
|
||||||
|
Q_INVOKABLE void updateMqttPolicy(MqttPolicy* policy);
|
||||||
|
Q_INVOKABLE void deleteMqttPolicy(const QString &clientId);
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -84,8 +87,11 @@ private:
|
|||||||
Q_INVOKABLE void deleteTcpConfigReply(const QVariantMap ¶ms);
|
Q_INVOKABLE void deleteTcpConfigReply(const QVariantMap ¶ms);
|
||||||
Q_INVOKABLE void setWebSocketConfigReply(const QVariantMap ¶ms);
|
Q_INVOKABLE void setWebSocketConfigReply(const QVariantMap ¶ms);
|
||||||
Q_INVOKABLE void deleteWebSocketConfigReply(const QVariantMap ¶ms);
|
Q_INVOKABLE void deleteWebSocketConfigReply(const QVariantMap ¶ms);
|
||||||
|
Q_INVOKABLE void getMqttServerConfigsReply(const QVariantMap ¶ms);
|
||||||
Q_INVOKABLE void setMqttConfigReply(const QVariantMap ¶ms);
|
Q_INVOKABLE void setMqttConfigReply(const QVariantMap ¶ms);
|
||||||
Q_INVOKABLE void deleteMqttConfigReply(const QVariantMap ¶ms);
|
Q_INVOKABLE void deleteMqttConfigReply(const QVariantMap ¶ms);
|
||||||
|
Q_INVOKABLE void getMqttPoliciesReply(const QVariantMap ¶ms);
|
||||||
|
Q_INVOKABLE void setMqttPolicyReply(const QVariantMap ¶ms);
|
||||||
|
|
||||||
Q_INVOKABLE void notificationReceived(const QVariantMap ¬ification);
|
Q_INVOKABLE void notificationReceived(const QVariantMap ¬ification);
|
||||||
|
|
||||||
@ -112,8 +118,8 @@ private:
|
|||||||
ServerConfigurations *m_tcpServerConfigurations = nullptr;
|
ServerConfigurations *m_tcpServerConfigurations = nullptr;
|
||||||
ServerConfigurations *m_webSocketServerConfigurations = nullptr;
|
ServerConfigurations *m_webSocketServerConfigurations = nullptr;
|
||||||
ServerConfigurations *m_mqttServerConfigurations = nullptr;
|
ServerConfigurations *m_mqttServerConfigurations = nullptr;
|
||||||
|
MqttPolicies *m_mqttPolicies = nullptr;
|
||||||
|
|
||||||
MqttBrokerConfiguration *m_mqttBrokerConfiguration = nullptr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // NYMEACONFIGURATION_H
|
#endif // NYMEACONFIGURATION_H
|
||||||
|
|||||||
@ -32,7 +32,6 @@ class RuleManager;
|
|||||||
class LogManager;
|
class LogManager;
|
||||||
class TagsManager;
|
class TagsManager;
|
||||||
class NymeaConfiguration;
|
class NymeaConfiguration;
|
||||||
class AWSClient;
|
|
||||||
|
|
||||||
class Engine : public QObject
|
class Engine : public QObject
|
||||||
{
|
{
|
||||||
|
|||||||
@ -38,7 +38,8 @@
|
|||||||
#include "configuration/nymeaconfiguration.h"
|
#include "configuration/nymeaconfiguration.h"
|
||||||
#include "configuration/serverconfiguration.h"
|
#include "configuration/serverconfiguration.h"
|
||||||
#include "configuration/serverconfigurations.h"
|
#include "configuration/serverconfigurations.h"
|
||||||
#include "configuration/mqttbrokerconfiguration.h"
|
#include "configuration/mqttpolicy.h"
|
||||||
|
#include "configuration/mqttpolicies.h"
|
||||||
#include "wifisetup/networkmanagercontroller.h"
|
#include "wifisetup/networkmanagercontroller.h"
|
||||||
#include "wifisetup/wirelessaccesspoint.h"
|
#include "wifisetup/wirelessaccesspoint.h"
|
||||||
#include "wifisetup/wirelessaccesspoints.h"
|
#include "wifisetup/wirelessaccesspoints.h"
|
||||||
@ -149,7 +150,8 @@ void registerQmlTypes() {
|
|||||||
qmlRegisterUncreatableType<NymeaConfiguration>(uri, 1, 0, "NymeaConfiguration", "Get it from Engine");
|
qmlRegisterUncreatableType<NymeaConfiguration>(uri, 1, 0, "NymeaConfiguration", "Get it from Engine");
|
||||||
qmlRegisterUncreatableType<ServerConfiguration>(uri, 1, 0, "ServerConfiguration", "Get it from NymeaConfiguration");
|
qmlRegisterUncreatableType<ServerConfiguration>(uri, 1, 0, "ServerConfiguration", "Get it from NymeaConfiguration");
|
||||||
qmlRegisterUncreatableType<ServerConfigurations>(uri, 1, 0, "ServerConfigurations", "Get it from NymeaConfiguration");
|
qmlRegisterUncreatableType<ServerConfigurations>(uri, 1, 0, "ServerConfigurations", "Get it from NymeaConfiguration");
|
||||||
qmlRegisterUncreatableType<MqttBrokerConfiguration>(uri, 1, 0, "MqttBrokerConfiguration", "Get it from NymeaConfiguration");
|
qmlRegisterUncreatableType<MqttPolicy>(uri, 1, 0, "MqttPolicy", "Get it from NymeaConfiguration");
|
||||||
|
qmlRegisterUncreatableType<MqttPolicies>(uri, 1, 0, "MqttPolicies", "Get it from NymeaConfiguration");
|
||||||
|
|
||||||
qmlRegisterType<NymeaDiscovery>(uri, 1, 0, "NymeaDiscovery");
|
qmlRegisterType<NymeaDiscovery>(uri, 1, 0, "NymeaDiscovery");
|
||||||
qmlRegisterUncreatableType<DiscoveryModel>(uri, 1, 0, "DiscoveryModel", "Get it from NymeaDiscovery");
|
qmlRegisterUncreatableType<DiscoveryModel>(uri, 1, 0, "DiscoveryModel", "Get it from NymeaDiscovery");
|
||||||
|
|||||||
@ -80,8 +80,10 @@ SOURCES += \
|
|||||||
ruletemplates/ruleactionparamtemplate.cpp \
|
ruletemplates/ruleactionparamtemplate.cpp \
|
||||||
configuration/serverconfiguration.cpp \
|
configuration/serverconfiguration.cpp \
|
||||||
configuration/serverconfigurations.cpp \
|
configuration/serverconfigurations.cpp \
|
||||||
configuration/mqttbrokerconfiguration.cpp \
|
configuration/nymeaconfiguration.cpp \
|
||||||
configuration/nymeaconfiguration.cpp
|
models/mqttpolicies.cpp \
|
||||||
|
configuration/mqttpolicy.cpp \
|
||||||
|
configuration/mqttpolicies.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
engine.h \
|
engine.h \
|
||||||
@ -141,8 +143,9 @@ HEADERS += \
|
|||||||
ruletemplates/ruleactionparamtemplate.h \
|
ruletemplates/ruleactionparamtemplate.h \
|
||||||
configuration/serverconfiguration.h \
|
configuration/serverconfiguration.h \
|
||||||
configuration/serverconfigurations.h \
|
configuration/serverconfigurations.h \
|
||||||
configuration/mqttbrokerconfiguration.h \
|
configuration/nymeaconfiguration.h \
|
||||||
configuration/nymeaconfiguration.h
|
configuration/mqttpolicy.h \
|
||||||
|
configuration/mqttpolicies.h
|
||||||
|
|
||||||
unix {
|
unix {
|
||||||
target.path = /usr/lib
|
target.path = /usr/lib
|
||||||
|
|||||||
6
libnymea-app-core/models/mqttpolicies.cpp
Normal file
6
libnymea-app-core/models/mqttpolicies.cpp
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#include "mqttpolicies.h"
|
||||||
|
|
||||||
|
MqttPolicies::MqttPolicies(QObject *parent) : QObject(parent)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@ -138,5 +138,6 @@
|
|||||||
<file>ui/devicelistpages/SmartMeterDeviceListPage.qml</file>
|
<file>ui/devicelistpages/SmartMeterDeviceListPage.qml</file>
|
||||||
<file>ui/system/MqttBrokerSettingsPage.qml</file>
|
<file>ui/system/MqttBrokerSettingsPage.qml</file>
|
||||||
<file>ui/system/ServerConfigurationDialog.qml</file>
|
<file>ui/system/ServerConfigurationDialog.qml</file>
|
||||||
|
<file>ui/system/MqttPolicyDialog.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
@ -11,17 +11,57 @@ Page {
|
|||||||
onBackPressed: pageStack.pop();
|
onBackPressed: pageStack.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
Flickable {
|
// Flickable {
|
||||||
anchors.fill: parent
|
// anchors.fill: parent
|
||||||
contentHeight: connectionsColumn.implicitHeight
|
// contentHeight: connectionsColumn.implicitHeight
|
||||||
interactive: contentHeight > height
|
// interactive: contentHeight > height
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: connectionsColumn
|
id: connectionsColumn
|
||||||
anchors { left: parent.left; top: parent.top; right: parent.right }
|
// anchors { left: parent.left; top: parent.top; right: parent.right }
|
||||||
|
anchors.fill: parent
|
||||||
|
// layoutDirection: Qt.
|
||||||
Label {
|
Label {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: qsTr("Server password protection")
|
Layout.margins: app.margins
|
||||||
|
text: qsTr("MQTT permissions")
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
color: app.accentColor
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: contentHeight
|
||||||
|
model: engine.nymeaConfiguration.mqttPolicies
|
||||||
|
delegate: MeaListItemDelegate {
|
||||||
|
width: parent.width
|
||||||
|
iconName: "../images/account.svg"
|
||||||
|
text: qsTr("Client ID: %1").arg(model.clientId)
|
||||||
|
subText: qsTr("Username: %1").arg(model.username)
|
||||||
|
progressive: false
|
||||||
|
canDelete: true
|
||||||
|
onDeleteClicked: {
|
||||||
|
engine.nymeaConfiguration.deleteMqttPolicy(model.clientId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: app.margins
|
||||||
|
text: qsTr("Add")
|
||||||
|
onClicked: {
|
||||||
|
var component = Qt.createComponent(Qt.resolvedUrl("MqttPolicyDialog.qml"));
|
||||||
|
var popup = component.createObject(root, { policy: engine.nymeaConfiguration.createMqttPolicy() });
|
||||||
|
popup.accepted.connect(function() {
|
||||||
|
engine.nymeaConfiguration.updateMqttPolicy(popup.policy)
|
||||||
|
popup.policy.destroy();
|
||||||
|
})
|
||||||
|
popup.rejected.connect(function() {
|
||||||
|
popup.policy.destroy();
|
||||||
|
})
|
||||||
|
popup.open()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
@ -34,10 +74,13 @@ Page {
|
|||||||
color: app.accentColor
|
color: app.accentColor
|
||||||
}
|
}
|
||||||
|
|
||||||
Repeater {
|
ListView {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumHeight: 0
|
||||||
|
Layout.preferredHeight: contentHeight
|
||||||
model: engine.nymeaConfiguration.mqttServerConfigurations
|
model: engine.nymeaConfiguration.mqttServerConfigurations
|
||||||
delegate: ConnectionInterfaceDelegate {
|
delegate: ConnectionInterfaceDelegate {
|
||||||
Layout.fillWidth: true
|
width: parent.width
|
||||||
canDelete: true
|
canDelete: true
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var component = Qt.createComponent(Qt.resolvedUrl("ServerConfigurationDialog.qml"));
|
var component = Qt.createComponent(Qt.resolvedUrl("ServerConfigurationDialog.qml"));
|
||||||
@ -76,6 +119,11 @@ Page {
|
|||||||
popup.open()
|
popup.open()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.minimumHeight: 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
57
nymea-app/ui/system/MqttPolicyDialog.qml
Normal file
57
nymea-app/ui/system/MqttPolicyDialog.qml
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.1
|
||||||
|
import QtQuick.Controls.Material 2.1
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
import Nymea 1.0
|
||||||
|
|
||||||
|
Dialog {
|
||||||
|
id: root
|
||||||
|
title: qsTr("Mqtt permission")
|
||||||
|
width: parent.width * .8
|
||||||
|
x: (parent.width - width) / 2
|
||||||
|
y: (parent.height - height) / 2
|
||||||
|
|
||||||
|
property MqttPolicy policy: null
|
||||||
|
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors { left: parent.left; top: parent.top; right: parent.right }
|
||||||
|
RowLayout {
|
||||||
|
Label {
|
||||||
|
text: qsTr("Client ID:")
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: clientIdTextField
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: root.policy ? root.policy.clientId : ""
|
||||||
|
onEditingFinished: root.policy.clientId = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RowLayout {
|
||||||
|
Label {
|
||||||
|
text: qsTr("Username:")
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: usernameTextField
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: root.policy ? root.policy.username : ""
|
||||||
|
onEditingFinished: root.policy.username = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Label {
|
||||||
|
text: qsTr("Password:")
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: root.policy ? root.policy.password : ""
|
||||||
|
onEditingFinished: root.policy.password = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user