Mailcommon

filteractionaddtoaddressbook.cpp
1 /*
2  * SPDX-FileCopyrightText: 1996-1998 Stefan Taferner <[email protected]>
3  *
4  * SPDX-License-Identifier: GPL-2.0-or-later
5  *
6  */
7 
8 #include "filteractionaddtoaddressbook.h"
9 
10 #include <Akonadi/AddContactJob>
11 
12 #include <Akonadi/TagSelectionDialog>
13 #include <Akonadi/TagWidget>
14 
15 #include <Akonadi/CollectionComboBox>
16 #include <KComboBox>
17 #include <KContacts/Addressee>
18 #include <KEmailAddress>
19 #include <KLocalizedString>
20 
21 #include <QGridLayout>
22 #include <QLabel>
23 
24 using namespace MailCommon;
25 
26 FilterAction *FilterActionAddToAddressBook::newAction()
27 {
28  return new FilterActionAddToAddressBook;
29 }
30 
31 FilterActionAddToAddressBook::FilterActionAddToAddressBook(QObject *parent)
32  : FilterActionWithStringList(QStringLiteral("add to address book"), i18n("Add to Address Book"), parent)
33  , mFromStr(i18nc("Email sender", "From"))
34  , mToStr(i18nc("Email recipient", "To"))
35  , mCCStr(i18n("CC"))
36  , mBCCStr(i18n("BCC"))
37  , mHeaderType(UnknownHeader)
38  , mCollectionId(-1)
39  , mCategory(i18n("KMail Filter"))
40 {
41 }
42 
43 bool FilterActionAddToAddressBook::isEmpty() const
44 {
45  return (mCollectionId == -1) || (mHeaderType == UnknownHeader);
46 }
47 
48 FilterAction::ReturnCode FilterActionAddToAddressBook::process(ItemContext &context, bool) const
49 {
50  if (isEmpty()) {
51  return ErrorButGoOn;
52  }
53 
54  const auto msg = context.item().payload<KMime::Message::Ptr>();
55 
56  QString headerLine;
57  switch (mHeaderType) {
58  case FromHeader:
59  headerLine = msg->from()->asUnicodeString();
60  break;
61  case ToHeader:
62  headerLine = msg->to()->asUnicodeString();
63  break;
64  case CcHeader:
65  headerLine = msg->cc()->asUnicodeString();
66  break;
67  case BccHeader:
68  headerLine = msg->bcc()->asUnicodeString();
69  break;
70  case UnknownHeader:
71  break;
72  }
73  if (headerLine.isEmpty()) {
74  return ErrorButGoOn;
75  }
76 
77  const QStringList emails = KEmailAddress::splitAddressList(headerLine);
78 
79  for (const QString &singleEmail : emails) {
80  QString name;
81  QString emailString;
82  KContacts::Addressee::parseEmailAddress(singleEmail, name, emailString);
83 
84  KContacts::Addressee contact;
85  contact.setNameFromString(name);
86  KContacts::Email email(emailString);
87  email.setPreferred(true);
88  contact.addEmail(email);
89  if (!mCategory.isEmpty()) {
90  contact.setCategories(mCategory.split(QLatin1Char(';')));
91  }
92 
93  auto job = new Akonadi::AddContactJob(contact, Akonadi::Collection(mCollectionId));
94  job->showMessageBox(false);
95  job->start();
96  }
97 
98  return GoOn;
99 }
100 
101 SearchRule::RequiredPart FilterActionAddToAddressBook::requiredPart() const
102 {
103  return SearchRule::Envelope;
104 }
105 
106 QWidget *FilterActionAddToAddressBook::createParamWidget(QWidget *parent) const
107 {
108  auto widget = new QWidget(parent);
109  auto layout = new QGridLayout(widget);
110 
111  const auto headerCombo = new KComboBox(widget);
112  headerCombo->setMinimumWidth(50);
113  headerCombo->setObjectName(QStringLiteral("HeaderComboBox"));
114  layout->addWidget(headerCombo, 0, 0, 2, 1, Qt::AlignVCenter);
115 
116  auto label = new QLabel(i18n("with category"), widget);
117  label->setObjectName(QStringLiteral("label_with_category"));
118  layout->addWidget(label, 0, 1);
119 
120  auto categoryEdit = new Akonadi::TagWidget(widget);
121  categoryEdit->setObjectName(QStringLiteral("CategoryEdit"));
122  layout->addWidget(categoryEdit, 0, 2);
123 
124  label = new QLabel(i18n("in address book"), widget);
125  label->setObjectName(QStringLiteral("label_in_addressbook"));
126  layout->addWidget(label, 1, 1);
127 
128  auto collectionComboBox = new Akonadi::CollectionComboBox(widget);
129  collectionComboBox->setMimeTypeFilter(QStringList() << KContacts::Addressee::mimeType());
130  collectionComboBox->setAccessRightsFilter(Akonadi::Collection::CanCreateItem);
131 
132  collectionComboBox->setObjectName(QStringLiteral("AddressBookComboBox"));
133  collectionComboBox->setToolTip(
134  i18n("This defines the preferred address book.\n"
135  "If it is not accessible, the filter will fallback to the default address book."));
136  layout->addWidget(collectionComboBox, 1, 2);
137  connect(headerCombo, &KComboBox::currentIndexChanged, this, &FilterActionAddToAddressBook::filterActionModified);
138  connect(collectionComboBox, &Akonadi::CollectionComboBox::activated, this, &FilterActionAddToAddressBook::filterActionModified);
139  connect(categoryEdit, &Akonadi::TagWidget::selectionChanged, this, &FilterActionAddToAddressBook::filterActionModified);
140 
141  setParamWidgetValue(widget);
142 
143  return widget;
144 }
145 
146 namespace
147 {
148 Akonadi::Tag::List namesToTags(const QStringList &names)
149 {
150  Akonadi::Tag::List tags;
151  tags.reserve(names.size());
152  std::transform(names.cbegin(), names.cend(), std::back_inserter(tags), [](const QString &name) {
153  return Akonadi::Tag{name};
154  });
155  return tags;
156 }
157 
158 QStringList tagsToNames(const Akonadi::Tag::List &tags)
159 {
160  QStringList names;
161  names.reserve(tags.size());
162  std::transform(tags.cbegin(), tags.cend(), std::back_inserter(names), std::bind(&Akonadi::Tag::name, std::placeholders::_1));
163  return names;
164 }
165 }
166 
167 void FilterActionAddToAddressBook::setParamWidgetValue(QWidget *paramWidget) const
168 {
169  const auto headerCombo = paramWidget->findChild<KComboBox *>(QStringLiteral("HeaderComboBox"));
170  Q_ASSERT(headerCombo);
171  headerCombo->clear();
172  headerCombo->addItem(mFromStr, FromHeader);
173  headerCombo->addItem(mToStr, ToHeader);
174  headerCombo->addItem(mCCStr, CcHeader);
175  headerCombo->addItem(mBCCStr, BccHeader);
176 
177  headerCombo->setCurrentIndex(headerCombo->findData(mHeaderType));
178 
179  auto categoryEdit = paramWidget->findChild<Akonadi::TagWidget *>(QStringLiteral("CategoryEdit"));
180  Q_ASSERT(categoryEdit);
181  categoryEdit->setSelection(namesToTags(mCategory.split(QLatin1Char(';'))));
182 
183  auto *collectionComboBox = paramWidget->findChild<Akonadi::CollectionComboBox *>(QStringLiteral("AddressBookComboBox"));
184  Q_ASSERT(collectionComboBox);
185  collectionComboBox->setDefaultCollection(Akonadi::Collection(mCollectionId));
186  collectionComboBox->setProperty("collectionId", mCollectionId);
187 }
188 
189 void FilterActionAddToAddressBook::applyParamWidgetValue(QWidget *paramWidget)
190 {
191  const auto headerCombo = paramWidget->findChild<QComboBox *>(QStringLiteral("HeaderComboBox"));
192  Q_ASSERT(headerCombo);
193  mHeaderType = static_cast<HeaderType>(headerCombo->itemData(headerCombo->currentIndex()).toInt());
194 
195  const auto categoryEdit = paramWidget->findChild<Akonadi::TagWidget *>(QStringLiteral("CategoryEdit"));
196  Q_ASSERT(categoryEdit);
197  mCategory = tagsToNames(categoryEdit->selection()).join(QLatin1Char(';'));
198 
199  const Akonadi::CollectionComboBox *collectionComboBox = paramWidget->findChild<Akonadi::CollectionComboBox *>(QStringLiteral("AddressBookComboBox"));
200  Q_ASSERT(collectionComboBox);
201  const Akonadi::Collection collection = collectionComboBox->currentCollection();
202 
203  // it might be that the model of collectionComboBox has not finished loading yet, so
204  // we use the previously 'stored' value from the 'collectionId' property
205  if (collection.isValid()) {
206  mCollectionId = collection.id();
207  connect(collectionComboBox, &Akonadi::CollectionComboBox::currentIndexChanged, this, &FilterActionAddToAddressBook::filterActionModified);
208  } else {
209  const QVariant value = collectionComboBox->property("collectionId");
210  if (value.isValid()) {
211  mCollectionId = value.toLongLong();
212  }
213  }
214 }
215 
216 void FilterActionAddToAddressBook::clearParamWidget(QWidget *paramWidget) const
217 {
218  const auto headerCombo = paramWidget->findChild<QComboBox *>(QStringLiteral("HeaderComboBox"));
219  Q_ASSERT(headerCombo);
220  headerCombo->setCurrentIndex(0);
221 
222  auto categoryEdit = paramWidget->findChild<Akonadi::TagWidget *>(QStringLiteral("CategoryEdit"));
223  Q_ASSERT(categoryEdit);
224  categoryEdit->setSelection(namesToTags(mCategory.split(QLatin1Char(';'))));
225 }
226 
227 QString FilterActionAddToAddressBook::argsAsString() const
228 {
229  QString result;
230 
231  switch (mHeaderType) {
232  case FromHeader:
233  result = QStringLiteral("From");
234  break;
235  case ToHeader:
236  result = QStringLiteral("To");
237  break;
238  case CcHeader:
239  result = QStringLiteral("CC");
240  break;
241  case BccHeader:
242  result = QStringLiteral("BCC");
243  break;
244  case UnknownHeader:
245  break;
246  }
247 
248  result += QLatin1Char('\t');
249  result += QString::number(mCollectionId);
250  result += QLatin1Char('\t');
251  result += mCategory;
252 
253  return result;
254 }
255 
256 void FilterActionAddToAddressBook::argsFromString(const QString &argsStr)
257 {
258  const QStringList parts = argsStr.split(QLatin1Char('\t'), Qt::KeepEmptyParts);
259  const QString firstElement = parts[0];
260  if (firstElement == QLatin1String("From")) {
261  mHeaderType = FromHeader;
262  } else if (firstElement == QLatin1String("To")) {
263  mHeaderType = ToHeader;
264  } else if (firstElement == QLatin1String("CC")) {
265  mHeaderType = CcHeader;
266  } else if (firstElement == QLatin1String("BCC")) {
267  mHeaderType = BccHeader;
268  } else {
269  mHeaderType = UnknownHeader;
270  }
271  if (parts.count() >= 2) {
272  mCollectionId = parts[1].toLongLong();
273  }
274 
275  if (parts.count() < 3) {
276  mCategory.clear();
277  } else {
278  mCategory = parts[2];
279  }
280 }
281 
282 QString FilterActionAddToAddressBook::informationAboutNotValidAction() const
283 {
284  QString result;
285  if (mHeaderType == UnknownHeader) {
286  result = i18n("Header type selected is unknown.");
287  }
288  if (mCollectionId == -1) {
289  if (!result.isEmpty()) {
290  result += QLatin1Char('\n');
291  }
292  result += i18n("No addressbook selected.");
293  }
294  return result;
295 }
AlignVCenter
bool isValid() const const
QString number(int n, int base)
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
int count(const T &value) const const
Abstract base class for filter actions with a fixed set of string parameters.
qlonglong toLongLong(bool *ok) const const
KCODECS_EXPORT QStringList splitAddressList(const QString &aStr)
void setCategories(const QStringList &category)
void reserve(int alloc)
int size() const const
static void parseEmailAddress(const QString &rawEmail, QString &fullName, QString &email)
Abstract base class for mail filter actions.
Definition: filteraction.h:38
QString i18n(const char *text, const TYPE &arg...)
KeepEmptyParts
RequiredPart
Possible required parts.
Definition: searchrule.h:68
bool isEmpty() const const
QList::const_iterator cend() const const
T findChild(const QString &name, Qt::FindChildOptions options) const const
A helper class for the filtering process.
Definition: itemcontext.h:26
QString label(StandardShortcut id)
ReturnCode
Describes the possible return codes of filter processing:
Definition: filteraction.h:45
Akonadi::Collection currentCollection() const
void setNameFromString(const QString &s)
QString name(StandardShortcut id)
QList::const_iterator cbegin() const const
void currentIndexChanged(int index)
void clear()
QString i18nc(const char *context, const char *text, const TYPE &arg...)
void addEmail(const Email &email)
bool isValid() const
void activated(int index)
Akonadi::Item & item()
Returns the item of the context.
Definition: itemcontext.cpp:18
static QString mimeType()
The filter dialog.
QVariant property(const char *name) const const
T payload() const
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sat Sep 24 2022 03:58:14 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.