diff --git a/retroshare-gui/src/CMakeLists.txt b/retroshare-gui/src/CMakeLists.txt index d2025ae9c..2698c23e1 100644 --- a/retroshare-gui/src/CMakeLists.txt +++ b/retroshare-gui/src/CMakeLists.txt @@ -1052,6 +1052,7 @@ if(RS_GXSIDENTITIES) list( APPEND RS_IMPLEMENTATION_HEADERS src/gui/Identity/IdDialog.h + src/gui/Identity/BannedIdentitiesDialog.h src/gui/Identity/IdEditDialog.h src/gui/Identity/IdDetailsDialog.h src/gui/Identity/IdentityListModel.h @@ -1066,6 +1067,7 @@ if(RS_GXSIDENTITIES) list( APPEND RS_GUI_SOURCES + src/gui/Identity/BannedIdentitiesDialog.cpp src/gui/Identity/IdDialog.cpp src/gui/Identity/IdEditDialog.cpp src/gui/Identity/IdDetailsDialog.cpp diff --git a/retroshare-gui/src/gui/Identity/BannedIdentitiesDialog.cpp b/retroshare-gui/src/gui/Identity/BannedIdentitiesDialog.cpp new file mode 100644 index 000000000..6b7b01f51 --- /dev/null +++ b/retroshare-gui/src/gui/Identity/BannedIdentitiesDialog.cpp @@ -0,0 +1,156 @@ +#include "BannedIdentitiesDialog.h" + +#include "retroshare/rsidentity.h" +#include "retroshare/rsreputations.h" +#include "util/DateTime.h" +#include "util/misc.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ +enum Columns : int +{ + COLUMN_ID = 0, + COLUMN_NICKNAME, + COLUMN_IN_LOCAL_DB, + COLUMN_BANNED_SINCE, + COLUMN_LAST_SEEN, + COLUMN_COUNT +}; +} + +BannedIdentitiesDialog::BannedIdentitiesDialog(QWidget* parent) + : QDialog(parent), mTableWidget(nullptr), mRefreshButton(nullptr), + mUnbanButton(nullptr), mCloseButton(nullptr) +{ + setupUi(); + refreshList(); +} + +void BannedIdentitiesDialog::setupUi() +{ + setWindowTitle(tr("Banned identities")); + resize(860, 420); + + auto* mainLayout = new QVBoxLayout(this); + + mTableWidget = new QTableWidget(this); + mTableWidget->setColumnCount(COLUMN_COUNT); + mTableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); + mTableWidget->setSelectionMode(QAbstractItemView::ExtendedSelection); + mTableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); + mTableWidget->setAlternatingRowColors(true); + mTableWidget->setSortingEnabled(true); + mTableWidget->setHorizontalHeaderLabels( + { tr("Identity ID"), tr("Nickname"), tr("In local DB"), + tr("Banned since"), tr("Last seen") }); + mTableWidget->horizontalHeader()->setSectionResizeMode( + COLUMN_ID, QHeaderView::Stretch ); + mTableWidget->horizontalHeader()->setSectionResizeMode( + COLUMN_NICKNAME, QHeaderView::Stretch ); + mTableWidget->horizontalHeader()->setSectionResizeMode( + COLUMN_IN_LOCAL_DB, QHeaderView::ResizeToContents ); + mTableWidget->horizontalHeader()->setSectionResizeMode( + COLUMN_BANNED_SINCE, QHeaderView::ResizeToContents ); + mTableWidget->horizontalHeader()->setSectionResizeMode( + COLUMN_LAST_SEEN, QHeaderView::ResizeToContents ); + mainLayout->addWidget(mTableWidget); + + auto* buttonsLayout = new QHBoxLayout(); + mRefreshButton = new QPushButton(tr("Refresh"), this); + mUnbanButton = new QPushButton(tr("Unban selected"), this); + mCloseButton = new QPushButton(tr("Close"), this); + buttonsLayout->addWidget(mRefreshButton); + buttonsLayout->addWidget(mUnbanButton); + buttonsLayout->addStretch(1); + buttonsLayout->addWidget(mCloseButton); + mainLayout->addLayout(buttonsLayout); + + connect(mRefreshButton, &QPushButton::clicked, + this, &BannedIdentitiesDialog::refreshList); + connect(mUnbanButton, &QPushButton::clicked, + this, &BannedIdentitiesDialog::unbanSelected); + connect(mCloseButton, &QPushButton::clicked, this, &QDialog::accept); +} + +void BannedIdentitiesDialog::setRowData( + int row, const RsGxsId& id, bool inLocalDb, const QString& nickname, + const QString& bannedSince, const QString& lastSeen ) +{ + auto* idItem = new QTableWidgetItem(QString::fromStdString(id.toStdString())); + idItem->setData(Qt::UserRole, QString::fromStdString(id.toStdString())); + mTableWidget->setItem(row, COLUMN_ID, idItem); + mTableWidget->setItem(row, COLUMN_NICKNAME, new QTableWidgetItem(nickname)); + mTableWidget->setItem( + row, COLUMN_IN_LOCAL_DB, new QTableWidgetItem(inLocalDb ? tr("Yes") : tr("No")) ); + mTableWidget->setItem(row, COLUMN_BANNED_SINCE, new QTableWidgetItem(bannedSince)); + mTableWidget->setItem(row, COLUMN_LAST_SEEN, new QTableWidgetItem(lastSeen)); +} + +QString BannedIdentitiesDialog::relativeTimeString(rstime_t ts) const +{ + if(ts == 0) return tr("Unknown"); + + const rstime_t now = time(nullptr); + if(ts > now) return tr("In the future"); + + return DateTime::formatLongDateTime(ts) + + tr(" (%1 ago)").arg(misc::userFriendlyDuration(now - ts)); +} + +void BannedIdentitiesDialog::refreshList() +{ + std::vector bannedIds; + rsReputations->getLocallyBannedIdentities(bannedIds); + + mTableWidget->setSortingEnabled(false); + mTableWidget->setRowCount(static_cast(bannedIds.size())); + + for(size_t i = 0; i < bannedIds.size(); ++i) + { + const RsBannedIdentityInfo& info(bannedIds[i]); + RsIdentityDetails details; + const bool inLocalDb = rsIdentity->getIdDetails(info.mId, details); + const QString nickname = inLocalDb + ? QString::fromUtf8(details.mNickname.c_str()) : tr(""); + + setRowData( + static_cast(i), info.mId, inLocalDb, nickname, + relativeTimeString(info.mOwnOpinionTs), + relativeTimeString(info.mLastUsedTS) ); + } + + mTableWidget->setSortingEnabled(true); + mTableWidget->sortByColumn(COLUMN_BANNED_SINCE, Qt::DescendingOrder); +} + +void BannedIdentitiesDialog::unbanSelected() +{ + const auto selected = mTableWidget->selectionModel()->selectedRows(COLUMN_ID); + if(selected.empty()) + { + QMessageBox::information( + this, tr("No selection"), + tr("Select one or more identities to unban.") ); + return; + } + + for(const QModelIndex& idx : selected) + { + const QString idStr = mTableWidget->item(idx.row(), COLUMN_ID)->data(Qt::UserRole).toString(); + const RsGxsId id(idStr.toStdString()); + rsReputations->setOwnOpinion(id, RsOpinion::NEUTRAL); + } + + refreshList(); +} diff --git a/retroshare-gui/src/gui/Identity/BannedIdentitiesDialog.h b/retroshare-gui/src/gui/Identity/BannedIdentitiesDialog.h new file mode 100644 index 000000000..d5f0d5a93 --- /dev/null +++ b/retroshare-gui/src/gui/Identity/BannedIdentitiesDialog.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include "retroshare/rsids.h" +#include "util/rstime.h" + +class QTableWidget; +class QPushButton; + +class BannedIdentitiesDialog : public QDialog +{ +public: + explicit BannedIdentitiesDialog(QWidget* parent = nullptr); + +private: + void refreshList(); + void unbanSelected(); + void setupUi(); + void setRowData(int row, const RsGxsId& id, bool inLocalDb, const QString& nickname, + const QString& bannedSince, const QString& lastSeen); + QString relativeTimeString(rstime_t ts) const; + + QTableWidget* mTableWidget; + QPushButton* mRefreshButton; + QPushButton* mUnbanButton; + QPushButton* mCloseButton; +}; diff --git a/retroshare-gui/src/gui/Identity/IdDialog.cpp b/retroshare-gui/src/gui/Identity/IdDialog.cpp index 9b0b0a0bd..239730bb4 100644 --- a/retroshare-gui/src/gui/Identity/IdDialog.cpp +++ b/retroshare-gui/src/gui/Identity/IdDialog.cpp @@ -31,6 +31,7 @@ #include "IdDialog.h" #include "ui_IdDialog.h" +#include "BannedIdentitiesDialog.h" #include "IdEditDialog.h" #include "IdentityListModel.h" @@ -392,9 +393,18 @@ IdDialog::IdDialog(QWidget *parent) QAction *CreateCircleAction = new QAction(FilesDefs::getIconFromQtResourcePath(":/icons/png/circles.png"),tr("Create new circle"), this); connect(CreateCircleAction, SIGNAL(triggered()), this, SLOT(createExternalCircle())); + QAction *ShowBannedIdentitiesAction = new QAction( + FilesDefs::getIconFromQtResourcePath(":/icons/png/thumbs-down.png"), + tr("Show banned identities"), this ); + connect( + ShowBannedIdentitiesAction, SIGNAL(triggered()), + this, SLOT(openBannedIdentitiesDialog()) ); + QMenu *menu = new QMenu(); menu->addAction(CreateIDAction); menu->addAction(CreateCircleAction); + menu->addSeparator(); + menu->addAction(ShowBannedIdentitiesAction); ui->toolButton_New->setMenu(menu); QFontMetricsF fm(ui->idTreeWidget->font()) ; @@ -1047,6 +1057,13 @@ void IdDialog::createExternalCircle() dlg.editNewId(true); dlg.exec(); } + +void IdDialog::openBannedIdentitiesDialog() +{ + BannedIdentitiesDialog dlg(this); + dlg.exec(); +} + void IdDialog::showEditExistingCircle() { RsGxsCircleId id ; diff --git a/retroshare-gui/src/gui/Identity/IdDialog.h b/retroshare-gui/src/gui/Identity/IdDialog.h index 1d35390ae..f222c65bb 100644 --- a/retroshare-gui/src/gui/Identity/IdDialog.h +++ b/retroshare-gui/src/gui/Identity/IdDialog.h @@ -91,6 +91,7 @@ private slots: void filterToggled(const bool &value); void addIdentity(); + void openBannedIdentitiesDialog(); void removeIdentity(); void editIdentity(); void chatIdentity(); diff --git a/retroshare-gui/src/retroshare-gui.pro b/retroshare-gui/src/retroshare-gui.pro index cdb9cef3d..d27c4b751 100644 --- a/retroshare-gui/src/retroshare-gui.pro +++ b/retroshare-gui/src/retroshare-gui.pro @@ -1273,6 +1273,7 @@ gxsthewire { identities { HEADERS += \ + gui/Identity/BannedIdentitiesDialog.h \ gui/Identity/IdDialog.h \ gui/Identity/IdentityListModel.h \ gui/Identity/IdEditDialog.h \ @@ -1283,6 +1284,7 @@ identities { gui/Identity/IdDetailsDialog.ui \ SOURCES += \ + gui/Identity/BannedIdentitiesDialog.cpp \ gui/Identity/IdDialog.cpp \ gui/Identity/IdentityListModel.cpp \ gui/Identity/IdEditDialog.cpp \