File indexing completed on 2024-05-12 05:04:11

0001 // SPDX-FileCopyrightText: 2023 Rishi Kumar <rsi.dev17@gmail.com>
0002 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0003 
0004 #include "reporttoolmodel.h"
0005 
0006 #include "abstractaccount.h"
0007 #include "accountmanager.h"
0008 
0009 #include <KLocalizedString>
0010 
0011 using namespace Qt::StringLiterals;
0012 
0013 ReportToolModel::ReportToolModel(QObject *parent)
0014     : QAbstractListModel(parent)
0015 {
0016     fillTimeline();
0017     fetchSelectedAccountDetails();
0018 }
0019 
0020 bool ReportToolModel::loading() const
0021 {
0022     return m_loading;
0023 }
0024 
0025 void ReportToolModel::setLoading(bool loading)
0026 {
0027     if (m_loading == loading) {
0028         return;
0029     }
0030     m_loading = loading;
0031     Q_EMIT loadingChanged();
0032 }
0033 
0034 QUrlQuery ReportToolModel::buildQuery() const
0035 {
0036     QUrlQuery query;
0037     if (m_moderationStatus == QStringLiteral("resolved")) {
0038         query.addQueryItem(QStringLiteral("resolved"), QStringLiteral("true"));
0039     }
0040     if (!m_accountId.isEmpty()) {
0041         query.addQueryItem(QStringLiteral("account_id"), m_accountId);
0042     }
0043     if (!m_targetAccountId.isEmpty()) {
0044         query.addQueryItem(QStringLiteral("target_account_id"), m_targetAccountId);
0045     }
0046     return query;
0047 }
0048 
0049 QVariant ReportToolModel::data(const QModelIndex &index, int role) const
0050 {
0051     Q_ASSERT(checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid));
0052 
0053     const auto identity = m_reports[index.row()].get();
0054     switch (role) {
0055     case CustomRoles::ReportRole:
0056         return QVariant::fromValue<ReportInfo *>(identity);
0057     default:
0058         return {};
0059     }
0060 }
0061 
0062 int ReportToolModel::rowCount(const QModelIndex &) const
0063 {
0064     return m_reports.count();
0065 }
0066 
0067 QHash<int, QByteArray> ReportToolModel::roleNames() const
0068 {
0069     return {
0070         {CustomRoles::ReportRole, "reportInfo"},
0071     };
0072 }
0073 
0074 QString ReportToolModel::moderationStatus() const
0075 {
0076     return m_moderationStatus;
0077 }
0078 
0079 void ReportToolModel::setModerationStatus(const QString &moderationStatus)
0080 {
0081     if (moderationStatus == m_moderationStatus) {
0082         return;
0083     }
0084     m_moderationStatus = moderationStatus;
0085     Q_EMIT moderationStatusChanged();
0086     m_pagination = false;
0087     clear();
0088     fillTimeline();
0089 }
0090 
0091 QString ReportToolModel::origin() const
0092 {
0093     return m_origin;
0094 }
0095 
0096 void ReportToolModel::setOrigin(const QString &origin)
0097 {
0098     if (origin == m_origin) {
0099         return;
0100     }
0101     m_origin = origin;
0102     Q_EMIT moderationStatusChanged();
0103     m_pagination = false;
0104     clear();
0105     fillTimeline();
0106 }
0107 
0108 void ReportToolModel::clear()
0109 {
0110     beginResetModel();
0111     m_reports.clear();
0112     endResetModel();
0113     setLoading(false);
0114 }
0115 
0116 void ReportToolModel::resolveReport(const int row)
0117 {
0118     executeReportAction(row, ReportAction::ResolveReport);
0119 }
0120 
0121 void ReportToolModel::unresolveReport(const int row)
0122 {
0123     executeReportAction(row, ReportAction::UnresolveReport);
0124 }
0125 
0126 void ReportToolModel::assignReport(const int row)
0127 {
0128     executeReportAction(row, ReportAction::AssignReport);
0129 }
0130 
0131 void ReportToolModel::unassignReport(const int row)
0132 {
0133     executeReportAction(row, ReportAction::UnassignReport);
0134 }
0135 
0136 void ReportToolModel::updateReport(const int row, const QString &type, const QList<int> &ruleIds)
0137 {
0138     QJsonArray ruleIdArray;
0139     for (const int ruleId : ruleIds) {
0140         ruleIdArray.append(ruleId);
0141     }
0142     QJsonObject obj{{"category"_L1, type}, {"rule_ids"_L1, ruleIdArray}};
0143     const auto doc = QJsonDocument(obj);
0144     const auto account = AccountManager::instance().selectedAccount();
0145     const auto report = m_reports[row];
0146     const auto reportId = report->reportId();
0147     QUrl url = account->apiUrl(QStringLiteral("/api/v1/admin/reports/%1").arg(reportId));
0148     account->put(url, doc, true, this, [=](QNetworkReply *) {});
0149     Q_EMIT dataChanged(index(row, 0), index(row, 0));
0150 }
0151 
0152 void ReportToolModel::fetchSelectedAccountDetails()
0153 {
0154     auto account = AccountManager::instance().selectedAccount();
0155 
0156     const auto id = account->identity()->id();
0157 
0158     QUrl url = account->apiUrl(QStringLiteral("/api/v1/admin/accounts/%1").arg(id));
0159 
0160     account->get(url, true, this, [this, account, id](QNetworkReply *reply) {
0161         const auto doc = QJsonDocument::fromJson(reply->readAll());
0162         m_selectedAccount = account->adminIdentityLookup(id, doc.object());
0163     });
0164     Q_EMIT moderationStatusChanged();
0165 }
0166 
0167 void ReportToolModel::executeReportAction(const int row, ReportAction reportAction, const QJsonObject &extraArguments)
0168 {
0169     auto report = m_reports[row];
0170     const QHash<ReportAction, QString> reportActionMap = {
0171         {ReportAction::ResolveReport, QStringLiteral("/resolve")},
0172         {ReportAction::UnresolveReport, QStringLiteral("/reopen")},
0173         {ReportAction::AssignReport, QStringLiteral("/assign_to_self")},
0174         {ReportAction::UnassignReport, QStringLiteral("/unassign")},
0175     };
0176 
0177     const auto apiCall = reportActionMap[reportAction];
0178 
0179     const auto reportId = report->reportId();
0180 
0181     const QString reportApiUrl = QStringLiteral("/api/v1/admin/reports/%1/%2").arg(reportId, apiCall);
0182 
0183     const QJsonDocument doc(extraArguments);
0184 
0185     auto account = AccountManager::instance().selectedAccount();
0186     QUrl url = account->apiUrl(reportApiUrl);
0187 
0188     account->post(url, doc, true, this, [=](QNetworkReply *reply) {
0189         auto doc = QJsonDocument::fromJson(reply->readAll());
0190         auto jsonObj = doc.object();
0191 
0192         if (!jsonObj.value("error"_L1).isUndefined()) {
0193             const QHash<ReportAction, QString> accountActionMap = {
0194                 {ReportAction::ResolveReport, i18n("Could not resolve report")},
0195                 {ReportAction::UnresolveReport, i18n("Could not unresolve report")},
0196                 {ReportAction::AssignReport, i18n("Could not assign report")},
0197                 {ReportAction::UnassignReport, i18n("Could not unassign report")},
0198             };
0199             const auto errorMessage = accountActionMap[reportAction];
0200             Q_EMIT account->errorOccured(errorMessage);
0201             return;
0202         }
0203 
0204         switch (reportAction) {
0205         case ReportAction::ResolveReport:
0206             report->setActionTaken(true);
0207             break;
0208         case ReportAction::UnresolveReport:
0209             report->setActionTaken(false);
0210             break;
0211         case ReportAction::AssignReport:
0212             report->setAssignedModerator(true);
0213             report->setAssignedAccount(m_selectedAccount.get());
0214             break;
0215         case ReportAction::UnassignReport:
0216             report->setAssignedModerator(false);
0217             report->setAssignedAccount({});
0218             break;
0219         }
0220         Q_EMIT dataChanged(index(row, 0), index(row, 0));
0221     });
0222 }
0223 
0224 bool ReportToolModel::canFetchMore(const QModelIndex &parent) const
0225 {
0226     Q_UNUSED(parent);
0227     return !m_next.isEmpty() && m_pagination;
0228 }
0229 
0230 void ReportToolModel::fetchMore(const QModelIndex &parent)
0231 {
0232     Q_UNUSED(parent);
0233 
0234     fillTimeline();
0235 }
0236 
0237 void ReportToolModel::fillTimeline()
0238 {
0239     auto account = AccountManager::instance().selectedAccount();
0240     m_pagination = true;
0241 
0242     if (m_loading) {
0243         return;
0244     }
0245     setLoading(true);
0246 
0247     QUrl url;
0248     if (m_next.isEmpty()) {
0249         url = account->apiUrl(QStringLiteral("/api/v1/admin/reports"));
0250     } else {
0251         url = m_next;
0252     }
0253     url.setQuery(buildQuery());
0254 
0255     account->get(url, true, this, [this, account](QNetworkReply *reply) {
0256         const auto doc = QJsonDocument::fromJson(reply->readAll());
0257         const auto reportsArray = doc.array();
0258 
0259         if (!reportsArray.isEmpty()) {
0260             static QRegularExpression re(QStringLiteral("<(.*)>; rel=\"next\""));
0261             const auto next = reply->rawHeader(QByteArrayLiteral("Link"));
0262             const auto match = re.match(QString::fromUtf8(next));
0263             if (re.isValid()) {
0264                 m_next = QUrl::fromUserInput(match.captured(1));
0265             }
0266             QList<std::shared_ptr<ReportInfo>> fetchedReports;
0267 
0268             std::transform(
0269                 reportsArray.cbegin(),
0270                 reportsArray.cend(),
0271                 std::back_inserter(fetchedReports),
0272                 [ account, this ](const QJsonValue &value) -> auto{
0273                     const auto reportInfoJson = value.toObject();
0274                     const auto accountPopulate = account->reportInfoLookup(reportInfoJson["id"_L1].toString(), reportInfoJson);
0275                     // hack to determine the report's origin to be removed when we have the specific query for it
0276                     if (m_origin == QStringLiteral("local") && accountPopulate->targetAccount()->isLocal()) {
0277                         return accountPopulate;
0278                     } else if (m_origin == QStringLiteral("remote") && !accountPopulate->targetAccount()->isLocal()) {
0279                         return accountPopulate;
0280                     } else if (m_origin.isEmpty()) {
0281                         return accountPopulate;
0282                     }
0283                     return std::shared_ptr<ReportInfo>();
0284                 });
0285             beginInsertRows({}, m_reports.size(), m_reports.size() + fetchedReports.size() - 1);
0286             m_reports += fetchedReports;
0287             endInsertRows();
0288         }
0289         setLoading(false);
0290     });
0291 }
0292 
0293 #include "moc_reporttoolmodel.cpp"