File indexing completed on 2024-12-22 04:55:36

0001 /*
0002  *  akonadiplugin.cpp  -  plugin to provide features requiring Akonadi
0003  *  Program:  kalarm
0004  *  SPDX-FileCopyrightText: 2022 David Jarvie <djarvie@kde.org>
0005  *
0006  *  SPDX-License-Identifier: LGPL-2.0-or-later
0007  */
0008 
0009 #include "akonadiplugin.h"
0010 
0011 #include "akonadicollectionsearch.h"
0012 #include "akonadiresourcemigrator.h"
0013 #include "birthdaymodel.h"
0014 #include "sendakonadimail.h"
0015 #include "lib/autoqpointer.h"
0016 #include "akonadiplugin_debug.h"
0017 
0018 #include <Akonadi/ControlGui>
0019 #include <Akonadi/EmailAddressSelectionDialog>
0020 #include <Akonadi/EntityMimeTypeFilterModel>
0021 #include <Akonadi/Item>
0022 #include <Akonadi/ItemFetchJob>
0023 #include <Akonadi/ItemFetchScope>
0024 #include <KCalendarCore/Person>
0025 
0026 #include <KPluginFactory>
0027 #include <KLocalizedString>
0028 #include <KDescendantsProxyModel>
0029 
0030 #include <QUrlQuery>
0031 
0032 K_PLUGIN_CLASS_WITH_JSON(AkonadiPlugin, "akonadiplugin.json")
0033 
0034 AkonadiPlugin::AkonadiPlugin(QObject* parent, const QList<QVariant>& args)
0035     : PluginBase(parent, args)
0036 {
0037     setName(QStringLiteral("Akonadi"));
0038 }
0039 
0040 /******************************************************************************
0041 * Start Akonadi and create an instance of both birthday models.
0042 */
0043 QSortFilterProxyModel* AkonadiPlugin::createBirthdayModels(QWidget* messageParent, QObject* parent)
0044 {
0045     // Start Akonadi server as we need it for the birthday model to access contacts information
0046     Akonadi::ControlGui::widgetNeedsAkonadi(messageParent);
0047 
0048     BirthdayModel* model = BirthdayModel::instance();
0049     connect(model, &BirthdayModel::dataChanged, this, &AkonadiPlugin::birthdayModelDataChanged);
0050 
0051     auto descendantsModel = new KDescendantsProxyModel(parent);
0052     descendantsModel->setSourceModel(model);
0053 
0054     auto mimeTypeFilter = new Akonadi::EntityMimeTypeFilterModel(parent);
0055     mimeTypeFilter->setSourceModel(descendantsModel);
0056     mimeTypeFilter->addMimeTypeExclusionFilter(Akonadi::Collection::mimeType());
0057     mimeTypeFilter->setHeaderGroup(Akonadi::EntityTreeModel::ItemListHeaders);
0058 
0059     BirthdaySortModel* sortModel = new BirthdaySortModel(parent);
0060     sortModel->setSourceModel(mimeTypeFilter);
0061     sortModel->setSortCaseSensitivity(Qt::CaseInsensitive);
0062     return sortModel;
0063 }
0064 
0065 void AkonadiPlugin::setPrefixSuffix(QSortFilterProxyModel* model, const QString& prefix, const QString& suffix, const QStringList& alarmMessageList)
0066 {
0067     BirthdaySortModel* bmodel = qobject_cast<BirthdaySortModel*>(model);
0068     if (bmodel)
0069         bmodel->setPrefixSuffix(prefix, suffix, alarmMessageList);
0070 }
0071 
0072 int AkonadiPlugin::birthdayModelEnum(BirthdayModelValue value) const
0073 {
0074     switch (value)
0075     {
0076         case BirthdayModelValue::NameColumn:  return BirthdayModel::NameColumn;
0077         case BirthdayModelValue::DateColumn:  return BirthdayModel::DateColumn;
0078         case BirthdayModelValue::DateRole:    return BirthdayModel::DateRole;
0079         default:  return -1;
0080     }
0081 }
0082 
0083 /******************************************************************************
0084 * Send an email via Akonadi.
0085 */
0086 QString AkonadiPlugin::sendMail(KMime::Message::Ptr message, const KIdentityManagementCore::Identity& identity,
0087                                 const QString& normalizedFrom, bool keepSentMail, MailSend::JobData& jobdata)
0088 {
0089     if (!mSendAkonadiMail)
0090     {
0091         mSendAkonadiMail = SendAkonadiMail::instance();
0092         connect(mSendAkonadiMail, &SendAkonadiMail::sent, this, &AkonadiPlugin::emailSent);
0093         connect(mSendAkonadiMail, &SendAkonadiMail::queued, this, &AkonadiPlugin::emailQueued);
0094     }
0095     return mSendAkonadiMail->send(message, identity, normalizedFrom, keepSentMail, jobdata);
0096 }
0097 
0098 /******************************************************************************
0099 * Extract dragged and dropped Akonadi RFC822 message data.
0100 */
0101 KMime::Message::Ptr AkonadiPlugin::fetchAkonadiEmail(const QUrl& url, qint64& emailId)
0102 {
0103     static_assert(sizeof(Akonadi::Item::Id) == sizeof(emailId), "AkonadiPlugin::fetchAkonadiEmail: parameter is wrong type");
0104 
0105     emailId = -1;
0106     Akonadi::Item item = Akonadi::Item::fromUrl(url);
0107     if (!item.isValid())
0108         return {};
0109 
0110     // It's an Akonadi item
0111     qCDebug(AKONADIPLUGIN_LOG) << "AkonadiPlugin::fetchAkonadiEmail: Akonadi item" << item.id();
0112     if (QUrlQuery(url).queryItemValue(QStringLiteral("type")) != QLatin1String("message/rfc822"))
0113         return {};   // it's not an email
0114 
0115     // It's an email held in Akonadi
0116     qCDebug(AKONADIPLUGIN_LOG) << "AkonadiPlugin::fetchAkonadiEmail: Akonadi email";
0117     auto job = new Akonadi::ItemFetchJob(item);
0118     job->fetchScope().fetchFullPayload();
0119     Akonadi::Item::List items;
0120     if (job->exec())
0121         items = job->items();
0122     if (items.isEmpty())
0123         qCWarning(AKONADIPLUGIN_LOG) << "AkonadiPlugin::fetchAkonadiEmail: Akonadi item" << item.id() << "not found";
0124     else
0125     {
0126         const Akonadi::Item& it = items.at(0);
0127         if (!it.isValid()  ||  !it.hasPayload<KMime::Message::Ptr>())
0128             qCWarning(AKONADIPLUGIN_LOG) << "AkonadiPlugin::fetchAkonadiEmail: invalid email";
0129         else
0130         {
0131             emailId = it.id();
0132             return it.payload<KMime::Message::Ptr>();
0133         }
0134     }
0135     return {};
0136 }
0137 
0138 bool AkonadiPlugin::getAddressBookSelection(KCalendarCore::Person& person, QWidget* parent)
0139 {
0140     person = KCalendarCore::Person();
0141 
0142     // Use AutoQPointer to guard against crash on application exit while
0143     // the dialogue is still open. It prevents double deletion (both on
0144     // deletion of MainWindow, and on return from this function).
0145     AutoQPointer<Akonadi::EmailAddressSelectionDialog> dlg = new Akonadi::EmailAddressSelectionDialog(parent);
0146     if (dlg->exec() != QDialog::Accepted)
0147         return false;
0148 
0149     Akonadi::EmailAddressSelection::List selections = dlg->selectedAddresses();
0150     if (selections.isEmpty())
0151         return false;
0152     person = KCalendarCore::Person(selections.first().name(), selections.first().email());
0153     return true;
0154 }
0155 
0156 qint64 AkonadiPlugin::getCollectionId(qint64 emailId)
0157 {
0158     static_assert(sizeof(Akonadi::Item::Id) == sizeof(emailId), "AkonadiPlugin::getCollectionId: parameter is wrong type");
0159     static_assert(sizeof(Akonadi::Collection::Id) == sizeof(qint64), "AkonadiPlugin::getCollectionId: wrong return type");
0160 
0161     Akonadi::ItemFetchJob* job = new Akonadi::ItemFetchJob(Akonadi::Item(emailId));
0162     job->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent);
0163     Akonadi::Item::List items;
0164     if (job->exec())
0165         items = job->items();
0166     if (items.isEmpty()  ||  !items.at(0).isValid())
0167         return -1;
0168     const Akonadi::Item& it = items.at(0);
0169     return it.parentCollection().id();
0170 }
0171 
0172 void AkonadiPlugin::deleteEvent(const QString& mimeType, const QString& gid, const QString& uid)
0173 {
0174     new AkonadiCollectionSearch(mimeType, gid, uid, true);  // this auto-deletes when complete
0175 }
0176 
0177 void AkonadiPlugin::initiateAkonadiResourceMigration()
0178 {
0179     AkonadiResourceMigrator* akonadiMigrator = AkonadiResourceMigrator::instance();
0180     if (akonadiMigrator)
0181     {
0182         connect(akonadiMigrator, &AkonadiResourceMigrator::migrationComplete, this, &AkonadiPlugin::akonadiMigrationComplete);
0183         connect(akonadiMigrator, &AkonadiResourceMigrator::fileResource, this, &AkonadiPlugin::migrateFileResource);
0184         connect(akonadiMigrator, &AkonadiResourceMigrator::dirResource, this, &AkonadiPlugin::migrateDirResource);
0185         akonadiMigrator->initiateMigration();
0186     }
0187 }
0188 
0189 void AkonadiPlugin::deleteAkonadiResource(const QString& resourceName)
0190 {
0191     AkonadiResourceMigrator* akonadiMigrator = AkonadiResourceMigrator::instance();
0192     if (akonadiMigrator)
0193         akonadiMigrator->deleteAkonadiResource(resourceName);
0194 }
0195 
0196 #include "akonadiplugin.moc"
0197 #include "moc_akonadiplugin.cpp"
0198 
0199 // vim: et sw=4: