File indexing completed on 2024-12-15 03:45:03
0001 /* 0002 SPDX-FileCopyrightText: 2017 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: MIT 0005 */ 0006 0007 #include "auditloguicontroller.h" 0008 0009 #include <provider.h> 0010 0011 #include <QAbstractListModel> 0012 #include <QDateTime> 0013 #include <QDebug> 0014 #include <QDir> 0015 #include <QJsonArray> 0016 #include <QJsonDocument> 0017 #include <QJsonObject> 0018 #include <QLocale> 0019 #include <QMetaEnum> 0020 #include <QStandardPaths> 0021 0022 #include <algorithm> 0023 #include <vector> 0024 0025 using namespace KUserFeedback; 0026 0027 namespace KUserFeedback { 0028 class AuditLogEntryModel : public QAbstractListModel 0029 { 0030 Q_OBJECT 0031 public: 0032 explicit AuditLogEntryModel(const QString &path, QObject *parent); 0033 0034 void reload(); 0035 0036 int rowCount(const QModelIndex &parent = QModelIndex()) const override; 0037 QVariant data(const QModelIndex &index, int role) const override; 0038 0039 QHash<int, QByteArray> roleNames() const override; 0040 0041 private: 0042 QString m_path; 0043 std::vector<QDateTime> m_entries; 0044 }; 0045 0046 0047 class AuditLogUiControllerPrivate 0048 { 0049 public: 0050 QString path; 0051 AuditLogEntryModel *logEntryModel; 0052 }; 0053 } 0054 0055 0056 AuditLogEntryModel::AuditLogEntryModel(const QString &path, QObject *parent) 0057 : QAbstractListModel(parent) 0058 , m_path(path) 0059 { 0060 reload(); 0061 } 0062 0063 void AuditLogEntryModel::reload() 0064 { 0065 beginResetModel(); 0066 m_entries.clear(); 0067 0068 foreach (auto e, QDir(m_path).entryList(QDir::Files | QDir::Readable)) { 0069 if (!e.endsWith(QLatin1String(".log"))) 0070 continue; 0071 e.chop(4); 0072 const auto dt = QDateTime::fromString(e, QStringLiteral("yyyyMMdd-hhmmss")); 0073 if (dt.isValid()) 0074 m_entries.push_back(dt); 0075 } 0076 std::sort(m_entries.begin(), m_entries.end(), [](const QDateTime &lhs, const QDateTime &rhs) { 0077 return lhs > rhs; 0078 }); 0079 endResetModel(); 0080 } 0081 0082 int AuditLogEntryModel::rowCount(const QModelIndex &parent) const 0083 { 0084 if (parent.isValid()) 0085 return 0; 0086 return m_entries.size(); 0087 } 0088 0089 QVariant AuditLogEntryModel::data(const QModelIndex &index, int role) const 0090 { 0091 switch (role) { 0092 case Qt::DisplayRole: 0093 return QLocale().toString(m_entries[index.row()]); 0094 case Qt::UserRole: 0095 return m_entries[index.row()]; 0096 } 0097 return QVariant(); 0098 } 0099 0100 QHash<int, QByteArray> AuditLogEntryModel::roleNames() const 0101 { 0102 QHash<int, QByteArray> roles; 0103 roles.insert(Qt::DisplayRole, "display"); 0104 roles.insert(Qt::UserRole, "data"); 0105 return roles; 0106 } 0107 0108 0109 AuditLogUiController::AuditLogUiController(QObject* parent) 0110 : QObject(parent) 0111 , d(new AuditLogUiControllerPrivate) 0112 { 0113 d->path = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + QStringLiteral("/kuserfeedback/audit/"); 0114 d->logEntryModel = new AuditLogEntryModel(d->path, this); 0115 0116 connect(d->logEntryModel, &QAbstractItemModel::modelReset, this, &AuditLogUiController::logEntryCountChanged); 0117 } 0118 0119 AuditLogUiController::~AuditLogUiController() 0120 { 0121 } 0122 0123 bool AuditLogUiController::hasLogEntries() const 0124 { 0125 return d->logEntryModel->rowCount() != 0; 0126 } 0127 0128 QAbstractItemModel* AuditLogUiController::logEntryModel() const 0129 { 0130 return d->logEntryModel; 0131 } 0132 0133 static QString telemetryModeString(Provider::TelemetryMode mode) 0134 { 0135 switch (mode) { 0136 case Provider::NoTelemetry: 0137 Q_ASSERT(false); 0138 return QString(); 0139 case Provider::BasicSystemInformation: 0140 return AuditLogUiController::tr("Basic System Information"); 0141 case Provider::BasicUsageStatistics: 0142 return AuditLogUiController::tr("Basic Usage Statistics"); 0143 case Provider::DetailedSystemInformation: 0144 return AuditLogUiController::tr("Detailed System Information"); 0145 case Provider::DetailedUsageStatistics: 0146 return AuditLogUiController::tr("Detailed Usage Statistics"); 0147 } 0148 Q_UNREACHABLE(); 0149 } 0150 0151 QString AuditLogUiController::logEntry(const QDateTime &dt) const 0152 { 0153 const QString fn = d->path + dt.toString(QStringLiteral("yyyyMMdd-hhmmss")) + QStringLiteral(".log"); 0154 QFile file(fn); 0155 if (!file.open(QFile::ReadOnly)) 0156 return tr("Unable to open file %1: %2.").arg(fn, file.errorString()); 0157 0158 const auto doc = QJsonDocument::fromJson(file.readAll()); 0159 const auto topObj = doc.object(); 0160 struct Entry { 0161 QString key; 0162 QString desc; 0163 QString rawData; 0164 Provider::TelemetryMode mode; 0165 }; 0166 std::vector<Entry> entries; 0167 entries.reserve(topObj.size()); 0168 0169 const auto idx = Provider::staticMetaObject.indexOfEnumerator("TelemetryMode"); 0170 Q_ASSERT(idx >= 0); 0171 const auto modeEnum = Provider::staticMetaObject.enumerator(idx); 0172 0173 for (auto it = topObj.begin(); it != topObj.end(); ++it) { 0174 Entry e; 0175 e.key = it.key(); 0176 const auto obj = it.value().toObject(); 0177 e.desc = obj.value(QLatin1String("description")).toString(); 0178 const auto data = obj.value(QLatin1String("data")); 0179 if (data.isObject()) 0180 e.rawData = QString::fromUtf8(QJsonDocument(data.toObject()).toJson()); 0181 else if (data.isArray()) 0182 e.rawData = QString::fromUtf8(QJsonDocument(data.toArray()).toJson()); 0183 e.mode = static_cast<Provider::TelemetryMode>(modeEnum.keyToValue(obj.value(QLatin1String("telemetryMode")).toString().toUtf8().constData())); 0184 entries.push_back(e); 0185 } 0186 0187 std::sort(entries.begin(), entries.end(), [](const Entry &lhs, const Entry &rhs) -> bool { 0188 if (lhs.mode == rhs.mode) 0189 return lhs.key < rhs.key; 0190 return lhs.mode < rhs.mode; 0191 }); 0192 0193 QString res; 0194 for (auto it = entries.begin(); it != entries.end(); ++it) { 0195 res += QStringLiteral("<b>") + (*it).desc + QStringLiteral("</b><br/>"); 0196 res += tr("Category: <i>%1</i><br/>").arg(telemetryModeString((*it).mode)); 0197 res += tr("Key: <i>%1</i><br/>").arg((*it).key); 0198 res += tr("Submitted data: <tt>%1</tt><br/><br/>").arg((*it).rawData); 0199 } 0200 return res; 0201 } 0202 0203 void AuditLogUiController::clear() 0204 { 0205 QDir dir(d->path); 0206 foreach (auto e, dir.entryList(QDir::Files | QDir::Readable)) { 0207 if (!e.endsWith(QLatin1String(".log"))) 0208 continue; 0209 dir.remove(e); 0210 } 0211 0212 d->logEntryModel->reload(); 0213 } 0214 0215 #include "auditloguicontroller.moc" 0216 0217 #include "moc_auditloguicontroller.cpp"