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 ¬e) 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 }