Akonadi Contacts

addemailaddressjob.cpp
1/*
2 SPDX-FileCopyrightText: 2010 Tobias Koenig <tokoe@kde.org>
3 SPDX-FileCopyrightText: 2010 Nicolas Lécureuil <nicolas.lecureuil@free.fr>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "addemailaddressjob.h"
9#include "widgets/selectaddressbookdialog.h"
10#include <Akonadi/AgentConfigurationDialog>
11#include <Akonadi/AgentFilterProxyModel>
12#include <Akonadi/AgentInstance>
13#include <Akonadi/AgentInstanceCreateJob>
14#include <Akonadi/AgentManager>
15#include <Akonadi/AgentType>
16#include <Akonadi/AgentTypeDialog>
17#include <Akonadi/Collection>
18#include <Akonadi/CollectionDialog>
19#include <Akonadi/CollectionFetchJob>
20#include <Akonadi/CollectionFetchScope>
21#include <Akonadi/ContactEditorDialog>
22#include <Akonadi/ContactSearchJob>
23#include <Akonadi/ItemCreateJob>
24#include <KContacts/ContactGroup>
25
26#include <KLocalizedString>
27#include <KMessageBox>
28
29#include <QPointer>
30
31using namespace Akonadi;
32
33class Akonadi::AddEmailAddressJobPrivate
34{
35public:
36 AddEmailAddressJobPrivate(AddEmailAddressJob *qq, const QString &emailString, QWidget *parentWidget)
37 : q(qq)
38 , mCompleteAddress(emailString)
39 , mParentWidget(parentWidget)
40 , mInteractive(true)
41 {
42 KContacts::Addressee::parseEmailAddress(emailString, mName, mEmail);
43 }
44
45 void slotResourceCreationDone(KJob *job)
46 {
47 if (job->error()) {
48 q->setError(job->error());
49 q->setErrorText(job->errorText());
50 q->emitResult();
51 return;
52 }
53 createContact();
54 }
55
56 void slotSearchDone(KJob *job)
57 {
58 if (job->error()) {
59 q->setError(job->error());
60 q->setErrorText(job->errorText());
61 q->emitResult();
62 return;
63 }
64
65 const Akonadi::ContactSearchJob *searchJob = qobject_cast<Akonadi::ContactSearchJob *>(job);
66
67 const KContacts::Addressee::List contacts = searchJob->contacts();
68 if (!contacts.isEmpty()) {
69 if (mInteractive) {
70 const QString text = xi18nc("@info",
71 "A contact with the email address <email>%1</email> "
72 "is already in your address book.",
73 mCompleteAddress);
74
75 KMessageBox::information(mParentWidget, text, QString(), QStringLiteral("alreadyInAddressBook"));
76 }
77 q->setError(KJob::UserDefinedError);
78 q->emitResult();
79 return;
80 }
81 createContact();
82 }
83
84 void createContact()
85 {
86 const QStringList mimeTypes(KContacts::Addressee::mimeType());
87
88 auto const addressBookJob = new Akonadi::CollectionFetchJob(Akonadi::Collection::root(), Akonadi::CollectionFetchJob::Recursive);
89
90 addressBookJob->fetchScope().setContentMimeTypes(mimeTypes);
91 q->connect(addressBookJob, &Akonadi::CollectionFetchJob::result, q, [this](KJob *job) {
92 slotCollectionsFetched(job);
93 });
94 }
95
96 void slotCollectionsFetched(KJob *job)
97 {
98 if (job->error()) {
99 q->setError(job->error());
100 q->setErrorText(job->errorText());
101 q->emitResult();
102 return;
103 }
104
105 const Akonadi::CollectionFetchJob *addressBookJob = qobject_cast<Akonadi::CollectionFetchJob *>(job);
106
107 Akonadi::Collection::List canCreateItemCollections;
108
109 const Akonadi::Collection::List lstColls = addressBookJob->collections();
110 for (const Akonadi::Collection &collection : lstColls) {
111 if (Akonadi::Collection::CanCreateItem & collection.rights()) {
112 canCreateItemCollections.append(collection);
113 }
114 }
115
116 Akonadi::Collection addressBook;
117
118 const int nbItemCollection(canCreateItemCollections.size());
119 if (nbItemCollection == 0) {
121 mParentWidget,
122 i18nc("@info", "You must create an address book before adding a contact. Do you want to create an address book?"),
123 i18nc("@title:window", "No Address Book Available"),
124 KGuiItem(i18nc("@action:button", "Create Address Book"), QStringLiteral("address-book-new")),
126 == KMessageBox::ButtonCode::PrimaryAction) {
127 QPointer<Akonadi::AgentTypeDialog> dlg = new Akonadi::AgentTypeDialog(mParentWidget);
128 dlg->setWindowTitle(i18nc("@title:window", "Add Address Book"));
129 dlg->agentFilterProxyModel()->addMimeTypeFilter(KContacts::Addressee::mimeType());
130 dlg->agentFilterProxyModel()->addMimeTypeFilter(KContacts::ContactGroup::mimeType());
131 dlg->agentFilterProxyModel()->addCapabilityFilter(QStringLiteral("Resource"));
132
133 if (dlg->exec()) {
134 const Akonadi::AgentType agentType = dlg->agentType();
135
136 if (agentType.isValid()) {
137 auto job = new Akonadi::AgentInstanceCreateJob(agentType, q);
138 q->connect(job, &Akonadi::AgentInstanceCreateJob::result, q, [this, job](KJob *) {
139 if (job->error()) {
140 slotResourceCreationDone(job);
141 return;
142 }
143 auto configureDialog = new Akonadi::AgentConfigurationDialog(job->instance(), mParentWidget);
144 configureDialog->setAttribute(Qt::WA_DeleteOnClose);
145 q->connect(configureDialog, &QDialog::rejected, q, [this, instance = job->instance()] {
146 Akonadi::AgentManager::self()->removeInstance(instance);
147 q->emitResult();
148 });
149 q->connect(configureDialog, &QDialog::accepted, q, [this] {
150 createContact();
151 });
152 configureDialog->show();
153 });
154 job->start();
155 delete dlg;
156 return;
157 } else { // if agent is not valid => return error and finish job
158 q->setError(KJob::UserDefinedError);
159 q->emitResult();
160 delete dlg;
161 return;
162 }
163 } else { // Canceled create agent => return error and finish job
164 q->setError(KJob::UserDefinedError);
165 q->emitResult();
166 delete dlg;
167 return;
168 }
169 } else {
170 q->setError(KJob::UserDefinedError);
171 q->emitResult();
172 return;
173 }
174 } else if (nbItemCollection == 1) {
175 addressBook = canCreateItemCollections[0];
176 } else {
177 // ask user in which address book the new contact shall be stored
178 QPointer<Akonadi::SelectAddressBookDialog> dlg = new Akonadi::SelectAddressBookDialog(mParentWidget);
179
180 bool gotIt = true;
181 if (dlg->exec()) {
182 addressBook = dlg->selectedCollection();
183 } else {
184 q->setError(KJob::UserDefinedError);
185 q->emitResult();
186 gotIt = false;
187 }
188 delete dlg;
189 if (!gotIt) {
190 return;
191 }
192 }
193
194 if (!addressBook.isValid()) {
195 q->setError(KJob::UserDefinedError);
196 q->emitResult();
197 return;
198 }
199 KContacts::Addressee contact;
200 contact.setNameFromString(mName);
201 KContacts::Email email(mEmail);
202 email.setPreferred(true);
203 contact.addEmail(email);
204 // create the new item
205 Akonadi::Item item;
207 item.setPayload<KContacts::Addressee>(contact);
208
209 // save the new item in akonadi storage
210 auto createJob = new Akonadi::ItemCreateJob(item, addressBook, q);
211 q->connect(createJob, &Akonadi::ItemCreateJob::result, q, [this](KJob *job) {
212 slotAddContactDone(job);
213 });
214 }
215
216 void slotAddContactDone(KJob *job)
217 {
218 if (job->error()) {
219 q->setError(job->error());
220 q->setErrorText(job->errorText());
221 q->emitResult();
222 return;
223 }
224
225 const Akonadi::ItemCreateJob *createJob = qobject_cast<Akonadi::ItemCreateJob *>(job);
226 mItem = createJob->item();
227
228 if (mInteractive) {
229 const QString text = xi18nc("@info",
230 "<para>A contact for \"%1\" was successfully added "
231 "to your address book.</para>"
232 "<para>Do you want to edit this new contact now?</para>",
233 mCompleteAddress);
234
235 if (KMessageBox::questionTwoActions(mParentWidget,
236 text,
237 QString(),
238 KGuiItem(i18nc("@action:button", "Edit"), QStringLiteral("document-edit")),
239 KGuiItem(i18nc("@action:button", "Finish"), QStringLiteral("dialog-ok-apply")),
240 QStringLiteral("addedtokabc"))
241 == KMessageBox::ButtonCode::PrimaryAction) {
242 QPointer<Akonadi::ContactEditorDialog> dlg = new Akonadi::ContactEditorDialog(Akonadi::ContactEditorDialog::EditMode, mParentWidget);
243 dlg->setContact(mItem);
244 QObject::connect(dlg.data(), &Akonadi::ContactEditorDialog::contactStored, q, [this](const Akonadi::Item &item) {
245 contactStored(item);
246 });
247 QObject::connect(dlg.data(), &Akonadi::ContactEditorDialog::error, q, [this](const QString &str) {
248 slotContactEditorError(str);
249 });
250 dlg->exec();
251 delete dlg;
252 }
253 }
254 q->emitResult();
255 }
256
257 void slotContactEditorError(const QString &error)
258 {
259 if (mInteractive) {
260 KMessageBox::error(mParentWidget, i18n("Contact cannot be stored: %1", error), i18nc("@title:window", "Failed to store contact"));
261 }
262 }
263
264 void contactStored(const Akonadi::Item &)
265 {
266 if (mInteractive) {
267 Q_EMIT q->successMessage(i18n("Contact created successfully"));
268 }
269 }
270
271 AddEmailAddressJob *const q;
272 const QString mCompleteAddress;
273 QString mEmail;
274 QString mName;
275 QWidget *const mParentWidget;
276 Akonadi::Item mItem;
277 bool mInteractive = false;
278};
279
281 : KJob(parent)
282 , d(new AddEmailAddressJobPrivate(this, email, parentWidget))
283{
284}
285
287
289{
290 // first check whether a contact with the same email exists already
291 auto searchJob = new Akonadi::ContactSearchJob(this);
292 searchJob->setLimit(1);
294 connect(searchJob, &Akonadi::ContactSearchJob::result, this, [this](KJob *job) {
295 d->slotSearchDone(job);
296 });
297}
298
300{
301 return d->mItem;
302}
303
304void AddEmailAddressJob::setInteractive(bool b)
305{
306 d->mInteractive = b;
307}
308
309#include "moc_addemailaddressjob.cpp"
~AddEmailAddressJob() override
Destroys the add email address job.
void start() override
Starts the job.
Akonadi::Item contact() const
Returns the item that represents the new contact.
AddEmailAddressJob(const QString &email, QWidget *parentWidget, QObject *parent=nullptr)
Creates a new add email address job.
bool isValid() const
Collection::List collections() const
bool isValid() const
static Collection root()
QList< Collection > List
@ EditMode
Edits an existing contact.
void error(const QString &errMsg)
This signal is emitted whenever a contact is not updated or stored.
void contactStored(const Akonadi::Item &contact)
This signal is emitted whenever a contact was updated or stored.
Job that searches for contacts in the Akonadi storage.
void setQuery(Criterion criterion, const QString &value, Match match=ExactMatch)
Sets the criterion and value for the search with match.
@ 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.
void setLimit(int limit)
Sets a limit on how many results will be returned by this search job.
void setPayload(const T &p)
void setMimeType(const QString &mimeType)
AddresseeList List
void setNameFromString(const QString &s)
void addEmail(const Email &email)
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
KJob(QObject *parent=nullptr)
QString errorText() const
QString xi18nc(const char *context, const char *text, const TYPE &arg...)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
A widget for editing the display name of a contact.
void information(QWidget *parent, const QString &text, const QString &title=QString(), const QString &dontShowAgainName=QString(), Options options=Notify)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
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)
qsizetype size() const const
QObject(QObject *parent)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * parent() const const
T * data() 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.