Akonadi Contacts

contacteditor.cpp
1 /*
2  This file is part of Akonadi Contact.
3 
4  SPDX-FileCopyrightText: 2009 Tobias Koenig <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 
9 #include "contacteditor.h"
10 
11 #include "abstractcontacteditorwidget_p.h"
12 #include "attributes/contactmetadataattribute_p.h"
13 #include "contactmetadataakonadi_p.h"
14 #include "editor/contacteditorwidget.h"
15 
16 #include <Akonadi/CollectionDialog>
17 #include <Akonadi/CollectionFetchJob>
18 #include <Akonadi/ItemCreateJob>
19 #include <Akonadi/ItemFetchJob>
20 #include <Akonadi/ItemFetchScope>
21 #include <Akonadi/ItemModifyJob>
22 #include <Akonadi/Monitor>
23 #include <Akonadi/Session>
24 #include <KContacts/Addressee>
25 #include <KLocalizedString>
26 
27 #include <QMessageBox>
28 #include <QPointer>
29 #include <QVBoxLayout>
30 
31 using namespace Akonadi;
32 
33 class Akonadi::AkonadiContactEditorPrivate
34 {
35 public:
36  AkonadiContactEditorPrivate(AkonadiContactEditor::Mode mode,
38  ContactEditor::AbstractContactEditorWidget *editorWidget,
39  AkonadiContactEditor *parent)
40  : mParent(parent)
41  , mMode(mode)
42  {
43  if (editorWidget) {
44  mEditorWidget = editorWidget;
45  } else {
46  mEditorWidget =
48  mParent);
49  }
50 
51  auto layout = new QVBoxLayout(mParent);
52  layout->setContentsMargins({});
53  layout->setSpacing(0);
54  layout->addWidget(mEditorWidget);
55  }
56 
57  ~AkonadiContactEditorPrivate()
58  {
59  delete mMonitor;
60  }
61 
62  void itemFetchDone(KJob *job);
63  void parentCollectionFetchDone(KJob *job);
64  void storeDone(KJob *job);
65  void itemChanged(const Akonadi::Item &item, const QSet<QByteArray> &);
66 
67  void loadContact(const KContacts::Addressee &addr, const ContactMetaDataAkonadi &metaData);
68  void storeContact(KContacts::Addressee &addr, ContactMetaDataAkonadi &metaData);
69  void setupMonitor();
70 
71  AkonadiContactEditor *const mParent;
72  const AkonadiContactEditor::Mode mMode;
73  Akonadi::Item mItem;
74  Akonadi::ContactMetaDataAkonadi mContactMetaData;
75  Akonadi::Monitor *mMonitor = nullptr;
76  Akonadi::Collection mDefaultCollection;
77  ContactEditor::AbstractContactEditorWidget *mEditorWidget = nullptr;
78  bool mReadOnly = false;
79 };
80 
81 void Akonadi::AkonadiContactEditorPrivate::itemFetchDone(KJob *job)
82 {
83  if (job->error() != KJob::NoError) {
84  Q_EMIT mParent->error(job->errorString());
85  Q_EMIT mParent->finished();
86  return;
87  }
88 
89  auto fetchJob = qobject_cast<Akonadi::ItemFetchJob *>(job);
90  if (!fetchJob) {
91  return;
92  }
93 
94  if (fetchJob->items().isEmpty()) {
95  return;
96  }
97 
98  mItem = fetchJob->items().at(0);
99 
100  mReadOnly = false;
101  if (mMode == AkonadiContactEditor::EditMode) {
102  // if in edit mode we have to fetch the parent collection to find out
103  // about the modify rights of the item
104 
105  auto collectionFetchJob = new Akonadi::CollectionFetchJob(mItem.parentCollection(), Akonadi::CollectionFetchJob::Base);
106  mParent->connect(collectionFetchJob, &CollectionFetchJob::result, mParent, [this](KJob *job) {
107  parentCollectionFetchDone(job);
108  });
109  } else {
110  const auto addr = mItem.payload<KContacts::Addressee>();
111  mContactMetaData.load(mItem);
112  loadContact(addr, mContactMetaData);
113  mEditorWidget->setReadOnly(mReadOnly);
114  }
115 }
116 
117 void Akonadi::AkonadiContactEditorPrivate::parentCollectionFetchDone(KJob *job)
118 {
119  if (job->error()) {
120  Q_EMIT mParent->error(job->errorString());
121  Q_EMIT mParent->finished();
122  return;
123  }
124 
125  auto fetchJob = qobject_cast<Akonadi::CollectionFetchJob *>(job);
126  if (!fetchJob) {
127  return;
128  }
129 
130  const Akonadi::Collection parentCollection = fetchJob->collections().at(0);
131  if (parentCollection.isValid()) {
132  mReadOnly = !(parentCollection.rights() & Collection::CanChangeItem);
133  }
134 
135  const auto addr = mItem.payload<KContacts::Addressee>();
136  mContactMetaData.load(mItem);
137  loadContact(addr, mContactMetaData);
138  mEditorWidget->setReadOnly(mReadOnly);
139 }
140 
141 void Akonadi::AkonadiContactEditorPrivate::storeDone(KJob *job)
142 {
143  if (job->error() != KJob::NoError) {
144  Q_EMIT mParent->error(job->errorString());
145  Q_EMIT mParent->finished();
146  return;
147  }
148 
149  if (mMode == AkonadiContactEditor::EditMode) {
150  Q_EMIT mParent->contactStored(mItem);
151  } else if (mMode == AkonadiContactEditor::CreateMode) {
152  Q_EMIT mParent->contactStored(static_cast<Akonadi::ItemCreateJob *>(job)->item());
153  }
154  Q_EMIT mParent->finished();
155 }
156 
157 void Akonadi::AkonadiContactEditorPrivate::itemChanged(const Akonadi::Item &item, const QSet<QByteArray> &)
158 {
159  Q_UNUSED(item)
160  QPointer<QMessageBox> dlg = new QMessageBox(mParent); // krazy:exclude=qclasses
161 
162  dlg->setInformativeText(i18n("The contact has been changed by someone else.\nWhat should be done?"));
163  dlg->addButton(i18n("Take over changes"), QMessageBox::AcceptRole);
164  dlg->addButton(i18n("Ignore and Overwrite changes"), QMessageBox::RejectRole);
165 
166  if (dlg->exec() == QMessageBox::AcceptRole) {
167  auto job = new Akonadi::ItemFetchJob(mItem);
168  job->fetchScope().fetchFullPayload();
169  job->fetchScope().fetchAttribute<ContactMetaDataAttribute>();
170  job->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent);
171 
172  mParent->connect(job, &ItemFetchJob::result, mParent, [this](KJob *job) {
173  itemFetchDone(job);
174  });
175  } else {
176  // Still update the item so that the internal revision match
177  mItem = item;
178  }
179 
180  delete dlg;
181 }
182 
183 void Akonadi::AkonadiContactEditorPrivate::loadContact(const KContacts::Addressee &addr, const ContactMetaDataAkonadi &metaData)
184 {
185  mEditorWidget->loadContact(addr, metaData);
186 }
187 
188 void Akonadi::AkonadiContactEditorPrivate::storeContact(KContacts::Addressee &addr, ContactMetaDataAkonadi &metaData)
189 {
190  mEditorWidget->storeContact(addr, metaData);
191 }
192 
193 void Akonadi::AkonadiContactEditorPrivate::setupMonitor()
194 {
195  delete mMonitor;
196  mMonitor = new Akonadi::Monitor;
197  mMonitor->setObjectName(QStringLiteral("ContactEditorMonitor"));
198  mMonitor->ignoreSession(Akonadi::Session::defaultSession());
199 
200  QObject::connect(mMonitor, &Monitor::itemChanged, mParent, [this](const Akonadi::Item &item, const QSet<QByteArray> &set) {
201  itemChanged(item, set);
202  });
203 }
204 
206  : QWidget(parent)
207  , d(new AkonadiContactEditorPrivate(mode, FullMode, nullptr, this))
208 {
209 }
210 
211 Akonadi::AkonadiContactEditor::AkonadiContactEditor(Mode mode, ContactEditor::AbstractContactEditorWidget *editorWidget, QWidget *parent)
212  : QWidget(parent)
213  , d(new AkonadiContactEditorPrivate(mode, FullMode, editorWidget, this))
214 {
215 }
216 
218  : QWidget(parent)
219  , d(new AkonadiContactEditorPrivate(mode, displayMode, nullptr, this))
220 {
221 }
222 
224 
226 {
227  if (d->mMode == CreateMode) {
228  Q_ASSERT_X(false, "ContactEditor::loadContact", "You are calling loadContact in CreateMode!");
229  }
230 
231  auto job = new Akonadi::ItemFetchJob(item);
232  job->fetchScope().fetchFullPayload();
233  job->fetchScope().fetchAttribute<ContactMetaDataAttribute>();
234  job->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent);
235 
236  connect(job, &ItemFetchJob::result, this, [this](KJob *job) {
237  d->itemFetchDone(job);
238  });
239 
240  d->setupMonitor();
241  d->mMonitor->setItemMonitored(item);
242 }
243 
245 {
247  d->storeContact(addr, d->mContactMetaData);
248  return addr;
249 }
250 
252 {
253  if (d->mMode == EditMode) {
254  if (!d->mItem.isValid() || d->mReadOnly) {
255  Q_EMIT finished();
256  return;
257  }
258 
259  auto addr = d->mItem.payload<KContacts::Addressee>();
260 
261  d->storeContact(addr, d->mContactMetaData);
262 
263  d->mContactMetaData.store(d->mItem);
264 
265  d->mItem.setPayload<KContacts::Addressee>(addr);
266 
267  auto job = new Akonadi::ItemModifyJob(d->mItem);
268  connect(job, &ItemModifyJob::result, this, [this](KJob *job) {
269  d->storeDone(job);
270  });
271  } else if (d->mMode == CreateMode) {
272  if (!d->mDefaultCollection.isValid()) {
273  const QStringList mimeTypeFilter(KContacts::Addressee::mimeType());
274 
276  dlg->setMimeTypeFilter(mimeTypeFilter);
277  dlg->setAccessRightsFilter(Collection::CanCreateItem);
278  dlg->setWindowTitle(i18nc("@title:window", "Select Address Book"));
279  dlg->setDescription(i18n("Select the address book the new contact shall be saved in:"));
280  if (dlg->exec() == QDialog::Accepted) {
281  setDefaultAddressBook(dlg->selectedCollection());
282  delete dlg;
283  } else {
284  delete dlg;
285  return;
286  }
287  }
288 
290  d->storeContact(addr, d->mContactMetaData);
291 
292  Akonadi::Item item;
293  item.setPayload<KContacts::Addressee>(addr);
295 
296  d->mContactMetaData.store(item);
297 
298  auto job = new Akonadi::ItemCreateJob(item, d->mDefaultCollection);
299  connect(job, &ItemCreateJob::result, this, [this](KJob *job) {
300  d->storeDone(job);
301  });
302  }
303 }
304 
306 {
307  d->loadContact(contact, d->mContactMetaData);
308 }
309 
311 {
312  d->mDefaultCollection = collection;
313 }
314 
315 bool Akonadi::AkonadiContactEditor::hasNoSavedData() const
316 {
317  return d->mEditorWidget->hasNoSavedData();
318 }
319 
320 #include "moc_contacteditor.cpp"
A widget for editing a contact.
void result(KJob *job)
void loadContact(const Akonadi::Item &contact)
Loads the contact into the editor.
@ FullMode
Show all pages.
void setMimeType(const QString &mimeType)
@ VCardMode
Show just pages with elements stored in vcard.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void setDefaultAddressBook(const Akonadi::Collection &addressbook)
Sets the addressbook which shall be used to store new contacts.
QString i18n(const char *text, const TYPE &arg...)
static Session * defaultSession()
@ CreateMode
Creates a new contact.
Definition: contacteditor.h:81
KContacts::Addressee contact()
ContactEditor::contact.
Rights rights() const
~AkonadiContactEditor() override
Destroys the contact editor.
Mode
Describes the mode of the editor.
Definition: contacteditor.h:80
void saveContactInAddressBook()
Save the contact from the editor back to the storage.
void itemChanged(const Akonadi::Item &item, const QSet< QByteArray > &partIdentifiers)
void setContactTemplate(const KContacts::Addressee &contact)
Sets a contact that is used as template in create mode.
@ EditMode
Edits an existing contact.
Definition: contacteditor.h:82
void setObjectName(const QString &name)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
bool isValid() const
void setPayload(const T &p)
AkonadiContactEditor(Mode mode, QWidget *parent=nullptr)
Creates a new contact editor with the standard editor widget.
virtual QString errorString() const
int error() const
An widget to edit contacts in Akonadi.
Definition: contacteditor.h:72
static QString mimeType()
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sat Apr 1 2023 04:09:04 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.