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

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