File indexing completed on 2024-05-12 16:28:06

0001 // SPDX-FileCopyrightText: 2023 Rishi Kumar <rsi.dev17@gmail.com>
0002 // SPDX-License-Identifier: GPL-3.0-only
0003 
0004 #include "admin/accounttoolmodel.h"
0005 
0006 #include "account/abstractaccount.h"
0007 #include "account/accountmanager.h"
0008 #include "account/relationship.h"
0009 #include <KLocalizedString>
0010 #include <QFile>
0011 #include <QJsonDocument>
0012 #include <QNetworkReply>
0013 #include <QUrlQuery>
0014 #include <qstringliteral.h>
0015 
0016 AccountsToolModel::AccountsToolModel(QObject *parent)
0017     : QAbstractListModel(parent)
0018 {
0019     fillTimeline();
0020     fetchSelectedAccountPosition();
0021 }
0022 
0023 bool AccountsToolModel::loading() const
0024 {
0025     return m_loading;
0026 }
0027 
0028 void AccountsToolModel::setLoading(bool loading)
0029 {
0030     if (m_loading == loading) {
0031         return;
0032     }
0033     m_loading = loading;
0034     Q_EMIT loadingChanged();
0035 }
0036 
0037 QUrlQuery AccountsToolModel::buildQuery() const
0038 {
0039     QUrlQuery query;
0040     query.addQueryItem("origin", m_location);
0041     query.addQueryItem("status", m_moderationStatus);
0042     query.addQueryItem("role_ids", m_role);
0043     query.addQueryItem("username", m_username);
0044     query.addQueryItem("display_name", m_displayName);
0045     query.addQueryItem("email", m_email);
0046     query.addQueryItem("ip", m_ip);
0047     return query;
0048 }
0049 
0050 QVariant AccountsToolModel::data(const QModelIndex &index, int role) const
0051 {
0052     Q_ASSERT(checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid));
0053 
0054     const auto identity = m_accounts[index.row()].get();
0055     switch (role) {
0056     case CustomRoles::IdentityRole:
0057         return QVariant::fromValue<AdminAccountInfo *>(identity);
0058     default:
0059         return {};
0060     }
0061 }
0062 
0063 int AccountsToolModel::rowCount(const QModelIndex &) const
0064 {
0065     return m_accounts.count();
0066 }
0067 
0068 QHash<int, QByteArray> AccountsToolModel::roleNames() const
0069 {
0070     return {
0071         {CustomRoles::IdentityRole, "identity"},
0072     };
0073 }
0074 
0075 QString AccountsToolModel::location() const
0076 {
0077     return m_location;
0078 }
0079 
0080 void AccountsToolModel::setLocation(const QString &location)
0081 {
0082     if (location == m_location) {
0083         return;
0084     }
0085     m_location = location;
0086     Q_EMIT locationChanged();
0087     // resetting everything before populating
0088     m_pagination = false;
0089     clear();
0090     fillTimeline();
0091 }
0092 
0093 QString AccountsToolModel::moderationStatus() const
0094 {
0095     return m_location;
0096 }
0097 
0098 void AccountsToolModel::setModerationStatus(const QString &moderationStatus)
0099 {
0100     if (moderationStatus == m_moderationStatus) {
0101         return;
0102     }
0103     m_moderationStatus = moderationStatus;
0104     Q_EMIT moderationStatusChanged();
0105     // resetting everything before populating
0106     m_pagination = false;
0107     clear();
0108     fillTimeline();
0109 }
0110 
0111 QString AccountsToolModel::role() const
0112 {
0113     return m_role;
0114 }
0115 
0116 void AccountsToolModel::setRole(const QString &role)
0117 {
0118     if (role == m_role) {
0119         return;
0120     }
0121     m_role = role;
0122     Q_EMIT roleChanged();
0123     // resetting everything before populating
0124     m_pagination = false;
0125     clear();
0126     fillTimeline();
0127 }
0128 
0129 QString AccountsToolModel::username() const
0130 {
0131     return m_username;
0132 }
0133 
0134 void AccountsToolModel::setUsername(const QString &username)
0135 {
0136     if (username == m_username) {
0137         return;
0138     }
0139     m_username = username;
0140     Q_EMIT usernameChanged();
0141     // resetting everything before populating
0142     m_pagination = false;
0143     clear();
0144     fillTimeline();
0145 }
0146 
0147 QString AccountsToolModel::displayName() const
0148 {
0149     return m_displayName;
0150 }
0151 
0152 void AccountsToolModel::setDisplayName(const QString &displayName)
0153 {
0154     if (displayName == m_displayName) {
0155         return;
0156     }
0157     m_displayName = displayName;
0158     Q_EMIT displayNameChanged();
0159     // resetting everything before populating
0160     m_pagination = false;
0161     clear();
0162     fillTimeline();
0163 }
0164 
0165 QString AccountsToolModel::email() const
0166 {
0167     return m_email;
0168 }
0169 
0170 void AccountsToolModel::setEmail(const QString &email)
0171 {
0172     if (email == m_email) {
0173         return;
0174     }
0175     m_email = email;
0176     Q_EMIT emailChanged();
0177     // resetting everything before populating
0178     m_pagination = false;
0179     clear();
0180     fillTimeline();
0181 }
0182 
0183 QString AccountsToolModel::ip() const
0184 {
0185     return m_ip;
0186 }
0187 
0188 void AccountsToolModel::setIp(const QString &ip)
0189 {
0190     if (ip == m_ip) {
0191         return;
0192     }
0193     m_ip = ip;
0194     Q_EMIT ipChanged();
0195     // resetting everything before populating
0196     m_pagination = false;
0197     clear();
0198     fillTimeline();
0199 }
0200 
0201 int AccountsToolModel::selectedAccountPosition() const
0202 {
0203     return m_selectedAccountPosition;
0204 }
0205 
0206 void AccountsToolModel::clear()
0207 {
0208     beginResetModel();
0209     m_accounts.clear();
0210     endResetModel();
0211     setLoading(false);
0212 }
0213 
0214 void AccountsToolModel::approveAccount(const int row)
0215 {
0216     executeAdminAction(row, AdminAccountAction::ApproveAccount);
0217 }
0218 
0219 void AccountsToolModel::rejectAccount(const int row)
0220 {
0221     executeAdminAction(row, AdminAccountAction::RejectAccount);
0222 }
0223 
0224 void AccountsToolModel::enableAccount(const int row)
0225 {
0226     executeAdminAction(row, AdminAccountAction::EnableDisabledAccount);
0227 }
0228 
0229 void AccountsToolModel::unsilenceAccount(const int row)
0230 {
0231     executeAdminAction(row, AdminAccountAction::UnsilenceAccount);
0232 }
0233 
0234 void AccountsToolModel::unsuspendAccount(const int row)
0235 {
0236     executeAdminAction(row, AdminAccountAction::UnsuspendAccount);
0237 }
0238 void AccountsToolModel::unsensitiveAccount(const int row)
0239 {
0240     executeAdminAction(row, AdminAccountAction::UnmarkSensitiveAccount);
0241 }
0242 
0243 void AccountsToolModel::actionAgainstAccount(const int row, const QString &type, const bool &emailWarning, const QString &note)
0244 {
0245     executeAdminAction(row, AdminAccountAction::ActionAgainstAccount, {{"type", type}, {"send_email_notification", emailWarning}, {"text", note}});
0246 }
0247 
0248 bool AccountsToolModel::canFetchMore(const QModelIndex &parent) const
0249 {
0250     Q_UNUSED(parent);
0251     return !m_next.isEmpty() && m_pagination;
0252 }
0253 
0254 void AccountsToolModel::deleteAccountData(const int row)
0255 {
0256     auto account = AccountManager::instance().selectedAccount();
0257     auto identity = m_accounts[row];
0258     const auto accountId = identity->userLevelIdentity()->id();
0259 
0260     account->deleteResource(account->apiUrl(QString("/api/v1/admin/accounts/%1").arg(accountId)), true, this, [=](QNetworkReply *reply) {
0261         const auto doc = QJsonDocument::fromJson(reply->readAll()).object();
0262         qDebug() << "DELETED: " << doc;
0263     });
0264 }
0265 
0266 void AccountsToolModel::executeAdminAction(const int row, AdminAccountAction adminAccountAction, const QJsonObject &extraArguments)
0267 {
0268     auto identity = m_accounts[row];
0269     const QHash<AdminAccountAction, QString> accountActionMap = {
0270         {AdminAccountAction::ApproveAccount, QStringLiteral("/approve")},
0271         {AdminAccountAction::RejectAccount, QStringLiteral("/reject")},
0272         {AdminAccountAction::ActionAgainstAccount, QStringLiteral("/action")},
0273         {AdminAccountAction::EnableDisabledAccount, QStringLiteral("/enable")},
0274         {AdminAccountAction::UnsilenceAccount, QStringLiteral("/unsilence")},
0275         {AdminAccountAction::UnsuspendAccount, QStringLiteral("/unsuspend")},
0276         {AdminAccountAction::UnmarkSensitiveAccount, QStringLiteral("/unsensitive")},
0277     };
0278 
0279     const auto apiCall = accountActionMap[adminAccountAction];
0280 
0281     const auto accountId = identity->userLevelIdentity()->id();
0282 
0283     const QString accountApiUrl = QStringLiteral("/api/v1/admin/accounts/") + accountId + apiCall;
0284 
0285     const QJsonDocument doc(extraArguments);
0286     // to be used when receiving parameter from actionAgainstAccount
0287     const auto type = doc["type"].toString();
0288 
0289     auto account = AccountManager::instance().selectedAccount();
0290     QUrl url = account->apiUrl(accountApiUrl);
0291 
0292     account->post(url, doc, true, this, [=](QNetworkReply *reply) {
0293         auto doc = QJsonDocument::fromJson(reply->readAll());
0294         auto jsonObj = doc.object();
0295 
0296         // Check if the request failed due to one account blocking the other
0297         if (!jsonObj.value("error").isUndefined()) {
0298             const QHash<AdminAccountAction, QString> accountActionMap = {
0299                 {AdminAccountAction::ApproveAccount, i18n("Could not accept account")},
0300                 {AdminAccountAction::RejectAccount, i18n("Could not reject account")},
0301                 {AdminAccountAction::ActionAgainstAccount, i18n("Could not take action against the account")},
0302                 {AdminAccountAction::EnableDisabledAccount, i18n("Could not enable  the disabled account")},
0303                 {AdminAccountAction::UnsilenceAccount, i18n("Could not unsilence the account")},
0304                 {AdminAccountAction::UnsuspendAccount, i18n("Could not unsuspend the account")},
0305                 {AdminAccountAction::UnmarkSensitiveAccount, i18n("Could not mark the account as not sensitive")},
0306             };
0307             const auto errorMessage = accountActionMap[adminAccountAction];
0308             Q_EMIT account->errorOccured(errorMessage);
0309             return;
0310         }
0311 
0312         switch (adminAccountAction) {
0313         case AdminAccountAction::ApproveAccount:
0314             identity->setApproved(true);
0315             break;
0316         case AdminAccountAction::RejectAccount:
0317             identity->setApproved(false);
0318             break;
0319         case AdminAccountAction::ActionAgainstAccount:
0320             if (type == "disable") {
0321                 identity->setDisabled(true);
0322             } else if (type == "sensitive") {
0323                 identity->setSensitized(true);
0324             } else if (type == "silence") {
0325                 identity->setSilence(true);
0326             } else if (type == "suspend") {
0327                 identity->setSuspended(true);
0328             }
0329             break;
0330         case AdminAccountAction::EnableDisabledAccount:
0331             identity->setDisabled(false);
0332             break;
0333         case AdminAccountAction::UnsilenceAccount:
0334             identity->setSilence(false);
0335             break;
0336         case AdminAccountAction::UnsuspendAccount:
0337             identity->setSuspended(false);
0338             break;
0339         case AdminAccountAction::UnmarkSensitiveAccount:
0340             identity->setSensitized(false);
0341             break;
0342         }
0343         Q_EMIT dataChanged(index(row, 0), index(row, 0));
0344     });
0345 }
0346 
0347 void AccountsToolModel::fetchMore(const QModelIndex &parent)
0348 {
0349     Q_UNUSED(parent);
0350 
0351     fillTimeline();
0352 }
0353 
0354 void AccountsToolModel::fetchSelectedAccountPosition()
0355 {
0356     auto account = AccountManager::instance().selectedAccount();
0357 
0358     const auto id = account->identity()->id();
0359 
0360     QUrl url = account->apiUrl(QString("/api/v1/admin/accounts/%1").arg(id));
0361 
0362     account->get(url, true, this, [this](QNetworkReply *reply) {
0363         const auto doc = QJsonDocument::fromJson(reply->readAll());
0364         m_selectedAccountPosition = doc["role"]["position"].toInt();
0365     });
0366 }
0367 
0368 void AccountsToolModel::fillTimeline()
0369 {
0370     // selecting the current logged in account
0371     auto account = AccountManager::instance().selectedAccount();
0372 
0373     // loading logic here
0374     m_pagination = true;
0375     if (m_loading) {
0376         return;
0377     }
0378     setLoading(true);
0379 
0380     QUrl url;
0381     if (m_next.isEmpty()) {
0382         url = account->apiUrl(QStringLiteral("/api/v2/admin/accounts"));
0383     } else {
0384         url = m_next;
0385     }
0386     // To be removed when the pagination api response in fixed
0387     if (url.toString().contains("v1")) {
0388         url = url.toString().replace("/v1/", "/v2/");
0389     }
0390 
0391     url.setQuery(buildQuery());
0392 
0393     account->get(url, true, this, [this, account](QNetworkReply *reply) {
0394         const auto doc = QJsonDocument::fromJson(reply->readAll());
0395         const auto accounts = doc.array();
0396 
0397         if (!accounts.isEmpty()) {
0398             static QRegularExpression re("<(.*)>; rel=\"next\"");
0399             const auto next = reply->rawHeader(QByteArrayLiteral("Link"));
0400             const auto match = re.match(next);
0401             if (re.isValid()) {
0402                 m_next = QUrl::fromUserInput(match.captured(1));
0403             }
0404             QList<std::shared_ptr<AdminAccountInfo>> fetchedAccounts;
0405 
0406             std::transform(
0407                 accounts.cbegin(),
0408                 accounts.cend(),
0409                 std::back_inserter(fetchedAccounts),
0410                 [account](const QJsonValue &value) -> auto{
0411                     const auto identityJson = value.toObject();
0412                     return account->adminIdentityLookup(identityJson["id"].toString(), identityJson);
0413                 });
0414             beginInsertRows({}, m_accounts.size(), m_accounts.size() + fetchedAccounts.size() - 1);
0415             m_accounts += fetchedAccounts;
0416             endInsertRows();
0417         }
0418 
0419         setLoading(false);
0420     });
0421 }