File indexing completed on 2024-05-12 05:20:58

0001 /*
0002    SPDX-FileCopyrightText: 2013-2024 Laurent Montel <montel@kde.org>
0003 
0004    SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 #include "notesmanager.h"
0007 #include "akonadi/noteschangerecorder.h"
0008 #include "attributes/notealarmattribute.h"
0009 #include "attributes/notedisplayattribute.h"
0010 #include "attributes/notelockattribute.h"
0011 #include "job/createnewnotejob.h"
0012 #include "network/notesnetworkreceiver.h"
0013 #include "notesharedglobalconfig.h"
0014 
0015 #include <Akonadi/ChangeRecorder>
0016 #include <Akonadi/Collection>
0017 #include <Akonadi/CollectionFetchJob>
0018 #include <Akonadi/CollectionFetchScope>
0019 #include <Akonadi/ItemFetchJob>
0020 #include <Akonadi/ItemFetchScope>
0021 #include <Akonadi/NoteUtils>
0022 #include <Akonadi/Session>
0023 
0024 #include <KMime/KMimeMessage>
0025 
0026 #include <KLocalizedString>
0027 #include <KNotification>
0028 
0029 #include <QDateTime>
0030 #include <QTcpServer>
0031 #include <QTimer>
0032 
0033 NotesManager::NotesManager(QObject *parent)
0034     : QObject(parent)
0035     , mSession(new Akonadi::Session("KNotes Session", this))
0036 {
0037     mNoteRecorder = new NoteShared::NotesChangeRecorder(this);
0038     mNoteRecorder->changeRecorder()->setSession(mSession);
0039     connect(mNoteRecorder->changeRecorder(), &Akonadi::Monitor::itemAdded, this, &NotesManager::slotItemAdded);
0040     connect(mNoteRecorder->changeRecorder(), &Akonadi::Monitor::itemChanged, this, &NotesManager::slotItemChanged);
0041     connect(mNoteRecorder->changeRecorder(), &Akonadi::Monitor::itemRemoved, this, &NotesManager::slotItemRemoved);
0042 
0043     auto job = new Akonadi::CollectionFetchJob(Akonadi::Collection::root(), Akonadi::CollectionFetchJob::Recursive, mSession);
0044     job->fetchScope().setContentMimeTypes({Akonadi::NoteUtils::noteMimeType()});
0045     job->fetchScope().setFetchIdOnly(true);
0046     connect(job, &Akonadi::CollectionFetchJob::collectionsReceived, this, &NotesManager::slotCollectionsReceived);
0047 }
0048 
0049 NotesManager::~NotesManager()
0050 {
0051     clear();
0052 }
0053 
0054 void NotesManager::clear()
0055 {
0056     delete mListener;
0057     mListener = nullptr;
0058     if (mCheckAlarm && mCheckAlarm->isActive()) {
0059         mCheckAlarm->stop();
0060     }
0061 }
0062 
0063 void NotesManager::slotCollectionsReceived(const Akonadi::Collection::List &collections)
0064 {
0065     for (const Akonadi::Collection &col : collections) {
0066         if (!col.contentMimeTypes().contains(Akonadi::NoteUtils::noteMimeType())) {
0067             continue;
0068         }
0069         auto job = new Akonadi::ItemFetchJob(col, mSession);
0070         job->setDeliveryOption(Akonadi::ItemFetchJob::EmitItemsInBatches);
0071         job->fetchScope().fetchAttribute<NoteShared::NoteAlarmAttribute>();
0072         job->fetchScope().fetchAttribute<NoteShared::NoteLockAttribute>();
0073         job->fetchScope().fetchAttribute<NoteShared::NoteDisplayAttribute>();
0074         job->fetchScope().fetchAttribute<NoteShared::NoteAlarmAttribute>();
0075         job->fetchScope().fetchFullPayload(true);
0076         connect(job, &Akonadi::ItemFetchJob::itemsReceived, this, [this](const Akonadi::Item::List &items) {
0077             for (const Akonadi::Item &item : items) {
0078                 slotItemAdded(item);
0079             }
0080         });
0081     }
0082 }
0083 
0084 void NotesManager::slotItemAdded(const Akonadi::Item &item)
0085 {
0086     if (item.hasAttribute<NoteShared::NoteAlarmAttribute>()) {
0087         mListItem.append(item);
0088     }
0089 }
0090 
0091 void NotesManager::slotItemRemoved(const Akonadi::Item &item)
0092 {
0093     mListItem.removeAll(item);
0094 }
0095 
0096 void NotesManager::slotItemChanged(const Akonadi::Item &item, const QSet<QByteArray> &set)
0097 {
0098     if (set.contains("ATR:NoteAlarmAttribute")) {
0099         mListItem.removeAll(item);
0100         if (item.hasAttribute<NoteShared::NoteAlarmAttribute>()) {
0101             mListItem.append(item);
0102         } else {
0103             if (mAlarmDialog) {
0104                 mAlarmDialog->removeAlarm(item);
0105             }
0106         }
0107     }
0108 }
0109 
0110 void NotesManager::slotCheckAlarm()
0111 {
0112     QDateTime from = NoteShared::NoteSharedGlobalConfig::self()->alarmsLastChecked().addSecs(1);
0113     if (!from.isValid()) {
0114         from.setSecsSinceEpoch(0);
0115     }
0116 
0117     const QDateTime now = QDateTime::currentDateTime();
0118     NoteShared::NoteSharedGlobalConfig::self()->setAlarmsLastChecked(now);
0119 
0120     Akonadi::Item::List lst;
0121     for (const Akonadi::Item &item : std::as_const(mListItem)) {
0122         const auto attrAlarm = item.attribute<NoteShared::NoteAlarmAttribute>();
0123         if (attrAlarm) {
0124             if (attrAlarm->dateTime() < QDateTime::currentDateTime()) {
0125                 lst.append(item);
0126             }
0127         }
0128     }
0129     if (!lst.isEmpty()) {
0130         if (!mAlarmDialog) {
0131             mAlarmDialog = new NotesAgentAlarmDialog;
0132         }
0133         mAlarmDialog->addListAlarm(lst);
0134         mAlarmDialog->show();
0135     }
0136     mCheckAlarm->start();
0137 }
0138 
0139 void NotesManager::load()
0140 {
0141     updateNetworkListener();
0142     if (!mCheckAlarm) {
0143         mCheckAlarm = new QTimer(this);
0144     } else if (mCheckAlarm->isActive()) {
0145         mCheckAlarm->stop();
0146     }
0147 
0148     mCheckAlarm->setInterval(1000 * 60 * NoteShared::NoteSharedGlobalConfig::checkInterval());
0149     connect(mCheckAlarm, &QTimer::timeout, this, &NotesManager::slotCheckAlarm);
0150     slotCheckAlarm();
0151 }
0152 
0153 void NotesManager::stopAll()
0154 {
0155     clear();
0156 }
0157 
0158 void NotesManager::slotAcceptConnection()
0159 {
0160     // Accept the connection and make KNotesNetworkReceiver do the job
0161     QTcpSocket *s = mListener->nextPendingConnection();
0162 
0163     if (s) {
0164         auto recv = new NoteShared::NotesNetworkReceiver(s);
0165         connect(recv, &NoteShared::NotesNetworkReceiver::sigNoteReceived, this, &NotesManager::slotNewNote);
0166     }
0167 }
0168 
0169 void NotesManager::slotNewNote(const QString &name, const QString &text)
0170 {
0171     KNotification::event(QStringLiteral("receivednotes"),
0172                          QString(),
0173                          i18n("Note Received"),
0174                          QStringLiteral("knotes"),
0175                          KNotification::CloseOnTimeout,
0176                          QStringLiteral("akonadi_notes_agent"));
0177     auto job = new NoteShared::CreateNewNoteJob(this, nullptr);
0178     // For the moment it doesn't support richtext.
0179     job->setRichText(false);
0180     job->setNote(name, text);
0181     job->start();
0182 }
0183 
0184 void NotesManager::updateNetworkListener()
0185 {
0186     delete mListener;
0187     mListener = nullptr;
0188 
0189     if (NoteShared::NoteSharedGlobalConfig::receiveNotes()) {
0190         // create the socket and start listening for connections
0191         mListener = new QTcpServer;
0192         mListener->listen(QHostAddress::Any, NoteShared::NoteSharedGlobalConfig::port());
0193         connect(mListener, &QTcpServer::newConnection, this, &NotesManager::slotAcceptConnection);
0194     }
0195 }
0196 
0197 #include "moc_notesmanager.cpp"