Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

@@ -0,0 +1,38 @@
#
# Created by Mike Lennart Wille, ESZS, 2025-06-17
#
SET(src
nom_edit_gui_plugin.cpp
nom_edit_gui_plugin.h
nom_dialog.cpp
nom_dialog.h
nom_dialog_model.cpp
nom_dialog_model.h
)
NGL_WRAP_UI(src
nom_dialog.ui
)
NGL_ADD_LIBRARY(WS NomEditGuiPlugin MODULE ${src})
NGL_TARGET_LINK_LIBRARIES(NomEditGuiPlugin
ngltools # dtg
NGLLogger
LtsSession
NomDataPluginInterface
NomGuiPluginInterface
Qt5::Widgets
LtsWidgets
LtsUtilsGuiDds
LtsDataView
LtsSessionData
LtsNotification
svcNomClient
svcOpmClient
svcAsmClient
)
NGL_INSTALL_TARGET(NomEditGuiPlugin)
@@ -0,0 +1,269 @@
/**
* @RDE_HEADER@
* NotificationManagement - Notification Overview Dialog
*
* Created by Mike Lennart Wille, ESZS, 2025-06-17
*/
#include "nom_dialog.h"
#include <ui_nom_dialog.h>
#include "nom_dialog_model.h"
#include <QLabel>
#include <QTextBrowser>
#include <LtsSession/lts_session.h>
#include <LtsSession/lts_session_data.h>
#include <LtsWidgets/lts_events.h>
#include <LtsUtilsSettings/settings.h>
#include <LtsWidgets/lts_base_layout_widget.h>
#include <LtsWidgets/lts_events.h>
#include <Plugins/NotificationManagement/nom_data_plugin_interface.h>
namespace NotificationManagement
{
NotificationManagementDialog::NotificationManagementDialog(LtsSession::LtsSession *session, const QString &menuGroup, QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::CloseOnly)
, m_model(new NotificationManagementDialogModel(this))
, m_ui(new Ui::NotificationManagementDialog)
, m_session(session)
, m_menuGroup(menuGroup)
, m_requestHelper(new LtsUtils::RequestHelper(this))
, m_logger(getLogger("NotificationManagementDialog"))
{
m_ui->setupUi(this);
m_ui->widget->setSelectionMode(QAbstractItemView::ExtendedSelection);
setOnlineHelpFilename(QStringLiteral("Administrator-Notifications"));
// Save-Button erzeugen
m_buttonBox->connectButton(LtsWidgets::DialogButton::Save, this, &NotificationManagementDialog::onAcknowledgeNotification);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Save, false);
m_buttonBox->setText(LtsWidgets::DialogButton::Save, tr("Acknowledge"));
// Delete-Button erzeugen
m_buttonBox->connectButton(LtsWidgets::DialogButton::Remove, this, &NotificationManagementDialog::onRemoveNotification);
// Show-Button erzeugen
m_buttonBox->connectButton(LtsWidgets::DialogButton::Show, this, &NotificationManagementDialog::onShowNotificationContent);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Show, false);
connect(m_ui->widget, &LtsDataView::LtsDataView::signalDoubleClicked, this, &NotificationManagementDialog::onShowNotificationContent);
if (!m_session->getDtc<NotificationManagement::DataInterface>(m_dtc))
return;
if (!m_session->getFtc<NotificationManagement::DataInterface>(m_ftc))
return;
if (!m_session->getDtc<OperatorManagement::DataInterface>(m_dtcOpm))
return;
if (!m_session->getDtc<AssetManagement::DataInterface>(m_dtcAsm))
return;
m_dtcAsm->enableAsset();
m_model->initModel(m_session);
auto proxy = new NotificationDialogProxyModel(this);
proxy->setSourceModel(m_model);
m_ui->widget->setModel(proxy);
connect(m_ui->widget, &LtsDataView::LtsDataView::signalSelectionChanged, this, &NotificationManagementDialog::enaDisa);
enaDisa();
}
NotificationManagementDialog::~NotificationManagementDialog() { delete m_ui; }
void NotificationManagementDialog::retranslate() { translate(m_ui, tr("Notifications")); }
void NotificationManagementDialog::tableColumnOrganiserSetting()
{
// LtsColumnOrganiser
m_ui->widget->setColumnsOrganiserEnabled(identifier(), true);
}
void NotificationManagementDialog::onAcknowledgeNotification()
{
const QModelIndex curIdx = m_ui->widget->currentIndex();
const NomNotification &noti = m_model->getRowData(curIdx.row());
ReadReceipt request;
int operatorId = LtsSessionData::LtsSessionData::getInstance()->getOperatorId();
int workstationId = LtsSessionData::LtsSessionData::getInstance()->getWorkstationId();
for (const auto &addr : noti.recipientIds())
{
if ((addr.addressType() == AddressType_def::Operator && addr.addressId() == operatorId)
|| (addr.addressType() == AddressType_def::Workstation && addr.addressId() == workstationId))
{
request.addressId(addr.id());
break;
}
}
request.operatorId(operatorId);
request.workstationId(workstationId);
bool onlyNotYetAcknowledged = true;
request.notificationIds(selectedNotificationIds(onlyNotYetAcknowledged));
if (request.notificationIds().empty())
return;
request.processed(true);
m_requestHelper->setCustomWaitMessage(tr("Marking notification as read"));
m_requestHelper->startSingleRequest(
[&]()
{
DEBUG_LOG(m_logger) << "m_ftc->readReceipt ";
return m_ftc->readReceiptRequest(request, this);
});
}
std::vector<int32_t> NotificationManagementDialog::selectedNotificationIds(bool onlyNotYetAcknowledged)
{
std::vector<int32_t> notificationIds;
QModelIndexList selectedRows = m_ui->widget->selectionModel()->selectedRows();
notificationIds.reserve(selectedRows.size());
for (const QModelIndex &index : selectedRows)
{
const NomNotification &noti = m_model->getRowData(index.row());
// If the notification is already acknowledged
// and this acknowledgment has already been processed: Continue.
if (onlyNotYetAcknowledged && noti.processed())
continue;
// Add the rest.
notificationIds.emplace_back(noti.id());
}
return notificationIds;
}
void NotificationManagementDialog::onRemoveNotification()
{
const QModelIndex curIdx = m_ui->widget->currentIndex();
const NomNotification &noti = m_model->getRowData(curIdx.row());
DeleteMessage request;
const int operatorId = LtsSessionData::LtsSessionData::getInstance()->getOperatorId();
const int workstationId = LtsSessionData::LtsSessionData::getInstance()->getWorkstationId();
for (const auto &addr : noti.recipientIds())
{
if ((addr.addressType() == AddressType_def::Operator && addr.addressId() == operatorId)
|| (addr.addressType() == AddressType_def::Workstation && addr.addressId() == workstationId))
{
request.addressId(addr.id());
break;
}
}
request.operatorId(operatorId);
request.workstationId(workstationId);
bool acknowledge = false;
request.notificationIds(selectedNotificationIds(acknowledge));
m_requestHelper->setCustomWaitMessage(tr("Delete message"));
m_requestHelper->startSingleRequest(
[&]()
{
DEBUG_LOG(m_logger) << "m_ftc->readReceipt ";
return m_ftc->deleteMessageRequest(request, this);
});
}
void NotificationManagementDialog::enaDisa()
{
QModelIndexList selectedRows = m_ui->widget->selectedRows();
const int cntSelectedRows = selectedRows.size();
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Save, cntSelectedRows >= 1);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Remove, cntSelectedRows >= 1);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Show, cntSelectedRows == 1);
}
void NotificationManagementDialog::onShowNotificationContent()
{
LtsWidgets::LtsFramelessDialog *contentPopup = new LtsWidgets::LtsFramelessDialog(this, LtsWidgets::Behaviour::Popup);
const QModelIndex curIdx = m_ui->widget->currentIndex();
const NomNotification &noti = m_model->getRowData(curIdx.row());
QString content = QCoreApplication::translate("Notification", noti.content().c_str());
NomNotification notiDDS = m_dtc->nomNotification(noti.id());
DEBUG_LOG(m_logger) << "onShowNotificationContent"
<< ", content=" << noti.content()
<< ", contentArgs().size()=" << noti.contentArgs().size()
<< ", notiDDS.contentArgs().size()=" << notiDDS.contentArgs().size();
for (const std::string& arg : notiDDS.contentArgs()) {
// .arg() ersetzt immer den kleinsten freien Platzhalter (z. B. zuerst %1, dann %2)
content = content.arg(QString::fromStdString(arg));
DEBUG_LOG(m_logger) << ".arg() ersetzt immer den kleinsten freien Platzhalter"
<< ", arg=" << arg
<< ", content=" << content;
}
QScrollArea *scrollArea = new QScrollArea(this);
scrollArea->setWidgetResizable(true);
QTextBrowser *wid = new QTextBrowser(this);
wid->setOpenExternalLinks(false);
wid->setOpenLinks(false);
//wid->setTextFormat(Qt::MarkdownText);
wid->setWordWrapMode(QTextOption::WordWrap); // Zeilenumbruch bei langem Text
wid->setMarkdown(content);
connect(wid, &QTextBrowser::anchorClicked, this, [this, contentPopup](QUrl link )
{
DEBUG_LOG(m_logger) << "LINK CLICKED: " << link.toString();
auto *event = new LtsWidgets::LtsLinkEvent(link, m_session->baseLayoutWidget());
event->setAccepted(false);
QApplication::postEvent(qApp, event); // receive link events by overriding eventFilter of your *parented* QObject class
contentPopup->close();
});
scrollArea->setWidget(wid);
QFont font;
font.setBold(true);
QLabel *lblTitle = new QLabel(QCoreApplication::translate("Notification", noti.title().c_str()), contentPopup);
lblTitle->setFont(font);
QLabel *lblFrom = new QLabel(tr("From: "));
lblFrom->setFont(font);
QLabel *lblSender = new QLabel(QString::fromStdString(noti.sender().name()));
QFrame *line = new QFrame(this);
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
QFrame *line2 = new QFrame(this);
line2->setFrameShape(QFrame::HLine);
line2->setFrameShadow(QFrame::Sunken);
LtsWidgets::LtsDialogButtonBox *btnBox = new LtsWidgets::LtsDialogButtonBox(contentPopup);
connect(btnBox->getButton(LtsWidgets::DialogButton::Close), &QPushButton::clicked, contentPopup, &LtsWidgets::LtsFramelessDialog::accept); // TODO: undo workaround !
btnBox->getButton(LtsWidgets::DialogButton::Ok)->hide();
// buildUI
QHBoxLayout *hLayFrom = new QHBoxLayout();
hLayFrom->addWidget(lblFrom);
hLayFrom->addWidget(lblSender);
hLayFrom->addStretch(1);
QVBoxLayout *lay = new QVBoxLayout(contentPopup);
lay->addWidget(lblTitle);
lay->addWidget(line);
lay->addLayout(hLayFrom);
lay->addWidget(line2);
lay->addWidget(scrollArea);
lay->addWidget(btnBox);
contentPopup->setLayout(lay);
contentPopup->setMinimumSize(m_session->baseLayoutWidget()->size()/2.0);
// exec
contentPopup->show();
}
} // namespace NotificationManagement
@@ -0,0 +1,70 @@
/**
* @RDE_HEADER@
* NotificationManagement - Notification Overview Dialog
*
* Created by Mike Lennart Wille, ESZS, 2025-06-17
*/
#pragma once
#include <LtsSession/lts_session.h>
#include <LtsWidgets/lts_dialog.h>
#include <NotificationManagement/logger.h>
#include <NotificationManagement/nom_dtc.h>
#include <NotificationManagement/nom_ftc.h>
#include <LtsUtilsGuiDds/request_helper.h>
#include <Plugins/AssetManagement/asm_data_plugin_interface.h>
#include <Plugins/NotificationManagement/nom_data_plugin_interface.h>
#include <Plugins/OperatorManagement/opm_data_plugin_interface.h>
namespace Ui
{
class NotificationManagementDialog;
}
namespace NotificationManagement
{
class NotificationManagementDialogModel;
class NotificationManagementDialog
: public LtsWidgets::LtsDialog
, protected NotificationManagement::Interface::Ftc::ReadReceiptReplyHandler
, protected NotificationManagement::Interface::Ftc::DeleteMessageReplyHandler
{
Q_OBJECT
public:
explicit NotificationManagementDialog(LtsSession::LtsSession *session, const QString &menuGroup, QWidget *parent = nullptr);
~NotificationManagementDialog();
private slots:
void enaDisa();
void onAcknowledgeNotification();
void onRemoveNotification();
void onShowNotificationContent();
protected:
void onReadReceiptReply(const Core::FunctionReplyObject &replyObject) override final { m_requestHelper->requestReply(replyObject); }
void onDeleteMessageReply(const Core::FunctionReplyObject &replyObject) override final { m_requestHelper->requestReply(replyObject); }
private:
void retranslate() override;
void tableColumnOrganiserSetting() override;
std::vector<int32_t> selectedNotificationIds(bool onlyNotYetAcknowledged);
NotificationManagementDialogModel *m_model;
Ui::NotificationManagementDialog *m_ui;
LtsSession::LtsSession *m_session;
std::shared_ptr<NotificationManagement::Interface::Dtc> m_dtc;
std::shared_ptr<NotificationManagement::Interface::Ftc> m_ftc;
std::shared_ptr<OperatorManagement::Interface::Dtc> m_dtcOpm;
std::shared_ptr<AssetManagement::Interface::Dtc> m_dtcAsm;
QString m_menuGroup;
LtsUtils::RequestHelper *m_requestHelper;
LOGGER_T m_logger;
};
} // namespace NotificationManagement
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NotificationManagementDialog</class>
<widget class="QWidget" name="NotificationManagementDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>600</width>
<height>500</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Exercise Management</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="LtsDataView::LtsDataView" name="widget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>250</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsDataView::LtsDataView</class>
<extends>QWidget</extends>
<header>LtsDataView/lts_data_view.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,240 @@
/**
* @RDE_HEADER@
* NotificationManagement - NomNotification Overview Dialog Model
*
* Created by Mike Lennart Wille, ESZS, 2026-06-17
*/
#include "nom_dialog_model.h"
#include <AssetManagement/asm_data_plugin_interface.h>
#include <LtsSession/lts_session_data.h>
#include <LtsUtilsCore/dds_vector.h>
#include <NotificationManagement/nom_data_plugin_interface.h>
#include <NotificationManagement/nom_translation.h>
#include <OperatorManagement/opm_data_plugin_interface.h>
#include <QCoreApplication>
// #include <QTimer>
namespace NotificationManagement
{
NotificationManagementDialogModel::NotificationManagementDialogModel(QObject *parent)
: LtsWidgets::LtsAbstractTableModel<NotificationManagement::NomNotification>(
{
tr("Id"),
tr("Title"),
tr("Timestamp"),
tr("ProcessedBy"),
tr("ProcessTime"),
tr("Priority"),
tr("Sender"), // Address
},
parent)
, m_dtc(nullptr)
, m_ftc(nullptr)
, m_logger(getLogger("NotificationManagementDialogModel"))
{
}
bool NotificationManagementDialogModel::initModel(const LtsSession::LtsSessionDataDds *session)
{
session->getDtc<NotificationManagement::DataInterface>(m_dtc);
session->getFtc<NotificationManagement::DataInterface>(m_ftc);
session->getDtc<OperatorManagement::DataInterface>(m_dtcOpm);
session->getDtc<AssetManagement::DataInterface>(m_dtcAsm);
m_exerciseId = session->exerciseId();
m_workstationId = LtsSessionData::LtsSessionData::getInstance()->getWorkstationId();
m_operatorId = LtsSessionData::LtsSessionData::getInstance()->getOperatorId();
connect(m_dtc.get(), &NotificationManagement::Interface::Dtc::nomNotificationChanged, this, &NotificationManagementDialogModel::onDataChanged);
m_dtc->nomNotifications(
[this](const NotificationManagement::NomNotification &noti)
{
if (!isFiltered(noti, DataModification::eAdded))
insertRow(noti);
});
INFO_LOG(m_logger) << "initModel - #notis: " << m_vecData.size();
return true;
}
int NotificationManagementDialogModel::getId(const NotificationManagement::NomNotification &data) const { return data.id(); }
QVariant NotificationManagementDialogModel::data(const QModelIndex &index, int role) const
{
if ((role != Qt::DisplayRole && role != Qt::UserRole && role != Qt::TextColorRole && role != Qt::FontRole) || !indexIsValid(index))
return QVariant();
const NotificationManagement::NomNotification &noti = m_vecData.at(index.row());
if (role == Qt::FontRole)
{
bool setBold = false;
for (const auto &addr : noti.recipientIds())
{
if (addr.addressType() == AddressType_def::Operator && addr.addressId() != m_operatorId)
continue;
if (addr.addressType() == AddressType_def::Workstation && addr.addressId() != m_workstationId)
continue;
if (!noti.processed())
setBold = true;
break;
}
if (setBold)
{
QFont font;
font.setBold(true);
return font;
}
}
if (role == Qt::DisplayRole)
{
switch (index.column())
{
case NotificationManagementDialogModel::Columns::Id:
return noti.id();
case NotificationManagementDialogModel::Columns::Title:
return QCoreApplication::translate("Notification", noti.title().c_str());
case NotificationManagementDialogModel::Columns::Content:
{
QString text = QCoreApplication::translate("Notification", noti.content().c_str());
for (const std::string& arg : noti.contentArgs()) {
// .arg() ersetzt immer den kleinsten freien Platzhalter (z. B. zuerst %1, dann %2)
text = text.arg(QString::fromStdString(arg));
}
return text;
}
case NotificationManagementDialogModel::Columns::Timestamp:
return QDateTime::fromMSecsSinceEpoch(noti.timeStamp());
case NotificationManagementDialogModel::Columns::Priority:
return NotificationManagement::toString(noti.priority().underlying());
case NotificationManagementDialogModel::Columns::ProcessedBy:
return getNameByAddressId(noti.processed(), noti.recipientIds());
case NotificationManagementDialogModel::Columns::ProcessTime:
return QDateTime::fromMSecsSinceEpoch(noti.processTime());
case NotificationManagementDialogModel::Columns::Sender:
return QString::fromStdString(noti.sender().name());
}
}
else if (role == Qt::TextColorRole)
{
if (index.column() == NotificationManagementDialogModel::Columns::Priority)
{
switch (noti.priority().underlying())
{
case NotificationManagement::Priority_def::Low:
return QColor(Qt::black);
case NotificationManagement::Priority_def::Normal:
return QColor(Qt::cyan);
case NotificationManagement::Priority_def::High:
return QColor(Qt::magenta);
case NotificationManagement::Priority::Highest:
return QColor(Qt::red);
}
}
}
return QVariant();
}
bool NotificationManagementDialogModel::isFiltered(const NotificationManagement::NomNotification &data, DataModification modType) const
{
if (modType == DataModification::eRemoved)
return false;
if (data.exerciseId() != m_exerciseId && data.exerciseId() != 0)
return true;
// Check Operator & workstation
bool isFiltered = true;
for (const auto &addr : data.recipientIds())
{
if (addr.addressType() == AddressType_def::Workstation)
if (addr.addressId() != m_workstationId)
continue;
if (addr.addressType() == AddressType_def::Operator)
if (addr.addressId() != m_operatorId)
continue;
isFiltered = false;
break;
}
return isFiltered;
}
int NotificationManagementDialogModel::isDeleted(const NomNotification &data) const
{
for (const auto &addr : data.recipientIds())
{
if (addr.addressType() == AddressType_def::Operator)
{
if (addr.addressId() != m_operatorId)
continue;
if (addr.isDeleted())
return true;
}
}
return false;
}
QString NotificationManagementDialogModel::getNameByAddressId(int addressId, const std::vector<NotificationManagement::Address> &recipients) const
{
for (const auto &addr : recipients)
{
if (addr.id() == addressId)
{
if (addr.addressType() == AddressType_def::Operator)
{
DEBUG_LOG(m_logger) << "addressId=" << addressId
<< ", addr.addressId()).name()=" << QString::fromStdString(m_dtcOpm->user(addr.addressId()).name());
return QString::fromStdString(m_dtcOpm->user(addr.addressId()).name());
}
if (addr.addressType() == AddressType_def::Workstation)
return QString::fromStdString(m_dtcAsm->asset(addr.addressId()).name());
}
}
return {};
}
//
// NotificationDialogProxyModel
//
void NotificationDialogProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
{
m_sourceModel = qobject_cast<NotificationManagementDialogModel *>(sourceModel);
LtsWidgets::LtsColumnFilterProxyModel::setSourceModel(sourceModel);
}
bool NotificationDialogProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
{
if (!m_sourceModel || source_left.column() != source_right.column() || !source_left.isValid() || !source_right.isValid())
return LtsWidgets::LtsColumnFilterProxyModel::lessThan(source_left, source_right);
const auto &dataLeft = m_sourceModel->m_vecData.at(source_left.row());
const auto &dataRight = m_sourceModel->m_vecData.at(source_right.row());
switch (source_left.column())
{
case NotificationManagementDialogModel::Timestamp:
return dataLeft.timeStamp() < dataRight.timeStamp();
case NotificationManagementDialogModel::Content:
case NotificationManagementDialogModel::isRead:
case NotificationManagementDialogModel::Priority:
case NotificationManagementDialogModel::ProcessTime:
case NotificationManagementDialogModel::ProcessedBy:
case NotificationManagementDialogModel::Sender:
case NotificationManagementDialogModel::Title:
break;
}
return LtsWidgets::LtsColumnFilterProxyModel::lessThan(source_left, source_right);
}
} // namespace NotificationManagement
@@ -0,0 +1,80 @@
/**
* @RDE_HEADER@
* NotificationManagement - NomNotification Overview Dialog Model
*
* Created by Mike Lennart Wille, ESZS, 2026-06-17
*/
#pragma once
#include <AssetManagement/asm_dtc.h>
#include <LtsSession/lts_session.h>
#include <LtsWidgets/lts_abstract_table_model.h>
#include <LtsWidgets/lts_column_filter_proxy_model.h>
#include <NotificationManagement/logger.h>
#include <NotificationManagement/nom_dtc.h>
#include <NotificationManagement/nom_ftc.h>
#include <OperatorManagement/opm_dtc.h>
namespace NotificationManagement
{
class NotificationManagementDialogModel final : public LtsWidgets::LtsAbstractTableModel<NotificationManagement::NomNotification>
{
Q_OBJECT
public:
enum Columns
{
Id,
Title,
Timestamp,
ProcessedBy,
ProcessTime,
Priority,
Sender, // Address
isRead, // myRecipients
Content,
};
explicit NotificationManagementDialogModel(QObject *parent);
bool initModel(const LtsSession::LtsSessionDataDds *session);
protected:
int getId(const NotificationManagement::NomNotification &data) const override final;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override final;
bool isFiltered(const NotificationManagement::NomNotification &data, DataModification modType) const override;
int isDeleted(const NotificationManagement::NomNotification &data) const override;
QString getNameByAddressId(int addressId, const std::vector<NotificationManagement::Address> &recipients) const;
private:
int m_exerciseId;
int m_workstationId;
int m_operatorId;
std::shared_ptr<NotificationManagement::Interface::Dtc> m_dtc;
std::shared_ptr<NotificationManagement::Interface::Ftc> m_ftc;
std::shared_ptr<OperatorManagement::Interface::Dtc> m_dtcOpm;
std::shared_ptr<AssetManagement::Interface::Dtc> m_dtcAsm;
LOGGER_T m_logger;
friend class NotificationDialogProxyModel;
};
class NotificationDialogProxyModel final : public LtsWidgets::LtsColumnFilterProxyModel
{
Q_OBJECT
public:
using LtsWidgets::LtsColumnFilterProxyModel::LtsColumnFilterProxyModel;
void setSourceModel(QAbstractItemModel *sourceModel) override;
protected:
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
private:
NotificationManagementDialogModel *m_sourceModel = nullptr;
};
} // namespace NotificationManagement
@@ -0,0 +1,390 @@
/**
* @RDE_HEADER@
* Gui Plugin for NotificationManangement
*
* Created by Mike Lennart Wille, ESZS, 2025-06-17
*/
#include "nom_edit_gui_plugin.h"
#include "nom_dialog.h"
#include <LtsNotification/lts_notification.h>
#include <LtsSession/lts_session.h>
#include <LtsSession/lts_session_data.h>
#include <LtsWidgets/lts_base_layout_widget.h>
#include <LtsWidgets/lts_events.h>
#include <NotificationManagement/nom_dtc.h>
#include <Services/AreaEffectSimulation/aes_operator_rights.h>
#include <QApplication>
#include <QDebug>
#include <QRegularExpression>
#include <QRegularExpressionMatch>
namespace NotificationManagement
{
std::shared_ptr<GuiPluginBase::Interface> GuiPlugin::createGuiInterface(LtsSession::LtsSession *session) { return std::make_shared<InterfaceImpl>(session); }
const QString InterfaceImpl::s_nomEditGuiPlugin = QStringLiteral("nomEditGuiPlugin");
InterfaceImpl::InterfaceImpl(LtsSession::LtsSession *session)
: GuiPluginBase::Interface(session, getLogger("Plugins.Gui.Nom.Edit"))
, m_noti(nullptr)
, m_requestHelper(new LtsUtils::RequestHelper(this))
{
qApp->installEventFilter(this);
}
InterfaceImpl::~InterfaceImpl() {}
int InterfaceImpl::isDeleted(const NomNotification &data) const
{
int operatorId = LtsSessionData::LtsSessionData::getInstance()->getOperatorId();
for (const auto &addr : data.recipientIds())
{
if (addr.addressType() == AddressType_def::Operator)
{
if (addr.addressId() != operatorId)
continue;
if (addr.isDeleted())
return true;
}
}
return false;
}
void InterfaceImpl::showNotificationList(int notificationId)
{
DEBUG_LOG(m_logger) << "showNotificationList notificationId=" << notificationId;
initNotificationSystem();
}
bool InterfaceImpl::eventFilter(QObject *watched, QEvent *event)
{
// DEBUG_LOG(m_logger) << "event->type()" << event->type();
if (watched == qApp && !event->isAccepted() && event->type() == LtsWidgets::LtsLinkEvent::eventTypeId())
{
DEBUG_LOG(m_logger) << "0 event->type()" << event->type();
}
auto ev = static_cast<LtsWidgets::LtsLinkEvent *>(event);
if (event->type() == LtsWidgets::LtsLinkEvent::eventTypeId())
{
DEBUG_LOG(m_logger) << "event->type() == LtsWidgets::LinkEvent::eventId()";
DEBUG_LOG(m_logger) << "ev->url()=" << ev->url().toString();
const auto ev = static_cast<LtsWidgets::LtsLinkEvent *>(event);
const auto identifier = ev->url().path().mid(1);
int id = 0;
QRegularExpression re(QStringLiteral("(\\d+)$"));
QRegularExpressionMatch match = re.match(identifier);
if (match.hasMatch())
{
id = match.captured(1).toInt();
}
DEBUG_LOG(m_logger) << "identifier=" << identifier << ", id=" << id;
if(ev->url().host().toLower() == QStringLiteral("notificationack") && id > 0)
{
DEBUG_LOG(m_logger) << "ack";
ack(m_nomNotifications.value(m_nomNotifications.value(id).id())); //hier eigentlich nicht
}
DEBUG_LOG(m_logger) << "ev->url().host()=" << ev->url().host();
if (ev->url().host().toLower() == s_nomEditGuiPlugin.toLower())
{
DEBUG_LOG(m_logger) << "ev->url().host()=" << ev->url().host();
m_session->addWidget(onShowNotificationManagementDialog(nullptr), menuGroup(), QStringLiteral("NotificationEdit"), LtsWidgets::LayoutArea::eDialogTab);
return true;
}
}
return QObject::eventFilter(watched, event);
}
void InterfaceImpl::onNotificationChanged(const NotificationManagement::NomNotification &noti, DataModification modType)
{
DEBUG_LOG(m_logger) << "onNotificationChanged title=" << noti.title()
<< ", content =" << noti.content()
<< ", args.size=" << noti.contentArgs().size()
<< ", modType=" << modType;
if (noti.exerciseId() != m_session->exerciseId())
return;
if (!isMyMessage(noti.recipientIds()))
return;
const QString notiIdent = QCoreApplication::translate("Notification", noti.title().c_str()) + QString::number(noti.id());
if (isDeleted(noti))
{
m_unreadMessages.remove(noti.id());
updateUnreadCount();
if (m_session->baseLayout()->messageIdentifierExists(notiIdent))
{
m_session->baseLayout()->removeQueuedMessage(notiIdent);
}
return;
}
NomNotification localNoti = noti;
QString content = QCoreApplication::translate("Notification", localNoti.content().c_str());
for (const std::string& arg : noti.contentArgs())
{
// .arg() ersetzt immer den kleinsten freien Platzhalter (z. B. zuerst %1, dann %2)
content = content.arg(QString::fromStdString(arg));
DEBUG_LOG(m_logger) << ".arg() ersetzt immer den kleinsten freien Platzhalter"
<< ", arg=" << arg
<< ", content=" << content;
}
localNoti.content(content.toStdString());
m_nomNotifications.insert(noti.id(), localNoti);
// When it's low, dont add it to the unread_message
if (noti.processed() <= 0 && (modType == DataModification::eAdded || modType == DataModification::eModified))
{
if (m_unreadMessages.contains(noti.id()) && noti.priority() < Priority::Normal)
{
m_unreadMessages.remove(noti.id());
}
if (!m_unreadMessages.contains(noti.id()))
{
m_unreadMessages.insert(noti.id());
QString content = QString::fromStdString(localNoti.content());
for (const std::string& arg : noti.contentArgs()) {
content = content.arg(QString::fromStdString(arg));
}
QMap<QString, QString> linkMap;
parseContent(content, linkMap);
// now replace the strings
QMapIterator<QString, QString> it(linkMap);
while (it.hasNext())
{
it.next();
content = content.replace(QStringLiteral("[%1](%2)").arg(it.value(), it.key()), QStringLiteral("<a href='%1'>%2</a>").arg(it.key(), it.value()));
}
const QString openMessageLink(QStringLiteral("OpenMessages"));
const LtsWidgets::LtsNotificationBox::MessageType messageType = static_cast<LtsWidgets::LtsNotificationBox::MessageType>(noti.messageType().underlying());
// If Priority is High add Notification for every user
if (noti.priority() == Priority::High)
{
m_session->baseLayout()->showMessage(
QCoreApplication::translate("Notification", noti.title().c_str()),
content,
messageType,
[this, linkMap, noti](const QString &link)
{
Q_UNUSED(link)
// Nothing to do here for now as links are already taken care of inside the Message see: LtsBaseLayoutWidget::showMessage
// do not try to ack already acked notification
if (!noti.processed())
{
DEBUG_LOG(m_logger) << "ack";
ack(noti); //hier nach neustart?
}
},
true,
notiIdent);
m_session->baseLayout()->installEventFilter(this);
}
else if (noti.priority() == Priority::Highest)
{
// if the proirity is highest parse the links and send them directly without user input
for (const auto &link : linkMap.keys())
{
auto *event = new LtsWidgets::LtsLinkEvent(QUrl(link), m_session->baseLayout());
event->setAccepted(false);
DEBUG_LOG(m_logger) << "Post event" << event << " " << event->url().toString();
QApplication::postEvent(qApp, event); // receive link events by overriding eventFilter of your *parented* QObject class
}
// do not try to ack already acked notification
if (!noti.processed())
{
DEBUG_LOG(m_logger) << "ack";
ack(noti); //oder hier?
}
}
}
}
if ((noti.processed() && (modType == DataModification::eModified)) || modType == DataModification::eRemoved)
{
m_unreadMessages.remove(noti.id()); // deletes nothing if there is no message with that id :)
// remove notification from user
if (m_session->baseLayout()->messageIdentifierExists(notiIdent))
{
m_session->baseLayout()->removeQueuedMessage(notiIdent);
}
}
updateUnreadCount();
// recalc notification icon counter
}
void InterfaceImpl::onReadReceiptReply(const Core::FunctionReplyObject &replyObject)
{
DEBUG_LOG(m_logger) << "Got ReadReceiptReply " << replyObject.translated() << " add info: " << replyObject.additionalInfo();
}
void InterfaceImpl::initNotificationSystem()
{
if (m_noti != nullptr)
return;
m_noti = m_session->createNotification(QStringLiteral("nom.Notifications"));
m_noti->setIcon(QIcon(QStringLiteral("icon:messages_notif.svg")));
m_noti->setToolTip(tr("No unread notifications"));
m_noti->setAction(
[this]()
{ m_session->addWidget(onShowNotificationManagementDialog(nullptr), menuGroup(), QStringLiteral("NotificationEdit"), LtsWidgets::LayoutArea::eDialogTab); });
connect(m_dtc.get(), &NotificationManagement::Interface::Dtc::nomNotificationChanged, this, &InterfaceImpl::onNotificationChanged, Qt::UniqueConnection);
m_dtc->enableNomNotification();
}
void InterfaceImpl::updateUnreadCount()
{
if (m_noti == nullptr)
return;
m_noti->setCount(m_unreadMessages.count());
m_noti->setToolTip(tr("%Ln unread message(s)", "", m_unreadMessages.count()));
}
bool InterfaceImpl::parseContent(const QString &contentString, QMap<QString, QString> &retMap)
{
if (contentString.isEmpty())
return false;
// Mark Down Regex lookig for String between ]( and ) eg: TestLingk "[LinkName](http::blahblah)"
QRegularExpression regLink(QStringLiteral("(?<=\\]\\()(.*?)(?=\\))"));
QRegularExpressionMatch matchLink = regLink.match(contentString);
QRegularExpression regDesc(QStringLiteral("(?<=\\[)(.*?)(?=\\]\\()"));
QRegularExpressionMatch matchDesc = regDesc.match(contentString);
QRegularExpressionMatchIterator itLink = regLink.globalMatch(contentString);
QRegularExpressionMatchIterator itDesc = regDesc.globalMatch(contentString);
while (itLink.hasNext() && itDesc.hasNext())
{
QString linkRef = itLink.next().captured();
QString linkDesc = itDesc.next().captured();
if (!linkRef.isEmpty() && !linkDesc.isEmpty())
{
retMap.insert(linkRef, linkDesc);
}
}
return retMap.size();
}
QVector<LtsWidgets::PluginData> InterfaceImpl::getActions()
{
QVector<LtsWidgets::PluginData> ret;
{
LtsWidgets::PluginData plData;
plData.menuGroup = menuGroup();
plData.menuText = tr("Notifications");
plData.menuIcon = QIcon(QLatin1String("icon:messages_notif.svg"));
plData.identifier = QLatin1String("NotificationEdit");
plData.operatorRight = OperatorRights::NotificationEdit;
plData.exercisePart = LtsWidgets::ExercisePart::eSpecial;
plData.createWidgetFunc = [this](QWidget *parent) { return onShowNotificationManagementDialog(parent); };
ret.append(plData);
}
// Init System without button
initNotificationSystem();
return ret;
}
bool InterfaceImpl::fetchInterfaces()
{
if (!m_session->getDtc<NotificationManagement::DataInterface>(m_dtc))
return false;
if (!m_session->getFtc<NotificationManagement::DataInterface>(m_ftc))
return false;
return true;
}
void InterfaceImpl::clearInterfaces()
{
m_dtc = nullptr;
m_ftc = nullptr;
m_dtcOpm = nullptr;
}
LtsWidgets::LtsDialog *InterfaceImpl::onShowNotificationManagementDialog(QWidget *parent)
{
auto *dialog = new NotificationManagementDialog(m_session, menuGroup(), parent);
return dialog;
}
void InterfaceImpl::ack(const NotificationManagement::NomNotification &notification)
{
ReadReceipt request;
int operatorId = LtsSessionData::LtsSessionData::getInstance()->getOperatorId();
int workstationId = LtsSessionData::LtsSessionData::getInstance()->getWorkstationId();
for (const auto &addr : notification.recipientIds())
{
if ((addr.addressType() == AddressType_def::Operator && addr.addressId() == operatorId)
|| (addr.addressType() == AddressType_def::Workstation && addr.addressId() == workstationId))
{
request.addressId(addr.id());
break;
}
}
request.operatorId(operatorId);
request.workstationId(workstationId);
request.notificationIds({notification.id()});
request.processed(!notification.processed());
DEBUG_LOG(m_logger) << "m_ftc->readReceipt ";
m_ftc->readReceiptRequest(request, this);
}
bool InterfaceImpl::isMyMessage(const std::vector<Address> &recipents)
{
if (recipents.empty())
return false;
using Iter = std::vector<Address>::const_iterator;
Iter it = recipents.begin();
while (it != recipents.end())
{
if (it->addressType() == AddressType::Operator && it->addressId() == LtsSessionData::LtsSessionData::getInstance()->getOperatorId())
{
return true;
}
else if (it->addressType() == AddressType::Workstation && it->addressId() == LtsSessionData::LtsSessionData::getInstance()->getWorkstationId())
{
return true;
}
++it;
}
return false;
}
} // namespace NotificationManagement
@@ -0,0 +1,89 @@
/**
* @RDE_HEADER@
* Gui Plugin for NotificationManangement
*
* Created by Mike Lennart Wille, ESZS, 2025-06-17
*/
#pragma once
#include <NotificationManagement/logger.h>
#include <LtsUtilsGuiDds/request_helper.h>
#include <OperatorManagement/opm_dtc.h>
#include <PluginInterfaces/gui_plugin_interface.h>
#include <Plugins/NotificationManagement/nom_data_plugin_interface.h>
#include <Plugins/NotificationManagement/nom_gui_plugin_interface.h>
namespace LtsNotification
{
class LtsNotification;
}
namespace NotificationManagement
{
class GuiPlugin final
: public QObject
, public GuiPluginBase::Plugin
{
Q_PLUGIN_METADATA(IID "gui:3:nom.edit")
Q_INTERFACES(GuiPluginBase::Plugin)
Q_OBJECT
public:
explicit GuiPlugin() = default;
~GuiPlugin() = default;
QString pluginName() override { return QLatin1String("NotificationManagementGuiPlugin"); }
std::shared_ptr<GuiPluginBase::Interface> createGuiInterface(LtsSession::LtsSession *session) override;
bool availableInPartition(IDL::PartitionType partition) const override { return partition == IDL::PartitionType::Global || partition == IDL::PartitionType::Exercise; }
};
class InterfaceImpl final
: public QObject
, public GuiPluginBase::Interface
, protected NotificationManagement::Interface::Ftc::ReadReceiptReplyHandler
{
Q_OBJECT
public:
explicit InterfaceImpl(LtsSession::LtsSession *session);
~InterfaceImpl();
static const QString s_nomEditGuiPlugin;
QVector<LtsWidgets::PluginData> getActions() override;
QString menuGroup() const override { return tr("Notifications"); }
bool fetchInterfaces() override;
void clearInterfaces() override;
bool availableInPartition(IDL::PartitionType partition) const override { return partition == IDL::PartitionType::Global || partition == IDL::PartitionType::Exercise; }
static const QString s_participantInfoScheme;
protected:
void initNotificationSystem();
void onNotificationChanged(const NotificationManagement::NomNotification &noti, DataModification modType);
void onReadReceiptReply(const Core::FunctionReplyObject &replyObject) override final;
private:
int isDeleted(const NomNotification &data) const;
LtsWidgets::LtsDialog *onShowNotificationManagementDialog(QWidget *parent);
void ack(const NotificationManagement::NomNotification &notification);
void updateUnreadCount();
bool parseContent(const QString &contentString, QMap<QString, QString> &retMap);
bool isMyMessage(const std::vector<NotificationManagement::Address> &recipents);
bool eventFilter(QObject *watched, QEvent *event) override;
void showNotificationList(int notificationId);
QSet<int> m_unreadMessages;
LtsNotification::LtsNotification *m_noti;
QHash<int, NomNotification> m_nomNotifications;
LtsUtils::RequestHelper *m_requestHelper;
std::shared_ptr<NotificationManagement::Interface::Dtc> m_dtc;
std::shared_ptr<NotificationManagement::Interface::Ftc> m_ftc;
std::shared_ptr<OperatorManagement::Interface::Dtc> m_dtcOpm;
std::shared_ptr<NotificationManagement::ForeignGuiInterface> m_nomGuiInterface;
};
} // namespace NotificationManagement