File indexing completed on 2025-01-05 04:49:30

0001 /*
0002    SPDX-FileCopyrightText: 2016-2024 Laurent Montel <montel@kde.org>
0003 
0004    SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "automaticaddcontactsjob.h"
0008 #include "automaticaddcontactsplugin_debug.h"
0009 #include <Akonadi/AgentFilterProxyModel>
0010 #include <Akonadi/AgentInstanceCreateJob>
0011 #include <Akonadi/AgentTypeDialog>
0012 #include <Akonadi/CollectionFetchJob>
0013 #include <Akonadi/CollectionFetchScope>
0014 #include <Akonadi/ContactSearchJob>
0015 #include <Akonadi/ItemCreateJob>
0016 #include <Akonadi/SelectAddressBookDialog>
0017 #include <KContacts/Addressee>
0018 #include <KContacts/ContactGroup>
0019 #include <KEmailAddress>
0020 #include <KLocalizedString>
0021 #include <KMessageBox>
0022 #include <PimCommon/PimUtil>
0023 #include <QPointer>
0024 
0025 AutomaticAddContactsJob::AutomaticAddContactsJob(QObject *parent)
0026     : QObject(parent)
0027 {
0028 }
0029 
0030 AutomaticAddContactsJob::~AutomaticAddContactsJob() = default;
0031 
0032 void AutomaticAddContactsJob::start()
0033 {
0034     if (mEmails.isEmpty()) {
0035         deleteLaterAndEmitSignal();
0036         return;
0037     } else {
0038         if (!mCollection.isValid()) {
0039             qCDebug(KMAIL_EDITOR_AUTOMATICADDCONTACTS_PLUGIN_LOG) << "Invalid collection";
0040             deleteLaterAndEmitSignal();
0041             return;
0042         }
0043     }
0044     mCurrentIndex = -1;
0045     fetchCollection();
0046 }
0047 
0048 void AutomaticAddContactsJob::fetchCollection()
0049 {
0050     auto const addressBookJob = new Akonadi::CollectionFetchJob(mCollection, Akonadi::CollectionFetchJob::Base);
0051 
0052     const QStringList mimeTypes(KContacts::Addressee::mimeType());
0053     addressBookJob->fetchScope().setContentMimeTypes(mimeTypes);
0054     connect(addressBookJob, &KJob::result, this, &AutomaticAddContactsJob::slotSelectedCollectionFetched);
0055 }
0056 
0057 void AutomaticAddContactsJob::slotSelectedCollectionFetched(KJob *job)
0058 {
0059     if (job->error()) {
0060         // Collection not found.
0061         // fetch all collection
0062         const QStringList mimeTypes(KContacts::Addressee::mimeType());
0063 
0064         auto const addressBookJob = new Akonadi::CollectionFetchJob(Akonadi::Collection::root(), Akonadi::CollectionFetchJob::Recursive);
0065 
0066         addressBookJob->fetchScope().setContentMimeTypes(mimeTypes);
0067         connect(addressBookJob, &KJob::result, this, &AutomaticAddContactsJob::slotFetchAllCollections);
0068         return;
0069     }
0070     const Akonadi::CollectionFetchJob *addressBookJob = qobject_cast<Akonadi::CollectionFetchJob *>(job);
0071     mCollection = addressBookJob->collections().at(0);
0072     addNextContact();
0073 }
0074 
0075 void AutomaticAddContactsJob::slotFetchAllCollections(KJob *job)
0076 {
0077     if (job->error()) {
0078         qCWarning(KMAIL_EDITOR_AUTOMATICADDCONTACTS_PLUGIN_LOG) << "Error during AutomaticAddContactsJob::slotFetchAllCollections : " << job->errorString();
0079         deleteLaterAndEmitSignal();
0080         return;
0081     }
0082 
0083     const Akonadi::CollectionFetchJob *addressBookJob = qobject_cast<Akonadi::CollectionFetchJob *>(job);
0084 
0085     Akonadi::Collection::List canCreateItemCollections;
0086     const Akonadi::Collection::List addressBookCollections = addressBookJob->collections();
0087     for (const Akonadi::Collection &collection : addressBookCollections) {
0088         if (Akonadi::Collection::CanCreateItem & collection.rights()) {
0089             canCreateItemCollections.append(collection);
0090         }
0091     }
0092     Akonadi::Collection addressBook;
0093 
0094     const int nbItemCollection(canCreateItemCollections.size());
0095     if (nbItemCollection == 0) {
0096         if (KMessageBox::questionTwoActions(nullptr,
0097                                             i18nc("@info", "You must create an address book before adding a contact. Do you want to create an address book?"),
0098                                             i18nc("@title:window", "No Address Book Available"),
0099                                             KGuiItem(i18nc("@action:button", "Create Address Book"), QStringLiteral("address-book-new")),
0100                                             KStandardGuiItem::cancel())
0101             == KMessageBox::ButtonCode::PrimaryAction) {
0102             QPointer<Akonadi::AgentTypeDialog> dlg = new Akonadi::AgentTypeDialog(nullptr);
0103             dlg->setWindowTitle(i18nc("@title:window", "Add Address Book"));
0104             dlg->agentFilterProxyModel()->addMimeTypeFilter(KContacts::Addressee::mimeType());
0105             dlg->agentFilterProxyModel()->addMimeTypeFilter(KContacts::ContactGroup::mimeType());
0106             dlg->agentFilterProxyModel()->addCapabilityFilter(QStringLiteral("Resource"));
0107 
0108             if (dlg->exec()) {
0109                 const Akonadi::AgentType agentType = dlg->agentType();
0110 
0111                 if (agentType.isValid()) {
0112                     auto createJob = new Akonadi::AgentInstanceCreateJob(agentType, this);
0113                     connect(createJob, &KJob::result, this, &AutomaticAddContactsJob::slotResourceCreationDone);
0114                     createJob->configure();
0115                     createJob->start();
0116                     delete dlg;
0117                     return;
0118                 } else { // if agent is not valid => return error and finish job
0119                     deleteLaterAndEmitSignal();
0120                     delete dlg;
0121                     return;
0122                 }
0123             } else { // Canceled create agent => return error and finish job
0124                 deleteLaterAndEmitSignal();
0125                 delete dlg;
0126                 return;
0127             }
0128         } else {
0129             deleteLaterAndEmitSignal();
0130             return;
0131         }
0132     } else if (nbItemCollection == 1) {
0133         addressBook = canCreateItemCollections[0];
0134     } else {
0135         // ask user in which address book the new contact shall be stored
0136         QPointer<Akonadi::SelectAddressBookDialog> dlg = new Akonadi::SelectAddressBookDialog(nullptr);
0137 
0138         bool gotIt = true;
0139         if (dlg->exec()) {
0140             addressBook = dlg->selectedCollection();
0141         } else {
0142             gotIt = false;
0143         }
0144         delete dlg;
0145         if (!gotIt) {
0146             qCWarning(KMAIL_EDITOR_AUTOMATICADDCONTACTS_PLUGIN_LOG) << "Unable to selected Addressbook selected not valid";
0147             deleteLaterAndEmitSignal();
0148             return;
0149         }
0150     }
0151 
0152     if (!addressBook.isValid()) {
0153         qCWarning(KMAIL_EDITOR_AUTOMATICADDCONTACTS_PLUGIN_LOG) << "Addressbook selected not valid";
0154         deleteLaterAndEmitSignal();
0155         return;
0156     }
0157     addNextContact();
0158 }
0159 
0160 void AutomaticAddContactsJob::slotResourceCreationDone(KJob *job)
0161 {
0162     if (job->error()) {
0163         qCWarning(KMAIL_EDITOR_AUTOMATICADDCONTACTS_PLUGIN_LOG) << "Unable to create resource:" << job->errorText();
0164         deleteLaterAndEmitSignal();
0165         return;
0166     }
0167     addNextContact();
0168 }
0169 
0170 void AutomaticAddContactsJob::verifyContactExist()
0171 {
0172     const QString email = mEmails.at(mCurrentIndex);
0173     QString tname;
0174     QString temail;
0175     KEmailAddress::extractEmailAddressAndName(email, temail, tname);
0176     if (temail.isEmpty()) {
0177         addNextContact();
0178     } else {
0179         if (mProcessedEmails.contains(email)) {
0180             addNextContact();
0181         } else {
0182             mProcessEmail = email;
0183             mName = tname;
0184             mProcessedEmails.append(email);
0185             auto searchJob = new Akonadi::ContactSearchJob(this);
0186             searchJob->setLimit(1);
0187             searchJob->setQuery(Akonadi::ContactSearchJob::Email, mProcessEmail.toLower(), Akonadi::ContactSearchJob::ExactMatch);
0188             connect(searchJob, &KJob::result, this, &AutomaticAddContactsJob::slotSearchDone);
0189         }
0190     }
0191 }
0192 
0193 void AutomaticAddContactsJob::slotSearchDone(KJob *job)
0194 {
0195     auto searchJob = static_cast<Akonadi::ContactSearchJob *>(job);
0196     if (searchJob->error()) {
0197         qCWarning(KMAIL_EDITOR_AUTOMATICADDCONTACTS_PLUGIN_LOG) << "Unable to fetch contact:" << searchJob->errorText();
0198     } else if (searchJob->contacts().isEmpty()) {
0199         KContacts::Addressee contact;
0200         contact.setNameFromString(mName);
0201         KContacts::Email email(mProcessEmail);
0202         email.setPreferred(true);
0203         contact.addEmail(email);
0204 
0205         // create the new item
0206         Akonadi::Item item;
0207         item.setMimeType(KContacts::Addressee::mimeType());
0208         item.setPayload<KContacts::Addressee>(contact);
0209 
0210         // save the new item in akonadi storage
0211         auto createJob = new Akonadi::ItemCreateJob(item, mCollection, this);
0212         connect(createJob, &KJob::result, this, &AutomaticAddContactsJob::slotAddContactDone);
0213         return;
0214     }
0215     addNextContact();
0216 }
0217 
0218 void AutomaticAddContactsJob::slotAddContactDone(KJob *job)
0219 {
0220     if (job->error()) {
0221         qCWarning(KMAIL_EDITOR_AUTOMATICADDCONTACTS_PLUGIN_LOG) << "Error when add contact to addressbook:" << job->errorText();
0222     }
0223     addNextContact();
0224 }
0225 
0226 void AutomaticAddContactsJob::addNextContact()
0227 {
0228     mCurrentIndex++;
0229     if (mCurrentIndex < mEmails.count()) {
0230         verifyContactExist();
0231     } else {
0232         deleteLaterAndEmitSignal();
0233     }
0234 }
0235 
0236 void AutomaticAddContactsJob::setEmails(const QStringList &list)
0237 {
0238     mEmails = PimCommon::Util::generateEmailList(list);
0239 }
0240 
0241 void AutomaticAddContactsJob::setCollection(const Akonadi::Collection &collection)
0242 {
0243     mCollection = collection;
0244 }
0245 
0246 void AutomaticAddContactsJob::deleteLaterAndEmitSignal()
0247 {
0248     Q_EMIT finished();
0249     deleteLater();
0250 }
0251 
0252 #include "moc_automaticaddcontactsjob.cpp"