Akonadi Contacts

addemaildisplayjob.cpp
1/*
2 SPDX-FileCopyrightText: 2013-2025 Laurent Montel <montel@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "addemaildisplayjob.h"
8#include "widgets/selectaddressbookdialog.h"
9#include <Akonadi/AgentConfigurationDialog>
10#include <Akonadi/AgentFilterProxyModel>
11#include <Akonadi/AgentInstance>
12#include <Akonadi/AgentInstanceCreateJob>
13#include <Akonadi/AgentManager>
14#include <Akonadi/AgentType>
15#include <Akonadi/AgentTypeDialog>
16#include <Akonadi/Collection>
17#include <Akonadi/CollectionDialog>
18#include <Akonadi/CollectionFetchJob>
19#include <Akonadi/CollectionFetchScope>
20#include <Akonadi/ContactEditorDialog>
21#include <Akonadi/ContactSearchJob>
22#include <Akonadi/ItemCreateJob>
23#include <Akonadi/ItemModifyJob>
24
25#include <KContacts/ContactGroup>
26#include <KLocalizedString>
27#include <KMessageBox>
28
29#include <QPointer>
30using namespace Akonadi;
31
32class Akonadi::AddEmailDisplayJobPrivate
33{
34public:
35 AddEmailDisplayJobPrivate(AddEmailDisplayJob *qq, const QString &emailString, QWidget *parentWidget)
36 : q(qq)
37 , mCompleteAddress(emailString)
38 , mParentWidget(parentWidget)
39 {
40 KContacts::Addressee::parseEmailAddress(emailString, mName, mEmail);
41 }
42
43 void slotResourceCreationDone(KJob *job)
44 {
45 if (job->error()) {
46 q->setError(job->error());
47 q->setErrorText(job->errorText());
48 q->emitResult();
49 return;
50 }
51 createContact();
52 }
53
54 void searchContact()
55 {
56 // first check whether a contact with the same email exists already
57 auto searchJob = new Akonadi::ContactSearchJob(q);
58 searchJob->setLimit(1);
59 searchJob->setQuery(Akonadi::ContactSearchJob::Email, mEmail.toLower(), Akonadi::ContactSearchJob::ExactMatch);
60 q->connect(searchJob, &Akonadi::ContactSearchJob::result, q, [this](KJob *job) {
61 slotSearchDone(job);
62 });
63 }
64
65 void modifyContact()
66 {
67 Akonadi::Item item = contact;
68 if (item.hasPayload<KContacts::Addressee>()) {
69 auto address = item.payload<KContacts::Addressee>();
70 address.insertCustom(QStringLiteral("KADDRESSBOOK"),
71 QStringLiteral("MailPreferedFormatting"),
72 mShowAsHTML ? QStringLiteral("HTML") : QStringLiteral("TEXT"));
73 address.insertCustom(QStringLiteral("KADDRESSBOOK"),
74 QStringLiteral("MailAllowToRemoteContent"),
75 mRemoteContent ? QStringLiteral("TRUE") : QStringLiteral("FALSE"));
76 item.setPayload<KContacts::Addressee>(address);
77 auto itemModifyJob = new Akonadi::ItemModifyJob(item);
78 q->connect(itemModifyJob, &Akonadi::ItemModifyJob::result, q, [this](KJob *itemModifyJob) {
79 auto modifiedJob = static_cast<Akonadi::ItemModifyJob *>(itemModifyJob);
80 if (!modifiedJob->error()) {
81 Q_EMIT q->contactUpdated(modifiedJob->item(), messageId, mShowAsHTML, mRemoteContent);
82 }
83 slotAddModifyContactDone(itemModifyJob);
84 });
85 } else {
86 searchContact();
87 }
88 }
89
90 void slotSearchDone(KJob *job)
91 {
92 if (job->error()) {
93 q->setError(job->error());
94 q->setErrorText(job->errorText());
95 q->emitResult();
96 return;
97 }
98
99 const Akonadi::ContactSearchJob *searchJob = qobject_cast<Akonadi::ContactSearchJob *>(job);
100
101 const Akonadi::Item::List items = searchJob->items();
102 if (items.isEmpty()) {
103 createContact();
104 } else {
105 Akonadi::Item item = items.at(0);
106 KContacts::Addressee contact = searchJob->contacts().at(0);
107 contact.insertCustom(QStringLiteral("KADDRESSBOOK"),
108 QStringLiteral("MailPreferedFormatting"),
109 mShowAsHTML ? QStringLiteral("HTML") : QStringLiteral("TEXT"));
110 contact.insertCustom(QStringLiteral("KADDRESSBOOK"),
111 QStringLiteral("MailAllowToRemoteContent"),
112 mRemoteContent ? QStringLiteral("TRUE") : QStringLiteral("FALSE"));
113 item.setPayload<KContacts::Addressee>(contact);
114 auto itemModifyJob = new Akonadi::ItemModifyJob(item);
115 q->connect(itemModifyJob, &Akonadi::ItemModifyJob::result, q, [this](KJob *itemModifyJob) {
116 auto modifiedJob = static_cast<Akonadi::ItemModifyJob *>(itemModifyJob);
117 if (!modifiedJob->error()) {
118 Q_EMIT q->contactUpdated(modifiedJob->item(), messageId, mShowAsHTML, mRemoteContent);
119 }
120 slotAddModifyContactDone(itemModifyJob);
121 });
122 }
123 }
124
125 void createContact()
126 {
127 const QStringList mimeTypes(KContacts::Addressee::mimeType());
128
129 auto const addressBookJob = new Akonadi::CollectionFetchJob(Akonadi::Collection::root(), Akonadi::CollectionFetchJob::Recursive);
130
131 addressBookJob->fetchScope().setContentMimeTypes(mimeTypes);
132 q->connect(addressBookJob, &Akonadi::CollectionFetchJob::result, q, [this](KJob *job) {
133 slotCollectionsFetched(job);
134 });
135 }
136
137 void slotCollectionsFetched(KJob *job)
138 {
139 if (job->error()) {
140 q->setError(job->error());
141 q->setErrorText(job->errorText());
142 q->emitResult();
143 return;
144 }
145
146 const Akonadi::CollectionFetchJob *addressBookJob = qobject_cast<Akonadi::CollectionFetchJob *>(job);
147
148 Akonadi::Collection::List canCreateItemCollections;
149 const Akonadi::Collection::List colsList = addressBookJob->collections();
150 for (const Akonadi::Collection &collection : colsList) {
151 if (Akonadi::Collection::CanCreateItem & collection.rights()) {
152 canCreateItemCollections.append(collection);
153 }
154 }
155
156 Akonadi::Collection addressBook;
157
158 const int nbItemCollection(canCreateItemCollections.size());
159 if (nbItemCollection == 0) {
161 mParentWidget,
162 i18nc("@info", "You must create an address book before adding a contact. Do you want to create an address book?"),
163 i18nc("@title:window", "No Address Book Available"),
164 KGuiItem(i18nc("@action:button", "Create Address Book"), QStringLiteral("address-book-new")),
166 == KMessageBox::ButtonCode::PrimaryAction) {
167 QPointer<Akonadi::AgentTypeDialog> dlg = new Akonadi::AgentTypeDialog(mParentWidget);
168 dlg->setWindowTitle(i18nc("@title:window", "Add Address Book"));
169 dlg->agentFilterProxyModel()->addMimeTypeFilter(KContacts::Addressee::mimeType());
170 dlg->agentFilterProxyModel()->addMimeTypeFilter(KContacts::ContactGroup::mimeType());
171 dlg->agentFilterProxyModel()->addCapabilityFilter(QStringLiteral("Resource"));
172
173 if (dlg->exec()) {
174 const Akonadi::AgentType agentType = dlg->agentType();
175
176 if (agentType.isValid()) {
177 auto job = new Akonadi::AgentInstanceCreateJob(agentType, q);
178 q->connect(job, &Akonadi::AgentInstanceCreateJob::result, q, [this, job](KJob *) {
179 if (job->error()) {
180 slotResourceCreationDone(job);
181 return;
182 }
183 auto configureDialog = new Akonadi::AgentConfigurationDialog(job->instance(), mParentWidget);
184 configureDialog->setAttribute(Qt::WA_DeleteOnClose);
185 q->connect(configureDialog, &QDialog::rejected, q, [this, instance = job->instance()] {
186 Akonadi::AgentManager::self()->removeInstance(instance);
187 q->emitResult();
188 });
189 q->connect(configureDialog, &QDialog::accepted, q, [this] {
190 createContact();
191 });
192 configureDialog->show();
193 });
194
195 job->start();
196 delete dlg;
197 return;
198 } else { // if agent is not valid => return error and finish job
199 q->setError(KJob::UserDefinedError);
200 q->emitResult();
201 delete dlg;
202 return;
203 }
204 } else { // Canceled create agent => return error and finish job
205 q->setError(KJob::UserDefinedError);
206 q->emitResult();
207 delete dlg;
208 return;
209 }
210 } else {
211 q->setError(KJob::UserDefinedError);
212 q->emitResult();
213 return;
214 }
215 } else if (nbItemCollection == 1) {
216 addressBook = canCreateItemCollections[0];
217 } else {
218 // ask user in which address book the new contact shall be stored
219 QPointer<Akonadi::SelectAddressBookDialog> dlg = new Akonadi::SelectAddressBookDialog(mParentWidget);
220
221 bool gotIt = true;
222 if (dlg->exec()) {
223 addressBook = dlg->selectedCollection();
224 } else {
225 q->setError(KJob::UserDefinedError);
226 q->emitResult();
227 gotIt = false;
228 }
229 delete dlg;
230 if (!gotIt) {
231 return;
232 }
233 }
234
235 if (!addressBook.isValid()) {
236 q->setError(KJob::UserDefinedError);
237 q->emitResult();
238 return;
239 }
240 KContacts::Addressee contact;
241 contact.setNameFromString(mName);
242 KContacts::Email email(mEmail);
243 email.setPreferred(true);
244 contact.addEmail(email);
245 contact.insertCustom(QStringLiteral("KADDRESSBOOK"),
246 QStringLiteral("MailPreferedFormatting"),
247 mShowAsHTML ? QStringLiteral("HTML") : QStringLiteral("TEXT"));
248 contact.insertCustom(QStringLiteral("KADDRESSBOOK"),
249 QStringLiteral("MailAllowToRemoteContent"),
250 mRemoteContent ? QStringLiteral("TRUE") : QStringLiteral("FALSE"));
251
252 // create the new item
253 Akonadi::Item item;
255 item.setPayload<KContacts::Addressee>(contact);
256
257 // save the new item in akonadi storage
258 auto createJob = new Akonadi::ItemCreateJob(item, addressBook, q);
259 q->connect(createJob, &Akonadi::ItemCreateJob::result, q, [this](KJob *createJob) {
260 auto modifiedJob = static_cast<Akonadi::ItemCreateJob *>(createJob);
261 if (!modifiedJob->error()) {
262 Q_EMIT q->contactUpdated(modifiedJob->item(), messageId, mShowAsHTML, mRemoteContent);
263 }
264 slotAddModifyContactDone(createJob);
265 });
266 }
267
268 void slotAddModifyContactDone(KJob *job)
269 {
270 if (job->error()) {
271 q->setError(job->error());
272 q->setErrorText(job->errorText());
273 }
274 q->emitResult();
275 }
276
277 AddEmailDisplayJob *const q;
278 Akonadi::Item contact;
279 Akonadi::Item::Id messageId;
280 const QString mCompleteAddress;
281 QString mEmail;
282 QString mName;
283 QWidget *const mParentWidget;
284 bool mShowAsHTML = false;
285 bool mRemoteContent = false;
286};
287
288AddEmailDisplayJob::AddEmailDisplayJob(const QString &email, QWidget *parentWidget, QObject *parent)
289 : KJob(parent)
290 , d(new AddEmailDisplayJobPrivate(this, email, parentWidget))
291{
292}
293
294AddEmailDisplayJob::~AddEmailDisplayJob() = default;
295
296void AddEmailDisplayJob::setShowAsHTML(bool html)
297{
298 d->mShowAsHTML = html;
299}
300
301void AddEmailDisplayJob::setRemoteContent(bool b)
302{
303 d->mRemoteContent = b;
304}
305
306void AddEmailDisplayJob::setContact(const Akonadi::Item &contact)
307{
308 d->contact = contact;
309}
310
311void AddEmailDisplayJob::setMessageId(Akonadi::Item::Id id)
312{
313 d->messageId = id;
314}
315
316void AddEmailDisplayJob::start()
317{
318 if (d->contact.isValid()) {
319 d->modifyContact();
320 } else {
321 d->searchContact();
322 }
323}
324
325#include "moc_addemaildisplayjob.cpp"
bool isValid() const
Collection::List collections() const
bool isValid() const
static Collection root()
QList< Collection > List
@ ExactMatch
The result must match exactly the pattern (case sensitive).
@ Email
The email address of the contact.
KContacts::Addressee::List contacts() const
Returns the contacts that matched the search criteria.
Item::List items() const
void setPayload(const T &p)
void setMimeType(const QString &mimeType)
bool hasPayload() const
T payload() const
QList< Item > List
static QString mimeType()
static void parseEmailAddress(const QString &rawEmail, QString &fullName, QString &email)
static QString mimeType()
int error() const
void result(KJob *job)
virtual Q_SCRIPTABLE void start()=0
QString errorText() const
QString i18nc(const char *context, const char *text, const TYPE &arg...)
A widget for editing the display name of a contact.
PostalAddress address(const QVariant &location)
ButtonCode questionTwoActions(QWidget *parent, const QString &text, const QString &title, const KGuiItem &primaryAction, const KGuiItem &secondaryAction, const QString &dontAskAgainName=QString(), Options options=Notify)
KGuiItem cancel()
void accepted()
void rejected()
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
bool isEmpty() const const
qsizetype size() const const
WA_DeleteOnClose
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Apr 4 2025 12:01:58 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.