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"