Messagelib

distributionlistdialog.cpp
1 /*
2  SPDX-FileCopyrightText: 2010 Volker Krause <[email protected]>
3  This file was part of KMail.
4  SPDX-FileCopyrightText: 2005 Cornelius Schumacher <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 
9 #include "distributionlistdialog.h"
10 
11 #include <Akonadi/CollectionDialog>
12 #include <Akonadi/Contact/ContactGroupSearchJob>
13 #include <Akonadi/Contact/ContactSearchJob>
14 #include <Akonadi/ItemCreateJob>
15 #include <KEmailAddress>
16 #include <kcontacts_version.h>
17 
18 #include "messagecomposer_debug.h"
19 #include <KLocalizedString>
20 #include <KMessageBox>
21 #include <QInputDialog>
22 #include <QLineEdit>
23 
24 #include <KConfigGroup>
25 #include <KSharedConfig>
26 #include <QDialogButtonBox>
27 #include <QHBoxLayout>
28 #include <QHeaderView>
29 #include <QLabel>
30 #include <QPushButton>
31 #include <QTreeWidget>
32 #include <QTreeWidgetItem>
33 #include <QVBoxLayout>
34 
35 using namespace MessageComposer;
36 
37 namespace MessageComposer
38 {
39 class DistributionListItem : public QTreeWidgetItem
40 {
41 public:
42  explicit DistributionListItem(QTreeWidget *tree)
43  : QTreeWidgetItem(tree)
44  {
46  }
47 
48  void setAddressee(const KContacts::Addressee &a, const QString &email)
49  {
50  init(a, email);
51  }
52 
53  void init(const KContacts::Addressee &a, const QString &email)
54  {
55  mAddressee = a;
56  mEmail = email;
57  mId = -1;
58  setText(0, mAddressee.realName());
59  setText(1, mEmail);
60  }
61 
62  Q_REQUIRED_RESULT KContacts::Addressee addressee() const
63  {
64  return mAddressee;
65  }
66 
67  Q_REQUIRED_RESULT QString email() const
68  {
69  return mEmail;
70  }
71 
72  Q_REQUIRED_RESULT bool isTransient() const
73  {
74  return mId == -1;
75  }
76 
77  void setId(Akonadi::Item::Id id)
78  {
79  mId = id;
80  }
81 
82  Q_REQUIRED_RESULT Akonadi::Item::Id id() const
83  {
84  return mId;
85  }
86 
87 private:
88  KContacts::Addressee mAddressee;
89  QString mEmail;
90  Akonadi::Item::Id mId = -1;
91 };
92 }
93 
94 DistributionListDialog::DistributionListDialog(QWidget *parent)
95  : QDialog(parent)
96 {
97  setWindowTitle(i18nc("@title:window", "Save Distribution List"));
98  setModal(false);
99 
100  auto mainLayout = new QVBoxLayout(this);
101 
102  auto topFrame = new QWidget(this);
103  mainLayout->addWidget(topFrame);
104 
105  auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel, this);
106  mUser1Button = new QPushButton(this);
107  buttonBox->addButton(mUser1Button, QDialogButtonBox::ActionRole);
108  mUser1Button->setText(i18nc("@action:button", "Save List"));
109  mUser1Button->setEnabled(false);
110  mUser1Button->setDefault(true);
111  connect(buttonBox, &QDialogButtonBox::accepted, this, &DistributionListDialog::accept);
112  connect(buttonBox, &QDialogButtonBox::rejected, this, &DistributionListDialog::reject);
113 
114  mainLayout->addWidget(buttonBox);
115 
116  QBoxLayout *topLayout = new QVBoxLayout(topFrame);
117  topLayout->setContentsMargins({});
118 
119  QBoxLayout *titleLayout = new QHBoxLayout;
120  topLayout->addLayout(titleLayout);
121 
122  auto label = new QLabel(i18nc("@label:textbox Name of the distribution list.", "&Name:"), topFrame);
123  titleLayout->addWidget(label);
124 
125  mTitleEdit = new QLineEdit(topFrame);
126  titleLayout->addWidget(mTitleEdit);
127  mTitleEdit->setFocus();
128  mTitleEdit->setClearButtonEnabled(true);
129  label->setBuddy(mTitleEdit);
130 
131  mRecipientsList = new QTreeWidget(topFrame);
132  mRecipientsList->setHeaderLabels(QStringList() << i18nc("@title:column Name of the recipient", "Name")
133  << i18nc("@title:column Email of the recipient", "Email"));
134  mRecipientsList->setRootIsDecorated(false);
135  mRecipientsList->header()->setSectionsMovable(false);
136  topLayout->addWidget(mRecipientsList);
137  connect(mUser1Button, &QPushButton::clicked, this, &DistributionListDialog::slotUser1);
138  connect(mTitleEdit, &QLineEdit::textChanged, this, &DistributionListDialog::slotTitleChanged);
139  readConfig();
140 }
141 
142 DistributionListDialog::~DistributionListDialog()
143 {
144  writeConfig();
145 }
146 
147 // This starts one ContactSearchJob for each of the specified recipients.
148 void DistributionListDialog::setRecipients(const Recipient::List &recipients)
149 {
150  Recipient::List::ConstIterator end(recipients.constEnd());
151  for (Recipient::List::ConstIterator it = recipients.constBegin(); it != end; ++it) {
152  const QStringList emails = KEmailAddress::splitAddressList((*it)->email());
153  QStringList::ConstIterator end2(emails.constEnd());
154  for (QStringList::ConstIterator it2 = emails.constBegin(); it2 != end2; ++it2) {
155  QString name;
156  QString email;
157  KContacts::Addressee::parseEmailAddress(*it2, name, email);
158  if (!email.isEmpty()) {
159  auto job = new Akonadi::ContactSearchJob(this);
161  job->setProperty("name", name);
162  job->setProperty("email", email);
163  connect(job, &Akonadi::ContactSearchJob::result, this, &DistributionListDialog::slotDelayedSetRecipients);
164  }
165  }
166  }
167 }
168 
169 // This result slot will be called once for each of the original recipients.
170 // There could potentially be more than one Akonadi item returned per
171 // recipient, in the case where email addresses are duplicated between contacts.
172 void DistributionListDialog::slotDelayedSetRecipients(KJob *job)
173 {
174  const Akonadi::ContactSearchJob *searchJob = qobject_cast<Akonadi::ContactSearchJob *>(job);
175  const Akonadi::Item::List akItems = searchJob->items();
176 
177  const QString email = searchJob->property("email").toString();
178  QString name = searchJob->property("name").toString();
179  if (name.isEmpty()) {
180  const int index = email.indexOf(QLatin1Char('@'));
181  if (index != -1) {
182  name = email.left(index);
183  } else {
184  name = email;
185  }
186  }
187 
188  if (akItems.isEmpty()) {
189  KContacts::Addressee contact;
190  contact.setNameFromString(name);
191 #if KContacts_VERSION < QT_VERSION_CHECK(5, 88, 0)
192  contact.insertEmail(email);
193 #else
194  contact.addEmail(KContacts::Email(email));
195 #endif
196 
197  auto item = new DistributionListItem(mRecipientsList);
198  item->setAddressee(contact, email);
199  item->setCheckState(0, Qt::Checked);
200  } else {
201  bool isFirst = true;
202  for (const Akonadi::Item &akItem : std::as_const(akItems)) {
203  if (akItem.hasPayload<KContacts::Addressee>()) {
204  const auto contact = akItem.payload<KContacts::Addressee>();
205 
206  auto item = new DistributionListItem(mRecipientsList);
207  item->setAddressee(contact, email);
208 
209  // Need to record the Akonadi ID of the contact, so that
210  // it can be added as a reference later. Setting an ID
211  // makes the item non-transient.
212  item->setId(akItem.id());
213 
214  // If there were multiple contacts returned for an email address,
215  // then check the first one and uncheck any subsequent ones.
216  if (isFirst) {
217  item->setCheckState(0, Qt::Checked);
218  isFirst = false;
219  } else {
220  // Need this to create an unchecked item, as otherwise the
221  // item will have no checkbox at all.
222  item->setCheckState(0, Qt::Unchecked);
223  }
224  }
225  }
226  }
227 }
228 
229 void DistributionListDialog::slotUser1()
230 {
231  bool isEmpty = true;
232  const int numberOfTopLevel(mRecipientsList->topLevelItemCount());
233  for (int i = 0; i < numberOfTopLevel; ++i) {
234  auto item = static_cast<DistributionListItem *>(mRecipientsList->topLevelItem(i));
235  if (item && item->checkState(0) == Qt::Checked) {
236  isEmpty = false;
237  break;
238  }
239  }
240 
241  if (isEmpty) {
243  i18nc("@info",
244  "There are no recipients in your list. "
245  "First select some recipients, "
246  "then try again."));
247  return;
248  }
249 
250  QString name = mTitleEdit->text();
251 
252  if (name.isEmpty()) {
253  bool ok = false;
254  name = QInputDialog::getText(this,
255  i18nc("@title:window", "New Distribution List"),
256  i18nc("@label:textbox", "Please enter name:"),
258  QString(),
259  &ok);
260  if (!ok || name.isEmpty()) {
261  return;
262  }
263  }
264 
265  auto job = new Akonadi::ContactGroupSearchJob();
266  job->setQuery(Akonadi::ContactGroupSearchJob::Name, name);
267  job->setProperty("name", name);
268  connect(job, &Akonadi::ContactSearchJob::result, this, &DistributionListDialog::slotDelayedUser1);
269 }
270 
271 void DistributionListDialog::slotDelayedUser1(KJob *job)
272 {
273  const Akonadi::ContactGroupSearchJob *searchJob = qobject_cast<Akonadi::ContactGroupSearchJob *>(job);
274  const QString name = searchJob->property("name").toString();
275 
276  if (!searchJob->contactGroups().isEmpty()) {
277  qDebug() << " searchJob->contactGroups()" << searchJob->contactGroups().count();
279  xi18nc("@info",
280  "<para>Distribution list with the given name <resource>%1</resource> "
281  "already exists. Please select a different name.</para>",
282  name));
283  return;
284  }
285 
286  QPointer<Akonadi::CollectionDialog> dlg = new Akonadi::CollectionDialog(Akonadi::CollectionDialog::KeepTreeExpanded, nullptr, this);
288  dlg->setAccessRightsFilter(Akonadi::Collection::CanCreateItem);
289  dlg->setWindowTitle(i18nc("@title:window", "Select Address Book"));
290  dlg->setDescription(i18n("Select the address book folder to store the contact group in:"));
291  if (dlg->exec()) {
292  const Akonadi::Collection targetCollection = dlg->selectedCollection();
293  delete dlg;
294 
295  KContacts::ContactGroup group(name);
296  const int numberOfTopLevel(mRecipientsList->topLevelItemCount());
297  for (int i = 0; i < numberOfTopLevel; ++i) {
298  auto item = static_cast<DistributionListItem *>(mRecipientsList->topLevelItem(i));
299  if (item && item->checkState(0) == Qt::Checked) {
300  qCDebug(MESSAGECOMPOSER_LOG) << item->addressee().fullEmail() << item->addressee().uid();
301  if (item->isTransient()) {
302  group.append(KContacts::ContactGroup::Data(item->addressee().realName(), item->email()));
303  } else {
305  if (item->email() != item->addressee().preferredEmail()) {
306  reference.setPreferredEmail(item->email());
307  }
308  group.append(reference);
309  }
310  }
311  }
312 
314  groupItem.setPayload<KContacts::ContactGroup>(group);
315 
316  Akonadi::Job *createJob = new Akonadi::ItemCreateJob(groupItem, targetCollection);
317  connect(createJob, &Akonadi::ItemCreateJob::result, this, &DistributionListDialog::slotContactGroupCreateJobResult);
318  }
319 
320  delete dlg;
321 }
322 
323 void DistributionListDialog::slotContactGroupCreateJobResult(KJob *job)
324 {
325  if (job->error()) {
326  KMessageBox::information(this, i18n("Unable to create distribution list: %1", job->errorString()));
327  qCWarning(MESSAGECOMPOSER_LOG) << "Unable to create distribution list:" << job->errorText();
328  } else {
329  accept();
330  }
331 }
332 
333 void DistributionListDialog::slotTitleChanged(const QString &text)
334 {
335  mUser1Button->setEnabled(!text.trimmed().isEmpty());
336 }
337 
338 void DistributionListDialog::readConfig()
339 {
340  KSharedConfig::Ptr cfg = KSharedConfig::openStateConfig();
341  KConfigGroup group(cfg, "DistributionListDialog");
342  const QSize size = group.readEntry("Size", QSize());
343  if (!size.isEmpty()) {
344  resize(size);
345  }
346  mRecipientsList->header()->restoreState(group.readEntry("Header", QByteArray()));
347 }
348 
349 void DistributionListDialog::writeConfig()
350 {
351  KSharedConfig::Ptr cfg = KSharedConfig::openStateConfig();
352  KConfigGroup group(cfg, "DistributionListDialog");
353  group.writeEntry("Size", size());
354  group.writeEntry("Header", mRecipientsList->header()->saveState());
355 }
static QString mimeType()
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
QString & append(QChar ch)
void setContentsMargins(int left, int top, int right, int bottom)
Item::List items() const
bool isEmpty() const const
QString xi18nc(const char *context, const char *text, const TYPE &arg...)
static void parseEmailAddress(const QString &rawEmail, QString &fullName, QString &email)
virtual QString errorString() const
QString uid() const
QVector::const_iterator constEnd() const const
void textChanged(const QString &text)
const QLatin1String name
void insertEmail(const QString &email, bool preferred=false, const QMap< QString, QStringList > &param=QMap< QString, QStringList >())
QString fullEmail(const QString &email=QString()) const
void setFlags(Qt::ItemFlags flags)
static KSharedConfig::Ptr openStateConfig(const QString &fileName=QString())
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
QString number(int n, int base)
QVariant property(const char *name) const const
QString i18nc(const char *context, const char *text, const TYPE &arg...)
static QString mimeType()
void addEmail(const Email &email)
void setNameFromString(const QString &s)
QString label(StandardShortcut id)
bool isEmpty() const const
QString trimmed() const const
void clicked(bool checked)
KContacts::ContactGroup::List contactGroups() const
QString realName() const
QString getText(QWidget *parent, const QString &title, const QString &label, QLineEdit::EchoMode mode, const QString &text, bool *ok, Qt::WindowFlags flags, Qt::InputMethodHints inputMethodHints)
QString preferredEmail() const
QString toLower() const const
KCODECS_EXPORT QStringList splitAddressList(const QString &aStr)
QCA_EXPORT void init()
QString i18n(const char *text, const TYPE &arg...)
void readConfig()
const QList< QKeySequence > & end()
QVector::const_iterator constBegin() const const
Qt::ItemFlags flags() const const
Simple interface that both EncryptJob and SignEncryptJob implement so the composer can extract some e...
bool isEmpty() const const
typedef ConstIterator
int count(const T &value) const const
void setText(int column, const QString &text)
QString left(int n) const const
bool setProperty(const char *name, const QVariant &value)
void result(KJob *job)
QList::const_iterator constEnd() const const
void information(QWidget *parent, const QString &text, const QString &caption=QString(), const QString &dontShowAgainName=QString(), Options options=Notify)
QList::const_iterator constBegin() const const
QString toString() const const
QString errorText() const
void addLayout(QLayout *layout, int stretch)
int error() const
ItemIsUserCheckable
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sat Dec 4 2021 23:12:52 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.