UI: AddressesPage: Edit Zones.

This commit is contained in:
Nodir Temirkhodjaev 2020-02-13 14:11:25 +05:00
parent 31017040ac
commit b110b59442
13 changed files with 339 additions and 75 deletions

View File

@ -143,7 +143,7 @@ bool SqliteDb::prepare(SqliteStmt &stmt, const char *sql,
const QVariantList &vars) const QVariantList &vars)
{ {
return stmt.prepare(db(), sql) return stmt.prepare(db(), sql)
&& stmt.bindVars(vars); && (vars.isEmpty() || stmt.bindVars(vars));
} }
qint64 SqliteDb::lastInsertRowid() const qint64 SqliteDb::lastInsertRowid() const

View File

@ -48,6 +48,44 @@ void AddressGroup::setExcludeText(const QString &excludeText)
} }
} }
void AddressGroup::addIncludeZone(int zoneId, bool sorting)
{
addZone(m_includeZones, zoneId, sorting);
}
void AddressGroup::removeIncludeZone(int zoneId)
{
removeZone(m_includeZones, zoneId);
}
void AddressGroup::addExcludeZone(int zoneId, bool sorting)
{
addZone(m_excludeZones, zoneId, sorting);
}
void AddressGroup::removeExcludeZone(int zoneId)
{
removeZone(m_excludeZones, zoneId);
}
void AddressGroup::addZone(QVector<int> &zones, int zoneId, bool sorting)
{
zones.append(zoneId);
if (sorting) {
std::sort(zones.begin(), zones.end());
}
setEdited(true);
}
void AddressGroup::removeZone(QVector<int> &zones, int zoneId)
{
zones.removeOne(zoneId);
setEdited(true);
}
void AddressGroup::copy(const AddressGroup &o) void AddressGroup::copy(const AddressGroup &o)
{ {
m_edited = o.edited(); m_edited = o.edited();
@ -59,4 +97,7 @@ void AddressGroup::copy(const AddressGroup &o)
m_includeText = o.includeText(); m_includeText = o.includeText();
m_excludeText = o.excludeText(); m_excludeText = o.excludeText();
m_includeZones = o.includeZones();
m_excludeZones = o.excludeZones();
} }

View File

@ -3,6 +3,7 @@
#include <QObject> #include <QObject>
#include <QVariant> #include <QVariant>
#include <QVector>
class AddressGroup : public QObject class AddressGroup : public QObject
{ {
@ -33,6 +34,15 @@ public:
QString excludeText() const { return m_excludeText; } QString excludeText() const { return m_excludeText; }
void setExcludeText(const QString &excludeText); void setExcludeText(const QString &excludeText);
const QVector<int> &includeZones() const { return m_includeZones; }
const QVector<int> &excludeZones() const { return m_excludeZones; }
void addIncludeZone(int zoneId, bool sorting = false);
void removeIncludeZone(int zoneId);
void addExcludeZone(int zoneId, bool sorting = false);
void removeExcludeZone(int zoneId);
void copy(const AddressGroup &o); void copy(const AddressGroup &o);
signals: signals:
@ -41,7 +51,9 @@ signals:
void includeTextChanged(); void includeTextChanged();
void excludeTextChanged(); void excludeTextChanged();
public slots: private:
void addZone(QVector<int> &zones, int zoneId, bool sorting);
void removeZone(QVector<int> &zones, int zoneId);
private: private:
bool m_edited : 1; bool m_edited : 1;
@ -53,6 +65,9 @@ private:
QString m_includeText; QString m_includeText;
QString m_excludeText; QString m_excludeText;
QVector<int> m_includeZones;
QVector<int> m_excludeZones;
}; };
#endif // ADDRESSGROUP_H #endif // ADDRESSGROUP_H

View File

@ -41,6 +41,13 @@ const char * const sqlSelectAddressGroups =
" ORDER BY order_index;" " ORDER BY order_index;"
; ;
const char * const sqlSelectAddressGroupZones =
"SELECT include, zone_id"
" FROM address_group_zone"
" WHERE addr_group_id = ?1"
" ORDER BY include, zone_id;"
;
const char * const sqlSelectAppGroups = const char * const sqlSelectAppGroups =
"SELECT app_group_id, enabled," "SELECT app_group_id, enabled,"
" fragment_packet, period_enabled," " fragment_packet, period_enabled,"
@ -66,6 +73,16 @@ const char * const sqlUpdateAddressGroup =
" WHERE addr_group_id = ?1;" " WHERE addr_group_id = ?1;"
; ;
const char * const sqlDeleteAddressGroupZones =
"DELETE FROM address_group_zone"
" WHERE addr_group_id = ?1;"
;
const char * const sqlInsertAddressGroupZone =
"INSERT INTO address_group_zone(addr_group_id, zone_id, include)"
" VALUES(?1, ?2, ?3);"
;
const char * const sqlInsertAppGroup = const char * const sqlInsertAppGroup =
"INSERT INTO app_group(app_group_id, order_index, enabled," "INSERT INTO app_group(app_group_id, order_index, enabled,"
" fragment_packet, period_enabled," " fragment_packet, period_enabled,"
@ -236,6 +253,86 @@ bool migrateFunc(SqliteDb *db, int version, bool isNewDb, void *ctx)
return true; return true;
} }
bool loadAddressGroupZones(SqliteDb *db, AddressGroup *addrGroup)
{
SqliteStmt stmt;
if (!db->prepare(stmt, sqlSelectAddressGroupZones, {addrGroup->id()}))
return false;
while (stmt.step() == SqliteStmt::StepRow) {
const bool include = stmt.columnBool(0);
const int zoneId = stmt.columnInt(1);
if (include) {
addrGroup->addIncludeZone(zoneId);
} else {
addrGroup->addExcludeZone(zoneId);
}
}
return true;
}
bool saveAddressGroupZones(SqliteDb *db, AddressGroup *addrGroup)
{
const auto addrGroupId = addrGroup->id();
bool ok;
db->executeEx(sqlDeleteAddressGroupZones, {addrGroupId}, 0, &ok);
if (!ok) return false;
SqliteStmt stmt;
if (!db->prepare(stmt, sqlInsertAddressGroupZone))
return false;
const auto addAddressGroupZone = [&](int zoneId, bool include) -> bool {
return stmt.bindVars({addrGroupId, zoneId, include})
&& stmt.step() == SqliteStmt::StepDone
&& stmt.reset();
};
for (const int zoneId : addrGroup->includeZones()) {
if (!addAddressGroupZone(zoneId, true))
return false;
}
for (const int zoneId : addrGroup->excludeZones()) {
if (!addAddressGroupZone(zoneId, false))
return false;
}
return true;
}
bool loadAddressGroups(SqliteDb *db, const QList<AddressGroup *> &addressGroups,
int &index)
{
SqliteStmt stmt;
if (!db->prepare(stmt, sqlSelectAddressGroups))
return false;
index = 0;
while (stmt.step() == SqliteStmt::StepRow) {
auto addrGroup = addressGroups.at(index);
Q_ASSERT(addrGroup != nullptr);
addrGroup->setId(stmt.columnInt64(0));
addrGroup->setIncludeAll(stmt.columnBool(1));
addrGroup->setExcludeAll(stmt.columnBool(2));
addrGroup->setIncludeText(stmt.columnText(3));
addrGroup->setExcludeText(stmt.columnText(4));
if (!loadAddressGroupZones(db, addrGroup))
return false;
addrGroup->setEdited(false);
if (++index > 1)
break;
}
return true;
}
bool saveAddressGroup(SqliteDb *db, AddressGroup *addrGroup, int orderIndex) bool saveAddressGroup(SqliteDb *db, AddressGroup *addrGroup, int orderIndex)
{ {
const bool rowExists = (addrGroup->id() != 0); const bool rowExists = (addrGroup->id() != 0);
@ -260,11 +357,45 @@ bool saveAddressGroup(SqliteDb *db, AddressGroup *addrGroup, int orderIndex)
if (!rowExists) { if (!rowExists) {
addrGroup->setId(db->lastInsertRowid()); addrGroup->setId(db->lastInsertRowid());
} }
if (!saveAddressGroupZones(db, addrGroup))
return false;
addrGroup->setEdited(false); addrGroup->setEdited(false);
return true; return true;
} }
bool loadAppGroups(SqliteDb *db, FirewallConf &conf)
{
SqliteStmt stmt;
if (!db->prepare(stmt, sqlSelectAppGroups))
return false;
while (stmt.step() == SqliteStmt::StepRow) {
auto appGroup = new AppGroup();
appGroup->setId(stmt.columnInt64(0));
appGroup->setEnabled(stmt.columnBool(1));
appGroup->setFragmentPacket(stmt.columnInt(2));
appGroup->setPeriodEnabled(stmt.columnBool(3));
appGroup->setLimitInEnabled(stmt.columnBool(4));
appGroup->setLimitOutEnabled(stmt.columnBool(5));
appGroup->setSpeedLimitIn(quint32(stmt.columnInt(6)));
appGroup->setSpeedLimitOut(quint32(stmt.columnInt(7)));
appGroup->setName(stmt.columnText(8));
appGroup->setBlockText(stmt.columnText(9));
appGroup->setAllowText(stmt.columnText(10));
appGroup->setPeriodFrom(stmt.columnText(11));
appGroup->setPeriodTo(stmt.columnText(12));
appGroup->setEdited(false);
conf.addAppGroup(appGroup);
}
return true;
}
bool saveAppGroup(SqliteDb *db, AppGroup *appGroup, int orderIndex) bool saveAppGroup(SqliteDb *db, AppGroup *appGroup, int orderIndex)
{ {
const bool rowExists = (appGroup->id() != 0); const bool rowExists = (appGroup->id() != 0);
@ -851,26 +982,11 @@ bool ConfManager::loadFromDb(FirewallConf &conf, bool &isNew)
{ {
// Load Address Groups // Load Address Groups
{ {
SqliteStmt stmt; int count = 0;
if (!stmt.prepare(m_sqliteDb->db(), sqlSelectAddressGroups)) if (!loadAddressGroups(m_sqliteDb, conf.addressGroups(), count))
return false; return false;
int index = 0; if (count == 0) {
while (stmt.step() == SqliteStmt::StepRow) {
auto addrGroup = conf.addressGroups().at(index);
Q_ASSERT(addrGroup != nullptr);
addrGroup->setId(stmt.columnInt64(0));
addrGroup->setIncludeAll(stmt.columnBool(1));
addrGroup->setExcludeAll(stmt.columnBool(2));
addrGroup->setIncludeText(stmt.columnText(3));
addrGroup->setExcludeText(stmt.columnText(4));
if (++index > 1)
break;
}
if (index == 0) {
isNew = true; isNew = true;
return true; return true;
} }
@ -878,31 +994,8 @@ bool ConfManager::loadFromDb(FirewallConf &conf, bool &isNew)
} }
// Load App Groups // Load App Groups
{ if (!loadAppGroups(m_sqliteDb, conf))
SqliteStmt stmt; return false;
if (!stmt.prepare(m_sqliteDb->db(), sqlSelectAppGroups))
return false;
while (stmt.step() == SqliteStmt::StepRow) {
auto appGroup = new AppGroup();
appGroup->setId(stmt.columnInt64(0));
appGroup->setEnabled(stmt.columnBool(1));
appGroup->setFragmentPacket(stmt.columnInt(2));
appGroup->setPeriodEnabled(stmt.columnBool(3));
appGroup->setLimitInEnabled(stmt.columnBool(4));
appGroup->setLimitOutEnabled(stmt.columnBool(5));
appGroup->setSpeedLimitIn(quint32(stmt.columnInt(6)));
appGroup->setSpeedLimitOut(quint32(stmt.columnInt(7)));
appGroup->setName(stmt.columnText(8));
appGroup->setBlockText(stmt.columnText(9));
appGroup->setAllowText(stmt.columnText(10));
appGroup->setPeriodFrom(stmt.columnText(11));
appGroup->setPeriodTo(stmt.columnText(12));
conf.addAppGroup(appGroup);
}
}
return true; return true;
} }

View File

@ -26,7 +26,6 @@ CREATE TABLE address_group(
CREATE TABLE address_group_zone( CREATE TABLE address_group_zone(
addr_group_id INTEGER NOT NULL, addr_group_id INTEGER NOT NULL,
zone_id INTEGER NOT NULL, zone_id INTEGER NOT NULL,
order_index INTEGER NOT NULL,
include BOOLEAN NOT NULL include BOOLEAN NOT NULL
); );

View File

@ -7,6 +7,7 @@
#include <QVBoxLayout> #include <QVBoxLayout>
#include "../../../controls/controlutil.h" #include "../../../controls/controlutil.h"
#include "../../../controls/widebutton.h"
#include "../../../controls/plaintextedit.h" #include "../../../controls/plaintextedit.h"
AddressesColumn::AddressesColumn(QWidget *parent) : AddressesColumn::AddressesColumn(QWidget *parent) :
@ -17,7 +18,7 @@ AddressesColumn::AddressesColumn(QWidget *parent) :
void AddressesColumn::retranslateUi() void AddressesColumn::retranslateUi()
{ {
m_btSelectZones->setText(tr("Zones:")); m_btSelectZones->setText(tr("Zones"));
m_btSelectZones->setToolTip(tr("Select Zones")); m_btSelectZones->setToolTip(tr("Select Zones"));
} }
@ -55,9 +56,8 @@ QLayout *AddressesColumn::setupZonesRow()
{ {
auto layout = new QHBoxLayout(); auto layout = new QHBoxLayout();
layout->setMargin(0); layout->setMargin(0);
layout->setSpacing(0);
m_btSelectZones = ControlUtil::createLinkButton(":/images/map_magnify.png"); m_btSelectZones = new WideButton(QIcon(":/images/map_magnify.png"));
layout->addWidget(m_btSelectZones); layout->addWidget(m_btSelectZones);
m_labelZones = ControlUtil::createLabel(); m_labelZones = ControlUtil::createLabel();

View File

@ -4,6 +4,7 @@
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QIcon> #include <QIcon>
#include <QLabel> #include <QLabel>
#include <QMenu>
#include <QPlainTextEdit> #include <QPlainTextEdit>
#include <QPushButton> #include <QPushButton>
#include <QTabBar> #include <QTabBar>
@ -30,6 +31,7 @@ AddressesPage::AddressesPage(OptionsController *ctrl,
setupUi(); setupUi();
setupAddressGroup(); setupAddressGroup();
setupZones();
} }
AddressGroup *AddressesPage::addressGroup() const AddressGroup *AddressesPage::addressGroup() const
@ -210,12 +212,12 @@ void AddressesPage::setupSplitterButtons()
void AddressesPage::updateGroup() void AddressesPage::updateGroup()
{ {
m_includeAddresses->cbUseAll()->setChecked(addressGroup()->includeAll()); m_includeAddresses->cbUseAll()->setChecked(addressGroup()->includeAll());
m_includeAddresses->labelZones()->setText(zonesText(true));
m_includeAddresses->editIpText()->setText(addressGroup()->includeText()); m_includeAddresses->editIpText()->setText(addressGroup()->includeText());
m_excludeAddresses->cbUseAll()->setChecked(addressGroup()->excludeAll()); m_excludeAddresses->cbUseAll()->setChecked(addressGroup()->excludeAll());
m_excludeAddresses->labelZones()->setText(zonesText(false));
m_excludeAddresses->editIpText()->setText(addressGroup()->excludeText()); m_excludeAddresses->editIpText()->setText(addressGroup()->excludeText());
updateZonesTextAll();
} }
void AddressesPage::setupAddressGroup() void AddressesPage::setupAddressGroup()
@ -232,6 +234,119 @@ void AddressesPage::setupAddressGroup()
connect(m_tabBar, &QTabBar::currentChanged, this, refreshAddressGroup); connect(m_tabBar, &QTabBar::currentChanged, this, refreshAddressGroup);
} }
void AddressesPage::clearZonesMenu()
{
m_menuZones->close();
m_menuZones->clear();
}
void AddressesPage::createZonesMenu()
{
const auto onZoneActionTriggered = [&](bool checked) {
auto action = qobject_cast<QAction *>(sender());
const int zoneId = action->data().toInt();
const bool include = m_includeAddresses->btSelectZones()->isDown();
auto addrGroup = this->addressGroup();
if (checked) {
if (include) {
addrGroup->addIncludeZone(zoneId, true);
} else {
addrGroup->addExcludeZone(zoneId, true);
}
} else {
if (include) {
addrGroup->removeIncludeZone(zoneId);
} else {
addrGroup->removeExcludeZone(zoneId);
}
}
ctrl()->setConfEdited(true);
updateZonesText(include);
};
const int zoneCount = zoneListModel()->rowCount();
for (int zoneId = 1; zoneId <= zoneCount; ++zoneId) {
const auto zoneName = zoneListModel()->zoneNameById(zoneId);
if (zoneName.isEmpty())
continue;
auto action = new QAction(zoneName, m_menuZones);
action->setCheckable(true);
action->setData(zoneId);
connect(action, &QAction::triggered, this, onZoneActionTriggered);
m_menuZones->addAction(action);
}
}
void AddressesPage::updateZonesMenu(bool include)
{
if (m_menuZones->isEmpty()) {
createZonesMenu();
}
const auto actions = m_menuZones->actions();
if (actions.isEmpty())
return;
const auto zoneIds = addressGroupZones(include);
for (auto action : actions) {
const int zoneId = action->data().toInt();
const bool checked = zoneIds.contains(zoneId);
action->setChecked(checked);
}
}
void AddressesPage::updateZonesText(bool include)
{
if (include) {
m_includeAddresses->labelZones()->setText(zonesText(true));
} else {
m_excludeAddresses->labelZones()->setText(zonesText(false));
}
}
void AddressesPage::updateZonesTextAll()
{
updateZonesText(true);
updateZonesText(false);
}
void AddressesPage::setupZones()
{
m_menuZones = new QMenu(this);
const auto refreshZonesMenu = [&] {
const bool include = (sender() == m_includeAddresses->btSelectZones());
updateZonesMenu(include);
};
connect(m_includeAddresses->btSelectZones(), &QPushButton::pressed, this, refreshZonesMenu);
connect(m_excludeAddresses->btSelectZones(), &QPushButton::pressed, this, refreshZonesMenu);
m_includeAddresses->btSelectZones()->setMenu(m_menuZones);
m_excludeAddresses->btSelectZones()->setMenu(m_menuZones);
connect(zoneListModel(), &ZoneListModel::zoneRemoved, this, [&](int zoneId) {
for (auto addrGroup : addressGroups()) {
addrGroup->removeIncludeZone(zoneId);
addrGroup->removeExcludeZone(zoneId);
}
});
connect(zoneListModel(), &ZoneListModel::modelChanged, this, [&] {
clearZonesMenu();
updateZonesTextAll();
});
}
const QList<AddressGroup *> &AddressesPage::addressGroups() const const QList<AddressGroup *> &AddressesPage::addressGroups() const
{ {
return conf()->addressGroups(); return conf()->addressGroups();
@ -242,10 +357,15 @@ AddressGroup *AddressesPage::addressGroupByIndex(int index) const
return addressGroups().at(index); return addressGroups().at(index);
} }
const QVector<int> &AddressesPage::addressGroupZones(bool include) const
{
return include ? addressGroup()->includeZones()
: addressGroup()->excludeZones();
}
QString AddressesPage::zonesText(bool include) const QString AddressesPage::zonesText(bool include) const
{ {
const auto zoneIds = zoneListModel()->addressGroupZones( const auto zoneIds = addressGroupZones(include);
addressGroup()->id(), include);
if (zoneIds.isEmpty()) if (zoneIds.isEmpty())
return QString(); return QString();

View File

@ -40,10 +40,18 @@ private:
void setupSplitterButtons(); void setupSplitterButtons();
void updateGroup(); void updateGroup();
void setupAddressGroup(); void setupAddressGroup();
void clearZonesMenu();
void createZonesMenu();
void updateZonesMenu(bool include);
void updateZonesText(bool include);
void updateZonesTextAll();
void setupZones();
const QList<AddressGroup *> &addressGroups() const; const QList<AddressGroup *> &addressGroups() const;
AddressGroup *addressGroupByIndex(int index) const; AddressGroup *addressGroupByIndex(int index) const;
const QVector<int> &addressGroupZones(bool include) const;
QString zonesText(bool include) const; QString zonesText(bool include) const;
static QString localNetworks(); static QString localNetworks();
@ -56,6 +64,7 @@ private:
AddressesColumn *m_excludeAddresses = nullptr; AddressesColumn *m_excludeAddresses = nullptr;
TextArea2Splitter *m_splitter = nullptr; TextArea2Splitter *m_splitter = nullptr;
QPushButton *m_btAddLocals = nullptr; QPushButton *m_btAddLocals = nullptr;
QMenu *m_menuZones = nullptr;
}; };
#endif // ADDRESSESPAGE_H #endif // ADDRESSESPAGE_H

View File

@ -10,6 +10,7 @@ QT_FORWARD_DECLARE_CLASS(QComboBox)
QT_FORWARD_DECLARE_CLASS(QGroupBox) QT_FORWARD_DECLARE_CLASS(QGroupBox)
QT_FORWARD_DECLARE_CLASS(QLabel) QT_FORWARD_DECLARE_CLASS(QLabel)
QT_FORWARD_DECLARE_CLASS(QLineEdit) QT_FORWARD_DECLARE_CLASS(QLineEdit)
QT_FORWARD_DECLARE_CLASS(QMenu)
QT_FORWARD_DECLARE_CLASS(QPushButton) QT_FORWARD_DECLARE_CLASS(QPushButton)
QT_FORWARD_DECLARE_CLASS(QTabBar) QT_FORWARD_DECLARE_CLASS(QTabBar)

View File

@ -192,6 +192,7 @@ void ZoneListModel::deleteZone(int zoneId, int row)
beginRemoveRows(QModelIndex(), row, row); beginRemoveRows(QModelIndex(), row, row);
if (confManager()->deleteZone(zoneId)) { if (confManager()->deleteZone(zoneId)) {
emit zoneRemoved(zoneId);
invalidateRowCache(); invalidateRowCache();
removeRow(row); removeRow(row);
} }
@ -199,26 +200,6 @@ void ZoneListModel::deleteZone(int zoneId, int row)
endRemoveRows(); endRemoveRows();
} }
QVector<int> ZoneListModel::addressGroupZones(qint64 addrGroupId, bool include)
{
static const char * const sql =
"SELECT zone_id"
" FROM address_group_zone"
" WHERE addr_group_id = ?1 AND include = ?2"
" ORDER BY order_index;"
;
QVector<int> list;
SqliteStmt stmt;
if (sqliteDb()->prepare(stmt, sql, {addrGroupId, include})) {
while (stmt.step() == SqliteStmt::StepRow) {
const int id = stmt.columnInt(0);
list.append(id);
}
}
return list;
}
QString ZoneListModel::zoneNameById(int zoneId) QString ZoneListModel::zoneNameById(int zoneId)
{ {
static const char * const sql = static const char * const sql =

View File

@ -71,8 +71,6 @@ public:
const QDateTime &lastSuccess); const QDateTime &lastSuccess);
void deleteZone(int zoneId, int row); void deleteZone(int zoneId, int row);
QVector<int> addressGroupZones(qint64 addrGroupId, bool include);
QString zoneNameById(int zoneId); QString zoneNameById(int zoneId);
QVariant zoneTypeByCode(const QString &typeCode) const; QVariant zoneTypeByCode(const QString &typeCode) const;
@ -80,6 +78,9 @@ public:
QVariant zoneSourceByCode(const QString &sourceCode) const; QVariant zoneSourceByCode(const QString &sourceCode) const;
const QVariantList &zoneSources() const { return m_zoneSources; } const QVariantList &zoneSources() const { return m_zoneSources; }
signals:
void zoneRemoved(int zoneId);
protected: protected:
bool updateTableRow(int row) const override; bool updateTableRow(int row) const override;
TableRow &tableRow() const override { return m_zoneRow; } TableRow &tableRow() const override { return m_zoneRow; }

View File

@ -45,6 +45,7 @@ void TableSqlModel::invalidateRowCache()
{ {
m_rowCount = -1; m_rowCount = -1;
tableRow().invalidate(); tableRow().invalidate();
emit modelChanged();
} }
void TableSqlModel::updateRowCache(int row) const void TableSqlModel::updateRowCache(int row) const

View File

@ -25,6 +25,9 @@ public:
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;
signals:
void modelChanged();
public slots: public slots:
void reset(); void reset();
void refresh(); void refresh();