Mitgliederverwaltung für einen Landesverband, Clientkomponente
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
pmv-client/mainwindow.cpp

400 lines
12 KiB

/* Diese Datei ist Teil von pmv-client <https://git.piratenpartei-sh.de>
*
* pmv-client ist Freie Software: Sie können es unter den Bedingungen
* der GNU General Public License, wie von der Free Software Foundation,
* Version 3 der Lizenz weiter verteilen und/oder modifizieren.
*
* Dieses Programm wird in der Hoffnung bereitgestellt, dass es nützlich sein
* wird, jedoch OHNE JEDE GEWÄHR,; sogar ohne die implizite Gewähr der
* MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK.
* Siehe die GNU General Public License für weitere Einzelheiten.
*
* Sie sollten eine Kopie der GNU General Public License zusammen mit diesem
* Programm erhalten haben. Wenn nicht, siehe <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#include <QMessageBox>
#include <QSettings>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlQueryModel>
#include <QSqlRecord>
#include <QDebug>
#include <QNetworkInterface>
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "conndialog.h"
#include "passdialog.h"
#include "editdialog.h"
#include "helpdialog.h"
#include "importdialog.h"
#include "wahldialog.h"
#include "wahlkreisdialog.h"
#include "amtadressdialog.h"
#include "configdialog.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QMap<int, QString> fieldmap;
fieldmap.insert(1, "Nachname");
fieldmap.insert(2, "Vorname");
QStringList filterfeld = {"Nachname", "Vorname", "Vor- oder Nachname"};
ui->comboBox_filter->insertItems(0, filterfeld);
//ui->comboBox_filter-> ->insertItems(0, fieldmap);
statusLabel = new QLabel(this);
statusLabel->setText("Nicht verbunden");
ui->statusbar->addPermanentWidget(statusLabel);
QSettings settings;
development = settings.value("develop").toBool();
restoreGeometry(settings.value("geometry").toByteArray());
restoreState(settings.value("windowState").toByteArray());
if (!development && !check_wireguard()) {
ui->actionVerbindung->setDisabled(true);
ui->statusbar->showMessage("Wireguard-Interface 'wg0' nicht gefunden.Tunnel aktiv?");
QMessageBox msg;
msg.warning(this, "Mitgliederverwaltung", "Keine Wireguard-Verbindung 'wg0' gefunden!");
}
db = QSqlDatabase::addDatabase("QMYSQL");
if (!db.isValid()) {
QMessageBox msg;
msg.critical(this, "Mitgliederverwaltung", "Datenbanktreiber für MariaDB fehlt!");
exit(1);
}
#ifdef _WIN32
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
#endif
}
MainWindow::~MainWindow()
{
delete ui;
QSettings settings;
settings.setValue("geometry", saveGeometry());
settings.setValue("windowState", saveState());
if (db.isOpen()) {
QSqlQuery qry;
qry.prepare("DELETE FROM session WHERE sessionkey=:sessionkey");
qry.bindValue(":sessionkey", sessionkey);
if (qry.exec()) {
qDebug() << "Sessionkey aus Datenbank entfernt";
}
}
}
bool MainWindow::check_wireguard()
{
// Prüfe ob ein aktives Wireguard-Interface vorhanden ist
// Die lokale Wireguard-Konfiguration ist für einen normalen Benutzer
// nicht im Zugriff, Konfigurationsdatei kann also nicht ausgewertet
// werden.
// siehe auch: /sys/devices/virtual/net/wg0/uevent
// Für Windows
// - isP2P anscheinend nicht gesetzt
// - Interfacename muß im Wireguard-Konfigurationsdialog auf "wg0"
// gesetzt werden
QList<QNetworkInterface> ifaces = QNetworkInterface::allInterfaces();
if (!ifaces.isEmpty()) {
for (int i=0; i < ifaces.size(); i++) {
#ifdef _WIN32
if (ifaces[i].type() == QNetworkInterface::Unknown) {
#else
if (ifaces[i].type() == QNetworkInterface::Virtual) {
#endif
unsigned int flags = ifaces[i].flags();
bool isUp = bool(flags & QNetworkInterface::IsUp);
bool isRunning = bool(flags & QNetworkInterface::IsRunning);
#ifdef _WIN32
// kein P2P für Windows?
if (QString::compare(ifaces[i].name(), "wg0") == 0 && isUp && isRunning) {
#else
bool isP2P = bool(flags & QNetworkInterface::IsPointToPoint);
if (QString::compare(ifaces[i].name(), "wg0") == 0 && isUp && isRunning && isP2P) {
#endif
return true;
}
}
}
}
return false;
}
bool MainWindow::check_interface(QString ifname)
{
// Prüfe ob eine bestimmte IP-Adresse existiert.
// Diese sollte die lokale Adresse des Wireguard-Interfaces sein.
QNetworkInterface iface = QNetworkInterface::interfaceFromName(ifname);
QList<QHostAddress> addresses = iface.allAddresses();
for (int a=0; a < addresses.size(); a++) {
if (addresses[a] == QHostAddress::LocalHost ) continue;
if (!addresses[a].toIPv4Address()) continue;
QString ip = addresses[a].toString();
if (QString::compare(ip, "192.168.23.102", Qt::CaseInsensitive) == 0) {
qDebug() << "IP gefunden";
return true;
}
}
return false;
}
QString random_string(std::size_t length)
{
const QString CHARACTERS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
QString tempstring;
std::random_device random_device;
std::mt19937 generator(random_device());
std::uniform_int_distribution<> distribution(0, CHARACTERS.size() - 1);
for (std::size_t i = 0; i < length; ++i) {
tempstring += CHARACTERS[distribution(generator)];
}
return tempstring;
}
void MainWindow::init_sessionkey()
{
sessionkey = random_string(32);
qDebug() << "Sessionkey=" << sessionkey;
QSqlQuery qry;
qry.prepare("INSERT INTO session (sessionkey) VALUES (:sessionkey)");
qry.bindValue(":sessionkey", sessionkey);
qry.exec();
}
void MainWindow::on_actionVerbindung_triggered()
{
// Anzeige des Datenbankverbindungseditors
// Modaler Dialog mit Rückgabewert
ConnDialog d(this);
model = new QSqlQueryModel;
QSortFilterProxyModel *proxymodel = new QSortFilterProxyModel;
int result = d.exec();
//QString s;
if (result == QDialog::Accepted) {
// Werte aus dem Dialog auslesen
qDebug() << "Verbindung aufbauen ..";
QString host = d.getHost();
qint16 port = d.getPort();
QString dbname = d.getDatabaseName();
QString user = d.getUserName();
QString pass = d.getPassword();
db.setHostName(host);
db.setPort(port);
db.setDatabaseName(dbname);
db.setUserName(user);
db.setPassword(pass);
if (db.open()) {
// Der Sessionkey wird benötigt um über diesem mit dem PMV-Server
// über XMLRPC zu kommunizieren
init_sessionkey();
// Globale Landesverbandsdaten aus Datenbank vorladen
lv.loadFromDatabase();
qDebug() << "Version" << lv.getVersion();
qDebug() << "Releasedate" << lv.getReleaseDate();
statusLabel->setText(QString("Datenbank '%1' verbunden als '%2'").arg(dbname, user));
ui->actionVerbindung->setDisabled(true);
ui->actionKennwort->setDisabled(false);
ui->pushButton_add->setEnabled(true);
ui->pushButton_edit->setEnabled(true);
// Erfolgreiche Verbindung, speichern der Dialogeinstellung für nächste Verwendung
QSettings s;
s.setValue("host", host);
s.setValue("port", port);
s.setValue("db", dbname);
s.setValue("user", user);
model->setQuery("SELECT nr, vorname, nachname, status FROM mitglied WHERE status<>'ausgetreten' ORDER BY nr");
model->setHeaderData(0, Qt::Horizontal, "Nummer");
model->setHeaderData(1, Qt::Horizontal, "Vorname");
model->setHeaderData(2, Qt::Horizontal, "Nachname");
model->setHeaderData(3, Qt::Horizontal, "Status");
proxymodel->setSourceModel(model);
proxymodel->setFilterKeyColumn(2); // Nachname
proxymodel->setFilterCaseSensitivity(Qt::CaseInsensitive);
ui->tableView->setModel(proxymodel);
ui->tableView->show();
} else {
statusLabel->setText("Nicht verbunden");
}
}
}
void MainWindow::on_actionUeber_triggered()
{
QMessageBox msg;
QString msgstr = "<p>Piraten Mitglieder Verwaltung.<br>Schneller Hack um Ausfall des Bundessystems zu kompensieren.</p>";
if (db.isValid() && db.isOpen()) {
msgstr += QString("<p>Datenbankversion %1\n</p>").arg(lv.getVersion());
// TODO Versionsdatum einbauen. Funktionsweise von .arg() mit mehreren Variablen?
}
else {
msgstr += "Datenbank nicht geöffnet.";
}
msg.about(this, "Über PMV", msgstr);
}
void MainWindow::on_pushButton_edit_clicked()
{
QItemSelectionModel *selectionModel = ui->tableView->selectionModel();
const QModelIndexList indexes = selectionModel->selectedRows();
const QModelIndex row = indexes.at(0);
EditDialog ed(this, ui->tableView->model()->data(row).toInt());
int result = ed.exec();
if (result == QDialog::Accepted) {
// Geänderte Daten nachladen bzw. im Model aktualisieren?
model->query().exec();
// Beispiel für Zugriff auf die zweite Spalte der selektierten Modellzeile
// qDebug() << "row" << row.siblingAtColumn(1).data();
}
}
void MainWindow::on_actionBeenden_triggered()
{
qDebug() << "Programm über Menü normal beenden.";
}
void MainWindow::on_actionKennwort_triggered()
{
// Kennwortänderung auf Datenbankebene
PassDialog pd(this);
int result = pd.exec();
if (result == QDialog::Accepted) {
ui->statusbar->showMessage("Kennwort wurde geändert");
}
}
void MainWindow::on_actionAnleitung_triggered()
{
// Anleitung zur Mitgliederverwaltung
HelpDialog hd(this);
hd.exec();
}
void MainWindow::on_actionImport_triggered()
{
// Daten importieren
ImportDialog id(this);
id.exec();
}
void MainWindow::on_actionWahlen_triggered()
{
WahlDialog wd(this);
wd.exec();
}
void MainWindow::on_actionWahlkreise_triggered()
{
WahlkreisDialog wkd(this);
wkd.exec();
}
void MainWindow::on_actionAmtadressen_triggered()
{
AmtAdressDialog dlg(this);
dlg.exec();
}
void MainWindow::on_actionEinstellungen_triggered()
{
ConfigDialog dialog(this, &lv);
dialog.exec();
}
void MainWindow::on_lineEdit_filter_textChanged(const QString &arg1)
{
// Filter bei jedem Tastendruck aktualisieren
QSortFilterProxyModel *model = (QSortFilterProxyModel *)ui->tableView->model();
model->setFilterFixedString(arg1);
}
void MainWindow::on_comboBox_filter_currentIndexChanged(int index)
{
// Feldauswahl für den Filter
QSortFilterProxyModel *model = (QSortFilterProxyModel *)ui->tableView->model();
if (!model) {
return;
}
if (index == 0) {
// Nachname
model->setFilterKeyColumn(2);
} else if (index == 1) {
// Vorname
model->setFilterKeyColumn(1);
} else {
model->setFilterKeyColumn(-1);
}
}
void MainWindow::on_pushButton_clear_clicked()
{
ui->lineEdit_filter->setText("");
}
void MainWindow::on_pushButton_add_clicked()
{
// Neues Mitglied anlegen, direkt mit genau einem SQL-Statement in der Datenbank,
// damit es nicht zu Kollisionen bei gleichzeitigem Arbeiten mit mehreren Benutzern
// kommt. Temporäre Nummer verwenden, dabei immer die Basisnummer beachten!
QSqlQuery qry;
QString sql;
sql = QString("INSERT INTO mitglied"
" (nr, vorname, nachname, geburtsdatum, staatsang, eintrittsdatum) "
"SELECT"
" (SELECT IFNULL(MAX(nr)+1,%1) FROM mitglied WHERE nr>=%1),"
" 'Neu' , 'Mitglied', '1900-01-01', 'DE', NOW()").arg(lv.getOffsetNrNeu());
qDebug() << sql;
if (!qry.exec(sql)) {
qDebug() << "Fehler beim Erzeugen eines neuen Mitgliederdatensatzes";
}
}
void MainWindow::on_checkBox_stateChanged(int arg1)
{
if (!model) {
return;
}
if (arg1 == 2) {
model->setQuery("SELECT nr, vorname, nachname, status FROM mitglied ORDER BY nr");
} else {
model->setQuery("SELECT nr, vorname, nachname, status FROM mitglied WHERE status<>'ausgetreten' ORDER BY nr");
}
}